| Code Coverage | ||||||||||
| Classes and Traits | Functions and Methods | Lines | ||||||||
| Total |  | 0.00% | 0 / 1 |  | 55.56% | 25 / 45 | CRAP |  | 78.46% | 204 / 260 | 
| Request |  | 0.00% | 0 / 1 |  | 55.56% | 25 / 45 | 213.60 |  | 78.76% | 204 / 259 | 
| __construct ( $url = null, $method = self::METHOD_GET, $data = null, $headers = null, $opts = null) |  | 100.00% | 1 / 1 | 1 |  | 100.00% | 5 / 5 | |||
| setUrl ($url) |  | 100.00% | 1 / 1 | 1 |  | 100.00% | 2 / 2 | |||
| getUrl () |  | 100.00% | 1 / 1 | 1 |  | 100.00% | 1 / 1 | |||
| setMethod ($method) |  | 100.00% | 1 / 1 | 1 |  | 100.00% | 2 / 2 | |||
| getMethod () |  | 100.00% | 1 / 1 | 1 |  | 100.00% | 1 / 1 | |||
| setHeaders ($headers) |  | 100.00% | 1 / 1 | 1 |  | 100.00% | 2 / 2 | |||
| addHeader ($header) |  | 100.00% | 1 / 1 | 1 |  | 100.00% | 2 / 2 | |||
| getHeaders () |  | 100.00% | 1 / 1 | 1 |  | 100.00% | 1 / 1 | |||
| setPostBody ($postBody) |  | 100.00% | 1 / 1 | 1 |  | 100.00% | 2 / 2 | |||
| getPostBody () |  | 100.00% | 1 / 1 | 1 |  | 100.00% | 1 / 1 | |||
| addQueryData ($parms) |  | 0.00% | 0 / 1 | 2 |  | 0.00% | 0 / 2 | |||
| setCurlOptions ($opts) |  | 100.00% | 1 / 1 | 1 |  | 100.00% | 2 / 2 | |||
| addCurlOption ($key, $value) |  | 100.00% | 1 / 1 | 1 |  | 100.00% | 2 / 2 | |||
| getCurlOptions () |  | 100.00% | 1 / 1 | 1 |  | 100.00% | 1 / 1 | |||
| setUserAgent ($userAgent) |  | 0.00% | 0 / 1 | 2 |  | 0.00% | 0 / 2 | |||
| getUserAgent () |  | 100.00% | 1 / 1 | 1 |  | 100.00% | 1 / 1 | |||
| setReferrer ($ref) |  | 0.00% | 0 / 1 | 2 |  | 0.00% | 0 / 2 | |||
| setReferer ($ref) |  | 0.00% | 0 / 1 | 2 |  | 0.00% | 0 / 1 | |||
| setConnectTimeout ($ms) |  | 0.00% | 0 / 1 | 6 |  | 0.00% | 0 / 5 | |||
| setTimeout ($ms) |  | 0.00% | 0 / 1 | 2.03 |  | 80.00% | 4 / 5 | |||
| setCredentials ($user, $password, $type = CURLAUTH_ANYSAFE) |  | 0.00% | 0 / 1 | 2 |  | 0.00% | 0 / 3 | |||
| setX509Credentials ($cert, $key, $keypass, $type = 'PEM') |  | 0.00% | 0 / 1 | 2 |  | 0.00% | 0 / 5 | |||
| setCookieJar ($file) |  | 0.00% | 0 / 1 | 6 |  | 0.00% | 0 / 3 | |||
| failIfNot200 ($flag) |  | 0.00% | 0 / 1 | 2 |  | 0.00% | 0 / 2 | |||
| wasSubmitted () |  | 100.00% | 1 / 1 | 1 |  | 100.00% | 1 / 1 | |||
| throwIfNotSubmitted () |  | 0.00% | 0 / 1 | 2.50 |  | 50.00% | 1 / 2 | |||
| getResponseHttpCode () |  | 100.00% | 1 / 1 | 1 |  | 100.00% | 2 / 2 | |||
| setResponseHttpCode ($code) |  | 100.00% | 1 / 1 | 1 |  | 100.00% | 2 / 2 | |||
| getResponseHeaders () |  | 100.00% | 1 / 1 | 1 |  | 100.00% | 2 / 2 | |||
| getResponseHeader ($name) |  | 0.00% | 0 / 1 | 6 |  | 0.00% | 0 / 4 | |||
| setResponseHeaders ($headers) |  | 100.00% | 1 / 1 | 1 |  | 100.00% | 2 / 2 | |||
| getResponseBody () |  | 100.00% | 1 / 1 | 1 |  | 100.00% | 2 / 2 | |||
| setResponseBody ($body) |  | 100.00% | 1 / 1 | 1 |  | 100.00% | 2 / 2 | |||
| getResponseCurlInfo () |  | 0.00% | 0 / 1 | 2 |  | 0.00% | 0 / 2 | |||
| setResponseCurlInfo ($info) |  | 100.00% | 1 / 1 | 1 |  | 100.00% | 2 / 2 | |||
| submit ($failIfNot200 = null) |  | 0.00% | 0 / 1 | 2.01 |  | 85.71% | 6 / 7 | |||
| createCurlRequest () |  | 100.00% | 1 / 1 | 12 |  | 100.00% | 38 / 38 | |||
| processCurlResponse ($ch, $errCode, $errMsg, $rawResp) |  | 0.00% | 0 / 1 | 8.16 |  | 86.49% | 32 / 37 | |||
| validateResponse ($failIfNot200 = null) |  | 0.00% | 0 / 1 | 26.42 |  | 91.49% | 43 / 47 | |||
| parallelSubmit ($requests) |  | 0.00% | 0 / 1 | 12.05 |  | 92.86% | 26 / 28 | |||
| handleMultiResponse ($info, $mh, &$handles) |  | 100.00% | 1 / 1 | 2 |  | 100.00% | 10 / 10 | |||
| get ($url, $parms = null, $options = null) |  | 0.00% | 0 / 1 | 2 |  | 0.00% | 0 / 3 | |||
| post ($url, $parms, $options = null) |  | 0.00% | 0 / 1 | 2 |  | 0.00% | 0 / 4 | |||
| postContent ( $url, $content, $contentType = 'text/xml', $options = null) |  | 100.00% | 1 / 1 | 1 |  | 100.00% | 4 / 4 | |||
| postMultipart ($url, $parms, $options = null) |  | 0.00% | 0 / 1 | 2 |  | 0.00% | 0 / 3 | |||
| <?php | |
| /** | |
| * @package Moar\Net\Http | |
| */ | |
| namespace Moar\Net\Http; | |
| // ensure that curl error constants are available | |
| Util::ensureCurlErrorConstants(); | |
| /** | |
| * HTTP request handler that attempts to make using cURL easy. | |
| * | |
| * Static convenience methods are provided to performing typical requests such | |
| * as GET and POST. Several variations of POST are available for | |
| * "application/x-www-form-urlencoded", "multipart/form-data" and raw body | |
| * encodings. | |
| * | |
| * More complicated requests can be configured via direct use of the class. | |
| * Helper methods are provided for common operations such as authenticating | |
| * with a a username/password pair or an x509 certificate. | |
| * | |
| * Requests are sent by calling the submit() method directly or by | |
| * passing an array of prepared requests to the static parallelSubmit() method. | |
| * Either method of executing a request will result in the provided | |
| * Request being updated to contain response codes, headers and body data | |
| * returned by the server processing the URL. | |
| * | |
| * @package Moar\Net\Http | |
| */ | |
| class Request { | |
| /** | |
| * Default user-agent string. | |
| * @var string | |
| */ | |
| const DEFAULT_USERAGENT = 'Mozilla/5.0 (compatible; Moar-Net-Http-Request)'; | |
| /** | |
| * Default maximum redirects. | |
| * @var int | |
| */ | |
| const DEFAULT_MAXREDIRS = 10; | |
| /** | |
| * HTTP GET. | |
| * @var string | |
| */ | |
| const METHOD_GET = 'GET'; | |
| /** | |
| * HTTP POST. | |
| * @var string | |
| */ | |
| const METHOD_POST = 'POST'; | |
| /** | |
| * Default curlopts. | |
| * @var array | |
| */ | |
| protected static $defaultCurlOpts = array( | |
| CURLOPT_SSL_VERIFYPEER => false, | |
| CURLOPT_SSL_VERIFYHOST => false, | |
| CURLINFO_SSL_VERIFYRESULT => false, | |
| CURLOPT_FOLLOWLOCATION => true, | |
| CURLOPT_MAXREDIRS => self::DEFAULT_MAXREDIRS, | |
| CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1, | |
| CURLOPT_ENCODING => '', | |
| CURLOPT_CONNECTTIMEOUT => 3, // 3 seconds | |
| CURLOPT_TIMEOUT => 5, // 5 seconds | |
| ); | |
| /** | |
| * URL to request. | |
| * @var string | |
| */ | |
| protected $url; | |
| /** | |
| * HTTP request method. | |
| * @var string | |
| */ | |
| protected $method; | |
| /** | |
| * HTTP request headers. | |
| * @var array | |
| */ | |
| protected $headers = array(); | |
| /** | |
| * POST body. | |
| * @var string | |
| */ | |
| protected $postBody; | |
| /** | |
| * User-Agent header value. | |
| * @var string | |
| */ | |
| protected $userAgent = self::DEFAULT_USERAGENT; | |
| /** | |
| * Curl options. | |
| * @var array | |
| */ | |
| protected $curlOptions = array(); | |
| /** | |
| * Default behavior for non-200 status responses. | |
| * @var bool | |
| */ | |
| protected $defaultFailIfNot200 = true; | |
| /** | |
| * Response code. | |
| * @var int | |
| */ | |
| protected $responseHttpCode; | |
| /** | |
| * Response headers. | |
| * @var array | |
| */ | |
| protected $responseHeaders; | |
| /** | |
| * Response body. | |
| * @var string | |
| */ | |
| protected $responseBody; | |
| /** | |
| * Curl response information. | |
| * @var array | |
| */ | |
| protected $responseCurlInfo; | |
| /** | |
| * Curl error status. | |
| * @var int | |
| */ | |
| protected $responseCurlErr; | |
| /** | |
| * Curl error message. | |
| * @var string | |
| */ | |
| protected $responseCurlErrMessage; | |
| /** | |
| * Constructor. | |
| * | |
| * @param string $url URL to request | |
| * @param string $method HTTP request verb | |
| * @param mixed $data Data to send with request. Either a URL-encoded | |
| * string or an array of key=>value pairs. | |
| * @param array $headers Custom headers to set on request | |
| * @param array $opts Curl configuration options | |
| */ | |
| public function __construct ( | |
| $url = null, $method = self::METHOD_GET, $data = null, $headers = null, | |
| $opts = null) { | |
| $this->setUrl($url); | |
| $this->setMethod($method); | |
| $this->setHeaders($headers); | |
| $this->setPostBody($data); | |
| $this->setCurlOptions($opts); | |
| } //end __construct | |
| /** | |
| * Set the url for this request. | |
| * @param string $url URL to request | |
| * @return Request Self, for message chaining | |
| */ | |
| public function setUrl ($url) { | |
| $this->url = $url; | |
| return $this; | |
| } | |
| /** | |
| * Get the url for this request. | |
| * @return string URL of request | |
| */ | |
| public function getUrl () { | |
| return $this->url; | |
| } | |
| /** | |
| * Set the method for this request. | |
| * @param string $method HTTP request verb | |
| * @return Request Self, for message chaining | |
| */ | |
| public function setMethod ($method) { | |
| $this->method = $method; | |
| return $this; | |
| } | |
| /** | |
| * Get the method for this request. | |
| * @return string HTTP request verb | |
| */ | |
| public function getMethod () { | |
| return $this->method; | |
| } | |
| /** | |
| * Set the headers for this request. | |
| * @param array $headers Custom headers to set on request | |
| * @return Request Self, for message chaining | |
| */ | |
| public function setHeaders ($headers) { | |
| $this->headers = (array) $headers; | |
| return $this; | |
| } | |
| /** | |
| * Add a header for this request. | |
| * @param string $header Header to send with request | |
| * @return Request Self, for message chaining | |
| */ | |
| public function addHeader ($header) { | |
| $this->headers[] = $header; | |
| return $this; | |
| } | |
| /** | |
| * Get the custom headers for this request. | |
| * @return array Collection of custom headers | |
| */ | |
| public function getHeaders () { | |
| return $this->headers; | |
| } | |
| /** | |
| * Set the postBody for this request. | |
| * @param mixed $postBody Either literal payload for request or array of | |
| * key=>value pairs to encode as "multipart/form-data" on submission. | |
| * @return Request Self, for message chaining | |
| */ | |
| public function setPostBody ($postBody) { | |
| $this->postBody = $postBody; | |
| return $this; | |
| } | |
| /** | |
| * Get the postBody for this request. | |
| * @return string HTTP request verb | |
| */ | |
| public function getPostBody () { | |
| return $this->postBody; | |
| } | |
| /** | |
| * Append a query string to the current URL. | |
| * | |
| * @param string|array $parms Parameters to add as query string to url | |
| * @return Request Self, for message chaining | |
| */ | |
| public function addQueryData ($parms) { | |
| $this->setUrl(Util::addQueryData($this->getUrl(), $parms)); | |
| return $this; | |
| } | |
| /** | |
| * Set cURL options for this request. | |
| * @param array $opts Curl options | |
| * @return Request Self, for message chaining | |
| */ | |
| public function setCurlOptions ($opts) { | |
| $this->curlOptions = (array) $opts; | |
| return $this; | |
| } | |
| /** | |
| * Add a cURL option for this request. | |
| * @param mixed $key Curl option identifier | |
| * @param mixed $value Curl option value | |
| * @return Request Self, for message chaining | |
| */ | |
| public function addCurlOption ($key, $value) { | |
| $this->curlOptions[$key] = $value; | |
| return $this; | |
| } | |
| /** | |
| * Get the cURL options for this request. | |
| * @return array Curl configuration options | |
| */ | |
| public function getCurlOptions () { | |
| return $this->curlOptions; | |
| } | |
| /** | |
| * Set the value of the User-Agent header for this request. | |
| * @param string $userAgent The User-Agent | |
| * @return Request Self, for message chaining | |
| */ | |
| public function setUserAgent ($userAgent) { | |
| $this->userAgent = $userAgent; | |
| return $this; | |
| } | |
| /** | |
| * Get the value of the User-Agent header for this request. | |
| * @return string The User-Agent | |
| */ | |
| public function getUserAgent () { | |
| return $this->userAgent; | |
| } | |
| /** | |
| * Set the referring URL that this request is realted to. | |
| * @param string $ref URL to report to server in "Referer" header | |
| * @return Request Self, for message chaining | |
| */ | |
| public function setReferrer ($ref) { | |
| $this->addCurlOption(CURLOPT_REFERER, $ref); | |
| return $this; | |
| } | |
| /** | |
| * Synonym for setReferrer(). | |
| * @param string $ref URL to report to server in "Referer" header | |
| * @return Request Self, for message chaining | |
| * @see setReferrer() | |
| */ | |
| public function setReferer ($ref) { | |
| return $this->setReferrer($ref); | |
| } | |
| /** | |
| * Set HTTP connect timeout (in milliseconds). | |
| * @param int $ms Connect timeout in millseconds | |
| * @return Request Self, for message chaining | |
| */ | |
| public function setConnectTimeout ($ms) { | |
| if (defined('CURLOPT_CONNECTTIMEOUT_MS')) { | |
| $this->addCurlOption(CURLOPT_CONNECTTIMEOUT_MS, $ms); | |
| } else { | |
| // older versions of php/libcurl don't have the sub-second timeout | |
| // functionality. Convert to nearest whole seconds not less than 1. | |
| $this->addCurlOption(CURLOPT_CONNECTTIMEOUT, max(1, round($ms / 1000))); | |
| } | |
| return $this; | |
| } //end setConnectTimeout | |
| /** | |
| * Set HTTP timeout (in milliseconds). | |
| * @param int $ms Timeout in millseconds | |
| * @return Request Self, for message chaining | |
| */ | |
| public function setTimeout ($ms) { | |
| if (defined('CURLOPT_TIMEOUT_MS')) { | |
| $this->addCurlOption(CURLOPT_TIMEOUT_MS, $ms); | |
| } else { | |
| // older versions of php/libcurl don't have the sub-second timeout | |
| // functionality. Convert to nearest whole seconds not less than 1. | |
| $this->addCurlOption(CURLOPT_TIMEOUT, max(1, round($ms / 1000))); | |
| } | |
| return $this; | |
| } //end setTimeout | |
| /** | |
| * Set credentials for authenticating to remote host. | |
| * | |
| * @param string $user Username | |
| * @param string $password Password | |
| * @param int $type Auth type to attempt. See CURLOPT_HTTPAUTH section of | |
| * curl_setopt page at php.net for options. | |
| * @return Request Self, for message chaining | |
| */ | |
| public function setCredentials ($user, $password, $type = CURLAUTH_ANYSAFE) { | |
| $this->addCurlOption(CURLOPT_HTTPAUTH, $type); | |
| $this->addCurlOption(CURLOPT_USERPWD, "{$user}:{$password}"); | |
| return $this; | |
| } //end setCredentials | |
| /** | |
| * Set x509 credentials for authenticating to remote host. | |
| * | |
| * @param string $cert Path to x509 certificate | |
| * @param string $key Patch to x509 private key | |
| * @param string $keypass Passphrase to decrypt private key | |
| * @param string $type Certificate encoding (PEM|DER|ENG) | |
| * @return Request Self, for message chaining | |
| */ | |
| public function setX509Credentials ($cert, $key, $keypass, $type = 'PEM') { | |
| $this->addCurlOption(CURLOPT_SSLCERTTYPE, $type); | |
| $this->addCurlOption(CURLOPT_SSLCERT, $cert); | |
| $this->addCurlOption(CURLOPT_SSLKEY, $key); | |
| $this->addCurlOption(CURLOPT_SSLKEYPASSWD, $keypass); | |
| return $this; | |
| } | |
| /** | |
| * Read and store cookies in the provided file. | |
| * | |
| * @param string $file Path to cookie storage file | |
| * @return Request Self, for message chaining | |
| */ | |
| public function setCookieJar ($file) { | |
| if (null !== $file) { | |
| $this->addCurlOption(CURLOPT_COOKIEFILE, $file); //read from file | |
| $this->addCurlOption(CURLOPT_COOKIEJAR, $file); //write to file | |
| } | |
| return $this; | |
| } | |
| /** | |
| * Set the default behavior when a request returns a non-200 status code. | |
| * | |
| * @param bool $flag True to throw an exception, false otherwise | |
| * @return Request Self, for message chaining | |
| * @see getResponseHttpCode() | |
| */ | |
| public function failIfNot200 ($flag) { | |
| $this->defaultFailIfNot200 = (bool) $flag; | |
| return $this; | |
| } | |
| /** | |
| * Check to see if this request has been submitted yet. | |
| * | |
| * @return bool True if request has been submitted, false otherwise. | |
| */ | |
| public function wasSubmitted () { | |
| return null !== $this->responseCurlErr; | |
| } | |
| /** | |
| * Throw a \RuntimeException if this request has not been submitted | |
| * yet. | |
| * | |
| * @return void | |
| * @throws \RuntimeException If request has not been submitted. | |
| */ | |
| protected function throwIfNotSubmitted () { | |
| if (!$this->wasSubmitted()) { | |
| throw new \RuntimeException('Request not submitted.'); | |
| } | |
| } //end throwIfNotSubmitted | |
| /** | |
| * Get the HTTP response code sent by the server. | |
| * @return int HTTP response code | |
| * @throws \RuntimeException If request has not been submitted. | |
| */ | |
| public function getResponseHttpCode () { | |
| $this->throwIfNotSubmitted(); | |
| return $this->responseHttpCode; | |
| } | |
| /** | |
| * Set the HTTP response code sent by the server. | |
| * @param int $code HTTP response code | |
| * @return Request Self, for message chaining | |
| */ | |
| protected function setResponseHttpCode ($code) { | |
| $this->responseHttpCode = $code; | |
| return $this; | |
| } | |
| /** | |
| * Get the HTTP response headers sent by the server. | |
| * | |
| * Keys in the header array are the header names. The value is either the | |
| * raw header contents or an array of raw header contents if that particular | |
| * header appeared more than once in the response. | |
| * | |
| * @return array HTTP response headers | |
| * @throws \RuntimeException If request has not been submitted. | |
| */ | |
| public function getResponseHeaders () { | |
| $this->throwIfNotSubmitted(); | |
| return $this->responseHeaders; | |
| } | |
| /** | |
| * Get a particular HTTP response header sent by the server. | |
| * | |
| * @param string $name Header name | |
| * @return mixed Header value or null if not found. Value may be an array if | |
| * the named header occured more than once in the server's response. | |
| * @throws \RuntimeException If request has not been submitted. | |
| */ | |
| public function getResponseHeader ($name) { | |
| $this->throwIfNotSubmitted(); | |
| if (isset($this->responseHeaders[$name])) { | |
| return $this->responseHeaders[$name]; | |
| } else { | |
| return null; | |
| } | |
| } //end getResponseHeader | |
| /** | |
| * Set the HTTP response headers sent by the server. | |
| * @param array $headers HTTP response headers | |
| * @return Request Self, for message chaining | |
| */ | |
| protected function setResponseHeaders ($headers) { | |
| $this->responseHeaders = $headers; | |
| return $this; | |
| } | |
| /** | |
| * Get the HTTP response body sent by the server. | |
| * @return string HTTP response body | |
| * @throws \RuntimeException If request has not been submitted. | |
| */ | |
| public function getResponseBody () { | |
| $this->throwIfNotSubmitted(); | |
| return $this->responseBody; | |
| } | |
| /** | |
| * Set the HTTP response body sent by the server. | |
| * @param array $body HTTP response body | |
| * @return Request Self, for message chaining | |
| */ | |
| protected function setResponseBody ($body) { | |
| $this->responseBody = $body; | |
| return $this; | |
| } | |
| /** | |
| * Get the cURL response information. | |
| * @return string HTTP response body | |
| * @throws \RuntimeException If request has not been submitted. | |
| */ | |
| public function getResponseCurlInfo () { | |
| $this->throwIfNotSubmitted(); | |
| return $this->responseCurlInfo; | |
| } | |
| /** | |
| * Set the cURL response information. | |
| * @param array $info Curl info | |
| * @return Request Self, for message chaining | |
| */ | |
| protected function setResponseCurlInfo ($info) { | |
| $this->responseCurlInfo = $info; | |
| return $this; | |
| } | |
| /** | |
| * Submit this request. | |
| * | |
| * This request will be updated with the results of the request which can | |
| * then be retrieved using getResponseHttpCode() and releated methods. | |
| * | |
| * @param bool $failIfNot200 Throw an exception if a non-200 status was sent? | |
| * @return Request Request with response data populated | |
| * @throws Exception On cURL failure | |
| */ | |
| public function submit ($failIfNot200 = null) { | |
| if ($this->wasSubmitted()) { | |
| throw new Exception("Request be reused!"); | |
| } | |
| $ch = $this->createCurlRequest(); | |
| $raw = curl_exec($ch); | |
| $this->processCurlResponse($ch, curl_errno($ch), curl_error($ch), $raw); | |
| $this->validateResponse($failIfNot200); | |
| return $this; | |
| } | |
| /** | |
| * Prepare a cURL handle for this request. | |
| * @return resource Curl handle ready to be submitted | |
| */ | |
| protected function createCurlRequest () { | |
| // merge any custom cURL options into the default set | |
| $curlOpts = Util::mergeCurlOptions( | |
| self::$defaultCurlOpts, $this->getCurlOptions()); | |
| // set basic options that we always want to use | |
| $curlOpts[CURLOPT_RETURNTRANSFER] = true; | |
| $curlOpts[CURLOPT_HEADER] = true; | |
| $curlOpts[CURLOPT_FAILONERROR] = false; | |
| if (defined('CURLINFO_HEADER_OUT')) { | |
| $curlOpts[CURLINFO_HEADER_OUT] = true; | |
| } | |
| // timeouts less than 1 sec fail unless we disable signals | |
| // see http://www.php.net/manual/en/function.curl-setopt.php#104597 | |
| if (defined('CURLOPT_TIMEOUT_MS') || | |
| defined('CURLOPT_CONNECTTIMEOUT_MS')) { | |
| if ((isset($curlOpts[CURLOPT_TIMEOUT_MS]) && | |
| $curlOpts[CURLOPT_TIMEOUT_MS] < 1000) || | |
| (isset($curlOpts[CURLOPT_CONNECTTIMEOUT_MS]) && | |
| $curlOpts[CURLOPT_CONNECTTIMEOUT_MS] < 1000)) { | |
| $curlOpts[CURLOPT_NOSIGNAL] = true; | |
| } | |
| } | |
| // prepare the request | |
| $curlOpts[CURLOPT_URL] = $this->url; | |
| if (self::METHOD_POST == $this->getMethod()) { | |
| // using CURLOPT_CUSTOMREQUEST changes the behavior for POST | |
| // this change forces strict RFC2616:10.3.3 compliance which doesn't | |
| // work too well with many internet sites. | |
| $curlOpts[CURLOPT_POST] = true; | |
| } else { | |
| $curlOpts[CURLOPT_CUSTOMREQUEST] = $this->getMethod(); | |
| } | |
| $curlOpts[CURLOPT_USERAGENT] = $this->getUserAgent(); | |
| if ($this->getPostBody()) { | |
| // add post payload | |
| $pBody = $this->getPostBody(); | |
| if (!is_array($pBody)) { | |
| // caller supplied a URI-encoded payload, so make sure we set the | |
| // content-length header | |
| $len = mb_strlen($pBody, 'latin1'); | |
| $this->addHeader("Content-Length: {$len}"); | |
| } | |
| $curlOpts[CURLOPT_POSTFIELDS] = $pBody; | |
| } | |
| if ($this->getHeaders()) { | |
| // add custom headers | |
| $curlOpts[CURLOPT_HTTPHEADER] = $this->getHeaders(); | |
| } | |
| // remember the options we used. Might be handy for debugging. | |
| $this->setCurlOptions($curlOpts); | |
| // create curl resource | |
| $ch = curl_init(); | |
| // apply options | |
| curl_setopt_array($ch, $curlOpts); | |
| return $ch; | |
| } //end createCurlRequest | |
| /** | |
| * Process an submitted cURL response. | |
| * @param resource $ch Curl handle | |
| * @param int $errCode Curl error code | |
| * @param string $errMsg Curl error message | |
| * @param string $rawResp Raw response | |
| * @return Request Request with response data populated | |
| */ | |
| protected function processCurlResponse ($ch, $errCode, $errMsg, $rawResp) { | |
| // check error codes | |
| $this->responseCurlErr = $errCode; | |
| $this->responseCurlErrMessage = $errMsg; | |
| if (CURLE_OK == $errCode) { | |
| $info = curl_getinfo($ch); | |
| $hdrSize = $info['header_size']; | |
| $respCode = (int) $info['http_code']; | |
| // parse the raw response | |
| $rawHeaders = mb_substr($rawResp, 0, $hdrSize, 'latin1'); | |
| $respBody = mb_substr( | |
| $rawResp, $hdrSize, mb_strlen($rawResp, 'latin1'), 'latin1'); | |
| // parse response headers | |
| if ($info['redirect_count'] > 0) { | |
| // discard redirect headers | |
| // TODO: there may be useful info in here that we want to preserve | |
| $headerChunks = explode("\r\n\r\n", $rawHeaders); | |
| $rawHeaders = $headerChunks[$info['redirect_count']]; | |
| } | |
| $respHeaderParts = explode("\r\n", $rawHeaders); | |
| $respHeaders = array(); | |
| foreach ($respHeaderParts as $header) { | |
| if ($header) { | |
| $parts = explode(': ', $header, 2); | |
| $name = $parts[0]; | |
| $value = ''; | |
| if (count($parts) == 2) { | |
| $value = $parts[1]; | |
| } | |
| if (isset($respHeaders[$name])) { | |
| if (!is_array($respHeaders[$name])) { | |
| // convert single value to collection | |
| $respHeaders[$name] = array($respHeaders[$name]); | |
| } | |
| $respHeaders[$name][] = $value; | |
| } else { | |
| $respHeaders[$name] = $value; | |
| } | |
| } | |
| } //end foreach $header | |
| // fill in request with response | |
| $this->setResponseCurlInfo($info); | |
| $this->setResponseHttpCode($respCode); | |
| $this->setResponseHeaders($respHeaders); | |
| $this->setResponseBody($respBody); | |
| } //end if CURLE_OK | |
| curl_close($ch); | |
| return $this; | |
| } //end processCurlResponse | |
| /** | |
| * Check the cURL response code and throw an exception if it is an error. | |
| * @param bool $failIfNot200 Throw an exception if a non-200 status was sent? | |
| * @return void | |
| * @throws Exception On cURL failure | |
| */ | |
| public function validateResponse ($failIfNot200 = null) { | |
| if (null === $failIfNot200) { | |
| $failIfNot200 = $this->defaultFailIfNot200; | |
| } | |
| if (CURLE_OK != $this->responseCurlErr) { | |
| $exClazz = 'Moar\Net\Http\Exception'; | |
| switch ($this->responseCurlErr) { | |
| case CURLE_UNSUPPORTED_PROTOCOL: | |
| case CURLE_URL_MALFORMAT: | |
| $exClazz = 'Moar\Net\Http\BadUrlException'; | |
| break; | |
| case CURLE_COULDNT_RESOLVE_HOST: | |
| $exClazz = 'Moar\Net\Http\DnsFailureException'; | |
| break; | |
| case CURLE_COULDNT_CONNECT: | |
| $exClazz = 'Moar\Net\Http\ConnectFailedException'; | |
| break; | |
| case CURLE_HTTP_RETURNED_ERROR: | |
| $exClazz = 'Moar\Net\Http\StatusCodeException'; | |
| break; | |
| case CURLE_OPERATION_TIMEDOUT: | |
| $exClazz = 'Moar\Net\Http\TimeoutException'; | |
| break; | |
| case CURLE_PEER_FAILED_VERIFICATION: | |
| case CURLE_SSL_CACERT: | |
| case CURLE_SSL_CACERT_BADFILE: | |
| case CURLE_SSL_CERTPROBLEM: | |
| case CURLE_SSL_CERTPROBLEM: | |
| case CURLE_SSL_CIPHER: | |
| case CURLE_SSL_CONNECT_ERROR: | |
| case CURLE_SSL_CRL_BADFILE: | |
| case CURLE_SSL_ENGINE_INITFAILED: | |
| case CURLE_SSL_ENGINE_NOTFOUND: | |
| case CURLE_SSL_ENGINE_SETFAILED: | |
| case CURLE_SSL_ISSUER_ERROR: | |
| case CURLE_SSL_SHUTDOWN_FAILED: | |
| case CURLE_USE_SSL_FAILED: | |
| $exClazz = 'Moar\Net\Http\SslException'; | |
| break; | |
| } //end switch | |
| throw new $exClazz( | |
| $this->responseCurlErrMessage, $this->responseCurlErr, $this); | |
| } //end if !ok | |
| if ($failIfNot200) { | |
| $code = $this->getResponseHttpCode(); | |
| if ($code < 200 || $code > 299) { | |
| throw new StatusCodeException( | |
| "HTTP Error: ({$code}) from {$this->url}", | |
| CURLE_HTTP_RETURNED_ERROR, $this); | |
| } | |
| } | |
| } //end validateResponse | |
| /** | |
| * Submit a group of requests in parallel. | |
| * | |
| * Uses the curl_multi_exec engine to fire off several requests in parallel | |
| * and waits for all responses to finish before returing the collective | |
| * results. | |
| * | |
| * @param array $requests List of requests to submit | |
| * @return array List of submitted requests | |
| * @throws Exception If a fatal multi error occurs. | |
| */ | |
| public static function parallelSubmit ($requests) { | |
| $handles = array(); | |
| $mh = curl_multi_init(); | |
| // make a curl handle for each request | |
| foreach ($requests as $req) { | |
| $ch = $req->createCurlRequest(); | |
| $handles[(string) $ch] = $req; | |
| curl_multi_add_handle($mh, $ch); | |
| } | |
| $reqsRunning = 0; | |
| // fire off initial requests | |
| do { | |
| $status = curl_multi_exec($mh, $reqsRunning); | |
| } while (CURLM_CALL_MULTI_PERFORM == $status); | |
| // process the requests | |
| while ($reqsRunning && CURLM_OK == $status) { | |
| // wait to be woken up by network activity | |
| $selectReady = curl_multi_select($mh); | |
| if ($selectReady > 0) { | |
| // one or more requests are finished | |
| while ($info = curl_multi_info_read($mh)) { | |
| self::handleMultiResponse($info, $mh, $handles); | |
| } | |
| } //end if selectReady | |
| if (-1 != $selectReady) { | |
| // continue processing | |
| do { | |
| $status = curl_multi_exec($mh, $reqsRunning); | |
| } while (CURLM_CALL_MULTI_PERFORM == $status); | |
| } | |
| } //end while requests to process | |
| // check for critical failure | |
| if (CURLM_OK != $status) { | |
| throw new Exception( | |
| "Fatal error [{$status}] processing multiple requests", $status); | |
| } | |
| // any remaining results should be ready now | |
| while ($handles && ($info = curl_multi_info_read($mh))) { | |
| self::handleMultiResponse($info, $mh, $handles); | |
| } | |
| curl_multi_close($mh); | |
| return $requests; | |
| } //end parallelSubmit | |
| /** | |
| * Handle a curl_multi_info_read() response message. | |
| * | |
| * @param array $info Status message from Curl | |
| * @param resource $mh Curl multi handle | |
| * @param array &$handles List of Curl handles still outstanding | |
| * @return void | |
| * @see self::parallelSubmit() | |
| */ | |
| protected static function handleMultiResponse ($info, $mh, &$handles) { | |
| $ch = $info['handle']; | |
| $req = $handles[(string) $ch]; | |
| $rawResp = null; | |
| if (CURLE_OK == $info['result']) { | |
| // read the response from the handle | |
| $rawResp = curl_multi_getcontent($ch); | |
| } | |
| $req->processCurlResponse( | |
| $ch, $info['result'], curl_error($ch), $rawResp); | |
| // remove this handle from the queue | |
| curl_multi_remove_handle($mh, $ch); | |
| unset($handles[(string) $ch]); | |
| } //end handleMultiResponse | |
| /** | |
| * Convenience method to perform a GET request. | |
| * | |
| * Provided parameters will be "application/x-www-form-urlencoded" encoded | |
| * and appended to the provided URL. | |
| * | |
| * @param string $url URL to get (eg https://www.keynetics.com/page.php) | |
| * @param array $parms Array of key => value pairs to send | |
| * @param array $options Array of extra options to pass to cURL | |
| * @return Request Submitted request | |
| * @throws Exception On failure | |
| */ | |
| public static function get ($url, $parms = null, $options = null) { | |
| $url = Util::addQueryData($url, $parms); | |
| $r = new Request($url, self::METHOD_GET, null, null, $options); | |
| return $r->submit(); | |
| } //end get | |
| /** | |
| * Convenience method to POST data to a URL and retrieve the results. | |
| * | |
| * Provided parms will be encoded as "application/x-www-form-urlencoded" | |
| * data before being sent. | |
| * | |
| * @param string $url Full URL to post to (eg | |
| * https://www.keynetics.com/page.php) | |
| * @param array $parms Array of key => value pairs to post | |
| * @param array $options Array of extra options to pass to cURL | |
| * @return Request Submitted request | |
| * @throws Exception On failure | |
| */ | |
| public static function post ($url, $parms, $options = null) { | |
| $encParms = Util::urlEncode($parms); | |
| $r = new Request( | |
| $url, self::METHOD_POST, $encParms, null, $options); | |
| return $r->submit(); | |
| } //end post | |
| /** | |
| * Convenience method to POST content in the form of a raw string to a URL. | |
| * | |
| * This is useful for manually constructed SOAP requests and other document | |
| * body type operations. Default content type is text/xml. | |
| * | |
| * @param string $url Full URL to post to (eg | |
| * https://www.keynetics.com/page.php) | |
| * @param string $content Raw HTTP request body to be posted | |
| * @param string $contentType Value of the HTTP Content-Type header | |
| * @param array $options Array of extra options to pass to cURL | |
| * @return Request Submitted request | |
| * @throws Exception On failure | |
| */ | |
| public static function postContent ( | |
| $url, $content, $contentType = 'text/xml', $options = null) { | |
| $headers = array("Content-type: {$contentType}"); | |
| $r = new Request( | |
| $url, self::METHOD_POST, $content, $headers, $options); | |
| return $r->submit(); | |
| } //end postContent | |
| /** | |
| * Convenience method to POST data to a URL and retrieve the results. | |
| * | |
| * Provided parms will be encoded as "multipart/form-data" data before being | |
| * sent. | |
| * | |
| * @param string $url Full URL to post to (eg | |
| * https://www.keynetics.com/page.php) | |
| * @param array $parms Array of key => value pairs to post | |
| * @param array $options Array of extra options to pass to cURL | |
| * @return Request Submitted request | |
| * @throws Exception On failure | |
| */ | |
| public static function postMultipart ($url, $parms, $options = null) { | |
| $r = new Request( | |
| $url, self::METHOD_POST, $parms, null, $options); | |
| return $r->submit(); | |
| } //end postMultipart | |
| } //end Request |