Example #1
0
 /**
  * Performs parallel asynchronous GET requests.
  * 
  * @param array $hrefs the hrefs to request
  * @param array $header the header to be passed in all requests
  * @param callable $getAuthHeader that returns an appropriate bearer authentication header line, for instance 
  * Client::getBearerAuthHeader(). We do this on-the-fly as during large multi GET batches the access token might
  * expire.
  * @param callable $callback a function expecting one \iveeCrest\Response object as argument, called for every
  * successful response
  * @param callable $errCallback a function expecting one \iveeCrest\Response object as argument, called for every
  * non-successful response
  * @param bool $cache whether the Responses should be cached
  * 
  * @return void
  * @throws \iveeCrest\Exceptions\IveeCrestException on general CURL error
  */
 public function asyncMultiGet(array $hrefs, array $header, callable $getAuthHeader, callable $callback, callable $errCallback = null, $cache = true)
 {
     //echo time2s()."curl.asyncMultiGet()\n"; //var_dump($hrefs);
     //separate hrefs that are already cached from those that need to be requested
     $hrefsToQuery = array();
     foreach ($hrefs as $href) {
         $responseKey = 'get:' . $href;
         try {
             $callback($this->cache->getItem($responseKey));
         } catch (Exceptions\KeyNotFoundInCacheException $e) {
             $hrefsToQuery[] = $href;
         }
         if (!in_array($href, $hrefsToQuery)) {
             // $hrefsToQuery: not in cache
             $url_short = str_replace(Config::getCrestBaseUrl(), '', $href);
             echo time2s() . "cache " . $url_short . "\n";
         }
     }
     // make sure the rolling window isn't greater than the number of hrefs
     $rollingWindow = count($hrefsToQuery) > 10 ? 10 : count($hrefsToQuery);
     //CURL options for all requests
     $stdOptions = array(CURLOPT_RETURNTRANSFER => true, CURLOPT_USERAGENT => $this->userAgent, CURLOPT_SSL_VERIFYPEER => true, CURLOPT_SSL_CIPHER_LIST => 'TLSv1', CURLOPT_CAINFO => __DIR__ . '/cacert.pem', CURLOPT_HTTPHEADER => $header);
     $responses = array();
     $master = curl_multi_init();
     //setup the first batch of requests
     for ($i = 0; $i < $rollingWindow; $i++) {
         $href = $hrefsToQuery[$i];
         //echo time2s()."curl.multi  $href\n";
         $responses[$href] = $this->addHandleToMulti($master, $href, $stdOptions, $getAuthHeader, $header);
     }
     $running = false;
     do {
         //execute whichever handles need to be started
         do {
             $execrun = curl_multi_exec($master, $running);
         } while ($execrun == CURLM_CALL_MULTI_PERFORM);
         if ($execrun != CURLM_OK) {
             $crestExceptionClass = Config::getIveeClassName('IveeCrestException');
             throw new $crestExceptionClass("CURL Multi-GET error", $execrun);
         }
         //block until we have anything on at least one of the handles
         curl_multi_select($master);
         //a request returned, process it
         while ($done = curl_multi_info_read($master)) {
             //echo "curl_multi_info_read()...\n"; var_dump($done);
             $info = curl_getinfo($done['handle']);
             //find the Response object matching the URL
             $res = $responses[$info['url']];
             $url_short = str_replace(Config::getCrestBaseUrl(), '', $info['url']);
             //set info and content to Response object
             $res->setInfo($info);
             $res->setContent(curl_multi_getcontent($done['handle']));
             //execute the callbacks passing the response as argument
             if ($info['http_code'] == 200) {
                 //cache it if configured
                 if ($cache) {
                     $this->cache->setItem($res);
                 }
                 $callback($res);
                 if (isset($this->requeued[$info['url']])) {
                     $this->requeued[$info['url']] = NULL;
                     time2s() . ">>> recaptured " . $url_short . "\n";
                 }
                 echo time2s() . "got   " . $url_short . "\n";
             } elseif (isset($errCallback)) {
                 echo time2s() . "cw.asyncMultiGet(): curl_multi, http " . $info['http_code'] . "\n";
                 echo time2s() . "requeueing " . $url_short . "\n";
                 $errCallback($res);
                 $hrefsToQuery[] = $info['url'];
                 //put back on queue
                 $this->requeued[$info['url']] = true;
             }
             //remove the reference to response to conserve memory on large batches
             $responses[$info['url']] = null;
             //start a new request (it's important to do this before removing the old one)
             if ($i < count($hrefsToQuery)) {
                 $href = $hrefsToQuery[$i++];
                 //echo time2s()."curl.multi  $href\n";
                 $responses[$href] = $this->addHandleToMulti($master, $href, $stdOptions, $getAuthHeader, $header);
             }
             //remove the curl handle that just completed
             curl_multi_remove_handle($master, $done['handle']);
         }
         //don't waste too many CPU cycles on looping
         usleep(1000);
     } while ($running > 0);
     curl_multi_close($master);
 }
