├── .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 = '
'
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 "No ";
66 | echo "Nama ";
67 | echo "Harga ";
68 | echo "Lokasi ";
69 | echo "Foto ";
70 | echo "Status ";
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 "
";
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 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 | Made with ❤️ by Wahyu Arif Purnomo
22 |
23 |
24 |
25 | Terminal Shopee Scrape
26 |
27 |
28 |
29 |
30 | HTML Shopee Scrape
31 |
32 |
33 |
34 |
35 | Export to Excel
36 |
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 | 
85 |
86 | ##### Windows
87 |
88 | 
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 | [](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://github.com/php-curl-class/php-curl-class/releases/)
4 | [](https://github.com/php-curl-class/php-curl-class/blob/master/LICENSE)
5 | [](https://travis-ci.org/php-curl-class/php-curl-class/)
6 | [](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 | 
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 |
--------------------------------------------------------------------------------