'Yii2-Curl-Agent', CURLOPT_TIMEOUT => 10, CURLOPT_CONNECTTIMEOUT => 10, CURLOPT_RETURNTRANSFER => true, CURLOPT_HEADER => true, CURLOPT_SSL_VERIFYPEER => false, CURLOPT_SSL_VERIFYHOST => 2, ]; // ############################################### class methods // ############################################## /** * Start performing GET-HTTP-Request * * @param string $url * @param boolean $raw if response body contains JSON and should be decoded * * @return mixed response */ public function get($url, $raw = true) { $this->_baseUrl = $url; return $this->_httpRequest('GET', $raw); } /** * Start performing HEAD-HTTP-Request * * @param string $url * * @return mixed response */ public function head($url) { $this->_baseUrl = $url; return $this->_httpRequest('HEAD'); } /** * Start performing POST-HTTP-Request * * @param string $url * @param boolean $raw if response body contains JSON and should be decoded * * @return mixed response */ public function post($url, $raw = true) { $this->_baseUrl = $url; return $this->_httpRequest('POST', $raw); } /** * Start performing PUT-HTTP-Request * * @param string $url * @param boolean $raw if response body contains JSON and should be decoded * * @return mixed response */ public function put($url, $raw = true) { $this->_baseUrl = $url; return $this->_httpRequest('PUT', $raw); } /** * Start performing PATCH-HTTP-Request * * @param string $url * @param boolean $raw if response body contains JSON and should be decoded * * @return mixed response */ public function patch($url, $raw = true) { $this->_baseUrl = $url; $this->setHeaders([ 'X-HTTP-Method-Override' => 'PATCH' ]); return $this->_httpRequest('POST',$raw); } /** * Start performing DELETE-HTTP-Request * * @param string $url * @param boolean $raw if response body contains JSON and should be decoded * * @return mixed response */ public function delete($url, $raw = true) { $this->_baseUrl = $url; return $this->_httpRequest('DELETE', $raw); } /** * Set curl option * * @param string $key * @param mixed $value * * @return $this */ public function setOption($key, $value) { //set value if (array_key_exists($key, $this->_defaultOptions) && $key !== CURLOPT_WRITEFUNCTION) { $this->_defaultOptions[$key] = $value; } else { $this->_options[$key] = $value; } //return self return $this; } /** * Set get params * * @param array $params * @return $this */ public function setGetParams($params) { if (is_array($params)) { foreach ($params as $key => $value) { $this->_getParams[$key] = $value; } } //return self return $this; } /** * Set get params * * @param array $params * @return $this */ public function setPostParams($params) { if (is_array($params)) { $this->setOption( CURLOPT_POSTFIELDS, http_build_query($params) ); } //return self return $this; } /** * Set raw post data allows you to post any data format. * * @param mixed $data * @return $this */ public function setRawPostData($data) { $this->setOption( CURLOPT_POSTFIELDS, $data ); //return self return $this; } /** * Set get params * * @param string $data * @return $this */ public function setRequestBody($data) { if (is_string($data)) { $this->setOption( CURLOPT_POSTFIELDS, $data ); } //return self return $this; } /** * Get URL - return URL parsed with given params * * @return string The full URL with parsed get params */ public function getUrl() { if (Count($this->_getParams) > 0) { return $this->_baseUrl.'?'.http_build_query($this->_getParams); } else { return $this->_baseUrl; } } /** * Set curl options * * @param array $options * * @return $this */ public function setOptions($options) { $this->_options = $options + $this->_options; return $this; } /** * Set multiple headers for request. * * @param array $headers * * @return $this */ public function setHeaders($headers) { if (is_array($headers)) { //init $parsedHeader = []; //collect currently set headers foreach ($this->getRequestHeaders() as $header => $value) { array_push($parsedHeader, $header.':'.$value); } //parse header into right format key:value foreach ($headers as $header => $value) { array_push($parsedHeader, $header.':'.$value); } //set headers $this->setOption( CURLOPT_HTTPHEADER, $parsedHeader ); } return $this; } /** * Set a single header for request. * * @param string $header * @param string $value * * @return $this */ public function setHeader($header, $value) { //init $parsedHeader = []; //collect currently set headers foreach ($this->getRequestHeaders() as $headerToSet => $valueToSet) { array_push($parsedHeader, $headerToSet.':'.$valueToSet); } //add override new header if (strlen($header) > 0) { array_push($parsedHeader, $header.':'.$value); } //set headers $this->setOption( CURLOPT_HTTPHEADER, $parsedHeader ); return $this; } /** * Unset a single header. * * @param string $header * * @return $this */ public function unsetHeader($header) { //init $parsedHeader = []; //collect currently set headers and filter "unset" header param. foreach ($this->getRequestHeaders() as $headerToSet => $valueToSet) { if ($header !== $headerToSet) { array_push($parsedHeader, $headerToSet.':'.$valueToSet); } } //set headers $this->setOption( CURLOPT_HTTPHEADER, $parsedHeader ); return $this; } /** * Get all request headers as key:value array * * @return array */ public function getRequestHeaders() { //Init $requestHeaders = $this->getOption(CURLOPT_HTTPHEADER); $parsedRequestHeaders = []; if (is_array($requestHeaders)) { foreach ($requestHeaders as $headerValue) { list ($key, $value) = explode(':', $headerValue, 2); $parsedRequestHeaders[$key] = $value; } } return $parsedRequestHeaders; } /** * Get specific request header as key:value array * * @param string $headerKey * * @return string|null */ public function getRequestHeader($headerKey) { //Init $parsedRequestHeaders = $this->getRequestHeaders(); return isset($parsedRequestHeaders[$headerKey]) ? $parsedRequestHeaders[$headerKey] : null; } /** * Unset a single curl option * * @param string $key * * @return $this */ public function unsetOption($key) { //reset a single option if its set already if (isset($this->_options[$key])) { unset($this->_options[$key]); } return $this; } /** * Unset all curl option, excluding default options. * * @return $this */ public function unsetOptions() { //reset all options if (isset($this->_options)) { $this->_options = []; } return $this; } /** * Total reset of options, responses, etc. * * @return $this */ public function reset() { if ($this->curl !== null) { curl_close($this->curl); //stop curl } //reset all options if (isset($this->_options)) { $this->_options = []; } //reset response & status params $this->curl = null; $this->errorCode = null; $this->response = null; $this->responseCode = null; $this->responseCharset = null; $this->responseLength = -1; $this->responseType = null; $this->errorText = null; $this->_postParams = []; $this->_getParams = []; return $this; } /** * Return a single option * * @param string|integer $key * @return mixed|boolean */ public function getOption($key) { //get merged options depends on default and user options $mergesOptions = $this->getOptions(); //return value or false if key is not set. return isset($mergesOptions[$key]) ? $mergesOptions[$key] : false; } /** * Return merged curl options and keep keys! * * @return array */ public function getOptions() { return $this->_options + $this->_defaultOptions; } /** * Get curl info according to http://php.net/manual/de/function.curl-getinfo.php * * @param null $opt * @return array|mixed */ public function getInfo($opt = null) { if ($this->curl !== null && $opt === null) { return curl_getinfo($this->curl); } elseif ($this->curl !== null && $opt !== null) { return curl_getinfo($this->curl, $opt); } else { return []; } } /** * Performs HTTP request * * @param string $method * @param boolean $raw if response body contains JSON and should be decoded -> helper. * * @throws Exception if request failed * * @return mixed */ protected function _httpRequest($method, $raw = false) { //set request type and writer function $this->setOption(CURLOPT_CUSTOMREQUEST, strtoupper($method)); //check if method is head and set no body if ($method === 'HEAD') { $this->setOption(CURLOPT_NOBODY, true); $this->unsetOption(CURLOPT_WRITEFUNCTION); } //setup error reporting and profiling if (YII_DEBUG) { Yii::trace('Start sending cURL-Request: '.$this->getUrl().'\n', __METHOD__); Yii::beginProfile($method.' '.$this->_baseUrl.'#'.md5(serialize($this->getOption(CURLOPT_POSTFIELDS))), __METHOD__); } /** * proceed curl */ $curlOptions = $this->getOptions(); $this->curl = curl_init($this->getUrl()); curl_setopt_array($this->curl, $curlOptions); $response = curl_exec($this->curl); //check if curl was successful if ($response === false) { //set error code $this->errorCode = curl_errno($this->curl); $this->errorText = curl_strerror($this->errorCode); switch ($this->errorCode) { // 7, 28 = timeout case 7: case 28: $this->responseCode = 'timeout'; return false; break; default: return false; break; } } //extract header / body data if CURLOPT_HEADER are set to true if (isset($curlOptions[CURLOPT_HEADER]) && $curlOptions[CURLOPT_HEADER]) { $this->response = $this->_extractCurlBody($response); $this->responseHeaders = $this->_extractCurlHeaders($response); } else { $this->response = $response; } // Extract additional curl params $this->_extractAdditionalCurlParameter(); //end yii debug profile if (YII_DEBUG) { Yii::endProfile($method.' '.$this->getUrl().'#'.md5(serialize($this->getOption(CURLOPT_POSTFIELDS))), __METHOD__); } //check responseCode and return data/status if ($this->getOption(CURLOPT_CUSTOMREQUEST) === 'HEAD') { return true; } else { $this->response = $raw ? $this->response : Json::decode($this->response); return $this->response; } } /** * Extract additional curl params protected class helper */ protected function _extractAdditionalCurlParameter () { /** * retrieve response code */ $this->responseCode = curl_getinfo($this->curl, CURLINFO_HTTP_CODE); /** * try extract response type & charset. */ $this->responseType = curl_getinfo($this->curl, CURLINFO_CONTENT_TYPE); if (!is_null($this->responseType) && count(explode(';', $this->responseType)) > 1) { list($this->responseType, $possibleCharset) = explode(';', $this->responseType); //extract charset if (preg_match('~^charset=(.+?)$~', trim($possibleCharset), $matches) && isset($matches[1])) { $this->responseCharset = strtolower($matches[1]); } } /** * try extract response length */ $this->responseLength = curl_getinfo($this->curl, CURLINFO_CONTENT_LENGTH_DOWNLOAD); if((int)$this->responseLength == -1) { $this->responseLength = strlen($this->response); } } /** * Extract body curl data from response * * @param string $response * @return string */ protected function _extractCurlBody ($response) { return substr($response, $this->getInfo(CURLINFO_HEADER_SIZE)); } /** * Extract header curl data from response * * @param string $response * @return array */ protected function _extractCurlHeaders ($response) { //Init $headers = []; $headerText = substr($response, 0, strpos($response, "\r\n\r\n")); foreach (explode("\r\n", $headerText) as $i => $line) { if ($i === 0) { $headers['http_code'] = $line; } else { list ($key, $value) = explode(':', $line, 2); $headers[$key] = ltrim($value); } } return $headers; } }