private function retrieveResponse($connectionData) { $curlHandle = $this->curl->init(); if ($curlHandle === false) { $this->log->error("curl_init() did not return a handle for ID {$id}. " . 'Setting an empty response...'); return FF::getInstance('Core\\Server\\NullResponse'); } // We cannot use array_merge() here, because that does not preserve // numeric keys. So we use array union instead. However, as opposed to // array_merge(), the left-hand operator's keys will be preserved. $curlOptions = $this->necessaryCurlOptions + $connectionData->getConnectionOptions() + $this->defaultCurlOptions; $this->curl->setopt_array($curlHandle, $curlOptions); $responseText = $this->curl->exec($curlHandle); $httpCode = (int) $this->curl->getinfo($curlHandle, CURLINFO_HTTP_CODE); $curlErrorNumber = $this->curl->errno($curlHandle); $curlError = $this->curl->error($curlHandle); $this->curl->close($curlHandle); return FF::getInstance('Core\\Server\\Response', $responseText, $httpCode, $curlErrorNumber, $curlError); }
private function retrieveResponses($connectionsToFetch) { $curl = $this->curl; $multiHandle = $curl->multi_init(); if ($multiHandle === false) { $this->log->error("curl_multi_init() did not return a multi handle. " . 'Setting a empty responses...'); foreach ($connectionsToFetch as $id => $data) { $data['connection']->setResponse(FF::getInstance('Core\\Server\\NullResponse'), $data['url']); } } // Use a reference so that we can add a 'handle' field in the loop. foreach ($connectionsToFetch as $id => &$data) { $data['handle'] = $curl->init(); if ($data['handle'] === false) { $this->log->error("curl_init() did not return a handle for ID {$id}. " . 'Setting an empty response...'); $data['connection']->setResponse(FF::getInstance('Core\\Server\\NullResponse'), $data['url']); } // We cannot use array_merge() here, because that does not preserve // numeric keys. So we use array union instead. However, as opposed // to array_merge() the left-hand operator's keys will be preserved. $curlOptions = $this->necessaryCurlOptions + $data['connection']->getConnectionOptions() + $this->defaultCurlOptions; $curl->setopt_array($data['handle'], $curlOptions); $curl->multi_add_handle($multiHandle, $data['handle']); } unset($data); // Otherwise the reference remains and reusing the // variable name $data further down this function will // change the last element of the array. do { $status = $curl->multi_exec($multiHandle, $still_running); } while ($status == CURLM_CALL_MULTI_PERFORM); while ($still_running && $status == CURLM_OK) { // curl_multi_select sometimes returns -1 indefinitely in which case // the usual curl multi loop would run endlessly. This is due to the // underlying cURL library in C, which returns -1 upon errors which // cannot be checked from the outside. Simply waiting for 100 ms is // the suggested workaround. For further information see: // https://bugs.php.net/bug.php?id=61141 // https://bugs.php.net/bug.php?id=63411 // https://bugs.php.net/bug.php?id=63842 // http://curl.haxx.se/libcurl/c/curl_multi_fdset.html if ($curl->multi_select($multiHandle) == -1) { usleep(100); } do { $status = $curl->multi_exec($multiHandle, $still_running); } while ($status == CURLM_CALL_MULTI_PERFORM); // TODO: Fetch responses within this loop as they are ready (while // the others are still loading). } if ($status != CURLM_OK) { $this->log->error('There was a cURL error: ' . $status); } while (($msg = $curl->multi_info_read($multiHandle)) !== false) { // We do not check the value of $msg['msg'], because currently this // will always be CURLMSG_DONE. $curlErrorNumber = $msg['result']; // Set $data to the data array corresponding to the current handle. foreach ($connectionsToFetch as $data) { if ($data['handle'] === $msg['handle']) { break; } } // We could skip multi_getcontent if $curlErrorNumber is different // from CURLE_OK, as it will always return null. $responseText = $curl->multi_getcontent($data['handle']); $httpCode = (int) $curl->getinfo($data['handle'], CURLINFO_HTTP_CODE); $curlError = $curl->error($data['handle']); $curl->multi_remove_handle($multiHandle, $data['handle']); $this->curl->close($data['handle']); $response = FF::getInstance('Core\\Server\\Response', $responseText, $httpCode, $curlErrorNumber, $curlError); $data['connection']->setResponse($response, $data['url']); $this->logResult($response); } }