Пример #1
0
 /**
  * Performs a request, which may involve more than one call (e.g. if there
  * is a redirect involved or the request fails on the first attempt and is
  * subsequently retried). This method returns false if the HTTP status code
  * of the final call was anything other than 200, unless it was handled by
  * mapping an HTTP status to an action.
  *
  * @param GenericAPI\Request $request
  * @param boolean $parse = true
  * @return boolean
  */
 protected function _getResponse(Request $request, $parse = true)
 {
     if (!$this->_instanceSetupComplete) {
         $this->_setupInstance();
     }
     if ($this->_requestDelayInterval) {
         $this->_mutex->acquire();
         $timeSinceLast = \PFXUtils::millitime() - $this->_shmSegment->getVar($this->_lastRequestTimeVarName, 0);
         if ($timeSinceLast < $this->_requestDelayInterval) {
             usleep(($this->_requestDelayInterval - $timeSinceLast) * 1000);
         }
         $this->_mutex->release();
     }
     $this->_resetState();
     $this->_request = $request;
     for ($i = 0; $i < $this->_responseTries; $i++) {
         $this->_attemptCount++;
         if (API_VERBOSE_REQUESTS) {
             echo 'Executing attempt ' . $this->_attemptCount . ' of ' . $this->_responseTries . '...' . PHP_EOL;
         }
         if (!$this->_requestBound) {
             $this->_bindRequestToCurlHandle();
         }
         $response = $this->_executeCurlHandle();
         if (API_VERBOSE_REQUESTS) {
             echo 'Request trace:' . PHP_EOL . curl_getinfo($this->_curlHandle, CURLINFO_HEADER_OUT) . PHP_EOL . PHP_EOL;
         }
         /* I didn't used to set the raw response property until this loop
         			was finished, but we need it in order to make the call to
         			$this->_determineResponseAction() work correctly. In order to
         			preserve the legacy behavior, I'm going to leave the property null
         			if we get a zero-byte response. */
         if (strlen($response)) {
             $this->_responseRaw = $response;
         }
         if ($this->_requestDelayInterval) {
             // Store last request timestamp down to the millisecond
             $this->_shmSegment->putVar($this->_lastRequestTimeVarName, \PFXUtils::millitime());
         }
         $this->_responseCode = $this->_getLastHTTPResponse();
         if (API_VERBOSE_REQUESTS) {
             echo 'Response code was ' . $this->_responseCode . '; received ' . strlen($response) . ' bytes' . PHP_EOL;
         }
         $action = $this->_determineResponseAction();
         if ($action === null) {
             throw new $this->_EXCEPTION_TYPE('Failed to determine response action (response code was ' . $this->_responseCode . ').');
         }
         if ($action == self::ACTION_URL_MOVED) {
             /* This condition throws an exception so it's easier to know
             			that URLs in library code need to be updated. Note that this
             			only takes effect if the request is not set to redirect
             			automatically or if the number of redirects emitted by the
             			remote service exceeds 10. */
             $headers = $this->getResponseHeaderAsAssociativeArray();
             if (isset($headers['Location'])) {
                 $message = 'The remote service reports that this resource ' . 'has moved to ' . $headers['Location'] . ' (response code was ' . $this->_responseCode . ').';
             } else {
                 $message = 'Got response code ' . $this->_responseCode . ' from remote service.';
             }
             throw new $this->_EXCEPTION_TYPE($message);
         }
         if ($action != self::ACTION_REPEAT_REQUEST) {
             break;
         }
         sleep($this->_repeatPauseInterval);
     }
     /* In order for certain things to work properly (e.g. the storing of
     		raw SERP source code from the SEMRush API), we need to parse the
     		response before we store the raw response. However, if for some reason
     		the parse code throws an exception, we don't want to die without at
     		least attempting to store the raw data. Therefore, we'll catch any
     		exceptions here, then re-throw them after storing the raw response. */
     $rethrowException = null;
     if ($parse) {
         try {
             $this->_parseResponse();
         } catch (\Exception $e) {
             $rethrowException = $e;
         }
     }
     if (strlen($this->_responseRaw)) {
         if ($this->_transferFile) {
             $this->_storeRawResponse();
         }
     }
     $this->_finalizeRequest();
     if ($rethrowException) {
         throw $rethrowException;
     }
     $this->_handleError();
     $request->validateResponse($this->_responseRaw, $this->_responseParsed);
 }
 /**
  * Confirms that the enforcing of validation on the response works as
  * expected.
  */
 public function testResponseValidation()
 {
     $request = new Request('127.0.0.1');
     $response = array('foo' => 'a', 'bar' => 'b', 'baz' => 'c');
     /* Note that because a raw response with a non-zero length must be
        passed to GenericAPI\Request::validateResponse(), but it does not need
        to have anything to do with the parsed response, I am passing a
        placeholder argument in these calls. */
     $request->setResponseValidator(array('foo', 'bar'));
     // The response can have more keys than the validator checks for
     $request->validateResponse('a', $response);
     $request->setResponseValidator(array('foo', 'bar', 'baz', 'asdf'));
     $this->assertThrows(__NAMESPACE__ . '\\ResponseValidationException', array($request, 'validateResponse'), array('a', $response));
     $response['asdf'] = 'd';
     $request->validateResponse('a', $response);
     $proto = array('response' => array('foo' => null, 'bar' => array('knock-knock' => null), 'baz' => null));
     $request->setResponseValidator($proto);
     $request->setResponseValidationMethod(Request::VALIDATE_PROTOTYPE);
     $response = array('response' => array('foo' => array('', 1, 'b'), 'bar' => array('knock-knock' => "who's there"), 'baz' => '927381.29'));
     $request->validateResponse('a', $response);
     /* This doesn't just validate the presence of the array keys, so this
        should cause a failure. */
     $response['response']['bar'] = 'asdf';
     $this->assertThrows(__NAMESPACE__ . '\\ResponseValidationException', array($request, 'validateResponse'), array('a', $response));
     $response['response']['bar'] = array('knock-knock' => "who's there");
     // Adding a requirement to the prototype also causes a failure
     $proto['baz'] = null;
     $request->setResponseValidator($proto);
     $this->assertThrows(__NAMESPACE__ . '\\ResponseValidationException', array($request, 'validateResponse'), array('a', $response));
     // But we can selectively disble validation
     $request->setResponseValidationMethod(null);
     $request->validateResponse('a', $response);
 }