url = $url; $http->method = $method; if ($options && is_callable($options)) { $options($http); } return $http; } /** * Make a HTTP GET call. * @param string $url * @param callable $options * @return self */ public static function get($url, $options = null) { $http = self::make($url, self::METHOD_GET, $options); return $http->send(); } /** * Make a HTTP POST call. * @param string $url * @param callable $options * @return self */ public static function post($url, $options = null) { $http = self::make($url, self::METHOD_POST, $options); return $http->send(); } /** * Make a HTTP DELETE call. * @param string $url * @param callable $options * @return self */ public static function delete($url, $options = null) { $http = self::make($url, self::METHOD_DELETE, $options); return $http->send(); } /** * Make a HTTP PATCH call. * @param string $url * @param callable $options * @return self */ public static function patch($url, $options = null) { $http = self::make($url, self::METHOD_PATCH, $options); return $http->send(); } /** * Make a HTTP PUT call. * @param string $url * @param callable $options * @return self */ public static function put($url, $options = null) { $http = self::make($url, self::METHOD_PUT, $options); return $http->send(); } /** * Make a HTTP OPTIONS call. * @param string $url * @param callable $options * @return self */ public static function options($url, $options = null) { $http = self::make($url, self::METHOD_OPTIONS, $options); return $http->send(); } /** * Execute the HTTP request. * @return string response body */ public function send() { if (!function_exists('curl_init')) { echo 'cURL PHP extension required.'.PHP_EOL; exit(1); } /* * Create and execute the cURL Resource */ $curl = curl_init(); curl_setopt($curl, CURLOPT_URL, $this->url); curl_setopt($curl, CURLOPT_HEADER, true); curl_setopt($curl, CURLOPT_RETURNTRANSFER, true); curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, false); curl_setopt($curl, CURLOPT_SSL_VERIFYHOST, false); if (defined('CURLOPT_FOLLOWLOCATION') && !ini_get('open_basedir')) { curl_setopt($curl, CURLOPT_FOLLOWLOCATION, true); curl_setopt($curl, CURLOPT_MAXREDIRS, $this->maxRedirects); } if ($this->requestOptions && is_array($this->requestOptions)) { curl_setopt_array($curl, $this->requestOptions); } /* * Set request method */ if ($this->method == self::METHOD_POST) { curl_setopt($curl, CURLOPT_POST, true); } elseif ($this->method !== self::METHOD_GET) { curl_setopt($curl, CURLOPT_CUSTOMREQUEST, $this->method); } /* * Set request data */ if ($this->requestData) { if (in_array($this->method, [self::METHOD_POST, self::METHOD_PATCH, self::METHOD_PUT])) { curl_setopt($curl, CURLOPT_POSTFIELDS, $this->getRequestData()); } elseif ($this->method == self::METHOD_GET) { curl_setopt($curl, CURLOPT_URL, $this->url . '?' . $this->getRequestData()); } } /* * Set request headers */ if ($this->requestHeaders) { $requestHeaders = []; foreach ($this->requestHeaders as $key => $value) { $requestHeaders[] = $key . ': ' . $value; } curl_setopt($curl, CURLOPT_HTTPHEADER, $requestHeaders); } /* * Handle output to file */ if ($this->streamFile) { $stream = fopen($this->streamFile, 'w'); if ($this->streamFilter) { stream_filter_append($stream, $this->streamFilter, STREAM_FILTER_WRITE); } curl_setopt($curl, CURLOPT_HEADER, false); curl_setopt($curl, CURLOPT_FILE, $stream); curl_exec($curl); } /* * Handle output to variable */ else { $response = $this->rawBody = curl_exec($curl); $headerSize = curl_getinfo($curl, CURLINFO_HEADER_SIZE); $this->headers = $this->headerToArray(substr($response, 0, $headerSize)); $this->body = substr($response, $headerSize); } $this->info = curl_getinfo($curl); $this->code = curl_getinfo($curl, CURLINFO_HTTP_CODE); /* * Close resources */ curl_close($curl); if ($this->streamFile) { fclose($stream); } /* * Emulate FOLLOW LOCATION behavior */ if (!defined('CURLOPT_FOLLOWLOCATION') || ini_get('open_basedir')) { if ($this->redirectCount === null) { $this->redirectCount = $this->maxRedirects; } if (in_array($this->code, [301, 302])) { $this->url = array_get($this->info, 'url'); if (!empty($this->url) && $this->redirectCount > 0) { $this->redirectCount -= 1; return $this->send(); } } } return $this; } /** * Return the request data set. * @return string */ public function getRequestData() { if ( $this->method !== self::METHOD_GET && isset($this->requestOptions[CURLOPT_POSTFIELDS]) && empty($this->requestData) ) { return $this->requestOptions[CURLOPT_POSTFIELDS]; } if (!empty($this->requestData)) { return http_build_query($this->requestData, '', $this->argumentSeparator); } return ''; } /** * Turn a header string into an array. * @param string $header * @return array */ protected function headerToArray($header) { $headers = []; $parts = explode("\r\n", $header); foreach ($parts as $singleHeader) { $delimiter = strpos($singleHeader, ': '); if ($delimiter !== false) { $key = substr($singleHeader, 0, $delimiter); $val = substr($singleHeader, $delimiter + 2); $headers[$key] = $val; } else { $delimiter = strpos($singleHeader, ' '); if ($delimiter !== false) { $key = substr($singleHeader, 0, $delimiter); $val = substr($singleHeader, $delimiter + 1); $headers[$key] = $val; } } } return $headers; } /** * Add a data to the request. * @param string $value * @return self */ public function data($key, $value = null) { if (is_array($key)) { foreach ($key as $_key => $_value) { $this->data($_key, $_value); } return $this; } $this->requestData[$key] = $value; return $this; } /** * Add a header to the request. * @param string $value * @return self */ public function header($key, $value = null) { if (is_array($key)) { foreach ($key as $_key => $_value) { $this->header($_key, $_value); } return $this; } $this->requestHeaders[$key] = $value; return $this; } /** * Sets a proxy to use with this request */ public function proxy($type, $host, $port, $username = null, $password = null) { if ($type === 'http') { $this->setOption(CURLOPT_PROXYTYPE, CURLPROXY_HTTP); } elseif ($type === 'socks4') { $this->setOption(CURLOPT_PROXYTYPE, CURLPROXY_SOCKS5); } elseif ($type === 'socks5') { $this->setOption(CURLOPT_PROXYTYPE, CURLPROXY_SOCKS5); } $this->setOption(CURLOPT_PROXY, $host . ':' . $port); if ($username && $password) { $this->setOption(CURLOPT_PROXYUSERPWD, $username . ':' . $password); } return $this; } /** * Adds authentication to the comms. * @param string $user * @param string $pass * @return self */ public function auth($user, $pass = null) { if (strpos($user, ':') !== false && !$pass) { list($user, $pass) = explode(':', $user); } $this->setOption(CURLOPT_HTTPAUTH, CURLAUTH_BASIC); $this->setOption(CURLOPT_USERPWD, $user . ':' . $pass); return $this; } /** * Disable follow location (redirects) */ public function noRedirect() { $this->setOption(CURLOPT_FOLLOWLOCATION, false); return $this; } /** * Enable SSL verification */ public function verifySSL() { $this->setOption(CURLOPT_SSL_VERIFYPEER, true); $this->setOption(CURLOPT_SSL_VERIFYHOST, true); return $this; } /** * Sets the request timeout. * @param string $timeout * @return self */ public function timeout($timeout) { $this->setOption(CURLOPT_CONNECTTIMEOUT, $timeout); $this->setOption(CURLOPT_TIMEOUT, $timeout); return $this; } /** * Write the response to a file * @param string $path Path to file * @param string $filter Stream filter as listed in stream_get_filters() * @return self */ public function toFile($path, $filter = null) { $this->streamFile = $path; if ($filter) { $this->streamFilter = $filter; } return $this; } /** * Add a single option to the request. * @param string $option * @param string $value * @return self */ public function setOption($option, $value = null) { if (is_array($option)) { foreach ($option as $_option => $_value) { $this->setOption($_option, $_value); } return $this; } if (is_string($option) && defined($option)) { $optionKey = constant($option); $this->requestOptions[$optionKey] = $value; } elseif (is_int($option)) { $constants = get_defined_constants(true); $curlOptConstants = array_flip(array_filter($constants['curl'], function ($key) { return strpos($key, 'CURLOPT_') === 0; }, ARRAY_FILTER_USE_KEY)); if (isset($curlOptConstants[$option])) { $this->requestOptions[$option] = $value; } else { throw new RuntimeException('$option parameter must be a CURLOPT constant or equivalent integer'); } } else { throw new RuntimeException('$option parameter must be a CURLOPT constant or equivalent integer'); } return $this; } /** * Handy if this object is called directly. * @return string The last response. */ public function __toString() { return (string) $this->body; } }