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);
     }
 }