Example #2
0
 /**
  * Gathers multipage endpoint responses and joins them into one array, using the passed callback functions to 
  * traverse and index the data. Since this operation is potentially expensive, it is recommended to use
  * gatherCached() instead, which introduces another layer of caching.
  *
  * @param string $endpointHref the URL to the first page of the endpoint 
  * @param callable $indexFunc function to be used to extract the ID from/for and individual response item
  * @param callable $elementFunc function to be used to extract the desired data from and individual response item
  * @param string $accept the representation to request from CREST
  *
  * @return array
  */
 public function gather($endpointHref, callable $indexFunc = null, callable $elementFunc = null, $accept = null)
 {
     //if (strpos($endpointHref, 'https://crest-tq.eveonline.com/market/types') !== false)
     if (strpos($endpointHref, 'https://public-crest.eveonline.com/market/types') !== false) {
         //echo time2s()."cl.gather(mktTypes)\n";
         //echo " indexFunc="; echo ($indexFunc == null) ? '(null)' : 'defined';
         //echo " elementFunc="; echo ($elementFunc == null) ? '(null)' : 'defined';
         //echo " accept="; echo ($elementFunc == null) ? '(null)' : 'defined';
         //echo "\n";
     } else {
         //echo time2s()."cl.gather()\n";
     }
     $ret = array();
     $href = $endpointHref;
     while (true) {
         //get the response for the current href
         $response = $this->getEndpointResponse($href, true, $accept);
         foreach ($response->content->items as $item) {
             //if an element function has been given, call it, otherwise use the full item in the result array
             if (is_null($elementFunc)) {
                 $element = $item;
             } else {
                 $element = $elementFunc($item);
             }
             //if an index function has been given, call it to get a result key, otherwise just push the element
             //onto result array
             if (is_null($indexFunc)) {
                 $ret[] = $element;
             } else {
                 $ret[$indexFunc($item)] = $element;
             }
         }
         //if there are more pages, do another iteration with updated href
         if ($response->hasNextPage()) {
             $href = $response->getNextPageHref();
         }
         // HACK: for /market/types/?page=[2-N], do async multiget
         // multiget faster (5-18s) vs. 11 sequential gets (21-29s)
         //$prefix = 'https://crest-tq.eveonline.com/market/types/?page=';
         //$prefix = 'https://public-crest.eveonline.com/market/types/?page='; // no-auth CREST
         $base = Config::getCrestBaseUrl();
         $prefix = $base . 'market/types/?page=';
         if (strpos($href, $prefix . '2') !== false) {
             $hrefs = array();
             $pageCount = $response->content->pageCount;
             for ($i = 2; $i <= $pageCount; $i++) {
                 $hrefs[] = $prefix . $i;
             }
             $this->asyncGetMultiEndpointResponses($hrefs, function (Response $res2) use(&$ret, &$elementFunc, &$indexFunc) {
                 foreach ($res2->content->items as $item2) {
                     $element2 = is_null($elementFunc) ? $item2 : $elementFunc($item2);
                     if (is_null($indexFunc)) {
                         $ret[] = $element2;
                     } else {
                         $ret[$indexFunc($item2)] = $element2;
                     }
                 }
             }, function (Response $res) {
                 //echo time2s().">>> cl.gather().callbackErrMarketType() ".str_replace(Config::getCrestBaseUrl(), '', $res->info['url'])."\n";
                 var_dump($res);
             }, $accept);
             break;
         } else {
             break;
         }
     }
     return $ret;
 }