├── favicon.ico
├── static
├── img
│ ├── nopic.jpg
│ └── apple-touch-icon.png
├── css
│ └── style.css
└── js
│ └── music.js
├── core
├── composer.json
├── 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
│ │ ├── src
│ │ └── Curl
│ │ │ ├── StrUtil.php
│ │ │ ├── Decoder.php
│ │ │ ├── ArrayUtil.php
│ │ │ ├── CaseInsensitiveArray.php
│ │ │ ├── Url.php
│ │ │ ├── MultiCurl.php
│ │ │ └── Curl.php
│ │ ├── composer.json
│ │ ├── LICENSE
│ │ ├── SECURITY.md
│ │ └── README.md
└── composer.lock
├── .gitignore
├── .editorconfig
├── LICENSE
├── README.md
├── CHANGELOG.md
├── index.php
└── template
└── index.php
/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/maicong/music/HEAD/favicon.ico
--------------------------------------------------------------------------------
/static/img/nopic.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/maicong/music/HEAD/static/img/nopic.jpg
--------------------------------------------------------------------------------
/core/composer.json:
--------------------------------------------------------------------------------
1 | {
2 | "require": {
3 | "php-curl-class/php-curl-class": "^8.0"
4 | }
5 | }
6 |
--------------------------------------------------------------------------------
/static/img/apple-touch-icon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/maicong/music/HEAD/static/img/apple-touch-icon.png
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | node_modules
2 | coverage
3 | logs/**
4 | *.lcov
5 | .idea
6 | .vscode
7 | .git/
8 | .nyc_output
9 | npm-debug.log
10 | yarn-error.log
11 | debug.log
12 | Thumbs.db
13 | .DS_Store
14 |
--------------------------------------------------------------------------------
/.editorconfig:
--------------------------------------------------------------------------------
1 | # http://editorconfig.org
2 |
3 | root = true
4 |
5 | [*]
6 | charset = utf-8
7 | indent_style = space
8 | indent_size = 2
9 | end_of_line = lf
10 |
11 | [*.php]
12 | indent_size = 4
13 |
--------------------------------------------------------------------------------
/core/vendor/autoload.php:
--------------------------------------------------------------------------------
1 | array($vendorDir . '/php-curl-class/php-curl-class/src/Curl'),
10 | );
11 |
--------------------------------------------------------------------------------
/core/vendor/php-curl-class/php-curl-class/src/Curl/StrUtil.php:
--------------------------------------------------------------------------------
1 | =5.3",
17 | "ext-curl": "*"
18 | },
19 | "require-dev": {
20 | "phpunit/phpunit": "*",
21 | "squizlabs/php_codesniffer": "*"
22 | },
23 | "autoload": {
24 | "psr-4": {
25 | "Curl\\": "src/Curl/"
26 | }
27 | }
28 | }
29 |
--------------------------------------------------------------------------------
/core/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 = ComposerStaticInit82c91dc3983c1c2ce8fc9d4263765de4::$prefixLengthsPsr4;
27 | $loader->prefixDirsPsr4 = ComposerStaticInit82c91dc3983c1c2ce8fc9d4263765de4::$prefixDirsPsr4;
28 |
29 | }, null, ClassLoader::class);
30 | }
31 | }
32 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | The MIT License (MIT)
2 |
3 | Copyright (c) 2015 Maicong
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
23 |
--------------------------------------------------------------------------------
/core/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 |
--------------------------------------------------------------------------------
/core/vendor/php-curl-class/php-curl-class/src/Curl/Decoder.php:
--------------------------------------------------------------------------------
1 |
25 |
--------------------------------------------------------------------------------
/core/vendor/composer/autoload_real.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\ComposerStaticInit82c91dc3983c1c2ce8fc9d4263765de4::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 |
--------------------------------------------------------------------------------
/core/vendor/composer/installed.json:
--------------------------------------------------------------------------------
1 | [
2 | {
3 | "name": "php-curl-class/php-curl-class",
4 | "version": "8.0.0",
5 | "version_normalized": "8.0.0.0",
6 | "source": {
7 | "type": "git",
8 | "url": "https://github.com/php-curl-class/php-curl-class.git",
9 | "reference": "dc8282d086362d946520781c8908e0b084f01438"
10 | },
11 | "dist": {
12 | "type": "zip",
13 | "url": "https://files.phpcomposer.com/files/php-curl-class/php-curl-class/dc8282d086362d946520781c8908e0b084f01438.zip",
14 | "reference": "dc8282d086362d946520781c8908e0b084f01438",
15 | "shasum": ""
16 | },
17 | "require": {
18 | "ext-curl": "*",
19 | "php": ">=5.3"
20 | },
21 | "require-dev": {
22 | "phpunit/phpunit": "*",
23 | "squizlabs/php_codesniffer": "*"
24 | },
25 | "time": "2017-11-02T06:07:25+00:00",
26 | "type": "library",
27 | "installation-source": "dist",
28 | "autoload": {
29 | "psr-4": {
30 | "Curl\\": "src/Curl/"
31 | }
32 | },
33 | "notification-url": "https://packagist.org/downloads/",
34 | "license": [
35 | "Unlicense"
36 | ],
37 | "authors": [
38 | {
39 | "name": "Zach Borboa"
40 | }
41 | ],
42 | "description": "PHP Curl Class makes it easy to send HTTP requests and integrate with web APIs.",
43 | "homepage": "https://github.com/php-curl-class/php-curl-class",
44 | "keywords": [
45 | "api",
46 | "class",
47 | "client",
48 | "curl",
49 | "framework",
50 | "http",
51 | "http client",
52 | "json",
53 | "php",
54 | "requests",
55 | "rest",
56 | "restful",
57 | "web service",
58 | "xml"
59 | ]
60 | }
61 | ]
62 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # 音乐搜索器
2 |
3 | [](https://github.com/maicong/music/releases)
4 | [](https://github.com/php-src/php)
5 | [](#LICENSE)
6 |
7 | ## 说明
8 |
9 | **⚠️ 本项目已暂停维护,存档代码仅供学习交流,不得用于商业用途**
10 |
11 | 多站合一音乐搜索解决方案,支持搜索试听以下网站音乐:
12 |
13 | [网易云音乐](http://music.163.com) [QQ音乐](http://y.qq.com) [酷狗音乐](http://www.kugou.com) [酷我音乐](http://www.kuwo.cn) [虾米音乐](http://www.xiami.com) [百度音乐](http://music.baidu.com) [一听音乐](http://www.1ting.com) [咪咕音乐](http://music.migu.cn) [荔枝FM](http://www.lizhi.fm) [蜻蜓FM](http://www.qingting.fm) [喜马拉雅FM](http://www.ximalaya.com) [全民K歌](http://kg.qq.com) [5sing原创](http://5sing.kugou.com/yc) [5sing翻唱](http://5sing.kugou.com/fc)
14 |
15 | 数据调用的是各网站的 API 接口,有的接口并不是开放的,随时可能失效,本项目相关代码仅供参考。
16 |
17 | ## 演示
18 |
19 | 演示站点暂停维护,2018年11月01日起不再提供演示站点服务。
20 |
21 | 如果有需要改进的地方,欢迎提交 [Pull Requests](https://github.com/maicong/music/pulls)
22 |
23 | ## 下载
24 |
25 | [📦 下载开发版](https://github.com/maicong/music/archive/master.zip) [📦 获取稳定版](https://github.com/maicong/music/releases)
26 |
27 | ## 解决方案
28 |
29 | **1. 提示数据获取失败**
30 |
31 | 方案1:
32 |
33 | ```
34 | 修改 index.php 文件里的 MC_PROXY 为您的代理地址
35 | 将 core/music.php 里需要代理的 URL 'proxy' => false 改为 'proxy' => true
36 | ```
37 |
38 | 方案2:
39 |
40 | ```
41 | 在 core/music.php 里查找 setTimeout,将其后面的数值 20 改为更大。
42 | 在 static/js/music.js 里查找 `timeout`,将其数值 30000 改为更大。
43 | ```
44 |
45 | 方案3:
46 |
47 | ```
48 | 服务器要支持 curl。
49 | 更换服务器,选择延迟更低的服务器。
50 | ```
51 |
52 | **2. 播放器显示 `Error happens ╥﹏╥`**
53 |
54 | 音乐链接为空
55 |
56 | ```
57 | 1. 音乐需要付费才能收听
58 | 2. 版权限制,外站无法获取
59 | 3. 服务器 IP 所在地不在源站允许的区域
60 | 4. 音乐下架了,链接被去除
61 | ```
62 |
63 | 音乐链接不为空
64 |
65 | ```
66 | 1. 当前 IP 所在地因版权限制而无法播放
67 | 2. 音乐格式浏览器无法正常解析
68 | ```
69 |
70 | **3. 国内接口优化**
71 |
72 | 如果你的网站在国内,打开 [/index.php](index.php),将 `define('MC_INTERNAL', 0);` 修改为 `define('MC_INTERNAL', 1);`,这样就可以取到咪咕和网易云音乐的 320k 音频了。
73 |
74 | ## 更新日志
75 |
76 | 请查看 [CHANGELOG.md](CHANGELOG.md)
77 |
78 | ## 免责声明
79 |
80 | 1. 本站音频文件来自各网站接口,本站不会修改任何音频文件
81 | 2. 音频版权来自各网站,本站只提供数据查询服务,不提供任何音频存储和贩卖服务
82 | 3. 本项目代码仅供学习交流,不得用于商业用途,如有侵犯与代码贡献人员无关
83 |
84 | ## 开源协议
85 |
86 | The MIT License (MIT)
87 |
--------------------------------------------------------------------------------
/core/vendor/php-curl-class/php-curl-class/src/Curl/ArrayUtil.php:
--------------------------------------------------------------------------------
1 | $value) {
54 | if (is_scalar($value)) {
55 | if ($prefix) {
56 | $return[$prefix . '[' . $key . ']'] = $value;
57 | } else {
58 | $return[$key] = $value;
59 | }
60 | } else {
61 | if ($value instanceof \CURLFile) {
62 | $return[$key] = $value;
63 | } else {
64 | $return = array_merge(
65 | $return,
66 | self::array_flatten_multidim(
67 | $value,
68 | $prefix ? $prefix . '[' . $key . ']' : $key
69 | )
70 | );
71 | }
72 | }
73 | }
74 | }
75 | } elseif ($array === null) {
76 | $return[$prefix] = $array;
77 | }
78 | return $return;
79 | }
80 | }
81 |
--------------------------------------------------------------------------------
/core/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_website_url($url, $allowed_schemes = array('http', 'https')) {
25 | $validate_url = !(filter_var($url, FILTER_VALIDATE_URL) === false);
26 | $scheme = parse_url($url, PHP_URL_SCHEME);
27 | return $validate_url && in_array($scheme, $allowed_schemes, true);
28 | }
29 |
30 | $url = $_GET['url'];
31 | if (!is_website_url($url)) {
32 | die('Unsafe url detected.');
33 | }
34 | ```
35 |
36 | ### Url may point to internal urls
37 |
38 | * 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
39 | a whitelist to allow certain urls rather than a blacklist.
40 |
41 | ### Request data may refer to system files
42 |
43 | * Request data prefixed with the `@` character may have special interpretation and read from system files.
44 |
45 | ```bash
46 | # Attacker.
47 | $ curl https://www.example.com/upload_photo.php --data "photo=@/etc/passwd"
48 | ```
49 |
50 | ```php
51 | // upload_photo.php
52 | $curl = new Curl();
53 | $curl->post('http://www.anotherwebsite.com/', array(
54 | 'photo' => $_POST['photo'], // DANGER!
55 | ));
56 | ```
57 |
58 | ### Unsafe response with redirection enabled
59 |
60 | * Requests with redirection enabled may return responses from unexpected sources.
61 | Downloading https://www.example.com/image.png may redirect and download https://www.evil.com/virus.exe
62 |
63 | ```php
64 | $curl = new Curl();
65 | $curl->setOpt(CURLOPT_FOLLOWLOCATION, true); // DANGER!
66 | $curl->download('https://www.example.com/image.png', 'my_image.png');
67 | ```
68 |
69 | ```php
70 | $curl = new Curl();
71 | $curl->setOpt(CURLOPT_FOLLOWLOCATION, true); // DANGER!
72 | $curl->get('https://www.example.com/image.png');
73 | ```
74 |
75 | ### Keep SSL protections enabled
76 |
77 | * Do not disable SSL protections.
78 |
79 | ```php
80 | curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false); // DANGER!
81 | curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false); // DANGER!
82 | ```
83 |
--------------------------------------------------------------------------------
/core/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#composer-lock-the-lock-file",
5 | "This file is @generated automatically"
6 | ],
7 | "content-hash": "d03e2262635cae58cd2f5f38451aa43d",
8 | "packages": [
9 | {
10 | "name": "php-curl-class/php-curl-class",
11 | "version": "8.0.0",
12 | "source": {
13 | "type": "git",
14 | "url": "https://github.com/php-curl-class/php-curl-class.git",
15 | "reference": "dc8282d086362d946520781c8908e0b084f01438"
16 | },
17 | "dist": {
18 | "type": "zip",
19 | "url": "https://files.phpcomposer.com/files/php-curl-class/php-curl-class/dc8282d086362d946520781c8908e0b084f01438.zip",
20 | "reference": "dc8282d086362d946520781c8908e0b084f01438",
21 | "shasum": ""
22 | },
23 | "require": {
24 | "ext-curl": "*",
25 | "php": ">=5.3"
26 | },
27 | "require-dev": {
28 | "phpunit/phpunit": "*",
29 | "squizlabs/php_codesniffer": "*"
30 | },
31 | "type": "library",
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",
50 | "class",
51 | "client",
52 | "curl",
53 | "framework",
54 | "http",
55 | "http client",
56 | "json",
57 | "php",
58 | "requests",
59 | "rest",
60 | "restful",
61 | "web service",
62 | "xml"
63 | ],
64 | "time": "2017-11-02T06:07:25+00:00"
65 | }
66 | ],
67 | "packages-dev": [],
68 | "aliases": [],
69 | "minimum-stability": "stable",
70 | "stability-flags": [],
71 | "prefer-stable": false,
72 | "prefer-lowest": false,
73 | "platform": [],
74 | "platform-dev": []
75 | }
76 |
--------------------------------------------------------------------------------
/CHANGELOG.md:
--------------------------------------------------------------------------------
1 | # 更新日志
2 |
3 | 2018.09.26 `v1.6.2`
4 |
5 | - 优化酷狗搜索结果
6 | - 优化默认配置
7 |
8 | 2018.06.11 `v1.6.1`
9 |
10 | - 修复 QQ 音乐音频获取失败问题 (320k 暂时已失效)
11 |
12 | 2018.05.24 `v1.6.0`
13 |
14 | - 增加服务器国内外判断
15 | - 更新咪咕接口,支持 320k 音频
16 | - 优化网易云音乐接口
17 |
18 | 2018.05.23 `v1.5.10`
19 |
20 | - 修复 cc、top 域名使用外部资源403问题
21 |
22 | 2018.03.05 `v1.5.9`
23 |
24 | - 修复歌曲切换后相关信息显示错误问题
25 |
26 | 2018.03.02 `v1.5.8`
27 |
28 | - 修复咪咕地址解析失败问题
29 |
30 | 2018.02.23 `v1.5.7`
31 |
32 | - 增加歌词下载功能
33 | - 修复网易云音乐地址为空问题
34 |
35 | 2018.02.02 `v1.5.6`
36 |
37 | - 增加音乐链接下载功能 (如果浏览器支持的话)
38 | - 优化音乐信息显示框为不可编辑
39 | - 优化代码
40 |
41 | 2018.01.17 `v1.5.5`
42 |
43 | - 增加 URL 参数传递
44 | - 优化代码
45 | - 移除对 SoundCloud 的支持
46 |
47 | 2018.01.04 `v1.5.4`
48 |
49 | - 更新代码兼容性
50 | - 更新 meta 信息
51 | - 修复代理检测
52 | - 优化百度音乐外链地址
53 | - 修复一听封面地址403问题
54 | - 修复荔枝音频地址失效问题
55 | - 更新说明
56 |
57 | 2017.12.25 `v1.5.3`
58 |
59 | - 增加对 全民K歌 的支持
60 | - 搜索结果增加 载入更多 功能。
61 |
62 | 2017.12.09 `v1.5.2`
63 |
64 | - 更新帮助里的音乐地址
65 | - 修复网易云音乐和百度音乐解析问题
66 |
67 | 2017.12.08 `v1.5.1`
68 |
69 | - 优化代码
70 | - 更新说明
71 |
72 | 2017.12.08 `v1.5.0`
73 |
74 | - 更换播放器
75 | - 增加歌词显示
76 | - 更新接口
77 |
78 | 2017.12.05 `v1.4.5`
79 |
80 | - 修复网易云音乐音频数据不对应问题
81 |
82 | 2017.12.05 `v1.4.4`
83 |
84 | - 优化网易云音乐、虾米音乐、百度音乐接口
85 |
86 | 2017.12.05 `v1.4.3`
87 |
88 | - 优化酷狗音乐接口,支持 320k 音频
89 |
90 | 2017.12.04 `v1.4.2`
91 |
92 | - 优化代码
93 |
94 | 2017.12.04 `v1.4.1`
95 |
96 | - 优化 QQ 音乐品质
97 |
98 | 2017.11.28 `v1.4.0`
99 |
100 | - 优化接口和代码
101 | - 优化 Curl 模块加载方式
102 |
103 | 2017.09.12 `v1.3.0`
104 |
105 | - 更新 QQ 音乐 API 接口
106 |
107 | 2017.09.08 `v1.2.9`
108 |
109 | - 优化模版代码
110 | - 更新说明
111 |
112 | 2017.09.06 `v1.2.8`
113 |
114 | - 更新 5sing 接口
115 | - 优化代码
116 |
117 | 2017.09.04 `v1.2.7`
118 |
119 | - 修复低版本提示显示编码问题
120 |
121 | 2017.08.03 `v1.2.6`
122 |
123 | - 更新页脚和注释
124 |
125 | 2017.08.03 `v1.2.6`
126 |
127 | - 增加低版本提示
128 | - 优化 蜻蜓 FM 的 songid 代码
129 |
130 | 2017.08.01 `v1.2.5`
131 |
132 | - 增加对 喜马拉雅 FM 的支持
133 | - 修复 url 无法获取问题
134 |
135 | 2017.07.26 `v1.2.4`
136 |
137 | - 优化代码兼容性
138 |
139 | 2017.07.24 `v1.2.3`
140 |
141 | - 优化目录结构和模版
142 |
143 | 2017.07.20 `v1.2.2`
144 |
145 | - 优化回调代码
146 |
147 | 2017.07.20 `v1.2.1`
148 |
149 | - 更新正则匹配规则
150 |
151 | 2017.07.19 `v1.2.0`
152 |
153 | - 修复正则表达式问题
154 |
155 | 2017.07.19 `v1.1.9`
156 |
157 | - 增加对蜻蜓 FM 的支持 (resolve [#6](https://github.com/maicong/music/issues/6))
158 |
159 | 2017.07.10 `v1.1.8`
160 |
161 | - 修复 api 请求接口问题
162 |
163 | 2017.07.05 `v1.1.7`
164 |
165 | - 增加对 荔枝 FM 的支持
166 |
167 | 2017.06.26 `v1.1.6`
168 |
169 | - 修复数组写法兼容性
170 |
171 | 2017.05.19 `v1.1.5`
172 |
173 | - 修复 网易云音乐 音乐链接失效问题
174 |
175 | 2017.04.28 `v1.1.4`
176 |
177 | - 更新 QQ 音乐 API 接口
178 | - 优化代码
179 |
180 | 2017.04.21 `v1.1.3`
181 |
182 | - 优化代码和播放器视觉
183 |
184 | 2017.04.20 `v1.1.2`
185 |
186 | - 更新音乐地址匹配规则
187 |
188 | 2017.03.24 `v1.1.1`
189 |
190 | - 移除对天天动听的支持
191 | - 修复无法获取咪咕音乐的问题
192 | - 更新 SoundCloud 的支持 client_id
193 |
194 | 2017.03.23 `v1.1.0`
195 |
196 | - 更新外链资源地址
197 | - 优化代码
198 |
199 | 2015.06.15 `v1.0.4`
200 |
201 | - 增加对 SoundCloud 的支持
202 | - 增加代理支持
203 | - 修复音乐名称识别问题
204 | - 优化代码
205 |
206 | 2015.06.13 `v1.0.3`
207 |
208 | - 增加对 天天动听、咪咕 的支持
209 |
210 | 2015.06.12 `v1.0.2`
211 |
212 | - 增加对 5sing 的支持 (开源发布)
213 |
214 | 2015.06.12 `v1.0.1`
215 |
216 | - 代码优化
217 | - 修复 BUG
218 |
219 | 2015.06.10 `v1.0.0`
220 |
221 | - 音乐搜索器上线
222 |
--------------------------------------------------------------------------------
/index.php:
--------------------------------------------------------------------------------
1 |
7 | * @link https://github.com/maicong/music
8 | * @since 1.6.2
9 | *
10 | */
11 |
12 | // 定义核心
13 | define('MC_CORE', true);
14 |
15 | // 定义版本
16 | define('MC_VERSION', '1.6.2');
17 |
18 | // 核心文件目录
19 | define('MC_CORE_DIR', __DIR__ . '/core');
20 |
21 | // 模版文件目录
22 | define('MC_TEMP_DIR', __DIR__ . '/template');
23 |
24 | // 调试模式,0为关闭,-1为打开
25 | define('MC_DEBUG', 0);
26 |
27 | // Curl 代理地址,例如:define('MC_PROXY', 'someproxy.com:9999')
28 | define('MC_PROXY', false);
29 |
30 | // Curl 代理用户名和密码,例如:define('MC_PROXYUSERPWD', 'username:password')
31 | define('MC_PROXYUSERPWD', false);
32 |
33 | // 服务器是否在国内
34 | define('MC_INTERNAL', 1);
35 |
36 | // PHP 版本判断
37 | if (version_compare(phpversion(), '5.4', '<')) {
38 | header('Content-type:text/html;charset=utf-8');
39 | echo sprintf(
40 | '
程序运行失败:
您的 PHP 版本低于最低要求 5.4,当前版本为 %s
',
41 | phpversion()
42 | );
43 | exit;
44 | }
45 |
46 | // 判断是否启用 Curl
47 | if (!extension_loaded('curl')) {
48 | header('Content-type:text/html;charset=utf-8');
49 | echo '程序运行失败:
请启用 Curl 模块
';
50 | exit;
51 | }
52 |
53 |
54 | include_once MC_CORE_DIR . '/music.php';
55 |
56 | // 支持的网站
57 | $music_type_list = array(
58 | 'netease' => '网易',
59 | 'qq' => 'QQ',
60 | 'kugou' => '酷狗',
61 | 'kuwo' => '酷我',
62 | 'xiami' => '虾米',
63 | 'baidu' => '百度',
64 | '1ting' => '一听',
65 | 'migu' => '咪咕',
66 | 'lizhi' => '荔枝',
67 | 'qingting' => '蜻蜓',
68 | 'ximalaya' => '喜马拉雅',
69 | 'kg' => '全民K歌',
70 | '5singyc' => '5sing原创',
71 | '5singfc' => '5sing翻唱'
72 | );
73 |
74 | if (server('HTTP_X_REQUESTED_WITH') === 'XMLHttpRequest') {
75 | $music_input = trim(post('input'));
76 | $music_filter = post('filter');
77 | $music_type = post('type');
78 | $music_page = (int) post('page');
79 | $music_valid_patterns = array(
80 | 'name' => '/^.+$/i',
81 | 'id' => '/^[\w\/\|]+$/i',
82 | 'url' => '/^https?:\/\/\S+$/i'
83 | );
84 |
85 | if (!$music_input || !$music_filter || !$music_type) {
86 | response('', 403, '(°ー°〃) 传入的数据不对啊');
87 | }
88 |
89 | if ($music_filter !== 'url' && !in_array($music_type, array_keys($music_type_list), true)) {
90 | response('', 403, '(°ー°〃) 目前还不支持这个网站');
91 | }
92 |
93 | if (!preg_match($music_valid_patterns[$music_filter], $music_input)) {
94 | response('', 403, '(・-・*) 请检查您的输入是否正确');
95 | }
96 |
97 | switch ($music_filter) {
98 | case 'name':
99 | if (!$music_page) {
100 | $music_page = 1;
101 | }
102 | $music_response = mc_get_song_by_name($music_input, $music_type, $music_page);
103 | break;
104 | case 'id':
105 | $music_response = mc_get_song_by_id($music_input, $music_type);
106 | break;
107 | case 'url':
108 | $music_response = mc_get_song_by_url($music_input);
109 | break;
110 | }
111 |
112 | if (empty($music_response)) {
113 | response('', 404, 'ㄟ( ▔, ▔ )ㄏ 没有找到相关信息');
114 | }
115 |
116 | if ($music_response['error']) {
117 | response('', $music_response['code'], '(°ー°〃) ' . $music_response['error']);
118 | }
119 |
120 | response($music_response, 200, '');
121 | }
122 |
123 | include_once(MC_TEMP_DIR . '/index.php');
124 |
--------------------------------------------------------------------------------
/static/css/style.css:
--------------------------------------------------------------------------------
1 | /**
2 | *
3 | * 音乐搜索器 - CSS 文件
4 | *
5 | * @author MaiCong
6 | * @link https://github.com/maicong/music
7 | * @since 1.5.4
8 | *
9 | */
10 |
11 | html {
12 | font-family: -apple-system, BlinkMacSystemFont, 'San Francisco', 'Microsoft YaHei', 'PingFang SC',
13 | 'Hiragino Sans GB', 'WenQuanYi Micro Hei', 'Segoe UI', 'Roboto', 'Oxygen', 'Ubuntu', 'Cantarell',
14 | 'Fira Sans', 'Droid Sans', 'Noto Sans', 'Helvetica Neue', Helvetica, Arial, sans-serif,
15 | 'Apple Color Emoji', 'Segoe UI Emoji', 'Segoe UI Symbol';
16 | -webkit-text-size-adjust: 100%;
17 | -moz-text-size-adjust: 100%;
18 | -ms-text-size-adjust: 100%;
19 | text-size-adjust: 100%;
20 | }
21 | body {
22 | margin: 0;
23 | -webkit-tap-highlight-color: transparent;
24 | -webkit-font-smoothing: subpixel-antialiased;
25 | }
26 | a {
27 | -webkit-transition: all .3s;
28 | transition: all .3s;
29 | }
30 | .about {
31 | background: #fff;
32 | color: #7f8c8d;
33 | }
34 | .about-color {
35 | color: #34495e;
36 | }
37 | .about-title {
38 | font-size: 180%;
39 | text-align: center;
40 | }
41 | .main {
42 | margin: 60px 0;
43 | }
44 | .music-tabs > li > a {
45 | padding: 0.5em 1em;
46 | cursor: pointer;
47 | }
48 | .music-main {
49 | display: none;
50 | }
51 | .music-main input {
52 | text-overflow: ellipsis;
53 | }
54 | .music-main input:focus,
55 | .music-main input:hover {
56 | background: #eee;
57 | border-color: #ccc;
58 | color: #222;
59 | }
60 | .music-type .am-radio-inline,
61 | .music-type label {
62 | color: #7f8c8d;
63 | }
64 | .music-type .am-icon-unchecked,
65 | .music-type .am-icon-checked {
66 | color: #0e90d2;
67 | }
68 | .music-tips {
69 | margin-top: 50px;
70 | }
71 | .music-tips blockquote {
72 | font-family: Menlo, Monaco, Consolas, 'Courier New', monospace;
73 | font-size: 14px;
74 | margin-top: 30px;
75 | }
76 | .music-tips p span {
77 | display: inline-block;
78 | min-width: 50px;
79 | }
80 | .music-tips p b {
81 | font-weight: 500;
82 | color: #c7254e;
83 | }
84 | .music-overflow {
85 | max-height: 200px;
86 | overflow: hidden;
87 | }
88 | .music-more {
89 | padding: 10px;
90 | text-align: center;
91 | font-size: 14px;
92 | color: #666;
93 | background: #eee;
94 | cursor: pointer;
95 | }
96 | .footer {
97 | position: relative;
98 | left: 0;
99 | bottom: 0;
100 | width: 100%;
101 | overflow: hidden;
102 | }
103 | .footer p {
104 | color: #7f8c8d;
105 | margin: 0;
106 | padding: 15px;
107 | text-align: center;
108 | background: #2d3e50;
109 | }
110 | .footer p a {
111 | color: #7f8c8d;
112 | }
113 | .footer p a:hover {
114 | color: #bbb;
115 | }
116 | .footer a {
117 | text-decoration: underline;
118 | }
119 | .am-alert {
120 | display: none;
121 | }
122 | .am-popup-bd {
123 | height: 100%;
124 | }
125 | .aplayer {
126 | padding: 10px !important;
127 | margin: 0 !important;
128 | border: 1px solid #ccc;
129 | }
130 | .aplayer .aplayer-info .aplayer-music .aplayer-title,
131 | .aplayer .aplayer-info .aplayer-music .aplayer-author {
132 | font-size: 16px !important;
133 | color: #555 !important;
134 | }
135 |
136 | .aplayer .aplayer-list ol li .aplayer-list-index {
137 | display: inline-block;
138 | width: 20px;
139 | text-align: right;
140 | }
141 | .aplayer .aplayer-lrc p {
142 | color: #0e90d2 !important;
143 | }
144 | .aplayer .aplayer-more {
145 | position: relative;
146 | font-size: 12px;
147 | padding: 8px 10px;
148 | margin-top: 10px;
149 | text-align: center;
150 | color: #888;
151 | cursor: pointer;
152 | -webkit-transition: color .3s;
153 | transition: color .3s;
154 | }
155 | .aplayer .aplayer-more:hover,
156 | .aplayer .aplayer-more:active {
157 | color: #0e90d2;
158 | }
159 |
160 | .aplayer.aplayer-withlrc .aplayer-pic {
161 | width: 120px !important;
162 | height: 120px !important;
163 | -webkit-box-shadow: inset 0 0 3px 0 rgba(0, 0, 0, .5);
164 | box-shadow: inset 0 0 3px 0 rgba(0, 0, 0, .5);
165 | }
166 | .aplayer.aplayer-withlrc .aplayer-info {
167 | margin-left: 120px !important;
168 | height: 120px !important;
169 | }
170 | .aplayer.aplayer-withlist .aplayer-info {
171 | display: -webkit-box;
172 | display: -ms-flexbox;
173 | display: flex;
174 | -webkit-box-orient: vertical;
175 | -webkit-box-direction: normal;
176 | -ms-flex-direction: column;
177 | flex-direction: column;
178 | -webkit-box-pack: justify;
179 | -ms-flex-pack: justify;
180 | justify-content: space-between;
181 | border-bottom: 0 !important;
182 | }
183 | .aplayer.aplayer-withlist .aplayer-list {
184 | height: auto !important;
185 | padding-top: 20px;
186 | -webkit-transition: none;
187 | transition: none;
188 | }
189 | .aplayer.aplayer-withlist .aplayer-list-hide {
190 | padding: 0;
191 | }
192 |
--------------------------------------------------------------------------------
/core/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 lower-case, and
55 | * stores the Case-Sensitive Offset and the Data at the lower-case indexes
56 | * in $this->keys and @this->data.
57 | *
58 | * @see https://secure.php.net/manual/en/arrayaccess.offseteset.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 lower-case 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 |
--------------------------------------------------------------------------------
/core/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 (StrUtil::startsWith($input, '../')) {
40 | $input = substr($input, 3);
41 | } elseif (StrUtil::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 (StrUtil::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 (StrUtil::startsWith($input, '/../')) {
58 | $input = substr($input, 3);
59 | $output = substr_replace($output, '', mb_strrpos($output, '/'));
60 | } elseif ($input === '/..') {
61 | $input = '/';
62 | $output = substr_replace($output, '', mb_strrpos($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 = mb_strpos($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 |
96 | if (!($this->relativeUrl === null)) {
97 | $r = $this->parseUrl($this->relativeUrl);
98 |
99 | // Copy relative parts to base url.
100 | if (isset($r['scheme'])) {
101 | $b['scheme'] = $r['scheme'];
102 | }
103 | if (isset($r['host'])) {
104 | $b['host'] = $r['host'];
105 | }
106 | if (isset($r['port'])) {
107 | $b['port'] = $r['port'];
108 | }
109 | if (isset($r['user'])) {
110 | $b['user'] = $r['user'];
111 | }
112 | if (isset($r['pass'])) {
113 | $b['pass'] = $r['pass'];
114 | }
115 |
116 | if (!isset($r['path']) || $r['path'] === '') {
117 | $r['path'] = '/';
118 | }
119 | // Merge relative url with base when relative url's path doesn't start with a slash.
120 | if (!(StrUtil::startsWith($r['path'], '/'))) {
121 | $base = mb_strrchr($b['path'], '/', true);
122 | if ($base === false) {
123 | $base = '';
124 | }
125 | $r['path'] = $base . '/' . $r['path'];
126 | }
127 | $b['path'] = $r['path'];
128 | $b['path'] = $this->removeDotSegments($b['path']);
129 |
130 | if (isset($r['query'])) {
131 | $b['query'] = $r['query'];
132 | }
133 | if (isset($r['fragment'])) {
134 | $b['fragment'] = $r['fragment'];
135 | }
136 | }
137 |
138 | if (!isset($b['path'])) {
139 | $b['path'] = '/';
140 | }
141 |
142 | $absolutized_url = $this->unparseUrl($b);
143 | return $absolutized_url;
144 | }
145 |
146 | /**
147 | * Parse url.
148 | *
149 | * Parse url into components of a URI as specified by RFC 3986.
150 | */
151 | private function parseUrl($url)
152 | {
153 | return parse_url($url);
154 | }
155 |
156 | /**
157 | * Unparse url.
158 | *
159 | * Combine url components into a url.
160 | */
161 | private function unparseUrl($parsed_url) {
162 | $scheme = isset($parsed_url['scheme']) ? $parsed_url['scheme'] . '://' : '';
163 | $host = isset($parsed_url['host']) ? $parsed_url['host'] : '';
164 | $port = isset($parsed_url['port']) ? ':' . $parsed_url['port'] : '';
165 | $user = isset($parsed_url['user']) ? $parsed_url['user'] : '';
166 | $pass = isset($parsed_url['pass']) ? ':' . $parsed_url['pass'] : '';
167 | $pass = ($user || $pass) ? $pass . '@' : '';
168 | $path = isset($parsed_url['path']) ? $parsed_url['path'] : '';
169 | $query = isset($parsed_url['query']) ? '?' . $parsed_url['query'] : '';
170 | $fragment = isset($parsed_url['fragment']) ? '#' . $parsed_url['fragment'] : '';
171 | $unparsed_url = $scheme . $user . $pass . $host . $port . $path . $query . $fragment;
172 | return $unparsed_url;
173 | }
174 | }
175 |
--------------------------------------------------------------------------------
/core/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, 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 | // 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::getCookie($key)
211 | Curl::getInfo($opt = null)
212 | Curl::getOpt($option)
213 | Curl::getResponseCookie($key)
214 | Curl::getResponseCookies()
215 | Curl::head($url, $data = array())
216 | Curl::options($url, $data = array())
217 | Curl::patch($url, $data = array())
218 | Curl::post($url, $data = array(), $follow_303_with_post = false)
219 | Curl::progress($callback)
220 | Curl::put($url, $data = array())
221 | Curl::removeHeader($key)
222 | Curl::search($url, $data = array())
223 | Curl::setBasicAuthentication($username, $password = '')
224 | Curl::setConnectTimeout($seconds)
225 | Curl::setCookie($key, $value)
226 | Curl::setCookieFile($cookie_file)
227 | Curl::setCookieJar($cookie_jar)
228 | Curl::setCookieString($string)
229 | Curl::setCookies($cookies)
230 | Curl::setDefaultDecoder($mixed = 'json')
231 | Curl::setDefaultJsonDecoder()
232 | Curl::setDefaultTimeout()
233 | Curl::setDefaultUserAgent()
234 | Curl::setDefaultXmlDecoder()
235 | Curl::setDigestAuthentication($username, $password = '')
236 | Curl::setHeader($key, $value)
237 | Curl::setHeaders($headers)
238 | Curl::setJsonDecoder($mixed)
239 | Curl::setMaxFilesize($bytes)
240 | Curl::setOpt($option, $value)
241 | Curl::setOpts($options)
242 | Curl::setPort($port)
243 | Curl::setReferer($referer)
244 | Curl::setReferrer($referrer)
245 | Curl::setRetry($mixed)
246 | Curl::setTimeout($seconds)
247 | Curl::setUrl($url, $mixed_data = '')
248 | Curl::setUserAgent($user_agent)
249 | Curl::setXmlDecoder($mixed)
250 | Curl::success($callback)
251 | Curl::unsetHeader($key)
252 | Curl::verbose($on = true, $output = STDERR)
253 | MultiCurl::__construct($base_url = null)
254 | MultiCurl::__destruct()
255 | MultiCurl::addCurl(Curl $curl)
256 | MultiCurl::addDelete($url, $query_parameters = array(), $data = array())
257 | MultiCurl::addDownload($url, $mixed_filename)
258 | MultiCurl::addGet($url, $data = array())
259 | MultiCurl::addHead($url, $data = array())
260 | MultiCurl::addOptions($url, $data = array())
261 | MultiCurl::addPatch($url, $data = array())
262 | MultiCurl::addPost($url, $data = array(), $follow_303_with_post = false)
263 | MultiCurl::addPut($url, $data = array())
264 | MultiCurl::addSearch($url, $data = array())
265 | MultiCurl::beforeSend($callback)
266 | MultiCurl::close()
267 | MultiCurl::complete($callback)
268 | MultiCurl::error($callback)
269 | MultiCurl::getOpt($option)
270 | MultiCurl::removeHeader($key)
271 | MultiCurl::setBasicAuthentication($username, $password = '')
272 | MultiCurl::setConcurrency($concurrency)
273 | MultiCurl::setConnectTimeout($seconds)
274 | MultiCurl::setCookie($key, $value)
275 | MultiCurl::setCookieFile($cookie_file)
276 | MultiCurl::setCookieJar($cookie_jar)
277 | MultiCurl::setCookieString($string)
278 | MultiCurl::setCookies($cookies)
279 | MultiCurl::setDigestAuthentication($username, $password = '')
280 | MultiCurl::setHeader($key, $value)
281 | MultiCurl::setHeaders($headers)
282 | MultiCurl::setJsonDecoder($mixed)
283 | MultiCurl::setOpt($option, $value)
284 | MultiCurl::setOpts($options)
285 | MultiCurl::setPort($port)
286 | MultiCurl::setReferer($referer)
287 | MultiCurl::setReferrer($referrer)
288 | MultiCurl::setRetry($mixed)
289 | MultiCurl::setTimeout($seconds)
290 | MultiCurl::setUrl($url)
291 | MultiCurl::setUserAgent($user_agent)
292 | MultiCurl::setXmlDecoder($mixed)
293 | MultiCurl::start()
294 | MultiCurl::success($callback)
295 | MultiCurl::unsetHeader($key)
296 | MultiCurl::verbose($on = true, $output = STDERR)
297 | ```
298 |
299 | ### Security
300 |
301 | See [SECURITY](https://github.com/php-curl-class/php-curl-class/blob/master/SECURITY.md) for security considerations.
302 |
303 | ### Troubleshooting
304 |
305 | See [TROUBLESHOOTING](https://github.com/php-curl-class/php-curl-class/blob/master/TROUBLESHOOTING.md) for troubleshooting.
306 |
307 | ### Run Tests
308 |
309 | To run tests:
310 |
311 | $ git clone https://github.com/php-curl-class/php-curl-class.git
312 | $ cd php-curl-class/
313 | $ composer update
314 | $ ./tests/run.sh
315 |
316 | ### Contribute
317 | 1. Check for open issues or open a new issue to start a discussion around a bug or feature.
318 | 1. Fork the repository on GitHub to start making your changes.
319 | 1. Write one or more tests for the new feature or that expose the bug.
320 | 1. Make code changes to implement the feature or fix the bug.
321 | 1. Send a pull request to get your changes merged and published.
322 |
--------------------------------------------------------------------------------
/static/js/music.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | /**
4 | *
5 | * 音乐搜索器 - JS 文件
6 | *
7 | * @author MaiCong
8 | * @link https://github.com/maicong/music
9 | * @since 1.5.9
10 | *
11 | */
12 |
13 | $(function() {
14 | // 获取参数
15 | function q(key) {
16 | var value = null;
17 | var tmp = [];
18 | location.search
19 | .substr(1)
20 | .split('&')
21 | .forEach(function(v) {
22 | tmp = v.split('=');
23 | if (tmp[0] === key) {
24 | value = decodeURIComponent(tmp[1]);
25 | }
26 | });
27 | return value;
28 | }
29 |
30 | // 加入历史记录
31 | function pushState(title, link) {
32 | if (window.history && window.history.pushState) {
33 | window.history.pushState(null, title, link);
34 | }
35 | }
36 |
37 | // 获取 url
38 | function getUrl(path) {
39 | var url = location.href.split('?')[0];
40 | return path ? url + path : url;
41 | }
42 |
43 | // 申明变量
44 | var player = null;
45 | var playerList = [];
46 | var nopic = 'static/img/nopic.jpg';
47 | var qName = q('name');
48 | var qId = q('id');
49 | var qUrl = q('url');
50 | var qType = q('type');
51 | var siteTitle = document.title;
52 |
53 | // 如果参数存在 name/id 和 type
54 | if ((qName || qId) && qType) {
55 | setTimeout(function() {
56 | $('#j-input').val(qName || qId);
57 | $('#j-type input[value="' + qType + '"]').prop('checked', true);
58 | if (qName) {
59 | $('#j-nav [data-filter="name"]').trigger('click');
60 | }
61 | if (qId) {
62 | $('#j-nav [data-filter="id"]').trigger('click');
63 | }
64 | $('#j-validator').trigger('submit');
65 | }, 0);
66 | }
67 |
68 | // 如果参数存在 url
69 | if (qUrl) {
70 | setTimeout(function() {
71 | $('#j-type').hide();
72 | $('#j-input').val(qUrl);
73 | $('#j-nav [data-filter="url"]').trigger('click');
74 | $('#j-validator').trigger('submit');
75 | }, 0);
76 | }
77 |
78 | // Tab 切换
79 | $('#j-nav').on('click', 'li', function() {
80 | var holder = {
81 | name: '例如: 不要说话 陈奕迅',
82 | id: '例如: 25906124',
83 | url: '例如: http://music.163.com/#/song?id=25906124',
84 | pattern_name: '^.+$',
85 | pattern_id: '^[\\w\\/\\|]+$',
86 | pattern_url: '^https?:\\/\\/\\S+$'
87 | };
88 | var filter = $(this).data('filter');
89 |
90 | $(this)
91 | .addClass('am-active')
92 | .siblings('li')
93 | .removeClass('am-active');
94 |
95 | $('#j-input')
96 | .data('filter', filter)
97 | .attr({
98 | placeholder: holder[filter],
99 | pattern: holder['pattern_' + filter]
100 | })
101 | .removeClass('am-field-valid am-field-error am-active')
102 | .closest('.am-form-group')
103 | .removeClass('am-form-success am-form-error')
104 | .find('.am-alert')
105 | .hide();
106 |
107 | if (filter === 'url') {
108 | $('#j-type').hide();
109 | } else {
110 | $('#j-type').show();
111 | }
112 | });
113 |
114 | // 输入验证
115 | $('#j-validator').validator({
116 | onValid: function onValid(v) {
117 | $(v.field)
118 | .closest('.am-form-group')
119 | .find('.am-alert')
120 | .hide();
121 | },
122 | onInValid: function onInValid(v) {
123 | var $field = $(v.field);
124 | var $group = $field.closest('.am-form-group');
125 | var msgs = {
126 | name: '将 名称 和 作者 一起输入可提高匹配度',
127 | id: '输入错误,请查看下面的帮助',
128 | url: '输入错误,请查看下面的帮助'
129 | };
130 | var $alert = $group.find('.am-alert');
131 | var msg = msgs[$field.data('filter')] || this.getValidationMessage(v);
132 |
133 | if (!$alert.length) {
134 | $alert = $(
135 | ''
136 | )
137 | .hide()
138 | .appendTo($group);
139 | }
140 | $alert.html(msg).show();
141 | },
142 | submit: function submit(v) {
143 | v.preventDefault();
144 | if (this.isFormValid()) {
145 | var input = $.trim($('#j-input').val());
146 | var filter = $('#j-input').data('filter');
147 | var type =
148 | filter === 'url' ? '_' : $('input[name="music_type"]:checked').val();
149 | var page = 1;
150 | var $more = $('载入更多
');
151 | var isload = false;
152 | var ajax = function ajax(input, filter, type, page) {
153 | $.ajax({
154 | type: 'POST',
155 | url: getUrl(),
156 | timeout: 30000,
157 | data: {
158 | input: input,
159 | filter: filter,
160 | type: type,
161 | page: page
162 | },
163 | dataType: 'json',
164 | beforeSend: function beforeSend() {
165 | isload = true;
166 | var title = document.title;
167 | switch (filter) {
168 | case 'name':
169 | pushState(title, getUrl('?name=' + input + '&type=' + type));
170 | break;
171 | case 'id':
172 | pushState(title, getUrl('?id=' + input + '&type=' + type));
173 | break;
174 | case 'url':
175 | pushState(title, getUrl('?url=' + encodeURIComponent(input)));
176 | break;
177 | }
178 | if (page === 1) {
179 | $('#j-input').attr('disabled', true);
180 | $('#j-submit').button('loading');
181 | } else {
182 | $more.text('请稍后...');
183 | }
184 | },
185 | success: function success(result) {
186 | if (result.code === 200 && result.data) {
187 | result.data.map(function(v) {
188 | if (!v.title) v.title = '暂无';
189 | if (!v.author) v.author = '暂无';
190 | if (!v.pic) v.pic = nopic;
191 | if (!v.lrc) v.lrc = '[00:00.00] 暂无歌词';
192 | if (!/\[00:(\d{2})\./.test(v.lrc)) {
193 | v.lrc = '[00:00.00] 无效歌词';
194 | }
195 | });
196 | var setValue = function setValue(data) {
197 | $('#j-link').val(data.link);
198 | $('#j-link-btn').attr('href', data.link);
199 | $('#j-src').val(data.url);
200 | $('#j-src-btn').attr('href', data.url);
201 | $('#j-lrc').val(data.lrc);
202 | $('#j-lrc-btn').attr(
203 | 'href',
204 | 'data:application/octet-stream;base64,' +
205 | btoa(unescape(encodeURIComponent(data.lrc)))
206 | );
207 | if ('download' in $('#j-src-btn')[0]) {
208 | var name = data.title + '-' + data.author;
209 | $('#j-src-btn').attr('download', name + '.mp3');
210 | $('#j-lrc-btn').attr('download', name + '.lrc');
211 | $('#j-src-btn-icon, #j-lrc-btn-icon')
212 | .addClass('am-icon-download')
213 | .removeClass('am-icon-external-link');
214 | }
215 | $('#j-songid').val(data.songid);
216 | $('#j-name').val(data.title);
217 | $('#j-author').val(data.author);
218 | };
219 |
220 | if (page === 1) {
221 | if (player) {
222 | player.pause();
223 | }
224 |
225 | playerList = result.data;
226 |
227 | setValue(playerList[0]);
228 |
229 | $('#j-validator').slideUp();
230 | $('#j-main').slideDown();
231 |
232 | player = new APlayer({
233 | element: $('#j-player')[0],
234 | autoplay: false,
235 | narrow: false,
236 | showlrc: 1,
237 | mutex: false,
238 | mode: 'circulation',
239 | preload: 'metadata',
240 | theme: '#0e90d2',
241 | music: result.data
242 | });
243 |
244 | $('#j-player').append($more);
245 |
246 | $more.on('click', function() {
247 | if (isload) return;
248 | page++;
249 | ajax(input, filter, type, page);
250 | });
251 | } else {
252 | player.addMusic(result.data);
253 | playerList = playerList.concat(result.data);
254 | }
255 |
256 | player.on('canplay', function() {
257 | player.play();
258 | });
259 | player.on('play', function() {
260 | var data = playerList[player.playIndex];
261 | var img = new Image();
262 | img.src = data.pic;
263 | img.onerror = function() {
264 | $('.aplayer-pic').css(
265 | 'background-image',
266 | 'url(' + nopic + ')'
267 | );
268 | };
269 | document.title =
270 | '正在播放: ' + data.title + ' - ' + data.author;
271 | setValue(data);
272 | });
273 | player.on('ended', function() {
274 | document.title = siteTitle;
275 | });
276 | if (result.data.length < 10) {
277 | $more.hide();
278 | } else {
279 | $more.text('载入更多');
280 | }
281 | } else {
282 | if (page === 1) {
283 | $('#j-input')
284 | .closest('.am-form-group')
285 | .find('.am-alert')
286 | .html(result.error || '(°ー°〃) 服务器好像罢工了')
287 | .show();
288 | } else {
289 | $more.text('没有了');
290 | setTimeout(function() {
291 | $more.slideUp();
292 | }, 1000);
293 | }
294 | }
295 | },
296 | error: function error(e, t) {
297 | if (page === 1) {
298 | var err = '(°ー°〃) 出了点小问题,请重试';
299 | if (t === 'timeout') {
300 | err = '(°ー°〃) 请求超时了,请稍后重试';
301 | }
302 | $('#j-input')
303 | .closest('.am-form-group')
304 | .find('.am-alert')
305 | .html(err)
306 | .show();
307 | } else {
308 | $more.text('(°ー°〃) 加载失败了,点击重试');
309 | }
310 | },
311 | complete: function complete() {
312 | isload = false;
313 | if (page === 1) {
314 | $('#j-input').attr('disabled', false);
315 | $('#j-submit').button('reset');
316 | }
317 | }
318 | });
319 | };
320 |
321 | ajax(input, filter, type, page);
322 | }
323 | }
324 | });
325 |
326 | $('#j-main input').focus(function() {
327 | $(this).select();
328 | });
329 |
330 | $('#j-more').on('click', function() {
331 | $(this).hide();
332 | $('#j-quote').removeClass('music-overflow');
333 | });
334 |
335 | $('#j-back').on('click', function() {
336 | if (player) {
337 | player.pause();
338 | }
339 | $('#j-validator').slideDown();
340 | $('#j-main').slideUp();
341 | $('#j-main input').val('');
342 | document.title = siteTitle;
343 | });
344 | });
345 |
--------------------------------------------------------------------------------
/template/index.php:
--------------------------------------------------------------------------------
1 |
7 | * @link https://github.com/maicong/music
8 | * @since 1.5.10
9 | *
10 | */
11 |
12 | if (!defined('MC_CORE')) {
13 | header("Location: /");
14 | exit();
15 | }
16 | ?>
17 |
18 |
19 |
20 | 音乐搜索器 - 多站合一音乐搜索,音乐在线试听
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
59 |
60 |
61 |
62 | 音乐搜索器
63 | 多站合一音乐搜索解决方案
64 |
65 |
66 |
67 |
96 |
159 |
160 |
帮助:
161 |
标红 为 音乐 ID,下划线 表示 音乐地址
162 |
163 | - 蜻蜓 FM 的音乐 ID 需要使用
| (管道符) 组合,例如 158696|5266259
164 | - 全民 K 歌的音乐名称请输入
shareuid,这是用户的 uid,搜索结果是该用户的所有公开作品
165 | - 全民 K 歌的音乐 ID 请输入
shareid 这是单曲分享 id,搜索结果是该单曲信息
166 |
167 |
168 | 网易:http://music.163.com/#/song?id=25906124
169 | QQ:http://y.qq.com/n/yqq/song/002B2EAA3brD5b.html
170 | 酷狗:http://www.kugou.com/song/#hash=08228af3cb404e8a4e7e9871bf543ff6
171 | 酷我:http://www.kuwo.cn/yinyue/382425/
172 | 虾米:http://www.xiami.com/song/2113248
173 | 百度:http://music.baidu.com/song/266069
174 | 一听:http://www.1ting.com/player/b6/player_357838.html
175 | 咪咕:http://music.migu.cn/v2/music/song/477803
176 | 荔枝:http://www.lizhi.fm/1947925/2498707770886461446
177 | 蜻蜓:http://www.qingting.fm/channels/158696/programs/5266259
178 | 喜马拉雅:http://www.ximalaya.com/51701370/sound/24755731
179 | 全民K歌 (shareuid):http://kg.qq.com/node/personal?uid=619a958c25283e88
180 | 全民K歌 (shareid):https://kg.qq.com/node/play?s=FA3h1gFhd6Vk7Ft4
181 | 5sing原创:http://5sing.kugou.com/yc/3082899.html
182 | 5sing翻唱:http://5sing.kugou.com/fc/14369766.html
183 |
184 |
查看更多
185 |
186 |
187 |
188 |
200 |
201 |
204 |
205 |
206 |
207 |
208 |
209 |
210 |
211 |
--------------------------------------------------------------------------------
/core/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') && ini_get('apc.enabled') ? $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 | foreach ($this->prefixDirsPsr4[$search] as $dir) {
383 | $length = $this->prefixLengthsPsr4[$first][$search];
384 | if (file_exists($file = $dir . DIRECTORY_SEPARATOR . substr($logicalPathPsr4, $length))) {
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 |
--------------------------------------------------------------------------------
/core/vendor/php-curl-class/php-curl-class/src/Curl/MultiCurl.php:
--------------------------------------------------------------------------------
1 | multiCurl = curl_multi_init();
39 | $this->headers = new CaseInsensitiveArray();
40 | $this->setUrl($base_url);
41 | }
42 |
43 | /**
44 | * Add Delete
45 | *
46 | * @access public
47 | * @param $url
48 | * @param $query_parameters
49 | * @param $data
50 | *
51 | * @return object
52 | */
53 | public function addDelete($url, $query_parameters = array(), $data = array())
54 | {
55 | if (is_array($url)) {
56 | $data = $query_parameters;
57 | $query_parameters = $url;
58 | $url = $this->baseUrl;
59 | }
60 | $curl = new Curl();
61 | $curl->setUrl($url, $query_parameters);
62 | $curl->setOpt(CURLOPT_CUSTOMREQUEST, 'DELETE');
63 | $curl->setOpt(CURLOPT_POSTFIELDS, $curl->buildPostData($data));
64 | $this->queueHandle($curl);
65 | return $curl;
66 | }
67 |
68 | /**
69 | * Add Download
70 | *
71 | * @access public
72 | * @param $url
73 | * @param $mixed_filename
74 | *
75 | * @return object
76 | */
77 | public function addDownload($url, $mixed_filename)
78 | {
79 | $curl = new Curl();
80 | $curl->setUrl($url);
81 |
82 | // Use tmpfile() or php://temp to avoid "Too many open files" error.
83 | if (is_callable($mixed_filename)) {
84 | $callback = $mixed_filename;
85 | $curl->downloadCompleteFunction = $callback;
86 | $curl->fileHandle = tmpfile();
87 | } else {
88 | $filename = $mixed_filename;
89 | $curl->downloadCompleteFunction = function ($instance, $fh) use ($filename) {
90 | file_put_contents($filename, stream_get_contents($fh));
91 | };
92 | $curl->fileHandle = fopen('php://temp', 'wb');
93 | }
94 |
95 | $curl->setOpt(CURLOPT_FILE, $curl->fileHandle);
96 | $curl->setOpt(CURLOPT_CUSTOMREQUEST, 'GET');
97 | $curl->setOpt(CURLOPT_HTTPGET, true);
98 | $this->queueHandle($curl);
99 | return $curl;
100 | }
101 |
102 | /**
103 | * Add Get
104 | *
105 | * @access public
106 | * @param $url
107 | * @param $data
108 | *
109 | * @return object
110 | */
111 | public function addGet($url, $data = array())
112 | {
113 | if (is_array($url)) {
114 | $data = $url;
115 | $url = $this->baseUrl;
116 | }
117 | $curl = new Curl();
118 | $curl->setUrl($url, $data);
119 | $curl->setOpt(CURLOPT_CUSTOMREQUEST, 'GET');
120 | $curl->setOpt(CURLOPT_HTTPGET, true);
121 | $this->queueHandle($curl);
122 | return $curl;
123 | }
124 |
125 | /**
126 | * Add Head
127 | *
128 | * @access public
129 | * @param $url
130 | * @param $data
131 | *
132 | * @return object
133 | */
134 | public function addHead($url, $data = array())
135 | {
136 | if (is_array($url)) {
137 | $data = $url;
138 | $url = $this->baseUrl;
139 | }
140 | $curl = new Curl();
141 | $curl->setUrl($url, $data);
142 | $curl->setOpt(CURLOPT_CUSTOMREQUEST, 'HEAD');
143 | $curl->setOpt(CURLOPT_NOBODY, true);
144 | $this->queueHandle($curl);
145 | return $curl;
146 | }
147 |
148 | /**
149 | * Add Options
150 | *
151 | * @access public
152 | * @param $url
153 | * @param $data
154 | *
155 | * @return object
156 | */
157 | public function addOptions($url, $data = array())
158 | {
159 | if (is_array($url)) {
160 | $data = $url;
161 | $url = $this->baseUrl;
162 | }
163 | $curl = new Curl();
164 | $curl->setUrl($url, $data);
165 | $curl->removeHeader('Content-Length');
166 | $curl->setOpt(CURLOPT_CUSTOMREQUEST, 'OPTIONS');
167 | $this->queueHandle($curl);
168 | return $curl;
169 | }
170 |
171 | /**
172 | * Add Patch
173 | *
174 | * @access public
175 | * @param $url
176 | * @param $data
177 | *
178 | * @return object
179 | */
180 | public function addPatch($url, $data = array())
181 | {
182 | if (is_array($url)) {
183 | $data = $url;
184 | $url = $this->baseUrl;
185 | }
186 | $curl = new Curl();
187 | $curl->setUrl($url);
188 | $curl->removeHeader('Content-Length');
189 | $curl->setOpt(CURLOPT_CUSTOMREQUEST, 'PATCH');
190 | $curl->setOpt(CURLOPT_POSTFIELDS, $data);
191 | $this->queueHandle($curl);
192 | return $curl;
193 | }
194 |
195 | /**
196 | * Add Post
197 | *
198 | * @access public
199 | * @param $url
200 | * @param $data
201 | * @param $follow_303_with_post
202 | * If true, will cause 303 redirections to be followed using GET requests (default: false).
203 | * Note: Redirections are only followed if the CURLOPT_FOLLOWLOCATION option is set to true.
204 | *
205 | * @return object
206 | */
207 | public function addPost($url, $data = array(), $follow_303_with_post = false)
208 | {
209 | if (is_array($url)) {
210 | $follow_303_with_post = (bool)$data;
211 | $data = $url;
212 | $url = $this->baseUrl;
213 | }
214 |
215 | $curl = new Curl();
216 |
217 | if (is_array($data) && empty($data)) {
218 | $curl->removeHeader('Content-Length');
219 | }
220 |
221 | $curl->setUrl($url);
222 |
223 | /*
224 | * For post-redirect-get requests, the CURLOPT_CUSTOMREQUEST option must not
225 | * be set, otherwise cURL will perform POST requests for redirections.
226 | */
227 | if (!$follow_303_with_post) {
228 | $curl->setOpt(CURLOPT_CUSTOMREQUEST, 'POST');
229 | }
230 |
231 | $curl->setOpt(CURLOPT_POST, true);
232 | $curl->setOpt(CURLOPT_POSTFIELDS, $curl->buildPostData($data));
233 | $this->queueHandle($curl);
234 | return $curl;
235 | }
236 |
237 | /**
238 | * Add Put
239 | *
240 | * @access public
241 | * @param $url
242 | * @param $data
243 | *
244 | * @return object
245 | */
246 | public function addPut($url, $data = array())
247 | {
248 | if (is_array($url)) {
249 | $data = $url;
250 | $url = $this->baseUrl;
251 | }
252 | $curl = new Curl();
253 | $curl->setUrl($url);
254 | $curl->setOpt(CURLOPT_CUSTOMREQUEST, 'PUT');
255 | $put_data = $curl->buildPostData($data);
256 | if (is_string($put_data)) {
257 | $curl->setHeader('Content-Length', strlen($put_data));
258 | }
259 | $curl->setOpt(CURLOPT_POSTFIELDS, $put_data);
260 | $this->queueHandle($curl);
261 | return $curl;
262 | }
263 |
264 | /**
265 | * Add Search
266 | *
267 | * @access public
268 | * @param $url
269 | * @param $data
270 | *
271 | * @return object
272 | */
273 | public function addSearch($url, $data = array())
274 | {
275 | if (is_array($url)) {
276 | $data = $url;
277 | $url = $this->baseUrl;
278 | }
279 | $curl = new Curl();
280 | $curl->setUrl($url);
281 | $curl->setOpt(CURLOPT_CUSTOMREQUEST, 'SEARCH');
282 | $put_data = $curl->buildPostData($data);
283 | if (is_string($put_data)) {
284 | $curl->setHeader('Content-Length', strlen($put_data));
285 | }
286 | $curl->setOpt(CURLOPT_POSTFIELDS, $put_data);
287 | $this->queueHandle($curl);
288 | return $curl;
289 | }
290 |
291 | /**
292 | * Add Curl
293 | *
294 | * Add a Curl instance to the handle queue.
295 | *
296 | * @access public
297 | * @param $curl
298 | *
299 | * @return object
300 | */
301 | public function addCurl(Curl $curl)
302 | {
303 | $this->queueHandle($curl);
304 | return $curl;
305 | }
306 |
307 | /**
308 | * Before Send
309 | *
310 | * @access public
311 | * @param $callback
312 | */
313 | public function beforeSend($callback)
314 | {
315 | $this->beforeSendFunction = $callback;
316 | }
317 |
318 | /**
319 | * Close
320 | *
321 | * @access public
322 | */
323 | public function close()
324 | {
325 | foreach ($this->curls as $curl) {
326 | $curl->close();
327 | }
328 |
329 | if (is_resource($this->multiCurl)) {
330 | curl_multi_close($this->multiCurl);
331 | }
332 | }
333 |
334 | /**
335 | * Complete
336 | *
337 | * @access public
338 | * @param $callback
339 | */
340 | public function complete($callback)
341 | {
342 | $this->completeFunction = $callback;
343 | }
344 |
345 | /**
346 | * Error
347 | *
348 | * @access public
349 | * @param $callback
350 | */
351 | public function error($callback)
352 | {
353 | $this->errorFunction = $callback;
354 | }
355 |
356 | /**
357 | * Get Opt
358 | *
359 | * @access public
360 | * @param $option
361 | *
362 | * @return mixed
363 | */
364 | public function getOpt($option)
365 | {
366 | return isset($this->options[$option]) ? $this->options[$option] : null;
367 | }
368 |
369 | /**
370 | * Set Basic Authentication
371 | *
372 | * @access public
373 | * @param $username
374 | * @param $password
375 | */
376 | public function setBasicAuthentication($username, $password = '')
377 | {
378 | $this->setOpt(CURLOPT_HTTPAUTH, CURLAUTH_BASIC);
379 | $this->setOpt(CURLOPT_USERPWD, $username . ':' . $password);
380 | }
381 |
382 | /**
383 | * Set Concurrency
384 | *
385 | * @access public
386 | * @param $concurrency
387 | */
388 | public function setConcurrency($concurrency)
389 | {
390 | $this->concurrency = $concurrency;
391 | }
392 |
393 | /**
394 | * Set Digest Authentication
395 | *
396 | * @access public
397 | * @param $username
398 | * @param $password
399 | */
400 | public function setDigestAuthentication($username, $password = '')
401 | {
402 | $this->setOpt(CURLOPT_HTTPAUTH, CURLAUTH_DIGEST);
403 | $this->setOpt(CURLOPT_USERPWD, $username . ':' . $password);
404 | }
405 |
406 | /**
407 | * Set Cookie
408 | *
409 | * @access public
410 | * @param $key
411 | * @param $value
412 | */
413 | public function setCookie($key, $value)
414 | {
415 | $this->cookies[$key] = $value;
416 | }
417 |
418 | /**
419 | * Set Cookies
420 | *
421 | * @access public
422 | * @param $cookies
423 | */
424 | public function setCookies($cookies)
425 | {
426 | foreach ($cookies as $key => $value) {
427 | $this->cookies[$key] = $value;
428 | }
429 | }
430 |
431 | /**
432 | * Set Port
433 | *
434 | * @access public
435 | * @param $port
436 | */
437 | public function setPort($port)
438 | {
439 | $this->setOpt(CURLOPT_PORT, intval($port));
440 | }
441 |
442 | /**
443 | * Set Connect Timeout
444 | *
445 | * @access public
446 | * @param $seconds
447 | */
448 | public function setConnectTimeout($seconds)
449 | {
450 | $this->setOpt(CURLOPT_CONNECTTIMEOUT, $seconds);
451 | }
452 |
453 | /**
454 | * Set Cookie String
455 | *
456 | * @access public
457 | * @param $string
458 | */
459 | public function setCookieString($string)
460 | {
461 | $this->setOpt(CURLOPT_COOKIE, $string);
462 | }
463 |
464 | /**
465 | * Set Cookie File
466 | *
467 | * @access public
468 | * @param $cookie_file
469 | */
470 | public function setCookieFile($cookie_file)
471 | {
472 | $this->setOpt(CURLOPT_COOKIEFILE, $cookie_file);
473 | }
474 |
475 | /**
476 | * Set Cookie Jar
477 | *
478 | * @access public
479 | * @param $cookie_jar
480 | */
481 | public function setCookieJar($cookie_jar)
482 | {
483 | $this->setOpt(CURLOPT_COOKIEJAR, $cookie_jar);
484 | }
485 |
486 | /**
487 | * Set Header
488 | *
489 | * Add extra header to include in the request.
490 | *
491 | * @access public
492 | * @param $key
493 | * @param $value
494 | */
495 | public function setHeader($key, $value)
496 | {
497 | $this->headers[$key] = $value;
498 | }
499 |
500 | /**
501 | * Set Headers
502 | *
503 | * Add extra headers to include in the request.
504 | *
505 | * @access public
506 | * @param $headers
507 | */
508 | public function setHeaders($headers)
509 | {
510 | foreach ($headers as $key => $value) {
511 | $this->headers[$key] = $value;
512 | }
513 | }
514 |
515 | /**
516 | * Set JSON Decoder
517 | *
518 | * @access public
519 | * @param $mixed boolean|callable
520 | */
521 | public function setJsonDecoder($mixed)
522 | {
523 | if ($mixed === false) {
524 | $this->jsonDecoder = false;
525 | } elseif (is_callable($mixed)) {
526 | $this->jsonDecoder = $mixed;
527 | }
528 | }
529 |
530 | /**
531 | * Set XML Decoder
532 | *
533 | * @access public
534 | * @param $mixed boolean|callable
535 | */
536 | public function setXmlDecoder($mixed)
537 | {
538 | if ($mixed === false) {
539 | $this->xmlDecoder = false;
540 | } elseif (is_callable($mixed)) {
541 | $this->xmlDecoder = $mixed;
542 | }
543 | }
544 |
545 | /**
546 | * Set Opt
547 | *
548 | * @access public
549 | * @param $option
550 | * @param $value
551 | */
552 | public function setOpt($option, $value)
553 | {
554 | $this->options[$option] = $value;
555 | }
556 |
557 | /**
558 | * Set Opts
559 | *
560 | * @access public
561 | * @param $options
562 | */
563 | public function setOpts($options)
564 | {
565 | foreach ($options as $option => $value) {
566 | $this->setOpt($option, $value);
567 | }
568 | }
569 |
570 | /**
571 | * Set Referer
572 | *
573 | * @access public
574 | * @param $referer
575 | */
576 | public function setReferer($referer)
577 | {
578 | $this->setReferrer($referer);
579 | }
580 |
581 | /**
582 | * Set Referrer
583 | *
584 | * @access public
585 | * @param $referrer
586 | */
587 | public function setReferrer($referrer)
588 | {
589 | $this->setOpt(CURLOPT_REFERER, $referrer);
590 | }
591 |
592 | /**
593 | * Set Retry
594 | *
595 | * Number of retries to attempt or decider callable. Maximum number of
596 | * attempts is $maximum_number_of_retries + 1.
597 | *
598 | * @access public
599 | * @param $mixed
600 | */
601 | public function setRetry($mixed)
602 | {
603 | $this->retry = $mixed;
604 | }
605 |
606 | /**
607 | * Set Timeout
608 | *
609 | * @access public
610 | * @param $seconds
611 | */
612 | public function setTimeout($seconds)
613 | {
614 | $this->setOpt(CURLOPT_TIMEOUT, $seconds);
615 | }
616 |
617 | /**
618 | * Set Url
619 | *
620 | * @access public
621 | * @param $url
622 | */
623 | public function setUrl($url)
624 | {
625 | $this->baseUrl = $url;
626 | }
627 |
628 | /**
629 | * Set User Agent
630 | *
631 | * @access public
632 | * @param $user_agent
633 | */
634 | public function setUserAgent($user_agent)
635 | {
636 | $this->setOpt(CURLOPT_USERAGENT, $user_agent);
637 | }
638 |
639 | /**
640 | * Start
641 | *
642 | * @access public
643 | */
644 | public function start()
645 | {
646 | if ($this->isStarted) {
647 | return;
648 | }
649 |
650 | $this->isStarted = true;
651 |
652 | $concurrency = $this->concurrency;
653 | if ($concurrency > count($this->curls)) {
654 | $concurrency = count($this->curls);
655 | }
656 |
657 | for ($i = 0; $i < $concurrency; $i++) {
658 | $this->initHandle(array_shift($this->curls));
659 | }
660 |
661 | do {
662 | // Wait for activity on any curl_multi connection when curl_multi_select (libcurl) fails to correctly block.
663 | // https://bugs.php.net/bug.php?id=63411
664 | if (curl_multi_select($this->multiCurl) === -1) {
665 | usleep(100000);
666 | }
667 |
668 | curl_multi_exec($this->multiCurl, $active);
669 |
670 | while (!($info_array = curl_multi_info_read($this->multiCurl)) === false) {
671 | if ($info_array['msg'] === CURLMSG_DONE) {
672 | foreach ($this->activeCurls as $key => $ch) {
673 | if ($ch->curl === $info_array['handle']) {
674 | // Set the error code for multi handles using the "result" key in the array returned by
675 | // curl_multi_info_read(). Using curl_errno() on a multi handle will incorrectly return 0
676 | // for errors.
677 | $ch->curlErrorCode = $info_array['result'];
678 | $ch->exec($ch->curl);
679 |
680 | if ($ch->attemptRetry()) {
681 | // Remove completed handle before adding again in order to retry request.
682 | curl_multi_remove_handle($this->multiCurl, $ch->curl);
683 |
684 | $curlm_error_code = curl_multi_add_handle($this->multiCurl, $ch->curl);
685 | if (!($curlm_error_code === CURLM_OK)) {
686 | throw new \ErrorException(
687 | 'cURL multi add handle error: ' . curl_multi_strerror($curlm_error_code)
688 | );
689 | }
690 | } else {
691 | $ch->execDone();
692 |
693 | // Remove completed instance from active curls.
694 | unset($this->activeCurls[$key]);
695 |
696 | // Start a new request before removing the handle of the completed one.
697 | if (count($this->curls) >= 1) {
698 | $this->initHandle(array_shift($this->curls));
699 | }
700 | curl_multi_remove_handle($this->multiCurl, $ch->curl);
701 |
702 | // Clean up completed instance.
703 | $ch->close();
704 | }
705 |
706 | break;
707 | }
708 | }
709 | }
710 | }
711 |
712 | if (!$active) {
713 | $active = count($this->activeCurls);
714 | }
715 | } while ($active > 0);
716 |
717 | $this->isStarted = false;
718 | }
719 |
720 | /**
721 | * Success
722 | *
723 | * @access public
724 | * @param $callback
725 | */
726 | public function success($callback)
727 | {
728 | $this->successFunction = $callback;
729 | }
730 |
731 | /**
732 | * Unset Header
733 | *
734 | * Remove extra header previously set using Curl::setHeader().
735 | *
736 | * @access public
737 | * @param $key
738 | */
739 | public function unsetHeader($key)
740 | {
741 | unset($this->headers[$key]);
742 | }
743 |
744 | /**
745 | * Remove Header
746 | *
747 | * Remove an internal header from the request.
748 | * Using `curl -H "Host:" ...' is equivalent to $curl->removeHeader('Host');.
749 | *
750 | * @access public
751 | * @param $key
752 | */
753 | public function removeHeader($key)
754 | {
755 | $this->setHeader($key, '');
756 | }
757 |
758 | /**
759 | * Verbose
760 | *
761 | * @access public
762 | * @param bool $on
763 | * @param resource $output
764 | */
765 | public function verbose($on = true, $output = STDERR)
766 | {
767 | // Turn off CURLINFO_HEADER_OUT for verbose to work. This has the side
768 | // effect of causing Curl::requestHeaders to be empty.
769 | if ($on) {
770 | $this->setOpt(CURLINFO_HEADER_OUT, false);
771 | }
772 | $this->setOpt(CURLOPT_VERBOSE, $on);
773 | $this->setOpt(CURLOPT_STDERR, $output);
774 | }
775 |
776 | /**
777 | * Destruct
778 | *
779 | * @access public
780 | */
781 | public function __destruct()
782 | {
783 | $this->close();
784 | }
785 |
786 | /**
787 | * Queue Handle
788 | *
789 | * @access private
790 | * @param $curl
791 | */
792 | private function queueHandle($curl)
793 | {
794 | // Use sequential ids to allow for ordered post processing.
795 | $curl->id = $this->nextCurlId++;
796 | $curl->isChildOfMultiCurl = true;
797 | $this->curls[$curl->id] = $curl;
798 | }
799 |
800 | /**
801 | * Init Handle
802 | *
803 | * @access private
804 | * @param $curl
805 | * @throws \ErrorException
806 | */
807 | private function initHandle($curl)
808 | {
809 | // Set callbacks if not already individually set.
810 | if ($curl->beforeSendFunction === null) {
811 | $curl->beforeSend($this->beforeSendFunction);
812 | }
813 | if ($curl->successFunction === null) {
814 | $curl->success($this->successFunction);
815 | }
816 | if ($curl->errorFunction === null) {
817 | $curl->error($this->errorFunction);
818 | }
819 | if ($curl->completeFunction === null) {
820 | $curl->complete($this->completeFunction);
821 | }
822 |
823 | $curl->setOpts($this->options);
824 | $curl->setHeaders($this->headers);
825 | $curl->setRetry($this->retry);
826 |
827 | foreach ($this->cookies as $key => $value) {
828 | $curl->setCookie($key, $value);
829 | }
830 |
831 | $curl->setJsonDecoder($this->jsonDecoder);
832 | $curl->setXmlDecoder($this->xmlDecoder);
833 |
834 | $curlm_error_code = curl_multi_add_handle($this->multiCurl, $curl->curl);
835 | if (!($curlm_error_code === CURLM_OK)) {
836 | throw new \ErrorException('cURL multi add handle error: ' . curl_multi_strerror($curlm_error_code));
837 | }
838 |
839 | $this->activeCurls[$curl->id] = $curl;
840 | $curl->call($curl->beforeSendFunction);
841 | }
842 | }
843 |
--------------------------------------------------------------------------------
/core/vendor/php-curl-class/php-curl-class/src/Curl/Curl.php:
--------------------------------------------------------------------------------
1 |
63 | // CTL =
65 | // separators = "(" | ")" | "<" | ">" | "@"
66 | // | "," | ";" | ":" | "\" | <">
67 | // | "/" | "[" | "]" | "?" | "="
68 | // | "{" | "}" | SP | HT
69 | // SP =
70 | // HT =
71 | // <"> =
72 | '!', '#', '$', '%', '&', "'", '*', '+', '-', '.', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B',
73 | 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X',
74 | 'Y', 'Z', '^', '_', '`', 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q',
75 | 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', '|', '~',
76 | );
77 | public static $RFC6265 = array(
78 | // RFC 6265: "US-ASCII characters excluding CTLs, whitespace DQUOTE, comma, semicolon, and backslash".
79 | // %x21
80 | '!',
81 | // %x23-2B
82 | '#', '$', '%', '&', "'", '(', ')', '*', '+',
83 | // %x2D-3A
84 | '-', '.', '/', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', ':',
85 | // %x3C-5B
86 | '<', '=', '>', '?', '@', 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q',
87 | 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', '[',
88 | // %x5D-7E
89 | ']', '^', '_', '`', 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r',
90 | 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', '{', '|', '}', '~',
91 | );
92 |
93 | private static $deferredProperties = array(
94 | 'effectiveUrl',
95 | 'rfc2616',
96 | 'rfc6265',
97 | 'totalTime',
98 | );
99 |
100 | /**
101 | * Construct
102 | *
103 | * @access public
104 | * @param $base_url
105 | * @throws \ErrorException
106 | */
107 | public function __construct($base_url = null)
108 | {
109 | if (!extension_loaded('curl')) {
110 | throw new \ErrorException('cURL library is not loaded');
111 | }
112 |
113 | $this->curl = curl_init();
114 | $this->id = uniqid('', true);
115 | $this->setDefaultUserAgent();
116 | $this->setDefaultTimeout();
117 | $this->setOpt(CURLINFO_HEADER_OUT, true);
118 |
119 | // Create a placeholder to temporarily store the header callback data.
120 | $header_callback_data = new \stdClass();
121 | $header_callback_data->rawResponseHeaders = '';
122 | $header_callback_data->responseCookies = array();
123 | $this->headerCallbackData = $header_callback_data;
124 | $this->setOpt(CURLOPT_HEADERFUNCTION, $this->createHeaderCallback($header_callback_data));
125 |
126 | $this->setOpt(CURLOPT_RETURNTRANSFER, true);
127 | $this->headers = new CaseInsensitiveArray();
128 | $this->setUrl($base_url);
129 | }
130 |
131 | /**
132 | * Before Send
133 | *
134 | * @access public
135 | * @param $callback
136 | */
137 | public function beforeSend($callback)
138 | {
139 | $this->beforeSendFunction = $callback;
140 | }
141 |
142 | /**
143 | * Build Post Data
144 | *
145 | * @access public
146 | * @param $data
147 | *
148 | * @return array|string
149 | */
150 | public function buildPostData($data)
151 | {
152 | $binary_data = false;
153 | if (is_array($data)) {
154 | // Return JSON-encoded string when the request's content-type is JSON.
155 | if (isset($this->headers['Content-Type']) &&
156 | preg_match($this->jsonPattern, $this->headers['Content-Type'])) {
157 | $json_str = json_encode($data);
158 | if (!($json_str === false)) {
159 | $data = $json_str;
160 | }
161 | } else {
162 | // Manually build a single-dimensional array from a multi-dimensional array as using curl_setopt($ch,
163 | // CURLOPT_POSTFIELDS, $data) doesn't correctly handle multi-dimensional arrays when files are
164 | // referenced.
165 | if (ArrayUtil::is_array_multidim($data)) {
166 | $data = ArrayUtil::array_flatten_multidim($data);
167 | }
168 |
169 | // Modify array values to ensure any referenced files are properly handled depending on the support of
170 | // the @filename API or CURLFile usage. This also fixes the warning "curl_setopt(): The usage of the
171 | // @filename API for file uploading is deprecated. Please use the CURLFile class instead". Ignore
172 | // non-file values prefixed with the @ character.
173 | foreach ($data as $key => $value) {
174 | if (is_string($value) && strpos($value, '@') === 0 && is_file(substr($value, 1))) {
175 | $binary_data = true;
176 | if (class_exists('CURLFile')) {
177 | $data[$key] = new \CURLFile(substr($value, 1));
178 | }
179 | } elseif ($value instanceof \CURLFile) {
180 | $binary_data = true;
181 | }
182 | }
183 | }
184 | }
185 |
186 | if (!$binary_data && (is_array($data) || is_object($data))) {
187 | $data = http_build_query($data, '', '&');
188 | }
189 |
190 | return $data;
191 | }
192 |
193 | /**
194 | * Call
195 | *
196 | * @access public
197 | */
198 | public function call()
199 | {
200 | $args = func_get_args();
201 | $function = array_shift($args);
202 | if (is_callable($function)) {
203 | array_unshift($args, $this);
204 | call_user_func_array($function, $args);
205 | }
206 | }
207 |
208 | /**
209 | * Close
210 | *
211 | * @access public
212 | */
213 | public function close()
214 | {
215 | if (is_resource($this->curl)) {
216 | curl_close($this->curl);
217 | }
218 | $this->options = null;
219 | $this->jsonDecoder = null;
220 | $this->jsonDecoderArgs = null;
221 | $this->xmlDecoder = 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->completeFunction = $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
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 | $this->setOpt(CURLOPT_POSTFIELDS, $this->buildPostData($data));
269 | return $this->exec();
270 | }
271 |
272 | /**
273 | * Download
274 | *
275 | * @access public
276 | * @param $url
277 | * @param $mixed_filename
278 | *
279 | * @return boolean
280 | */
281 | public function download($url, $mixed_filename)
282 | {
283 | if (is_callable($mixed_filename)) {
284 | $this->downloadCompleteFunction = $mixed_filename;
285 | $this->fileHandle = tmpfile();
286 | } else {
287 | $filename = $mixed_filename;
288 |
289 | // Use a temporary file when downloading. Not using a temporary file can cause an error when an existing
290 | // file has already fully completed downloading and a new download is started with the same destination save
291 | // path. The download request will include header "Range: bytes=$filesize-" which is syntactically valid,
292 | // but unsatisfiable.
293 | $download_filename = $filename . '.pccdownload';
294 |
295 | $mode = 'wb';
296 | // Attempt to resume download only when a temporary download file exists and is not empty.
297 | if (file_exists($download_filename) && $filesize = filesize($download_filename)) {
298 | $mode = 'ab';
299 | $first_byte_position = $filesize;
300 | $range = $first_byte_position . '-';
301 | $this->setOpt(CURLOPT_RANGE, $range);
302 | }
303 | $this->fileHandle = fopen($download_filename, $mode);
304 |
305 | // Move the downloaded temporary file to the destination save path.
306 | $this->downloadCompleteFunction = function ($instance, $fh) use ($download_filename, $filename) {
307 | // Close the open file handle before renaming the file.
308 | if (is_resource($fh)) {
309 | fclose($fh);
310 | }
311 |
312 | rename($download_filename, $filename);
313 | };
314 | }
315 |
316 | $this->setOpt(CURLOPT_FILE, $this->fileHandle);
317 | $this->get($url);
318 |
319 | return ! $this->error;
320 | }
321 |
322 | /**
323 | * Error
324 | *
325 | * @access public
326 | * @param $callback
327 | */
328 | public function error($callback)
329 | {
330 | $this->errorFunction = $callback;
331 | }
332 |
333 | /**
334 | * Exec
335 | *
336 | * @access public
337 | * @param $ch
338 | *
339 | * @return mixed Returns the value provided by parseResponse.
340 | */
341 | public function exec($ch = null)
342 | {
343 | $this->attempts += 1;
344 |
345 | if ($ch === null) {
346 | $this->responseCookies = array();
347 | $this->call($this->beforeSendFunction);
348 | $this->rawResponse = curl_exec($this->curl);
349 | $this->curlErrorCode = curl_errno($this->curl);
350 | $this->curlErrorMessage = curl_error($this->curl);
351 | } else {
352 | $this->rawResponse = curl_multi_getcontent($ch);
353 | $this->curlErrorMessage = curl_error($ch);
354 | }
355 | $this->curlError = !($this->curlErrorCode === 0);
356 |
357 | // Transfer the header callback data and release the temporary store to avoid memory leak.
358 | $this->rawResponseHeaders = $this->headerCallbackData->rawResponseHeaders;
359 | $this->responseCookies = $this->headerCallbackData->responseCookies;
360 | $this->headerCallbackData->rawResponseHeaders = null;
361 | $this->headerCallbackData->responseCookies = null;
362 |
363 | // Include additional error code information in error message when possible.
364 | if ($this->curlError && function_exists('curl_strerror')) {
365 | $this->curlErrorMessage =
366 | curl_strerror($this->curlErrorCode) . (
367 | empty($this->curlErrorMessage) ? '' : ': ' . $this->curlErrorMessage
368 | );
369 | }
370 |
371 | $this->httpStatusCode = $this->getInfo(CURLINFO_HTTP_CODE);
372 | $this->httpError = in_array(floor($this->httpStatusCode / 100), array(4, 5));
373 | $this->error = $this->curlError || $this->httpError;
374 | $this->errorCode = $this->error ? ($this->curlError ? $this->curlErrorCode : $this->httpStatusCode) : 0;
375 |
376 | // NOTE: CURLINFO_HEADER_OUT set to true is required for requestHeaders
377 | // to not be empty (e.g. $curl->setOpt(CURLINFO_HEADER_OUT, true);).
378 | if ($this->getOpt(CURLINFO_HEADER_OUT) === true) {
379 | $this->requestHeaders = $this->parseRequestHeaders($this->getInfo(CURLINFO_HEADER_OUT));
380 | }
381 | $this->responseHeaders = $this->parseResponseHeaders($this->rawResponseHeaders);
382 | $this->response = $this->parseResponse($this->responseHeaders, $this->rawResponse);
383 |
384 | $this->httpErrorMessage = '';
385 | if ($this->error) {
386 | if (isset($this->responseHeaders['Status-Line'])) {
387 | $this->httpErrorMessage = $this->responseHeaders['Status-Line'];
388 | }
389 | }
390 | $this->errorMessage = $this->curlError ? $this->curlErrorMessage : $this->httpErrorMessage;
391 |
392 | // Reset select deferred properties so that they may be recalculated.
393 | unset($this->effectiveUrl);
394 | unset($this->totalTime);
395 |
396 | // Reset content-length possibly set from a PUT or SEARCH request.
397 | $this->unsetHeader('Content-Length');
398 |
399 | // Reset nobody setting possibly set from a HEAD request.
400 | $this->setOpt(CURLOPT_NOBODY, false);
401 |
402 | // Allow multicurl to attempt retry as needed.
403 | if ($this->isChildOfMultiCurl) {
404 | return;
405 | }
406 |
407 | if ($this->attemptRetry()) {
408 | return $this->exec($ch);
409 | }
410 |
411 | $this->execDone();
412 |
413 | return $this->response;
414 | }
415 |
416 | public function execDone()
417 | {
418 | if ($this->error) {
419 | $this->call($this->errorFunction);
420 | } else {
421 | $this->call($this->successFunction);
422 | }
423 |
424 | $this->call($this->completeFunction);
425 |
426 | // Close open file handles and reset the curl instance.
427 | if (!($this->fileHandle === null)) {
428 | $this->downloadComplete($this->fileHandle);
429 | }
430 | }
431 |
432 | /**
433 | * Get
434 | *
435 | * @access public
436 | * @param $url
437 | * @param $data
438 | *
439 | * @return mixed Returns the value provided by exec.
440 | */
441 | public function get($url, $data = array())
442 | {
443 | if (is_array($url)) {
444 | $data = $url;
445 | $url = (string)$this->url;
446 | }
447 | $this->setUrl($url, $data);
448 | $this->setOpt(CURLOPT_CUSTOMREQUEST, 'GET');
449 | $this->setOpt(CURLOPT_HTTPGET, true);
450 | return $this->exec();
451 | }
452 |
453 | /**
454 | * Get Info
455 | *
456 | * @access public
457 | * @param $opt
458 | *
459 | * @return mixed
460 | */
461 | public function getInfo($opt = null)
462 | {
463 | $args = array();
464 | $args[] = $this->curl;
465 |
466 | if (func_num_args()) {
467 | $args[] = $opt;
468 | }
469 |
470 | return call_user_func_array('curl_getinfo', $args);
471 | }
472 |
473 | /**
474 | * Get Opt
475 | *
476 | * @access public
477 | * @param $option
478 | *
479 | * @return mixed
480 | */
481 | public function getOpt($option)
482 | {
483 | return isset($this->options[$option]) ? $this->options[$option] : null;
484 | }
485 |
486 | /**
487 | * Head
488 | *
489 | * @access public
490 | * @param $url
491 | * @param $data
492 | *
493 | * @return mixed
494 | */
495 | public function head($url, $data = array())
496 | {
497 | if (is_array($url)) {
498 | $data = $url;
499 | $url = (string)$this->url;
500 | }
501 | $this->setUrl($url, $data);
502 | $this->setOpt(CURLOPT_CUSTOMREQUEST, 'HEAD');
503 | $this->setOpt(CURLOPT_NOBODY, true);
504 | return $this->exec();
505 | }
506 |
507 | /**
508 | * Options
509 | *
510 | * @access public
511 | * @param $url
512 | * @param $data
513 | *
514 | * @return mixed
515 | */
516 | public function options($url, $data = array())
517 | {
518 | if (is_array($url)) {
519 | $data = $url;
520 | $url = (string)$this->url;
521 | }
522 | $this->setUrl($url, $data);
523 | $this->setOpt(CURLOPT_CUSTOMREQUEST, 'OPTIONS');
524 | return $this->exec();
525 | }
526 |
527 | /**
528 | * Patch
529 | *
530 | * @access public
531 | * @param $url
532 | * @param $data
533 | *
534 | * @return mixed
535 | */
536 | public function patch($url, $data = array())
537 | {
538 | if (is_array($url)) {
539 | $data = $url;
540 | $url = (string)$this->url;
541 | }
542 |
543 | if (is_array($data) && empty($data)) {
544 | $this->removeHeader('Content-Length');
545 | }
546 |
547 | $this->setUrl($url);
548 | $this->setOpt(CURLOPT_CUSTOMREQUEST, 'PATCH');
549 | $this->setOpt(CURLOPT_POSTFIELDS, $this->buildPostData($data));
550 | return $this->exec();
551 | }
552 |
553 | /**
554 | * Post
555 | *
556 | * @access public
557 | * @param $url
558 | * @param $data
559 | * @param $follow_303_with_post
560 | * If true, will cause 303 redirections to be followed using a POST request (default: false).
561 | * Notes:
562 | * - Redirections are only followed if the CURLOPT_FOLLOWLOCATION option is set to true.
563 | * - According to the HTTP specs (see [1]), a 303 redirection should be followed using
564 | * the GET method. 301 and 302 must not.
565 | * - In order to force a 303 redirection to be performed using the same method, the
566 | * underlying cURL object must be set in a special state (the CURLOPT_CURSTOMREQUEST
567 | * option must be set to the method to use after the redirection). Due to a limitation
568 | * of the cURL extension of PHP < 5.5.11 ([2], [3]) and of HHVM, it is not possible
569 | * to reset this option. Using these PHP engines, it is therefore impossible to
570 | * restore this behavior on an existing php-curl-class Curl object.
571 | *
572 | * @return mixed
573 | *
574 | * [1] https://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10.3.2
575 | * [2] https://github.com/php/php-src/pull/531
576 | * [3] http://php.net/ChangeLog-5.php#5.5.11
577 | */
578 | public function post($url, $data = array(), $follow_303_with_post = false)
579 | {
580 | if (is_array($url)) {
581 | $follow_303_with_post = (bool)$data;
582 | $data = $url;
583 | $url = (string)$this->url;
584 | }
585 |
586 | $this->setUrl($url);
587 |
588 | if ($follow_303_with_post) {
589 | $this->setOpt(CURLOPT_CUSTOMREQUEST, 'POST');
590 | } else {
591 | if (isset($this->options[CURLOPT_CUSTOMREQUEST])) {
592 | if ((version_compare(PHP_VERSION, '5.5.11') < 0) || defined('HHVM_VERSION')) {
593 | trigger_error(
594 | 'Due to technical limitations of PHP <= 5.5.11 and HHVM, it is not possible to '
595 | . 'perform a post-redirect-get request using a php-curl-class Curl object that '
596 | . 'has already been used to perform other types of requests. Either use a new '
597 | . 'php-curl-class Curl object or upgrade your PHP engine.',
598 | E_USER_ERROR
599 | );
600 | } else {
601 | $this->setOpt(CURLOPT_CUSTOMREQUEST, null);
602 | }
603 | }
604 | }
605 |
606 | $this->setOpt(CURLOPT_POST, true);
607 | $this->setOpt(CURLOPT_POSTFIELDS, $this->buildPostData($data));
608 | return $this->exec();
609 | }
610 |
611 | /**
612 | * Put
613 | *
614 | * @access public
615 | * @param $url
616 | * @param $data
617 | *
618 | * @return mixed
619 | */
620 | public function put($url, $data = array())
621 | {
622 | if (is_array($url)) {
623 | $data = $url;
624 | $url = (string)$this->url;
625 | }
626 | $this->setUrl($url);
627 | $this->setOpt(CURLOPT_CUSTOMREQUEST, 'PUT');
628 | $put_data = $this->buildPostData($data);
629 | if (empty($this->options[CURLOPT_INFILE]) && empty($this->options[CURLOPT_INFILESIZE])) {
630 | if (is_string($put_data)) {
631 | $this->setHeader('Content-Length', strlen($put_data));
632 | }
633 | }
634 | if (!empty($put_data)) {
635 | $this->setOpt(CURLOPT_POSTFIELDS, $put_data);
636 | }
637 | return $this->exec();
638 | }
639 |
640 | /**
641 | * Search
642 | *
643 | * @access public
644 | * @param $url
645 | * @param $data
646 | *
647 | * @return mixed
648 | */
649 | public function search($url, $data = array())
650 | {
651 | if (is_array($url)) {
652 | $data = $url;
653 | $url = (string)$this->url;
654 | }
655 | $this->setUrl($url);
656 | $this->setOpt(CURLOPT_CUSTOMREQUEST, 'SEARCH');
657 | $put_data = $this->buildPostData($data);
658 | if (empty($this->options[CURLOPT_INFILE]) && empty($this->options[CURLOPT_INFILESIZE])) {
659 | if (is_string($put_data)) {
660 | $this->setHeader('Content-Length', strlen($put_data));
661 | }
662 | }
663 | if (!empty($put_data)) {
664 | $this->setOpt(CURLOPT_POSTFIELDS, $put_data);
665 | }
666 | return $this->exec();
667 | }
668 |
669 | /**
670 | * Set Basic Authentication
671 | *
672 | * @access public
673 | * @param $username
674 | * @param $password
675 | */
676 | public function setBasicAuthentication($username, $password = '')
677 | {
678 | $this->setOpt(CURLOPT_HTTPAUTH, CURLAUTH_BASIC);
679 | $this->setOpt(CURLOPT_USERPWD, $username . ':' . $password);
680 | }
681 |
682 | /**
683 | * Set Digest Authentication
684 | *
685 | * @access public
686 | * @param $username
687 | * @param $password
688 | */
689 | public function setDigestAuthentication($username, $password = '')
690 | {
691 | $this->setOpt(CURLOPT_HTTPAUTH, CURLAUTH_DIGEST);
692 | $this->setOpt(CURLOPT_USERPWD, $username . ':' . $password);
693 | }
694 |
695 | /**
696 | * Set Cookie
697 | *
698 | * @access public
699 | * @param $key
700 | * @param $value
701 | */
702 | public function setCookie($key, $value)
703 | {
704 | $this->setEncodedCookie($key, $value);
705 | $this->buildCookies();
706 | }
707 |
708 | /**
709 | * Set Cookies
710 | *
711 | * @access public
712 | * @param $cookies
713 | */
714 | public function setCookies($cookies)
715 | {
716 | foreach ($cookies as $key => $value) {
717 | $this->setEncodedCookie($key, $value);
718 | }
719 | $this->buildCookies();
720 | }
721 |
722 | /**
723 | * Get Cookie
724 | *
725 | * @access public
726 | * @param $key
727 | *
728 | * @return mixed
729 | */
730 | public function getCookie($key)
731 | {
732 | return $this->getResponseCookie($key);
733 | }
734 |
735 | /**
736 | * Get Response Cookie
737 | *
738 | * @access public
739 | * @param $key
740 | *
741 | * @return mixed
742 | */
743 | public function getResponseCookie($key)
744 | {
745 | return isset($this->responseCookies[$key]) ? $this->responseCookies[$key] : null;
746 | }
747 |
748 | /**
749 | * Get Response Cookies
750 | *
751 | * @access public
752 | *
753 | * @return array
754 | */
755 | public function getResponseCookies()
756 | {
757 | return $this->responseCookies;
758 | }
759 |
760 | /**
761 | * Set Max Filesize
762 | *
763 | * @access public
764 | * @param $bytes
765 | */
766 | public function setMaxFilesize($bytes)
767 | {
768 | // Make compatible with PHP version both before and after 5.5.0. PHP 5.5.0 added the cURL resource as the first
769 | // argument to the CURLOPT_PROGRESSFUNCTION callback.
770 | $gte_v550 = version_compare(PHP_VERSION, '5.5.0') >= 0;
771 | if ($gte_v550) {
772 | $callback = function ($resource, $download_size, $downloaded, $upload_size, $uploaded) use ($bytes) {
773 | // Abort the transfer when $downloaded bytes exceeds maximum $bytes by returning a non-zero value.
774 | return $downloaded > $bytes ? 1 : 0;
775 | };
776 | } else {
777 | $callback = function ($download_size, $downloaded, $upload_size, $uploaded) use ($bytes) {
778 | return $downloaded > $bytes ? 1 : 0;
779 | };
780 | }
781 |
782 | $this->progress($callback);
783 | }
784 |
785 | /**
786 | * Set Port
787 | *
788 | * @access public
789 | * @param $port
790 | */
791 | public function setPort($port)
792 | {
793 | $this->setOpt(CURLOPT_PORT, intval($port));
794 | }
795 |
796 | /**
797 | * Set Connect Timeout
798 | *
799 | * @access public
800 | * @param $seconds
801 | */
802 | public function setConnectTimeout($seconds)
803 | {
804 | $this->setOpt(CURLOPT_CONNECTTIMEOUT, $seconds);
805 | }
806 |
807 | /**
808 | * Set Cookie String
809 | *
810 | * @access public
811 | * @param $string
812 | *
813 | * @return bool
814 | */
815 | public function setCookieString($string)
816 | {
817 | return $this->setOpt(CURLOPT_COOKIE, $string);
818 | }
819 |
820 | /**
821 | * Set Cookie File
822 | *
823 | * @access public
824 | * @param $cookie_file
825 | *
826 | * @return boolean
827 | */
828 | public function setCookieFile($cookie_file)
829 | {
830 | return $this->setOpt(CURLOPT_COOKIEFILE, $cookie_file);
831 | }
832 |
833 | /**
834 | * Set Cookie Jar
835 | *
836 | * @access public
837 | * @param $cookie_jar
838 | *
839 | * @return boolean
840 | */
841 | public function setCookieJar($cookie_jar)
842 | {
843 | return $this->setOpt(CURLOPT_COOKIEJAR, $cookie_jar);
844 | }
845 |
846 | /**
847 | * Set Default JSON Decoder
848 | *
849 | * @access public
850 | * @param $assoc
851 | * @param $depth
852 | * @param $options
853 | */
854 | public function setDefaultJsonDecoder()
855 | {
856 | $this->jsonDecoder = '\Curl\Decoder::decodeJson';
857 | $this->jsonDecoderArgs = func_get_args();
858 | }
859 |
860 | /**
861 | * Set Default XML Decoder
862 | *
863 | * @access public
864 | */
865 | public function setDefaultXmlDecoder()
866 | {
867 | $this->xmlDecoder = '\Curl\Decoder::decodeXml';
868 | }
869 |
870 | /**
871 | * Set Default Decoder
872 | *
873 | * @access public
874 | * @param $mixed boolean|callable|string
875 | */
876 | public function setDefaultDecoder($mixed = 'json')
877 | {
878 | if ($mixed === false) {
879 | $this->defaultDecoder = false;
880 | } elseif (is_callable($mixed)) {
881 | $this->defaultDecoder = $mixed;
882 | } else {
883 | if ($mixed === 'json') {
884 | $this->defaultDecoder = $this->jsonDecoder;
885 | } elseif ($mixed === 'xml') {
886 | $this->defaultDecoder = $this->xmlDecoder;
887 | }
888 | }
889 | }
890 |
891 | /**
892 | * Set Default Timeout
893 | *
894 | * @access public
895 | */
896 | public function setDefaultTimeout()
897 | {
898 | $this->setTimeout(self::DEFAULT_TIMEOUT);
899 | }
900 |
901 | /**
902 | * Set Default User Agent
903 | *
904 | * @access public
905 | */
906 | public function setDefaultUserAgent()
907 | {
908 | $user_agent = 'PHP-Curl-Class/' . self::VERSION . ' (+https://github.com/php-curl-class/php-curl-class)';
909 | $user_agent .= ' PHP/' . PHP_VERSION;
910 | $curl_version = curl_version();
911 | $user_agent .= ' curl/' . $curl_version['version'];
912 | $this->setUserAgent($user_agent);
913 | }
914 |
915 | /**
916 | * Set Header
917 | *
918 | * Add extra header to include in the request.
919 | *
920 | * @access public
921 | * @param $key
922 | * @param $value
923 | */
924 | public function setHeader($key, $value)
925 | {
926 | $this->headers[$key] = $value;
927 | $headers = array();
928 | foreach ($this->headers as $key => $value) {
929 | $headers[] = $key . ': ' . $value;
930 | }
931 | $this->setOpt(CURLOPT_HTTPHEADER, $headers);
932 | }
933 |
934 | /**
935 | * Set Headers
936 | *
937 | * Add extra headers to include in the request.
938 | *
939 | * @access public
940 | * @param $headers
941 | */
942 | public function setHeaders($headers)
943 | {
944 | foreach ($headers as $key => $value) {
945 | $this->headers[$key] = $value;
946 | }
947 |
948 | $headers = array();
949 | foreach ($this->headers as $key => $value) {
950 | $headers[] = $key . ': ' . $value;
951 | }
952 | $this->setOpt(CURLOPT_HTTPHEADER, $headers);
953 | }
954 |
955 | /**
956 | * Set JSON Decoder
957 | *
958 | * @access public
959 | * @param $mixed boolean|callable
960 | */
961 | public function setJsonDecoder($mixed)
962 | {
963 | if ($mixed === false) {
964 | $this->jsonDecoder = false;
965 | $this->jsonDecoderArgs = array();
966 | } elseif (is_callable($mixed)) {
967 | $this->jsonDecoder = $mixed;
968 | $this->jsonDecoderArgs = array();
969 | }
970 | }
971 |
972 | /**
973 | * Set XML Decoder
974 | *
975 | * @access public
976 | * @param $mixed boolean|callable
977 | */
978 | public function setXmlDecoder($mixed)
979 | {
980 | if ($mixed === false) {
981 | $this->xmlDecoder = false;
982 | } elseif (is_callable($mixed)) {
983 | $this->xmlDecoder = $mixed;
984 | }
985 | }
986 |
987 | /**
988 | * Set Opt
989 | *
990 | * @access public
991 | * @param $option
992 | * @param $value
993 | *
994 | * @return boolean
995 | */
996 | public function setOpt($option, $value)
997 | {
998 | $required_options = array(
999 | CURLOPT_RETURNTRANSFER => 'CURLOPT_RETURNTRANSFER',
1000 | );
1001 |
1002 | if (in_array($option, array_keys($required_options), true) && !($value === true)) {
1003 | trigger_error($required_options[$option] . ' is a required option', E_USER_WARNING);
1004 | }
1005 |
1006 | $success = curl_setopt($this->curl, $option, $value);
1007 | if ($success) {
1008 | $this->options[$option] = $value;
1009 | }
1010 | return $success;
1011 | }
1012 |
1013 | /**
1014 | * Set Opts
1015 | *
1016 | * @access public
1017 | * @param $options
1018 | *
1019 | * @return boolean
1020 | * Returns true if all options were successfully set. If an option could not be successfully set, false is
1021 | * immediately returned, ignoring any future options in the options array. Similar to curl_setopt_array().
1022 | */
1023 | public function setOpts($options)
1024 | {
1025 | foreach ($options as $option => $value) {
1026 | if (!$this->setOpt($option, $value)) {
1027 | return false;
1028 | }
1029 | }
1030 | return true;
1031 | }
1032 |
1033 | /**
1034 | * Set Referer
1035 | *
1036 | * @access public
1037 | * @param $referer
1038 | */
1039 | public function setReferer($referer)
1040 | {
1041 | $this->setReferrer($referer);
1042 | }
1043 |
1044 | /**
1045 | * Set Referrer
1046 | *
1047 | * @access public
1048 | * @param $referrer
1049 | */
1050 | public function setReferrer($referrer)
1051 | {
1052 | $this->setOpt(CURLOPT_REFERER, $referrer);
1053 | }
1054 |
1055 | /**
1056 | * Set Retry
1057 | *
1058 | * Number of retries to attempt or decider callable. Maximum number of
1059 | * attempts is $maximum_number_of_retries + 1.
1060 | *
1061 | * @access public
1062 | * @param $mixed
1063 | */
1064 | public function setRetry($mixed)
1065 | {
1066 | if (is_callable($mixed)) {
1067 | $this->retryDecider = $mixed;
1068 | } elseif (is_int($mixed)) {
1069 | $maximum_number_of_retries = $mixed;
1070 | $this->remainingRetries = $maximum_number_of_retries;
1071 | }
1072 | }
1073 |
1074 | /**
1075 | * Set Timeout
1076 | *
1077 | * @access public
1078 | * @param $seconds
1079 | */
1080 | public function setTimeout($seconds)
1081 | {
1082 | $this->setOpt(CURLOPT_TIMEOUT, $seconds);
1083 | }
1084 |
1085 | /**
1086 | * Set Url
1087 | *
1088 | * @access public
1089 | * @param $url
1090 | * @param $mixed_data
1091 | */
1092 | public function setUrl($url, $mixed_data = '')
1093 | {
1094 | $built_url = $this->buildUrl($url, $mixed_data);
1095 |
1096 | if ($this->url === null) {
1097 | $this->url = (string)new Url($built_url);
1098 | } else {
1099 | $this->url = (string)new Url($this->url, $built_url);
1100 | }
1101 |
1102 | $this->setOpt(CURLOPT_URL, $this->url);
1103 | }
1104 |
1105 | /**
1106 | * Set User Agent
1107 | *
1108 | * @access public
1109 | * @param $user_agent
1110 | */
1111 | public function setUserAgent($user_agent)
1112 | {
1113 | $this->setOpt(CURLOPT_USERAGENT, $user_agent);
1114 | }
1115 |
1116 | /**
1117 | * Attempt Retry
1118 | *
1119 | * @access public
1120 | */
1121 | public function attemptRetry()
1122 | {
1123 | $attempt_retry = false;
1124 | if ($this->error) {
1125 | if ($this->retryDecider === null) {
1126 | $attempt_retry = $this->remainingRetries >= 1;
1127 | } else {
1128 | $attempt_retry = call_user_func($this->retryDecider, $this);
1129 | }
1130 | if ($attempt_retry) {
1131 | $this->retries += 1;
1132 | if ($this->remainingRetries) {
1133 | $this->remainingRetries -= 1;
1134 | }
1135 | }
1136 | }
1137 | return $attempt_retry;
1138 | }
1139 |
1140 | /**
1141 | * Success
1142 | *
1143 | * @access public
1144 | * @param $callback
1145 | */
1146 | public function success($callback)
1147 | {
1148 | $this->successFunction = $callback;
1149 | }
1150 |
1151 | /**
1152 | * Unset Header
1153 | *
1154 | * Remove extra header previously set using Curl::setHeader().
1155 | *
1156 | * @access public
1157 | * @param $key
1158 | */
1159 | public function unsetHeader($key)
1160 | {
1161 | unset($this->headers[$key]);
1162 | $headers = array();
1163 | foreach ($this->headers as $key => $value) {
1164 | $headers[] = $key . ': ' . $value;
1165 | }
1166 | $this->setOpt(CURLOPT_HTTPHEADER, $headers);
1167 | }
1168 |
1169 | /**
1170 | * Remove Header
1171 | *
1172 | * Remove an internal header from the request.
1173 | * Using `curl -H "Host:" ...' is equivalent to $curl->removeHeader('Host');.
1174 | *
1175 | * @access public
1176 | * @param $key
1177 | */
1178 | public function removeHeader($key)
1179 | {
1180 | $this->setHeader($key, '');
1181 | }
1182 |
1183 | /**
1184 | * Verbose
1185 | *
1186 | * @access public
1187 | * @param bool $on
1188 | * @param resource $output
1189 | */
1190 | public function verbose($on = true, $output = STDERR)
1191 | {
1192 | // Turn off CURLINFO_HEADER_OUT for verbose to work. This has the side
1193 | // effect of causing Curl::requestHeaders to be empty.
1194 | if ($on) {
1195 | $this->setOpt(CURLINFO_HEADER_OUT, false);
1196 | }
1197 | $this->setOpt(CURLOPT_VERBOSE, $on);
1198 | $this->setOpt(CURLOPT_STDERR, $output);
1199 | }
1200 |
1201 | /**
1202 | * Destruct
1203 | *
1204 | * @access public
1205 | */
1206 | public function __destruct()
1207 | {
1208 | $this->close();
1209 | }
1210 |
1211 | public function __get($name)
1212 | {
1213 | $return = null;
1214 | if (in_array($name, self::$deferredProperties) && is_callable(array($this, $getter = '__get_' . $name))) {
1215 | $return = $this->$name = $this->$getter();
1216 | }
1217 | return $return;
1218 | }
1219 |
1220 | /**
1221 | * Get Effective Url
1222 | *
1223 | * @access private
1224 | */
1225 | private function __get_effectiveUrl()
1226 | {
1227 | return $this->getInfo(CURLINFO_EFFECTIVE_URL);
1228 | }
1229 |
1230 | /**
1231 | * Get RFC 2616
1232 | *
1233 | * @access private
1234 | */
1235 | private function __get_rfc2616()
1236 | {
1237 | return array_fill_keys(self::$RFC2616, true);
1238 | }
1239 |
1240 | /**
1241 | * Get RFC 6265
1242 | *
1243 | * @access private
1244 | */
1245 | private function __get_rfc6265()
1246 | {
1247 | return array_fill_keys(self::$RFC6265, true);
1248 | }
1249 |
1250 | /**
1251 | * Get Total Time
1252 | *
1253 | * @access private
1254 | */
1255 | private function __get_totalTime()
1256 | {
1257 | return $this->getInfo(CURLINFO_TOTAL_TIME);
1258 | }
1259 |
1260 | /**
1261 | * Build Cookies
1262 | *
1263 | * @access private
1264 | */
1265 | private function buildCookies()
1266 | {
1267 | // Avoid using http_build_query() as unnecessary encoding is performed.
1268 | // http_build_query($this->cookies, '', '; ');
1269 | $this->setOpt(CURLOPT_COOKIE, implode('; ', array_map(function ($k, $v) {
1270 | return $k . '=' . $v;
1271 | }, array_keys($this->cookies), array_values($this->cookies))));
1272 | }
1273 |
1274 | /**
1275 | * Build Url
1276 | *
1277 | * @access private
1278 | * @param $url
1279 | * @param $mixed_data
1280 | *
1281 | * @return string
1282 | */
1283 | private function buildUrl($url, $mixed_data = '')
1284 | {
1285 | $query_string = '';
1286 | if (!empty($mixed_data)) {
1287 | $query_mark = strpos($url, '?') > 0 ? '&' : '?';
1288 | if (is_string($mixed_data)) {
1289 | $query_string .= $query_mark . $mixed_data;
1290 | } elseif (is_array($mixed_data)) {
1291 | $query_string .= $query_mark . http_build_query($mixed_data, '', '&');
1292 | }
1293 | }
1294 | return $url . $query_string;
1295 | }
1296 |
1297 | /**
1298 | * Create Header Callback
1299 | *
1300 | * @access private
1301 | * @param $header_callback_data
1302 | *
1303 | * @return callable
1304 | */
1305 | private function createHeaderCallback($header_callback_data)
1306 | {
1307 | return function ($ch, $header) use ($header_callback_data) {
1308 | if (preg_match('/^Set-Cookie:\s*([^=]+)=([^;]+)/mi', $header, $cookie) === 1) {
1309 | $header_callback_data->responseCookies[$cookie[1]] = trim($cookie[2], " \n\r\t\0\x0B");
1310 | }
1311 | $header_callback_data->rawResponseHeaders .= $header;
1312 | return strlen($header);
1313 | };
1314 | }
1315 |
1316 | /**
1317 | * Download Complete
1318 | *
1319 | * @access private
1320 | * @param $fh
1321 | */
1322 | private function downloadComplete($fh)
1323 | {
1324 | if (!$this->error && $this->downloadCompleteFunction) {
1325 | rewind($fh);
1326 | $this->call($this->downloadCompleteFunction, $fh);
1327 | $this->downloadCompleteFunction = null;
1328 | }
1329 |
1330 | if (is_resource($fh)) {
1331 | fclose($fh);
1332 | }
1333 |
1334 | // Fix "PHP Notice: Use of undefined constant STDOUT" when reading the
1335 | // PHP script from stdin. Using null causes "Warning: curl_setopt():
1336 | // supplied argument is not a valid File-Handle resource".
1337 | if (!defined('STDOUT')) {
1338 | define('STDOUT', fopen('php://stdout', 'w'));
1339 | }
1340 |
1341 | // Reset CURLOPT_FILE with STDOUT to avoid: "curl_exec(): CURLOPT_FILE
1342 | // resource has gone away, resetting to default".
1343 | $this->setOpt(CURLOPT_FILE, STDOUT);
1344 |
1345 | // Reset CURLOPT_RETURNTRANSFER to tell cURL to return subsequent
1346 | // responses as the return value of curl_exec(). Without this,
1347 | // curl_exec() will revert to returning boolean values.
1348 | $this->setOpt(CURLOPT_RETURNTRANSFER, true);
1349 | }
1350 |
1351 | /**
1352 | * Parse Headers
1353 | *
1354 | * @access private
1355 | * @param $raw_headers
1356 | *
1357 | * @return array
1358 | */
1359 | private function parseHeaders($raw_headers)
1360 | {
1361 | $raw_headers = preg_split('/\r\n/', $raw_headers, null, PREG_SPLIT_NO_EMPTY);
1362 | $http_headers = new CaseInsensitiveArray();
1363 |
1364 | $raw_headers_count = count($raw_headers);
1365 | for ($i = 1; $i < $raw_headers_count; $i++) {
1366 | list($key, $value) = explode(':', $raw_headers[$i], 2);
1367 | $key = trim($key);
1368 | $value = trim($value);
1369 | // Use isset() as array_key_exists() and ArrayAccess are not compatible.
1370 | if (isset($http_headers[$key])) {
1371 | $http_headers[$key] .= ',' . $value;
1372 | } else {
1373 | $http_headers[$key] = $value;
1374 | }
1375 | }
1376 |
1377 | return array(isset($raw_headers['0']) ? $raw_headers['0'] : '', $http_headers);
1378 | }
1379 |
1380 | /**
1381 | * Parse Request Headers
1382 | *
1383 | * @access private
1384 | * @param $raw_headers
1385 | *
1386 | * @return \Curl\CaseInsensitiveArray
1387 | */
1388 | private function parseRequestHeaders($raw_headers)
1389 | {
1390 | $request_headers = new CaseInsensitiveArray();
1391 | list($first_line, $headers) = $this->parseHeaders($raw_headers);
1392 | $request_headers['Request-Line'] = $first_line;
1393 | foreach ($headers as $key => $value) {
1394 | $request_headers[$key] = $value;
1395 | }
1396 | return $request_headers;
1397 | }
1398 |
1399 | /**
1400 | * Parse Response
1401 | *
1402 | * @access private
1403 | * @param $response_headers
1404 | * @param $raw_response
1405 | *
1406 | * @return mixed
1407 | * Provided the content-type is determined to be json or xml:
1408 | * Returns stdClass object when the default json decoder is used and the content-type is json.
1409 | * Returns SimpleXMLElement object when the default xml decoder is used and the content-type is xml.
1410 | */
1411 | private function parseResponse($response_headers, $raw_response)
1412 | {
1413 | $response = $raw_response;
1414 | if (isset($response_headers['Content-Type'])) {
1415 | if (preg_match($this->jsonPattern, $response_headers['Content-Type'])) {
1416 | if ($this->jsonDecoder) {
1417 | $args = $this->jsonDecoderArgs;
1418 | array_unshift($args, $response);
1419 | $response = call_user_func_array($this->jsonDecoder, $args);
1420 | }
1421 | } elseif (preg_match($this->xmlPattern, $response_headers['Content-Type'])) {
1422 | if ($this->xmlDecoder) {
1423 | $response = call_user_func($this->xmlDecoder, $response);
1424 | }
1425 | } else {
1426 | if ($this->defaultDecoder) {
1427 | $response = call_user_func($this->defaultDecoder, $response);
1428 | }
1429 | }
1430 | }
1431 |
1432 | return $response;
1433 | }
1434 |
1435 | /**
1436 | * Parse Response Headers
1437 | *
1438 | * @access private
1439 | * @param $raw_response_headers
1440 | *
1441 | * @return \Curl\CaseInsensitiveArray
1442 | */
1443 | private function parseResponseHeaders($raw_response_headers)
1444 | {
1445 | $response_header_array = explode("\r\n\r\n", $raw_response_headers);
1446 | $response_header = '';
1447 | for ($i = count($response_header_array) - 1; $i >= 0; $i--) {
1448 | if (stripos($response_header_array[$i], 'HTTP/') === 0) {
1449 | $response_header = $response_header_array[$i];
1450 | break;
1451 | }
1452 | }
1453 |
1454 | $response_headers = new CaseInsensitiveArray();
1455 | list($first_line, $headers) = $this->parseHeaders($response_header);
1456 | $response_headers['Status-Line'] = $first_line;
1457 | foreach ($headers as $key => $value) {
1458 | $response_headers[$key] = $value;
1459 | }
1460 | return $response_headers;
1461 | }
1462 |
1463 | /**
1464 | * Set Encoded Cookie
1465 | *
1466 | * @access private
1467 | * @param $key
1468 | * @param $value
1469 | */
1470 | private function setEncodedCookie($key, $value)
1471 | {
1472 | $name_chars = array();
1473 | foreach (str_split($key) as $name_char) {
1474 | if (isset($this->rfc2616[$name_char])) {
1475 | $name_chars[] = $name_char;
1476 | } else {
1477 | $name_chars[] = rawurlencode($name_char);
1478 | }
1479 | }
1480 |
1481 | $value_chars = array();
1482 | foreach (str_split($value) as $value_char) {
1483 | if (isset($this->rfc6265[$value_char])) {
1484 | $value_chars[] = $value_char;
1485 | } else {
1486 | $value_chars[] = rawurlencode($value_char);
1487 | }
1488 | }
1489 |
1490 | $this->cookies[implode('', $name_chars)] = implode('', $value_chars);
1491 | }
1492 | }
1493 |
--------------------------------------------------------------------------------