| 1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282 | <?phpnamespace App\Services\Common;class CurlService{    const VERSION = '5.1.0';    const DEFAULT_TIMEOUT = 30;    public static $RFC2616 = array(        // RFC2616: "any CHAR except CTLs or separators".        // CHAR           = <any US-ASCII character (octets 0 - 127)>        // CTL            = <any US-ASCII control character        //                  (octets 0 - 31) and DEL (127)>        // separators     = "(" | ")" | "<" | ">" | "@"        //                | "," | ";" | ":" | "\" | <">        //                | "/" | "[" | "]" | "?" | "="        //                | "{" | "}" | SP | HT        // SP             = <US-ASCII SP, space (32)>        // HT             = <US-ASCII HT, horizontal-tab (9)>        // <">            = <US-ASCII double-quote mark (34)>        '!', '#', '$', '%', '&', "'", '*', '+', '-', '.', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B',        'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X',        'Y', 'Z', '^', '_', '`', 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q',        'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', '|', '~',    );    public static $RFC6265 = array(        // RFC6265: "US-ASCII characters excluding CTLs, whitespace DQUOTE, comma, semicolon, and backslash".        // %x21        '!',        // %x23-2B        '#', '$', '%', '&', "'", '(', ')', '*', '+',        // %x2D-3A        '-', '.', '/', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', ':',        // %x3C-5B        '<', '=', '>', '?', '@', 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q',        'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', '[',        // %x5D-7E        ']', '^', '_', '`', 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r',        's', 't', 'u', 'v', 'w', 'x', 'y', 'z', '{', '|', '}', '~',    );    public $curl;    public $id = null;    public $error = false;    public $errorCode = 0;    public $errorMessage = null;    public $curlError = false;    public $curlErrorCode = 0;    public $curlErrorMessage = null;    public $httpError = false;    public $httpStatusCode = 0;    public $httpErrorMessage = null;    public $baseUrl = null;    public $url = null;    public $requestHeaders = null;    public $responseHeaders = null;    public $rawResponseHeaders = '';    public $response = null;    public $rawResponse = null;    public $beforeSendFunction = null;    public $downloadCompleteFunction = null;    public $successFunction = null;    public $errorFunction = null;    public $completeFunction = null;    public $fileHandle = null;    private $cookies = array();    private $responseCookies = array();    private $headers = array();    private $options = array();    private $jsonDecoder = null;    private $jsonPattern = '/^(?:application|text)\/(?:[a-z]+(?:[\.-][0-9a-z]+){0,}[\+\.]|x-)?json(?:-[a-z]+)?/i';    private $xmlDecoder = null;    private $xmlPattern = '~^(?:text/|application/(?:atom\+|rss\+)?)xml~i';    private $defaultDecoder = null;    private static $deferredProperties = array(        'effectiveUrl',        'totalTime',    );    /**     * Construct     *     * @access public     * @param  $base_url     * @throws \ErrorException     */    public function __construct($base_url = null)    {        if (!extension_loaded('curl')) {            throw new \ErrorException('cURL library is not loaded');        }        $this->curl = curl_init();        $this->id = 1;        $this->setDefaultUserAgent();        $this->setDefaultJsonDecoder();        $this->setDefaultXmlDecoder();        $this->setDefaultTimeout();        $this->setOpt(CURLINFO_HEADER_OUT, true);        $this->setOpt(CURLOPT_HEADERFUNCTION, array($this, 'headerCallback'));        $this->setOpt(CURLOPT_RETURNTRANSFER, true);        $this->headers = new CaseInsensitiveArrayService();        $this->setURL($base_url);        $this->rfc2616 = array_fill_keys(self::$RFC2616, true);        $this->rfc6265 = array_fill_keys(self::$RFC6265, true);    }    /**     * Before Send     *     * @access public     * @param  $callback     */    public function beforeSend($callback)    {        $this->beforeSendFunction = $callback;    }    /**     * Build Post Data     *     * @access public     * @param  $data     *     * @return array|string     */    public function buildPostData($data)    {        $binary_data = false;        if (is_array($data)) {            // Return JSON-encoded string when the request's content-type is JSON.            if (isset($this->headers['Content-Type']) &&                preg_match($this->jsonPattern, $this->headers['Content-Type'])) {                $json_str = json_encode($data);                if (!($json_str === false)) {                    $data = $json_str;                }            } else {                // Manually build a single-dimensional array from a multi-dimensional array as using curl_setopt($ch,                // CURLOPT_POSTFIELDS, $data) doesn't correctly handle multi-dimensional arrays when files are                // referenced.                if (self::is_array_multidim($data)) {                    $data = self::array_flatten_multidim($data);                }                // Modify array values to ensure any referenced files are properly handled depending on the support of                // the @filename API or CURLFile usage. This also fixes the warning "curl_setopt(): The usage of the                // @filename API for file uploading is deprecated. Please use the CURLFile class instead". Ignore                // non-file values prefixed with the @ character.                foreach ($data as $key => $value) {                    if (is_string($value) && strpos($value, '@') === 0 && is_file(substr($value, 1))) {                        $binary_data = true;                        if (class_exists('CURLFile')) {                            $data[$key] = new \CURLFile(substr($value, 1));                        }                    } elseif ($value instanceof \CURLFile) {                        $binary_data = true;                    }                }            }        }        if (!$binary_data && (is_array($data) || is_object($data))) {            $data = http_build_query($data, '', '&');        }        return $data;    }    /**     * Call     *     * @access public     */    public function call()    {        $args = func_get_args();        $function = array_shift($args);        if (is_callable($function)) {            array_unshift($args, $this);            call_user_func_array($function, $args);        }    }    /**     * Close     *     * @access public     */    public function close()    {        if (is_resource($this->curl)) {            curl_close($this->curl);        }        $this->options = null;        $this->jsonDecoder = null;        $this->xmlDecoder = null;    }    /**     * Complete     *     * @access public     * @param  $callback     */    public function complete($callback)    {        $this->completeFunction = $callback;    }    /**     * Progress     *     * @access public     * @param  $callback     */    public function progress($callback)    {        $this->setOpt(CURLOPT_PROGRESSFUNCTION, $callback);        $this->setOpt(CURLOPT_NOPROGRESS, false);    }    /**     * Delete     *     * @access public     * @param  $url     * @param  $query_parameters     * @param  $data     *     * @return string     */    public function delete($url, $query_parameters = array(), $data = array())    {        if (is_array($url)) {            $data = $query_parameters;            $query_parameters = $url;            $url = $this->baseUrl;        }        $this->setURL($url, $query_parameters);        $this->setOpt(CURLOPT_CUSTOMREQUEST, 'DELETE');        $this->setOpt(CURLOPT_POSTFIELDS, $this->buildPostData($data));        return $this->exec();    }    /**     * Download Complete     *     * @access public     * @param  $fh     */    public function downloadComplete($fh)    {        if (!$this->error && $this->downloadCompleteFunction) {            rewind($fh);            $this->call($this->downloadCompleteFunction, $fh);            $this->downloadCompleteFunction = null;        }        if (is_resource($fh)) {            fclose($fh);        }        // Fix "PHP Notice: Use of undefined constant STDOUT" when reading the        // PHP script from stdin. Using null causes "Warning: curl_setopt():        // supplied argument is not a valid File-Handle resource".        if (!defined('STDOUT')) {            define('STDOUT', fopen('php://stdout', 'w'));        }        // Reset CURLOPT_FILE with STDOUT to avoid: "curl_exec(): CURLOPT_FILE        // resource has gone away, resetting to default".        $this->setOpt(CURLOPT_FILE, STDOUT);        // Reset CURLOPT_RETURNTRANSFER to tell cURL to return subsequent        // responses as the return value of curl_exec(). Without this,        // curl_exec() will revert to returning boolean values.        $this->setOpt(CURLOPT_RETURNTRANSFER, true);    }    /**     * Download     *     * @access public     * @param  $url     * @param  $mixed_filename     *     * @return boolean     */    public function download($url, $mixed_filename)    {        if (is_callable($mixed_filename)) {            $this->downloadCompleteFunction = $mixed_filename;            $fh = tmpfile();        } else {            $filename = $mixed_filename;            $fh = fopen($filename, 'wb');        }        $this->setOpt(CURLOPT_FILE, $fh);        $this->get($url);        $this->downloadComplete($fh);        return ! $this->error;    }    /**     * Error     *     * @access public     * @param  $callback     */    public function error($callback)    {        $this->errorFunction = $callback;    }    /**     * Exec     *     * @access public     * @param  $ch     *     * @return mixed Returns the value provided by parseResponse.     */    public function exec($ch = null)    {        $this->responseCookies = array();        if ($ch === null) {            $this->call($this->beforeSendFunction);            $this->rawResponse = curl_exec($this->curl);            $this->curlErrorCode = curl_errno($this->curl);        } else {            $this->rawResponse = curl_multi_getcontent($ch);        }        $this->curlErrorMessage = curl_error($this->curl);        $this->curlError = !($this->curlErrorCode === 0);        $this->httpStatusCode = $this->getInfo(CURLINFO_HTTP_CODE);        $this->httpError = in_array(floor($this->httpStatusCode / 100), array(4, 5));        $this->error = $this->curlError || $this->httpError;        $this->errorCode = $this->error ? ($this->curlError ? $this->curlErrorCode : $this->httpStatusCode) : 0;        // NOTE: CURLINFO_HEADER_OUT set to true is required for requestHeaders        // to not be empty (e.g. $curl->setOpt(CURLINFO_HEADER_OUT, true);).        if ($this->getOpt(CURLINFO_HEADER_OUT) === true) {            $this->requestHeaders = $this->parseRequestHeaders($this->getInfo(CURLINFO_HEADER_OUT));        }        $this->responseHeaders = $this->parseResponseHeaders($this->rawResponseHeaders);        $this->response = $this->parseResponse($this->responseHeaders, $this->rawResponse);        $this->httpErrorMessage = '';        if ($this->error) {            if (isset($this->responseHeaders['Status-Line'])) {                $this->httpErrorMessage = $this->responseHeaders['Status-Line'];            }        }        $this->errorMessage = $this->curlError ? $this->curlErrorMessage : $this->httpErrorMessage;        if (!$this->error) {            $this->call($this->successFunction);        } else {            $this->call($this->errorFunction);        }        $this->call($this->completeFunction);        return $this->response;    }    /**     * Get     *     * @access public     * @param  $url     * @param  $data     *     * @return mixed Returns the value provided by exec.     */    public function get($url, $data = array())    {        if (is_array($url)) {            $data = $url;            $url = $this->baseUrl;        }        $this->setURL($url, $data);        $this->setOpt(CURLOPT_CUSTOMREQUEST, 'GET');        $this->setOpt(CURLOPT_HTTPGET, true);        return $this->exec();    }    /**     * Get Info     *     * @access public     * @param  $opt     */    public function getInfo($opt)    {        return curl_getinfo($this->curl, $opt);    }    /**     * Get Opt     *     * @access public     * @param  $option     *     * @return mixed     */    public function getOpt($option)    {        return $this->options[$option];    }    /**     * Head     *     * @access public     * @param  $url     * @param  $data     *     * @return string     */    public function head($url, $data = array())    {        if (is_array($url)) {            $data = $url;            $url = $this->baseUrl;        }        $this->setURL($url, $data);        $this->setOpt(CURLOPT_CUSTOMREQUEST, 'HEAD');        $this->setOpt(CURLOPT_NOBODY, true);        return $this->exec();    }    /**     * Header Callback     *     * @access public     * @param  $ch     * @param  $header     *     * @return integer     */    public function headerCallback($ch, $header)    {        if (preg_match('/^Set-Cookie:\s*([^=]+)=([^;]+)/mi', $header, $cookie) === 1) {            $this->responseCookies[$cookie[1]] = trim($cookie[2], " \n\r\t\0\x0B");        }        $this->rawResponseHeaders .= $header;        return strlen($header);    }    /**     * Options     *     * @access public     * @param  $url     * @param  $data     *     * @return string     */    public function options($url, $data = array())    {        if (is_array($url)) {            $data = $url;            $url = $this->baseUrl;        }        $this->setURL($url, $data);        $this->unsetHeader('Content-Length');        $this->setOpt(CURLOPT_CUSTOMREQUEST, 'OPTIONS');        return $this->exec();    }    /**     * Patch     *     * @access public     * @param  $url     * @param  $data     *     * @return string     */    public function patch($url, $data = array())    {        if (is_array($url)) {            $data = $url;            $url = $this->baseUrl;        }        if (is_array($data) && empty($data)) {            $this->unsetHeader('Content-Length');        }        $this->setURL($url);        $this->setOpt(CURLOPT_CUSTOMREQUEST, 'PATCH');        $this->setOpt(CURLOPT_POSTFIELDS, $this->buildPostData($data));        return $this->exec();    }    /**     * Post     *     * @access public     * @param  $url     * @param  $data     * @param  $follow_303_with_post If true, will cause 303 redirections to be followed using     *     a POST request (default: false).     *     Notes:     *       - Redirections are only followed if the CURLOPT_FOLLOWLOCATION option is set to true.     *       - According to the HTTP specs (see [1]), a 303 redirection should be followed using     *         the GET method. 301 and 302 must not.     *       - In order to force a 303 redirection to be performed using the same method, the     *         underlying cURL object must be set in a special state (the CURLOPT_CURSTOMREQUEST     *         option must be set to the method to use after the redirection). Due to a limitation     *         of the cURL extension of PHP < 5.5.11 ([2], [3]) and of HHVM, it is not possible     *         to reset this option. Using these PHP engines, it is therefore impossible to     *         restore this behavior on an existing php-curl-class Curl object.     *     * @return string     *     * [1] https://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10.3.2     * [2] https://github.com/php/php-src/pull/531     * [3] http://php.net/ChangeLog-5.php#5.5.11     */    public function post($url, $data = array(), $follow_303_with_post = false)    {        if (is_array($url)) {            $follow_303_with_post = (bool)$data;            $data = $url;            $url = $this->baseUrl;        }        $this->setURL($url);        if ($follow_303_with_post) {            $this->setOpt(CURLOPT_CUSTOMREQUEST, 'POST');        } else {            if (isset($this->options[CURLOPT_CUSTOMREQUEST])) {                if ((version_compare(PHP_VERSION, '5.5.11') < 0) || defined('HHVM_VERSION')) {                    trigger_error('Due to technical limitations of PHP <= 5.5.11 and HHVM, it is not possible to '                        . 'perform a post-redirect-get request using a php-curl-class Curl object that '                        . 'has already been used to perform other types of requests. Either use a new '                        . 'php-curl-class Curl object or upgrade your PHP engine.',                        E_USER_ERROR);                } else {                    $this->setOpt(CURLOPT_CUSTOMREQUEST, null);                }            }        }        $this->setOpt(CURLOPT_POST, true);        $this->setOpt(CURLOPT_POSTFIELDS, $this->buildPostData($data));        return $this->exec();    }    /**     * Put     *     * @access public     * @param  $url     * @param  $data     *     * @return string     */    public function put($url, $data = array())    {        if (is_array($url)) {            $data = $url;            $url = $this->baseUrl;        }        $this->setURL($url);        $this->setOpt(CURLOPT_CUSTOMREQUEST, 'PUT');        $put_data = $this->buildPostData($data);        if (empty($this->options[CURLOPT_INFILE]) && empty($this->options[CURLOPT_INFILESIZE])) {            if (is_string($put_data)) {                $this->setHeader('Content-Length', strlen($put_data));            }        }        if (!empty($put_data)) {            $this->setOpt(CURLOPT_POSTFIELDS, $put_data);        }        return $this->exec();    }    /**     * Search     *     * @access public     * @param  $url     * @param  $data     *     * @return string     */    public function search($url, $data = array())    {        if (is_array($url)) {            $data = $url;            $url = $this->baseUrl;        }        $this->setURL($url);        $this->setOpt(CURLOPT_CUSTOMREQUEST, 'SEARCH');        $put_data = $this->buildPostData($data);        if (empty($this->options[CURLOPT_INFILE]) && empty($this->options[CURLOPT_INFILESIZE])) {            if (is_string($put_data)) {                $this->setHeader('Content-Length', strlen($put_data));            }        }        if (!empty($put_data)) {            $this->setOpt(CURLOPT_POSTFIELDS, $put_data);        }        return $this->exec();    }    /**     * Set Basic Authentication     *     * @access public     * @param  $username     * @param  $password     */    public function setBasicAuthentication($username, $password = '')    {        $this->setOpt(CURLOPT_HTTPAUTH, CURLAUTH_BASIC);        $this->setOpt(CURLOPT_USERPWD, $username . ':' . $password);    }    /**     * Set Digest Authentication     *     * @access public     * @param  $username     * @param  $password     */    public function setDigestAuthentication($username, $password = '')    {        $this->setOpt(CURLOPT_HTTPAUTH, CURLAUTH_DIGEST);        $this->setOpt(CURLOPT_USERPWD, $username . ':' . $password);    }    /**     * Set Cookie     *     * @access public     * @param  $key     * @param  $value     */    public function setCookie($key, $value)    {        $name_chars = array();        foreach (str_split($key) as $name_char) {            if (!isset($this->rfc2616[$name_char])) {                $name_chars[] = rawurlencode($name_char);            } else {                $name_chars[] = $name_char;            }        }        $value_chars = array();        foreach (str_split($value) as $value_char) {            if (!isset($this->rfc6265[$value_char])) {                $value_chars[] = rawurlencode($value_char);            } else {                $value_chars[] = $value_char;            }        }        $this->cookies[implode('', $name_chars)] = implode('', $value_chars);        $this->setOpt(CURLOPT_COOKIE, implode('; ', array_map(function($k, $v) {            return $k . '=' . $v;        }, array_keys($this->cookies), array_values($this->cookies))));    }    /**     * Get cookie.     *     * @access public     * @param  $key     * @return mixed     */    public function getCookie($key)    {        return $this->getResponseCookie($key);    }    /**     * Get response cookie.     *     * @access public     * @param  $key     * @return mixed     */    public function getResponseCookie($key)    {        return isset($this->responseCookies[$key]) ? $this->responseCookies[$key] : null;    }    /**     * Get response cookies.     *     * @access public     * @return array     */    public function getResponseCookies()    {        return $this->responseCookies;    }    /**     * Set Port     *     * @access public     * @param  $port     */    public function setPort($port)    {        $this->setOpt(CURLOPT_PORT, intval($port));    }    /**     * Set Connect Timeout     *     * @access public     * @param  $seconds     */    public function setConnectTimeout($seconds)    {        $this->setOpt(CURLOPT_CONNECTTIMEOUT, $seconds);    }    /**     * Set Cookie String     *     * @access public     * @param  $string     */    public function setCookieString($string)    {        return $this->setOpt(CURLOPT_COOKIE, $string);    }    /**     * Set Cookie File     *     * @access public     * @param  $cookie_file     */    public function setCookieFile($cookie_file)    {        $this->setOpt(CURLOPT_COOKIEFILE, $cookie_file);    }    /**     * Set Cookie Jar     *     * @access public     * @param  $cookie_jar     */    public function setCookieJar($cookie_jar)    {        $this->setOpt(CURLOPT_COOKIEJAR, $cookie_jar);    }    /**     * Set Default JSON Decoder     *     * @access public     * @param  $assoc     * @param  $depth     * @param  $options     */    public function setDefaultJsonDecoder()    {        $args = func_get_args();        $this->jsonDecoder = function($response) use ($args) {            array_unshift($args, $response);            // Call json_decode() without the $options parameter in PHP            // versions less than 5.4.0 as the $options parameter was added in            // PHP version 5.4.0.            if (version_compare(PHP_VERSION, '5.4.0', '<')) {                $args = array_slice($args, 0, 3);            }            $json_obj = call_user_func_array('json_decode', $args);            if (!($json_obj === null)) {                $response = $json_obj;            }            return $response;        };    }    /**     * Set Default XML Decoder     *     * @access public     */    public function setDefaultXmlDecoder()    {        $this->xmlDecoder = function($response) {            $xml_obj = @simplexml_load_string($response);            if (!($xml_obj === false)) {                $response = $xml_obj;            }            return $response;        };    }    /**     * Set Default Decoder     *     * @access public     * @param  $decoder string|callable     */    public function setDefaultDecoder($decoder = 'json')    {        if (is_callable($decoder)) {            $this->defaultDecoder = $decoder;        } else {            if ($decoder === 'json') {                $this->defaultDecoder = $this->jsonDecoder;            } elseif ($decoder === 'xml') {                $this->defaultDecoder = $this->xmlDecoder;            }        }    }    /**     * Set Default Timeout     *     * @access public     */    public function setDefaultTimeout()    {        $this->setTimeout(self::DEFAULT_TIMEOUT);    }    /**     * Set Default User Agent     *     * @access public     */    public function setDefaultUserAgent()    {        $user_agent = 'PHP-Curl-Class/' . self::VERSION . ' (+https://github.com/php-curl-class/php-curl-class)';        $user_agent .= ' PHP/' . PHP_VERSION;        $curl_version = curl_version();        $user_agent .= ' curl/' . $curl_version['version'];        $this->setUserAgent($user_agent);    }    /**     * Set Header     *     * @access public     * @param  $key     * @param  $value     */    public function setHeader($key, $value)    {        $this->headers[$key] = $value;        $headers = array();        foreach ($this->headers as $key => $value) {            $headers[] = $key . ': ' . $value;        }        $this->setOpt(CURLOPT_HTTPHEADER, $headers);    }    /**     * Set JSON Decoder     *     * @access public     * @param  $function     */    public function setJsonDecoder($function)    {        if (is_callable($function)) {            $this->jsonDecoder = $function;        }    }    /**     * Set XML Decoder     *     * @access public     * @param  $function     */    public function setXmlDecoder($function)    {        if (is_callable($function)) {            $this->xmlDecoder = $function;        }    }    /**     * Set Opt     *     * @access public     * @param  $option     * @param  $value     *     * @return boolean     */    public function setOpt($option, $value)    {        $required_options = array(            CURLOPT_RETURNTRANSFER => 'CURLOPT_RETURNTRANSFER',        );        if (in_array($option, array_keys($required_options), true) && !($value === true)) {            trigger_error($required_options[$option] . ' is a required option', E_USER_WARNING);        }        $success = curl_setopt($this->curl, $option, $value);        if ($success) {            $this->options[$option] = $value;        }        return $success;    }    /**     * Set Opts     *     * @access public     * @param  $options     *     * @return boolean     *   Returns true if all options were successfully set. If an option could not be successfully set, false is     *   immediately returned, ignoring any future options in the options array. Similar to curl_setopt_array().     */    public function setOpts($options)    {        foreach ($options as $option => $value) {            if (!$this->setOpt($option, $value)) {                return false;            }        }    }    /**     * Set Referer     *     * @access public     * @param  $referer     */    public function setReferer($referer)    {        $this->setReferrer($referer);    }    /**     * Set Referrer     *     * @access public     * @param  $referrer     */    public function setReferrer($referrer)    {        $this->setOpt(CURLOPT_REFERER, $referrer);    }    /**     * Set Timeout     *     * @access public     * @param  $seconds     */    public function setTimeout($seconds)    {        $this->setOpt(CURLOPT_TIMEOUT, $seconds);    }    /**     * Set Url     *     * @access public     * @param  $url     * @param  $data     */    public function setURL($url, $data = array())    {        $this->baseUrl = $url;        $this->url = $this->buildURL($url, $data);        $this->setOpt(CURLOPT_URL, $this->url);    }    /**     * Set User Agent     *     * @access public     * @param  $user_agent     */    public function setUserAgent($user_agent)    {        $this->setOpt(CURLOPT_USERAGENT, $user_agent);    }    /**     * Success     *     * @access public     * @param  $callback     */    public function success($callback)    {        $this->successFunction = $callback;    }    /**     * Unset Header     *     * @access public     * @param  $key     */    public function unsetHeader($key)    {        $this->setHeader($key, '');        unset($this->headers[$key]);    }    /**     * Verbose     *     * @access public     * @param  bool $on     * @param  resource $output     */    public function verbose($on = true, $output = STDERR)    {        // Turn off CURLINFO_HEADER_OUT for verbose to work. This has the side        // effect of causing Curl::requestHeaders to be empty.        if ($on) {            $this->setOpt(CURLINFO_HEADER_OUT, false);        }        $this->setOpt(CURLOPT_VERBOSE, $on);        $this->setOpt(CURLOPT_STDERR, $output);    }    /**     * Destruct     *     * @access public     */    public function __destruct()    {        $this->close();    }    public function __get($name)    {        $return = null;        if (in_array($name, self::$deferredProperties) && is_callable(array($this, $getter = '__get_' . $name))) {            $return = $this->$name = $this->$getter();        }        return $return;    }    /**     * Get Effective Url     *     * @access private     */    private function __get_effectiveUrl() {        return $this->getInfo(CURLINFO_EFFECTIVE_URL);    }    /**     * Get Total Time     *     * @access private     */    private function __get_totalTime() {        return $this->getInfo(CURLINFO_TOTAL_TIME);    }    /**     * Build Url     *     * @access private     * @param  $url     * @param  $data     *     * @return string     */    private function buildURL($url, $data = array())    {        return $url . (empty($data) ? '' : '?' . http_build_query($data, '', '&'));    }    /**     * Parse Headers     *     * @access private     * @param  $raw_headers     *     * @return array     */    private function parseHeaders($raw_headers)    {        $raw_headers = preg_split('/\r\n/', $raw_headers, null, PREG_SPLIT_NO_EMPTY);        $http_headers = new CaseInsensitiveArrayService();        $raw_headers_count = count($raw_headers);        for ($i = 1; $i < $raw_headers_count; $i++) {            list($key, $value) = explode(':', $raw_headers[$i], 2);            $key = trim($key);            $value = trim($value);            // Use isset() as array_key_exists() and ArrayAccess are not compatible.            if (isset($http_headers[$key])) {                $http_headers[$key] .= ',' . $value;            } else {                $http_headers[$key] = $value;            }        }        return array(isset($raw_headers['0']) ? $raw_headers['0'] : '', $http_headers);    }    /**     * Parse Request Headers     *     * @access private     * @param  $raw_headers     *     * @return array     */    private function parseRequestHeaders($raw_headers)    {        $request_headers = new CaseInsensitiveArrayService();        list($first_line, $headers) = $this->parseHeaders($raw_headers);        $request_headers['Request-Line'] = $first_line;        foreach ($headers as $key => $value) {            $request_headers[$key] = $value;        }        return $request_headers;    }    /**     * Parse Response     *     * @access private     * @param  $response_headers     * @param  $raw_response     *     * @return mixed     *   Provided the content-type is determined to be json or xml:     *     Returns stdClass object when the default json decoder is used and the content-type is json.     *     Returns SimpleXMLElement object when the default xml decoder is used and the content-type is xml.     */    private function parseResponse($response_headers, $raw_response)    {        $response = $raw_response;        if (isset($response_headers['Content-Type'])) {            if (preg_match($this->jsonPattern, $response_headers['Content-Type'])) {                $json_decoder = $this->jsonDecoder;                if (is_callable($json_decoder)) {                    $response = $json_decoder($response);                }            } elseif (preg_match($this->xmlPattern, $response_headers['Content-Type'])) {                $xml_decoder = $this->xmlDecoder;                if (is_callable($xml_decoder)) {                    $response = $xml_decoder($response);                }            } else {                $decoder = $this->defaultDecoder;                if (is_callable($decoder)) {                    $response = $decoder($response);                }            }        }        return $response;    }    /**     * Parse Response Headers     *     * @access private     * @param  $raw_response_headers     *     * @return array     */    private function parseResponseHeaders($raw_response_headers)    {        $response_header_array = explode("\r\n\r\n", $raw_response_headers);        $response_header  = '';        for ($i = count($response_header_array) - 1; $i >= 0; $i--) {            if (stripos($response_header_array[$i], 'HTTP/') === 0) {                $response_header = $response_header_array[$i];                break;            }        }        $response_headers = new CaseInsensitiveArrayService();        list($first_line, $headers) = $this->parseHeaders($response_header);        $response_headers['Status-Line'] = $first_line;        foreach ($headers as $key => $value) {            $response_headers[$key] = $value;        }        return $response_headers;    }    /**     * Is Array Assoc     *     * @access public     * @param  $array     *     * @return boolean     */    public static function is_array_assoc($array)    {        return (bool)count(array_filter(array_keys($array), 'is_string'));    }    /**     * Is Array Multidim     *     * @access public     * @param  $array     *     * @return boolean     */    public static function is_array_multidim($array)    {        if (!is_array($array)) {            return false;        }        return (bool)count(array_filter($array, 'is_array'));    }    /**     * Array Flatten Multidim     *     * @access public     * @param  $array     * @param  $prefix     *     * @return array     */    public static function array_flatten_multidim($array, $prefix = false)    {        $return = array();        if (is_array($array) || is_object($array)) {            if (empty($array)) {                $return[$prefix] = '';            } else {                foreach ($array as $key => $value) {                    if (is_scalar($value)) {                        if ($prefix) {                            $return[$prefix . '[' . $key . ']'] = $value;                        } else {                            $return[$key] = $value;                        }                    } else {                        if ($value instanceof \CURLFile) {                            $return[$key] = $value;                        } else {                            $return = array_merge(                                $return, self::array_flatten_multidim(                                $value, $prefix ? $prefix . '[' . $key . ']' : $key));                        }                    }                }            }        } elseif ($array === null) {            $return[$prefix] = $array;        }        return $return;    }}
 |