J
J
jekahm2016-03-15 19:16:48
Yii
jekahm, 2016-03-15 19:16:48

Why does the VK API method return an empty result during an asynchronous GET request?

Good day!
I am developing a small application on Yii2 to search for users in communities using the VK API. The selection takes a lot of time, so I initially show the user the results of a single message selection and run the script in the background using an asynchronous GET request.
But in the end I get an empty result.
The method responsible for generating an asynchronous request:

private function curlRequestAsync($url, $params, $type = 'POST')
  {
    if (is_array($params)) {
      foreach ($params as $key => &$val) {
        if (is_array($val)) $val = implode(',', $val);
        $post_params[] = $key.'='.urlencode($val);
      }
      $post_string = implode('&', $post_params);
    } else {
      $post_string = $params;
    }

    $parts = parse_url($url);

    $fp = fsockopen($parts['host'],
      isset($parts['port'])?$parts['port']:80,
      $errno, $errstr, 30);

    // Data goes in the path for a GET request
    if('GET' == $type) {
      $parts['path'] .= (!empty($post_string)) ? '?' . $post_string . '&user_id=' . Yii::$app->user->identity->id : '';
    }

    $out = "$type ".$parts['path']." HTTP/1.1\r\n";
    $out.= "Host: ".$parts['host']."\r\n";
    $out.= "Content-Type: application/x-www-form-urlencoded\r\n";
    $out.= "Content-Length: ".((!empty($post_string)) ? strlen($post_string) : '1000')."\r\n";
    $out.= "Connection: Close\r\n\r\n";
    // Data goes in the request body for a POST request
    if ('POST' == $type && isset($post_string)) {
      $out.= $post_string;
    }

    fwrite($fp, $out);
    var_dump($out);
    fclose($fp);
  }

Method call:
$this->curlRequestAsync(Url::toRoute(['get-all-members'], true), http_build_query(Yii::$app->request->get()), 'GET');

The part of the action in which the background process of fetching the rest of the results (namely the community list) takes place:
public function actionGetAllMembers() {
    set_time_limit(0);
    ignore_user_abort(true);

    $model = new SearchForm();

    $groups = $model->getUserSubscriptionsNew();

...
  }

Well, part of the model method itself for sampling, using the VK API method:
public function getUserSubscriptionsNew() {
    $vk = \Yii::$app->authClientCollection->getClient('vkontakte');
    $groups = $vk->post('users.getSubscriptions', ['extended' => 1, 'fields' => 'members_count'])['response'];
    return $groups;
  }

How can this problem be dealt with?
Thanks in advance to everyone for help!

Answer the question

In order to leave comments, you need to log in

2 answer(s)
J
jekahm, 2016-03-16
@jekahm

Solved this problem. Maybe someone will come in handy. Of course, using the functionality of Yii2, but the general principle, I think, is the same everywhere.
We create an additional field in the user table to store the token. When authorizing the user through VK, we write the OAuthToken object there (respectively processed by the serialize function).
In the actionGetAllMembers action, add the following. the code:

$user_id = Yii::$app->request->get('user_id');
$model->refreshSocialToken($user_id);

where user_id is the id of the current user that we pass to this action.
refreshSocialToken - method of the SearchForm model :
public function refreshSocialToken($user_id) {
    $vk = \Yii::$app->authClientCollection->getClient('vkontakte');
    $user = Auth::findOne(['user_id' => $user_id]);
    $vk->setAccessToken(unserialize($user->token));
    return true;
  }

in which we extract the values ​​of the token field for the current user, where the serialized object was written before, perform the unserialize reverse procedure and call the setAccessToken method to essentially set the same token value again.
This is all done because the session based on files is blocked during an asynchronous request. And the values ​​must be set new in the current action ( actionGetAllMembers ). Which is achieved using the setAccessToken method .

D
Dmitry Voronkov, 2016-03-15
@DmitryVoronkov

I already wrote to you about background tasks. in another question (gearman). Why did you decide that your method is asynchronous?
There is another solution, if you are too lazy to deal with task queues, make a cron that will access the database once every 10 seconds, for example. Add the user's request to the database, let the cron pull it and get the data. The user at you in any case will wait.
There is another option, but again with queues. You add requests to the queue and return the result to the comet server on some channel, the user listens to this channel and as the tasks are solved, the data on the page will be updated.
I'll try it visually:

Пользователь -запрос-> Сервер
Сервер -Часть ответа сразу-> Пользователь
Сервер -Сложная часть->Можно в очередь, можно в консольное приложение
Обработка -Какая-то часть-> Комет сервер (канал)
Пользователь (слушает канал) <-Обновляет данные - Комет сервер

Didn't find what you were looking for?

Ask your question

Ask a Question

731 491 924 answers to any question