├── .github └── FUNDING.yml ├── hasil ├── json │ └── readme.txt └── html │ └── tableToExcel.js ├── composer.json ├── images ├── shopee_scrape_xls.png ├── shopee_scrape_html.png └── shopee_scrape_terminal.png ├── vendor ├── autoload.php ├── composer │ ├── autoload_classmap.php │ ├── autoload_namespaces.php │ ├── autoload_psr4.php │ ├── autoload_static.php │ ├── LICENSE │ ├── autoload_real.php │ ├── installed.json │ └── ClassLoader.php └── php-curl-class │ └── php-curl-class │ ├── composer.json │ ├── CHANGELOG.md │ ├── LICENSE │ ├── src │ └── Curl │ │ ├── Encoder.php │ │ ├── Decoder.php │ │ ├── StringUtil.php │ │ ├── ArrayUtil.php │ │ ├── CaseInsensitiveArray.php │ │ ├── Url.php │ │ ├── MultiCurl.php │ │ └── Curl.php │ ├── SECURITY.md │ └── README.md ├── composer.lock ├── modules └── function.php ├── run.php └── README.md /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | custom : paypal.me/wahyuarifpurnomo 2 | -------------------------------------------------------------------------------- /hasil/json/readme.txt: -------------------------------------------------------------------------------- 1 | Kamu bisa menggunakan file .json ini sebagai API. -------------------------------------------------------------------------------- /composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "require": { 3 | "php-curl-class/php-curl-class": "^8.6" 4 | } 5 | } 6 | -------------------------------------------------------------------------------- /images/shopee_scrape_xls.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/warifp/Shopee-Scrape/HEAD/images/shopee_scrape_xls.png -------------------------------------------------------------------------------- /images/shopee_scrape_html.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/warifp/Shopee-Scrape/HEAD/images/shopee_scrape_html.png -------------------------------------------------------------------------------- /images/shopee_scrape_terminal.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/warifp/Shopee-Scrape/HEAD/images/shopee_scrape_terminal.png -------------------------------------------------------------------------------- /vendor/autoload.php: -------------------------------------------------------------------------------- 1 | array($vendorDir . '/php-curl-class/php-curl-class/src/Curl'), 10 | ); 11 | -------------------------------------------------------------------------------- /vendor/composer/autoload_static.php: -------------------------------------------------------------------------------- 1 | 11 | array ( 12 | 'Curl\\' => 5, 13 | ), 14 | ); 15 | 16 | public static $prefixDirsPsr4 = array ( 17 | 'Curl\\' => 18 | array ( 19 | 0 => __DIR__ . '/..' . '/php-curl-class/php-curl-class/src/Curl', 20 | ), 21 | ); 22 | 23 | public static function getInitializer(ClassLoader $loader) 24 | { 25 | return \Closure::bind(function () use ($loader) { 26 | $loader->prefixLengthsPsr4 = ComposerStaticInit816a2c7879010f445504109bf556af4b::$prefixLengthsPsr4; 27 | $loader->prefixDirsPsr4 = ComposerStaticInit816a2c7879010f445504109bf556af4b::$prefixDirsPsr4; 28 | 29 | }, null, ClassLoader::class); 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /hasil/html/tableToExcel.js: -------------------------------------------------------------------------------- 1 | var tableToExcel = (function() { 2 | var uri = 'data:application/vnd.ms-excel;base64,' 3 | , template = '{table}
' 4 | , base64 = function(s) { return window.btoa(unescape(encodeURIComponent(s))) } 5 | , format = function(s, c) { return s.replace(/{(\w+)}/g, function(m, p) { return c[p]; }) } 6 | return function(table, name) { 7 | if (!table.nodeType) table = document.getElementById(table) 8 | var ctx = {worksheet: name || 'Worksheet', table: table.innerHTML} 9 | window.location.href = uri + base64(format(template, ctx)) 10 | } 11 | })() -------------------------------------------------------------------------------- /vendor/php-curl-class/php-curl-class/composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "php-curl-class/php-curl-class", 3 | "description": "PHP Curl Class makes it easy to send HTTP requests and integrate with web APIs.", 4 | "homepage": "https://github.com/php-curl-class/php-curl-class", 5 | "license": "Unlicense", 6 | "keywords": [ 7 | "php", "curl", "class", "api", "api-client", "client", "framework", "http", "http-client", "http-proxy", "json", 8 | "php-curl", "php-curl-library", "proxy", "requests", "restful", "web-scraper", "web-scraping", "web-service", 9 | "xml" 10 | ], 11 | "authors": [ 12 | { 13 | "name": "Zach Borboa" 14 | } 15 | ], 16 | "require": { 17 | "php": ">=5.3", 18 | "ext-curl": "*" 19 | }, 20 | "require-dev": { 21 | "ext-gd": "*", 22 | "phpunit/phpunit": "*", 23 | "squizlabs/php_codesniffer": "*" 24 | }, 25 | "suggest": { 26 | "ext-mbstring": "*" 27 | }, 28 | "autoload": { 29 | "psr-4": { 30 | "Curl\\": "src/Curl/" 31 | } 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /vendor/composer/LICENSE: -------------------------------------------------------------------------------- 1 | 2 | Copyright (c) Nils Adermann, Jordi Boggiano 3 | 4 | Permission is hereby granted, free of charge, to any person obtaining a copy 5 | of this software and associated documentation files (the "Software"), to deal 6 | in the Software without restriction, including without limitation the rights 7 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | copies of the Software, and to permit persons to whom the Software is furnished 9 | to do so, subject to the following conditions: 10 | 11 | The above copyright notice and this permission notice shall be included in all 12 | copies or substantial portions of the Software. 13 | 14 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 20 | THE SOFTWARE. 21 | 22 | -------------------------------------------------------------------------------- /vendor/php-curl-class/php-curl-class/CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Change Log 2 | 3 | PHP Curl Class uses semantic versioning with version numbers written as `MAJOR.MINOR.PATCH`. You may safely update 4 | `MINOR` and `PATCH` version changes. It is recommended to review `MAJOR` changes prior to upgrade as there may be 5 | backwards-incompatible changes that will affect existing usage. 6 | 7 | ### Changes 8 | 9 | (TODO: Add changes for next `MAJOR` version release.) 10 | 11 | ### Manual Review 12 | 13 | Manually view changes on the [comparison page](https://github.com/php-curl-class/php-curl-class/compare/). For example, 14 | visit [7.4.0...8.0.0](https://github.com/php-curl-class/php-curl-class/compare/7.4.0...8.0.0) to compare the changes for 15 | the `MAJOR` upgrade from 7.4.0 to 8.0.0. Comparing against `HEAD` is also possible using the `tag...HEAD` syntax 16 | ([8.3.0...HEAD](https://github.com/php-curl-class/php-curl-class/compare/8.3.0...HEAD)). 17 | 18 | View the log between releases: 19 | 20 | $ git fetch --tags 21 | $ git log 7.4.0...8.0.0 22 | 23 | View the code changes between releases: 24 | 25 | $ git fetch --tags 26 | $ git diff 7.4.0...8.0.0 27 | 28 | View only the source log and code changes between releases: 29 | 30 | $ git log 7.4.0...8.0.0 "src/" 31 | $ git diff 7.4.0...8.0.0 "src/" 32 | -------------------------------------------------------------------------------- /vendor/php-curl-class/php-curl-class/LICENSE: -------------------------------------------------------------------------------- 1 | This is free and unencumbered software released into the public domain. 2 | 3 | Anyone is free to copy, modify, publish, use, compile, sell, or 4 | distribute this software, either in source code form or as a compiled 5 | binary, for any purpose, commercial or non-commercial, and by any 6 | means. 7 | 8 | In jurisdictions that recognize copyright laws, the author or authors 9 | of this software dedicate any and all copyright interest in the 10 | software to the public domain. We make this dedication for the benefit 11 | of the public at large and to the detriment of our heirs and 12 | successors. We intend this dedication to be an overt act of 13 | relinquishment in perpetuity of all present and future rights to this 14 | software under copyright law. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 17 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 18 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 19 | IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR 20 | OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, 21 | ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR 22 | OTHER DEALINGS IN THE SOFTWARE. 23 | 24 | For more information, please refer to 25 | -------------------------------------------------------------------------------- /vendor/php-curl-class/php-curl-class/src/Curl/Encoder.php: -------------------------------------------------------------------------------- 1 | = 50600 && !defined('HHVM_VERSION') && (!function_exists('zend_loader_file_encoded') || !zend_loader_file_encoded()); 27 | if ($useStaticLoader) { 28 | require_once __DIR__ . '/autoload_static.php'; 29 | 30 | call_user_func(\Composer\Autoload\ComposerStaticInit816a2c7879010f445504109bf556af4b::getInitializer($loader)); 31 | } else { 32 | $map = require __DIR__ . '/autoload_namespaces.php'; 33 | foreach ($map as $namespace => $path) { 34 | $loader->set($namespace, $path); 35 | } 36 | 37 | $map = require __DIR__ . '/autoload_psr4.php'; 38 | foreach ($map as $namespace => $path) { 39 | $loader->setPsr4($namespace, $path); 40 | } 41 | 42 | $classMap = require __DIR__ . '/autoload_classmap.php'; 43 | if ($classMap) { 44 | $loader->addClassMap($classMap); 45 | } 46 | } 47 | 48 | $loader->register(true); 49 | 50 | return $loader; 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /vendor/php-curl-class/php-curl-class/src/Curl/StringUtil.php: -------------------------------------------------------------------------------- 1 | =5.3" 20 | }, 21 | "require-dev": { 22 | "ext-gd": "*", 23 | "phpunit/phpunit": "*", 24 | "squizlabs/php_codesniffer": "*" 25 | }, 26 | "suggest": { 27 | "ext-mbstring": "*" 28 | }, 29 | "time": "2019-08-05T04:18:26+00:00", 30 | "type": "library", 31 | "installation-source": "dist", 32 | "autoload": { 33 | "psr-4": { 34 | "Curl\\": "src/Curl/" 35 | } 36 | }, 37 | "notification-url": "https://packagist.org/downloads/", 38 | "license": [ 39 | "Unlicense" 40 | ], 41 | "authors": [ 42 | { 43 | "name": "Zach Borboa" 44 | } 45 | ], 46 | "description": "PHP Curl Class makes it easy to send HTTP requests and integrate with web APIs.", 47 | "homepage": "https://github.com/php-curl-class/php-curl-class", 48 | "keywords": [ 49 | "API-Client", 50 | "api", 51 | "class", 52 | "client", 53 | "curl", 54 | "framework", 55 | "http", 56 | "http-client", 57 | "http-proxy", 58 | "json", 59 | "php", 60 | "php-curl", 61 | "php-curl-library", 62 | "proxy", 63 | "requests", 64 | "restful", 65 | "web-scraper", 66 | "web-scraping ", 67 | "web-service", 68 | "xml" 69 | ] 70 | } 71 | ] 72 | -------------------------------------------------------------------------------- /composer.lock: -------------------------------------------------------------------------------- 1 | { 2 | "_readme": [ 3 | "This file locks the dependencies of your project to a known state", 4 | "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", 5 | "This file is @generated automatically" 6 | ], 7 | "content-hash": "6d8590b70e9ac09ad016b24c69ccee71", 8 | "packages": [ 9 | { 10 | "name": "php-curl-class/php-curl-class", 11 | "version": "8.6.1", 12 | "source": { 13 | "type": "git", 14 | "url": "https://github.com/php-curl-class/php-curl-class.git", 15 | "reference": "a418962c4385aba6b97d2d57d65a0d405b514cf7" 16 | }, 17 | "dist": { 18 | "type": "zip", 19 | "url": "https://api.github.com/repos/php-curl-class/php-curl-class/zipball/a418962c4385aba6b97d2d57d65a0d405b514cf7", 20 | "reference": "a418962c4385aba6b97d2d57d65a0d405b514cf7", 21 | "shasum": "" 22 | }, 23 | "require": { 24 | "ext-curl": "*", 25 | "php": ">=5.3" 26 | }, 27 | "require-dev": { 28 | "ext-gd": "*", 29 | "phpunit/phpunit": "*", 30 | "squizlabs/php_codesniffer": "*" 31 | }, 32 | "suggest": { 33 | "ext-mbstring": "*" 34 | }, 35 | "type": "library", 36 | "autoload": { 37 | "psr-4": { 38 | "Curl\\": "src/Curl/" 39 | } 40 | }, 41 | "notification-url": "https://packagist.org/downloads/", 42 | "license": [ 43 | "Unlicense" 44 | ], 45 | "authors": [ 46 | { 47 | "name": "Zach Borboa" 48 | } 49 | ], 50 | "description": "PHP Curl Class makes it easy to send HTTP requests and integrate with web APIs.", 51 | "homepage": "https://github.com/php-curl-class/php-curl-class", 52 | "keywords": [ 53 | "API-Client", 54 | "api", 55 | "class", 56 | "client", 57 | "curl", 58 | "framework", 59 | "http", 60 | "http-client", 61 | "http-proxy", 62 | "json", 63 | "php", 64 | "php-curl", 65 | "php-curl-library", 66 | "proxy", 67 | "requests", 68 | "restful", 69 | "web-scraper", 70 | "web-scraping ", 71 | "web-service", 72 | "xml" 73 | ], 74 | "time": "2019-08-05T04:18:26+00:00" 75 | } 76 | ], 77 | "packages-dev": [], 78 | "aliases": [], 79 | "minimum-stability": "stable", 80 | "stability-flags": [], 81 | "prefer-stable": false, 82 | "prefer-lowest": false, 83 | "platform": [], 84 | "platform-dev": [] 85 | } 86 | -------------------------------------------------------------------------------- /modules/function.php: -------------------------------------------------------------------------------- 1 | get('https://shopee.co.id/api/v2/search_items/?by=relevancy&keyword=' . $search . '&limit=' . $totalSearch . '&newest=0&order=desc&page_type=search&version=2'); 15 | 16 | if ($curl->error) { 17 | echo 'Error: ' . $curl->errorCode . ': ' . $curl->errorMessage . "\n"; 18 | } else { 19 | //echo 'Response:' . "\n"; 20 | return $curl->response; 21 | } 22 | } 23 | 24 | function getItem($curl, $itemID, $shopID) { 25 | $curl->get('https://shopee.co.id/api/v2/item/get?itemid=' . $itemID . '&shopid=' . $shopID); 26 | 27 | if ($curl->error) { 28 | echo 'Error: ' . $curl->errorCode . ': ' . $curl->errorMessage . "\n"; 29 | } else { 30 | //echo 'Response:' . "\n"; 31 | return $curl->response; 32 | } 33 | } 34 | 35 | function htmlConverter() { 36 | function printHtml($value) 37 | { 38 | $data = ''; 39 | $data .= '' . $value . ''; 40 | 41 | return $data; 42 | } 43 | function printImage($value) 44 | { 45 | $data = ''; 46 | $data .=''; 47 | 48 | return $data; 49 | } 50 | 51 | $data = file_get_contents('hasil/json/results.json'); 52 | $data = json_decode($data, true); 53 | 54 | $date = date("Y-m-d"); 55 | $exportDetail = "'table', '" . $date . "'"; 56 | 57 | echo ""; 58 | echo ""; 59 | echo ''; 60 | echo ""; 61 | echo ""; 62 | echo ""; 63 | echo ''; 64 | echo ""; 65 | echo ""; 66 | echo ""; 67 | echo ""; 68 | echo ""; 69 | echo ""; 70 | echo ""; 71 | foreach ($data["data"] as $key => $value) { 72 | echo "\n"; 73 | echo printHtml($data['data'][$key]['no']) . "\n"; 74 | echo printHtml($data['data'][$key]['nama']) . "\n"; 75 | echo printHtml($data['data'][$key]['harga']) . "\n"; 76 | echo printHtml($data['data'][$key]['lokasi']) . "\n"; 77 | echo printImage($data['data'][$key]['foto']) . "\n"; 78 | echo printHtml($data['data'][$key]['status']) . "\n"; 79 | echo "\n"; 80 | } 81 | echo "
NoNamaHargaLokasiFotoStatus
"; 82 | echo "
"; 83 | echo ''; 84 | echo "
"; 85 | echo ''; 90 | } 91 | /** 92 | * Author : Wahyu Arif Purnomo 93 | * Name : Shopee Scrape 94 | * Version : 1.0 95 | * Update : 04 Desember 2019 96 | * 97 | * If you are a reliable programmer or the best developer, please don't change anything. 98 | * If you want to be appreciated by others, then don't change anything in this script. 99 | * Please respect me for making this tool from the beginning. 100 | */ -------------------------------------------------------------------------------- /run.php: -------------------------------------------------------------------------------- 1 | error == null) { 45 | $no = 0; 46 | for ($x = 0; $x < $totalSearch; $x++) { 47 | $no++; 48 | $itemID = $getSearch->items[$x]->itemid; 49 | $shopID = $getSearch->items[$x]->shopid; 50 | 51 | $getItem = getItem($curl, $itemID, $shopID); 52 | $nameItem = $getItem->item->name; 53 | $priceItem = $getItem->item->price; 54 | //$diskonItem = $getItem->item->discount; 55 | $statusItem = $getItem->item->item_status; 56 | $lokasiToko = $getItem->item->shop_location; 57 | $imageItem = 'https://cf.shopee.co.id/file/' . $getItem->item->image; 58 | 59 | if($statusItem == "normal") { 60 | $status = "Tersedia"; 61 | } else { 62 | $status = "Tidak Tersedia"; 63 | } 64 | echo $no . '. [' . $status . '] [' . $priceItem . '] [' . $nameItem . '] [' . $lokasiToko . '] [' . $imageItem . "] \n"; 65 | 66 | $export['data'][] = array( 67 | 'no' => $no, 68 | 'status' => $status, 69 | 'nama' => $nameItem, 70 | 'harga' => $priceItem, 71 | 'lokasi' => $lokasiToko, 72 | 'foto' => $imageItem, 73 | 'status' => $status 74 | ); 75 | //echo json_encode($export) . "\n"; 76 | if (($id = fopen('hasil/json/results.json', 'wb'))) { 77 | fwrite($id, json_encode($export)); 78 | fclose($id); 79 | } 80 | } 81 | } 82 | ob_start(); 83 | htmlConverter(); 84 | $htmlResults = ob_get_contents(); 85 | ob_end_clean(); 86 | file_put_contents("hasil/html/results.html", $htmlResults); 87 | 88 | echo "\n\e[0;32mSuccessfully scrape data from Shopee.\e[0m\n\n"; 89 | echo "\e[0;31mFile saved :\n"; 90 | echo "JSON : hasil/json/results.json\n"; 91 | echo "HTML : hasil/html/results.html\e[0m"; 92 | 93 | /** 94 | * Author : Wahyu Arif Purnomo 95 | * Name : Shopee Scrape 96 | * Version : 1.0 97 | * Update : 04 Desember 2019 98 | * 99 | * If you are a reliable programmer or the best developer, please don't change anything. 100 | * If you want to be appreciated by others, then don't change anything in this script. 101 | * Please respect me for making this tool from the beginning. 102 | */ 103 | ?> -------------------------------------------------------------------------------- /vendor/php-curl-class/php-curl-class/SECURITY.md: -------------------------------------------------------------------------------- 1 | # Security Considerations 2 | 3 | ### Url may point to system files 4 | 5 | * Don't blindly accept urls from users as they may point to system files. Curl supports many protocols including `FILE`. 6 | The following would show the contents of `file:///etc/passwd`. 7 | 8 | ```bash 9 | # Attacker. 10 | $ curl https://www.example.com/display_webpage.php?url=file%3A%2F%2F%2Fetc%2Fpasswd 11 | ``` 12 | 13 | ```php 14 | // display_webpage.php 15 | $url = $_GET['url']; // DANGER! 16 | $curl = new Curl(); 17 | $curl->get($url); 18 | echo $curl->response; 19 | ``` 20 | 21 | Safer: 22 | 23 | ```php 24 | function is_allowed_url($url, $allowed_url_schemes = array('http', 'https')) { 25 | $valid_url = filter_var($url, FILTER_VALIDATE_URL, FILTER_FLAG_SCHEME_REQUIRED | FILTER_FLAG_HOST_REQUIRED) !== false; 26 | if ($valid_url) { 27 | $scheme = parse_url($url, PHP_URL_SCHEME); 28 | return in_array($scheme, $allowed_url_schemes, true); 29 | } 30 | $valid_ip = filter_var($url, FILTER_VALIDATE_IP, FILTER_FLAG_NO_PRIV_RANGE | FILTER_FLAG_NO_RES_RANGE) !== false; 31 | return $valid_ip; 32 | } 33 | 34 | $url = $_GET['url']; 35 | if (!is_allowed_url($url)) { 36 | die('Unsafe url detected.'); 37 | } 38 | ``` 39 | 40 | ### Url may point to internal urls 41 | 42 | * Url may point to internal urls including those behind a firewall (e.g. http://192.168.0.1/ or ftp://192.168.0.1/). Use 43 | a whitelist to allow certain urls rather than a blacklist. 44 | 45 | ### Request data may refer to system files 46 | 47 | * Request data prefixed with the `@` character may have special interpretation and read from system files. 48 | 49 | ```bash 50 | # Attacker. 51 | $ curl https://www.example.com/upload_photo.php --data "photo=@/etc/passwd" 52 | ``` 53 | 54 | ```php 55 | // upload_photo.php 56 | $curl = new Curl(); 57 | $curl->post('http://www.anotherwebsite.com/', array( 58 | 'photo' => $_POST['photo'], // DANGER! 59 | )); 60 | ``` 61 | 62 | ### Unsafe response with redirection enabled 63 | 64 | * Requests with redirection enabled may return responses from unexpected sources. 65 | Downloading https://www.example.com/image.png may redirect and download https://www.evil.com/virus.exe 66 | 67 | ```php 68 | $curl = new Curl(); 69 | $curl->setOpt(CURLOPT_FOLLOWLOCATION, true); // DANGER! 70 | $curl->download('https://www.example.com/image.png', 'my_image.png'); 71 | ``` 72 | 73 | ```php 74 | $curl = new Curl(); 75 | $curl->setOpt(CURLOPT_FOLLOWLOCATION, true); // DANGER! 76 | $curl->get('https://www.example.com/image.png'); 77 | ``` 78 | 79 | ### Keep SSL protections enabled 80 | 81 | * Do not disable SSL protections. 82 | 83 | ```php 84 | curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false); // DANGER! 85 | curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false); // DANGER! 86 | ``` 87 | 88 | ### Prevent XML External Entity injection 89 | 90 | * Set the following when using the default PHP XML parser to prevent XML external entity injection. 91 | 92 | ```php 93 | libxml_disable_entity_loader(true); 94 | ``` 95 | 96 | ### Prevent PHP execution of library files 97 | 98 | PHP files in this library are not intended to be accessible by users browsing websites. Prevent direct access to library files by moving the library folder at least one level higher than the web root directory. Alternatively, configure the server to disable php file execution for all library files. 99 | 100 | #### For WordPress plugin developers 101 | 102 | WordPress plugin developers that wish to incorporate the PHP Curl Class library into their plugin, should take special care to include only the "core" library files. 103 | 104 | Do one of the following: 105 | 106 | Option 1. Download an official release from the [releases page](https://github.com/php-curl-class/php-curl-class/releases) and incorporate the files contained in the compressed file into the plugin. The releases include only the necessary php files for the library to function. 107 | 108 | Option 2. Manually copy only the [src/](https://github.com/php-curl-class/php-curl-class/tree/master/src) directory into your plugin. Be sure not to copy any other php files as they may be executable by users visiting the php files directly. 109 | -------------------------------------------------------------------------------- /vendor/php-curl-class/php-curl-class/src/Curl/ArrayUtil.php: -------------------------------------------------------------------------------- 1 | isArrayAssoc($array); 32 | } 33 | 34 | /** 35 | * Is Array Multidim 36 | * 37 | * @access public 38 | * @param $array 39 | * 40 | * @return boolean 41 | */ 42 | public static function isArrayMultidim($array) 43 | { 44 | if (!is_array($array)) { 45 | return false; 46 | } 47 | 48 | return (bool)count(array_filter($array, 'is_array')); 49 | } 50 | 51 | /** 52 | * Is Array Multidim 53 | * 54 | * @deprecated Use ArrayUtil::isArrayMultidim(). 55 | * @access public 56 | * @param $array 57 | * 58 | * @return boolean 59 | */ 60 | public static function is_array_multidim($array) 61 | { 62 | return $this->isArrayMultidim($array); 63 | } 64 | 65 | /** 66 | * Array Flatten Multidim 67 | * 68 | * @access public 69 | * @param $array 70 | * @param $prefix 71 | * 72 | * @return array 73 | */ 74 | public static function arrayFlattenMultidim($array, $prefix = false) 75 | { 76 | $return = array(); 77 | if (is_array($array) || is_object($array)) { 78 | if (empty($array)) { 79 | $return[$prefix] = ''; 80 | } else { 81 | foreach ($array as $key => $value) { 82 | if (is_scalar($value)) { 83 | if ($prefix) { 84 | $return[$prefix . '[' . $key . ']'] = $value; 85 | } else { 86 | $return[$key] = $value; 87 | } 88 | } else { 89 | if ($value instanceof \CURLFile) { 90 | $return[$key] = $value; 91 | } else { 92 | $return = array_merge( 93 | $return, 94 | self::arrayFlattenMultidim( 95 | $value, 96 | $prefix ? $prefix . '[' . $key . ']' : $key 97 | ) 98 | ); 99 | } 100 | } 101 | } 102 | } 103 | } elseif ($array === null) { 104 | $return[$prefix] = $array; 105 | } 106 | return $return; 107 | } 108 | 109 | /** 110 | * Array Flatten Multidim 111 | * 112 | * @deprecated Use ArrayUtil::arrayFlattenMultidim(). 113 | * @access public 114 | * @param $array 115 | * @param $prefix 116 | * 117 | * @return array 118 | */ 119 | public static function array_flatten_multidim($array, $prefix = false) 120 | { 121 | return $this->arrayFlattenMultidim($array, $prefix); 122 | } 123 | 124 | /** 125 | * Array Random 126 | * 127 | * @access public 128 | * @param $array 129 | * 130 | * @return mixed 131 | */ 132 | public static function arrayRandom($array) 133 | { 134 | return $array[mt_rand(0, count($array) - 1)]; 135 | } 136 | 137 | /** 138 | * Array Random 139 | * 140 | * @deprecated Use ArrayUtil::arrayRandom(). 141 | * @access public 142 | * @param $array 143 | * 144 | * @return mixed 145 | */ 146 | public static function array_random($array) 147 | { 148 | return $this->arrayRandom($array); 149 | } 150 | } 151 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 |

2 | SHOPEE SCRAPE 3 |

4 |

5 | Shopee Scrape is a tool that functions to collect data - the data needed, such as finding data from photos, prices, names, store locations and others.
6 |

7 |

8 | Last Commit 9 | Release 10 | Contributors 11 | Language 12 | TopLanguage 13 |
14 | Search 15 | Repository Size 16 | Forks 17 | Star 18 |

19 | 20 |

21 | Made with ❤️ by Wahyu Arif Purnomo 22 |

23 | 24 |

25 |
Terminal Shopee Scrape
26 | Terminal Shopee Scrape 27 |

28 | 29 |

30 |
HTML Shopee Scrape
31 | HTML Result Shopee Scrape 32 |

33 | 34 |

35 |
Export to Excel
36 | Export to Excel 37 |

38 | 39 | ## Features in tools 40 | 41 | | Name | Status | Information | 42 | | ---------------------------------- | ------------------ | -------------------------------------------------- | 43 | | Nama Barang | :white_check_mark: | nama lengkap barang [judul] | 44 | | Harga Barang | :white_check_mark: | harga barang terkini [price] | 45 | | Status Stock | :white_check_mark: | status stock tersedia atau tidak tersedia | 46 | | Foto Barang | :white_check_mark: | foto barang artikel | 47 | | Lokasi Toko | :white_check_mark: | lokasi toko berdasarkan informasi toko | 48 | 49 | You can use your search results with the "results.json" API file stored in "results / json / results.json". 50 | 51 | 52 | ## Version 53 | 54 | Version 1.0 : 55 | 56 | Changelog : 57 | 58 | - Add tools 59 | 60 | ## Installation 61 | 62 | ### Windows or Linux 63 | You can download the latest composer in [here](https://getcomposer.org/download/). 64 | 65 | git clone https://github.com/warifp/Shopee-Scrape 66 | 67 | composer update 68 | 69 | ### Termux 70 | 71 | pkg install php 72 | 73 | pkg update 74 | 75 | git clone https://github.com/warifp/Shopee-Scrape 76 | 77 | 78 | ## Requirements for using this tool 79 | 80 | We need several requirements to use this tool to run smoothly. 81 | 82 | ##### Linux 83 | 84 | ![PHP 7.X](https://img.shields.io/badge/PHP-7.X-success.svg "PHP 7.X") 85 | 86 | ##### Windows 87 | 88 | ![PHP CURL](https://img.shields.io/badge/XAMPP-7.3.5-success.svg "XAMPP 7.X") 89 | 90 | ## Usage 91 | 92 | Enough to execute the command : 93 | 94 | php run.php 95 | 96 | 97 | and don't forget to ask at [issue page](https://github.com/warifp/Shopee-Scrape/issues) 98 | If you have additional information, you can make it on the [issue page](https://github.com/warifp/Shopee-Scrape/issues). 99 | 100 | ## Thanks 101 | 102 | Thank you for all. 103 | 104 | 1. ASCII Art Generator : [TAAG](http://patorjk.com/software/taag). 105 | 106 | ## Donation 107 | 108 | If you want to buy my coffee, you can send payments Paypal. 109 | 110 | [![Donate](https://img.shields.io/badge/Donate-PayPal-green.svg)](https://paypal.me/wahyuarifpurnomo) 111 | 112 | ## Disclaimer 113 | 114 | This is an open source for everyone, you may redistribute, modify, use patents and use privately without any obligation to redistribute. but it should be noted to include the source code of the library that was modified (not the source code of the entire program), include the license, include the original copyright of the author (warifp), and include any changes made (if modified). Users do not have the right to sue the creator when there is damage to the software or even demand if there is a problem caused by the makers of this tool. because every risk is caused by the user risk itself. 115 | -------------------------------------------------------------------------------- /vendor/php-curl-class/php-curl-class/src/Curl/CaseInsensitiveArray.php: -------------------------------------------------------------------------------- 1 | $value) { 46 | $this->offsetSet($key, $value); 47 | } 48 | } 49 | } 50 | 51 | /** 52 | * Offset Set 53 | * 54 | * Set data at a specified offset. Converts the offset to lowercase, and 55 | * stores the case-sensitive offset and the data at the lowercase indexes in 56 | * $this->keys and @this->data. 57 | * 58 | * @see https://secure.php.net/manual/en/arrayaccess.offsetset.php 59 | * 60 | * @param string $offset The offset to store the data at (case-insensitive). 61 | * @param mixed $value The data to store at the specified offset. 62 | * 63 | * @return void 64 | * 65 | * @access public 66 | */ 67 | public function offsetSet($offset, $value) 68 | { 69 | if ($offset === null) { 70 | $this->data[] = $value; 71 | } else { 72 | $offsetlower = strtolower($offset); 73 | $this->data[$offsetlower] = $value; 74 | $this->keys[$offsetlower] = $offset; 75 | } 76 | } 77 | 78 | /** 79 | * Offset Exists 80 | * 81 | * Checks if the offset exists in data storage. The index is looked up with 82 | * the lowercase version of the provided offset. 83 | * 84 | * @see https://secure.php.net/manual/en/arrayaccess.offsetexists.php 85 | * 86 | * @param string $offset Offset to check 87 | * 88 | * @return bool If the offset exists. 89 | * 90 | * @access public 91 | */ 92 | public function offsetExists($offset) 93 | { 94 | return (bool) array_key_exists(strtolower($offset), $this->data); 95 | } 96 | 97 | /** 98 | * Offset Unset 99 | * 100 | * Unsets the specified offset. Converts the provided offset to lowercase, 101 | * and unsets the case-sensitive key, as well as the stored data. 102 | * 103 | * @see https://secure.php.net/manual/en/arrayaccess.offsetunset.php 104 | * 105 | * @param string $offset The offset to unset. 106 | * 107 | * @return void 108 | * 109 | * @access public 110 | */ 111 | public function offsetUnset($offset) 112 | { 113 | $offsetlower = strtolower($offset); 114 | unset($this->data[$offsetlower]); 115 | unset($this->keys[$offsetlower]); 116 | } 117 | 118 | /** 119 | * Offset Get 120 | * 121 | * Return the stored data at the provided offset. The offset is converted to 122 | * lowercase and the lookup is done on the data store directly. 123 | * 124 | * @see https://secure.php.net/manual/en/arrayaccess.offsetget.php 125 | * 126 | * @param string $offset Offset to lookup. 127 | * 128 | * @return mixed The data stored at the offset. 129 | * 130 | * @access public 131 | */ 132 | public function offsetGet($offset) 133 | { 134 | $offsetlower = strtolower($offset); 135 | return isset($this->data[$offsetlower]) ? $this->data[$offsetlower] : null; 136 | } 137 | 138 | /** 139 | * Count 140 | * 141 | * @see https://secure.php.net/manual/en/countable.count.php 142 | * 143 | * @param void 144 | * 145 | * @return int The number of elements stored in the array. 146 | * 147 | * @access public 148 | */ 149 | public function count() 150 | { 151 | return (int) count($this->data); 152 | } 153 | 154 | /** 155 | * Current 156 | * 157 | * @see https://secure.php.net/manual/en/iterator.current.php 158 | * 159 | * @param void 160 | * 161 | * @return mixed Data at the current position. 162 | * 163 | * @access public 164 | */ 165 | public function current() 166 | { 167 | return current($this->data); 168 | } 169 | 170 | /** 171 | * Next 172 | * 173 | * @see https://secure.php.net/manual/en/iterator.next.php 174 | * 175 | * @param void 176 | * 177 | * @return void 178 | * 179 | * @access public 180 | */ 181 | public function next() 182 | { 183 | next($this->data); 184 | } 185 | 186 | /** 187 | * Key 188 | * 189 | * @see https://secure.php.net/manual/en/iterator.key.php 190 | * 191 | * @param void 192 | * 193 | * @return mixed Case-sensitive key at current position. 194 | * 195 | * @access public 196 | */ 197 | public function key() 198 | { 199 | $key = key($this->data); 200 | return isset($this->keys[$key]) ? $this->keys[$key] : $key; 201 | } 202 | 203 | /** 204 | * Valid 205 | * 206 | * @see https://secure.php.net/manual/en/iterator.valid.php 207 | * 208 | * @return bool If the current position is valid. 209 | * 210 | * @access public 211 | */ 212 | public function valid() 213 | { 214 | return (bool) (key($this->data) !== null); 215 | } 216 | 217 | /** 218 | * Rewind 219 | * 220 | * @see https://secure.php.net/manual/en/iterator.rewind.php 221 | * 222 | * @param void 223 | * 224 | * @return void 225 | * 226 | * @access public 227 | */ 228 | public function rewind() 229 | { 230 | reset($this->data); 231 | } 232 | } 233 | -------------------------------------------------------------------------------- /vendor/php-curl-class/php-curl-class/src/Curl/Url.php: -------------------------------------------------------------------------------- 1 | baseUrl = $base_url; 15 | $this->relativeUrl = $relative_url; 16 | } 17 | 18 | public function __toString() 19 | { 20 | return $this->absolutizeUrl(); 21 | } 22 | 23 | /** 24 | * Remove dot segments. 25 | * 26 | * Interpret and remove the special "." and ".." path segments from a referenced path. 27 | */ 28 | public static function removeDotSegments($input) 29 | { 30 | // 1. The input buffer is initialized with the now-appended path 31 | // components and the output buffer is initialized to the empty 32 | // string. 33 | $output = ''; 34 | 35 | // 2. While the input buffer is not empty, loop as follows: 36 | while (!empty($input)) { 37 | // A. If the input buffer begins with a prefix of "../" or "./", 38 | // then remove that prefix from the input buffer; otherwise, 39 | if (StringUtil::startsWith($input, '../')) { 40 | $input = substr($input, 3); 41 | } elseif (StringUtil::startsWith($input, './')) { 42 | $input = substr($input, 2); 43 | 44 | // B. if the input buffer begins with a prefix of "/./" or "/.", 45 | // where "." is a complete path segment, then replace that 46 | // prefix with "/" in the input buffer; otherwise, 47 | } elseif (StringUtil::startsWith($input, '/./')) { 48 | $input = substr($input, 2); 49 | } elseif ($input === '/.') { 50 | $input = '/'; 51 | 52 | // C. if the input buffer begins with a prefix of "/../" or "/..", 53 | // where ".." is a complete path segment, then replace that 54 | // prefix with "/" in the input buffer and remove the last 55 | // segment and its preceding "/" (if any) from the output 56 | // buffer; otherwise, 57 | } elseif (StringUtil::startsWith($input, '/../')) { 58 | $input = substr($input, 3); 59 | $output = substr_replace($output, '', StringUtil::reversePosition($output, '/')); 60 | } elseif ($input === '/..') { 61 | $input = '/'; 62 | $output = substr_replace($output, '', StringUtil::reversePosition($output, '/')); 63 | 64 | // D. if the input buffer consists only of "." or "..", then remove 65 | // that from the input buffer; otherwise, 66 | } elseif ($input === '.' || $input === '..') { 67 | $input = ''; 68 | 69 | // E. move the first path segment in the input buffer to the end of 70 | // the output buffer, including the initial "/" character (if 71 | // any) and any subsequent characters up to, but not including, 72 | // the next "/" character or the end of the input buffer. 73 | } elseif (!(($pos = StringUtil::position($input, '/', 1)) === false)) { 74 | $output .= substr($input, 0, $pos); 75 | $input = substr_replace($input, '', 0, $pos); 76 | } else { 77 | $output .= $input; 78 | $input = ''; 79 | } 80 | } 81 | 82 | // 3. Finally, the output buffer is returned as the result of 83 | // remove_dot_segments. 84 | return $output . $input; 85 | } 86 | 87 | /** 88 | * Absolutize url. 89 | * 90 | * Combine the base and relative url into an absolute url. 91 | */ 92 | private function absolutizeUrl() 93 | { 94 | $b = $this->parseUrl($this->baseUrl); 95 | if (!isset($b['path'])) { 96 | $b['path'] = '/'; 97 | } 98 | if ($this->relativeUrl === null) { 99 | return $this->unparseUrl($b); 100 | } 101 | $r = $this->parseUrl($this->relativeUrl); 102 | $r['authorized'] = isset($r['scheme']) || isset($r['host']) || isset($r['port']) 103 | || isset($r['user']) || isset($r['pass']); 104 | $target = array(); 105 | if (isset($r['scheme'])) { 106 | $target['scheme'] = $r['scheme']; 107 | $target['host'] = isset($r['host']) ? $r['host'] : null; 108 | $target['port'] = isset($r['port']) ? $r['port'] : null; 109 | $target['user'] = isset($r['user']) ? $r['user'] : null; 110 | $target['pass'] = isset($r['pass']) ? $r['pass'] : null; 111 | $target['path'] = isset($r['path']) ? self::removeDotSegments($r['path']) : null; 112 | $target['query'] = isset($r['query']) ? $r['query'] : null; 113 | } else { 114 | $target['scheme'] = isset($b['scheme']) ? $b['scheme'] : null; 115 | if ($r['authorized']) { 116 | $target['host'] = isset($r['host']) ? $r['host'] : null; 117 | $target['port'] = isset($r['port']) ? $r['port'] : null; 118 | $target['user'] = isset($r['user']) ? $r['user'] : null; 119 | $target['pass'] = isset($r['pass']) ? $r['pass'] : null; 120 | $target['path'] = isset($r['path']) ? self::removeDotSegments($r['path']) : null; 121 | $target['query'] = isset($r['query']) ? $r['query'] : null; 122 | } else { 123 | $target['host'] = isset($b['host']) ? $b['host'] : null; 124 | $target['port'] = isset($b['port']) ? $b['port'] : null; 125 | $target['user'] = isset($b['user']) ? $b['user'] : null; 126 | $target['pass'] = isset($b['pass']) ? $b['pass'] : null; 127 | if (!isset($r['path']) || $r['path'] === '') { 128 | $target['path'] = $b['path']; 129 | $target['query'] = isset($r['query']) ? $r['query'] : (isset($b['query']) ? $b['query'] : null); 130 | } else { 131 | if (StringUtil::startsWith($r['path'], '/')) { 132 | $target['path'] = self::removeDotSegments($r['path']); 133 | } else { 134 | $base = StringUtil::characterReversePosition($b['path'], '/', true); 135 | if ($base === false) { 136 | $base = ''; 137 | } 138 | $target['path'] = self::removeDotSegments($base . '/' . $r['path']); 139 | } 140 | $target['query'] = isset($r['query']) ? $r['query'] : null; 141 | } 142 | } 143 | } 144 | if ($this->relativeUrl === '') { 145 | $target['fragment'] = isset($b['fragment']) ? $b['fragment'] : null; 146 | } else { 147 | $target['fragment'] = isset($r['fragment']) ? $r['fragment'] : null; 148 | } 149 | $absolutized_url = $this->unparseUrl($target); 150 | return $absolutized_url; 151 | } 152 | 153 | /** 154 | * Parse url. 155 | * 156 | * Parse url into components of a URI as specified by RFC 3986. 157 | */ 158 | private function parseUrl($url) 159 | { 160 | // ALPHA = A-Z / a-z 161 | $alpha = 'A-Za-z'; 162 | 163 | // DIGIT = 0-9 164 | $digit = '0-9'; 165 | 166 | // unreserved = ALPHA / DIGIT / "-" / "." / "_" / "~" 167 | $unreserved = $alpha . $digit . preg_quote('-._~'); 168 | 169 | // sub-delims = "!" / "$" / "&" / "'" / "(" / ")" 170 | // / "*" / "+" / "," / ";" / "=" / "#" 171 | $sub_delims = preg_quote('!$&\'()*+,;=#'); 172 | 173 | // HEXDIG = DIGIT / "A" / "B" / "C" / "D" / "E" / "F" 174 | $hexdig = $digit . 'A-F'; 175 | // "The uppercase hexadecimal digits 'A' through 'F' are equivalent to 176 | // the lowercase digits 'a' through 'f', respectively." 177 | $hexdig .= 'a-f'; 178 | 179 | $pattern = '/(?:[^' . $unreserved . $sub_delims . preg_quote(':@%/?', '/') . ']++|%(?![' . $hexdig . ']{2}))/'; 180 | $url = preg_replace_callback( 181 | $pattern, 182 | function ($matches) { 183 | return rawurlencode($matches[0]); 184 | }, 185 | $url 186 | ); 187 | return parse_url($url); 188 | } 189 | 190 | /** 191 | * Unparse url. 192 | * 193 | * Combine url components into a url. 194 | */ 195 | private function unparseUrl($parsed_url) 196 | { 197 | $scheme = isset($parsed_url['scheme']) ? $parsed_url['scheme'] . '://' : ''; 198 | $user = isset($parsed_url['user']) ? $parsed_url['user'] : ''; 199 | $pass = isset($parsed_url['pass']) ? ':' . $parsed_url['pass'] : ''; 200 | $pass = ($user || $pass) ? $pass . '@' : ''; 201 | $host = isset($parsed_url['host']) ? $parsed_url['host'] : ''; 202 | $port = isset($parsed_url['port']) ? ':' . $parsed_url['port'] : ''; 203 | $path = isset($parsed_url['path']) ? $parsed_url['path'] : ''; 204 | $query = isset($parsed_url['query']) ? '?' . $parsed_url['query'] : ''; 205 | $fragment = isset($parsed_url['fragment']) ? '#' . $parsed_url['fragment'] : ''; 206 | $unparsed_url = $scheme . $user . $pass . $host . $port . $path . $query . $fragment; 207 | return $unparsed_url; 208 | } 209 | } 210 | -------------------------------------------------------------------------------- /vendor/php-curl-class/php-curl-class/README.md: -------------------------------------------------------------------------------- 1 | # PHP Curl Class: HTTP requests made easy 2 | 3 | [![](https://img.shields.io/github/release/php-curl-class/php-curl-class.svg)](https://github.com/php-curl-class/php-curl-class/releases/) 4 | [![](https://img.shields.io/github/license/php-curl-class/php-curl-class.svg)](https://github.com/php-curl-class/php-curl-class/blob/master/LICENSE) 5 | [![](https://img.shields.io/travis/php-curl-class/php-curl-class.svg)](https://travis-ci.org/php-curl-class/php-curl-class/) 6 | [![](https://img.shields.io/packagist/dt/php-curl-class/php-curl-class.svg)](https://github.com/php-curl-class/php-curl-class/releases/) 7 | 8 | PHP Curl Class makes it easy to send HTTP requests and integrate with web APIs. 9 | 10 | ![PHP Curl Class screencast](www/img/screencast.gif) 11 | 12 | --- 13 | 14 | - [Installation](#installation) 15 | - [Requirements](#requirements) 16 | - [Quick Start and Examples](#quick-start-and-examples) 17 | - [Available Methods](#available-methods) 18 | - [Security](#security) 19 | - [Troubleshooting](#troubleshooting) 20 | - [Run Tests](#run-tests) 21 | - [Contribute](#contribute) 22 | 23 | --- 24 | 25 | ### Installation 26 | 27 | To install PHP Curl Class, simply: 28 | 29 | $ composer require php-curl-class/php-curl-class 30 | 31 | For latest commit version: 32 | 33 | $ composer require php-curl-class/php-curl-class @dev 34 | 35 | ### Requirements 36 | 37 | PHP Curl Class works with PHP 5.3, 5.4, 5.5, 5.6, 7.0, 7.1, 7.2, 7.3, and HHVM. 38 | 39 | ### Quick Start and Examples 40 | 41 | More examples are available under [/examples](https://github.com/php-curl-class/php-curl-class/tree/master/examples). 42 | 43 | ```php 44 | require __DIR__ . '/vendor/autoload.php'; 45 | 46 | use \Curl\Curl; 47 | 48 | $curl = new Curl(); 49 | $curl->get('https://www.example.com/'); 50 | 51 | if ($curl->error) { 52 | echo 'Error: ' . $curl->errorCode . ': ' . $curl->errorMessage . "\n"; 53 | } else { 54 | echo 'Response:' . "\n"; 55 | var_dump($curl->response); 56 | } 57 | ``` 58 | 59 | ```php 60 | // https://www.example.com/search?q=keyword 61 | $curl = new Curl(); 62 | $curl->get('https://www.example.com/search', array( 63 | 'q' => 'keyword', 64 | )); 65 | ``` 66 | 67 | ```php 68 | $curl = new Curl(); 69 | $curl->post('https://www.example.com/login/', array( 70 | 'username' => 'myusername', 71 | 'password' => 'mypassword', 72 | )); 73 | ``` 74 | 75 | ```php 76 | $curl = new Curl(); 77 | $curl->setBasicAuthentication('username', 'password'); 78 | $curl->setUserAgent('MyUserAgent/0.0.1 (+https://www.example.com/bot.html)'); 79 | $curl->setReferrer('https://www.example.com/url?url=https%3A%2F%2Fwww.example.com%2F'); 80 | $curl->setHeader('X-Requested-With', 'XMLHttpRequest'); 81 | $curl->setCookie('key', 'value'); 82 | $curl->get('https://www.example.com/'); 83 | 84 | if ($curl->error) { 85 | echo 'Error: ' . $curl->errorCode . ': ' . $curl->errorMessage . "\n"; 86 | } else { 87 | echo 'Response:' . "\n"; 88 | var_dump($curl->response); 89 | } 90 | 91 | var_dump($curl->requestHeaders); 92 | var_dump($curl->responseHeaders); 93 | ``` 94 | 95 | ```php 96 | $curl = new Curl(); 97 | $curl->setOpt(CURLOPT_FOLLOWLOCATION, true); 98 | $curl->get('https://shortn.example.com/bHbVsP'); 99 | ``` 100 | 101 | ```php 102 | $curl = new Curl(); 103 | $curl->put('https://api.example.com/user/', array( 104 | 'first_name' => 'Zach', 105 | 'last_name' => 'Borboa', 106 | )); 107 | ``` 108 | 109 | ```php 110 | $curl = new Curl(); 111 | $curl->patch('https://api.example.com/profile/', array( 112 | 'image' => '@path/to/file.jpg', 113 | )); 114 | ``` 115 | 116 | ```php 117 | $curl = new Curl(); 118 | $curl->patch('https://api.example.com/profile/', array( 119 | 'image' => new CURLFile('path/to/file.jpg'), 120 | )); 121 | ``` 122 | 123 | ```php 124 | $curl = new Curl(); 125 | $curl->delete('https://api.example.com/user/', array( 126 | 'id' => '1234', 127 | )); 128 | ``` 129 | 130 | ```php 131 | // Enable all supported encoding types and download a file. 132 | $curl = new Curl(); 133 | $curl->setOpt(CURLOPT_ENCODING , ''); 134 | $curl->download('https://www.example.com/file.bin', '/tmp/myfile.bin'); 135 | ``` 136 | 137 | ```php 138 | // Case-insensitive access to headers. 139 | $curl = new Curl(); 140 | $curl->download('https://www.example.com/image.png', '/tmp/myimage.png'); 141 | echo $curl->responseHeaders['Content-Type'] . "\n"; // image/png 142 | echo $curl->responseHeaders['CoNTeNT-TyPE'] . "\n"; // image/png 143 | ``` 144 | 145 | ```php 146 | // Manual clean up. 147 | $curl->close(); 148 | ``` 149 | 150 | ```php 151 | // Example access to curl object. 152 | curl_set_opt($curl->curl, CURLOPT_USERAGENT, 'Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 5.1'); 153 | curl_close($curl->curl); 154 | ``` 155 | 156 | ```php 157 | require __DIR__ . '/vendor/autoload.php'; 158 | 159 | use \Curl\MultiCurl; 160 | 161 | // Requests in parallel with callback functions. 162 | $multi_curl = new MultiCurl(); 163 | 164 | $multi_curl->success(function($instance) { 165 | echo 'call to "' . $instance->url . '" was successful.' . "\n"; 166 | echo 'response:' . "\n"; 167 | var_dump($instance->response); 168 | }); 169 | $multi_curl->error(function($instance) { 170 | echo 'call to "' . $instance->url . '" was unsuccessful.' . "\n"; 171 | echo 'error code: ' . $instance->errorCode . "\n"; 172 | echo 'error message: ' . $instance->errorMessage . "\n"; 173 | }); 174 | $multi_curl->complete(function($instance) { 175 | echo 'call completed' . "\n"; 176 | }); 177 | 178 | $multi_curl->addGet('https://www.google.com/search', array( 179 | 'q' => 'hello world', 180 | )); 181 | $multi_curl->addGet('https://duckduckgo.com/', array( 182 | 'q' => 'hello world', 183 | )); 184 | $multi_curl->addGet('https://www.bing.com/search', array( 185 | 'q' => 'hello world', 186 | )); 187 | 188 | $multi_curl->start(); // Blocks until all items in the queue have been processed. 189 | ``` 190 | 191 | More examples are available under [/examples](https://github.com/php-curl-class/php-curl-class/tree/master/examples). 192 | 193 | ### Available Methods 194 | ```php 195 | Curl::__construct($base_url = null) 196 | Curl::__destruct() 197 | Curl::__get($name) 198 | Curl::attemptRetry() 199 | Curl::beforeSend($callback) 200 | Curl::buildPostData($data) 201 | Curl::call() 202 | Curl::close() 203 | Curl::complete($callback) 204 | Curl::delete($url, $query_parameters = array(), $data = array()) 205 | Curl::download($url, $mixed_filename) 206 | Curl::error($callback) 207 | Curl::exec($ch = null) 208 | Curl::execDone() 209 | Curl::get($url, $data = array()) 210 | Curl::getAttempts() 211 | Curl::getBeforeSendCallback() 212 | Curl::getCompleteCallback() 213 | Curl::getCookie($key) 214 | Curl::getCurl() 215 | Curl::getCurlErrorCode() 216 | Curl::getCurlErrorMessage() 217 | Curl::getDownloadCompleteCallback() 218 | Curl::getDownloadFileName() 219 | Curl::getErrorCallback() 220 | Curl::getErrorCode() 221 | Curl::getErrorMessage() 222 | Curl::getFileHandle() 223 | Curl::getHttpErrorMessage() 224 | Curl::getHttpStatusCode() 225 | Curl::getId() 226 | Curl::getInfo($opt = null) 227 | Curl::getJsonDecoder() 228 | Curl::getOpt($option) 229 | Curl::getRawResponse() 230 | Curl::getRawResponseHeaders() 231 | Curl::getRemainingRetries() 232 | Curl::getRequestHeaders() 233 | Curl::getResponse() 234 | Curl::getResponseCookie($key) 235 | Curl::getResponseCookies() 236 | Curl::getResponseHeaders() 237 | Curl::getRetries() 238 | Curl::getRetryDecider() 239 | Curl::getSuccessCallback() 240 | Curl::getUrl() 241 | Curl::getXmlDecoder() 242 | Curl::head($url, $data = array()) 243 | Curl::isChildOfMultiCurl() 244 | Curl::isCurlError() 245 | Curl::isError() 246 | Curl::isHttpError() 247 | Curl::options($url, $data = array()) 248 | Curl::patch($url, $data = array()) 249 | Curl::post($url, $data = '', $follow_303_with_post = false) 250 | Curl::progress($callback) 251 | Curl::put($url, $data = array()) 252 | Curl::removeHeader($key) 253 | Curl::reset() 254 | Curl::search($url, $data = array()) 255 | Curl::setBasicAuthentication($username, $password = '') 256 | Curl::setConnectTimeout($seconds) 257 | Curl::setCookie($key, $value) 258 | Curl::setCookieFile($cookie_file) 259 | Curl::setCookieJar($cookie_jar) 260 | Curl::setCookieString($string) 261 | Curl::setCookies($cookies) 262 | Curl::setDefaultDecoder($mixed = 'json') 263 | Curl::setDefaultJsonDecoder() 264 | Curl::setDefaultTimeout() 265 | Curl::setDefaultUserAgent() 266 | Curl::setDefaultXmlDecoder() 267 | Curl::setDigestAuthentication($username, $password = '') 268 | Curl::setHeader($key, $value) 269 | Curl::setHeaders($headers) 270 | Curl::setJsonDecoder($mixed) 271 | Curl::setMaxFilesize($bytes) 272 | Curl::setOpt($option, $value) 273 | Curl::setOpts($options) 274 | Curl::setPort($port) 275 | Curl::setProxy($proxy, $port = null, $username = null, $password = null) 276 | Curl::setProxyAuth($auth) 277 | Curl::setProxyTunnel($tunnel = true) 278 | Curl::setProxyType($type) 279 | Curl::setReferer($referer) 280 | Curl::setReferrer($referrer) 281 | Curl::setRetry($mixed) 282 | Curl::setTimeout($seconds) 283 | Curl::setUrl($url, $mixed_data = '') 284 | Curl::setUserAgent($user_agent) 285 | Curl::setXmlDecoder($mixed) 286 | Curl::success($callback) 287 | Curl::unsetHeader($key) 288 | Curl::unsetProxy() 289 | Curl::verbose($on = true, $output = STDERR) 290 | MultiCurl::__construct($base_url = null) 291 | MultiCurl::__destruct() 292 | MultiCurl::addCurl(Curl $curl) 293 | MultiCurl::addDelete($url, $query_parameters = array(), $data = array()) 294 | MultiCurl::addDownload($url, $mixed_filename) 295 | MultiCurl::addGet($url, $data = array()) 296 | MultiCurl::addHead($url, $data = array()) 297 | MultiCurl::addOptions($url, $data = array()) 298 | MultiCurl::addPatch($url, $data = array()) 299 | MultiCurl::addPost($url, $data = '', $follow_303_with_post = false) 300 | MultiCurl::addPut($url, $data = array()) 301 | MultiCurl::addSearch($url, $data = array()) 302 | MultiCurl::beforeSend($callback) 303 | MultiCurl::close() 304 | MultiCurl::complete($callback) 305 | MultiCurl::error($callback) 306 | MultiCurl::getOpt($option) 307 | MultiCurl::removeHeader($key) 308 | MultiCurl::setBasicAuthentication($username, $password = '') 309 | MultiCurl::setConcurrency($concurrency) 310 | MultiCurl::setConnectTimeout($seconds) 311 | MultiCurl::setCookie($key, $value) 312 | MultiCurl::setCookieFile($cookie_file) 313 | MultiCurl::setCookieJar($cookie_jar) 314 | MultiCurl::setCookieString($string) 315 | MultiCurl::setCookies($cookies) 316 | MultiCurl::setDigestAuthentication($username, $password = '') 317 | MultiCurl::setHeader($key, $value) 318 | MultiCurl::setHeaders($headers) 319 | MultiCurl::setJsonDecoder($mixed) 320 | MultiCurl::setOpt($option, $value) 321 | MultiCurl::setOpts($options) 322 | MultiCurl::setPort($port) 323 | MultiCurl::setProxies($proxies) 324 | MultiCurl::setProxy($proxy, $port = null, $username = null, $password = null) 325 | MultiCurl::setProxyAuth($auth) 326 | MultiCurl::setProxyTunnel($tunnel = true) 327 | MultiCurl::setProxyType($type) 328 | MultiCurl::setReferer($referer) 329 | MultiCurl::setReferrer($referrer) 330 | MultiCurl::setRetry($mixed) 331 | MultiCurl::setTimeout($seconds) 332 | MultiCurl::setUrl($url) 333 | MultiCurl::setUserAgent($user_agent) 334 | MultiCurl::setXmlDecoder($mixed) 335 | MultiCurl::start() 336 | MultiCurl::success($callback) 337 | MultiCurl::unsetHeader($key) 338 | MultiCurl::unsetProxy() 339 | MultiCurl::verbose($on = true, $output = STDERR) 340 | ``` 341 | 342 | ### Security 343 | 344 | See [SECURITY](https://github.com/php-curl-class/php-curl-class/blob/master/SECURITY.md) for security considerations. 345 | 346 | ### Troubleshooting 347 | 348 | See [TROUBLESHOOTING](https://github.com/php-curl-class/php-curl-class/blob/master/TROUBLESHOOTING.md) for troubleshooting. 349 | 350 | ### Run Tests 351 | 352 | To run tests: 353 | 354 | $ git clone https://github.com/php-curl-class/php-curl-class.git 355 | $ cd php-curl-class/ 356 | $ composer update 357 | $ ./tests/run.sh 358 | 359 | To run select tests: 360 | 361 | $ git clone https://github.com/php-curl-class/php-curl-class.git 362 | $ cd php-curl-class/ 363 | $ composer update 364 | $ ./tests/run.sh --filter=keyword 365 | 366 | To test all PHP versions in containers: 367 | 368 | $ git clone https://github.com/php-curl-class/php-curl-class.git 369 | $ cd php-curl-class/ 370 | $ ./tests/test_all.sh 371 | 372 | ### Contribute 373 | 374 | 1. Check for open issues or open a new issue to start a discussion around a bug or feature. 375 | 1. Fork the repository on GitHub to start making your changes. 376 | 1. Write one or more tests for the new feature or that expose the bug. 377 | 1. Make code changes to implement the feature or fix the bug. 378 | 1. Send a pull request to get your changes merged and published. 379 | -------------------------------------------------------------------------------- /vendor/composer/ClassLoader.php: -------------------------------------------------------------------------------- 1 | 7 | * Jordi Boggiano 8 | * 9 | * For the full copyright and license information, please view the LICENSE 10 | * file that was distributed with this source code. 11 | */ 12 | 13 | namespace Composer\Autoload; 14 | 15 | /** 16 | * ClassLoader implements a PSR-0, PSR-4 and classmap class loader. 17 | * 18 | * $loader = new \Composer\Autoload\ClassLoader(); 19 | * 20 | * // register classes with namespaces 21 | * $loader->add('Symfony\Component', __DIR__.'/component'); 22 | * $loader->add('Symfony', __DIR__.'/framework'); 23 | * 24 | * // activate the autoloader 25 | * $loader->register(); 26 | * 27 | * // to enable searching the include path (eg. for PEAR packages) 28 | * $loader->setUseIncludePath(true); 29 | * 30 | * In this example, if you try to use a class in the Symfony\Component 31 | * namespace or one of its children (Symfony\Component\Console for instance), 32 | * the autoloader will first look for the class under the component/ 33 | * directory, and it will then fallback to the framework/ directory if not 34 | * found before giving up. 35 | * 36 | * This class is loosely based on the Symfony UniversalClassLoader. 37 | * 38 | * @author Fabien Potencier 39 | * @author Jordi Boggiano 40 | * @see http://www.php-fig.org/psr/psr-0/ 41 | * @see http://www.php-fig.org/psr/psr-4/ 42 | */ 43 | class ClassLoader 44 | { 45 | // PSR-4 46 | private $prefixLengthsPsr4 = array(); 47 | private $prefixDirsPsr4 = array(); 48 | private $fallbackDirsPsr4 = array(); 49 | 50 | // PSR-0 51 | private $prefixesPsr0 = array(); 52 | private $fallbackDirsPsr0 = array(); 53 | 54 | private $useIncludePath = false; 55 | private $classMap = array(); 56 | private $classMapAuthoritative = false; 57 | private $missingClasses = array(); 58 | private $apcuPrefix; 59 | 60 | public function getPrefixes() 61 | { 62 | if (!empty($this->prefixesPsr0)) { 63 | return call_user_func_array('array_merge', $this->prefixesPsr0); 64 | } 65 | 66 | return array(); 67 | } 68 | 69 | public function getPrefixesPsr4() 70 | { 71 | return $this->prefixDirsPsr4; 72 | } 73 | 74 | public function getFallbackDirs() 75 | { 76 | return $this->fallbackDirsPsr0; 77 | } 78 | 79 | public function getFallbackDirsPsr4() 80 | { 81 | return $this->fallbackDirsPsr4; 82 | } 83 | 84 | public function getClassMap() 85 | { 86 | return $this->classMap; 87 | } 88 | 89 | /** 90 | * @param array $classMap Class to filename map 91 | */ 92 | public function addClassMap(array $classMap) 93 | { 94 | if ($this->classMap) { 95 | $this->classMap = array_merge($this->classMap, $classMap); 96 | } else { 97 | $this->classMap = $classMap; 98 | } 99 | } 100 | 101 | /** 102 | * Registers a set of PSR-0 directories for a given prefix, either 103 | * appending or prepending to the ones previously set for this prefix. 104 | * 105 | * @param string $prefix The prefix 106 | * @param array|string $paths The PSR-0 root directories 107 | * @param bool $prepend Whether to prepend the directories 108 | */ 109 | public function add($prefix, $paths, $prepend = false) 110 | { 111 | if (!$prefix) { 112 | if ($prepend) { 113 | $this->fallbackDirsPsr0 = array_merge( 114 | (array) $paths, 115 | $this->fallbackDirsPsr0 116 | ); 117 | } else { 118 | $this->fallbackDirsPsr0 = array_merge( 119 | $this->fallbackDirsPsr0, 120 | (array) $paths 121 | ); 122 | } 123 | 124 | return; 125 | } 126 | 127 | $first = $prefix[0]; 128 | if (!isset($this->prefixesPsr0[$first][$prefix])) { 129 | $this->prefixesPsr0[$first][$prefix] = (array) $paths; 130 | 131 | return; 132 | } 133 | if ($prepend) { 134 | $this->prefixesPsr0[$first][$prefix] = array_merge( 135 | (array) $paths, 136 | $this->prefixesPsr0[$first][$prefix] 137 | ); 138 | } else { 139 | $this->prefixesPsr0[$first][$prefix] = array_merge( 140 | $this->prefixesPsr0[$first][$prefix], 141 | (array) $paths 142 | ); 143 | } 144 | } 145 | 146 | /** 147 | * Registers a set of PSR-4 directories for a given namespace, either 148 | * appending or prepending to the ones previously set for this namespace. 149 | * 150 | * @param string $prefix The prefix/namespace, with trailing '\\' 151 | * @param array|string $paths The PSR-4 base directories 152 | * @param bool $prepend Whether to prepend the directories 153 | * 154 | * @throws \InvalidArgumentException 155 | */ 156 | public function addPsr4($prefix, $paths, $prepend = false) 157 | { 158 | if (!$prefix) { 159 | // Register directories for the root namespace. 160 | if ($prepend) { 161 | $this->fallbackDirsPsr4 = array_merge( 162 | (array) $paths, 163 | $this->fallbackDirsPsr4 164 | ); 165 | } else { 166 | $this->fallbackDirsPsr4 = array_merge( 167 | $this->fallbackDirsPsr4, 168 | (array) $paths 169 | ); 170 | } 171 | } elseif (!isset($this->prefixDirsPsr4[$prefix])) { 172 | // Register directories for a new namespace. 173 | $length = strlen($prefix); 174 | if ('\\' !== $prefix[$length - 1]) { 175 | throw new \InvalidArgumentException("A non-empty PSR-4 prefix must end with a namespace separator."); 176 | } 177 | $this->prefixLengthsPsr4[$prefix[0]][$prefix] = $length; 178 | $this->prefixDirsPsr4[$prefix] = (array) $paths; 179 | } elseif ($prepend) { 180 | // Prepend directories for an already registered namespace. 181 | $this->prefixDirsPsr4[$prefix] = array_merge( 182 | (array) $paths, 183 | $this->prefixDirsPsr4[$prefix] 184 | ); 185 | } else { 186 | // Append directories for an already registered namespace. 187 | $this->prefixDirsPsr4[$prefix] = array_merge( 188 | $this->prefixDirsPsr4[$prefix], 189 | (array) $paths 190 | ); 191 | } 192 | } 193 | 194 | /** 195 | * Registers a set of PSR-0 directories for a given prefix, 196 | * replacing any others previously set for this prefix. 197 | * 198 | * @param string $prefix The prefix 199 | * @param array|string $paths The PSR-0 base directories 200 | */ 201 | public function set($prefix, $paths) 202 | { 203 | if (!$prefix) { 204 | $this->fallbackDirsPsr0 = (array) $paths; 205 | } else { 206 | $this->prefixesPsr0[$prefix[0]][$prefix] = (array) $paths; 207 | } 208 | } 209 | 210 | /** 211 | * Registers a set of PSR-4 directories for a given namespace, 212 | * replacing any others previously set for this namespace. 213 | * 214 | * @param string $prefix The prefix/namespace, with trailing '\\' 215 | * @param array|string $paths The PSR-4 base directories 216 | * 217 | * @throws \InvalidArgumentException 218 | */ 219 | public function setPsr4($prefix, $paths) 220 | { 221 | if (!$prefix) { 222 | $this->fallbackDirsPsr4 = (array) $paths; 223 | } else { 224 | $length = strlen($prefix); 225 | if ('\\' !== $prefix[$length - 1]) { 226 | throw new \InvalidArgumentException("A non-empty PSR-4 prefix must end with a namespace separator."); 227 | } 228 | $this->prefixLengthsPsr4[$prefix[0]][$prefix] = $length; 229 | $this->prefixDirsPsr4[$prefix] = (array) $paths; 230 | } 231 | } 232 | 233 | /** 234 | * Turns on searching the include path for class files. 235 | * 236 | * @param bool $useIncludePath 237 | */ 238 | public function setUseIncludePath($useIncludePath) 239 | { 240 | $this->useIncludePath = $useIncludePath; 241 | } 242 | 243 | /** 244 | * Can be used to check if the autoloader uses the include path to check 245 | * for classes. 246 | * 247 | * @return bool 248 | */ 249 | public function getUseIncludePath() 250 | { 251 | return $this->useIncludePath; 252 | } 253 | 254 | /** 255 | * Turns off searching the prefix and fallback directories for classes 256 | * that have not been registered with the class map. 257 | * 258 | * @param bool $classMapAuthoritative 259 | */ 260 | public function setClassMapAuthoritative($classMapAuthoritative) 261 | { 262 | $this->classMapAuthoritative = $classMapAuthoritative; 263 | } 264 | 265 | /** 266 | * Should class lookup fail if not found in the current class map? 267 | * 268 | * @return bool 269 | */ 270 | public function isClassMapAuthoritative() 271 | { 272 | return $this->classMapAuthoritative; 273 | } 274 | 275 | /** 276 | * APCu prefix to use to cache found/not-found classes, if the extension is enabled. 277 | * 278 | * @param string|null $apcuPrefix 279 | */ 280 | public function setApcuPrefix($apcuPrefix) 281 | { 282 | $this->apcuPrefix = function_exists('apcu_fetch') && filter_var(ini_get('apc.enabled'), FILTER_VALIDATE_BOOLEAN) ? $apcuPrefix : null; 283 | } 284 | 285 | /** 286 | * The APCu prefix in use, or null if APCu caching is not enabled. 287 | * 288 | * @return string|null 289 | */ 290 | public function getApcuPrefix() 291 | { 292 | return $this->apcuPrefix; 293 | } 294 | 295 | /** 296 | * Registers this instance as an autoloader. 297 | * 298 | * @param bool $prepend Whether to prepend the autoloader or not 299 | */ 300 | public function register($prepend = false) 301 | { 302 | spl_autoload_register(array($this, 'loadClass'), true, $prepend); 303 | } 304 | 305 | /** 306 | * Unregisters this instance as an autoloader. 307 | */ 308 | public function unregister() 309 | { 310 | spl_autoload_unregister(array($this, 'loadClass')); 311 | } 312 | 313 | /** 314 | * Loads the given class or interface. 315 | * 316 | * @param string $class The name of the class 317 | * @return bool|null True if loaded, null otherwise 318 | */ 319 | public function loadClass($class) 320 | { 321 | if ($file = $this->findFile($class)) { 322 | includeFile($file); 323 | 324 | return true; 325 | } 326 | } 327 | 328 | /** 329 | * Finds the path to the file where the class is defined. 330 | * 331 | * @param string $class The name of the class 332 | * 333 | * @return string|false The path if found, false otherwise 334 | */ 335 | public function findFile($class) 336 | { 337 | // class map lookup 338 | if (isset($this->classMap[$class])) { 339 | return $this->classMap[$class]; 340 | } 341 | if ($this->classMapAuthoritative || isset($this->missingClasses[$class])) { 342 | return false; 343 | } 344 | if (null !== $this->apcuPrefix) { 345 | $file = apcu_fetch($this->apcuPrefix.$class, $hit); 346 | if ($hit) { 347 | return $file; 348 | } 349 | } 350 | 351 | $file = $this->findFileWithExtension($class, '.php'); 352 | 353 | // Search for Hack files if we are running on HHVM 354 | if (false === $file && defined('HHVM_VERSION')) { 355 | $file = $this->findFileWithExtension($class, '.hh'); 356 | } 357 | 358 | if (null !== $this->apcuPrefix) { 359 | apcu_add($this->apcuPrefix.$class, $file); 360 | } 361 | 362 | if (false === $file) { 363 | // Remember that this class does not exist. 364 | $this->missingClasses[$class] = true; 365 | } 366 | 367 | return $file; 368 | } 369 | 370 | private function findFileWithExtension($class, $ext) 371 | { 372 | // PSR-4 lookup 373 | $logicalPathPsr4 = strtr($class, '\\', DIRECTORY_SEPARATOR) . $ext; 374 | 375 | $first = $class[0]; 376 | if (isset($this->prefixLengthsPsr4[$first])) { 377 | $subPath = $class; 378 | while (false !== $lastPos = strrpos($subPath, '\\')) { 379 | $subPath = substr($subPath, 0, $lastPos); 380 | $search = $subPath . '\\'; 381 | if (isset($this->prefixDirsPsr4[$search])) { 382 | $pathEnd = DIRECTORY_SEPARATOR . substr($logicalPathPsr4, $lastPos + 1); 383 | foreach ($this->prefixDirsPsr4[$search] as $dir) { 384 | if (file_exists($file = $dir . $pathEnd)) { 385 | return $file; 386 | } 387 | } 388 | } 389 | } 390 | } 391 | 392 | // PSR-4 fallback dirs 393 | foreach ($this->fallbackDirsPsr4 as $dir) { 394 | if (file_exists($file = $dir . DIRECTORY_SEPARATOR . $logicalPathPsr4)) { 395 | return $file; 396 | } 397 | } 398 | 399 | // PSR-0 lookup 400 | if (false !== $pos = strrpos($class, '\\')) { 401 | // namespaced class name 402 | $logicalPathPsr0 = substr($logicalPathPsr4, 0, $pos + 1) 403 | . strtr(substr($logicalPathPsr4, $pos + 1), '_', DIRECTORY_SEPARATOR); 404 | } else { 405 | // PEAR-like class name 406 | $logicalPathPsr0 = strtr($class, '_', DIRECTORY_SEPARATOR) . $ext; 407 | } 408 | 409 | if (isset($this->prefixesPsr0[$first])) { 410 | foreach ($this->prefixesPsr0[$first] as $prefix => $dirs) { 411 | if (0 === strpos($class, $prefix)) { 412 | foreach ($dirs as $dir) { 413 | if (file_exists($file = $dir . DIRECTORY_SEPARATOR . $logicalPathPsr0)) { 414 | return $file; 415 | } 416 | } 417 | } 418 | } 419 | } 420 | 421 | // PSR-0 fallback dirs 422 | foreach ($this->fallbackDirsPsr0 as $dir) { 423 | if (file_exists($file = $dir . DIRECTORY_SEPARATOR . $logicalPathPsr0)) { 424 | return $file; 425 | } 426 | } 427 | 428 | // PSR-0 include paths. 429 | if ($this->useIncludePath && $file = stream_resolve_include_path($logicalPathPsr0)) { 430 | return $file; 431 | } 432 | 433 | return false; 434 | } 435 | } 436 | 437 | /** 438 | * Scope isolated include. 439 | * 440 | * Prevents access to $this/self from included files. 441 | */ 442 | function includeFile($file) 443 | { 444 | include $file; 445 | } 446 | -------------------------------------------------------------------------------- /vendor/php-curl-class/php-curl-class/src/Curl/MultiCurl.php: -------------------------------------------------------------------------------- 1 | multiCurl = curl_multi_init(); 42 | $this->headers = new CaseInsensitiveArray(); 43 | $this->setUrl($base_url); 44 | } 45 | 46 | /** 47 | * Add Delete 48 | * 49 | * @access public 50 | * @param $url 51 | * @param $query_parameters 52 | * @param $data 53 | * 54 | * @return object 55 | */ 56 | public function addDelete($url, $query_parameters = array(), $data = array()) 57 | { 58 | if (is_array($url)) { 59 | $data = $query_parameters; 60 | $query_parameters = $url; 61 | $url = $this->baseUrl; 62 | } 63 | $curl = new Curl(); 64 | $this->queueHandle($curl); 65 | $curl->setUrl($url, $query_parameters); 66 | $curl->setOpt(CURLOPT_CUSTOMREQUEST, 'DELETE'); 67 | $curl->setOpt(CURLOPT_POSTFIELDS, $curl->buildPostData($data)); 68 | return $curl; 69 | } 70 | 71 | /** 72 | * Add Download 73 | * 74 | * @access public 75 | * @param $url 76 | * @param $mixed_filename 77 | * 78 | * @return object 79 | */ 80 | public function addDownload($url, $mixed_filename) 81 | { 82 | $curl = new Curl(); 83 | $this->queueHandle($curl); 84 | $curl->setUrl($url); 85 | 86 | // Use tmpfile() or php://temp to avoid "Too many open files" error. 87 | if (is_callable($mixed_filename)) { 88 | $callback = $mixed_filename; 89 | $curl->downloadCompleteCallback = $callback; 90 | $curl->fileHandle = tmpfile(); 91 | } else { 92 | $filename = $mixed_filename; 93 | $curl->downloadCompleteCallback = function ($instance, $fh) use ($filename) { 94 | file_put_contents($filename, stream_get_contents($fh)); 95 | }; 96 | $curl->fileHandle = fopen('php://temp', 'wb'); 97 | } 98 | 99 | $curl->setOpt(CURLOPT_FILE, $curl->fileHandle); 100 | $curl->setOpt(CURLOPT_CUSTOMREQUEST, 'GET'); 101 | $curl->setOpt(CURLOPT_HTTPGET, true); 102 | return $curl; 103 | } 104 | 105 | /** 106 | * Add Get 107 | * 108 | * @access public 109 | * @param $url 110 | * @param $data 111 | * 112 | * @return object 113 | */ 114 | public function addGet($url, $data = array()) 115 | { 116 | if (is_array($url)) { 117 | $data = $url; 118 | $url = $this->baseUrl; 119 | } 120 | $curl = new Curl(); 121 | $this->queueHandle($curl); 122 | $curl->setUrl($url, $data); 123 | $curl->setOpt(CURLOPT_CUSTOMREQUEST, 'GET'); 124 | $curl->setOpt(CURLOPT_HTTPGET, true); 125 | return $curl; 126 | } 127 | 128 | /** 129 | * Add Head 130 | * 131 | * @access public 132 | * @param $url 133 | * @param $data 134 | * 135 | * @return object 136 | */ 137 | public function addHead($url, $data = array()) 138 | { 139 | if (is_array($url)) { 140 | $data = $url; 141 | $url = $this->baseUrl; 142 | } 143 | $curl = new Curl(); 144 | $this->queueHandle($curl); 145 | $curl->setUrl($url, $data); 146 | $curl->setOpt(CURLOPT_CUSTOMREQUEST, 'HEAD'); 147 | $curl->setOpt(CURLOPT_NOBODY, true); 148 | return $curl; 149 | } 150 | 151 | /** 152 | * Add Options 153 | * 154 | * @access public 155 | * @param $url 156 | * @param $data 157 | * 158 | * @return object 159 | */ 160 | public function addOptions($url, $data = array()) 161 | { 162 | if (is_array($url)) { 163 | $data = $url; 164 | $url = $this->baseUrl; 165 | } 166 | $curl = new Curl(); 167 | $this->queueHandle($curl); 168 | $curl->setUrl($url, $data); 169 | $curl->removeHeader('Content-Length'); 170 | $curl->setOpt(CURLOPT_CUSTOMREQUEST, 'OPTIONS'); 171 | return $curl; 172 | } 173 | 174 | /** 175 | * Add Patch 176 | * 177 | * @access public 178 | * @param $url 179 | * @param $data 180 | * 181 | * @return object 182 | */ 183 | public function addPatch($url, $data = array()) 184 | { 185 | if (is_array($url)) { 186 | $data = $url; 187 | $url = $this->baseUrl; 188 | } 189 | 190 | $curl = new Curl(); 191 | 192 | if (is_array($data) && empty($data)) { 193 | $curl->removeHeader('Content-Length'); 194 | } 195 | 196 | $this->queueHandle($curl); 197 | $curl->setUrl($url); 198 | $curl->setOpt(CURLOPT_CUSTOMREQUEST, 'PATCH'); 199 | $curl->setOpt(CURLOPT_POSTFIELDS, $curl->buildPostData($data)); 200 | return $curl; 201 | } 202 | 203 | /** 204 | * Add Post 205 | * 206 | * @access public 207 | * @param $url 208 | * @param $data 209 | * @param $follow_303_with_post 210 | * If true, will cause 303 redirections to be followed using GET requests (default: false). 211 | * Note: Redirections are only followed if the CURLOPT_FOLLOWLOCATION option is set to true. 212 | * 213 | * @return object 214 | */ 215 | public function addPost($url, $data = '', $follow_303_with_post = false) 216 | { 217 | if (is_array($url)) { 218 | $follow_303_with_post = (bool)$data; 219 | $data = $url; 220 | $url = $this->baseUrl; 221 | } 222 | 223 | $curl = new Curl(); 224 | $this->queueHandle($curl); 225 | 226 | if (is_array($data) && empty($data)) { 227 | $curl->removeHeader('Content-Length'); 228 | } 229 | 230 | $curl->setUrl($url); 231 | 232 | /* 233 | * For post-redirect-get requests, the CURLOPT_CUSTOMREQUEST option must not 234 | * be set, otherwise cURL will perform POST requests for redirections. 235 | */ 236 | if (!$follow_303_with_post) { 237 | $curl->setOpt(CURLOPT_CUSTOMREQUEST, 'POST'); 238 | } 239 | 240 | $curl->setOpt(CURLOPT_POST, true); 241 | $curl->setOpt(CURLOPT_POSTFIELDS, $curl->buildPostData($data)); 242 | return $curl; 243 | } 244 | 245 | /** 246 | * Add Put 247 | * 248 | * @access public 249 | * @param $url 250 | * @param $data 251 | * 252 | * @return object 253 | */ 254 | public function addPut($url, $data = array()) 255 | { 256 | if (is_array($url)) { 257 | $data = $url; 258 | $url = $this->baseUrl; 259 | } 260 | $curl = new Curl(); 261 | $this->queueHandle($curl); 262 | $curl->setUrl($url); 263 | $curl->setOpt(CURLOPT_CUSTOMREQUEST, 'PUT'); 264 | $put_data = $curl->buildPostData($data); 265 | if (is_string($put_data)) { 266 | $curl->setHeader('Content-Length', strlen($put_data)); 267 | } 268 | $curl->setOpt(CURLOPT_POSTFIELDS, $put_data); 269 | return $curl; 270 | } 271 | 272 | /** 273 | * Add Search 274 | * 275 | * @access public 276 | * @param $url 277 | * @param $data 278 | * 279 | * @return object 280 | */ 281 | public function addSearch($url, $data = array()) 282 | { 283 | if (is_array($url)) { 284 | $data = $url; 285 | $url = $this->baseUrl; 286 | } 287 | $curl = new Curl(); 288 | $this->queueHandle($curl); 289 | $curl->setUrl($url); 290 | $curl->setOpt(CURLOPT_CUSTOMREQUEST, 'SEARCH'); 291 | $put_data = $curl->buildPostData($data); 292 | if (is_string($put_data)) { 293 | $curl->setHeader('Content-Length', strlen($put_data)); 294 | } 295 | $curl->setOpt(CURLOPT_POSTFIELDS, $put_data); 296 | return $curl; 297 | } 298 | 299 | /** 300 | * Add Curl 301 | * 302 | * Add a Curl instance to the handle queue. 303 | * 304 | * @access public 305 | * @param $curl 306 | * 307 | * @return object 308 | */ 309 | public function addCurl(Curl $curl) 310 | { 311 | $this->queueHandle($curl); 312 | return $curl; 313 | } 314 | 315 | /** 316 | * Before Send 317 | * 318 | * @access public 319 | * @param $callback 320 | */ 321 | public function beforeSend($callback) 322 | { 323 | $this->beforeSendCallback = $callback; 324 | } 325 | 326 | /** 327 | * Close 328 | * 329 | * @access public 330 | */ 331 | public function close() 332 | { 333 | foreach ($this->curls as $curl) { 334 | $curl->close(); 335 | } 336 | 337 | if (is_resource($this->multiCurl)) { 338 | curl_multi_close($this->multiCurl); 339 | } 340 | } 341 | 342 | /** 343 | * Complete 344 | * 345 | * @access public 346 | * @param $callback 347 | */ 348 | public function complete($callback) 349 | { 350 | $this->completeCallback = $callback; 351 | } 352 | 353 | /** 354 | * Error 355 | * 356 | * @access public 357 | * @param $callback 358 | */ 359 | public function error($callback) 360 | { 361 | $this->errorCallback = $callback; 362 | } 363 | 364 | /** 365 | * Get Opt 366 | * 367 | * @access public 368 | * @param $option 369 | * 370 | * @return mixed 371 | */ 372 | public function getOpt($option) 373 | { 374 | return isset($this->options[$option]) ? $this->options[$option] : null; 375 | } 376 | 377 | /** 378 | * Set Basic Authentication 379 | * 380 | * @access public 381 | * @param $username 382 | * @param $password 383 | */ 384 | public function setBasicAuthentication($username, $password = '') 385 | { 386 | $this->setOpt(CURLOPT_HTTPAUTH, CURLAUTH_BASIC); 387 | $this->setOpt(CURLOPT_USERPWD, $username . ':' . $password); 388 | } 389 | 390 | /** 391 | * Set Concurrency 392 | * 393 | * @access public 394 | * @param $concurrency 395 | */ 396 | public function setConcurrency($concurrency) 397 | { 398 | $this->concurrency = $concurrency; 399 | } 400 | 401 | /** 402 | * Set Digest Authentication 403 | * 404 | * @access public 405 | * @param $username 406 | * @param $password 407 | */ 408 | public function setDigestAuthentication($username, $password = '') 409 | { 410 | $this->setOpt(CURLOPT_HTTPAUTH, CURLAUTH_DIGEST); 411 | $this->setOpt(CURLOPT_USERPWD, $username . ':' . $password); 412 | } 413 | 414 | /** 415 | * Set Cookie 416 | * 417 | * @access public 418 | * @param $key 419 | * @param $value 420 | */ 421 | public function setCookie($key, $value) 422 | { 423 | $this->cookies[$key] = $value; 424 | } 425 | 426 | /** 427 | * Set Cookies 428 | * 429 | * @access public 430 | * @param $cookies 431 | */ 432 | public function setCookies($cookies) 433 | { 434 | foreach ($cookies as $key => $value) { 435 | $this->cookies[$key] = $value; 436 | } 437 | } 438 | 439 | /** 440 | * Set Port 441 | * 442 | * @access public 443 | * @param $port 444 | */ 445 | public function setPort($port) 446 | { 447 | $this->setOpt(CURLOPT_PORT, intval($port)); 448 | } 449 | 450 | /** 451 | * Set Connect Timeout 452 | * 453 | * @access public 454 | * @param $seconds 455 | */ 456 | public function setConnectTimeout($seconds) 457 | { 458 | $this->setOpt(CURLOPT_CONNECTTIMEOUT, $seconds); 459 | } 460 | 461 | /** 462 | * Set Cookie String 463 | * 464 | * @access public 465 | * @param $string 466 | */ 467 | public function setCookieString($string) 468 | { 469 | $this->setOpt(CURLOPT_COOKIE, $string); 470 | } 471 | 472 | /** 473 | * Set Cookie File 474 | * 475 | * @access public 476 | * @param $cookie_file 477 | */ 478 | public function setCookieFile($cookie_file) 479 | { 480 | $this->setOpt(CURLOPT_COOKIEFILE, $cookie_file); 481 | } 482 | 483 | /** 484 | * Set Cookie Jar 485 | * 486 | * @access public 487 | * @param $cookie_jar 488 | */ 489 | public function setCookieJar($cookie_jar) 490 | { 491 | $this->setOpt(CURLOPT_COOKIEJAR, $cookie_jar); 492 | } 493 | 494 | /** 495 | * Set Header 496 | * 497 | * Add extra header to include in the request. 498 | * 499 | * @access public 500 | * @param $key 501 | * @param $value 502 | */ 503 | public function setHeader($key, $value) 504 | { 505 | $this->headers[$key] = $value; 506 | $this->updateHeaders(); 507 | } 508 | 509 | /** 510 | * Set Headers 511 | * 512 | * Add extra headers to include in the request. 513 | * 514 | * @access public 515 | * @param $headers 516 | */ 517 | public function setHeaders($headers) 518 | { 519 | foreach ($headers as $key => $value) { 520 | $this->headers[$key] = $value; 521 | } 522 | $this->updateHeaders(); 523 | } 524 | 525 | /** 526 | * Set JSON Decoder 527 | * 528 | * @access public 529 | * @param $mixed boolean|callable 530 | */ 531 | public function setJsonDecoder($mixed) 532 | { 533 | if ($mixed === false) { 534 | $this->jsonDecoder = false; 535 | } elseif (is_callable($mixed)) { 536 | $this->jsonDecoder = $mixed; 537 | } 538 | } 539 | 540 | /** 541 | * Set XML Decoder 542 | * 543 | * @access public 544 | * @param $mixed boolean|callable 545 | */ 546 | public function setXmlDecoder($mixed) 547 | { 548 | if ($mixed === false) { 549 | $this->xmlDecoder = false; 550 | } elseif (is_callable($mixed)) { 551 | $this->xmlDecoder = $mixed; 552 | } 553 | } 554 | 555 | /** 556 | * Set Proxy 557 | * 558 | * Set an HTTP proxy to tunnel requests through. 559 | * 560 | * @access public 561 | * @param $proxy - The HTTP proxy to tunnel requests through. May include port number. 562 | * @param $port - The port number of the proxy to connect to. This port number can also be set in $proxy. 563 | * @param $username - The username to use for the connection to the proxy. 564 | * @param $password - The password to use for the connection to the proxy. 565 | */ 566 | public function setProxy($proxy, $port = null, $username = null, $password = null) 567 | { 568 | $this->setOpt(CURLOPT_PROXY, $proxy); 569 | if ($port !== null) { 570 | $this->setOpt(CURLOPT_PROXYPORT, $port); 571 | } 572 | if ($username !== null && $password !== null) { 573 | $this->setOpt(CURLOPT_PROXYUSERPWD, $username . ':' . $password); 574 | } 575 | } 576 | 577 | /** 578 | * Set Proxies 579 | * 580 | * Set proxies to tunnel requests through. When set, a random proxy will be 581 | * used for the request. 582 | * 583 | * @access public 584 | * @param $proxies array - A list of HTTP proxies to tunnel requests 585 | * through. May include port number. 586 | */ 587 | public function setProxies($proxies) 588 | { 589 | $this->proxies = $proxies; 590 | } 591 | 592 | /** 593 | * Set Proxy Auth 594 | * 595 | * Set the HTTP authentication method(s) to use for the proxy connection. 596 | * 597 | * @access public 598 | * @param $auth 599 | */ 600 | public function setProxyAuth($auth) 601 | { 602 | $this->setOpt(CURLOPT_PROXYAUTH, $auth); 603 | } 604 | 605 | /** 606 | * Set Proxy Type 607 | * 608 | * Set the proxy protocol type. 609 | * 610 | * @access public 611 | * @param $type 612 | */ 613 | public function setProxyType($type) 614 | { 615 | $this->setOpt(CURLOPT_PROXYTYPE, $type); 616 | } 617 | 618 | /** 619 | * Set Proxy Tunnel 620 | * 621 | * Set the proxy to tunnel through HTTP proxy. 622 | * 623 | * @access public 624 | * @param $tunnel boolean 625 | */ 626 | public function setProxyTunnel($tunnel = true) 627 | { 628 | $this->setOpt(CURLOPT_HTTPPROXYTUNNEL, $tunnel); 629 | } 630 | 631 | /** 632 | * Unset Proxy 633 | * 634 | * Disable use of the proxy. 635 | * 636 | * @access public 637 | */ 638 | public function unsetProxy() 639 | { 640 | $this->setOpt(CURLOPT_PROXY, null); 641 | } 642 | 643 | /** 644 | * Set Opt 645 | * 646 | * @access public 647 | * @param $option 648 | * @param $value 649 | */ 650 | public function setOpt($option, $value) 651 | { 652 | $this->options[$option] = $value; 653 | } 654 | 655 | /** 656 | * Set Opts 657 | * 658 | * @access public 659 | * @param $options 660 | */ 661 | public function setOpts($options) 662 | { 663 | foreach ($options as $option => $value) { 664 | $this->setOpt($option, $value); 665 | } 666 | } 667 | 668 | /** 669 | * Set Referer 670 | * 671 | * @access public 672 | * @param $referer 673 | */ 674 | public function setReferer($referer) 675 | { 676 | $this->setReferrer($referer); 677 | } 678 | 679 | /** 680 | * Set Referrer 681 | * 682 | * @access public 683 | * @param $referrer 684 | */ 685 | public function setReferrer($referrer) 686 | { 687 | $this->setOpt(CURLOPT_REFERER, $referrer); 688 | } 689 | 690 | /** 691 | * Set Retry 692 | * 693 | * Number of retries to attempt or decider callable. 694 | * 695 | * When using a number of retries to attempt, the maximum number of attempts 696 | * for the request is $maximum_number_of_retries + 1. 697 | * 698 | * When using a callable decider, the request will be retried until the 699 | * function returns a value which evaluates to false. 700 | * 701 | * @access public 702 | * @param $mixed 703 | */ 704 | public function setRetry($mixed) 705 | { 706 | $this->retry = $mixed; 707 | } 708 | 709 | /** 710 | * Set Timeout 711 | * 712 | * @access public 713 | * @param $seconds 714 | */ 715 | public function setTimeout($seconds) 716 | { 717 | $this->setOpt(CURLOPT_TIMEOUT, $seconds); 718 | } 719 | 720 | /** 721 | * Set Url 722 | * 723 | * @access public 724 | * @param $url 725 | */ 726 | public function setUrl($url) 727 | { 728 | $this->baseUrl = $url; 729 | } 730 | 731 | /** 732 | * Set User Agent 733 | * 734 | * @access public 735 | * @param $user_agent 736 | */ 737 | public function setUserAgent($user_agent) 738 | { 739 | $this->setOpt(CURLOPT_USERAGENT, $user_agent); 740 | } 741 | 742 | /** 743 | * Start 744 | * 745 | * @access public 746 | */ 747 | public function start() 748 | { 749 | if ($this->isStarted) { 750 | return; 751 | } 752 | 753 | $this->isStarted = true; 754 | 755 | $concurrency = $this->concurrency; 756 | if ($concurrency > count($this->curls)) { 757 | $concurrency = count($this->curls); 758 | } 759 | 760 | for ($i = 0; $i < $concurrency; $i++) { 761 | $this->initHandle(array_shift($this->curls)); 762 | } 763 | 764 | do { 765 | // Wait for activity on any curl_multi connection when curl_multi_select (libcurl) fails to correctly block. 766 | // https://bugs.php.net/bug.php?id=63411 767 | if (curl_multi_select($this->multiCurl) === -1) { 768 | usleep(100000); 769 | } 770 | 771 | curl_multi_exec($this->multiCurl, $active); 772 | 773 | while (!($info_array = curl_multi_info_read($this->multiCurl)) === false) { 774 | if ($info_array['msg'] === CURLMSG_DONE) { 775 | foreach ($this->activeCurls as $key => $curl) { 776 | if ($curl->curl === $info_array['handle']) { 777 | // Set the error code for multi handles using the "result" key in the array returned by 778 | // curl_multi_info_read(). Using curl_errno() on a multi handle will incorrectly return 0 779 | // for errors. 780 | $curl->curlErrorCode = $info_array['result']; 781 | $curl->exec($curl->curl); 782 | 783 | if ($curl->attemptRetry()) { 784 | // Remove completed handle before adding again in order to retry request. 785 | curl_multi_remove_handle($this->multiCurl, $curl->curl); 786 | 787 | $curlm_error_code = curl_multi_add_handle($this->multiCurl, $curl->curl); 788 | if ($curlm_error_code !== CURLM_OK) { 789 | throw new \ErrorException( 790 | 'cURL multi add handle error: ' . curl_multi_strerror($curlm_error_code) 791 | ); 792 | } 793 | } else { 794 | $curl->execDone(); 795 | 796 | // Remove completed instance from active curls. 797 | unset($this->activeCurls[$key]); 798 | 799 | // Start new requests before removing the handle of the completed one. 800 | while (count($this->curls) >= 1 && count($this->activeCurls) < $this->concurrency) { 801 | $this->initHandle(array_shift($this->curls)); 802 | } 803 | curl_multi_remove_handle($this->multiCurl, $curl->curl); 804 | 805 | // Clean up completed instance. 806 | $curl->close(); 807 | } 808 | 809 | break; 810 | } 811 | } 812 | } 813 | } 814 | 815 | if (!$active) { 816 | $active = count($this->activeCurls); 817 | } 818 | } while ($active > 0); 819 | 820 | $this->isStarted = false; 821 | } 822 | 823 | /** 824 | * Success 825 | * 826 | * @access public 827 | * @param $callback 828 | */ 829 | public function success($callback) 830 | { 831 | $this->successCallback = $callback; 832 | } 833 | 834 | /** 835 | * Unset Header 836 | * 837 | * Remove extra header previously set using Curl::setHeader(). 838 | * 839 | * @access public 840 | * @param $key 841 | */ 842 | public function unsetHeader($key) 843 | { 844 | unset($this->headers[$key]); 845 | } 846 | 847 | /** 848 | * Remove Header 849 | * 850 | * Remove an internal header from the request. 851 | * Using `curl -H "Host:" ...' is equivalent to $curl->removeHeader('Host');. 852 | * 853 | * @access public 854 | * @param $key 855 | */ 856 | public function removeHeader($key) 857 | { 858 | $this->setHeader($key, ''); 859 | } 860 | 861 | /** 862 | * Verbose 863 | * 864 | * @access public 865 | * @param bool $on 866 | * @param resource $output 867 | */ 868 | public function verbose($on = true, $output = STDERR) 869 | { 870 | // Turn off CURLINFO_HEADER_OUT for verbose to work. This has the side 871 | // effect of causing Curl::requestHeaders to be empty. 872 | if ($on) { 873 | $this->setOpt(CURLINFO_HEADER_OUT, false); 874 | } 875 | $this->setOpt(CURLOPT_VERBOSE, $on); 876 | $this->setOpt(CURLOPT_STDERR, $output); 877 | } 878 | 879 | /** 880 | * Destruct 881 | * 882 | * @access public 883 | */ 884 | public function __destruct() 885 | { 886 | $this->close(); 887 | } 888 | 889 | /** 890 | * Update Headers 891 | * 892 | * @access private 893 | */ 894 | private function updateHeaders() 895 | { 896 | foreach ($this->curls as $curl) { 897 | $curl->setHeaders($this->headers); 898 | } 899 | } 900 | 901 | /** 902 | * Queue Handle 903 | * 904 | * @access private 905 | * @param $curl 906 | */ 907 | private function queueHandle($curl) 908 | { 909 | // Use sequential ids to allow for ordered post processing. 910 | $curl->id = $this->nextCurlId++; 911 | $curl->childOfMultiCurl = true; 912 | $this->curls[$curl->id] = $curl; 913 | 914 | $curl->setHeaders($this->headers); 915 | } 916 | 917 | /** 918 | * Init Handle 919 | * 920 | * @access private 921 | * @param $curl 922 | * @throws \ErrorException 923 | */ 924 | private function initHandle($curl) 925 | { 926 | // Set callbacks if not already individually set. 927 | if ($curl->beforeSendCallback === null) { 928 | $curl->beforeSend($this->beforeSendCallback); 929 | } 930 | if ($curl->successCallback === null) { 931 | $curl->success($this->successCallback); 932 | } 933 | if ($curl->errorCallback === null) { 934 | $curl->error($this->errorCallback); 935 | } 936 | if ($curl->completeCallback === null) { 937 | $curl->complete($this->completeCallback); 938 | } 939 | 940 | // Set decoders if not already individually set. 941 | if ($curl->jsonDecoder === null) { 942 | $curl->setJsonDecoder($this->jsonDecoder); 943 | } 944 | if ($curl->xmlDecoder === null) { 945 | $curl->setXmlDecoder($this->xmlDecoder); 946 | } 947 | 948 | $curl->setOpts($this->options); 949 | $curl->setRetry($this->retry); 950 | $curl->setCookies($this->cookies); 951 | 952 | // Use a random proxy for the curl instance when proxies have been set 953 | // and the curl instance doesn't already have a proxy set. 954 | if (is_array($this->proxies) && $curl->getOpt(CURLOPT_PROXY) === null) { 955 | $random_proxy = ArrayUtil::arrayRandom($this->proxies); 956 | $curl->setProxy($random_proxy); 957 | } 958 | 959 | $curlm_error_code = curl_multi_add_handle($this->multiCurl, $curl->curl); 960 | if ($curlm_error_code !== CURLM_OK) { 961 | throw new \ErrorException('cURL multi add handle error: ' . curl_multi_strerror($curlm_error_code)); 962 | } 963 | 964 | $this->activeCurls[$curl->id] = $curl; 965 | $curl->call($curl->beforeSendCallback); 966 | } 967 | } 968 | -------------------------------------------------------------------------------- /vendor/php-curl-class/php-curl-class/src/Curl/Curl.php: -------------------------------------------------------------------------------- 1 | 66 | // CTL = 68 | // separators = "(" | ")" | "<" | ">" | "@" 69 | // | "," | ";" | ":" | "\" | <"> 70 | // | "/" | "[" | "]" | "?" | "=" 71 | // | "{" | "}" | SP | HT 72 | // SP = 73 | // HT = 74 | // <"> = 75 | '!', '#', '$', '%', '&', "'", '*', '+', '-', '.', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 76 | 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 77 | 'Y', 'Z', '^', '_', '`', 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 78 | 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', '|', '~', 79 | ); 80 | public static $RFC6265 = array( 81 | // RFC 6265: "US-ASCII characters excluding CTLs, whitespace DQUOTE, comma, semicolon, and backslash". 82 | // %x21 83 | '!', 84 | // %x23-2B 85 | '#', '$', '%', '&', "'", '(', ')', '*', '+', 86 | // %x2D-3A 87 | '-', '.', '/', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', ':', 88 | // %x3C-5B 89 | '<', '=', '>', '?', '@', 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 90 | 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', '[', 91 | // %x5D-7E 92 | ']', '^', '_', '`', 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 93 | 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', '{', '|', '}', '~', 94 | ); 95 | 96 | private static $deferredProperties = array( 97 | 'effectiveUrl', 98 | 'rfc2616', 99 | 'rfc6265', 100 | 'totalTime', 101 | ); 102 | 103 | /** 104 | * Construct 105 | * 106 | * @access public 107 | * @param $base_url 108 | * @throws \ErrorException 109 | */ 110 | public function __construct($base_url = null) 111 | { 112 | if (!extension_loaded('curl')) { 113 | throw new \ErrorException('cURL library is not loaded'); 114 | } 115 | 116 | $this->curl = curl_init(); 117 | $this->initialize($base_url); 118 | } 119 | 120 | /** 121 | * Before Send 122 | * 123 | * @access public 124 | * @param $callback 125 | */ 126 | public function beforeSend($callback) 127 | { 128 | $this->beforeSendCallback = $callback; 129 | } 130 | 131 | /** 132 | * Build Post Data 133 | * 134 | * @access public 135 | * @param $data 136 | * 137 | * @return array|string 138 | * @throws \ErrorException 139 | */ 140 | public function buildPostData($data) 141 | { 142 | $binary_data = false; 143 | 144 | // Return JSON-encoded string when the request's content-type is JSON and the data is serializable. 145 | if (isset($this->headers['Content-Type']) && 146 | preg_match($this->jsonPattern, $this->headers['Content-Type']) && 147 | ( 148 | is_array($data) || 149 | ( 150 | is_object($data) && 151 | interface_exists('JsonSerializable', false) && 152 | $data instanceof \JsonSerializable 153 | ) 154 | )) { 155 | $data = \Curl\Encoder::encodeJson($data); 156 | } elseif (is_array($data)) { 157 | // Manually build a single-dimensional array from a multi-dimensional array as using curl_setopt($ch, 158 | // CURLOPT_POSTFIELDS, $data) doesn't correctly handle multi-dimensional arrays when files are 159 | // referenced. 160 | if (ArrayUtil::isArrayMultidim($data)) { 161 | $data = ArrayUtil::arrayFlattenMultidim($data); 162 | } 163 | 164 | // Modify array values to ensure any referenced files are properly handled depending on the support of 165 | // the @filename API or CURLFile usage. This also fixes the warning "curl_setopt(): The usage of the 166 | // @filename API for file uploading is deprecated. Please use the CURLFile class instead". Ignore 167 | // non-file values prefixed with the @ character. 168 | foreach ($data as $key => $value) { 169 | if (is_string($value) && strpos($value, '@') === 0 && is_file(substr($value, 1))) { 170 | $binary_data = true; 171 | if (class_exists('CURLFile')) { 172 | $data[$key] = new \CURLFile(substr($value, 1)); 173 | } 174 | } elseif ($value instanceof \CURLFile) { 175 | $binary_data = true; 176 | } 177 | } 178 | } 179 | 180 | if (!$binary_data && 181 | (is_array($data) || is_object($data)) && 182 | ( 183 | !isset($this->headers['Content-Type']) || 184 | !preg_match('/^multipart\/form-data/', $this->headers['Content-Type']) 185 | )) { 186 | $data = http_build_query($data, '', '&'); 187 | } 188 | 189 | return $data; 190 | } 191 | 192 | /** 193 | * Call 194 | * 195 | * @access public 196 | */ 197 | public function call() 198 | { 199 | $args = func_get_args(); 200 | $function = array_shift($args); 201 | if (is_callable($function)) { 202 | array_unshift($args, $this); 203 | call_user_func_array($function, $args); 204 | } 205 | } 206 | 207 | /** 208 | * Close 209 | * 210 | * @access public 211 | */ 212 | public function close() 213 | { 214 | if (is_resource($this->curl)) { 215 | curl_close($this->curl); 216 | } 217 | $this->options = null; 218 | $this->jsonDecoder = null; 219 | $this->jsonDecoderArgs = null; 220 | $this->xmlDecoder = null; 221 | $this->xmlDecoderArgs = null; 222 | $this->defaultDecoder = null; 223 | } 224 | 225 | /** 226 | * Complete 227 | * 228 | * @access public 229 | * @param $callback 230 | */ 231 | public function complete($callback) 232 | { 233 | $this->completeCallback = $callback; 234 | } 235 | 236 | /** 237 | * Progress 238 | * 239 | * @access public 240 | * @param $callback 241 | */ 242 | public function progress($callback) 243 | { 244 | $this->setOpt(CURLOPT_PROGRESSFUNCTION, $callback); 245 | $this->setOpt(CURLOPT_NOPROGRESS, false); 246 | } 247 | 248 | /** 249 | * Delete 250 | * 251 | * @access public 252 | * @param $url 253 | * @param $query_parameters 254 | * @param $data 255 | * 256 | * @return mixed Returns the value provided by exec. 257 | */ 258 | public function delete($url, $query_parameters = array(), $data = array()) 259 | { 260 | if (is_array($url)) { 261 | $data = $query_parameters; 262 | $query_parameters = $url; 263 | $url = (string)$this->url; 264 | } 265 | 266 | $this->setUrl($url, $query_parameters); 267 | $this->setOpt(CURLOPT_CUSTOMREQUEST, 'DELETE'); 268 | 269 | // Avoid including a content-length header in DELETE requests unless there is a message body. The following 270 | // would include "Content-Length: 0" in the request header: 271 | // curl_setopt($ch, CURLOPT_POSTFIELDS, array()); 272 | // RFC 2616 4.3 Message Body: 273 | // The presence of a message-body in a request is signaled by the 274 | // inclusion of a Content-Length or Transfer-Encoding header field in 275 | // the request's message-headers. 276 | if (!empty($data)) { 277 | $this->setOpt(CURLOPT_POSTFIELDS, $this->buildPostData($data)); 278 | } 279 | return $this->exec(); 280 | } 281 | 282 | /** 283 | * Download 284 | * 285 | * @access public 286 | * @param $url 287 | * @param $mixed_filename 288 | * 289 | * @return boolean 290 | */ 291 | public function download($url, $mixed_filename) 292 | { 293 | if (is_callable($mixed_filename)) { 294 | $this->downloadCompleteCallback = $mixed_filename; 295 | $this->downloadFileName = null; 296 | $this->fileHandle = tmpfile(); 297 | } else { 298 | $filename = $mixed_filename; 299 | 300 | // Use a temporary file when downloading. Not using a temporary file can cause an error when an existing 301 | // file has already fully completed downloading and a new download is started with the same destination save 302 | // path. The download request will include header "Range: bytes=$filesize-" which is syntactically valid, 303 | // but unsatisfiable. 304 | $download_filename = $filename . '.pccdownload'; 305 | 306 | $mode = 'wb'; 307 | // Attempt to resume download only when a temporary download file exists and is not empty. 308 | if (is_file($download_filename) && $filesize = filesize($download_filename)) { 309 | $mode = 'ab'; 310 | $first_byte_position = $filesize; 311 | $range = $first_byte_position . '-'; 312 | $this->setOpt(CURLOPT_RANGE, $range); 313 | } 314 | $this->downloadFileName = $download_filename; 315 | $this->fileHandle = fopen($download_filename, $mode); 316 | 317 | // Move the downloaded temporary file to the destination save path. 318 | $this->downloadCompleteCallback = function ($instance, $fh) use ($download_filename, $filename) { 319 | // Close the open file handle before renaming the file. 320 | if (is_resource($fh)) { 321 | fclose($fh); 322 | } 323 | 324 | rename($download_filename, $filename); 325 | }; 326 | } 327 | 328 | $this->setOpt(CURLOPT_FILE, $this->fileHandle); 329 | $this->get($url); 330 | 331 | return ! $this->error; 332 | } 333 | 334 | /** 335 | * Error 336 | * 337 | * @access public 338 | * @param $callback 339 | */ 340 | public function error($callback) 341 | { 342 | $this->errorCallback = $callback; 343 | } 344 | 345 | /** 346 | * Exec 347 | * 348 | * @access public 349 | * @param $ch 350 | * 351 | * @return mixed Returns the value provided by parseResponse. 352 | */ 353 | public function exec($ch = null) 354 | { 355 | $this->attempts += 1; 356 | 357 | if ($this->jsonDecoder === null) { 358 | $this->setDefaultJsonDecoder(); 359 | } 360 | if ($this->xmlDecoder === null) { 361 | $this->setDefaultXmlDecoder(); 362 | } 363 | 364 | if ($ch === null) { 365 | $this->responseCookies = array(); 366 | $this->call($this->beforeSendCallback); 367 | $this->rawResponse = curl_exec($this->curl); 368 | $this->curlErrorCode = curl_errno($this->curl); 369 | $this->curlErrorMessage = curl_error($this->curl); 370 | } else { 371 | $this->rawResponse = curl_multi_getcontent($ch); 372 | $this->curlErrorMessage = curl_error($ch); 373 | } 374 | $this->curlError = $this->curlErrorCode !== 0; 375 | 376 | // Transfer the header callback data and release the temporary store to avoid memory leak. 377 | $this->rawResponseHeaders = $this->headerCallbackData->rawResponseHeaders; 378 | $this->responseCookies = $this->headerCallbackData->responseCookies; 379 | $this->headerCallbackData->rawResponseHeaders = ''; 380 | $this->headerCallbackData->responseCookies = array(); 381 | 382 | // Include additional error code information in error message when possible. 383 | if ($this->curlError && function_exists('curl_strerror')) { 384 | $this->curlErrorMessage = 385 | curl_strerror($this->curlErrorCode) . ( 386 | empty($this->curlErrorMessage) ? '' : ': ' . $this->curlErrorMessage 387 | ); 388 | } 389 | 390 | $this->httpStatusCode = $this->getInfo(CURLINFO_HTTP_CODE); 391 | $this->httpError = in_array(floor($this->httpStatusCode / 100), array(4, 5)); 392 | $this->error = $this->curlError || $this->httpError; 393 | $this->errorCode = $this->error ? ($this->curlError ? $this->curlErrorCode : $this->httpStatusCode) : 0; 394 | 395 | // NOTE: CURLINFO_HEADER_OUT set to true is required for requestHeaders 396 | // to not be empty (e.g. $curl->setOpt(CURLINFO_HEADER_OUT, true);). 397 | if ($this->getOpt(CURLINFO_HEADER_OUT) === true) { 398 | $this->requestHeaders = $this->parseRequestHeaders($this->getInfo(CURLINFO_HEADER_OUT)); 399 | } 400 | $this->responseHeaders = $this->parseResponseHeaders($this->rawResponseHeaders); 401 | $this->response = $this->parseResponse($this->responseHeaders, $this->rawResponse); 402 | 403 | $this->httpErrorMessage = ''; 404 | if ($this->error) { 405 | if (isset($this->responseHeaders['Status-Line'])) { 406 | $this->httpErrorMessage = $this->responseHeaders['Status-Line']; 407 | } 408 | } 409 | $this->errorMessage = $this->curlError ? $this->curlErrorMessage : $this->httpErrorMessage; 410 | 411 | // Reset select deferred properties so that they may be recalculated. 412 | unset($this->effectiveUrl); 413 | unset($this->totalTime); 414 | 415 | // Reset content-length header possibly set from a PUT or SEARCH request. 416 | $this->unsetHeader('Content-Length'); 417 | 418 | // Reset nobody setting possibly set from a HEAD request. 419 | $this->setOpt(CURLOPT_NOBODY, false); 420 | 421 | // Allow multicurl to attempt retry as needed. 422 | if ($this->isChildOfMultiCurl()) { 423 | return; 424 | } 425 | 426 | if ($this->attemptRetry()) { 427 | return $this->exec($ch); 428 | } 429 | 430 | $this->execDone(); 431 | 432 | return $this->response; 433 | } 434 | 435 | public function execDone() 436 | { 437 | if ($this->error) { 438 | $this->call($this->errorCallback); 439 | } else { 440 | $this->call($this->successCallback); 441 | } 442 | 443 | $this->call($this->completeCallback); 444 | 445 | // Close open file handles and reset the curl instance. 446 | if ($this->fileHandle !== null) { 447 | $this->downloadComplete($this->fileHandle); 448 | } 449 | } 450 | 451 | /** 452 | * Get 453 | * 454 | * @access public 455 | * @param $url 456 | * @param $data 457 | * 458 | * @return mixed Returns the value provided by exec. 459 | */ 460 | public function get($url, $data = array()) 461 | { 462 | if (is_array($url)) { 463 | $data = $url; 464 | $url = (string)$this->url; 465 | } 466 | $this->setUrl($url, $data); 467 | $this->setOpt(CURLOPT_CUSTOMREQUEST, 'GET'); 468 | $this->setOpt(CURLOPT_HTTPGET, true); 469 | return $this->exec(); 470 | } 471 | 472 | /** 473 | * Get Info 474 | * 475 | * @access public 476 | * @param $opt 477 | * 478 | * @return mixed 479 | */ 480 | public function getInfo($opt = null) 481 | { 482 | $args = array(); 483 | $args[] = $this->curl; 484 | 485 | if (func_num_args()) { 486 | $args[] = $opt; 487 | } 488 | 489 | return call_user_func_array('curl_getinfo', $args); 490 | } 491 | 492 | /** 493 | * Get Opt 494 | * 495 | * @access public 496 | * @param $option 497 | * 498 | * @return mixed 499 | */ 500 | public function getOpt($option) 501 | { 502 | return isset($this->options[$option]) ? $this->options[$option] : null; 503 | } 504 | 505 | /** 506 | * Head 507 | * 508 | * @access public 509 | * @param $url 510 | * @param $data 511 | * 512 | * @return mixed Returns the value provided by exec. 513 | */ 514 | public function head($url, $data = array()) 515 | { 516 | if (is_array($url)) { 517 | $data = $url; 518 | $url = (string)$this->url; 519 | } 520 | $this->setUrl($url, $data); 521 | $this->setOpt(CURLOPT_CUSTOMREQUEST, 'HEAD'); 522 | $this->setOpt(CURLOPT_NOBODY, true); 523 | return $this->exec(); 524 | } 525 | 526 | /** 527 | * Options 528 | * 529 | * @access public 530 | * @param $url 531 | * @param $data 532 | * 533 | * @return mixed Returns the value provided by exec. 534 | */ 535 | public function options($url, $data = array()) 536 | { 537 | if (is_array($url)) { 538 | $data = $url; 539 | $url = (string)$this->url; 540 | } 541 | $this->setUrl($url, $data); 542 | $this->setOpt(CURLOPT_CUSTOMREQUEST, 'OPTIONS'); 543 | return $this->exec(); 544 | } 545 | 546 | /** 547 | * Patch 548 | * 549 | * @access public 550 | * @param $url 551 | * @param $data 552 | * 553 | * @return mixed Returns the value provided by exec. 554 | */ 555 | public function patch($url, $data = array()) 556 | { 557 | if (is_array($url)) { 558 | $data = $url; 559 | $url = (string)$this->url; 560 | } 561 | 562 | if (is_array($data) && empty($data)) { 563 | $this->removeHeader('Content-Length'); 564 | } 565 | 566 | $this->setUrl($url); 567 | $this->setOpt(CURLOPT_CUSTOMREQUEST, 'PATCH'); 568 | $this->setOpt(CURLOPT_POSTFIELDS, $this->buildPostData($data)); 569 | return $this->exec(); 570 | } 571 | 572 | /** 573 | * Post 574 | * 575 | * @access public 576 | * @param $url 577 | * @param $data 578 | * @param $follow_303_with_post 579 | * If true, will cause 303 redirections to be followed using a POST request (default: false). 580 | * Notes: 581 | * - Redirections are only followed if the CURLOPT_FOLLOWLOCATION option is set to true. 582 | * - According to the HTTP specs (see [1]), a 303 redirection should be followed using 583 | * the GET method. 301 and 302 must not. 584 | * - In order to force a 303 redirection to be performed using the same method, the 585 | * underlying cURL object must be set in a special state (the CURLOPT_CURSTOMREQUEST 586 | * option must be set to the method to use after the redirection). Due to a limitation 587 | * of the cURL extension of PHP < 5.5.11 ([2], [3]) and of HHVM, it is not possible 588 | * to reset this option. Using these PHP engines, it is therefore impossible to 589 | * restore this behavior on an existing php-curl-class Curl object. 590 | * 591 | * @return mixed Returns the value provided by exec. 592 | * 593 | * [1] https://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10.3.2 594 | * [2] https://github.com/php/php-src/pull/531 595 | * [3] http://php.net/ChangeLog-5.php#5.5.11 596 | */ 597 | public function post($url, $data = '', $follow_303_with_post = false) 598 | { 599 | if (is_array($url)) { 600 | $follow_303_with_post = (bool)$data; 601 | $data = $url; 602 | $url = (string)$this->url; 603 | } 604 | 605 | $this->setUrl($url); 606 | 607 | if ($follow_303_with_post) { 608 | $this->setOpt(CURLOPT_CUSTOMREQUEST, 'POST'); 609 | } else { 610 | if (isset($this->options[CURLOPT_CUSTOMREQUEST])) { 611 | if ((version_compare(PHP_VERSION, '5.5.11') < 0) || defined('HHVM_VERSION')) { 612 | trigger_error( 613 | 'Due to technical limitations of PHP <= 5.5.11 and HHVM, it is not possible to ' 614 | . 'perform a post-redirect-get request using a php-curl-class Curl object that ' 615 | . 'has already been used to perform other types of requests. Either use a new ' 616 | . 'php-curl-class Curl object or upgrade your PHP engine.', 617 | E_USER_ERROR 618 | ); 619 | } else { 620 | $this->setOpt(CURLOPT_CUSTOMREQUEST, null); 621 | } 622 | } 623 | } 624 | 625 | $this->setOpt(CURLOPT_POST, true); 626 | $this->setOpt(CURLOPT_POSTFIELDS, $this->buildPostData($data)); 627 | return $this->exec(); 628 | } 629 | 630 | /** 631 | * Put 632 | * 633 | * @access public 634 | * @param $url 635 | * @param $data 636 | * 637 | * @return mixed Returns the value provided by exec. 638 | */ 639 | public function put($url, $data = array()) 640 | { 641 | if (is_array($url)) { 642 | $data = $url; 643 | $url = (string)$this->url; 644 | } 645 | $this->setUrl($url); 646 | $this->setOpt(CURLOPT_CUSTOMREQUEST, 'PUT'); 647 | $put_data = $this->buildPostData($data); 648 | if (empty($this->options[CURLOPT_INFILE]) && empty($this->options[CURLOPT_INFILESIZE])) { 649 | if (is_string($put_data)) { 650 | $this->setHeader('Content-Length', strlen($put_data)); 651 | } 652 | } 653 | if (!empty($put_data)) { 654 | $this->setOpt(CURLOPT_POSTFIELDS, $put_data); 655 | } 656 | return $this->exec(); 657 | } 658 | 659 | /** 660 | * Search 661 | * 662 | * @access public 663 | * @param $url 664 | * @param $data 665 | * 666 | * @return mixed Returns the value provided by exec. 667 | */ 668 | public function search($url, $data = array()) 669 | { 670 | if (is_array($url)) { 671 | $data = $url; 672 | $url = (string)$this->url; 673 | } 674 | $this->setUrl($url); 675 | $this->setOpt(CURLOPT_CUSTOMREQUEST, 'SEARCH'); 676 | $put_data = $this->buildPostData($data); 677 | if (empty($this->options[CURLOPT_INFILE]) && empty($this->options[CURLOPT_INFILESIZE])) { 678 | if (is_string($put_data)) { 679 | $this->setHeader('Content-Length', strlen($put_data)); 680 | } 681 | } 682 | if (!empty($put_data)) { 683 | $this->setOpt(CURLOPT_POSTFIELDS, $put_data); 684 | } 685 | return $this->exec(); 686 | } 687 | 688 | /** 689 | * Set Basic Authentication 690 | * 691 | * @access public 692 | * @param $username 693 | * @param $password 694 | */ 695 | public function setBasicAuthentication($username, $password = '') 696 | { 697 | $this->setOpt(CURLOPT_HTTPAUTH, CURLAUTH_BASIC); 698 | $this->setOpt(CURLOPT_USERPWD, $username . ':' . $password); 699 | } 700 | 701 | /** 702 | * Set Digest Authentication 703 | * 704 | * @access public 705 | * @param $username 706 | * @param $password 707 | */ 708 | public function setDigestAuthentication($username, $password = '') 709 | { 710 | $this->setOpt(CURLOPT_HTTPAUTH, CURLAUTH_DIGEST); 711 | $this->setOpt(CURLOPT_USERPWD, $username . ':' . $password); 712 | } 713 | 714 | /** 715 | * Set Cookie 716 | * 717 | * @access public 718 | * @param $key 719 | * @param $value 720 | */ 721 | public function setCookie($key, $value) 722 | { 723 | $this->setEncodedCookie($key, $value); 724 | $this->buildCookies(); 725 | } 726 | 727 | /** 728 | * Set Cookies 729 | * 730 | * @access public 731 | * @param $cookies 732 | */ 733 | public function setCookies($cookies) 734 | { 735 | foreach ($cookies as $key => $value) { 736 | $this->setEncodedCookie($key, $value); 737 | } 738 | $this->buildCookies(); 739 | } 740 | 741 | /** 742 | * Get Cookie 743 | * 744 | * @access public 745 | * @param $key 746 | * 747 | * @return mixed 748 | */ 749 | public function getCookie($key) 750 | { 751 | return $this->getResponseCookie($key); 752 | } 753 | 754 | /** 755 | * Get Response Cookie 756 | * 757 | * @access public 758 | * @param $key 759 | * 760 | * @return mixed 761 | */ 762 | public function getResponseCookie($key) 763 | { 764 | return isset($this->responseCookies[$key]) ? $this->responseCookies[$key] : null; 765 | } 766 | 767 | /** 768 | * Set Max Filesize 769 | * 770 | * @access public 771 | * @param $bytes 772 | */ 773 | public function setMaxFilesize($bytes) 774 | { 775 | // Make compatible with PHP version both before and after 5.5.0. PHP 5.5.0 added the cURL resource as the first 776 | // argument to the CURLOPT_PROGRESSFUNCTION callback. 777 | $gte_v550 = version_compare(PHP_VERSION, '5.5.0') >= 0; 778 | if ($gte_v550) { 779 | $callback = function ($resource, $download_size, $downloaded, $upload_size, $uploaded) use ($bytes) { 780 | // Abort the transfer when $downloaded bytes exceeds maximum $bytes by returning a non-zero value. 781 | return $downloaded > $bytes ? 1 : 0; 782 | }; 783 | } else { 784 | $callback = function ($download_size, $downloaded, $upload_size, $uploaded) use ($bytes) { 785 | return $downloaded > $bytes ? 1 : 0; 786 | }; 787 | } 788 | 789 | $this->progress($callback); 790 | } 791 | 792 | /** 793 | * Set Port 794 | * 795 | * @access public 796 | * @param $port 797 | */ 798 | public function setPort($port) 799 | { 800 | $this->setOpt(CURLOPT_PORT, intval($port)); 801 | } 802 | 803 | /** 804 | * Set Connect Timeout 805 | * 806 | * @access public 807 | * @param $seconds 808 | */ 809 | public function setConnectTimeout($seconds) 810 | { 811 | $this->setOpt(CURLOPT_CONNECTTIMEOUT, $seconds); 812 | } 813 | 814 | /** 815 | * Set Cookie String 816 | * 817 | * @access public 818 | * @param $string 819 | * 820 | * @return bool 821 | */ 822 | public function setCookieString($string) 823 | { 824 | return $this->setOpt(CURLOPT_COOKIE, $string); 825 | } 826 | 827 | /** 828 | * Set Cookie File 829 | * 830 | * @access public 831 | * @param $cookie_file 832 | * 833 | * @return boolean 834 | */ 835 | public function setCookieFile($cookie_file) 836 | { 837 | return $this->setOpt(CURLOPT_COOKIEFILE, $cookie_file); 838 | } 839 | 840 | /** 841 | * Set Cookie Jar 842 | * 843 | * @access public 844 | * @param $cookie_jar 845 | * 846 | * @return boolean 847 | */ 848 | public function setCookieJar($cookie_jar) 849 | { 850 | return $this->setOpt(CURLOPT_COOKIEJAR, $cookie_jar); 851 | } 852 | 853 | /** 854 | * Set Default JSON Decoder 855 | * 856 | * @access public 857 | * @param $assoc 858 | * @param $depth 859 | * @param $options 860 | */ 861 | public function setDefaultJsonDecoder() 862 | { 863 | $this->jsonDecoder = '\Curl\Decoder::decodeJson'; 864 | $this->jsonDecoderArgs = func_get_args(); 865 | } 866 | 867 | /** 868 | * Set Default XML Decoder 869 | * 870 | * @access public 871 | * @param $class_name 872 | * @param $options 873 | * @param $ns 874 | * @param $is_prefix 875 | */ 876 | public function setDefaultXmlDecoder() 877 | { 878 | $this->xmlDecoder = '\Curl\Decoder::decodeXml'; 879 | $this->xmlDecoderArgs = func_get_args(); 880 | } 881 | 882 | /** 883 | * Set Default Decoder 884 | * 885 | * @access public 886 | * @param $mixed boolean|callable|string 887 | */ 888 | public function setDefaultDecoder($mixed = 'json') 889 | { 890 | if ($mixed === false) { 891 | $this->defaultDecoder = false; 892 | } elseif (is_callable($mixed)) { 893 | $this->defaultDecoder = $mixed; 894 | } else { 895 | if ($mixed === 'json') { 896 | $this->defaultDecoder = '\Curl\Decoder::decodeJson'; 897 | } elseif ($mixed === 'xml') { 898 | $this->defaultDecoder = '\Curl\Decoder::decodeXml'; 899 | } 900 | } 901 | } 902 | 903 | /** 904 | * Set Default Timeout 905 | * 906 | * @access public 907 | */ 908 | public function setDefaultTimeout() 909 | { 910 | $this->setTimeout(self::DEFAULT_TIMEOUT); 911 | } 912 | 913 | /** 914 | * Set Default User Agent 915 | * 916 | * @access public 917 | */ 918 | public function setDefaultUserAgent() 919 | { 920 | $user_agent = 'PHP-Curl-Class/' . self::VERSION . ' (+https://github.com/php-curl-class/php-curl-class)'; 921 | $user_agent .= ' PHP/' . PHP_VERSION; 922 | $curl_version = curl_version(); 923 | $user_agent .= ' curl/' . $curl_version['version']; 924 | $this->setUserAgent($user_agent); 925 | } 926 | 927 | /** 928 | * Set Header 929 | * 930 | * Add extra header to include in the request. 931 | * 932 | * @access public 933 | * @param $key 934 | * @param $value 935 | */ 936 | public function setHeader($key, $value) 937 | { 938 | $this->headers[$key] = $value; 939 | $headers = array(); 940 | foreach ($this->headers as $key => $value) { 941 | $headers[] = $key . ': ' . $value; 942 | } 943 | $this->setOpt(CURLOPT_HTTPHEADER, $headers); 944 | } 945 | 946 | /** 947 | * Set Headers 948 | * 949 | * Add extra headers to include in the request. 950 | * 951 | * @access public 952 | * @param $headers 953 | */ 954 | public function setHeaders($headers) 955 | { 956 | foreach ($headers as $key => $value) { 957 | $this->headers[$key] = $value; 958 | } 959 | 960 | $headers = array(); 961 | foreach ($this->headers as $key => $value) { 962 | $headers[] = $key . ': ' . $value; 963 | } 964 | $this->setOpt(CURLOPT_HTTPHEADER, $headers); 965 | } 966 | 967 | /** 968 | * Set JSON Decoder 969 | * 970 | * @access public 971 | * @param $mixed boolean|callable 972 | */ 973 | public function setJsonDecoder($mixed) 974 | { 975 | if ($mixed === false || is_callable($mixed)) { 976 | $this->jsonDecoder = $mixed; 977 | $this->jsonDecoderArgs = array(); 978 | } 979 | } 980 | 981 | /** 982 | * Set XML Decoder 983 | * 984 | * @access public 985 | * @param $mixed boolean|callable 986 | */ 987 | public function setXmlDecoder($mixed) 988 | { 989 | if ($mixed === false || is_callable($mixed)) { 990 | $this->xmlDecoder = $mixed; 991 | $this->xmlDecoderArgs = array(); 992 | } 993 | } 994 | 995 | /** 996 | * Set Opt 997 | * 998 | * @access public 999 | * @param $option 1000 | * @param $value 1001 | * 1002 | * @return boolean 1003 | */ 1004 | public function setOpt($option, $value) 1005 | { 1006 | $required_options = array( 1007 | CURLOPT_RETURNTRANSFER => 'CURLOPT_RETURNTRANSFER', 1008 | ); 1009 | 1010 | if (in_array($option, array_keys($required_options), true) && $value !== true) { 1011 | trigger_error($required_options[$option] . ' is a required option', E_USER_WARNING); 1012 | } 1013 | 1014 | $success = curl_setopt($this->curl, $option, $value); 1015 | if ($success) { 1016 | $this->options[$option] = $value; 1017 | } 1018 | return $success; 1019 | } 1020 | 1021 | /** 1022 | * Set Opts 1023 | * 1024 | * @access public 1025 | * @param $options 1026 | * 1027 | * @return boolean 1028 | * Returns true if all options were successfully set. If an option could not be successfully set, false is 1029 | * immediately returned, ignoring any future options in the options array. Similar to curl_setopt_array(). 1030 | */ 1031 | public function setOpts($options) 1032 | { 1033 | foreach ($options as $option => $value) { 1034 | if (!$this->setOpt($option, $value)) { 1035 | return false; 1036 | } 1037 | } 1038 | return true; 1039 | } 1040 | 1041 | /** 1042 | * Set Proxy 1043 | * 1044 | * Set an HTTP proxy to tunnel requests through. 1045 | * 1046 | * @access public 1047 | * @param $proxy - The HTTP proxy to tunnel requests through. May include port number. 1048 | * @param $port - The port number of the proxy to connect to. This port number can also be set in $proxy. 1049 | * @param $username - The username to use for the connection to the proxy. 1050 | * @param $password - The password to use for the connection to the proxy. 1051 | */ 1052 | public function setProxy($proxy, $port = null, $username = null, $password = null) 1053 | { 1054 | $this->setOpt(CURLOPT_PROXY, $proxy); 1055 | if ($port !== null) { 1056 | $this->setOpt(CURLOPT_PROXYPORT, $port); 1057 | } 1058 | if ($username !== null && $password !== null) { 1059 | $this->setOpt(CURLOPT_PROXYUSERPWD, $username . ':' . $password); 1060 | } 1061 | } 1062 | 1063 | /** 1064 | * Set Proxy Auth 1065 | * 1066 | * Set the HTTP authentication method(s) to use for the proxy connection. 1067 | * 1068 | * @access public 1069 | * @param $auth 1070 | */ 1071 | public function setProxyAuth($auth) 1072 | { 1073 | $this->setOpt(CURLOPT_PROXYAUTH, $auth); 1074 | } 1075 | 1076 | /** 1077 | * Set Proxy Type 1078 | * 1079 | * Set the proxy protocol type. 1080 | * 1081 | * @access public 1082 | * @param $type 1083 | */ 1084 | public function setProxyType($type) 1085 | { 1086 | $this->setOpt(CURLOPT_PROXYTYPE, $type); 1087 | } 1088 | 1089 | /** 1090 | * Set Proxy Tunnel 1091 | * 1092 | * Set the proxy to tunnel through HTTP proxy. 1093 | * 1094 | * @access public 1095 | * @param $tunnel boolean 1096 | */ 1097 | public function setProxyTunnel($tunnel = true) 1098 | { 1099 | $this->setOpt(CURLOPT_HTTPPROXYTUNNEL, $tunnel); 1100 | } 1101 | 1102 | /** 1103 | * Unset Proxy 1104 | * 1105 | * Disable use of the proxy. 1106 | * 1107 | * @access public 1108 | */ 1109 | public function unsetProxy() 1110 | { 1111 | $this->setOpt(CURLOPT_PROXY, null); 1112 | } 1113 | 1114 | /** 1115 | * Set Referer 1116 | * 1117 | * @access public 1118 | * @param $referer 1119 | */ 1120 | public function setReferer($referer) 1121 | { 1122 | $this->setReferrer($referer); 1123 | } 1124 | 1125 | /** 1126 | * Set Referrer 1127 | * 1128 | * @access public 1129 | * @param $referrer 1130 | */ 1131 | public function setReferrer($referrer) 1132 | { 1133 | $this->setOpt(CURLOPT_REFERER, $referrer); 1134 | } 1135 | 1136 | /** 1137 | * Set Retry 1138 | * 1139 | * Number of retries to attempt or decider callable. 1140 | * 1141 | * When using a number of retries to attempt, the maximum number of attempts 1142 | * for the request is $maximum_number_of_retries + 1. 1143 | * 1144 | * When using a callable decider, the request will be retried until the 1145 | * function returns a value which evaluates to false. 1146 | * 1147 | * @access public 1148 | * @param $mixed 1149 | */ 1150 | public function setRetry($mixed) 1151 | { 1152 | if (is_callable($mixed)) { 1153 | $this->retryDecider = $mixed; 1154 | } elseif (is_int($mixed)) { 1155 | $maximum_number_of_retries = $mixed; 1156 | $this->remainingRetries = $maximum_number_of_retries; 1157 | } 1158 | } 1159 | 1160 | /** 1161 | * Set Timeout 1162 | * 1163 | * @access public 1164 | * @param $seconds 1165 | */ 1166 | public function setTimeout($seconds) 1167 | { 1168 | $this->setOpt(CURLOPT_TIMEOUT, $seconds); 1169 | } 1170 | 1171 | /** 1172 | * Set Url 1173 | * 1174 | * @access public 1175 | * @param $url 1176 | * @param $mixed_data 1177 | */ 1178 | public function setUrl($url, $mixed_data = '') 1179 | { 1180 | $built_url = $this->buildUrl($url, $mixed_data); 1181 | 1182 | if ($this->url === null) { 1183 | $this->url = (string)new Url($built_url); 1184 | } else { 1185 | $this->url = (string)new Url($this->url, $built_url); 1186 | } 1187 | 1188 | $this->setOpt(CURLOPT_URL, $this->url); 1189 | } 1190 | 1191 | /** 1192 | * Set User Agent 1193 | * 1194 | * @access public 1195 | * @param $user_agent 1196 | */ 1197 | public function setUserAgent($user_agent) 1198 | { 1199 | $this->setOpt(CURLOPT_USERAGENT, $user_agent); 1200 | } 1201 | 1202 | /** 1203 | * Attempt Retry 1204 | * 1205 | * @access public 1206 | */ 1207 | public function attemptRetry() 1208 | { 1209 | $attempt_retry = false; 1210 | if ($this->error) { 1211 | if ($this->retryDecider === null) { 1212 | $attempt_retry = $this->remainingRetries >= 1; 1213 | } else { 1214 | $attempt_retry = call_user_func($this->retryDecider, $this); 1215 | } 1216 | if ($attempt_retry) { 1217 | $this->retries += 1; 1218 | if ($this->remainingRetries) { 1219 | $this->remainingRetries -= 1; 1220 | } 1221 | } 1222 | } 1223 | return $attempt_retry; 1224 | } 1225 | 1226 | /** 1227 | * Success 1228 | * 1229 | * @access public 1230 | * @param $callback 1231 | */ 1232 | public function success($callback) 1233 | { 1234 | $this->successCallback = $callback; 1235 | } 1236 | 1237 | /** 1238 | * Unset Header 1239 | * 1240 | * Remove extra header previously set using Curl::setHeader(). 1241 | * 1242 | * @access public 1243 | * @param $key 1244 | */ 1245 | public function unsetHeader($key) 1246 | { 1247 | unset($this->headers[$key]); 1248 | $headers = array(); 1249 | foreach ($this->headers as $key => $value) { 1250 | $headers[] = $key . ': ' . $value; 1251 | } 1252 | $this->setOpt(CURLOPT_HTTPHEADER, $headers); 1253 | } 1254 | 1255 | /** 1256 | * Remove Header 1257 | * 1258 | * Remove an internal header from the request. 1259 | * Using `curl -H "Host:" ...' is equivalent to $curl->removeHeader('Host');. 1260 | * 1261 | * @access public 1262 | * @param $key 1263 | */ 1264 | public function removeHeader($key) 1265 | { 1266 | $this->setHeader($key, ''); 1267 | } 1268 | 1269 | /** 1270 | * Verbose 1271 | * 1272 | * @access public 1273 | * @param bool $on 1274 | * @param resource $output 1275 | */ 1276 | public function verbose($on = true, $output = STDERR) 1277 | { 1278 | // Turn off CURLINFO_HEADER_OUT for verbose to work. This has the side 1279 | // effect of causing Curl::requestHeaders to be empty. 1280 | if ($on) { 1281 | $this->setOpt(CURLINFO_HEADER_OUT, false); 1282 | } 1283 | $this->setOpt(CURLOPT_VERBOSE, $on); 1284 | $this->setOpt(CURLOPT_STDERR, $output); 1285 | } 1286 | 1287 | /** 1288 | * Reset 1289 | * 1290 | * @access public 1291 | */ 1292 | public function reset() 1293 | { 1294 | if (function_exists('curl_reset') && is_resource($this->curl)) { 1295 | curl_reset($this->curl); 1296 | } else { 1297 | $this->curl = curl_init(); 1298 | } 1299 | 1300 | $this->initialize(); 1301 | } 1302 | 1303 | public function getCurl() 1304 | { 1305 | return $this->curl; 1306 | } 1307 | 1308 | public function getId() 1309 | { 1310 | return $this->id; 1311 | } 1312 | 1313 | public function isError() 1314 | { 1315 | return $this->error; 1316 | } 1317 | 1318 | public function getErrorCode() 1319 | { 1320 | return $this->errorCode; 1321 | } 1322 | 1323 | public function getErrorMessage() 1324 | { 1325 | return $this->errorMessage; 1326 | } 1327 | 1328 | public function isCurlError() 1329 | { 1330 | return $this->curlError; 1331 | } 1332 | 1333 | public function getCurlErrorCode() 1334 | { 1335 | return $this->curlErrorCode; 1336 | } 1337 | 1338 | public function getCurlErrorMessage() 1339 | { 1340 | return $this->curlErrorMessage; 1341 | } 1342 | 1343 | public function isHttpError() 1344 | { 1345 | return $this->httpError; 1346 | } 1347 | 1348 | public function getHttpStatusCode() 1349 | { 1350 | return $this->httpStatusCode; 1351 | } 1352 | 1353 | public function getHttpErrorMessage() 1354 | { 1355 | return $this->httpErrorMessage; 1356 | } 1357 | 1358 | public function getUrl() 1359 | { 1360 | return $this->url; 1361 | } 1362 | 1363 | public function getRequestHeaders() 1364 | { 1365 | return $this->requestHeaders; 1366 | } 1367 | 1368 | public function getResponseHeaders() 1369 | { 1370 | return $this->responseHeaders; 1371 | } 1372 | 1373 | public function getRawResponseHeaders() 1374 | { 1375 | return $this->rawResponseHeaders; 1376 | } 1377 | 1378 | public function getResponseCookies() 1379 | { 1380 | return $this->responseCookies; 1381 | } 1382 | 1383 | public function getResponse() 1384 | { 1385 | return $this->response; 1386 | } 1387 | 1388 | public function getRawResponse() 1389 | { 1390 | return $this->rawResponse; 1391 | } 1392 | 1393 | public function getBeforeSendCallback() 1394 | { 1395 | return $this->beforeSendCallback; 1396 | } 1397 | 1398 | public function getDownloadCompleteCallback() 1399 | { 1400 | return $this->downloadCompleteCallback; 1401 | } 1402 | 1403 | public function getDownloadFileName() 1404 | { 1405 | return $this->downloadFileName; 1406 | } 1407 | 1408 | public function getSuccessCallback() 1409 | { 1410 | return $this->successCallback; 1411 | } 1412 | 1413 | public function getErrorCallback() 1414 | { 1415 | return $this->errorCallback; 1416 | } 1417 | 1418 | public function getCompleteCallback() 1419 | { 1420 | return $this->completeCallback; 1421 | } 1422 | 1423 | public function getFileHandle() 1424 | { 1425 | return $this->fileHandle; 1426 | } 1427 | 1428 | public function getAttempts() 1429 | { 1430 | return $this->attempts; 1431 | } 1432 | 1433 | public function getRetries() 1434 | { 1435 | return $this->retries; 1436 | } 1437 | 1438 | public function isChildOfMultiCurl() 1439 | { 1440 | return $this->childOfMultiCurl; 1441 | } 1442 | 1443 | public function getRemainingRetries() 1444 | { 1445 | return $this->remainingRetries; 1446 | } 1447 | 1448 | public function getRetryDecider() 1449 | { 1450 | return $this->retryDecider; 1451 | } 1452 | 1453 | public function getJsonDecoder() 1454 | { 1455 | return $this->jsonDecoder; 1456 | } 1457 | 1458 | public function getXmlDecoder() 1459 | { 1460 | return $this->xmlDecoder; 1461 | } 1462 | 1463 | /** 1464 | * Destruct 1465 | * 1466 | * @access public 1467 | */ 1468 | public function __destruct() 1469 | { 1470 | $this->close(); 1471 | } 1472 | 1473 | public function __get($name) 1474 | { 1475 | $return = null; 1476 | if (in_array($name, self::$deferredProperties) && is_callable(array($this, $getter = '__get_' . $name))) { 1477 | $return = $this->$name = $this->$getter(); 1478 | } 1479 | return $return; 1480 | } 1481 | 1482 | /** 1483 | * Get Effective Url 1484 | * 1485 | * @access private 1486 | */ 1487 | private function __get_effectiveUrl() 1488 | { 1489 | return $this->getInfo(CURLINFO_EFFECTIVE_URL); 1490 | } 1491 | 1492 | /** 1493 | * Get RFC 2616 1494 | * 1495 | * @access private 1496 | */ 1497 | private function __get_rfc2616() 1498 | { 1499 | return array_fill_keys(self::$RFC2616, true); 1500 | } 1501 | 1502 | /** 1503 | * Get RFC 6265 1504 | * 1505 | * @access private 1506 | */ 1507 | private function __get_rfc6265() 1508 | { 1509 | return array_fill_keys(self::$RFC6265, true); 1510 | } 1511 | 1512 | /** 1513 | * Get Total Time 1514 | * 1515 | * @access private 1516 | */ 1517 | private function __get_totalTime() 1518 | { 1519 | return $this->getInfo(CURLINFO_TOTAL_TIME); 1520 | } 1521 | 1522 | /** 1523 | * Build Cookies 1524 | * 1525 | * @access private 1526 | */ 1527 | private function buildCookies() 1528 | { 1529 | // Avoid using http_build_query() as unnecessary encoding is performed. 1530 | // http_build_query($this->cookies, '', '; '); 1531 | $this->setOpt(CURLOPT_COOKIE, implode('; ', array_map(function ($k, $v) { 1532 | return $k . '=' . $v; 1533 | }, array_keys($this->cookies), array_values($this->cookies)))); 1534 | } 1535 | 1536 | /** 1537 | * Build Url 1538 | * 1539 | * @access private 1540 | * @param $url 1541 | * @param $mixed_data 1542 | * 1543 | * @return string 1544 | */ 1545 | private function buildUrl($url, $mixed_data = '') 1546 | { 1547 | $query_string = ''; 1548 | if (!empty($mixed_data)) { 1549 | $query_mark = strpos($url, '?') > 0 ? '&' : '?'; 1550 | if (is_string($mixed_data)) { 1551 | $query_string .= $query_mark . $mixed_data; 1552 | } elseif (is_array($mixed_data)) { 1553 | $query_string .= $query_mark . http_build_query($mixed_data, '', '&'); 1554 | } 1555 | } 1556 | return $url . $query_string; 1557 | } 1558 | 1559 | /** 1560 | * Download Complete 1561 | * 1562 | * @access private 1563 | * @param $fh 1564 | */ 1565 | private function downloadComplete($fh) 1566 | { 1567 | if ($this->error && is_file($this->downloadFileName)) { 1568 | @unlink($this->downloadFileName); 1569 | } elseif (!$this->error && $this->downloadCompleteCallback) { 1570 | rewind($fh); 1571 | $this->call($this->downloadCompleteCallback, $fh); 1572 | $this->downloadCompleteCallback = null; 1573 | } 1574 | 1575 | if (is_resource($fh)) { 1576 | fclose($fh); 1577 | } 1578 | 1579 | // Fix "PHP Notice: Use of undefined constant STDOUT" when reading the 1580 | // PHP script from stdin. Using null causes "Warning: curl_setopt(): 1581 | // supplied argument is not a valid File-Handle resource". 1582 | if (!defined('STDOUT')) { 1583 | define('STDOUT', fopen('php://stdout', 'w')); 1584 | } 1585 | 1586 | // Reset CURLOPT_FILE with STDOUT to avoid: "curl_exec(): CURLOPT_FILE 1587 | // resource has gone away, resetting to default". 1588 | $this->setOpt(CURLOPT_FILE, STDOUT); 1589 | 1590 | // Reset CURLOPT_RETURNTRANSFER to tell cURL to return subsequent 1591 | // responses as the return value of curl_exec(). Without this, 1592 | // curl_exec() will revert to returning boolean values. 1593 | $this->setOpt(CURLOPT_RETURNTRANSFER, true); 1594 | } 1595 | 1596 | /** 1597 | * Parse Headers 1598 | * 1599 | * @access private 1600 | * @param $raw_headers 1601 | * 1602 | * @return array 1603 | */ 1604 | private function parseHeaders($raw_headers) 1605 | { 1606 | $raw_headers = preg_split('/\r\n/', $raw_headers, null, PREG_SPLIT_NO_EMPTY); 1607 | $http_headers = new CaseInsensitiveArray(); 1608 | 1609 | $raw_headers_count = count($raw_headers); 1610 | for ($i = 1; $i < $raw_headers_count; $i++) { 1611 | if (strpos($raw_headers[$i], ':') !== false) { 1612 | list($key, $value) = explode(':', $raw_headers[$i], 2); 1613 | $key = trim($key); 1614 | $value = trim($value); 1615 | // Use isset() as array_key_exists() and ArrayAccess are not compatible. 1616 | if (isset($http_headers[$key])) { 1617 | $http_headers[$key] .= ',' . $value; 1618 | } else { 1619 | $http_headers[$key] = $value; 1620 | } 1621 | } 1622 | } 1623 | 1624 | return array(isset($raw_headers['0']) ? $raw_headers['0'] : '', $http_headers); 1625 | } 1626 | 1627 | /** 1628 | * Parse Request Headers 1629 | * 1630 | * @access private 1631 | * @param $raw_headers 1632 | * 1633 | * @return \Curl\CaseInsensitiveArray 1634 | */ 1635 | private function parseRequestHeaders($raw_headers) 1636 | { 1637 | $request_headers = new CaseInsensitiveArray(); 1638 | list($first_line, $headers) = $this->parseHeaders($raw_headers); 1639 | $request_headers['Request-Line'] = $first_line; 1640 | foreach ($headers as $key => $value) { 1641 | $request_headers[$key] = $value; 1642 | } 1643 | return $request_headers; 1644 | } 1645 | 1646 | /** 1647 | * Parse Response 1648 | * 1649 | * @access private 1650 | * @param $response_headers 1651 | * @param $raw_response 1652 | * 1653 | * @return mixed 1654 | * If the response content-type is json: 1655 | * Returns the json decoder's return value: A stdClass object when the default json decoder is used. 1656 | * If the response content-type is xml: 1657 | * Returns the xml decoder's return value: A SimpleXMLElement object when the default xml decoder is used. 1658 | * If the response content-type is something else: 1659 | * Returns the original raw response unless a default decoder has been set. 1660 | * If the response content-type cannot be determined: 1661 | * Returns the original raw response. 1662 | */ 1663 | private function parseResponse($response_headers, $raw_response) 1664 | { 1665 | $response = $raw_response; 1666 | if (isset($response_headers['Content-Type'])) { 1667 | if (preg_match($this->jsonPattern, $response_headers['Content-Type'])) { 1668 | if ($this->jsonDecoder) { 1669 | $args = $this->jsonDecoderArgs; 1670 | array_unshift($args, $response); 1671 | $response = call_user_func_array($this->jsonDecoder, $args); 1672 | } 1673 | } elseif (preg_match($this->xmlPattern, $response_headers['Content-Type'])) { 1674 | if ($this->xmlDecoder) { 1675 | $args = $this->xmlDecoderArgs; 1676 | array_unshift($args, $response); 1677 | $response = call_user_func_array($this->xmlDecoder, $args); 1678 | } 1679 | } else { 1680 | if ($this->defaultDecoder) { 1681 | $response = call_user_func($this->defaultDecoder, $response); 1682 | } 1683 | } 1684 | } 1685 | 1686 | return $response; 1687 | } 1688 | 1689 | /** 1690 | * Parse Response Headers 1691 | * 1692 | * @access private 1693 | * @param $raw_response_headers 1694 | * 1695 | * @return \Curl\CaseInsensitiveArray 1696 | */ 1697 | private function parseResponseHeaders($raw_response_headers) 1698 | { 1699 | $response_header_array = explode("\r\n\r\n", $raw_response_headers); 1700 | $response_header = ''; 1701 | for ($i = count($response_header_array) - 1; $i >= 0; $i--) { 1702 | if (stripos($response_header_array[$i], 'HTTP/') === 0) { 1703 | $response_header = $response_header_array[$i]; 1704 | break; 1705 | } 1706 | } 1707 | 1708 | $response_headers = new CaseInsensitiveArray(); 1709 | list($first_line, $headers) = $this->parseHeaders($response_header); 1710 | $response_headers['Status-Line'] = $first_line; 1711 | foreach ($headers as $key => $value) { 1712 | $response_headers[$key] = $value; 1713 | } 1714 | return $response_headers; 1715 | } 1716 | 1717 | /** 1718 | * Set Encoded Cookie 1719 | * 1720 | * @access private 1721 | * @param $key 1722 | * @param $value 1723 | */ 1724 | private function setEncodedCookie($key, $value) 1725 | { 1726 | $name_chars = array(); 1727 | foreach (str_split($key) as $name_char) { 1728 | if (isset($this->rfc2616[$name_char])) { 1729 | $name_chars[] = $name_char; 1730 | } else { 1731 | $name_chars[] = rawurlencode($name_char); 1732 | } 1733 | } 1734 | 1735 | $value_chars = array(); 1736 | foreach (str_split($value) as $value_char) { 1737 | if (isset($this->rfc6265[$value_char])) { 1738 | $value_chars[] = $value_char; 1739 | } else { 1740 | $value_chars[] = rawurlencode($value_char); 1741 | } 1742 | } 1743 | 1744 | $this->cookies[implode('', $name_chars)] = implode('', $value_chars); 1745 | } 1746 | 1747 | /** 1748 | * Initialize 1749 | * 1750 | * @access private 1751 | * @param $base_url 1752 | */ 1753 | private function initialize($base_url = null) 1754 | { 1755 | $this->id = uniqid('', true); 1756 | $this->setDefaultUserAgent(); 1757 | $this->setDefaultTimeout(); 1758 | $this->setOpt(CURLINFO_HEADER_OUT, true); 1759 | 1760 | // Create a placeholder to temporarily store the header callback data. 1761 | $header_callback_data = new \stdClass(); 1762 | $header_callback_data->rawResponseHeaders = ''; 1763 | $header_callback_data->responseCookies = array(); 1764 | $this->headerCallbackData = $header_callback_data; 1765 | $this->setOpt(CURLOPT_HEADERFUNCTION, createHeaderCallback($header_callback_data)); 1766 | 1767 | $this->setOpt(CURLOPT_RETURNTRANSFER, true); 1768 | $this->headers = new CaseInsensitiveArray(); 1769 | $this->setUrl($base_url); 1770 | } 1771 | } 1772 | 1773 | /** 1774 | * Create Header Callback 1775 | * 1776 | * Gather headers and parse cookies as response headers are received. Keep this function separate from the class so that 1777 | * unset($curl) automatically calls __destruct() as expected. Otherwise, manually calling $curl->close() will be 1778 | * necessary to prevent a memory leak. 1779 | * 1780 | * @param $header_callback_data 1781 | * 1782 | * @return callable 1783 | */ 1784 | function createHeaderCallback($header_callback_data) { 1785 | return function ($ch, $header) use ($header_callback_data) { 1786 | if (preg_match('/^Set-Cookie:\s*([^=]+)=([^;]+)/mi', $header, $cookie) === 1) { 1787 | $header_callback_data->responseCookies[$cookie[1]] = trim($cookie[2], " \n\r\t\0\x0B"); 1788 | } 1789 | $header_callback_data->rawResponseHeaders .= $header; 1790 | return strlen($header); 1791 | }; 1792 | } 1793 | --------------------------------------------------------------------------------