diff --git a/.vscode/launch.json b/.vscode/launch.json
new file mode 100644
index 00000000..ae0aa1f7
--- /dev/null
+++ b/.vscode/launch.json
@@ -0,0 +1,22 @@
+{
+ // 使用 IntelliSense 了解相关属性。
+ // 悬停以查看现有属性的描述。
+ // 欲了解更多信息,请访问: https://go.microsoft.com/fwlink/?linkid=830387
+ "version": "0.2.0",
+ "configurations": [
+ {
+ "name": "Listen for XDebug",
+ "type": "php",
+ "request": "launch",
+ "port": 9000
+ },
+ {
+ "name": "Launch currently open script",
+ "type": "php",
+ "request": "launch",
+ "program": "${file}",
+ "cwd": "${fileDirname}",
+ "port": 9000
+ }
+ ]
+}
\ No newline at end of file
diff --git a/application/common/model/Images.php b/application/common/model/Images.php
index b583f13a..c20d13fd 100644
--- a/application/common/model/Images.php
+++ b/application/common/model/Images.php
@@ -36,4 +36,4 @@ public function getDateAttr($date, $data)
{
return format_time($data['create_time']);
}
-}
\ No newline at end of file
+}
diff --git a/composer.json b/composer.json
index bdaf585b..9a968134 100644
--- a/composer.json
+++ b/composer.json
@@ -23,7 +23,9 @@
"upyun/sdk": "^3.3",
"qcloud/cos-sdk-v5": "^1.2",
"topthink/think-image": "^1.0",
- "phpmailer/phpmailer": "^6.0"
+ "phpmailer/phpmailer": "^6.0",
+ "knplabs/github-api": "^2.10",
+ "php-http/guzzle6-adapter": "^1.1"
},
"autoload": {
"psr-4": {
diff --git a/composer.lock b/composer.lock
index b0defeca..6748f400 100644
--- a/composer.lock
+++ b/composer.lock
@@ -4,7 +4,7 @@
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
"This file is @generated automatically"
],
- "content-hash": "f3a07db17ca2f758f19303939f3bb3d6",
+ "content-hash": "8dc954d23d41815720bd345b1a0f6613",
"packages": [
{
"name": "aliyuncs/oss-sdk-php",
@@ -47,6 +47,58 @@
"homepage": "http://www.aliyun.com/product/oss/",
"time": "2018-01-08T06:59:35+00:00"
},
+ {
+ "name": "clue/stream-filter",
+ "version": "v1.4.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/clue/php-stream-filter.git",
+ "reference": "d80fdee9b3a7e0d16fc330a22f41f3ad0eeb09d0"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/clue/php-stream-filter/zipball/d80fdee9b3a7e0d16fc330a22f41f3ad0eeb09d0",
+ "reference": "d80fdee9b3a7e0d16fc330a22f41f3ad0eeb09d0",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=5.3"
+ },
+ "require-dev": {
+ "phpunit/phpunit": "^5.0 || ^4.8"
+ },
+ "type": "library",
+ "autoload": {
+ "psr-4": {
+ "Clue\\StreamFilter\\": "src/"
+ },
+ "files": [
+ "src/functions.php"
+ ]
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Christian Lück",
+ "email": "christian@lueck.tv"
+ }
+ ],
+ "description": "A simple and modern approach to stream filtering in PHP",
+ "homepage": "https://github.com/clue/php-stream-filter",
+ "keywords": [
+ "bucket brigade",
+ "callback",
+ "filter",
+ "php_user_filter",
+ "stream",
+ "stream_filter_append",
+ "stream_filter_register"
+ ],
+ "time": "2017-08-18T09:54:01+00:00"
+ },
{
"name": "guzzle/guzzle",
"version": "v3.9.3",
@@ -326,6 +378,543 @@
],
"time": "2018-12-04T20:46:45+00:00"
},
+ {
+ "name": "knplabs/github-api",
+ "version": "2.10.1",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/KnpLabs/php-github-api.git",
+ "reference": "493423ae7ad1fa9075924cdfb98537828b9e80b5"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/KnpLabs/php-github-api/zipball/493423ae7ad1fa9075924cdfb98537828b9e80b5",
+ "reference": "493423ae7ad1fa9075924cdfb98537828b9e80b5",
+ "shasum": ""
+ },
+ "require": {
+ "php": "^5.6 || ^7.0",
+ "php-http/cache-plugin": "^1.4",
+ "php-http/client-common": "^1.6",
+ "php-http/client-implementation": "^1.0",
+ "php-http/discovery": "^1.0",
+ "php-http/httplug": "^1.1",
+ "psr/cache": "^1.0",
+ "psr/http-message": "^1.0"
+ },
+ "require-dev": {
+ "cache/array-adapter": "^0.4",
+ "guzzlehttp/psr7": "^1.2",
+ "php-http/guzzle6-adapter": "^1.0",
+ "php-http/mock-client": "^1.0",
+ "phpunit/phpunit": "^5.5 || ^6.0"
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-master": "2.10.x-dev"
+ }
+ },
+ "autoload": {
+ "psr-4": {
+ "Github\\": "lib/Github/"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Thibault Duplessis",
+ "email": "thibault.duplessis@gmail.com",
+ "homepage": "http://ornicar.github.com"
+ },
+ {
+ "name": "KnpLabs Team",
+ "homepage": "http://knplabs.com"
+ }
+ ],
+ "description": "GitHub API v3 client",
+ "homepage": "https://github.com/KnpLabs/php-github-api",
+ "keywords": [
+ "api",
+ "gh",
+ "gist",
+ "github"
+ ],
+ "time": "2018-09-05T19:12:14+00:00"
+ },
+ {
+ "name": "php-http/cache-plugin",
+ "version": "v1.5.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/php-http/cache-plugin.git",
+ "reference": "c573ac6ea9b4e33fad567f875b844229d18000b9"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/php-http/cache-plugin/zipball/c573ac6ea9b4e33fad567f875b844229d18000b9",
+ "reference": "c573ac6ea9b4e33fad567f875b844229d18000b9",
+ "shasum": ""
+ },
+ "require": {
+ "php": "^5.4 || ^7.0",
+ "php-http/client-common": "^1.1",
+ "php-http/message-factory": "^1.0",
+ "psr/cache": "^1.0",
+ "symfony/options-resolver": "^2.6 || ^3.0 || ^4.0"
+ },
+ "require-dev": {
+ "henrikbjorn/phpspec-code-coverage": "^1.0",
+ "phpspec/phpspec": "^2.5"
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-master": "1.5-dev"
+ }
+ },
+ "autoload": {
+ "psr-4": {
+ "Http\\Client\\Common\\Plugin\\": "src/"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Márk Sági-Kazár",
+ "email": "mark.sagikazar@gmail.com"
+ }
+ ],
+ "description": "PSR-6 Cache plugin for HTTPlug",
+ "homepage": "http://httplug.io",
+ "keywords": [
+ "cache",
+ "http",
+ "httplug",
+ "plugin"
+ ],
+ "time": "2017-11-29T20:45:41+00:00"
+ },
+ {
+ "name": "php-http/client-common",
+ "version": "1.9.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/php-http/client-common.git",
+ "reference": "9c21b6058caafdf2fcc99a0cabdf31b3ecb33961"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/php-http/client-common/zipball/9c21b6058caafdf2fcc99a0cabdf31b3ecb33961",
+ "reference": "9c21b6058caafdf2fcc99a0cabdf31b3ecb33961",
+ "shasum": ""
+ },
+ "require": {
+ "php": "^5.4 || ^7.0",
+ "php-http/httplug": "^1.1",
+ "php-http/message": "^1.6",
+ "php-http/message-factory": "^1.0",
+ "symfony/options-resolver": "^2.6 || ^3.0 || ^4.0"
+ },
+ "require-dev": {
+ "guzzlehttp/psr7": "^1.4",
+ "phpspec/phpspec": "^2.5 || ^3.4 || ^4.2"
+ },
+ "suggest": {
+ "php-http/cache-plugin": "PSR-6 Cache plugin",
+ "php-http/logger-plugin": "PSR-3 Logger plugin",
+ "php-http/stopwatch-plugin": "Symfony Stopwatch plugin"
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-master": "1.9.x-dev"
+ }
+ },
+ "autoload": {
+ "psr-4": {
+ "Http\\Client\\Common\\": "src/"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Márk Sági-Kazár",
+ "email": "mark.sagikazar@gmail.com"
+ }
+ ],
+ "description": "Common HTTP Client implementations and tools for HTTPlug",
+ "homepage": "http://httplug.io",
+ "keywords": [
+ "client",
+ "common",
+ "http",
+ "httplug"
+ ],
+ "time": "2019-01-03T10:59:55+00:00"
+ },
+ {
+ "name": "php-http/discovery",
+ "version": "1.5.2",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/php-http/discovery.git",
+ "reference": "ffef11d54171336d841a34816a35bc035fb8cef0"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/php-http/discovery/zipball/ffef11d54171336d841a34816a35bc035fb8cef0",
+ "reference": "ffef11d54171336d841a34816a35bc035fb8cef0",
+ "shasum": ""
+ },
+ "require": {
+ "php": "^5.5 || ^7.0"
+ },
+ "conflict": {
+ "nyholm/psr7": "<1.0"
+ },
+ "require-dev": {
+ "henrikbjorn/phpspec-code-coverage": "^2.0.2",
+ "php-http/httplug": "^1.0|^2.0",
+ "php-http/message-factory": "^1.0",
+ "phpspec/phpspec": "^2.4",
+ "puli/composer-plugin": "1.0.0-beta10"
+ },
+ "suggest": {
+ "php-http/message": "Allow to use Guzzle, Diactoros or Slim Framework factories",
+ "puli/composer-plugin": "Sets up Puli which is recommended for Discovery to work. Check http://docs.php-http.org/en/latest/discovery.html for more details."
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-master": "1.5-dev"
+ }
+ },
+ "autoload": {
+ "psr-4": {
+ "Http\\Discovery\\": "src/"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Márk Sági-Kazár",
+ "email": "mark.sagikazar@gmail.com"
+ }
+ ],
+ "description": "Finds installed HTTPlug implementations and PSR-7 message factories",
+ "homepage": "http://php-http.org",
+ "keywords": [
+ "adapter",
+ "client",
+ "discovery",
+ "factory",
+ "http",
+ "message",
+ "psr7"
+ ],
+ "time": "2018-12-31T07:31:26+00:00"
+ },
+ {
+ "name": "php-http/guzzle6-adapter",
+ "version": "v1.1.1",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/php-http/guzzle6-adapter.git",
+ "reference": "a56941f9dc6110409cfcddc91546ee97039277ab"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/php-http/guzzle6-adapter/zipball/a56941f9dc6110409cfcddc91546ee97039277ab",
+ "reference": "a56941f9dc6110409cfcddc91546ee97039277ab",
+ "shasum": ""
+ },
+ "require": {
+ "guzzlehttp/guzzle": "^6.0",
+ "php": ">=5.5.0",
+ "php-http/httplug": "^1.0"
+ },
+ "provide": {
+ "php-http/async-client-implementation": "1.0",
+ "php-http/client-implementation": "1.0"
+ },
+ "require-dev": {
+ "ext-curl": "*",
+ "php-http/adapter-integration-tests": "^0.4"
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-master": "1.2-dev"
+ }
+ },
+ "autoload": {
+ "psr-4": {
+ "Http\\Adapter\\Guzzle6\\": "src/"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Márk Sági-Kazár",
+ "email": "mark.sagikazar@gmail.com"
+ },
+ {
+ "name": "David de Boer",
+ "email": "david@ddeboer.nl"
+ }
+ ],
+ "description": "Guzzle 6 HTTP Adapter",
+ "homepage": "http://httplug.io",
+ "keywords": [
+ "Guzzle",
+ "http"
+ ],
+ "time": "2016-05-10T06:13:32+00:00"
+ },
+ {
+ "name": "php-http/httplug",
+ "version": "v1.1.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/php-http/httplug.git",
+ "reference": "1c6381726c18579c4ca2ef1ec1498fdae8bdf018"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/php-http/httplug/zipball/1c6381726c18579c4ca2ef1ec1498fdae8bdf018",
+ "reference": "1c6381726c18579c4ca2ef1ec1498fdae8bdf018",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=5.4",
+ "php-http/promise": "^1.0",
+ "psr/http-message": "^1.0"
+ },
+ "require-dev": {
+ "henrikbjorn/phpspec-code-coverage": "^1.0",
+ "phpspec/phpspec": "^2.4"
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-master": "1.1-dev"
+ }
+ },
+ "autoload": {
+ "psr-4": {
+ "Http\\Client\\": "src/"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Eric GELOEN",
+ "email": "geloen.eric@gmail.com"
+ },
+ {
+ "name": "Márk Sági-Kazár",
+ "email": "mark.sagikazar@gmail.com"
+ }
+ ],
+ "description": "HTTPlug, the HTTP client abstraction for PHP",
+ "homepage": "http://httplug.io",
+ "keywords": [
+ "client",
+ "http"
+ ],
+ "time": "2016-08-31T08:30:17+00:00"
+ },
+ {
+ "name": "php-http/message",
+ "version": "1.7.2",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/php-http/message.git",
+ "reference": "b159ffe570dffd335e22ef0b91a946eacb182fa1"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/php-http/message/zipball/b159ffe570dffd335e22ef0b91a946eacb182fa1",
+ "reference": "b159ffe570dffd335e22ef0b91a946eacb182fa1",
+ "shasum": ""
+ },
+ "require": {
+ "clue/stream-filter": "^1.4",
+ "php": "^5.4 || ^7.0",
+ "php-http/message-factory": "^1.0.2",
+ "psr/http-message": "^1.0"
+ },
+ "provide": {
+ "php-http/message-factory-implementation": "1.0"
+ },
+ "require-dev": {
+ "akeneo/phpspec-skip-example-extension": "^1.0",
+ "coduo/phpspec-data-provider-extension": "^1.0",
+ "ext-zlib": "*",
+ "guzzlehttp/psr7": "^1.0",
+ "henrikbjorn/phpspec-code-coverage": "^1.0",
+ "phpspec/phpspec": "^2.4",
+ "slim/slim": "^3.0",
+ "zendframework/zend-diactoros": "^1.0"
+ },
+ "suggest": {
+ "ext-zlib": "Used with compressor/decompressor streams",
+ "guzzlehttp/psr7": "Used with Guzzle PSR-7 Factories",
+ "slim/slim": "Used with Slim Framework PSR-7 implementation",
+ "zendframework/zend-diactoros": "Used with Diactoros Factories"
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-master": "1.6-dev"
+ }
+ },
+ "autoload": {
+ "psr-4": {
+ "Http\\Message\\": "src/"
+ },
+ "files": [
+ "src/filters.php"
+ ]
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Márk Sági-Kazár",
+ "email": "mark.sagikazar@gmail.com"
+ }
+ ],
+ "description": "HTTP Message related tools",
+ "homepage": "http://php-http.org",
+ "keywords": [
+ "http",
+ "message",
+ "psr-7"
+ ],
+ "time": "2018-11-01T09:32:41+00:00"
+ },
+ {
+ "name": "php-http/message-factory",
+ "version": "v1.0.2",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/php-http/message-factory.git",
+ "reference": "a478cb11f66a6ac48d8954216cfed9aa06a501a1"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/php-http/message-factory/zipball/a478cb11f66a6ac48d8954216cfed9aa06a501a1",
+ "reference": "a478cb11f66a6ac48d8954216cfed9aa06a501a1",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=5.4",
+ "psr/http-message": "^1.0"
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-master": "1.0-dev"
+ }
+ },
+ "autoload": {
+ "psr-4": {
+ "Http\\Message\\": "src/"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Márk Sági-Kazár",
+ "email": "mark.sagikazar@gmail.com"
+ }
+ ],
+ "description": "Factory interfaces for PSR-7 HTTP Message",
+ "homepage": "http://php-http.org",
+ "keywords": [
+ "factory",
+ "http",
+ "message",
+ "stream",
+ "uri"
+ ],
+ "time": "2015-12-19T14:08:53+00:00"
+ },
+ {
+ "name": "php-http/promise",
+ "version": "v1.0.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/php-http/promise.git",
+ "reference": "dc494cdc9d7160b9a09bd5573272195242ce7980"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/php-http/promise/zipball/dc494cdc9d7160b9a09bd5573272195242ce7980",
+ "reference": "dc494cdc9d7160b9a09bd5573272195242ce7980",
+ "shasum": ""
+ },
+ "require-dev": {
+ "henrikbjorn/phpspec-code-coverage": "^1.0",
+ "phpspec/phpspec": "^2.4"
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-master": "1.1-dev"
+ }
+ },
+ "autoload": {
+ "psr-4": {
+ "Http\\Promise\\": "src/"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Márk Sági-Kazár",
+ "email": "mark.sagikazar@gmail.com"
+ },
+ {
+ "name": "Joel Wurtz",
+ "email": "joel.wurtz@gmail.com"
+ }
+ ],
+ "description": "Promise used for asynchronous HTTP requests",
+ "homepage": "http://httplug.io",
+ "keywords": [
+ "promise"
+ ],
+ "time": "2016-01-26T13:27:02+00:00"
+ },
{
"name": "phpmailer/phpmailer",
"version": "v6.0.6",
@@ -392,6 +981,52 @@
"description": "PHPMailer is a full-featured email creation and transfer class for PHP",
"time": "2018-11-16T00:41:32+00:00"
},
+ {
+ "name": "psr/cache",
+ "version": "1.0.1",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/php-fig/cache.git",
+ "reference": "d11b50ad223250cf17b86e38383413f5a6764bf8"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/php-fig/cache/zipball/d11b50ad223250cf17b86e38383413f5a6764bf8",
+ "reference": "d11b50ad223250cf17b86e38383413f5a6764bf8",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=5.3.0"
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-master": "1.0.x-dev"
+ }
+ },
+ "autoload": {
+ "psr-4": {
+ "Psr\\Cache\\": "src/"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "PHP-FIG",
+ "homepage": "http://www.php-fig.org/"
+ }
+ ],
+ "description": "Common interface for caching libraries",
+ "keywords": [
+ "cache",
+ "psr",
+ "psr-6"
+ ],
+ "time": "2016-08-06T20:24:11+00:00"
+ },
{
"name": "psr/http-message",
"version": "1.0.1",
@@ -639,18 +1274,72 @@
"homepage": "https://symfony.com",
"time": "2018-11-21T14:20:20+00:00"
},
+ {
+ "name": "symfony/options-resolver",
+ "version": "v4.2.2",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/symfony/options-resolver.git",
+ "reference": "fbcb106aeee72f3450298bf73324d2cc00d083d1"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/symfony/options-resolver/zipball/fbcb106aeee72f3450298bf73324d2cc00d083d1",
+ "reference": "fbcb106aeee72f3450298bf73324d2cc00d083d1",
+ "shasum": ""
+ },
+ "require": {
+ "php": "^7.1.3"
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-master": "4.2-dev"
+ }
+ },
+ "autoload": {
+ "psr-4": {
+ "Symfony\\Component\\OptionsResolver\\": ""
+ },
+ "exclude-from-classmap": [
+ "/Tests/"
+ ]
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Fabien Potencier",
+ "email": "fabien@symfony.com"
+ },
+ {
+ "name": "Symfony Community",
+ "homepage": "https://symfony.com/contributors"
+ }
+ ],
+ "description": "Symfony OptionsResolver Component",
+ "homepage": "https://symfony.com",
+ "keywords": [
+ "config",
+ "configuration",
+ "options"
+ ],
+ "time": "2019-01-03T09:07:35+00:00"
+ },
{
"name": "topthink/framework",
- "version": "v5.1.31",
+ "version": "v5.1.32",
"source": {
"type": "git",
"url": "https://github.com/top-think/framework.git",
- "reference": "93339b1a4df5a73e0143db0847a4c5e0b2e46fb0"
+ "reference": "88a2ab625b35e047896718db320e08375cf021da"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/top-think/framework/zipball/93339b1a4df5a73e0143db0847a4c5e0b2e46fb0",
- "reference": "93339b1a4df5a73e0143db0847a4c5e0b2e46fb0",
+ "url": "https://api.github.com/repos/top-think/framework/zipball/88a2ab625b35e047896718db320e08375cf021da",
+ "reference": "88a2ab625b35e047896718db320e08375cf021da",
"shasum": ""
},
"require": {
@@ -688,7 +1377,7 @@
"orm",
"thinkphp"
],
- "time": "2018-12-09T12:41:21+00:00"
+ "time": "2018-12-23T13:42:11+00:00"
},
{
"name": "topthink/think-captcha",
diff --git a/config/app.php b/config/app.php
index 61dc0dd6..0d2c9163 100644
--- a/config/app.php
+++ b/config/app.php
@@ -89,7 +89,7 @@
// IP代理获取标识
'http_agent_ip' => 'X-REAL-IP',
// URL伪静态后缀
- 'url_html_suffix' => 'html',
+ 'url_html_suffix' => '',
// URL普通方式参数 用于自动生成
'url_common_param' => true,
// URL参数方式 0 按名称成对解析 1 按顺序解析
@@ -139,7 +139,7 @@
// 错误显示信息,非调试模式有效
'error_message' => '页面错误!请稍后再试~',
// 显示错误信息
- 'show_error_msg' => false,
+ 'show_error_msg' => true,
// 异常处理handle类 留空使用 \think\exception\Handle
'exception_handle' => '',
diff --git a/application/index/config/naming.php b/config/naming.php
similarity index 100%
rename from application/index/config/naming.php
rename to config/naming.php
diff --git a/config/strategy.php b/config/strategy.php
index b448356b..d783290b 100644
--- a/config/strategy.php
+++ b/config/strategy.php
@@ -29,4 +29,8 @@
'name' => '又拍云USS',
'class' => \strategy\driver\Uss::class
],
+ 'github' => [
+ 'name' => 'Github',
+ 'class' => \strategy\driver\Github::class
+ ],
];
diff --git a/extend/strategy/Driver.php b/extend/strategy/Driver.php
index 58b45098..ad3db291 100644
--- a/extend/strategy/Driver.php
+++ b/extend/strategy/Driver.php
@@ -47,4 +47,4 @@ public function deletes(array $list);
* @return mixed
*/
public function getError();
-}
\ No newline at end of file
+}
diff --git a/extend/strategy/driver/Github.php b/extend/strategy/driver/Github.php
new file mode 100644
index 00000000..dfc1cb17
--- /dev/null
+++ b/extend/strategy/driver/Github.php
@@ -0,0 +1,121 @@
+options = $options;
+ try {
+ $this->client = new \Github\Client();
+ $this->client->authenticate($this->options['github_token'],null, \Github\Client::AUTH_URL_TOKEN);
+ } catch (\Exception $e) {
+ $this->error = $e->getMessage();
+ }
+ }
+
+ /**
+ * 创建文件
+ *
+ * @param $pathname
+ * @param $file
+ *
+ * @return bool
+ */
+ public function create($pathname, $file)
+ {
+ try {
+ $image_info = getimagesize($file);
+ $content = fread(fopen($file, 'r'), filesize($file));
+ $path = $this->options['github_path']."/".$pathname;
+ $fileInfo = $this->client->api('repo')->contents()->create($this->options['github_user'], $this->options['github_repo'], $path,$content,$this->options['github_message'],$this->options['github_branch']);
+ } catch (\Exception $e) {
+ $this->error = $e->getMessage();
+ return false;
+ }
+
+ return true;
+ }
+
+ /**
+ * 删除文件
+ *
+ * @param $pathname
+ *
+ * @return bool
+ */
+ public function delete($pathname)
+ {
+ try {
+ $path = $this->options['github_path']."/".$pathname;
+ $oldFile =$this->client->api('repo')->contents()->show($this->options['github_user'], $this->options['github_repo'], $path,$this->options['github_branch']);
+ $fileInfo =$this->client->api('repo')->contents()->rm($this->options['github_user'], $this->options['github_repo'], $path, "", $oldFile['sha'],$this->options['github_branch']);
+
+ } catch (\Exception $e) {
+ $this->error = $e->getMessage();
+ return false;
+ }
+
+ return true;
+ }
+
+ /**
+ * 删除多个文件
+ *
+ * @param array $list
+ * @return bool|mixed
+ */
+ public function deletes(array $list)
+ {
+ try {
+ foreach ($list as $value) {
+ $this->delete($value);
+ }
+ } catch (\Exception $e) {
+ $this->error = $e->getMessage();
+ return false;
+ }
+
+ return true;
+ }
+
+ public function getError()
+ {
+ return 'client:' . $this->error;
+ }
+}
diff --git a/install.sql b/install.sql
index 8817edff..6de450d5 100644
--- a/install.sql
+++ b/install.sql
@@ -1,103 +1,111 @@
--- phpMyAdmin SQL Dump
--- version 4.8.2
--- https://www.phpmyadmin.net/
---
--- Host: localhost:3306
--- Generation Time: 2018-09-28 17:54:47
--- 服务器版本: 5.7.21
--- PHP Version: 7.2.7
-
-SET SQL_MODE = "NO_AUTO_VALUE_ON_ZERO";
-SET time_zone = "+00:00";
-
---
--- Database: `lsky`
---
-
+-- --------------------------------------------------------
+-- 主机: 127.0.0.1
+-- 服务器版本: 8.0.13 - MySQL Community Server - GPL
+-- 服务器操作系统: Win64
+-- HeidiSQL 版本: 9.4.0.5125
-- --------------------------------------------------------
---
--- 表的结构 `lsky_config`
---
+/*!40101 SET @OLD_CHARACTER_SET_CLIENT=@@CHARACTER_SET_CLIENT */;
+/*!40101 SET NAMES utf8 */;
+/*!50503 SET NAMES utf8mb4 */;
+/*!40014 SET @OLD_FOREIGN_KEY_CHECKS=@@FOREIGN_KEY_CHECKS, FOREIGN_KEY_CHECKS=0 */;
+/*!40101 SET @OLD_SQL_MODE=@@SQL_MODE, SQL_MODE='NO_AUTO_VALUE_ON_ZERO' */;
-DROP TABLE IF EXISTS `lsky_config`;
+-- 导出 表 tuchuang.lsky_config 结构
CREATE TABLE IF NOT EXISTS `lsky_config` (
- `id` smallint(6) UNSIGNED NOT NULL,
+ `id` smallint(6) unsigned NOT NULL AUTO_INCREMENT,
`key` varchar(32) DEFAULT NULL COMMENT 'key',
`type` varchar(32) NOT NULL DEFAULT 'text' COMMENT 'text|bool|textarea|select',
`input_type` varchar(32) NOT NULL DEFAULT 'text' COMMENT 'input type属性',
- `name` varchar(32) CHARACTER SET utf8mb4 NOT NULL COMMENT '配置名',
- `title` varchar(100) CHARACTER SET utf8mb4 DEFAULT NULL COMMENT '配置标题',
- `tip` varchar(100) CHARACTER SET utf8mb4 DEFAULT NULL COMMENT '配置描述',
- `value` text CHARACTER SET utf8mb4 NOT NULL COMMENT '配置值',
- `extend` text CHARACTER SET utf8mb4 NOT NULL COMMENT '扩展属性'
-) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='系统配置' ROW_FORMAT=COMPACT;
-
---
--- 转存表中的数据 `lsky_config`
---
-
+ `name` varchar(32) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL COMMENT '配置名',
+ `title` varchar(100) CHARACTER SET utf8 COLLATE utf8_general_ci DEFAULT NULL COMMENT '配置标题',
+ `tip` varchar(100) CHARACTER SET utf8 COLLATE utf8_general_ci DEFAULT NULL COMMENT '配置描述',
+ `value` text CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL COMMENT '配置值',
+ `extend` text CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL COMMENT '扩展属性',
+ PRIMARY KEY (`id`),
+ UNIQUE KEY `name` (`name`)
+) ENGINE=InnoDB AUTO_INCREMENT=54 DEFAULT CHARSET=utf8 ROW_FORMAT=COMPACT COMMENT='系统配置';
+
+-- 正在导出表 tuchuang.lsky_config 的数据:~5 rows (大约)
+DELETE FROM `lsky_config`;
+/*!40000 ALTER TABLE `lsky_config` DISABLE KEYS */;
INSERT INTO `lsky_config` (`id`, `key`, `type`, `input_type`, `name`, `title`, `tip`, `value`, `extend`) VALUES
-(1, 'basics', 'bool', 'checkbox', 'close_register', '关闭注册', NULL, '0', ''),
-(2, 'basics', 'text', 'text', 'site_name', '网站标题', NULL, 'Lsky Pro', ''),
-(3, 'basics', 'text', 'text', 'site_keywords', '网站关键字', NULL, 'Lsky Pro', ''),
-(4, 'basics', 'text', 'text', 'site_description', '网站描述', NULL, 'Lsky Pro, Your photo album on the cloud.', ''),
-(5, 'basics', 'text', 'text', 'icp_number', '备案号', NULL, '', ''),
-(6, 'upload', 'bool', 'checkbox', 'allowed_tourist_upload', '允许游客上传', '是否允许游客上传', '1', ''),
-(7, 'upload', 'text', 'text', 'upload_max_size', '最大上传大小', '单位:b,默认5242880:5M', '5242880', ''),
-(8, 'upload', 'text', 'number', 'upload_single_num', '单次同时上传数量', NULL, '10', ''),
-(9, 'upload', 'text', 'text', 'upload_allowed_exts', '允许上传的文件后缀', '逗号隔开', 'jpg,jpeg,gif,png,ico', ''),
-(10, 'upload', 'text', 'text', 'path_naming_rule', '文件路径命名规则', '路径命名变量对照表', '{Y}/{m}/{d}', ''),
-(11, 'upload', 'text', 'text', 'file_naming_rule', '文件命名规则', '文件命名变量对照表', '{uniqid}', ''),
-(12, 'user', 'text', 'text', 'user_initial_quota', '用户初始配额容量', '单位:b,默认1073741824:1G,最大18位', '1073741824', ''),
-(13, 'mail', 'select', 'text', 'mail_send_mode', '邮件发送方式', NULL, 'smtp', '{\"smtp\":\"SMTP\"}'),
-(14, 'mail', 'select', 'text', 'mail_smtp_secure', 'SMTP验证方式', NULL, 'none', '{\"none\":\"None\",\"tls\":\"TLS\",\"ssl\":\"SSL\"}'),
-(15, 'mail', 'text', 'text', 'mail_smtp_host', 'SMTP主机地址', NULL, '', ''),
-(16, 'mail', 'text', 'text', 'mail_smtp_username', 'SMTP用户名', NULL, '', ''),
-(17, 'mail', 'text', 'password', 'mail_smtp_password', 'SMTP密码', NULL, '', ''),
-(18, 'mail', 'text', 'number', 'mail_smtp_port', 'SMTP端口', '25/465', '25', ''),
-(19, 'mail', 'text', 'email', 'mail_form_email', '发件人邮箱', NULL, '', ''),
-(20, 'other', 'bool', 'checkbox', 'soft_delete', '软删除', '删除图片时不删除源文件,不建议开启', '0', ''),
-
-(21, 'storage_strategy', 'select', 'text', 'storage_strategy', '储存策略', NULL, 'local', ''),
-(22, 'local', 'text', 'text', 'local_cdn_domain', 'CDN加速域名', NULL, '', ''),
-(23, 'oss', 'text', 'text', 'oss_cdn_domain', 'Bucket域名', NULL, '', ''),
-(24, 'oss', 'text', 'text', 'oss_access_key_id', 'AccessKeyId', NULL, '', ''),
-(25, 'oss', 'text', 'text', 'oss_access_key_secret', 'AccessKeySecret', NULL, '', ''),
-(26, 'oss', 'text', 'text', 'oss_endpoint', 'Endpoint', '地域节点', '', ''),
-(27, 'oss', 'text', 'text', 'oss_bucket', 'Bucket', NULL, '', ''),
-(28, 'cos', 'text', 'text', 'cos_cdn_domain', 'CDN加速域名', NULL, '', ''),
-(29, 'cos', 'text', 'text', 'cos_secret_id', 'SecretId', NULL, '', ''),
-(30, 'cos', 'text', 'text', 'cos_secret_key', 'SecretKey', NULL, '', ''),
-(31, 'cos', 'text', 'text', 'cos_region', '所属地域', NULL, '', ''),
-(32, 'cos', 'text', 'text', 'cos_bucket', 'Bucket', '储存桶名称', '', ''),
-(33, 'kodo', 'text', 'text', 'kodo_cdn_domain', 'CDN加速域名', NULL, '', ''),
-(34, 'kodo', 'text', 'text', 'kodo_access_key', 'AccessKey', NULL, '', ''),
-(35, 'kodo', 'text', 'text', 'kodo_secret_key', 'SecretKey', NULL, '', ''),
-(36, 'kodo', 'text', 'text', 'kodo_bucket', 'Bucket', NULL, '', ''),
-(37, 'uss', 'text', 'text', 'uss_cdn_domain', 'CDN加速域名', NULL, '', ''),
-(38, 'uss', 'text', 'text', 'uss_operator_name', 'OperatorName', '操作员账号', '', ''),
-(39, 'uss', 'text', 'password', 'uss_operator_pwd', 'OperatorPwd', '操作员密码', '', ''),
-(40, 'uss', 'text', 'text', 'uss_service_name', 'ServiceName', '云储存服务名称', '', ''),
-(41, '', 'text', 'text', 'system_version', '系统版本', NULL, '1.4.2', ''),
-
-
-(42, 'audit', 'bool', 'checkbox', 'open_audit', '开启图片鉴黄', '鉴黄接口申请地址:https://www.moderatecontent.com', '0', ''),
-(43, 'audit', 'text', 'text', 'audit_key', 'Key', NULL, '', ''),
-(44, 'audit', 'select', 'text', 'audit_index', '内容评级', '1=所有人,2=少年,3=成人', '3', '{\"1\": \"所有人\", \"2\": \"少年\", \"3\": \"成人\"}'),
-(45, 'other', 'bool', 'checkbox', 'open_api', '开启API', '是否开放接口', '0', '');
-
--- --------------------------------------------------------
+ (1, 'basics', 'bool', 'checkbox', 'close_register', '关闭注册', NULL, '0', ''),
+ (2, 'basics', 'text', 'text', 'site_name', '网站标题', NULL, 'Lsky Pro', ''),
+ (3, 'basics', 'text', 'text', 'site_keywords', '网站关键字', NULL, 'Lsky Pro', ''),
+ (4, 'basics', 'text', 'text', 'site_description', '网站描述', NULL, 'Lsky Pro, Your photo album on the cloud.', ''),
+ (5, 'basics', 'text', 'text', 'icp_number', '备案号', NULL, '', ''),
+ (6, 'upload', 'bool', 'checkbox', 'allowed_tourist_upload', '允许游客上传', '是否允许游客上传', '1', ''),
+ (7, 'upload', 'text', 'text', 'upload_max_size', '最大上传大小', '单位:b,默认5242880:5M', '5242880', ''),
+ (8, 'upload', 'text', 'number', 'upload_single_num', '单次同时上传数量', NULL, '10', ''),
+ (9, 'upload', 'text', 'text', 'upload_allowed_exts', '允许上传的文件后缀', '逗号隔开', 'jpg,jpeg,gif,png,ico', ''),
+ (10, 'upload', 'text', 'text', 'path_naming_rule', '文件路径命名规则', '路径命名变量对照表', '{Y}/{m}/{d}', ''),
+ (11, 'upload', 'text', 'text', 'file_naming_rule', '文件命名规则', '文件命名变量对照表', '{uniqid}', ''),
+ (12, 'user', 'text', 'text', 'user_initial_quota', '用户初始配额容量', '单位:b,默认1073741824:1G,最大18位', '1073741824', ''),
+ (13, 'mail', 'select', 'text', 'mail_send_mode', '邮件发送方式', NULL, 'smtp', '{"smtp":"SMTP"}'),
+ (14, 'mail', 'select', 'text', 'mail_smtp_secure', 'SMTP验证方式', NULL, 'none', '{"none":"None","tls":"TLS","ssl":"SSL"}'),
+ (15, 'mail', 'text', 'text', 'mail_smtp_host', 'SMTP主机地址', NULL, '', ''),
+ (16, 'mail', 'text', 'text', 'mail_smtp_username', 'SMTP用户名', NULL, '', ''),
+ (17, 'mail', 'text', 'password', 'mail_smtp_password', 'SMTP密码', NULL, '', ''),
+ (18, 'mail', 'text', 'number', 'mail_smtp_port', 'SMTP端口', '25/465', '25', ''),
+ (19, 'mail', 'text', 'email', 'mail_form_email', '发件人邮箱', NULL, '', ''),
+ (20, 'other', 'bool', 'checkbox', 'soft_delete', '软删除', '删除图片时不删除源文件,不建议开启', '0', ''),
+ (21, 'storage_strategy', 'select', 'text', 'storage_strategy', '储存策略', NULL, 'github', ''),
+ (22, 'local', 'text', 'text', 'local_cdn_domain', 'CDN加速域名', NULL, '', ''),
+ (23, 'oss', 'text', 'text', 'oss_cdn_domain', 'Bucket域名', NULL, '', ''),
+ (24, 'oss', 'text', 'text', 'oss_access_key_id', 'AccessKeyId', NULL, '', ''),
+ (25, 'oss', 'text', 'text', 'oss_access_key_secret', 'AccessKeySecret', NULL, '', ''),
+ (26, 'oss', 'text', 'text', 'oss_endpoint', 'Endpoint', '地域节点', '', ''),
+ (27, 'oss', 'text', 'text', 'oss_bucket', 'Bucket', NULL, '', ''),
+ (28, 'cos', 'text', 'text', 'cos_cdn_domain', 'CDN加速域名', NULL, '', ''),
+ (29, 'cos', 'text', 'text', 'cos_secret_id', 'SecretId', NULL, '', ''),
+ (30, 'cos', 'text', 'text', 'cos_secret_key', 'SecretKey', NULL, '', ''),
+ (31, 'cos', 'text', 'text', 'cos_region', '所属地域', NULL, '', ''),
+ (32, 'cos', 'text', 'text', 'cos_bucket', 'Bucket', '储存桶名称', '', ''),
+ (33, 'kodo', 'text', 'text', 'kodo_cdn_domain', 'CDN加速域名', NULL, '', ''),
+ (34, 'kodo', 'text', 'text', 'kodo_access_key', 'AccessKey', NULL, '', ''),
+ (35, 'kodo', 'text', 'text', 'kodo_secret_key', 'SecretKey', NULL, '', ''),
+ (36, 'kodo', 'text', 'text', 'kodo_bucket', 'Bucket', NULL, '', ''),
+ (37, 'uss', 'text', 'text', 'uss_cdn_domain', 'CDN加速域名', NULL, '', ''),
+ (38, 'uss', 'text', 'text', 'uss_operator_name', 'OperatorName', '操作员账号', '', ''),
+ (39, 'uss', 'text', 'password', 'uss_operator_pwd', 'OperatorPwd', '操作员密码', '', ''),
+ (40, 'uss', 'text', 'text', 'uss_service_name', 'ServiceName', '云储存服务名称', '', ''),
+ (41, '', 'text', 'text', 'system_version', '系统版本', NULL, '1.4.2', ''),
+ (42, 'audit', 'bool', 'checkbox', 'open_audit', '开启图片鉴黄', '鉴黄接口申请地址:https://www.moderatecontent.com', '0', ''),
+ (43, 'audit', 'text', 'text', 'audit_key', 'Key', NULL, '', ''),
+ (44, 'audit', 'select', 'text', 'audit_index', '内容评级', '1=所有人,2=少年,3=成人', '3', '{"1": "所有人", "2": "少年", "3": "成人"}'),
+ (45, 'other', 'bool', 'checkbox', 'open_api', '开启API', '是否开放接口', '1', ''),
+ (46, 'github', 'text', 'text', 'github_token', 'Token', '', '', ''),
+ (48, 'github', 'text', 'text', 'github_user', '用户账号', '', '', ''),
+ (49, 'github', 'text', 'text', 'github_repo', '仓库名称', '', '', ''),
+ (50, 'github', 'text', 'text', 'github_path', '存储路径', '', '', ''),
+ (51, 'github', 'text', 'text', 'github_message', '提交信息', '', '', ''),
+ (52, 'github', 'text', 'text', 'github_branch', '分支', '', '', ''),
+ (53, 'github', 'text', 'text', 'github_cdn_domain', '访问url', '', '', '');
+/*!40000 ALTER TABLE `lsky_config` ENABLE KEYS */;
+
+-- 导出 表 tuchuang.lsky_folders 结构
+CREATE TABLE IF NOT EXISTS `lsky_folders` (
+ `id` int(11) NOT NULL AUTO_INCREMENT COMMENT 'ID',
+ `user_id` int(11) NOT NULL COMMENT '用户ID',
+ `parent_id` int(11) NOT NULL DEFAULT '0' COMMENT '上级文件夹ID',
+ `name` varchar(100) NOT NULL COMMENT '文件夹名称',
+ `delete_time` int(11) DEFAULT NULL COMMENT '删除时间',
+ `update_time` int(11) DEFAULT NULL COMMENT '更新时间',
+ `create_time` int(11) DEFAULT NULL COMMENT '添加时间',
+ PRIMARY KEY (`id`)
+) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='文件夹表';
---
--- 表的结构 `lsky_images`
---
+-- 正在导出表 tuchuang.lsky_folders 的数据:~0 rows (大约)
+DELETE FROM `lsky_folders`;
+/*!40000 ALTER TABLE `lsky_folders` DISABLE KEYS */;
+/*!40000 ALTER TABLE `lsky_folders` ENABLE KEYS */;
-DROP TABLE IF EXISTS `lsky_images`;
+-- 导出 表 tuchuang.lsky_images 结构
CREATE TABLE IF NOT EXISTS `lsky_images` (
- `id` int(11) UNSIGNED NOT NULL COMMENT 'ID',
- `user_id` int(11) NOT NULL DEFAULT 0 COMMENT '用户ID,为0表示游客上传',
- `folder_id` int(11) NOT NULL DEFAULT 0 COMMENT '文件夹ID',
+ `id` int(11) unsigned NOT NULL AUTO_INCREMENT COMMENT 'ID',
+ `user_id` int(11) NOT NULL DEFAULT '0' COMMENT '用户ID,为0表示游客上传',
+ `folder_id` int(11) NOT NULL DEFAULT '0' COMMENT '文件夹ID',
`strategy` varchar(32) NOT NULL DEFAULT 'local' COMMENT '储存策略,默认本地',
`path` varchar(500) NOT NULL COMMENT '保存路径',
`name` varchar(500) NOT NULL COMMENT '保存名称',
@@ -107,18 +115,18 @@ CREATE TABLE IF NOT EXISTS `lsky_images` (
`sha1` varchar(100) NOT NULL COMMENT 'hash sha1',
`md5` varchar(32) NOT NULL COMMENT 'hash md5',
`ip` varchar(128) DEFAULT NULL COMMENT '上传者IP',
- `create_time` int(11) NOT NULL COMMENT '创建时间'
-) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='图片表';
+ `create_time` int(11) NOT NULL COMMENT '创建时间',
+ PRIMARY KEY (`id`)
+) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='图片表';
--- --------------------------------------------------------
-
---
--- 表的结构 `lsky_users`
---
+-- 正在导出表 tuchuang.lsky_images 的数据:~0 rows (大约)
+DELETE FROM `lsky_images`;
+/*!40000 ALTER TABLE `lsky_images` DISABLE KEYS */;
+/*!40000 ALTER TABLE `lsky_images` ENABLE KEYS */;
-DROP TABLE IF EXISTS `lsky_users`;
+-- 导出 表 tuchuang.lsky_users 结构
CREATE TABLE IF NOT EXISTS `lsky_users` (
- `id` int(11) UNSIGNED NOT NULL,
+ `id` int(11) unsigned NOT NULL AUTO_INCREMENT,
`username` varchar(32) NOT NULL COMMENT '用户名',
`nickname` varchar(32) DEFAULT NULL COMMENT '昵称',
`email` varchar(100) NOT NULL COMMENT '邮箱',
@@ -131,79 +139,14 @@ CREATE TABLE IF NOT EXISTS `lsky_users` (
`reg_ip` varchar(32) DEFAULT NULL COMMENT '注册IP',
`delete_time` int(11) DEFAULT NULL COMMENT '删除时间',
`update_time` int(11) NOT NULL COMMENT '更新时间',
- `create_time` int(11) NOT NULL COMMENT '添加时间'
-) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='用户表';
-
---
--- 表的结构 `lsky_folders`
---
-
-DROP TABLE IF EXISTS `lsky_folders`;
-CREATE TABLE `lsky_folders` (
- `id` int(11) NOT NULL COMMENT 'ID',
- `user_id` int(11) NOT NULL COMMENT '用户ID',
- `parent_id` int(11) NOT NULL DEFAULT '0' COMMENT '上级文件夹ID',
- `name` varchar(100) NOT NULL COMMENT '文件夹名称',
- `delete_time` int(11) DEFAULT NULL COMMENT '删除时间',
- `update_time` int(11) DEFAULT NULL COMMENT '更新时间',
- `create_time` int(11) DEFAULT NULL COMMENT '添加时间'
-) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='文件夹表';
-
---
--- Indexes for dumped tables
---
-
---
--- Indexes for table `lsky_config`
---
-ALTER TABLE `lsky_config`
- ADD PRIMARY KEY (`id`),
- ADD UNIQUE KEY `name` (`name`);
-
---
--- Indexes for table `lsky_images`
---
-ALTER TABLE `lsky_images`
- ADD PRIMARY KEY (`id`);
-
---
--- Indexes for table `lsky_users`
---
-ALTER TABLE `lsky_users`
- ADD PRIMARY KEY (`id`),
- ADD UNIQUE KEY `username` (`username`),
- ADD UNIQUE KEY `email` (`email`);
-
---
--- Indexes for table `lsky_folders`
---
-ALTER TABLE `lsky_folders`
-ADD PRIMARY KEY (`id`);
-
---
--- 在导出的表使用AUTO_INCREMENT
---
-
---
--- 使用表AUTO_INCREMENT `lsky_config`
---
-ALTER TABLE `lsky_config`
- MODIFY `id` smallint(6) UNSIGNED NOT NULL AUTO_INCREMENT, AUTO_INCREMENT=46;
+ `create_time` int(11) NOT NULL COMMENT '添加时间',
+ PRIMARY KEY (`id`),
+ UNIQUE KEY `username` (`username`),
+ UNIQUE KEY `email` (`email`)
+) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8 COMMENT='用户表';
---
--- 使用表AUTO_INCREMENT `lsky_images`
---
-ALTER TABLE `lsky_images`
- MODIFY `id` int(11) UNSIGNED NOT NULL AUTO_INCREMENT COMMENT 'ID';
---
--- 使用表AUTO_INCREMENT `lsky_users`
---
-ALTER TABLE `lsky_users`
- MODIFY `id` int(11) UNSIGNED NOT NULL AUTO_INCREMENT, AUTO_INCREMENT=1;
---
--- 使用表AUTO_INCREMENT `lsky_folders`
---
-ALTER TABLE `lsky_folders`
- MODIFY `id` int(11) NOT NULL AUTO_INCREMENT COMMENT 'ID', AUTO_INCREMENT=1;
+/*!40101 SET SQL_MODE=IFNULL(@OLD_SQL_MODE, '') */;
+/*!40014 SET FOREIGN_KEY_CHECKS=IF(@OLD_FOREIGN_KEY_CHECKS IS NULL, 1, @OLD_FOREIGN_KEY_CHECKS) */;
+/*!40101 SET CHARACTER_SET_CLIENT=@OLD_CHARACTER_SET_CLIENT */;
diff --git a/thinkphp/library/think/App.php b/thinkphp/library/think/App.php
index cfa2601e..ac8b0e91 100644
--- a/thinkphp/library/think/App.php
+++ b/thinkphp/library/think/App.php
@@ -20,7 +20,7 @@
*/
class App extends Container
{
- const VERSION = '5.1.31 LTS';
+ const VERSION = '5.1.32 LTS';
/**
* 当前模块路径
@@ -722,9 +722,9 @@ public function controller($name, $layer = 'controller', $appendSuffix = false,
list($module, $class) = $this->parseModuleAndClass($name, $layer, $appendSuffix);
if (class_exists($class)) {
- return $this->__get($class);
+ return $this->make($class, true);
} elseif ($empty && class_exists($emptyClass = $this->parseClass($module, $layer, $empty, $appendSuffix))) {
- return $this->__get($emptyClass);
+ return $this->make($emptyClass, true);
}
throw new ClassNotFoundException('class not exists:' . $class, $class);
diff --git a/thinkphp/library/think/Controller.php b/thinkphp/library/think/Controller.php
index a57da9e3..d16a1ed5 100644
--- a/thinkphp/library/think/Controller.php
+++ b/thinkphp/library/think/Controller.php
@@ -67,6 +67,8 @@ public function __construct(App $app = null)
// 控制器初始化
$this->initialize();
+ $this->registerMiddleware();
+
// 前置操作方法 即将废弃
foreach ((array) $this->beforeActionList as $method => $options) {
is_numeric($method) ?
diff --git a/thinkphp/library/think/Model.php b/thinkphp/library/think/Model.php
index e0dcecf6..9896f9ff 100644
--- a/thinkphp/library/think/Model.php
+++ b/thinkphp/library/think/Model.php
@@ -392,11 +392,12 @@ public function replace($replace = true)
* 设置数据是否存在
* @access public
* @param bool $exists
- * @return void
+ * @return $this
*/
public function exists($exists)
{
$this->exists = $exists;
+ return $this;
}
/**
diff --git a/thinkphp/library/think/Validate.php b/thinkphp/library/think/Validate.php
index 4672a5cb..54c3a737 100644
--- a/thinkphp/library/think/Validate.php
+++ b/thinkphp/library/think/Validate.php
@@ -516,7 +516,7 @@ protected function checkItem($field, $value, $rules, $data, $title = '', $msg =
$rules = array_merge($rules, $this->append[$field]);
}
- $i = 0;
+ $i = 0;
$result = true;
foreach ($rules as $key => $rule) {
@@ -1002,10 +1002,14 @@ public function unique($value, $rule, $data, $field)
// 支持多个字段验证
$fields = explode('^', $key);
foreach ($fields as $key) {
- $map[] = [$key, '=', $data[$key]];
+ if (isset($data[$key])) {
+ $map[] = [$key, '=', $data[$key]];
+ }
}
- } else {
+ } elseif (isset($data[$field])) {
$map[] = [$key, '=', $data[$field]];
+ } else {
+ $map = [];
}
$pk = !empty($rule[3]) ? $rule[3] : $db->getPk();
diff --git a/thinkphp/library/think/cache/Driver.php b/thinkphp/library/think/cache/Driver.php
index f0ec7baf..f4c5dcbf 100644
--- a/thinkphp/library/think/cache/Driver.php
+++ b/thinkphp/library/think/cache/Driver.php
@@ -219,7 +219,7 @@ public function tag($name, $keys = null, $overlay = false)
} elseif (is_null($keys)) {
$this->tag = $name;
} else {
- $key = 'tag_' . md5($name);
+ $key = $this->getTagkey($name);
if (is_string($keys)) {
$keys = explode(',', $keys);
@@ -248,14 +248,19 @@ public function tag($name, $keys = null, $overlay = false)
protected function setTagItem($name)
{
if ($this->tag) {
- $key = 'tag_' . md5($this->tag);
+ $key = $this->getTagkey($this->tag);
$prev = $this->tag;
$this->tag = null;
if ($this->has($key)) {
$value = explode(',', $this->get($key));
$value[] = $name;
- $value = implode(',', array_unique($value));
+
+ if (count($value) > 1000) {
+ array_shift($value);
+ }
+
+ $value = implode(',', array_unique($value));
} else {
$value = $name;
}
@@ -273,7 +278,7 @@ protected function setTagItem($name)
*/
protected function getTagItem($tag)
{
- $key = 'tag_' . md5($tag);
+ $key = $this->getTagkey($tag);
$value = $this->get($key);
if ($value) {
@@ -283,6 +288,11 @@ protected function getTagItem($tag)
}
}
+ protected function getTagKey($tag)
+ {
+ return 'tag_' . md5($tag);
+ }
+
/**
* 序列化数据
* @access protected
diff --git a/thinkphp/library/think/cache/driver/File.php b/thinkphp/library/think/cache/driver/File.php
index 7c5661e3..93d321f2 100644
--- a/thinkphp/library/think/cache/driver/File.php
+++ b/thinkphp/library/think/cache/driver/File.php
@@ -266,7 +266,7 @@ public function clear($tag = null)
foreach ($keys as $key) {
$this->unlink($key);
}
- $this->rm('tag_' . md5($tag));
+ $this->rm($this->getTagKey($tag));
return true;
}
diff --git a/thinkphp/library/think/cache/driver/Lite.php b/thinkphp/library/think/cache/driver/Lite.php
index 544663c0..0cfe3907 100644
--- a/thinkphp/library/think/cache/driver/Lite.php
+++ b/thinkphp/library/think/cache/driver/Lite.php
@@ -198,7 +198,7 @@ public function clear($tag = null)
unlink($key);
}
- $this->rm('tag_' . md5($tag));
+ $this->rm($this->getTagKey($tag));
return true;
}
diff --git a/thinkphp/library/think/cache/driver/Memcache.php b/thinkphp/library/think/cache/driver/Memcache.php
index 162ca520..1c535597 100644
--- a/thinkphp/library/think/cache/driver/Memcache.php
+++ b/thinkphp/library/think/cache/driver/Memcache.php
@@ -188,11 +188,13 @@ public function clear($tag = null)
if ($tag) {
// 指定标签清除
$keys = $this->getTagItem($tag);
+
foreach ($keys as $key) {
$this->handler->delete($key);
}
- $this->rm('tag_' . md5($tag));
+ $tagName = $this->getTagKey($tag);
+ $this->rm($tagName);
return true;
}
@@ -200,4 +202,5 @@ public function clear($tag = null)
return $this->handler->flush();
}
+
}
diff --git a/thinkphp/library/think/cache/driver/Memcached.php b/thinkphp/library/think/cache/driver/Memcached.php
index d04fac08..6af60d19 100644
--- a/thinkphp/library/think/cache/driver/Memcached.php
+++ b/thinkphp/library/think/cache/driver/Memcached.php
@@ -204,7 +204,7 @@ public function clear($tag = null)
$keys = $this->getTagItem($tag);
$this->handler->deleteMulti($keys);
- $this->rm('tag_' . md5($tag));
+ $this->rm($this->getTagKey($tag));
return true;
}
@@ -213,4 +213,67 @@ public function clear($tag = null)
return $this->handler->flush();
}
+
+ /**
+ * 缓存标签
+ * @access public
+ * @param string $name 标签名
+ * @param string|array $keys 缓存标识
+ * @param bool $overlay 是否覆盖
+ * @return $this
+ */
+ public function tag($name, $keys = null, $overlay = false)
+ {
+ if (is_null($keys)) {
+ $this->tag = $name;
+ } else {
+ $tagName = $this->getTagKey($name);
+ if ($overlay) {
+ $this->handler->delete($tagName);
+ }
+
+ if (!$this->handler->has($tagName)) {
+ $this->handler->set($tagName, '');
+ }
+
+ foreach ($keys as $key) {
+ $this->handler->append($tagName, ',' . $key);
+ }
+ }
+
+ return $this;
+ }
+
+ /**
+ * 更新标签
+ * @access protected
+ * @param string $name 缓存标识
+ * @return void
+ */
+ protected function setTagItem($name)
+ {
+ if ($this->tag) {
+ $tagName = $this->getTagKey($this->tag);
+
+ if ($this->handler->has($tagName)) {
+ $this->handler->append($tagName, ',' . $name);
+ } else {
+ $this->handler->set($tagName, $name);
+ }
+
+ $this->tag = null;
+ }
+ }
+
+ /**
+ * 获取标签包含的缓存标识
+ * @access public
+ * @param string $tag 缓存标签
+ * @return array
+ */
+ public function getTagItem($tag)
+ {
+ $tagName = $this->getTagKey($tag);
+ return explode(',', trim($this->handler->get($tagName), ','));
+ }
}
diff --git a/thinkphp/library/think/cache/driver/Redis.php b/thinkphp/library/think/cache/driver/Redis.php
index b924ec3d..813746e7 100644
--- a/thinkphp/library/think/cache/driver/Redis.php
+++ b/thinkphp/library/think/cache/driver/Redis.php
@@ -70,6 +70,10 @@ public function __construct($options = [])
}
}
+ if ('' == $this->options['password']) {
+ unset($this->options['password']);
+ }
+
$this->handler = new \Predis\Client($this->options, $params);
$this->options['prefix'] = '';
@@ -187,7 +191,7 @@ public function rm($name)
{
$this->writeTimes++;
- return $this->handler->delete($this->getCacheKey($name));
+ return $this->handler->del($this->getCacheKey($name));
}
/**
@@ -202,11 +206,10 @@ public function clear($tag = null)
// 指定标签清除
$keys = $this->getTagItem($tag);
- foreach ($keys as $key) {
- $this->handler->delete($key);
- }
+ $this->handler->del($keys);
- $this->rm('tag_' . md5($tag));
+ $tagName = $this->getTagKey($tag);
+ $this->handler->del($tagName);
return true;
}
@@ -215,4 +218,55 @@ public function clear($tag = null)
return $this->handler->flushDB();
}
+ /**
+ * 缓存标签
+ * @access public
+ * @param string $name 标签名
+ * @param string|array $keys 缓存标识
+ * @param bool $overlay 是否覆盖
+ * @return $this
+ */
+ public function tag($name, $keys = null, $overlay = false)
+ {
+ if (is_null($keys)) {
+ $this->tag = $name;
+ } else {
+ $tagName = $this->getTagKey($name);
+ if ($overlay) {
+ $this->handler->del($tagName);
+ }
+
+ foreach ($keys as $key) {
+ $this->handler->sAdd($tagName, $key);
+ }
+ }
+
+ return $this;
+ }
+
+ /**
+ * 更新标签
+ * @access protected
+ * @param string $name 缓存标识
+ * @return void
+ */
+ protected function setTagItem($name)
+ {
+ if ($this->tag) {
+ $tagName = $this->getTagKey($this->tag);
+ $this->handler->sAdd($tagName, $name);
+ }
+ }
+
+ /**
+ * 获取标签包含的缓存标识
+ * @access protected
+ * @param string $tag 缓存标签
+ * @return array
+ */
+ protected function getTagItem($tag)
+ {
+ $tagName = $this->getTagKey($tag);
+ return $this->handler->sMembers($tagName);
+ }
}
diff --git a/thinkphp/library/think/cache/driver/Sqlite.php b/thinkphp/library/think/cache/driver/Sqlite.php
index 7e78ec12..f57361e3 100644
--- a/thinkphp/library/think/cache/driver/Sqlite.php
+++ b/thinkphp/library/think/cache/driver/Sqlite.php
@@ -216,7 +216,7 @@ public function rm($name)
public function clear($tag = null)
{
if ($tag) {
- $name = sqlite_escape_string($tag);
+ $name = sqlite_escape_string($this->getTagKey($tag));
$sql = 'DELETE FROM ' . $this->options['table'] . ' WHERE tag=\'' . $name . '\'';
sqlite_query($this->handler, $sql);
return true;
diff --git a/thinkphp/library/think/cache/driver/Wincache.php b/thinkphp/library/think/cache/driver/Wincache.php
index 10966e78..ef157841 100644
--- a/thinkphp/library/think/cache/driver/Wincache.php
+++ b/thinkphp/library/think/cache/driver/Wincache.php
@@ -160,15 +160,16 @@ public function clear($tag = null)
{
if ($tag) {
$keys = $this->getTagItem($tag);
- foreach ($keys as $key) {
- wincache_ucache_delete($key);
- }
- $this->rm('tag_' . md5($tag));
+
+ wincache_ucache_delete($keys);
+
+ $tagName = $this->getTagkey($tag);
+ $this->rm($tagName);
return true;
- } else {
- $this->writeTimes++;
- return wincache_ucache_clear();
}
+
+ $this->writeTimes++;
+ return wincache_ucache_clear();
}
}
diff --git a/thinkphp/library/think/cache/driver/Xcache.php b/thinkphp/library/think/cache/driver/Xcache.php
index 6d1bf3f6..4e698597 100644
--- a/thinkphp/library/think/cache/driver/Xcache.php
+++ b/thinkphp/library/think/cache/driver/Xcache.php
@@ -159,10 +159,12 @@ public function clear($tag = null)
if ($tag) {
// 指定标签清除
$keys = $this->getTagItem($tag);
+
foreach ($keys as $key) {
xcache_unset($key);
}
- $this->rm('tag_' . md5($tag));
+
+ $this->rm($this->getTagKey($tag));
return true;
}
diff --git a/thinkphp/library/think/db/Query.php b/thinkphp/library/think/db/Query.php
index 00483328..2704c8df 100644
--- a/thinkphp/library/think/db/Query.php
+++ b/thinkphp/library/think/db/Query.php
@@ -628,9 +628,6 @@ public function aggregate($aggregate, $field, $force = false)
$result = (float) $result;
}
- // 查询完成后清空聚合字段信息
- $this->removeOption('field');
-
return $result;
}
diff --git a/thinkphp/library/think/model/concern/Attribute.php b/thinkphp/library/think/model/concern/Attribute.php
index 75b02ab5..b7a80fd3 100644
--- a/thinkphp/library/think/model/concern/Attribute.php
+++ b/thinkphp/library/think/model/concern/Attribute.php
@@ -371,7 +371,8 @@ protected function autoWriteTimestamp($name)
case 'datetime':
case 'date':
$format = !empty($param) ? $param : $this->dateFormat;
- $value = $this->formatDateTime($format . '.u');
+ $format .= strpos($format, 'u') || false !== strpos($format, '\\') ? '' : '.u';
+ $value = $this->formatDateTime($format);
break;
case 'timestamp':
case 'integer':
@@ -384,7 +385,8 @@ protected function autoWriteTimestamp($name)
'date',
'timestamp',
])) {
- $value = $this->formatDateTime($this->dateFormat . '.u');
+ $format = strpos($this->dateFormat, 'u') || false !== strpos($this->dateFormat, '\\') ? '' : '.u';
+ $value = $this->formatDateTime($this->dateFormat . $format);
} else {
$value = time();
}
diff --git a/thinkphp/library/think/model/relation/BelongsToMany.php b/thinkphp/library/think/model/relation/BelongsToMany.php
index b7cdebe1..747482b6 100644
--- a/thinkphp/library/think/model/relation/BelongsToMany.php
+++ b/thinkphp/library/think/model/relation/BelongsToMany.php
@@ -559,7 +559,9 @@ public function attach($data, $pivot = [])
foreach ($ids as $id) {
$pivot[$this->foreignKey] = $id;
- $this->pivot->replace()->save($pivot);
+ $this->pivot->replace()
+ ->exists(false)
+ ->save($pivot);
$result[] = $this->newPivot($pivot, true);
}
diff --git a/thinkphp/library/think/model/relation/HasMany.php b/thinkphp/library/think/model/relation/HasMany.php
index 72d83144..dbb8fa08 100644
--- a/thinkphp/library/think/model/relation/HasMany.php
+++ b/thinkphp/library/think/model/relation/HasMany.php
@@ -241,9 +241,9 @@ protected function eagerlyOneToMany($where, $relation, $subRelation = '', $closu
*/
public function save($data, $replace = true)
{
- $model = $this->make($data);
+ $model = $this->make();
- return $model->replace($replace)->save() ? $model : false;
+ return $model->replace($replace)->save($data) ? $model : false;
}
/**
diff --git a/thinkphp/library/think/model/relation/MorphMany.php b/thinkphp/library/think/model/relation/MorphMany.php
index 1a7f15eb..a1f54889 100644
--- a/thinkphp/library/think/model/relation/MorphMany.php
+++ b/thinkphp/library/think/model/relation/MorphMany.php
@@ -277,7 +277,7 @@ protected function eagerlyMorphToMany($where, $relation, $subRelation = '', $clo
*/
public function save($data)
{
- $model = $this->make($data);
+ $model = $this->make();
return $model->save($data) ? $model : false;
}
diff --git a/thinkphp/library/think/model/relation/MorphOne.php b/thinkphp/library/think/model/relation/MorphOne.php
index 716539f4..775b2dfd 100644
--- a/thinkphp/library/think/model/relation/MorphOne.php
+++ b/thinkphp/library/think/model/relation/MorphOne.php
@@ -211,8 +211,8 @@ protected function eagerlyMorphToOne($where, $relation, $subRelation = '', $clos
*/
public function save($data)
{
- $model = $this->make($data);
- return $model->save() ? $model : false;
+ $model = $this->make();
+ return $model->save($data) ? $model : false;
}
/**
diff --git a/thinkphp/library/think/route/Rule.php b/thinkphp/library/think/route/Rule.php
index 35730fee..fdfc29d1 100644
--- a/thinkphp/library/think/route/Rule.php
+++ b/thinkphp/library/think/route/Rule.php
@@ -722,13 +722,17 @@ public function parseRule($request, $rule, $route, $url, $option = [], $matches
// 替换路由地址中的变量
if (is_string($route) && !empty($matches)) {
- foreach ($matches as $key => $val) {
- if (false !== strpos($route, '<' . $key . '>')) {
- $route = str_replace('<' . $key . '>', $val, $route);
- } elseif (false !== strpos($route, ':' . $key)) {
- $route = str_replace(':' . $key, $val, $route);
- }
+ $search = $replace = [];
+
+ foreach ($matches as $key => $value) {
+ $search[] = '<' . $key . '>';
+ $replace[] = $value;
+
+ $search[] = ':' . $key;
+ $replace[] = $value;
}
+
+ $route = str_replace($search, $replace, $route);
}
// 解析额外参数
diff --git a/thinkphp/library/think/route/dispatch/Module.php b/thinkphp/library/think/route/dispatch/Module.php
index dc1974ce..e8842cd3 100644
--- a/thinkphp/library/think/route/dispatch/Module.php
+++ b/thinkphp/library/think/route/dispatch/Module.php
@@ -69,10 +69,6 @@ public function init()
// 获取控制器名
$controller = strip_tags($result[1] ?: $this->rule->getConfig('default_controller'));
- if (!preg_match('/^[A-Za-z](\w|\.)*$/', $controller)) {
- throw new HttpException(404, 'controller not exists:' . $controller);
- }
-
$this->controller = $convert ? strtolower($controller) : $controller;
// 获取操作名
@@ -97,10 +93,6 @@ public function exec()
$this->rule->getConfig('url_controller_layer'),
$this->rule->getConfig('controller_suffix'),
$this->rule->getConfig('empty_controller'));
-
- if ($instance instanceof Controller) {
- $instance->registerMiddleware();
- }
} catch (ClassNotFoundException $e) {
throw new HttpException(404, 'controller not exists:' . $e->getClass());
}
diff --git a/thinkphp/library/think/route/dispatch/Url.php b/thinkphp/library/think/route/dispatch/Url.php
index 95ee9e53..00dc8cca 100644
--- a/thinkphp/library/think/route/dispatch/Url.php
+++ b/thinkphp/library/think/route/dispatch/Url.php
@@ -60,6 +60,10 @@ protected function parseUrl($url)
$controller = !empty($path) ? array_shift($path) : null;
}
+ if ($controller && !preg_match('/^[A-Za-z][\w|\.]*$/', $controller)) {
+ throw new HttpException(404, 'controller not exists:' . $controller);
+ }
+
// 解析操作
$action = !empty($path) ? array_shift($path) : null;
diff --git a/update.sql b/update.sql
deleted file mode 100644
index b83bdcb5..00000000
--- a/update.sql
+++ /dev/null
@@ -1,48 +0,0 @@
---
--- Sql更新文件,选择数据库后导入,或直接复制内容执行
---
-
--- v1.2.0
-UPDATE `lsky_config` SET `value` = '1.2.1' WHERE `lsky_config`.`name` = 'system_version';
-
--- v1.3.2
-UPDATE `lsky_config` SET `value` = '1.3.2' WHERE `lsky_config`.`name` = 'system_version';
-
-INSERT IGNORE INTO `lsky_config` (`id`, `key`, `type`, `input_type`, `name`, `title`, `tip`, `value`, `extend`) VALUES
-(NULL, 'audit', 'bool', 'checkbox', 'open_audit', '开启图片鉴黄', '鉴黄接口申请地址:https://www.moderatecontent.com', '0', ''),
-(NULL, 'audit', 'text', 'text', 'audit_key', 'Key', NULL, '', ''),
-(NULL, 'audit', 'select', 'text', 'audit_index', '内容评级', '1=所有人,2=少年,3=成人', '3', '{\"1\": \"所有人\", \"2\": \"少年\", \"3\": \"成人\"}'),
-(NULL, 'other', 'bool', 'checkbox', 'open_api', '开启API', '是否开放接口', '0', '');
-
--- v1.3.3
-UPDATE `lsky_config` SET `value` = '1.3.3' WHERE `lsky_config`.`name` = 'system_version';
-
--- v1.4.0
-UPDATE `lsky_config` SET `value` = '1.4.0' WHERE `lsky_config`.`name` = 'system_version';
-UPDATE `lsky_config` SET `key` = 'uss', `name` = 'uss_cdn_domain' WHERE `lsky_config`.`name` = 'upyun_cdn_domain';
-UPDATE `lsky_config` SET `key` = 'uss', `name` = 'uss_operator_name' WHERE `lsky_config`.`name` = 'upyun_operator_name';
-UPDATE `lsky_config` SET `key` = 'uss', `name` = 'uss_operator_pwd' WHERE `lsky_config`.`name` = 'upyun_operator_pwd';
-UPDATE `lsky_config` SET `key` = 'uss', `name` = 'uss_service_name' WHERE `lsky_config`.`name` = 'upyun_service_name';
-UPDATE `lsky_config` SET `key` = 'kodo', `name` = 'kodo_cdn_domain' WHERE `lsky_config`.`name` = 'qiniu_cdn_domain';
-UPDATE `lsky_config` SET `key` = 'kodo', `name` = 'kodo_access_key' WHERE `lsky_config`.`name` = 'qiniu_access_key';
-UPDATE `lsky_config` SET `key` = 'kodo', `name` = 'kodo_secret_key' WHERE `lsky_config`.`name` = 'qiniu_secret_key';
-UPDATE `lsky_config` SET `key` = 'kodo', `name` = 'kodo_bucket' WHERE `lsky_config`.`name` = 'qiniu_bucket';
-UPDATE `lsky_config` SET `value` = 'kodo' WHERE `lsky_config`.`value` = 'qiniu';
-UPDATE `lsky_config` SET `value` = 'uss' WHERE `lsky_config`.`value` = 'upyun';
-
--- v1.4.1
-UPDATE `lsky_config` SET `value` = '1.4.1' WHERE `lsky_config`.`name` = 'system_version';
-CREATE TABLE IF NOT EXISTS `lsky_folders` (
- `id` int(11) PRIMARY KEY AUTO_INCREMENT NOT NULL COMMENT 'ID',
- `user_id` int(11) NOT NULL COMMENT '用户ID',
- `parent_id` int(11) NOT NULL DEFAULT '0' COMMENT '上级文件夹ID',
- `name` varchar(100) NOT NULL COMMENT '文件夹名称',
- `delete_time` int(11) DEFAULT NULL COMMENT '删除时间',
- `update_time` int(11) DEFAULT NULL COMMENT '更新时间',
- `create_time` int(11) DEFAULT NULL COMMENT '添加时间'
-) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='文件夹表';
-
--- v1.4.2
-UPDATE `lsky_config` SET `value` = '1.4.2' WHERE `lsky_config`.`name` = 'system_version';
-UPDATE `lsky_images` SET `strategy` = 'uss' WHERE `lsky_images`.`strategy` = 'upyun';
-UPDATE `lsky_images` SET `strategy` = 'kodo' WHERE `lsky_images`.`strategy` = 'qiniu';
diff --git a/vendor/clue/stream-filter/.gitignore b/vendor/clue/stream-filter/.gitignore
new file mode 100644
index 00000000..de4a392c
--- /dev/null
+++ b/vendor/clue/stream-filter/.gitignore
@@ -0,0 +1,2 @@
+/vendor
+/composer.lock
diff --git a/vendor/clue/stream-filter/.travis.yml b/vendor/clue/stream-filter/.travis.yml
new file mode 100644
index 00000000..a71864a3
--- /dev/null
+++ b/vendor/clue/stream-filter/.travis.yml
@@ -0,0 +1,26 @@
+language: php
+
+php:
+# - 5.3 # requires old distro, see below
+ - 5.4
+ - 5.5
+ - 5.6
+ - 7.0
+ - 7.1
+ - hhvm # ignore errors, see below
+
+# lock distro so new future defaults will not break the build
+dist: trusty
+
+matrix:
+ include:
+ - php: 5.3
+ dist: precise
+ allow_failures:
+ - php: hhvm
+
+install:
+ - composer install --no-interaction
+
+script:
+ - vendor/bin/phpunit --coverage-text
diff --git a/vendor/clue/stream-filter/CHANGELOG.md b/vendor/clue/stream-filter/CHANGELOG.md
new file mode 100644
index 00000000..9d53cd83
--- /dev/null
+++ b/vendor/clue/stream-filter/CHANGELOG.md
@@ -0,0 +1,54 @@
+# Changelog
+
+## 1.4.0 (2017-08-18)
+
+* Feature / Fix: The `fun()` function does not pass filter parameter `null`
+ to underlying `stream_filter_append()` by default
+ (#15 by @Nyholm)
+
+ Certain filters (such as `convert.quoted-printable-encode`) do not accept
+ a filter parameter at all. If no explicit filter parameter is given, we no
+ longer pass a default `null` value.
+
+ ```php
+ $encode = Filter\fun('convert.quoted-printable-encode');
+ assert('t=C3=A4st' === $encode('täst'));
+ ```
+
+* Add examples and improve documentation
+ (#13 and #20 by @clue and #18 by @Nyholm)
+
+* Improve test suite by adding PHPUnit to require-dev,
+ fix HHVM build for now again and ignore future HHVM build errors,
+ lock Travis distro so new future defaults will not break the build
+ and test on PHP 7.1
+ (#12, #14 and #19 by @clue and #16 by @Nyholm)
+
+## 1.3.0 (2015-11-08)
+
+* Feature: Support accessing built-in filters as callbacks
+ (#5 by @clue)
+
+ ```php
+ $fun = Filter\fun('zlib.deflate');
+
+ $ret = $fun('hello') . $fun('world') . $fun();
+ assert('helloworld' === gzinflate($ret));
+ ```
+
+## 1.2.0 (2015-10-23)
+
+* Feature: Invoke close event when closing filter (flush buffer)
+ (#9 by @clue)
+
+## 1.1.0 (2015-10-22)
+
+* Feature: Abort filter operation when catching an Exception
+ (#10 by @clue)
+
+* Feature: Additional safeguards to prevent filter state corruption
+ (#7 by @clue)
+
+## 1.0.0 (2015-10-18)
+
+* First tagged release
diff --git a/vendor/clue/stream-filter/LICENSE b/vendor/clue/stream-filter/LICENSE
new file mode 100644
index 00000000..dc09d1e6
--- /dev/null
+++ b/vendor/clue/stream-filter/LICENSE
@@ -0,0 +1,21 @@
+The MIT License (MIT)
+
+Copyright (c) 2015 Christian Lück
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is furnished
+to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+THE SOFTWARE.
diff --git a/vendor/clue/stream-filter/README.md b/vendor/clue/stream-filter/README.md
new file mode 100644
index 00000000..d46c2b57
--- /dev/null
+++ b/vendor/clue/stream-filter/README.md
@@ -0,0 +1,297 @@
+# clue/stream-filter [![Build Status](https://travis-ci.org/clue/php-stream-filter.svg?branch=master)](https://travis-ci.org/clue/php-stream-filter)
+
+A simple and modern approach to stream filtering in PHP
+
+**Table of contents**
+
+* [Why?](#why)
+* [Usage](#usage)
+ * [append()](#append)
+ * [prepend()](#prepend)
+ * [fun()](#fun)
+ * [remove()](#remove)
+* [Install](#install)
+* [Tests](#tests)
+* [License](#license)
+
+## Why?
+
+PHP's stream filtering system is great!
+
+It offers very powerful stream filtering options and comes with a useful set of built-in filters.
+These filters can be used to easily and efficiently perform various transformations on-the-fly, such as:
+
+* read from a gzip'ed input file,
+* transcode from ISO-8859-1 (Latin1) to UTF-8,
+* write to a bzip output file
+* and much more.
+
+But let's face it:
+Its API is [*difficult to work with*](http://php.net/manual/en/php-user-filter.filter.php)
+and its documentation is [*subpar*](http://stackoverflow.com/questions/27103269/what-is-a-bucket-brigade).
+This combined means its powerful features are often neglected.
+
+This project aims to make these features more accessible to a broader audience.
+* **Lightweight, SOLID design** -
+ Provides a thin abstraction that is [*just good enough*](http://en.wikipedia.org/wiki/Principle_of_good_enough)
+ and does not get in your way.
+ Custom filters require trivial effort.
+* **Good test coverage** -
+ Comes with an automated tests suite and is regularly tested in the *real world*
+
+## Usage
+
+This lightweight library consists only of a few simple functions.
+All functions reside under the `Clue\StreamFilter` namespace.
+
+The below examples assume you use an import statement similar to this:
+
+```php
+use Clue\StreamFilter as Filter;
+
+Filter\append(…);
+```
+
+Alternatively, you can also refer to them with their fully-qualified name:
+
+```php
+\Clue\StreamFilter\append(…);
+```
+
+### append()
+
+The `append($stream, $callback, $read_write = STREAM_FILTER_ALL)` function can be used to
+append a filter callback to the given stream.
+
+Each stream can have a list of filters attached.
+This function appends a filter to the end of this list.
+
+This function returns a filter resource which can be passed to [`remove()`](#remove).
+If the given filter can not be added, it throws an `Exception`.
+
+The `$stream` can be any valid stream resource, such as:
+
+```php
+$stream = fopen('demo.txt', 'w+');
+```
+
+The `$callback` should be a valid callable function which accepts an individual chunk of data
+and should return the updated chunk:
+
+```php
+$filter = Filter\append($stream, function ($chunk) {
+ // will be called each time you read or write a $chunk to/from the stream
+ return $chunk;
+});
+```
+
+As such, you can also use native PHP functions or any other `callable`:
+
+```php
+Filter\append($stream, 'strtoupper');
+
+// will write "HELLO" to the underlying stream
+fwrite($stream, 'hello');
+```
+
+If the `$callback` accepts invocation without parameters, then this signature
+will be invoked once ending (flushing) the filter:
+
+```php
+Filter\append($stream, function ($chunk = null) {
+ if ($chunk === null) {
+ // will be called once ending the filter
+ return 'end';
+ }
+ // will be called each time you read or write a $chunk to/from the stream
+ return $chunk;
+});
+
+fclose($stream);
+```
+
+> Note: Legacy PHP versions (PHP < 5.4) do not support passing additional data
+from the end signal handler if the stream is being closed.
+
+If your callback throws an `Exception`, then the filter process will be aborted.
+In order to play nice with PHP's stream handling, the `Exception` will be
+transformed to a PHP warning instead:
+
+```php
+Filter\append($stream, function ($chunk) {
+ throw new \RuntimeException('Unexpected chunk');
+});
+
+// raises an E_USER_WARNING with "Error invoking filter: Unexpected chunk"
+fwrite($stream, 'hello');
+```
+
+The optional `$read_write` parameter can be used to only invoke the `$callback` when either writing to the stream or only when reading from the stream:
+
+```php
+Filter\append($stream, function ($chunk) {
+ // will be called each time you write to the stream
+ return $chunk;
+}, STREAM_FILTER_WRITE);
+
+Filter\append($stream, function ($chunk) {
+ // will be called each time you read from the stream
+ return $chunk;
+}, STREAM_FILTER_READ);
+```
+
+> Note that once a filter has been added to stream, the stream can no longer be passed to
+> [`stream_select()`](http://php.net/manual/en/function.stream-select.php)
+> (and family).
+>
+> > Warning: stream_select(): cannot cast a filtered stream on this system in {file} on line {line}
+>
+> This is due to limitations of PHP's stream filter support, as it can no longer reliably
+> tell when the underlying stream resource is actually ready.
+> As an alternative, consider calling `stream_select()` on the unfiltered stream and
+> then pass the unfiltered data through the [`fun()`](#fun) function.
+
+### prepend()
+
+The `prepend($stream, $callback, $read_write = STREAM_FILTER_ALL)` function can be used to
+prepend a filter callback to the given stream.
+
+Each stream can have a list of filters attached.
+This function prepends a filter to the start of this list.
+
+This function returns a filter resource which can be passed to [`remove()`](#remove).
+If the given filter can not be added, it throws an `Exception`.
+
+```php
+$filter = Filter\prepend($stream, function ($chunk) {
+ // will be called each time you read or write a $chunk to/from the stream
+ return $chunk;
+});
+```
+
+Except for the position in the list of filters, this function behaves exactly
+like the [`append()`](#append) function.
+For more details about its behavior, see also the [`append()`](#append) function.
+
+### fun()
+
+The `fun($filter, $parameters = null)` function can be used to
+create a filter function which uses the given built-in `$filter`.
+
+PHP comes with a useful set of [built-in filters](http://php.net/manual/en/filters.php).
+Using `fun()` makes accessing these as easy as passing an input string to filter
+and getting the filtered output string.
+
+```php
+$fun = Filter\fun('string.rot13');
+
+assert('grfg' === $fun('test'));
+assert('test' === $fun($fun('test'));
+```
+
+Please note that not all filter functions may be available depending on installed
+PHP extensions and the PHP version in use.
+In particular, [HHVM](http://hhvm.com/) may not offer the same filter functions
+or parameters as Zend PHP.
+Accessing an unknown filter function will result in a `RuntimeException`:
+
+```php
+Filter\fun('unknown'); // throws RuntimeException
+```
+
+Some filters may accept or require additional filter parameters – most
+filters do not require filter parameters.
+If given, the optional `$parameters` argument will be passed to the
+underlying filter handler as-is.
+In particular, note how *not passing* this parameter at all differs from
+explicitly passing a `null` value (which many filters do not accept).
+Please refer to the individual filter definition for more details.
+For example, the `string.strip_tags` filter can be invoked like this:
+
+```php
+$fun = Filter\fun('string.strip_tags', '');
+
+$ret = $fun('h
i');
+assert('hi' === $ret);
+```
+
+Under the hood, this function allocates a temporary memory stream, so it's
+recommended to clean up the filter function after use.
+Also, some filter functions (in particular the
+[zlib compression filters](http://php.net/manual/en/filters.compression.php))
+may use internal buffers and may emit a final data chunk on close.
+The filter function can be closed by invoking without any arguments:
+
+```php
+$fun = Filter\fun('zlib.deflate');
+
+$ret = $fun('hello') . $fun('world') . $fun();
+assert('helloworld' === gzinflate($ret));
+```
+
+The filter function must not be used anymore after it has been closed.
+Doing so will result in a `RuntimeException`:
+
+```php
+$fun = Filter\fun('string.rot13');
+$fun();
+
+$fun('test'); // throws RuntimeException
+```
+
+> Note: If you're using the zlib compression filters, then you should be wary
+about engine inconsistencies between different PHP versions and HHVM.
+These inconsistencies exist in the underlying PHP engines and there's little we
+can do about this in this library.
+[Our test suite](tests/) contains several test cases that exhibit these issues.
+If you feel some test case is missing or outdated, we're happy to accept PRs! :)
+
+### remove()
+
+The `remove($filter)` function can be used to
+remove a filter previously added via [`append()`](#append) or [`prepend()`](#prepend).
+
+```php
+$filter = Filter\append($stream, function () {
+ // …
+});
+Filter\remove($filter);
+```
+
+## Install
+
+The recommended way to install this library is [through Composer](https://getcomposer.org).
+[New to Composer?](https://getcomposer.org/doc/00-intro.md)
+
+This will install the latest supported version:
+
+```bash
+$ composer require clue/stream-filter:^1.4
+```
+
+See also the [CHANGELOG](CHANGELOG.md) for details about version upgrades.
+
+This project aims to run on any platform and thus does not require any PHP
+extensions and supports running on legacy PHP 5.3 through current PHP 7+ and
+HHVM.
+It's *highly recommended to use PHP 7+* for this project.
+Older PHP versions may suffer from a number of inconsistencies documented above.
+
+## Tests
+
+To run the test suite, you first need to clone this repo and then install all
+dependencies [through Composer](http://getcomposer.org):
+
+```bash
+$ composer install
+```
+
+To run the test suite, go to the project root and run:
+
+```bash
+$ php vendor/bin/phpunit
+```
+
+## License
+
+MIT
diff --git a/vendor/clue/stream-filter/composer.json b/vendor/clue/stream-filter/composer.json
new file mode 100644
index 00000000..f8710531
--- /dev/null
+++ b/vendor/clue/stream-filter/composer.json
@@ -0,0 +1,23 @@
+{
+ "name": "clue/stream-filter",
+ "description": "A simple and modern approach to stream filtering in PHP",
+ "keywords": ["stream", "callback", "filter", "php_user_filter", "stream_filter_append", "stream_filter_register", "bucket brigade"],
+ "homepage": "https://github.com/clue/php-stream-filter",
+ "license": "MIT",
+ "authors": [
+ {
+ "name": "Christian Lück",
+ "email": "christian@lueck.tv"
+ }
+ ],
+ "require": {
+ "php": ">=5.3"
+ },
+ "require-dev": {
+ "phpunit/phpunit": "^5.0 || ^4.8"
+ },
+ "autoload": {
+ "psr-4": { "Clue\\StreamFilter\\": "src/" },
+ "files": [ "src/functions.php" ]
+ }
+}
diff --git a/vendor/clue/stream-filter/examples/base64_decode.php b/vendor/clue/stream-filter/examples/base64_decode.php
new file mode 100644
index 00000000..2b49f102
--- /dev/null
+++ b/vendor/clue/stream-filter/examples/base64_decode.php
@@ -0,0 +1,29 @@
+
+
+
+
+
+ ./tests/
+
+
+
+
+ ./src/
+
+
+
\ No newline at end of file
diff --git a/vendor/clue/stream-filter/src/CallbackFilter.php b/vendor/clue/stream-filter/src/CallbackFilter.php
new file mode 100644
index 00000000..710940b6
--- /dev/null
+++ b/vendor/clue/stream-filter/src/CallbackFilter.php
@@ -0,0 +1,120 @@
+closed = false;
+
+ if (!is_callable($this->params)) {
+ throw new InvalidArgumentException('No valid callback parameter given to stream_filter_(append|prepend)');
+ }
+ $this->callback = $this->params;
+
+ // callback supports end event if it accepts invocation without arguments
+ $ref = new ReflectionFunction($this->callback);
+ $this->supportsClose = ($ref->getNumberOfRequiredParameters() === 0);
+
+ return true;
+ }
+
+ public function onClose()
+ {
+ $this->closed = true;
+
+ // callback supports closing and is not already closed
+ if ($this->supportsClose) {
+ $this->supportsClose = false;
+ // invoke without argument to signal end and discard resulting buffer
+ try {
+ call_user_func($this->callback);
+ } catch (Exception $ignored) {
+ // this might be called during engine shutdown, so it's not safe
+ // to raise any errors or exceptions here
+ // trigger_error('Error closing filter: ' . $ignored->getMessage(), E_USER_WARNING);
+ }
+ }
+
+ $this->callback = null;
+ }
+
+ public function filter($in, $out, &$consumed, $closing)
+ {
+ // concatenate whole buffer from input brigade
+ $data = '';
+ while ($bucket = stream_bucket_make_writeable($in)) {
+ $consumed += $bucket->datalen;
+ $data .= $bucket->data;
+ }
+
+ // skip processing callback that already ended
+ if ($this->closed) {
+ return PSFS_FEED_ME;
+ }
+
+ // only invoke filter function if buffer is not empty
+ // this may skip flushing a closing filter
+ if ($data !== '') {
+ try {
+ $data = call_user_func($this->callback, $data);
+ } catch (Exception $e) {
+ // exception should mark filter as closed
+ $this->onClose();
+ trigger_error('Error invoking filter: ' . $e->getMessage(), E_USER_WARNING);
+
+ return PSFS_ERR_FATAL;
+ }
+ }
+
+ // mark filter as closed after processing closing chunk
+ if ($closing) {
+ $this->closed = true;
+
+ // callback supports closing and is not already closed
+ if ($this->supportsClose) {
+ $this->supportsClose = false;
+
+ // invoke without argument to signal end and append resulting buffer
+ try {
+ $data .= call_user_func($this->callback);
+ } catch (Exception $e) {
+ trigger_error('Error ending filter: ' . $e->getMessage(), E_USER_WARNING);
+
+ return PSFS_ERR_FATAL;
+ }
+ }
+ }
+
+ if ($data !== '') {
+ // create a new bucket for writing the resulting buffer to the output brigade
+ // reusing an existing bucket turned out to be bugged in some environments (ancient PHP versions and HHVM)
+ $bucket = @stream_bucket_new($this->stream, $data);
+
+ // legacy PHP versions (PHP < 5.4) do not support passing data from the event signal handler
+ // because closing the stream invalidates the stream and its stream bucket brigade before
+ // invoking the filter close handler.
+ if ($bucket !== false) {
+ stream_bucket_append($out, $bucket);
+ }
+ }
+
+ return PSFS_PASS_ON;
+ }
+}
diff --git a/vendor/clue/stream-filter/src/functions.php b/vendor/clue/stream-filter/src/functions.php
new file mode 100644
index 00000000..d1ca9dc0
--- /dev/null
+++ b/vendor/clue/stream-filter/src/functions.php
@@ -0,0 +1,146 @@
+ '');
+ throw new RuntimeException('Unable to append filter: ' . $error['message']);
+ }
+
+ return $ret;
+}
+
+/**
+ * prepend a callback filter to the given stream
+ *
+ * @param resource $stream
+ * @param callable $callback
+ * @param int $read_write
+ * @return resource filter resource which can be used for `remove()`
+ * @throws Exception on error
+ * @uses stream_filter_prepend()
+ */
+function prepend($stream, $callback, $read_write = STREAM_FILTER_ALL)
+{
+ $ret = @stream_filter_prepend($stream, register(), $read_write, $callback);
+
+ if ($ret === false) {
+ $error = error_get_last() + array('message' => '');
+ throw new RuntimeException('Unable to prepend filter: ' . $error['message']);
+ }
+
+ return $ret;
+}
+
+/**
+ * Creates filter fun (function) which uses the given built-in $filter
+ *
+ * Some filters may accept or require additional filter parameters – most
+ * filters do not require filter parameters.
+ * If given, the optional `$parameters` argument will be passed to the
+ * underlying filter handler as-is.
+ * In particular, note how *not passing* this parameter at all differs from
+ * explicitly passing a `null` value (which many filters do not accept).
+ * Please refer to the individual filter definition for more details.
+ *
+ * @param string $filter built-in filter name. See stream_get_filters() or http://php.net/manual/en/filters.php
+ * @param mixed $parameters (optional) parameters to pass to the built-in filter as-is
+ * @return callable a filter callback which can be append()'ed or prepend()'ed
+ * @throws RuntimeException on error
+ * @link http://php.net/manual/en/filters.php
+ * @see stream_get_filters()
+ * @see append()
+ */
+function fun($filter, $parameters = null)
+{
+ $fp = fopen('php://memory', 'w');
+ if (func_num_args() === 1) {
+ $filter = @stream_filter_append($fp, $filter, STREAM_FILTER_WRITE);
+ } else {
+ $filter = @stream_filter_append($fp, $filter, STREAM_FILTER_WRITE, $parameters);
+ }
+
+ if ($filter === false) {
+ fclose($fp);
+ $error = error_get_last() + array('message' => '');
+ throw new RuntimeException('Unable to access built-in filter: ' . $error['message']);
+ }
+
+ // append filter function which buffers internally
+ $buffer = '';
+ append($fp, function ($chunk) use (&$buffer) {
+ $buffer .= $chunk;
+
+ // always return empty string in order to skip actually writing to stream resource
+ return '';
+ }, STREAM_FILTER_WRITE);
+
+ $closed = false;
+
+ return function ($chunk = null) use ($fp, $filter, &$buffer, &$closed) {
+ if ($closed) {
+ throw new \RuntimeException('Unable to perform operation on closed stream');
+ }
+ if ($chunk === null) {
+ $closed = true;
+ $buffer = '';
+ fclose($fp);
+ return $buffer;
+ }
+ // initialize buffer and invoke filters by attempting to write to stream
+ $buffer = '';
+ fwrite($fp, $chunk);
+
+ // buffer now contains everything the filter function returned
+ return $buffer;
+ };
+}
+
+/**
+ * remove a callback filter from the given stream
+ *
+ * @param resource $filter
+ * @return boolean true on success or false on error
+ * @throws Exception on error
+ * @uses stream_filter_remove()
+ */
+function remove($filter)
+{
+ if (@stream_filter_remove($filter) === false) {
+ throw new RuntimeException('Unable to remove given filter');
+ }
+}
+
+/**
+ * registers the callback filter and returns the resulting filter name
+ *
+ * There should be little reason to call this function manually.
+ *
+ * @return string filter name
+ * @uses CallbackFilter
+ */
+function register()
+{
+ static $registered = null;
+ if ($registered === null) {
+ $registered = 'stream-callback';
+ stream_filter_register($registered, __NAMESPACE__ . '\CallbackFilter');
+ }
+ return $registered;
+}
diff --git a/vendor/clue/stream-filter/tests/FilterTest.php b/vendor/clue/stream-filter/tests/FilterTest.php
new file mode 100644
index 00000000..02aa3a40
--- /dev/null
+++ b/vendor/clue/stream-filter/tests/FilterTest.php
@@ -0,0 +1,386 @@
+createStream();
+
+ StreamFilter\append($stream, function ($chunk) {
+ return strtoupper($chunk);
+ });
+
+ fwrite($stream, 'hello');
+ fwrite($stream, 'world');
+ rewind($stream);
+
+ $this->assertEquals('HELLOWORLD', stream_get_contents($stream));
+
+ fclose($stream);
+ }
+
+ public function testAppendNativePhpFunction()
+ {
+ $stream = $this->createStream();
+
+ StreamFilter\append($stream, 'strtoupper');
+
+ fwrite($stream, 'hello');
+ fwrite($stream, 'world');
+ rewind($stream);
+
+ $this->assertEquals('HELLOWORLD', stream_get_contents($stream));
+
+ fclose($stream);
+ }
+
+ public function testAppendChangingChunkSize()
+ {
+ $stream = $this->createStream();
+
+ StreamFilter\append($stream, function ($chunk) {
+ return str_replace(array('a','e','i','o','u'), '', $chunk);
+ });
+
+ fwrite($stream, 'hello');
+ fwrite($stream, 'world');
+ rewind($stream);
+
+ $this->assertEquals('hllwrld', stream_get_contents($stream));
+
+ fclose($stream);
+ }
+
+ public function testAppendReturningEmptyStringWillNotPassThrough()
+ {
+ $stream = $this->createStream();
+
+ StreamFilter\append($stream, function ($chunk) {
+ return '';
+ });
+
+ fwrite($stream, 'hello');
+ fwrite($stream, 'world');
+ rewind($stream);
+
+ $this->assertEquals('', stream_get_contents($stream));
+
+ fclose($stream);
+ }
+
+ public function testAppendEndEventCanBeBufferedOnClose()
+ {
+ if (PHP_VERSION < 5.4) $this->markTestSkipped('Not supported on legacy PHP');
+
+ $stream = $this->createStream();
+
+ StreamFilter\append($stream, function ($chunk = null) {
+ if ($chunk === null) {
+ // this signals the end event
+ return '!';
+ }
+ return $chunk . ' ';
+ }, STREAM_FILTER_WRITE);
+
+ $buffered = '';
+ StreamFilter\append($stream, function ($chunk) use (&$buffered) {
+ $buffered .= $chunk;
+ return '';
+ });
+
+ fwrite($stream, 'hello');
+ fwrite($stream, 'world');
+
+ fclose($stream);
+
+ $this->assertEquals('hello world !', $buffered);
+ }
+
+ public function testAppendEndEventWillBeCalledOnRemove()
+ {
+ $stream = $this->createStream();
+
+ $ended = false;
+ $filter = StreamFilter\append($stream, function ($chunk = null) use (&$ended) {
+ if ($chunk === null) {
+ $ended = true;
+ }
+ return $chunk;
+ }, STREAM_FILTER_WRITE);
+
+ $this->assertEquals(0, $ended);
+ StreamFilter\remove($filter);
+ $this->assertEquals(1, $ended);
+ }
+
+ public function testAppendEndEventWillBeCalledOnClose()
+ {
+ $stream = $this->createStream();
+
+ $ended = false;
+ StreamFilter\append($stream, function ($chunk = null) use (&$ended) {
+ if ($chunk === null) {
+ $ended = true;
+ }
+ return $chunk;
+ }, STREAM_FILTER_WRITE);
+
+ $this->assertEquals(0, $ended);
+ fclose($stream);
+ $this->assertEquals(1, $ended);
+ }
+
+ public function testAppendWriteOnly()
+ {
+ $stream = $this->createStream();
+
+ $invoked = 0;
+
+ StreamFilter\append($stream, function ($chunk) use (&$invoked) {
+ ++$invoked;
+
+ return $chunk;
+ }, STREAM_FILTER_WRITE);
+
+ fwrite($stream, 'a');
+ fwrite($stream, 'b');
+ fwrite($stream, 'c');
+ rewind($stream);
+
+ $this->assertEquals(3, $invoked);
+ $this->assertEquals('abc', stream_get_contents($stream));
+
+ fclose($stream);
+ }
+
+ public function testAppendReadOnly()
+ {
+ $stream = $this->createStream();
+
+ $invoked = 0;
+
+ StreamFilter\append($stream, function ($chunk) use (&$invoked) {
+ ++$invoked;
+
+ return $chunk;
+ }, STREAM_FILTER_READ);
+
+ fwrite($stream, 'a');
+ fwrite($stream, 'b');
+ fwrite($stream, 'c');
+ rewind($stream);
+
+ $this->assertEquals(0, $invoked);
+ $this->assertEquals('abc', stream_get_contents($stream));
+ $this->assertEquals(1, $invoked);
+
+ fclose($stream);
+ }
+
+ public function testOrderCallingAppendAfterPrepend()
+ {
+ $stream = $this->createStream();
+
+ StreamFilter\append($stream, function ($chunk) {
+ return '[' . $chunk . ']';
+ }, STREAM_FILTER_WRITE);
+
+ StreamFilter\prepend($stream, function ($chunk) {
+ return '(' . $chunk . ')';
+ }, STREAM_FILTER_WRITE);
+
+ fwrite($stream, 'hello');
+ rewind($stream);
+
+ $this->assertEquals('[(hello)]', stream_get_contents($stream));
+
+ fclose($stream);
+ }
+
+ public function testRemoveFilter()
+ {
+ $stream = $this->createStream();
+
+ $first = StreamFilter\append($stream, function ($chunk) {
+ return $chunk . '?';
+ }, STREAM_FILTER_WRITE);
+
+ StreamFilter\append($stream, function ($chunk) {
+ return $chunk . '!';
+ }, STREAM_FILTER_WRITE);
+
+ StreamFilter\remove($first);
+
+ fwrite($stream, 'hello');
+ rewind($stream);
+
+ $this->assertEquals('hello!', stream_get_contents($stream));
+
+ fclose($stream);
+ }
+
+ public function testAppendFunDechunk()
+ {
+ if (defined('HHVM_VERSION')) $this->markTestSkipped('Not supported on HHVM (dechunk filter does not exist)');
+
+ $stream = $this->createStream();
+
+ StreamFilter\append($stream, StreamFilter\fun('dechunk'), STREAM_FILTER_WRITE);
+
+ fwrite($stream, "2\r\nhe\r\n");
+ fwrite($stream, "3\r\nllo\r\n");
+ fwrite($stream, "0\r\n\r\n");
+ rewind($stream);
+
+ $this->assertEquals('hello', stream_get_contents($stream));
+
+ fclose($stream);
+ }
+
+ public function testAppendThrows()
+ {
+ $this->createErrorHandler($errors);
+
+ $stream = $this->createStream();
+ $this->createErrorHandler($errors);
+
+ StreamFilter\append($stream, function ($chunk) {
+ throw new \DomainException($chunk);
+ });
+
+ fwrite($stream, 'test');
+
+ $this->removeErrorHandler();
+ $this->assertCount(1, $errors);
+ $this->assertContains('test', $errors[0]);
+ }
+
+ public function testAppendThrowsDuringEnd()
+ {
+ $stream = $this->createStream();
+ $this->createErrorHandler($errors);
+
+ StreamFilter\append($stream, function ($chunk = null) {
+ if ($chunk === null) {
+ throw new \DomainException('end');
+ }
+ return $chunk;
+ });
+
+ fclose($stream);
+
+ $this->removeErrorHandler();
+
+ // We can only assert we're not seeing an exception here…
+ // * php 5.3-5.6 sees one error here
+ // * php 7 does not see any error here
+ // * hhvm sees the same error twice
+ //
+ // If you're curious:
+ //
+ // var_dump($errors);
+ // $this->assertCount(1, $errors);
+ // $this->assertContains('end', $errors[0]);
+ }
+
+ public function testAppendThrowsShouldTriggerEnd()
+ {
+ $stream = $this->createStream();
+ $this->createErrorHandler($errors);
+
+ $ended = false;
+ StreamFilter\append($stream, function ($chunk = null) use (&$ended) {
+ if ($chunk === null) {
+ $ended = true;
+ return '';
+ }
+ throw new \DomainException($chunk);
+ });
+
+ $this->assertEquals(false, $ended);
+ fwrite($stream, 'test');
+ $this->assertEquals(true, $ended);
+
+ $this->removeErrorHandler();
+ $this->assertCount(1, $errors);
+ $this->assertContains('test', $errors[0]);
+ }
+
+ public function testAppendThrowsShouldTriggerEndButIgnoreExceptionDuringEnd()
+ {
+ //$this->markTestIncomplete();
+ $stream = $this->createStream();
+ $this->createErrorHandler($errors);
+
+ StreamFilter\append($stream, function ($chunk = null) {
+ if ($chunk === null) {
+ $chunk = 'end';
+ //return '';
+ }
+ throw new \DomainException($chunk);
+ });
+
+ fwrite($stream, 'test');
+
+ $this->removeErrorHandler();
+ $this->assertCount(1, $errors);
+ $this->assertContains('test', $errors[0]);
+ }
+
+ /**
+ * @expectedException RuntimeException
+ */
+ public function testAppendInvalidStreamIsRuntimeError()
+ {
+ if (defined('HHVM_VERSION')) $this->markTestSkipped('Not supported on HHVM (does not reject invalid stream)');
+ StreamFilter\append(false, function () { });
+ }
+
+ /**
+ * @expectedException RuntimeException
+ */
+ public function testPrependInvalidStreamIsRuntimeError()
+ {
+ if (defined('HHVM_VERSION')) $this->markTestSkipped('Not supported on HHVM (does not reject invalid stream)');
+ StreamFilter\prepend(false, function () { });
+ }
+
+ /**
+ * @expectedException RuntimeException
+ */
+ public function testRemoveInvalidFilterIsRuntimeError()
+ {
+ if (defined('HHVM_VERSION')) $this->markTestSkipped('Not supported on HHVM (does not reject invalid filters)');
+ StreamFilter\remove(false);
+ }
+
+ /**
+ * @expectedException InvalidArgumentException
+ */
+ public function testInvalidCallbackIsInvalidArgument()
+ {
+ $stream = $this->createStream();
+
+ StreamFilter\append($stream, 'a-b-c');
+ }
+
+ private function createStream()
+ {
+ return fopen('php://memory', 'r+');
+ }
+
+ private function createErrorHandler(&$errors)
+ {
+ $errors = array();
+ set_error_handler(function ($_, $message) use (&$errors) {
+ $errors []= $message;
+ });
+ }
+
+ private function removeErrorHandler()
+ {
+ restore_error_handler();
+ }
+}
diff --git a/vendor/clue/stream-filter/tests/FunTest.php b/vendor/clue/stream-filter/tests/FunTest.php
new file mode 100644
index 00000000..a52668c0
--- /dev/null
+++ b/vendor/clue/stream-filter/tests/FunTest.php
@@ -0,0 +1,44 @@
+assertEquals('grfg', $rot('test'));
+ $this->assertEquals('test', $rot($rot('test')));
+ $this->assertEquals(null, $rot());
+ }
+
+ public function testFunInQuotedPrintable()
+ {
+ $encode = Filter\fun('convert.quoted-printable-encode');
+ $decode = Filter\fun('convert.quoted-printable-decode');
+
+ $this->assertEquals('t=C3=A4st', $encode('täst'));
+ $this->assertEquals('täst', $decode($encode('täst')));
+ $this->assertEquals(null, $encode());
+ }
+
+ /**
+ * @expectedException RuntimeException
+ */
+ public function testFunWriteAfterCloseRot13()
+ {
+ $rot = Filter\fun('string.rot13');
+
+ $this->assertEquals(null, $rot());
+ $rot('test');
+ }
+
+ /**
+ * @expectedException RuntimeException
+ */
+ public function testFunInvalid()
+ {
+ Filter\fun('unknown');
+ }
+}
diff --git a/vendor/clue/stream-filter/tests/FunZlibTest.php b/vendor/clue/stream-filter/tests/FunZlibTest.php
new file mode 100644
index 00000000..752c8a2c
--- /dev/null
+++ b/vendor/clue/stream-filter/tests/FunZlibTest.php
@@ -0,0 +1,79 @@
+assertEquals(gzdeflate('hello world'), $data);
+ }
+
+ public function testFunZlibDeflateEmpty()
+ {
+ if (PHP_VERSION >= 7) $this->markTestSkipped('Not supported on PHP7 (empty string does not invoke filter)');
+
+ $deflate = StreamFilter\fun('zlib.deflate');
+
+ //$data = gzdeflate('');
+ $data = $deflate();
+
+ $this->assertEquals("\x03\x00", $data);
+ }
+
+ public function testFunZlibDeflateBig()
+ {
+ $deflate = StreamFilter\fun('zlib.deflate');
+
+ $n = 1000;
+ $expected = str_repeat('hello', $n);
+
+ $bytes = '';
+ for ($i = 0; $i < $n; ++$i) {
+ $bytes .= $deflate('hello');
+ }
+ $bytes .= $deflate();
+
+ $this->assertEquals($expected, gzinflate($bytes));
+ }
+
+ public function testFunZlibInflateHelloWorld()
+ {
+ $inflate = StreamFilter\fun('zlib.inflate');
+
+ $data = $inflate(gzdeflate('hello world')) . $inflate();
+
+ $this->assertEquals('hello world', $data);
+ }
+
+ public function testFunZlibInflateEmpty()
+ {
+ $inflate = StreamFilter\fun('zlib.inflate');
+
+ $data = $inflate("\x03\x00") . $inflate();
+
+ $this->assertEquals('', $data);
+ }
+
+ public function testFunZlibInflateBig()
+ {
+ if (defined('HHVM_VERSION')) $this->markTestSkipped('Not supported on HHVM (final chunk will not be emitted)');
+
+ $inflate = StreamFilter\fun('zlib.inflate');
+
+ $expected = str_repeat('hello', 10);
+ $bytes = gzdeflate($expected);
+
+ $ret = '';
+ foreach (str_split($bytes, 2) as $chunk) {
+ $ret .= $inflate($chunk);
+ }
+ $ret .= $inflate();
+
+ $this->assertEquals($expected, $ret);
+ }
+}
diff --git a/vendor/composer/autoload_files.php b/vendor/composer/autoload_files.php
index 23cf8ee9..c8fff915 100644
--- a/vendor/composer/autoload_files.php
+++ b/vendor/composer/autoload_files.php
@@ -7,8 +7,10 @@
return array(
'7b11c4dc42b3b3023073cb14e519683c' => $vendorDir . '/ralouphie/getallheaders/src/getallheaders.php',
- 'c964ee0ededf28c96ebd9db5099ef910' => $vendorDir . '/guzzlehttp/promises/src/functions_include.php',
'a0edc8309cc5e1d60e3047b5df6b7052' => $vendorDir . '/guzzlehttp/psr7/src/functions_include.php',
+ 'ddc0a4d7e61c0286f0f8593b1903e894' => $vendorDir . '/clue/stream-filter/src/functions.php',
+ 'c964ee0ededf28c96ebd9db5099ef910' => $vendorDir . '/guzzlehttp/promises/src/functions_include.php',
+ '8cff32064859f4559445b89279f3199c' => $vendorDir . '/php-http/message/src/filters.php',
'37a3dc5111fe8f707ab4c132ef1dbc62' => $vendorDir . '/guzzlehttp/guzzle/src/functions_include.php',
'841780ea2e1d6545ea3a253239d59c05' => $vendorDir . '/qiniu/php-sdk/src/Qiniu/functions.php',
'1cfd2761b63b0a29ed23657ea394cb2d' => $vendorDir . '/topthink/think-captcha/src/helper.php',
diff --git a/vendor/composer/autoload_psr4.php b/vendor/composer/autoload_psr4.php
index e7b950f6..fe9558c4 100644
--- a/vendor/composer/autoload_psr4.php
+++ b/vendor/composer/autoload_psr4.php
@@ -11,12 +11,23 @@
'think\\' => array($vendorDir . '/topthink/think-image/src'),
'app\\' => array($baseDir . '/application'),
'Upyun\\' => array($vendorDir . '/upyun/sdk/src/Upyun'),
+ 'Symfony\\Component\\OptionsResolver\\' => array($vendorDir . '/symfony/options-resolver'),
'Symfony\\Component\\EventDispatcher\\' => array($vendorDir . '/symfony/event-dispatcher'),
'Qiniu\\' => array($vendorDir . '/qiniu/php-sdk/src/Qiniu'),
'Psr\\Http\\Message\\' => array($vendorDir . '/psr/http-message/src'),
+ 'Psr\\Cache\\' => array($vendorDir . '/psr/cache/src'),
'PHPMailer\\PHPMailer\\' => array($vendorDir . '/phpmailer/phpmailer/src'),
'OSS\\' => array($vendorDir . '/aliyuncs/oss-sdk-php/src/OSS'),
+ 'Http\\Promise\\' => array($vendorDir . '/php-http/promise/src'),
+ 'Http\\Message\\' => array($vendorDir . '/php-http/message/src', $vendorDir . '/php-http/message-factory/src'),
+ 'Http\\Discovery\\' => array($vendorDir . '/php-http/discovery/src'),
+ 'Http\\Client\\Common\\Plugin\\' => array($vendorDir . '/php-http/cache-plugin/src'),
+ 'Http\\Client\\Common\\' => array($vendorDir . '/php-http/client-common/src'),
+ 'Http\\Client\\' => array($vendorDir . '/php-http/httplug/src'),
+ 'Http\\Adapter\\Guzzle6\\' => array($vendorDir . '/php-http/guzzle6-adapter/src'),
'GuzzleHttp\\Psr7\\' => array($vendorDir . '/guzzlehttp/psr7/src'),
'GuzzleHttp\\Promise\\' => array($vendorDir . '/guzzlehttp/promises/src'),
'GuzzleHttp\\' => array($vendorDir . '/guzzlehttp/guzzle/src'),
+ 'Github\\' => array($vendorDir . '/knplabs/github-api/lib/Github'),
+ 'Clue\\StreamFilter\\' => array($vendorDir . '/clue/stream-filter/src'),
);
diff --git a/vendor/composer/autoload_static.php b/vendor/composer/autoload_static.php
index 4e4ef451..d13af326 100644
--- a/vendor/composer/autoload_static.php
+++ b/vendor/composer/autoload_static.php
@@ -8,8 +8,10 @@ class ComposerStaticInit04f78adc0d26d025ab398ddde054e232
{
public static $files = array (
'7b11c4dc42b3b3023073cb14e519683c' => __DIR__ . '/..' . '/ralouphie/getallheaders/src/getallheaders.php',
- 'c964ee0ededf28c96ebd9db5099ef910' => __DIR__ . '/..' . '/guzzlehttp/promises/src/functions_include.php',
'a0edc8309cc5e1d60e3047b5df6b7052' => __DIR__ . '/..' . '/guzzlehttp/psr7/src/functions_include.php',
+ 'ddc0a4d7e61c0286f0f8593b1903e894' => __DIR__ . '/..' . '/clue/stream-filter/src/functions.php',
+ 'c964ee0ededf28c96ebd9db5099ef910' => __DIR__ . '/..' . '/guzzlehttp/promises/src/functions_include.php',
+ '8cff32064859f4559445b89279f3199c' => __DIR__ . '/..' . '/php-http/message/src/filters.php',
'37a3dc5111fe8f707ab4c132ef1dbc62' => __DIR__ . '/..' . '/guzzlehttp/guzzle/src/functions_include.php',
'841780ea2e1d6545ea3a253239d59c05' => __DIR__ . '/..' . '/qiniu/php-sdk/src/Qiniu/functions.php',
'1cfd2761b63b0a29ed23657ea394cb2d' => __DIR__ . '/..' . '/topthink/think-captcha/src/helper.php',
@@ -32,6 +34,7 @@ class ComposerStaticInit04f78adc0d26d025ab398ddde054e232
),
'S' =>
array (
+ 'Symfony\\Component\\OptionsResolver\\' => 34,
'Symfony\\Component\\EventDispatcher\\' => 34,
),
'Q' =>
@@ -41,17 +44,33 @@ class ComposerStaticInit04f78adc0d26d025ab398ddde054e232
'P' =>
array (
'Psr\\Http\\Message\\' => 17,
+ 'Psr\\Cache\\' => 10,
'PHPMailer\\PHPMailer\\' => 20,
),
'O' =>
array (
'OSS\\' => 4,
),
+ 'H' =>
+ array (
+ 'Http\\Promise\\' => 13,
+ 'Http\\Message\\' => 13,
+ 'Http\\Discovery\\' => 15,
+ 'Http\\Client\\Common\\Plugin\\' => 26,
+ 'Http\\Client\\Common\\' => 19,
+ 'Http\\Client\\' => 12,
+ 'Http\\Adapter\\Guzzle6\\' => 21,
+ ),
'G' =>
array (
'GuzzleHttp\\Psr7\\' => 16,
'GuzzleHttp\\Promise\\' => 19,
'GuzzleHttp\\' => 11,
+ 'Github\\' => 7,
+ ),
+ 'C' =>
+ array (
+ 'Clue\\StreamFilter\\' => 18,
),
);
@@ -76,6 +95,10 @@ class ComposerStaticInit04f78adc0d26d025ab398ddde054e232
array (
0 => __DIR__ . '/..' . '/upyun/sdk/src/Upyun',
),
+ 'Symfony\\Component\\OptionsResolver\\' =>
+ array (
+ 0 => __DIR__ . '/..' . '/symfony/options-resolver',
+ ),
'Symfony\\Component\\EventDispatcher\\' =>
array (
0 => __DIR__ . '/..' . '/symfony/event-dispatcher',
@@ -88,6 +111,10 @@ class ComposerStaticInit04f78adc0d26d025ab398ddde054e232
array (
0 => __DIR__ . '/..' . '/psr/http-message/src',
),
+ 'Psr\\Cache\\' =>
+ array (
+ 0 => __DIR__ . '/..' . '/psr/cache/src',
+ ),
'PHPMailer\\PHPMailer\\' =>
array (
0 => __DIR__ . '/..' . '/phpmailer/phpmailer/src',
@@ -96,6 +123,35 @@ class ComposerStaticInit04f78adc0d26d025ab398ddde054e232
array (
0 => __DIR__ . '/..' . '/aliyuncs/oss-sdk-php/src/OSS',
),
+ 'Http\\Promise\\' =>
+ array (
+ 0 => __DIR__ . '/..' . '/php-http/promise/src',
+ ),
+ 'Http\\Message\\' =>
+ array (
+ 0 => __DIR__ . '/..' . '/php-http/message/src',
+ 1 => __DIR__ . '/..' . '/php-http/message-factory/src',
+ ),
+ 'Http\\Discovery\\' =>
+ array (
+ 0 => __DIR__ . '/..' . '/php-http/discovery/src',
+ ),
+ 'Http\\Client\\Common\\Plugin\\' =>
+ array (
+ 0 => __DIR__ . '/..' . '/php-http/cache-plugin/src',
+ ),
+ 'Http\\Client\\Common\\' =>
+ array (
+ 0 => __DIR__ . '/..' . '/php-http/client-common/src',
+ ),
+ 'Http\\Client\\' =>
+ array (
+ 0 => __DIR__ . '/..' . '/php-http/httplug/src',
+ ),
+ 'Http\\Adapter\\Guzzle6\\' =>
+ array (
+ 0 => __DIR__ . '/..' . '/php-http/guzzle6-adapter/src',
+ ),
'GuzzleHttp\\Psr7\\' =>
array (
0 => __DIR__ . '/..' . '/guzzlehttp/psr7/src',
@@ -108,6 +164,14 @@ class ComposerStaticInit04f78adc0d26d025ab398ddde054e232
array (
0 => __DIR__ . '/..' . '/guzzlehttp/guzzle/src',
),
+ 'Github\\' =>
+ array (
+ 0 => __DIR__ . '/..' . '/knplabs/github-api/lib/Github',
+ ),
+ 'Clue\\StreamFilter\\' =>
+ array (
+ 0 => __DIR__ . '/..' . '/clue/stream-filter/src',
+ ),
);
public static $prefixesPsr0 = array (
diff --git a/vendor/composer/installed.json b/vendor/composer/installed.json
index cad0b7ec..f864ad9f 100644
--- a/vendor/composer/installed.json
+++ b/vendor/composer/installed.json
@@ -42,6 +42,60 @@
"description": "Aliyun OSS SDK for PHP",
"homepage": "http://www.aliyun.com/product/oss/"
},
+ {
+ "name": "clue/stream-filter",
+ "version": "v1.4.0",
+ "version_normalized": "1.4.0.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/clue/php-stream-filter.git",
+ "reference": "d80fdee9b3a7e0d16fc330a22f41f3ad0eeb09d0"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/clue/php-stream-filter/zipball/d80fdee9b3a7e0d16fc330a22f41f3ad0eeb09d0",
+ "reference": "d80fdee9b3a7e0d16fc330a22f41f3ad0eeb09d0",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=5.3"
+ },
+ "require-dev": {
+ "phpunit/phpunit": "^5.0 || ^4.8"
+ },
+ "time": "2017-08-18T09:54:01+00:00",
+ "type": "library",
+ "installation-source": "dist",
+ "autoload": {
+ "psr-4": {
+ "Clue\\StreamFilter\\": "src/"
+ },
+ "files": [
+ "src/functions.php"
+ ]
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Christian Lück",
+ "email": "christian@lueck.tv"
+ }
+ ],
+ "description": "A simple and modern approach to stream filtering in PHP",
+ "homepage": "https://github.com/clue/php-stream-filter",
+ "keywords": [
+ "bucket brigade",
+ "callback",
+ "filter",
+ "php_user_filter",
+ "stream",
+ "stream_filter_append",
+ "stream_filter_register"
+ ]
+ },
{
"name": "guzzle/guzzle",
"version": "v3.9.3",
@@ -329,6 +383,561 @@
"url"
]
},
+ {
+ "name": "knplabs/github-api",
+ "version": "2.10.1",
+ "version_normalized": "2.10.1.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/KnpLabs/php-github-api.git",
+ "reference": "493423ae7ad1fa9075924cdfb98537828b9e80b5"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/KnpLabs/php-github-api/zipball/493423ae7ad1fa9075924cdfb98537828b9e80b5",
+ "reference": "493423ae7ad1fa9075924cdfb98537828b9e80b5",
+ "shasum": ""
+ },
+ "require": {
+ "php": "^5.6 || ^7.0",
+ "php-http/cache-plugin": "^1.4",
+ "php-http/client-common": "^1.6",
+ "php-http/client-implementation": "^1.0",
+ "php-http/discovery": "^1.0",
+ "php-http/httplug": "^1.1",
+ "psr/cache": "^1.0",
+ "psr/http-message": "^1.0"
+ },
+ "require-dev": {
+ "cache/array-adapter": "^0.4",
+ "guzzlehttp/psr7": "^1.2",
+ "php-http/guzzle6-adapter": "^1.0",
+ "php-http/mock-client": "^1.0",
+ "phpunit/phpunit": "^5.5 || ^6.0"
+ },
+ "time": "2018-09-05T19:12:14+00:00",
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-master": "2.10.x-dev"
+ }
+ },
+ "installation-source": "dist",
+ "autoload": {
+ "psr-4": {
+ "Github\\": "lib/Github/"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Thibault Duplessis",
+ "email": "thibault.duplessis@gmail.com",
+ "homepage": "http://ornicar.github.com"
+ },
+ {
+ "name": "KnpLabs Team",
+ "homepage": "http://knplabs.com"
+ }
+ ],
+ "description": "GitHub API v3 client",
+ "homepage": "https://github.com/KnpLabs/php-github-api",
+ "keywords": [
+ "api",
+ "gh",
+ "gist",
+ "github"
+ ]
+ },
+ {
+ "name": "php-http/cache-plugin",
+ "version": "v1.5.0",
+ "version_normalized": "1.5.0.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/php-http/cache-plugin.git",
+ "reference": "c573ac6ea9b4e33fad567f875b844229d18000b9"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/php-http/cache-plugin/zipball/c573ac6ea9b4e33fad567f875b844229d18000b9",
+ "reference": "c573ac6ea9b4e33fad567f875b844229d18000b9",
+ "shasum": ""
+ },
+ "require": {
+ "php": "^5.4 || ^7.0",
+ "php-http/client-common": "^1.1",
+ "php-http/message-factory": "^1.0",
+ "psr/cache": "^1.0",
+ "symfony/options-resolver": "^2.6 || ^3.0 || ^4.0"
+ },
+ "require-dev": {
+ "henrikbjorn/phpspec-code-coverage": "^1.0",
+ "phpspec/phpspec": "^2.5"
+ },
+ "time": "2017-11-29T20:45:41+00:00",
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-master": "1.5-dev"
+ }
+ },
+ "installation-source": "dist",
+ "autoload": {
+ "psr-4": {
+ "Http\\Client\\Common\\Plugin\\": "src/"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Márk Sági-Kazár",
+ "email": "mark.sagikazar@gmail.com"
+ }
+ ],
+ "description": "PSR-6 Cache plugin for HTTPlug",
+ "homepage": "http://httplug.io",
+ "keywords": [
+ "cache",
+ "http",
+ "httplug",
+ "plugin"
+ ]
+ },
+ {
+ "name": "php-http/client-common",
+ "version": "1.9.0",
+ "version_normalized": "1.9.0.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/php-http/client-common.git",
+ "reference": "9c21b6058caafdf2fcc99a0cabdf31b3ecb33961"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/php-http/client-common/zipball/9c21b6058caafdf2fcc99a0cabdf31b3ecb33961",
+ "reference": "9c21b6058caafdf2fcc99a0cabdf31b3ecb33961",
+ "shasum": ""
+ },
+ "require": {
+ "php": "^5.4 || ^7.0",
+ "php-http/httplug": "^1.1",
+ "php-http/message": "^1.6",
+ "php-http/message-factory": "^1.0",
+ "symfony/options-resolver": "^2.6 || ^3.0 || ^4.0"
+ },
+ "require-dev": {
+ "guzzlehttp/psr7": "^1.4",
+ "phpspec/phpspec": "^2.5 || ^3.4 || ^4.2"
+ },
+ "suggest": {
+ "php-http/cache-plugin": "PSR-6 Cache plugin",
+ "php-http/logger-plugin": "PSR-3 Logger plugin",
+ "php-http/stopwatch-plugin": "Symfony Stopwatch plugin"
+ },
+ "time": "2019-01-03T10:59:55+00:00",
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-master": "1.9.x-dev"
+ }
+ },
+ "installation-source": "dist",
+ "autoload": {
+ "psr-4": {
+ "Http\\Client\\Common\\": "src/"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Márk Sági-Kazár",
+ "email": "mark.sagikazar@gmail.com"
+ }
+ ],
+ "description": "Common HTTP Client implementations and tools for HTTPlug",
+ "homepage": "http://httplug.io",
+ "keywords": [
+ "client",
+ "common",
+ "http",
+ "httplug"
+ ]
+ },
+ {
+ "name": "php-http/discovery",
+ "version": "1.5.2",
+ "version_normalized": "1.5.2.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/php-http/discovery.git",
+ "reference": "ffef11d54171336d841a34816a35bc035fb8cef0"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/php-http/discovery/zipball/ffef11d54171336d841a34816a35bc035fb8cef0",
+ "reference": "ffef11d54171336d841a34816a35bc035fb8cef0",
+ "shasum": ""
+ },
+ "require": {
+ "php": "^5.5 || ^7.0"
+ },
+ "conflict": {
+ "nyholm/psr7": "<1.0"
+ },
+ "require-dev": {
+ "henrikbjorn/phpspec-code-coverage": "^2.0.2",
+ "php-http/httplug": "^1.0|^2.0",
+ "php-http/message-factory": "^1.0",
+ "phpspec/phpspec": "^2.4",
+ "puli/composer-plugin": "1.0.0-beta10"
+ },
+ "suggest": {
+ "php-http/message": "Allow to use Guzzle, Diactoros or Slim Framework factories",
+ "puli/composer-plugin": "Sets up Puli which is recommended for Discovery to work. Check http://docs.php-http.org/en/latest/discovery.html for more details."
+ },
+ "time": "2018-12-31T07:31:26+00:00",
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-master": "1.5-dev"
+ }
+ },
+ "installation-source": "dist",
+ "autoload": {
+ "psr-4": {
+ "Http\\Discovery\\": "src/"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Márk Sági-Kazár",
+ "email": "mark.sagikazar@gmail.com"
+ }
+ ],
+ "description": "Finds installed HTTPlug implementations and PSR-7 message factories",
+ "homepage": "http://php-http.org",
+ "keywords": [
+ "adapter",
+ "client",
+ "discovery",
+ "factory",
+ "http",
+ "message",
+ "psr7"
+ ]
+ },
+ {
+ "name": "php-http/guzzle6-adapter",
+ "version": "v1.1.1",
+ "version_normalized": "1.1.1.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/php-http/guzzle6-adapter.git",
+ "reference": "a56941f9dc6110409cfcddc91546ee97039277ab"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/php-http/guzzle6-adapter/zipball/a56941f9dc6110409cfcddc91546ee97039277ab",
+ "reference": "a56941f9dc6110409cfcddc91546ee97039277ab",
+ "shasum": ""
+ },
+ "require": {
+ "guzzlehttp/guzzle": "^6.0",
+ "php": ">=5.5.0",
+ "php-http/httplug": "^1.0"
+ },
+ "provide": {
+ "php-http/async-client-implementation": "1.0",
+ "php-http/client-implementation": "1.0"
+ },
+ "require-dev": {
+ "ext-curl": "*",
+ "php-http/adapter-integration-tests": "^0.4"
+ },
+ "time": "2016-05-10T06:13:32+00:00",
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-master": "1.2-dev"
+ }
+ },
+ "installation-source": "dist",
+ "autoload": {
+ "psr-4": {
+ "Http\\Adapter\\Guzzle6\\": "src/"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Márk Sági-Kazár",
+ "email": "mark.sagikazar@gmail.com"
+ },
+ {
+ "name": "David de Boer",
+ "email": "david@ddeboer.nl"
+ }
+ ],
+ "description": "Guzzle 6 HTTP Adapter",
+ "homepage": "http://httplug.io",
+ "keywords": [
+ "Guzzle",
+ "http"
+ ]
+ },
+ {
+ "name": "php-http/httplug",
+ "version": "v1.1.0",
+ "version_normalized": "1.1.0.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/php-http/httplug.git",
+ "reference": "1c6381726c18579c4ca2ef1ec1498fdae8bdf018"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/php-http/httplug/zipball/1c6381726c18579c4ca2ef1ec1498fdae8bdf018",
+ "reference": "1c6381726c18579c4ca2ef1ec1498fdae8bdf018",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=5.4",
+ "php-http/promise": "^1.0",
+ "psr/http-message": "^1.0"
+ },
+ "require-dev": {
+ "henrikbjorn/phpspec-code-coverage": "^1.0",
+ "phpspec/phpspec": "^2.4"
+ },
+ "time": "2016-08-31T08:30:17+00:00",
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-master": "1.1-dev"
+ }
+ },
+ "installation-source": "dist",
+ "autoload": {
+ "psr-4": {
+ "Http\\Client\\": "src/"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Eric GELOEN",
+ "email": "geloen.eric@gmail.com"
+ },
+ {
+ "name": "Márk Sági-Kazár",
+ "email": "mark.sagikazar@gmail.com"
+ }
+ ],
+ "description": "HTTPlug, the HTTP client abstraction for PHP",
+ "homepage": "http://httplug.io",
+ "keywords": [
+ "client",
+ "http"
+ ]
+ },
+ {
+ "name": "php-http/message",
+ "version": "1.7.2",
+ "version_normalized": "1.7.2.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/php-http/message.git",
+ "reference": "b159ffe570dffd335e22ef0b91a946eacb182fa1"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/php-http/message/zipball/b159ffe570dffd335e22ef0b91a946eacb182fa1",
+ "reference": "b159ffe570dffd335e22ef0b91a946eacb182fa1",
+ "shasum": ""
+ },
+ "require": {
+ "clue/stream-filter": "^1.4",
+ "php": "^5.4 || ^7.0",
+ "php-http/message-factory": "^1.0.2",
+ "psr/http-message": "^1.0"
+ },
+ "provide": {
+ "php-http/message-factory-implementation": "1.0"
+ },
+ "require-dev": {
+ "akeneo/phpspec-skip-example-extension": "^1.0",
+ "coduo/phpspec-data-provider-extension": "^1.0",
+ "ext-zlib": "*",
+ "guzzlehttp/psr7": "^1.0",
+ "henrikbjorn/phpspec-code-coverage": "^1.0",
+ "phpspec/phpspec": "^2.4",
+ "slim/slim": "^3.0",
+ "zendframework/zend-diactoros": "^1.0"
+ },
+ "suggest": {
+ "ext-zlib": "Used with compressor/decompressor streams",
+ "guzzlehttp/psr7": "Used with Guzzle PSR-7 Factories",
+ "slim/slim": "Used with Slim Framework PSR-7 implementation",
+ "zendframework/zend-diactoros": "Used with Diactoros Factories"
+ },
+ "time": "2018-11-01T09:32:41+00:00",
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-master": "1.6-dev"
+ }
+ },
+ "installation-source": "dist",
+ "autoload": {
+ "psr-4": {
+ "Http\\Message\\": "src/"
+ },
+ "files": [
+ "src/filters.php"
+ ]
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Márk Sági-Kazár",
+ "email": "mark.sagikazar@gmail.com"
+ }
+ ],
+ "description": "HTTP Message related tools",
+ "homepage": "http://php-http.org",
+ "keywords": [
+ "http",
+ "message",
+ "psr-7"
+ ]
+ },
+ {
+ "name": "php-http/message-factory",
+ "version": "v1.0.2",
+ "version_normalized": "1.0.2.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/php-http/message-factory.git",
+ "reference": "a478cb11f66a6ac48d8954216cfed9aa06a501a1"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/php-http/message-factory/zipball/a478cb11f66a6ac48d8954216cfed9aa06a501a1",
+ "reference": "a478cb11f66a6ac48d8954216cfed9aa06a501a1",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=5.4",
+ "psr/http-message": "^1.0"
+ },
+ "time": "2015-12-19T14:08:53+00:00",
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-master": "1.0-dev"
+ }
+ },
+ "installation-source": "dist",
+ "autoload": {
+ "psr-4": {
+ "Http\\Message\\": "src/"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Márk Sági-Kazár",
+ "email": "mark.sagikazar@gmail.com"
+ }
+ ],
+ "description": "Factory interfaces for PSR-7 HTTP Message",
+ "homepage": "http://php-http.org",
+ "keywords": [
+ "factory",
+ "http",
+ "message",
+ "stream",
+ "uri"
+ ]
+ },
+ {
+ "name": "php-http/promise",
+ "version": "v1.0.0",
+ "version_normalized": "1.0.0.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/php-http/promise.git",
+ "reference": "dc494cdc9d7160b9a09bd5573272195242ce7980"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/php-http/promise/zipball/dc494cdc9d7160b9a09bd5573272195242ce7980",
+ "reference": "dc494cdc9d7160b9a09bd5573272195242ce7980",
+ "shasum": ""
+ },
+ "require-dev": {
+ "henrikbjorn/phpspec-code-coverage": "^1.0",
+ "phpspec/phpspec": "^2.4"
+ },
+ "time": "2016-01-26T13:27:02+00:00",
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-master": "1.1-dev"
+ }
+ },
+ "installation-source": "dist",
+ "autoload": {
+ "psr-4": {
+ "Http\\Promise\\": "src/"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Márk Sági-Kazár",
+ "email": "mark.sagikazar@gmail.com"
+ },
+ {
+ "name": "Joel Wurtz",
+ "email": "joel.wurtz@gmail.com"
+ }
+ ],
+ "description": "Promise used for asynchronous HTTP requests",
+ "homepage": "http://httplug.io",
+ "keywords": [
+ "promise"
+ ]
+ },
{
"name": "phpmailer/phpmailer",
"version": "v6.0.6",
@@ -397,6 +1006,54 @@
],
"description": "PHPMailer is a full-featured email creation and transfer class for PHP"
},
+ {
+ "name": "psr/cache",
+ "version": "1.0.1",
+ "version_normalized": "1.0.1.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/php-fig/cache.git",
+ "reference": "d11b50ad223250cf17b86e38383413f5a6764bf8"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/php-fig/cache/zipball/d11b50ad223250cf17b86e38383413f5a6764bf8",
+ "reference": "d11b50ad223250cf17b86e38383413f5a6764bf8",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=5.3.0"
+ },
+ "time": "2016-08-06T20:24:11+00:00",
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-master": "1.0.x-dev"
+ }
+ },
+ "installation-source": "dist",
+ "autoload": {
+ "psr-4": {
+ "Psr\\Cache\\": "src/"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "PHP-FIG",
+ "homepage": "http://www.php-fig.org/"
+ }
+ ],
+ "description": "Common interface for caching libraries",
+ "keywords": [
+ "cache",
+ "psr",
+ "psr-6"
+ ]
+ },
{
"name": "psr/http-message",
"version": "1.0.1",
@@ -654,19 +1311,75 @@
"description": "Symfony EventDispatcher Component",
"homepage": "https://symfony.com"
},
+ {
+ "name": "symfony/options-resolver",
+ "version": "v4.2.2",
+ "version_normalized": "4.2.2.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/symfony/options-resolver.git",
+ "reference": "fbcb106aeee72f3450298bf73324d2cc00d083d1"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/symfony/options-resolver/zipball/fbcb106aeee72f3450298bf73324d2cc00d083d1",
+ "reference": "fbcb106aeee72f3450298bf73324d2cc00d083d1",
+ "shasum": ""
+ },
+ "require": {
+ "php": "^7.1.3"
+ },
+ "time": "2019-01-03T09:07:35+00:00",
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-master": "4.2-dev"
+ }
+ },
+ "installation-source": "dist",
+ "autoload": {
+ "psr-4": {
+ "Symfony\\Component\\OptionsResolver\\": ""
+ },
+ "exclude-from-classmap": [
+ "/Tests/"
+ ]
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Fabien Potencier",
+ "email": "fabien@symfony.com"
+ },
+ {
+ "name": "Symfony Community",
+ "homepage": "https://symfony.com/contributors"
+ }
+ ],
+ "description": "Symfony OptionsResolver Component",
+ "homepage": "https://symfony.com",
+ "keywords": [
+ "config",
+ "configuration",
+ "options"
+ ]
+ },
{
"name": "topthink/framework",
- "version": "v5.1.31",
- "version_normalized": "5.1.31.0",
+ "version": "v5.1.32",
+ "version_normalized": "5.1.32.0",
"source": {
"type": "git",
"url": "https://github.com/top-think/framework.git",
- "reference": "93339b1a4df5a73e0143db0847a4c5e0b2e46fb0"
+ "reference": "88a2ab625b35e047896718db320e08375cf021da"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/top-think/framework/zipball/93339b1a4df5a73e0143db0847a4c5e0b2e46fb0",
- "reference": "93339b1a4df5a73e0143db0847a4c5e0b2e46fb0",
+ "url": "https://api.github.com/repos/top-think/framework/zipball/88a2ab625b35e047896718db320e08375cf021da",
+ "reference": "88a2ab625b35e047896718db320e08375cf021da",
"shasum": ""
},
"require": {
@@ -682,7 +1395,7 @@
"sebastian/phpcpd": "2.*",
"squizlabs/php_codesniffer": "2.*"
},
- "time": "2018-12-09T12:41:21+00:00",
+ "time": "2018-12-23T13:42:11+00:00",
"type": "think-framework",
"installation-source": "dist",
"notification-url": "https://packagist.org/downloads/",
diff --git a/vendor/knplabs/github-api/.editorconfig b/vendor/knplabs/github-api/.editorconfig
new file mode 100644
index 00000000..ed1247ac
--- /dev/null
+++ b/vendor/knplabs/github-api/.editorconfig
@@ -0,0 +1,13 @@
+root = true
+
+[*]
+charset = utf-8
+end_of_line = lf
+insert_final_newline = true
+indent_style = space
+indent_size = 4
+trim_trailing_whitespace = true
+
+[*.yml]
+indent_style = space
+indent_size = 2
diff --git a/vendor/knplabs/github-api/.php_cs b/vendor/knplabs/github-api/.php_cs
new file mode 100644
index 00000000..83782b0f
--- /dev/null
+++ b/vendor/knplabs/github-api/.php_cs
@@ -0,0 +1,14 @@
+in(__DIR__);
+
+$config = PhpCsFixer\Config::create()
+ ->setRiskyAllowed(true)
+ ->setRules([
+
+ ])
+ ->setFinder($finder)
+;
+
+return $config;
diff --git a/vendor/knplabs/github-api/.styleci.yml b/vendor/knplabs/github-api/.styleci.yml
new file mode 100644
index 00000000..504456e7
--- /dev/null
+++ b/vendor/knplabs/github-api/.styleci.yml
@@ -0,0 +1,5 @@
+preset: recommended
+
+disabled:
+ - align_double_arrow
+ - no_multiline_whitespace_before_semicolons
diff --git a/vendor/knplabs/github-api/CHANGELOG.md b/vendor/knplabs/github-api/CHANGELOG.md
new file mode 100644
index 00000000..d370a032
--- /dev/null
+++ b/vendor/knplabs/github-api/CHANGELOG.md
@@ -0,0 +1,249 @@
+# Change Log
+
+The change log describes what is "Added", "Removed", "Changed" or "Fixed" between each release.
+
+## 2.10.1
+
+### Fixed
+
+- Convert the assignee parameter to array to avoid getting a 422 error on github (#738)
+- Fix GraphQL test warnings when they do not assert anything (#735)
+
+### Changed
+
+- Check for BC breaks during the travis build (#734)
+
+## 2.10.0
+
+### Added
+
+- Support for "before" parameter on Notification API (#724)
+
+### Changed
+
+- Allow unspecified `event` when creating review (#723)
+
+### Fixed
+
+- Adjust: installationn access token endpoint (#731)
+- Fixed "get single label" example and add correct example for getting issue's labels (#732)
+- Add comment about `Key` constructor argument (#722)
+
+## 2.9.0
+
+### Added
+
+- API endpoint `Github\Api\Repo::transfer()`
+- API endpoint `Github\Api\Notification::markThreadRead()`
+- API endpoint `Github\Api\Search::topics()`
+
+### Fixed
+
+- Make sure to always reset the "per page" in `Github\ResultPager::fetchAll()`.
+
+## 2.8.0
+
+### Added
+
+- Allow our HTTP plugins to show up in the Symfony web profiler page. (#687)
+- Repository documentation to current user (#671)
+- Add collaborator permission call (#678)
+- Add missing parameters for User/CurrentUser Repositories (#684)
+- Pimp the readme with badge poser (#686)
+
+### Fixed
+
+- Typo in assignee documentation
+- Missing use statement in security example
+- Fixed phpdoc typo (#695)
+- Replace use of deprecated api to the correct one in the security docs (#697)
+
+### Changed
+
+- Updated requirements in readme (#689)
+
+## 2.7.0
+
+### Added
+
+- Phpunit 6 compatibility
+- `Github\Api\AbstractApi::setPage()` to allow you to set the page on all endpoints.
+- Support for query parameters and request headers on `Github\Api\User::following` and `Github\Api\User::followers`
+- API endpoint `Github\Api\CurrentUser\Emails::allPublic()`
+- API endpoint `Github\Api\Search::commits()`
+- API endpoint `Github\Api\Miscellaneous\CodeOfConduct`
+- API endpoint `Github\Api\Repo::topics()`
+- API endpoint `Github\Api\Repo::replaceTopics()`
+
+### Fixed
+
+- Fixed bug in `PathPrepend` plugin where "api/vX" could be duplicated.
+
+### Changed
+
+- Improved documentation and doc blocks
+
+### Removed
+
+- Dropped support for php 5.5
+
+### Deprecated
+
+The following endpoints were deprecated by Github and are also deprecated in the client:
+
+- `Github\Api\Repo::find()`
+- `Github\Api\User::find()`
+- `Github\Api\Issue::find()`
+
+## 2.6.0
+
+### Added
+
+- Support for graphql api [variables](https://developer.github.com/v4/guides/forming-calls/#working-with-variables) (#612)
+- Added missing branch protection methods (#616)
+- Helper function `fromFile ` to get GraphQL queries from a file (#628)
+- Extra parameter `params` to collaborators api calls (#623)
+- Documentation for GitData API (#613)
+
+### Fixed
+- Remove `body` as a required parameter when creating an issue (#624)
+- Minor fixes in example code (#617)
+
+## 2.5.0
+
+### Added
+
+- Stable support for graphql api (V4) (#593)
+- Stable support for apps (previously integrations) (#592)
+- `Repo::events()`
+
+### Fixed
+
+- Incorrect link in repository search docs (#594)
+- Added the required parameter `$message` on `Review::dismiss`.
+
+## 2.4.0
+
+### Added
+
+- `Integrations::configure` to allow accessing early access program endpoints.
+- Add support for pagination and parameters in the pull request comments
+- Add the ability to fetch user installations (`CurrentUser::installations`)
+- Allow getting repo info by id (`Repo::showById`)
+- Allow fetching repositories for a specific installation and user (`CurrentUser::repositoriesByInstallation`)
+
+### Changed
+
+- `PullRequest\Review` and `PullRequest\ReviewRequest` is now part of the official API. No need to call `configure`.
+
+## 2.3.0
+
+### Fixed
+
+- Issue where we serve the wrong cached response. We vary on authorization header now.
+
+### Added
+
+- `PullRequest::status`
+- Throw InvalidArgumentException on `PullRequest::merge` when wrong merge method is used.
+- Added `Protection::configure`
+
+### Changed
+
+- First argument to `Integrations::listRepositories()` is now optional.
+- Moved tests from "functional" to "integration"
+
+## 2.2.0
+
+### Added
+
+- API support for Pull Request Review Requests.
+- API support for Traffic.
+- API support for issue Assignees.
+- API support for Miscellaneous Gitignore and Emojis.
+- Added endpoints for issue lock, unlock and issue label show.
+- Added more parameters to `User::starred`.
+- Fluid interface by allowing `configure()` to return `$this`.
+- `configure()` support for issues API.
+
+### Fixed
+
+- Cache issue where some requests are not cached
+- Issue with `User::all()` creates a query with double question marks.
+
+## 2.1.0
+
+### Added
+
+- Add support for retrieving a single notification info using his ID
+- Add a function to get user organizations
+- Added GraphQL support
+- Add page variable to organization repo list (Organization::repositories())
+- Add support for pull request review.
+- Add support for adding branch protection.
+
+### Fixed
+
+- Bug with double slashes when using enterprise URL.
+- Bug when headers not being passed to request (#529)
+
+## 2.0.0
+
+### Added
+
+- Support for JWT authentication
+- API for Organization\Members
+- API for Integrations
+- API for Repo\Cards
+- API for Repo\Columns
+- API for Repo\Projects
+- API for User\MyRepositories
+- Methods in Repo API for frequency and participation
+
+### Changed
+
+- `ApiLimitExceedException::__construct` has a new second parameter for the remaining API calls.
+- First parameter of `Github\Client` has changed type from `\Http\Client\HttpClient` to
+`Github\HttpClient\Builder`. A factory class was also added. To upgrade you need to change:
+
+```php
+// Old way does not work:
+$github = new Github\Client($httpClient);
+
+// New way will work:
+$github = new Github\Client(new Github\HttpClient\Builder($httpClient));
+$github = Github\Client::createWithHttpClient($httpClient);
+```
+- Renamed the currentuser `DeployKeys` api class to `PublicKeys` to reflect to github api name.
+
+## 2.0.0-rc4
+
+### Added
+
+- HTTPlug to decouple from Guzzle
+- `Github\Client::getLastResponse` was added
+- Support for PSR-6 cache
+- `Github\Client::addPlugin` and `Github\Client::removePlugin`
+- `Github\Client::getApiVersion`
+- `Github\Client::removeCache`
+
+### Changed
+
+- Uses of `Github\HttpClient\HttpClientInterface` is replaced by `Http\Client\HttpClient` ie the constructor of `Github\Client`.
+- We use PSR-7's representation of HTTP message instead of `Guzzle\Http\Message\Response` and `Guzzle\Http\Message\Request`.
+- `Github\Client::addHeaders` was added instead of `Github\Client::setHeaders`
+- Signature of `Github\Client::useCache` has changed. First argument must be a `CacheItemPoolInterface`
+- We use PSR-4 instead of PSR-0
+
+### Removed
+
+- Support for PHP 5.3 and 5.4
+- `Github/HttpClient/HttpClientInterface` was removed
+- `Github/HttpClient/HttpClient` was removed
+- All classes in `Github/HttpClient/HttpClient/Listener/*` were removed
+- `Github/HttpClient/CachedHttpClient` was removed
+- All classes in `Github/HttpClient/Cache/*` were removed
+
+## 1.7.1
+
+No change log before this version
diff --git a/vendor/knplabs/github-api/LICENSE b/vendor/knplabs/github-api/LICENSE
new file mode 100644
index 00000000..0fd8dd8e
--- /dev/null
+++ b/vendor/knplabs/github-api/LICENSE
@@ -0,0 +1,22 @@
+The MIT License
+
+Copyright (c) 2012 KnpLabs
+Copyright (c) 2010 Thibault Duplessis
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+THE SOFTWARE.
diff --git a/vendor/knplabs/github-api/README.md b/vendor/knplabs/github-api/README.md
new file mode 100644
index 00000000..86bcb041
--- /dev/null
+++ b/vendor/knplabs/github-api/README.md
@@ -0,0 +1,111 @@
+# PHP GitHub API
+
+[![Build Status](https://travis-ci.org/KnpLabs/php-github-api.svg?branch=master)](https://travis-ci.org/KnpLabs/php-github-api)
+[![StyleCI](https://styleci.io/repos/3948501/shield?style=flat)](https://styleci.io/repos/3948501)
+[![Latest Stable Version](https://poser.pugx.org/knplabs/github-api/v/stable)](https://packagist.org/packages/knplabs/github-api)
+[![Total Downloads](https://poser.pugx.org/knplabs/github-api/downloads)](https://packagist.org/packages/knplabs/github-api)
+[![Latest Unstable Version](https://poser.pugx.org/knplabs/github-api/v/unstable)](https://packagist.org/packages/knplabs/github-api)
+[![Monthly Downloads](https://poser.pugx.org/knplabs/github-api/d/monthly)](https://packagist.org/packages/knplabs/github-api)
+[![Daily Downloads](https://poser.pugx.org/knplabs/github-api/d/daily)](https://packagist.org/packages/knplabs/github-api)
+
+A simple Object Oriented wrapper for GitHub API, written with PHP5.
+
+Uses [GitHub API v3](http://developer.github.com/v3/) & supports [GitHub API v4](http://developer.github.com/v4). The object API (v3) is very similar to the RESTful API.
+
+## Features
+
+* Light and fast thanks to lazy loading of API classes
+* Extensively tested and documented
+
+## Requirements
+
+* PHP >= 5.6
+* A [HTTP client](https://packagist.org/providers/php-http/client-implementation)
+* A [PSR-7 implementation](https://packagist.org/providers/psr/http-message-implementation)
+* (optional) PHPUnit to run tests.
+
+## Install
+
+Via Composer:
+
+```bash
+$ composer require knplabs/github-api php-http/guzzle6-adapter
+```
+
+Why `php-http/guzzle6-adapter`? We are decoupled from any HTTP messaging client with help by [HTTPlug](http://httplug.io/). Read about clients in our [docs](doc/customize.md).
+
+
+## Using Laravel?
+
+[Laravel GitHub](https://github.com/GrahamCampbell/Laravel-GitHub) by [Graham Campbell](https://github.com/GrahamCampbell) might interest you.
+
+## Basic usage of `php-github-api` client
+
+```php
+api('user')->repositories('ornicar');
+```
+
+From `$client` object, you can access to all GitHub.
+
+## Cache usage
+
+This example uses the PSR6 cache pool [redis-adapter](https://github.com/php-cache/redis-adapter). See http://www.php-cache.com/ for alternatives.
+
+```php
+connect('127.0.0.1', 6379);
+// Create a PSR6 cache pool
+$pool = new RedisCachePool($client);
+
+$client = new \Github\Client();
+$client->addCache($pool);
+
+// Do some request
+
+// Stop using cache
+$client->removeCache();
+```
+
+Using cache, the client will get cached responses if resources haven't changed since last time,
+**without** reaching the `X-Rate-Limit` [imposed by github](http://developer.github.com/v3/#rate-limiting).
+
+
+## Documentation
+
+See the [`doc` directory](doc/) for more detailed documentation.
+
+## License
+
+`php-github-api` is licensed under the MIT License - see the LICENSE file for details
+
+## Credits
+
+### Sponsored by
+
+[![KnpLabs Team](http://knplabs.com/front/images/knp-labs-logo.png)](http://knplabs.com)
+
+### Contributors
+
+- Thanks to [Thibault Duplessis aka. ornicar](http://github.com/ornicar) for his first version of this library.
+- Thanks to [Joseph Bielawski aka. stloyd](http://github.com/stloyd) for his contributions and support.
+- Thanks to [noloh](http://github.com/noloh) for his contribution on the Object API.
+- Thanks to [bshaffer](http://github.com/bshaffer) for his contribution on the Repo API.
+- Thanks to [Rolf van de Krol](http://github.com/rolfvandekrol) for his countless contributions.
+- Thanks to [Nicolas Pastorino](http://github.com/jeanvoye) for his contribution on the Pull Request API.
+- Thanks to [Edoardo Rivello](http://github.com/erivello) for his contribution on the Gists API.
+- Thanks to [Miguel Piedrafita](https://github.com/m1guelpf) for his contribution to the v4 & Apps API.
+
+Thanks to GitHub for the high quality API and documentation.
diff --git a/vendor/knplabs/github-api/composer.json b/vendor/knplabs/github-api/composer.json
new file mode 100644
index 00000000..d3641f47
--- /dev/null
+++ b/vendor/knplabs/github-api/composer.json
@@ -0,0 +1,49 @@
+{
+ "name": "knplabs/github-api",
+ "type": "library",
+ "description": "GitHub API v3 client",
+ "homepage": "https://github.com/KnpLabs/php-github-api",
+ "keywords": ["github", "gh", "api", "gist"],
+ "license": "MIT",
+ "authors": [
+ {
+ "name": "KnpLabs Team",
+ "homepage": "http://knplabs.com"
+ },
+ {
+ "name": "Thibault Duplessis",
+ "email": "thibault.duplessis@gmail.com",
+ "homepage": "http://ornicar.github.com"
+ }
+ ],
+ "require": {
+ "php": "^5.6 || ^7.0",
+ "psr/http-message": "^1.0",
+ "psr/cache": "^1.0",
+ "php-http/httplug": "^1.1",
+ "php-http/discovery": "^1.0",
+ "php-http/client-implementation": "^1.0",
+ "php-http/client-common": "^1.6",
+ "php-http/cache-plugin": "^1.4"
+ },
+ "require-dev": {
+ "phpunit/phpunit": "^5.5 || ^6.0",
+ "php-http/guzzle6-adapter": "^1.0",
+ "php-http/mock-client": "^1.0",
+ "guzzlehttp/psr7": "^1.2",
+ "cache/array-adapter": "^0.4"
+ },
+ "autoload": {
+ "psr-4": { "Github\\": "lib/Github/" }
+ },
+ "autoload-dev": {
+ "psr-4": { "Github\\Tests\\": "test/Github/Tests/"}
+ },
+ "minimum-stability": "dev",
+ "prefer-stable": true,
+ "extra": {
+ "branch-alias": {
+ "dev-master": "2.10.x-dev"
+ }
+ }
+}
diff --git a/vendor/knplabs/github-api/lib/Github/Api/AbstractApi.php b/vendor/knplabs/github-api/lib/Github/Api/AbstractApi.php
new file mode 100644
index 00000000..135ac2b2
--- /dev/null
+++ b/vendor/knplabs/github-api/lib/Github/Api/AbstractApi.php
@@ -0,0 +1,243 @@
+
+ */
+abstract class AbstractApi implements ApiInterface
+{
+ /**
+ * The client.
+ *
+ * @var Client
+ */
+ protected $client;
+
+ /**
+ * The requested page (GitHub pagination).
+ *
+ * @var null|int
+ */
+ private $page;
+
+ /**
+ * Number of items per page (GitHub pagination).
+ *
+ * @var null|int
+ */
+ protected $perPage;
+
+ /**
+ * @param Client $client
+ */
+ public function __construct(Client $client)
+ {
+ $this->client = $client;
+ }
+
+ public function configure()
+ {
+ }
+
+ /**
+ * @return null|int
+ */
+ public function getPage()
+ {
+ return $this->page;
+ }
+
+ /**
+ * @param null|int $page
+ */
+ public function setPage($page)
+ {
+ $this->page = (null === $page ? $page : (int) $page);
+
+ return $this;
+ }
+
+ /**
+ * @return null|int
+ */
+ public function getPerPage()
+ {
+ return $this->perPage;
+ }
+
+ /**
+ * @param null|int $perPage
+ */
+ public function setPerPage($perPage)
+ {
+ $this->perPage = (null === $perPage ? $perPage : (int) $perPage);
+
+ return $this;
+ }
+
+ /**
+ * Send a GET request with query parameters.
+ *
+ * @param string $path Request path.
+ * @param array $parameters GET parameters.
+ * @param array $requestHeaders Request Headers.
+ *
+ * @return array|string
+ */
+ protected function get($path, array $parameters = [], array $requestHeaders = [])
+ {
+ if (null !== $this->page && !isset($parameters['page'])) {
+ $parameters['page'] = $this->page;
+ }
+ if (null !== $this->perPage && !isset($parameters['per_page'])) {
+ $parameters['per_page'] = $this->perPage;
+ }
+ if (array_key_exists('ref', $parameters) && is_null($parameters['ref'])) {
+ unset($parameters['ref']);
+ }
+
+ if (count($parameters) > 0) {
+ $path .= '?'.http_build_query($parameters);
+ }
+
+ $response = $this->client->getHttpClient()->get($path, $requestHeaders);
+
+ return ResponseMediator::getContent($response);
+ }
+
+ /**
+ * Send a HEAD request with query parameters.
+ *
+ * @param string $path Request path.
+ * @param array $parameters HEAD parameters.
+ * @param array $requestHeaders Request headers.
+ *
+ * @return \Psr\Http\Message\ResponseInterface
+ */
+ protected function head($path, array $parameters = [], array $requestHeaders = [])
+ {
+ if (array_key_exists('ref', $parameters) && is_null($parameters['ref'])) {
+ unset($parameters['ref']);
+ }
+
+ $response = $this->client->getHttpClient()->head($path.'?'.http_build_query($parameters), $requestHeaders);
+
+ return $response;
+ }
+
+ /**
+ * Send a POST request with JSON-encoded parameters.
+ *
+ * @param string $path Request path.
+ * @param array $parameters POST parameters to be JSON encoded.
+ * @param array $requestHeaders Request headers.
+ *
+ * @return array|string
+ */
+ protected function post($path, array $parameters = [], array $requestHeaders = [])
+ {
+ return $this->postRaw(
+ $path,
+ $this->createJsonBody($parameters),
+ $requestHeaders
+ );
+ }
+
+ /**
+ * Send a POST request with raw data.
+ *
+ * @param string $path Request path.
+ * @param string $body Request body.
+ * @param array $requestHeaders Request headers.
+ *
+ * @return array|string
+ */
+ protected function postRaw($path, $body, array $requestHeaders = [])
+ {
+ $response = $this->client->getHttpClient()->post(
+ $path,
+ $requestHeaders,
+ $body
+ );
+
+ return ResponseMediator::getContent($response);
+ }
+
+ /**
+ * Send a PATCH request with JSON-encoded parameters.
+ *
+ * @param string $path Request path.
+ * @param array $parameters POST parameters to be JSON encoded.
+ * @param array $requestHeaders Request headers.
+ *
+ * @return array|string
+ */
+ protected function patch($path, array $parameters = [], array $requestHeaders = [])
+ {
+ $response = $this->client->getHttpClient()->patch(
+ $path,
+ $requestHeaders,
+ $this->createJsonBody($parameters)
+ );
+
+ return ResponseMediator::getContent($response);
+ }
+
+ /**
+ * Send a PUT request with JSON-encoded parameters.
+ *
+ * @param string $path Request path.
+ * @param array $parameters POST parameters to be JSON encoded.
+ * @param array $requestHeaders Request headers.
+ *
+ * @return array|string
+ */
+ protected function put($path, array $parameters = [], array $requestHeaders = [])
+ {
+ $response = $this->client->getHttpClient()->put(
+ $path,
+ $requestHeaders,
+ $this->createJsonBody($parameters)
+ );
+
+ return ResponseMediator::getContent($response);
+ }
+
+ /**
+ * Send a DELETE request with JSON-encoded parameters.
+ *
+ * @param string $path Request path.
+ * @param array $parameters POST parameters to be JSON encoded.
+ * @param array $requestHeaders Request headers.
+ *
+ * @return array|string
+ */
+ protected function delete($path, array $parameters = [], array $requestHeaders = [])
+ {
+ $response = $this->client->getHttpClient()->delete(
+ $path,
+ $requestHeaders,
+ $this->createJsonBody($parameters)
+ );
+
+ return ResponseMediator::getContent($response);
+ }
+
+ /**
+ * Create a JSON encoded version of an array of parameters.
+ *
+ * @param array $parameters Request parameters
+ *
+ * @return null|string
+ */
+ protected function createJsonBody(array $parameters)
+ {
+ return (count($parameters) === 0) ? null : json_encode($parameters, empty($parameters) ? JSON_FORCE_OBJECT : 0);
+ }
+}
diff --git a/vendor/knplabs/github-api/lib/Github/Api/AcceptHeaderTrait.php b/vendor/knplabs/github-api/lib/Github/Api/AcceptHeaderTrait.php
new file mode 100644
index 00000000..4a7e7a46
--- /dev/null
+++ b/vendor/knplabs/github-api/lib/Github/Api/AcceptHeaderTrait.php
@@ -0,0 +1,63 @@
+
+ */
+trait AcceptHeaderTrait
+{
+ protected $acceptHeaderValue = null;
+
+ protected function get($path, array $parameters = [], array $requestHeaders = [])
+ {
+ return parent::get($path, $parameters, $this->mergeHeaders($requestHeaders));
+ }
+
+ protected function head($path, array $parameters = [], array $requestHeaders = [])
+ {
+ return parent::head($path, $parameters, $this->mergeHeaders($requestHeaders));
+ }
+
+ protected function post($path, array $parameters = [], array $requestHeaders = [])
+ {
+ return parent::post($path, $parameters, $this->mergeHeaders($requestHeaders));
+ }
+
+ protected function postRaw($path, $body, array $requestHeaders = [])
+ {
+ return parent::postRaw($path, $body, $this->mergeHeaders($requestHeaders));
+ }
+
+ protected function patch($path, array $parameters = [], array $requestHeaders = [])
+ {
+ return parent::patch($path, $parameters, $this->mergeHeaders($requestHeaders));
+ }
+
+ protected function put($path, array $parameters = [], array $requestHeaders = [])
+ {
+ return parent::put($path, $parameters, $this->mergeHeaders($requestHeaders));
+ }
+
+ protected function delete($path, array $parameters = [], array $requestHeaders = [])
+ {
+ return parent::delete($path, $parameters, $this->mergeHeaders($requestHeaders));
+ }
+
+ /**
+ * Append a new accept header on all requests.
+ *
+ * @return array
+ */
+ private function mergeHeaders(array $headers = [])
+ {
+ $default = [];
+ if ($this->acceptHeaderValue) {
+ $default = ['Accept' => $this->acceptHeaderValue];
+ }
+
+ return array_merge($default, $headers);
+ }
+}
diff --git a/vendor/knplabs/github-api/lib/Github/Api/ApiInterface.php b/vendor/knplabs/github-api/lib/Github/Api/ApiInterface.php
new file mode 100644
index 00000000..49d5167c
--- /dev/null
+++ b/vendor/knplabs/github-api/lib/Github/Api/ApiInterface.php
@@ -0,0 +1,15 @@
+
+ */
+interface ApiInterface
+{
+ public function getPerPage();
+
+ public function setPerPage($perPage);
+}
diff --git a/vendor/knplabs/github-api/lib/Github/Api/Apps.php b/vendor/knplabs/github-api/lib/Github/Api/Apps.php
new file mode 100644
index 00000000..1467d2aa
--- /dev/null
+++ b/vendor/knplabs/github-api/lib/Github/Api/Apps.php
@@ -0,0 +1,93 @@
+
+ */
+class Apps extends AbstractApi
+{
+ /**
+ * Create an access token for an installation.
+ *
+ * @param int $installationId An integration installation id
+ * @param int $userId An optional user id on behalf of whom the
+ * token will be requested
+ *
+ * @link https://developer.github.com/v3/apps/#create-a-new-installation-token
+ *
+ * @return array token and token metadata
+ */
+ public function createInstallationToken($installationId, $userId = null)
+ {
+ $parameters = [];
+ if ($userId) {
+ $parameters['user_id'] = $userId;
+ }
+
+ return $this->post('/app/installations/'.rawurlencode($installationId).'/access_tokens', $parameters);
+ }
+
+ /**
+ * Find all installations for the authenticated application.
+ *
+ * @link https://developer.github.com/v3/apps/#find-installations
+ *
+ * @return array
+ */
+ public function findInstallations()
+ {
+ return $this->get('/app/installations');
+ }
+
+ /**
+ * List repositories that are accessible to the authenticated installation.
+ *
+ * @link https://developer.github.com/v3/apps/installations/#list-repositories
+ *
+ * @param int $userId
+ *
+ * @return array
+ */
+ public function listRepositories($userId = null)
+ {
+ $parameters = [];
+ if ($userId) {
+ $parameters['user_id'] = $userId;
+ }
+
+ return $this->get('/installation/repositories', $parameters);
+ }
+
+ /**
+ * Add a single repository to an installation.
+ *
+ * @link https://developer.github.com/v3/apps/installations/#add-repository-to-installation
+ *
+ * @param int $installationId
+ * @param int $repositoryId
+ *
+ * @return array
+ */
+ public function addRepository($installationId, $repositoryId)
+ {
+ return $this->put('/installations/'.rawurlencode($installationId).'/repositories/'.rawurlencode($repositoryId));
+ }
+
+ /**
+ * Remove a single repository from an installation.
+ *
+ * @link https://developer.github.com/v3/apps/installations/#remove-repository-from-installation
+ *
+ * @param int $installationId
+ * @param int $repositoryId
+ *
+ * @return array
+ */
+ public function removeRepository($installationId, $repositoryId)
+ {
+ return $this->delete('/installations/'.rawurlencode($installationId).'/repositories/'.rawurlencode($repositoryId));
+ }
+}
diff --git a/vendor/knplabs/github-api/lib/Github/Api/Authorizations.php b/vendor/knplabs/github-api/lib/Github/Api/Authorizations.php
new file mode 100644
index 00000000..fd8e9b23
--- /dev/null
+++ b/vendor/knplabs/github-api/lib/Github/Api/Authorizations.php
@@ -0,0 +1,122 @@
+
+ */
+class Authorizations extends AbstractApi
+{
+ /**
+ * List all authorizations.
+ *
+ * @return array
+ */
+ public function all()
+ {
+ return $this->get('/authorizations');
+ }
+
+ /**
+ * Show a single authorization.
+ *
+ * @param $clientId
+ *
+ * @return array
+ */
+ public function show($clientId)
+ {
+ return $this->get('/authorizations/'.rawurlencode($clientId));
+ }
+
+ /**
+ * Create an authorization.
+ *
+ * @param array $params
+ * @param null $OTPCode
+ *
+ * @return array
+ */
+ public function create(array $params, $OTPCode = null)
+ {
+ $headers = null === $OTPCode ? [] : ['X-GitHub-OTP' => $OTPCode];
+
+ return $this->post('/authorizations', $params, $headers);
+ }
+
+ /**
+ * Update an authorization.
+ *
+ * @param $clientId
+ * @param array $params
+ *
+ * @return array
+ */
+ public function update($clientId, array $params)
+ {
+ return $this->patch('/authorizations/'.rawurlencode($clientId), $params);
+ }
+
+ /**
+ * Remove an authorization.
+ *
+ * @param $clientId
+ *
+ * @return array
+ */
+ public function remove($clientId)
+ {
+ return $this->delete('/authorizations/'.rawurlencode($clientId));
+ }
+
+ /**
+ * Check an authorization.
+ *
+ * @param $clientId
+ * @param $token
+ *
+ * @return array
+ */
+ public function check($clientId, $token)
+ {
+ return $this->get('/applications/'.rawurlencode($clientId).'/tokens/'.rawurlencode($token));
+ }
+
+ /**
+ * Reset an authorization.
+ *
+ * @param $clientId
+ * @param $token
+ *
+ * @return array
+ */
+ public function reset($clientId, $token)
+ {
+ return $this->post('/applications/'.rawurlencode($clientId).'/tokens/'.rawurlencode($token));
+ }
+
+ /**
+ * Remove an authorization.
+ *
+ * @param $clientId
+ * @param $token
+ */
+ public function revoke($clientId, $token)
+ {
+ $this->delete('/applications/'.rawurlencode($clientId).'/tokens/'.rawurlencode($token));
+ }
+
+ /**
+ * Revoke all authorizations.
+ *
+ * @param $clientId
+ */
+ public function revokeAll($clientId)
+ {
+ $this->delete('/applications/'.rawurlencode($clientId).'/tokens');
+ }
+}
diff --git a/vendor/knplabs/github-api/lib/Github/Api/CurrentUser.php b/vendor/knplabs/github-api/lib/Github/Api/CurrentUser.php
new file mode 100644
index 00000000..0c858b07
--- /dev/null
+++ b/vendor/knplabs/github-api/lib/Github/Api/CurrentUser.php
@@ -0,0 +1,207 @@
+
+ * @author Felipe Valtl de Mello
+ */
+class CurrentUser extends AbstractApi
+{
+ public function show()
+ {
+ return $this->get('/user');
+ }
+
+ public function update(array $params)
+ {
+ return $this->patch('/user', $params);
+ }
+
+ /**
+ * @return Emails
+ */
+ public function emails()
+ {
+ return new Emails($this->client);
+ }
+
+ /**
+ * @return Followers
+ */
+ public function follow()
+ {
+ return new Followers($this->client);
+ }
+
+ public function followers($page = 1)
+ {
+ return $this->get('/user/followers', [
+ 'page' => $page,
+ ]);
+ }
+
+ /**
+ * @link http://developer.github.com/v3/issues/#list-issues
+ *
+ * @param array $params
+ * @param bool $includeOrgIssues
+ *
+ * @return array
+ */
+ public function issues(array $params = [], $includeOrgIssues = true)
+ {
+ return $this->get($includeOrgIssues ? '/issues' : '/user/issues', array_merge(['page' => 1], $params));
+ }
+
+ /**
+ * @return PublicKeys
+ */
+ public function keys()
+ {
+ return new PublicKeys($this->client);
+ }
+
+ /**
+ * @return Notifications
+ */
+ public function notifications()
+ {
+ return new Notifications($this->client);
+ }
+
+ /**
+ * @return Memberships
+ */
+ public function memberships()
+ {
+ return new Memberships($this->client);
+ }
+
+ /**
+ * @link http://developer.github.com/v3/orgs/#list-user-organizations
+ *
+ * @return array
+ */
+ public function organizations()
+ {
+ return $this->get('/user/orgs');
+ }
+
+ /**
+ * @link https://developer.github.com/v3/orgs/teams/#list-user-teams
+ *
+ * @return array
+ */
+ public function teams()
+ {
+ return $this->get('/user/teams');
+ }
+
+ /**
+ * @link http://developer.github.com/v3/repos/#list-your-repositories
+ *
+ * @param string $type role in the repository
+ * @param string $sort sort by
+ * @param string $direction direction of sort, asc or desc
+ * @param string $visibility visibility of repository
+ * @param string $affiliation relationship to repository
+ *
+ * @return array
+ */
+ public function repositories($type = 'owner', $sort = 'full_name', $direction = 'asc', $visibility = null, $affiliation = null)
+ {
+ $params = [
+ 'type' => $type,
+ 'sort' => $sort,
+ 'direction' => $direction,
+ ];
+
+ if (null !== $visibility) {
+ unset($params['type']);
+ $params['visibility'] = $visibility;
+ }
+
+ if (null !== $affiliation) {
+ unset($params['type']);
+ $params['affiliation'] = $affiliation;
+ }
+
+ return $this->get('/user/repos', $params);
+ }
+
+ /**
+ * @return Watchers
+ */
+ public function watchers()
+ {
+ return new Watchers($this->client);
+ }
+
+ /**
+ * @deprecated Use watchers() instead
+ */
+ public function watched($page = 1)
+ {
+ return $this->get('/user/watched', [
+ 'page' => $page,
+ ]);
+ }
+
+ /**
+ * @return Starring
+ */
+ public function starring()
+ {
+ return new Starring($this->client);
+ }
+
+ /**
+ * @deprecated Use starring() instead
+ */
+ public function starred($page = 1)
+ {
+ return $this->get('/user/starred', [
+ 'page' => $page,
+ ]);
+ }
+
+ /**
+ * @link https://developer.github.com/v3/activity/watching/#list-repositories-being-watched
+ */
+ public function subscriptions()
+ {
+ return $this->get('/user/subscriptions');
+ }
+
+ /**
+ * @link https://developer.github.com/v3/integrations/#list-installations-for-user
+ *
+ * @param array $params
+ */
+ public function installations(array $params = [])
+ {
+ return $this->get('/user/installations', array_merge(['page' => 1], $params));
+ }
+
+ /**
+ * @link https://developer.github.com/v3/integrations/installations/#list-repositories-accessible-to-the-user-for-an-installation
+ *
+ * @param string $installationId the ID of the Installation
+ * @param array $params
+ */
+ public function repositoriesByInstallation($installationId, array $params = [])
+ {
+ return $this->get(sprintf('/user/installations/%s/repositories', $installationId), array_merge(['page' => 1], $params));
+ }
+}
diff --git a/vendor/knplabs/github-api/lib/Github/Api/CurrentUser/Emails.php b/vendor/knplabs/github-api/lib/Github/Api/CurrentUser/Emails.php
new file mode 100644
index 00000000..15d4fad0
--- /dev/null
+++ b/vendor/knplabs/github-api/lib/Github/Api/CurrentUser/Emails.php
@@ -0,0 +1,94 @@
+
+ */
+class Emails extends AbstractApi
+{
+ /**
+ * List emails for the authenticated user.
+ *
+ * @link http://developer.github.com/v3/users/emails/
+ *
+ * @return array
+ */
+ public function all()
+ {
+ return $this->get('/user/emails');
+ }
+
+ /**
+ * List public email addresses for a user.
+ *
+ * @link https://developer.github.com/v3/users/emails/#list-public-email-addresses-for-a-user
+ *
+ * @return array
+ */
+ public function allPublic()
+ {
+ return $this->get('/user/public_emails');
+ }
+
+ /**
+ * Adds one or more email for the authenticated user.
+ *
+ * @link http://developer.github.com/v3/users/emails/
+ *
+ * @param string|array $emails
+ *
+ * @throws \Github\Exception\InvalidArgumentException
+ *
+ * @return array
+ */
+ public function add($emails)
+ {
+ if (is_string($emails)) {
+ $emails = [$emails];
+ } elseif (0 === count($emails)) {
+ throw new InvalidArgumentException();
+ }
+
+ return $this->post('/user/emails', $emails);
+ }
+
+ /**
+ * Removes one or more email for the authenticated user.
+ *
+ * @link http://developer.github.com/v3/users/emails/
+ *
+ * @param string|array $emails
+ *
+ * @throws \Github\Exception\InvalidArgumentException
+ *
+ * @return array
+ */
+ public function remove($emails)
+ {
+ if (is_string($emails)) {
+ $emails = [$emails];
+ } elseif (0 === count($emails)) {
+ throw new InvalidArgumentException();
+ }
+
+ return $this->delete('/user/emails', $emails);
+ }
+
+ /**
+ * Toggle primary email visibility.
+ *
+ * @link https://developer.github.com/v3/users/emails/#toggle-primary-email-visibility
+ *
+ * @return array
+ */
+ public function toggleVisibility()
+ {
+ return $this->patch('/user/email/visibility');
+ }
+}
diff --git a/vendor/knplabs/github-api/lib/Github/Api/CurrentUser/Followers.php b/vendor/knplabs/github-api/lib/Github/Api/CurrentUser/Followers.php
new file mode 100644
index 00000000..52a712ca
--- /dev/null
+++ b/vendor/knplabs/github-api/lib/Github/Api/CurrentUser/Followers.php
@@ -0,0 +1,71 @@
+
+ */
+class Followers extends AbstractApi
+{
+ /**
+ * List followed users by the authenticated user.
+ *
+ * @link http://developer.github.com/v3/repos/followers/
+ *
+ * @param int $page
+ *
+ * @return array
+ */
+ public function all($page = 1)
+ {
+ return $this->get('/user/following', [
+ 'page' => $page,
+ ]);
+ }
+
+ /**
+ * Check that the authenticated user follows a user.
+ *
+ * @link http://developer.github.com/v3/repos/followers/
+ *
+ * @param string $username the username to follow
+ *
+ * @return array
+ */
+ public function check($username)
+ {
+ return $this->get('/user/following/'.rawurlencode($username));
+ }
+
+ /**
+ * Make the authenticated user follow a user.
+ *
+ * @link http://developer.github.com/v3/repos/followers/
+ *
+ * @param string $username the username to follow
+ *
+ * @return array
+ */
+ public function follow($username)
+ {
+ return $this->put('/user/following/'.rawurlencode($username));
+ }
+
+ /**
+ * Make the authenticated user un-follow a user.
+ *
+ * @link http://developer.github.com/v3/repos/followers/
+ *
+ * @param string $username the username to un-follow
+ *
+ * @return array
+ */
+ public function unfollow($username)
+ {
+ return $this->delete('/user/following/'.rawurlencode($username));
+ }
+}
diff --git a/vendor/knplabs/github-api/lib/Github/Api/CurrentUser/Memberships.php b/vendor/knplabs/github-api/lib/Github/Api/CurrentUser/Memberships.php
new file mode 100644
index 00000000..da727397
--- /dev/null
+++ b/vendor/knplabs/github-api/lib/Github/Api/CurrentUser/Memberships.php
@@ -0,0 +1,48 @@
+get('/user/memberships/orgs');
+ }
+
+ /**
+ * Get your organization membership.
+ *
+ * @link https://developer.github.com/v3/orgs/members/#get-your-organization-membership
+ *
+ * @param string $organization
+ *
+ * @return array
+ */
+ public function organization($organization)
+ {
+ return $this->get('/user/memberships/orgs/'.rawurlencode($organization));
+ }
+
+ /**
+ * Edit your organization membership.
+ *
+ * @link https://developer.github.com/v3/orgs/members/#edit-your-organization-membership
+ *
+ * @param string $organization
+ *
+ * @return array
+ */
+ public function edit($organization)
+ {
+ return $this->patch('/user/memberships/orgs/'.rawurlencode($organization), ['state' => 'active']);
+ }
+}
diff --git a/vendor/knplabs/github-api/lib/Github/Api/CurrentUser/Notifications.php b/vendor/knplabs/github-api/lib/Github/Api/CurrentUser/Notifications.php
new file mode 100644
index 00000000..36dfb57e
--- /dev/null
+++ b/vendor/knplabs/github-api/lib/Github/Api/CurrentUser/Notifications.php
@@ -0,0 +1,145 @@
+
+ */
+class Notifications extends AbstractApi
+{
+ /**
+ * List all notifications for the authenticated user.
+ *
+ * @link http://developer.github.com/v3/activity/notifications/#list-your-notifications
+ *
+ * @param array $params
+ *
+ * @return array
+ */
+ public function all(array $params = [])
+ {
+ return $this->get('/notifications', $params);
+ }
+
+ /**
+ * List all notifications for the authenticated user in selected repository.
+ *
+ * @link http://developer.github.com/v3/activity/notifications/#list-your-notifications-in-a-repository
+ *
+ * @param string $username the user who owns the repo
+ * @param string $repository the name of the repo
+ * @param array $params
+ *
+ * @return array
+ */
+ public function allInRepository($username, $repository, array $params = [])
+ {
+ return $this->get('/repos/'.rawurlencode($username).'/'.rawurlencode($repository).'/notifications', $params);
+ }
+
+ /**
+ * Mark all notifications as read.
+ *
+ * @link http://developer.github.com/v3/activity/notifications/#mark-as-read
+ *
+ * @param array $params
+ *
+ * @return array
+ */
+ public function markAsReadAll(array $params = [])
+ {
+ return $this->put('/notifications', $params);
+ }
+
+ /**
+ * Mark all notifications for a repository as read.
+ *
+ * @link http://developer.github.com/v3/activity/notifications/#mark-notifications-as-read-in-a-repository
+ *
+ * @param string $username the user who owns the repo
+ * @param string $repository the name of the repo
+ * @param array $params
+ *
+ * @return array
+ */
+ public function markAsReadInRepository($username, $repository, array $params = [])
+ {
+ return $this->put('/repos/'.rawurlencode($username).'/'.rawurlencode($repository).'/notifications', $params);
+ }
+
+ /**
+ * Mark a notification as read.
+ *
+ * @link http://developer.github.com/v3/activity/notifications/#mark-a-thread-as-read
+ *
+ * @param int $id the notification number
+ * @param array $params
+ *
+ * @return array
+ */
+ public function markAsRead($id, array $params)
+ {
+ return $this->patch('/notifications/threads/'.rawurlencode($id), $params);
+ }
+
+ /**
+ * Show a notification.
+ *
+ * @link http://developer.github.com/v3/activity/notifications/#view-a-single-thread
+ *
+ * @param int $id the notification number
+ *
+ * @return array
+ */
+ public function show($id)
+ {
+ return $this->get('/notifications/threads/'.rawurlencode($id));
+ }
+
+ /**
+ * Show a subscription.
+ *
+ * @link http://developer.github.com/v3/activity/notifications/#get-a-thread-subscription
+ *
+ * @param int $id the notification number
+ *
+ * @return array
+ */
+ public function showSubscription($id)
+ {
+ return $this->get('/notifications/threads/'.rawurlencode($id).'/subscription');
+ }
+
+ /**
+ * Create a subscription.
+ *
+ * @link http://developer.github.com/v3/activity/notifications/#set-a-thread-subscription
+ *
+ * @param int $id the notification number
+ * @param array $params
+ *
+ * @return array
+ */
+ public function createSubscription($id, array $params)
+ {
+ return $this->put('/notifications/threads/'.rawurlencode($id).'/subscription', $params);
+ }
+
+ /**
+ * Delete a subscription.
+ *
+ * @link http://developer.github.com/v3/activity/notifications/#delete-a-thread-subscription
+ *
+ * @param int $id the notification number
+ *
+ * @return array
+ */
+ public function removeSubscription($id)
+ {
+ return $this->delete('/notifications/threads/'.rawurlencode($id).'/subscription');
+ }
+}
diff --git a/vendor/knplabs/github-api/lib/Github/Api/CurrentUser/PublicKeys.php b/vendor/knplabs/github-api/lib/Github/Api/CurrentUser/PublicKeys.php
new file mode 100644
index 00000000..706e1405
--- /dev/null
+++ b/vendor/knplabs/github-api/lib/Github/Api/CurrentUser/PublicKeys.php
@@ -0,0 +1,74 @@
+
+ */
+class PublicKeys extends AbstractApi
+{
+ /**
+ * List deploy keys for the authenticated user.
+ *
+ * @link https://developer.github.com/v3/users/keys/
+ *
+ * @return array
+ */
+ public function all()
+ {
+ return $this->get('/user/keys');
+ }
+
+ /**
+ * Shows deploy key for the authenticated user.
+ *
+ * @link https://developer.github.com/v3/users/keys/
+ *
+ * @param int $id
+ *
+ * @return array
+ */
+ public function show($id)
+ {
+ return $this->get('/user/keys/'.rawurlencode($id));
+ }
+
+ /**
+ * Adds deploy key for the authenticated user.
+ *
+ * @link https://developer.github.com/v3/users/keys/
+ *
+ * @param array $params
+ *
+ * @throws \Github\Exception\MissingArgumentException
+ *
+ * @return array
+ */
+ public function create(array $params)
+ {
+ if (!isset($params['title'], $params['key'])) {
+ throw new MissingArgumentException(['title', 'key']);
+ }
+
+ return $this->post('/user/keys', $params);
+ }
+
+ /**
+ * Removes deploy key for the authenticated user.
+ *
+ * @link https://developer.github.com/v3/users/keys/
+ *
+ * @param int $id
+ *
+ * @return array
+ */
+ public function remove($id)
+ {
+ return $this->delete('/user/keys/'.rawurlencode($id));
+ }
+}
diff --git a/vendor/knplabs/github-api/lib/Github/Api/CurrentUser/Starring.php b/vendor/knplabs/github-api/lib/Github/Api/CurrentUser/Starring.php
new file mode 100644
index 00000000..d823c0bf
--- /dev/null
+++ b/vendor/knplabs/github-api/lib/Github/Api/CurrentUser/Starring.php
@@ -0,0 +1,76 @@
+
+ */
+class Starring extends AbstractApi
+{
+ /**
+ * List repositories starred by the authenticated user.
+ *
+ * @link https://developer.github.com/v3/activity/starring/
+ *
+ * @param int $page
+ * @param int $perPage
+ *
+ * @return array
+ */
+ public function all($page = 1, $perPage = 30)
+ {
+ return $this->get('/user/starred', [
+ 'page' => $page,
+ 'per_page' => $perPage,
+ ]);
+ }
+
+ /**
+ * Check that the authenticated user starres a repository.
+ *
+ * @link https://developer.github.com/v3/activity/starring/
+ *
+ * @param string $username the user who owns the repo
+ * @param string $repository the name of the repo
+ *
+ * @return array
+ */
+ public function check($username, $repository)
+ {
+ return $this->get('/user/starred/'.rawurlencode($username).'/'.rawurlencode($repository));
+ }
+
+ /**
+ * Make the authenticated user star a repository.
+ *
+ * @link https://developer.github.com/v3/activity/starring/
+ *
+ * @param string $username the user who owns the repo
+ * @param string $repository the name of the repo
+ *
+ * @return array
+ */
+ public function star($username, $repository)
+ {
+ return $this->put('/user/starred/'.rawurlencode($username).'/'.rawurlencode($repository));
+ }
+
+ /**
+ * Make the authenticated user unstar a repository.
+ *
+ * @link https://developer.github.com/v3/activity/starring
+ *
+ * @param string $username the user who owns the repo
+ * @param string $repository the name of the repo
+ *
+ * @return array
+ */
+ public function unstar($username, $repository)
+ {
+ return $this->delete('/user/starred/'.rawurlencode($username).'/'.rawurlencode($repository));
+ }
+}
diff --git a/vendor/knplabs/github-api/lib/Github/Api/CurrentUser/Watchers.php b/vendor/knplabs/github-api/lib/Github/Api/CurrentUser/Watchers.php
new file mode 100644
index 00000000..1ef35972
--- /dev/null
+++ b/vendor/knplabs/github-api/lib/Github/Api/CurrentUser/Watchers.php
@@ -0,0 +1,75 @@
+
+ * @revised Felipe Valtl de Mello
+ */
+class Watchers extends AbstractApi
+{
+ /**
+ * List repositories watched by the authenticated user.
+ *
+ * @link https://developer.github.com/v3/activity/watching/
+ *
+ * @param int $page
+ *
+ * @return array
+ */
+ public function all($page = 1)
+ {
+ return $this->get('/user/subscriptions', [
+ 'page' => $page,
+ ]);
+ }
+
+ /**
+ * Check that the authenticated user watches a repository.
+ *
+ * @link https://developer.github.com/v3/activity/watching/
+ *
+ * @param string $username the user who owns the repo
+ * @param string $repository the name of the repo
+ *
+ * @return array
+ */
+ public function check($username, $repository)
+ {
+ return $this->get('/user/subscriptions/'.rawurlencode($username).'/'.rawurlencode($repository));
+ }
+
+ /**
+ * Make the authenticated user watch a repository.
+ *
+ * @link https://developer.github.com/v3/activity/watching/
+ *
+ * @param string $username the user who owns the repo
+ * @param string $repository the name of the repo
+ *
+ * @return array
+ */
+ public function watch($username, $repository)
+ {
+ return $this->put('/user/subscriptions/'.rawurlencode($username).'/'.rawurlencode($repository));
+ }
+
+ /**
+ * Make the authenticated user unwatch a repository.
+ *
+ * @link https://developer.github.com/v3/activity/watching/
+ *
+ * @param string $username the user who owns the repo
+ * @param string $repository the name of the repo
+ *
+ * @return array
+ */
+ public function unwatch($username, $repository)
+ {
+ return $this->delete('/user/subscriptions/'.rawurlencode($username).'/'.rawurlencode($repository));
+ }
+}
diff --git a/vendor/knplabs/github-api/lib/Github/Api/Deployment.php b/vendor/knplabs/github-api/lib/Github/Api/Deployment.php
new file mode 100644
index 00000000..a6e9bacd
--- /dev/null
+++ b/vendor/knplabs/github-api/lib/Github/Api/Deployment.php
@@ -0,0 +1,107 @@
+get('/repos/'.rawurlencode($username).'/'.rawurlencode($repository).'/deployments', $params);
+ }
+
+ /**
+ * Get a deployment in selected repository.
+ *
+ * @param string $username the user who owns the repo
+ * @param string $repository the name of the repo
+ * @param int $id the id of the deployment
+ *
+ * @return array
+ */
+ public function show($username, $repository, $id)
+ {
+ return $this->get('/repos/'.rawurlencode($username).'/'.rawurlencode($repository).'/deployments/'.rawurlencode($id));
+ }
+
+ /**
+ * Create a new deployment for the given username and repo.
+ *
+ * @link https://developer.github.com/v3/repos/deployments/#create-a-deployment
+ *
+ * Important: Once a deployment is created, it cannot be updated. Changes are indicated by creating new statuses.
+ * @see updateStatus
+ *
+ * @param string $username the username
+ * @param string $repository the repository
+ * @param array $params the new deployment data
+ *
+ * @throws MissingArgumentException
+ *
+ * @return array information about the deployment
+ */
+ public function create($username, $repository, array $params)
+ {
+ if (!isset($params['ref'])) {
+ throw new MissingArgumentException(['ref']);
+ }
+
+ return $this->post('/repos/'.rawurlencode($username).'/'.rawurlencode($repository).'/deployments', $params);
+ }
+
+ /**
+ * Updates a deployment by creating a new status update.
+ *
+ * @link https://developer.github.com/v3/repos/deployments/#create-a-deployment-status
+ *
+ * @param string $username the username
+ * @param string $repository the repository
+ * @param int $id the deployment number
+ * @param array $params The information about the deployment update.
+ * Must include a "state" field of pending, success, error, or failure.
+ * May also be given a target_url and description, ßee link for more details.
+ *
+ * @throws MissingArgumentException
+ *
+ * @return array information about the deployment
+ */
+ public function updateStatus($username, $repository, $id, array $params)
+ {
+ if (!isset($params['state'])) {
+ throw new MissingArgumentException(['state']);
+ }
+
+ return $this->post('/repos/'.rawurlencode($username).'/'.rawurlencode($repository).'/deployments/'.rawurlencode($id).'/statuses', $params);
+ }
+
+ /**
+ * Gets all of the status updates tied to a given deployment.
+ *
+ * @param string $username the username
+ * @param string $repository the repository
+ * @param int $id the deployment identifier
+ *
+ * @return array the deployment statuses
+ */
+ public function getStatuses($username, $repository, $id)
+ {
+ return $this->get('/repos/'.rawurlencode($username).'/'.rawurlencode($repository).'/deployments/'.rawurlencode($id).'/statuses');
+ }
+}
diff --git a/vendor/knplabs/github-api/lib/Github/Api/Enterprise.php b/vendor/knplabs/github-api/lib/Github/Api/Enterprise.php
new file mode 100644
index 00000000..3dbbee3e
--- /dev/null
+++ b/vendor/knplabs/github-api/lib/Github/Api/Enterprise.php
@@ -0,0 +1,51 @@
+
+ * @author Guillermo A. Fisher
+ */
+class Enterprise extends AbstractApi
+{
+ /**
+ * @return Stats
+ */
+ public function stats()
+ {
+ return new Stats($this->client);
+ }
+
+ /**
+ * @return License
+ */
+ public function license()
+ {
+ return new License($this->client);
+ }
+
+ /**
+ * @return ManagementConsole
+ */
+ public function console()
+ {
+ return new ManagementConsole($this->client);
+ }
+
+ /**
+ * @return UserAdmin
+ */
+ public function userAdmin()
+ {
+ return new UserAdmin($this->client);
+ }
+}
diff --git a/vendor/knplabs/github-api/lib/Github/Api/Enterprise/License.php b/vendor/knplabs/github-api/lib/Github/Api/Enterprise/License.php
new file mode 100644
index 00000000..67e1c2a9
--- /dev/null
+++ b/vendor/knplabs/github-api/lib/Github/Api/Enterprise/License.php
@@ -0,0 +1,20 @@
+get('/enterprise/settings/license');
+ }
+}
diff --git a/vendor/knplabs/github-api/lib/Github/Api/Enterprise/ManagementConsole.php b/vendor/knplabs/github-api/lib/Github/Api/Enterprise/ManagementConsole.php
new file mode 100644
index 00000000..f11c4764
--- /dev/null
+++ b/vendor/knplabs/github-api/lib/Github/Api/Enterprise/ManagementConsole.php
@@ -0,0 +1,77 @@
+getWithLicenseHash('/setup/api/configcheck', $hash);
+ }
+
+ /**
+ * Retrieves your installation’s settings.
+ *
+ * @link https://developer.github.com/v3/enterprise/management_console/#retrieve-settings
+ *
+ * @param string $hash md5 hash of your license
+ *
+ * @return array array of settings
+ */
+ public function settings($hash)
+ {
+ return $this->getWithLicenseHash('/setup/api/settings', $hash);
+ }
+
+ /**
+ * Checks your installation’s maintenance status.
+ *
+ * @link https://developer.github.com/v3/enterprise/management_console/#check-maintenance-status
+ *
+ * @param string $hash md5 hash of your license
+ *
+ * @return array array of maintenance status information
+ */
+ public function maintenance($hash)
+ {
+ return $this->getWithLicenseHash('/setup/api/maintenance', $hash);
+ }
+
+ /**
+ * Retrieves your installation’s authorized SSH keys.
+ *
+ * @link https://developer.github.com/v3/enterprise/management_console/#retrieve-authorized-ssh-keys
+ *
+ * @param string $hash md5 hash of your license
+ *
+ * @return array array of authorized keys
+ */
+ public function keys($hash)
+ {
+ return $this->getWithLicenseHash('/setup/api/settings/authorized-keys', $hash);
+ }
+
+ /**
+ * Sends an authenticated GET request.
+ *
+ * @param string $uri the request URI
+ * @param string $hash md5 hash of your license
+ *
+ * @return array|string
+ */
+ protected function getWithLicenseHash($uri, $hash)
+ {
+ return $this->get($uri, ['license_md5' => rawurlencode($hash)]);
+ }
+}
diff --git a/vendor/knplabs/github-api/lib/Github/Api/Enterprise/Stats.php b/vendor/knplabs/github-api/lib/Github/Api/Enterprise/Stats.php
new file mode 100644
index 00000000..78ba4256
--- /dev/null
+++ b/vendor/knplabs/github-api/lib/Github/Api/Enterprise/Stats.php
@@ -0,0 +1,128 @@
+show('issues');
+ }
+
+ /**
+ * Returns the number of active and inactive hooks.
+ *
+ * @return array array with totals of active and inactive hooks
+ */
+ public function hooks()
+ {
+ return $this->show('hooks');
+ }
+
+ /**
+ * Returns the number of open and closed milestones.
+ *
+ * @return array array with totals of open and closed milestones
+ */
+ public function milestones()
+ {
+ return $this->show('milestones');
+ }
+
+ /**
+ * Returns the number of organizations, teams, team members, and disabled organizations.
+ *
+ * @return array array with totals of organizations, teams, team members, and disabled organizations
+ */
+ public function orgs()
+ {
+ return $this->show('orgs');
+ }
+
+ /**
+ * Returns the number of comments on issues, pull requests, commits, and gists.
+ *
+ * @return array array with totals of comments on issues, pull requests, commits, and gists
+ */
+ public function comments()
+ {
+ return $this->show('comments');
+ }
+
+ /**
+ * Returns the number of GitHub Pages sites.
+ *
+ * @return array array with totals of GitHub Pages sites
+ */
+ public function pages()
+ {
+ return $this->show('pages');
+ }
+
+ /**
+ * Returns the number of suspended and admin users.
+ *
+ * @return array array with totals of suspended and admin users
+ */
+ public function users()
+ {
+ return $this->show('users');
+ }
+
+ /**
+ * Returns the number of private and public gists.
+ *
+ * @return array array with totals of private and public gists
+ */
+ public function gists()
+ {
+ return $this->show('gists');
+ }
+
+ /**
+ * Returns the number of merged, mergeable, and unmergeable pull requests.
+ *
+ * @return array array with totals of merged, mergeable, and unmergeable pull requests
+ */
+ public function pulls()
+ {
+ return $this->show('pulls');
+ }
+
+ /**
+ * Returns the number of organization-owned repositories, root repositories, forks, pushed commits, and wikis.
+ *
+ * @return array array with totals of organization-owned repositories, root repositories, forks, pushed commits, and wikis
+ */
+ public function repos()
+ {
+ return $this->show('repos');
+ }
+
+ /**
+ * Returns all of the statistics.
+ *
+ * @return array array with all of the statistics
+ */
+ public function all()
+ {
+ return $this->show('all');
+ }
+
+ /**
+ * @param string $type The type of statistics to show
+ *
+ * @return array
+ */
+ public function show($type)
+ {
+ return $this->get('/enterprise/stats/'.rawurlencode($type));
+ }
+}
diff --git a/vendor/knplabs/github-api/lib/Github/Api/Enterprise/UserAdmin.php b/vendor/knplabs/github-api/lib/Github/Api/Enterprise/UserAdmin.php
new file mode 100644
index 00000000..0cd55a38
--- /dev/null
+++ b/vendor/knplabs/github-api/lib/Github/Api/Enterprise/UserAdmin.php
@@ -0,0 +1,36 @@
+put('/users/'.rawurldecode($username).'/suspended', ['Content-Length' => 0]);
+ }
+
+ /**
+ * Unsuspend a user.
+ *
+ * @link https://developer.github.com/v3/users/administration/#unsuspend-a-user
+ *
+ * @param string $username
+ *
+ * @return array
+ */
+ public function unsuspend($username)
+ {
+ return $this->delete('/users/'.rawurldecode($username).'/suspended');
+ }
+}
diff --git a/vendor/knplabs/github-api/lib/Github/Api/Gist/Comments.php b/vendor/knplabs/github-api/lib/Github/Api/Gist/Comments.php
new file mode 100644
index 00000000..0e022622
--- /dev/null
+++ b/vendor/knplabs/github-api/lib/Github/Api/Gist/Comments.php
@@ -0,0 +1,101 @@
+
+ */
+class Comments extends AbstractApi
+{
+ use AcceptHeaderTrait;
+
+ /**
+ * Configure the body type.
+ *
+ * @link https://developer.github.com/v3/gists/comments/#custom-media-types
+ *
+ * @param string|null $bodyType
+ *
+ * @return self
+ */
+ public function configure($bodyType = null)
+ {
+ if (!in_array($bodyType, ['text', 'html', 'full'])) {
+ $bodyType = 'raw';
+ }
+
+ $this->acceptHeaderValue = sprintf('application/vnd.github.%s.%s+json', $this->client->getApiVersion(), $bodyType);
+
+ return $this;
+ }
+
+ /**
+ * Get all comments for a gist.
+ *
+ * @param string $gist
+ *
+ * @return array
+ */
+ public function all($gist)
+ {
+ return $this->get('/gists/'.rawurlencode($gist).'/comments');
+ }
+
+ /**
+ * Get a comment of a gist.
+ *
+ * @param string $gist
+ * @param int $comment
+ *
+ * @return array
+ */
+ public function show($gist, $comment)
+ {
+ return $this->get('/gists/'.rawurlencode($gist).'/comments/'.rawurlencode($comment));
+ }
+
+ /**
+ * Create a comment for gist.
+ *
+ * @param string $gist
+ * @param string $body
+ *
+ * @return array
+ */
+ public function create($gist, $body)
+ {
+ return $this->post('/gists/'.rawurlencode($gist).'/comments', ['body' => $body]);
+ }
+
+ /**
+ * Create a comment for a gist.
+ *
+ * @param string $gist
+ * @param int $comment_id
+ * @param string $body
+ *
+ * @return array
+ */
+ public function update($gist, $comment_id, $body)
+ {
+ return $this->patch('/gists/'.rawurlencode($gist).'/comments/'.rawurlencode($comment_id), ['body' => $body]);
+ }
+
+ /**
+ * Delete a comment for a gist.
+ *
+ * @param string $gist
+ * @param int $comment
+ *
+ * @return array
+ */
+ public function remove($gist, $comment)
+ {
+ return $this->delete('/gists/'.rawurlencode($gist).'/comments/'.rawurlencode($comment));
+ }
+}
diff --git a/vendor/knplabs/github-api/lib/Github/Api/Gists.php b/vendor/knplabs/github-api/lib/Github/Api/Gists.php
new file mode 100644
index 00000000..e6bdc430
--- /dev/null
+++ b/vendor/knplabs/github-api/lib/Github/Api/Gists.php
@@ -0,0 +1,116 @@
+
+ * @author Edoardo Rivello
+ */
+class Gists extends AbstractApi
+{
+ use AcceptHeaderTrait;
+
+ /**
+ * Configure the body type.
+ *
+ * @link https://developer.github.com/v3/gists/#custom-media-types
+ *
+ * @param string|null $bodyType
+ *
+ * @return self
+ */
+ public function configure($bodyType = null)
+ {
+ if (!in_array($bodyType, ['base64'])) {
+ $bodyType = 'raw';
+ }
+
+ $this->acceptHeaderValue = sprintf('application/vnd.github.%s.%s', $this->client->getApiVersion(), $bodyType);
+
+ return $this;
+ }
+
+ public function all($type = null)
+ {
+ if (!in_array($type, ['public', 'starred'])) {
+ return $this->get('/gists');
+ }
+
+ return $this->get('/gists/'.rawurlencode($type));
+ }
+
+ public function show($number)
+ {
+ return $this->get('/gists/'.rawurlencode($number));
+ }
+
+ public function create(array $params)
+ {
+ if (!isset($params['files']) || (!is_array($params['files']) || 0 === count($params['files']))) {
+ throw new MissingArgumentException('files');
+ }
+
+ $params['public'] = (bool) $params['public'];
+
+ return $this->post('/gists', $params);
+ }
+
+ public function update($id, array $params)
+ {
+ return $this->patch('/gists/'.rawurlencode($id), $params);
+ }
+
+ public function commits($id)
+ {
+ return $this->get('/gists/'.rawurlencode($id).'/commits');
+ }
+
+ public function fork($id)
+ {
+ return $this->post('/gists/'.rawurlencode($id).'/fork');
+ }
+
+ public function forks($id)
+ {
+ return $this->get('/gists/'.rawurlencode($id).'/forks');
+ }
+
+ public function remove($id)
+ {
+ return $this->delete('/gists/'.rawurlencode($id));
+ }
+
+ public function check($id)
+ {
+ return $this->get('/gists/'.rawurlencode($id).'/star');
+ }
+
+ public function star($id)
+ {
+ return $this->put('/gists/'.rawurlencode($id).'/star');
+ }
+
+ public function unstar($id)
+ {
+ return $this->delete('/gists/'.rawurlencode($id).'/star');
+ }
+
+ /**
+ * Get a gist's comments.
+ *
+ * @link http://developer.github.com/v3/gists/comments/
+ *
+ * @return Comments
+ */
+ public function comments()
+ {
+ return new Comments($this->client);
+ }
+}
diff --git a/vendor/knplabs/github-api/lib/Github/Api/GitData.php b/vendor/knplabs/github-api/lib/Github/Api/GitData.php
new file mode 100644
index 00000000..d431b788
--- /dev/null
+++ b/vendor/knplabs/github-api/lib/Github/Api/GitData.php
@@ -0,0 +1,59 @@
+
+ */
+class GitData extends AbstractApi
+{
+ /**
+ * @return Blobs
+ */
+ public function blobs()
+ {
+ return new Blobs($this->client);
+ }
+
+ /**
+ * @return Commits
+ */
+ public function commits()
+ {
+ return new Commits($this->client);
+ }
+
+ /**
+ * @return References
+ */
+ public function references()
+ {
+ return new References($this->client);
+ }
+
+ /**
+ * @return Tags
+ */
+ public function tags()
+ {
+ return new Tags($this->client);
+ }
+
+ /**
+ * @return Trees
+ */
+ public function trees()
+ {
+ return new Trees($this->client);
+ }
+}
diff --git a/vendor/knplabs/github-api/lib/Github/Api/GitData/Blobs.php b/vendor/knplabs/github-api/lib/Github/Api/GitData/Blobs.php
new file mode 100644
index 00000000..be68771d
--- /dev/null
+++ b/vendor/knplabs/github-api/lib/Github/Api/GitData/Blobs.php
@@ -0,0 +1,70 @@
+
+ * @author Tobias Nyholm
+ */
+class Blobs extends AbstractApi
+{
+ use AcceptHeaderTrait;
+
+ /**
+ * Configure the Accept header depending on the blob type.
+ *
+ * @param string|null $bodyType
+ *
+ * @return self
+ */
+ public function configure($bodyType = null)
+ {
+ if ('raw' === $bodyType) {
+ $this->acceptHeaderValue = sprintf('application/vnd.github.%s.raw', $this->client->getApiVersion());
+ }
+
+ return $this;
+ }
+
+ /**
+ * Show a blob of a sha for a repository.
+ *
+ * @param string $username
+ * @param string $repository
+ * @param string $sha
+ *
+ * @return array
+ */
+ public function show($username, $repository, $sha)
+ {
+ $response = $this->get('/repos/'.rawurlencode($username).'/'.rawurlencode($repository).'/git/blobs/'.rawurlencode($sha));
+
+ return $response;
+ }
+
+ /**
+ * Create a blob of a sha for a repository.
+ *
+ * @param string $username
+ * @param string $repository
+ * @param array $params
+ *
+ * @throws \Github\Exception\MissingArgumentException
+ *
+ * @return array
+ */
+ public function create($username, $repository, array $params)
+ {
+ if (!isset($params['content'], $params['encoding'])) {
+ throw new MissingArgumentException(['content', 'encoding']);
+ }
+
+ return $this->post('/repos/'.rawurlencode($username).'/'.rawurlencode($repository).'/git/blobs', $params);
+ }
+}
diff --git a/vendor/knplabs/github-api/lib/Github/Api/GitData/Commits.php b/vendor/knplabs/github-api/lib/Github/Api/GitData/Commits.php
new file mode 100644
index 00000000..4205931f
--- /dev/null
+++ b/vendor/knplabs/github-api/lib/Github/Api/GitData/Commits.php
@@ -0,0 +1,48 @@
+
+ */
+class Commits extends AbstractApi
+{
+ /**
+ * Show a commit for a repository.
+ *
+ * @param string $username
+ * @param string $repository
+ * @param string $sha
+ *
+ * @return array
+ */
+ public function show($username, $repository, $sha)
+ {
+ return $this->get('/repos/'.rawurlencode($username).'/'.rawurlencode($repository).'/git/commits/'.rawurlencode($sha));
+ }
+
+ /**
+ * Create a commit for a repository.
+ *
+ * @param string $username
+ * @param string $repository
+ * @param array $params
+ *
+ * @throws \Github\Exception\MissingArgumentException
+ *
+ * @return array
+ */
+ public function create($username, $repository, array $params)
+ {
+ if (!isset($params['message'], $params['tree'], $params['parents'])) {
+ throw new MissingArgumentException(['message', 'tree', 'parents']);
+ }
+
+ return $this->post('/repos/'.rawurlencode($username).'/'.rawurlencode($repository).'/git/commits', $params);
+ }
+}
diff --git a/vendor/knplabs/github-api/lib/Github/Api/GitData/References.php b/vendor/knplabs/github-api/lib/Github/Api/GitData/References.php
new file mode 100644
index 00000000..c54c0c8a
--- /dev/null
+++ b/vendor/knplabs/github-api/lib/Github/Api/GitData/References.php
@@ -0,0 +1,140 @@
+
+ */
+class References extends AbstractApi
+{
+ /**
+ * Get all references of a repository.
+ *
+ * @param string $username
+ * @param string $repository
+ *
+ * @return array
+ */
+ public function all($username, $repository)
+ {
+ return $this->get('/repos/'.rawurlencode($username).'/'.rawurlencode($repository).'/git/refs');
+ }
+
+ /**
+ * Get all branches of a repository.
+ *
+ * @param string $username
+ * @param string $repository
+ *
+ * @return array
+ */
+ public function branches($username, $repository)
+ {
+ return $this->get('/repos/'.rawurlencode($username).'/'.rawurlencode($repository).'/git/refs/heads');
+ }
+
+ /**
+ * Get all tags of a repository.
+ *
+ * @param string $username
+ * @param string $repository
+ *
+ * @return array
+ */
+ public function tags($username, $repository)
+ {
+ return $this->get('/repos/'.rawurlencode($username).'/'.rawurlencode($repository).'/git/refs/tags');
+ }
+
+ /**
+ * Show the reference of a repository.
+ *
+ * @param string $username
+ * @param string $repository
+ * @param string $reference
+ *
+ * @return array
+ */
+ public function show($username, $repository, $reference)
+ {
+ $reference = $this->encodeReference($reference);
+
+ return $this->get('/repos/'.rawurlencode($username).'/'.rawurlencode($repository).'/git/refs/'.$reference);
+ }
+
+ /**
+ * Create a reference for a repository.
+ *
+ * @param string $username
+ * @param string $repository
+ * @param array $params
+ *
+ * @throws \Github\Exception\MissingArgumentException
+ *
+ * @return array
+ */
+ public function create($username, $repository, array $params)
+ {
+ if (!isset($params['ref'], $params['sha'])) {
+ throw new MissingArgumentException(['ref', 'sha']);
+ }
+
+ return $this->post('/repos/'.rawurlencode($username).'/'.rawurlencode($repository).'/git/refs', $params);
+ }
+
+ /**
+ * Update a reference for a repository.
+ *
+ * @param string $username
+ * @param string $repository
+ * @param string $reference
+ * @param array $params
+ *
+ * @throws \Github\Exception\MissingArgumentException
+ *
+ * @return array
+ */
+ public function update($username, $repository, $reference, array $params)
+ {
+ if (!isset($params['sha'])) {
+ throw new MissingArgumentException('sha');
+ }
+
+ $reference = $this->encodeReference($reference);
+
+ return $this->patch('/repos/'.rawurlencode($username).'/'.rawurlencode($repository).'/git/refs/'.$reference, $params);
+ }
+
+ /**
+ * Delete a reference of a repository.
+ *
+ * @param string $username
+ * @param string $repository
+ * @param string $reference
+ *
+ * @return array
+ */
+ public function remove($username, $repository, $reference)
+ {
+ $reference = $this->encodeReference($reference);
+
+ return $this->delete('/repos/'.rawurlencode($username).'/'.rawurlencode($repository).'/git/refs/'.$reference);
+ }
+
+ /**
+ * Encode the raw reference.
+ *
+ * @param string $rawReference
+ *
+ * @return string
+ */
+ private function encodeReference($rawReference)
+ {
+ return implode('/', array_map('rawurlencode', explode('/', $rawReference)));
+ }
+}
diff --git a/vendor/knplabs/github-api/lib/Github/Api/GitData/Tags.php b/vendor/knplabs/github-api/lib/Github/Api/GitData/Tags.php
new file mode 100644
index 00000000..09f48bc0
--- /dev/null
+++ b/vendor/knplabs/github-api/lib/Github/Api/GitData/Tags.php
@@ -0,0 +1,69 @@
+
+ */
+class Tags extends AbstractApi
+{
+ /**
+ * Get all tags for a repository.
+ *
+ * @param string $username
+ * @param string $repository
+ *
+ * @return array
+ */
+ public function all($username, $repository)
+ {
+ return $this->get('/repos/'.rawurlencode($username).'/'.rawurlencode($repository).'/git/refs/tags');
+ }
+
+ /**
+ * Get a tag for a repository.
+ *
+ * @param string $username
+ * @param string $repository
+ * @param string $sha
+ *
+ * @return array
+ */
+ public function show($username, $repository, $sha)
+ {
+ return $this->get('/repos/'.rawurlencode($username).'/'.rawurlencode($repository).'/git/tags/'.rawurlencode($sha));
+ }
+
+ /**
+ * Create a tag for a repository.
+ *
+ * @param string $username
+ * @param string $repository
+ * @param array $params
+ *
+ * @throws \Github\Exception\MissingArgumentException
+ *
+ * @return array
+ */
+ public function create($username, $repository, array $params)
+ {
+ if (!isset($params['tag'], $params['message'], $params['object'], $params['type'])) {
+ throw new MissingArgumentException(['tag', 'message', 'object', 'type']);
+ }
+
+ if (!isset($params['tagger'])) {
+ throw new MissingArgumentException('tagger');
+ }
+
+ if (!isset($params['tagger']['name'], $params['tagger']['email'], $params['tagger']['date'])) {
+ throw new MissingArgumentException(['tagger.name', 'tagger.email', 'tagger.date']);
+ }
+
+ return $this->post('/repos/'.rawurlencode($username).'/'.rawurlencode($repository).'/git/tags', $params);
+ }
+}
diff --git a/vendor/knplabs/github-api/lib/Github/Api/GitData/Trees.php b/vendor/knplabs/github-api/lib/Github/Api/GitData/Trees.php
new file mode 100644
index 00000000..d514d9f8
--- /dev/null
+++ b/vendor/knplabs/github-api/lib/Github/Api/GitData/Trees.php
@@ -0,0 +1,64 @@
+
+ */
+class Trees extends AbstractApi
+{
+ /**
+ * Get the tree for a repository.
+ *
+ * @param string $username
+ * @param string $repository
+ * @param string $sha
+ * @param bool $recursive
+ *
+ * @return array
+ */
+ public function show($username, $repository, $sha, $recursive = false)
+ {
+ return $this->get('/repos/'.rawurlencode($username).'/'.rawurlencode($repository).'/git/trees/'.rawurlencode($sha), $recursive ? ['recursive' => 1] : []);
+ }
+
+ /**
+ * Create tree for a repository.
+ *
+ * @param string $username
+ * @param string $repository
+ * @param array $params
+ *
+ * @throws \Github\Exception\MissingArgumentException
+ *
+ * @return array
+ */
+ public function create($username, $repository, array $params)
+ {
+ if (!isset($params['tree']) || !is_array($params['tree'])) {
+ throw new MissingArgumentException('tree');
+ }
+
+ if (!isset($params['tree'][0])) {
+ $params['tree'] = [$params['tree']];
+ }
+
+ foreach ($params['tree'] as $key => $tree) {
+ if (!isset($tree['path'], $tree['mode'], $tree['type'])) {
+ throw new MissingArgumentException(["tree.$key.path", "tree.$key.mode", "tree.$key.type"]);
+ }
+
+ // If `sha` is not set, `content` is required
+ if (!isset($tree['sha']) && !isset($tree['content'])) {
+ throw new MissingArgumentException("tree.$key.content");
+ }
+ }
+
+ return $this->post('/repos/'.rawurlencode($username).'/'.rawurlencode($repository).'/git/trees', $params);
+ }
+}
diff --git a/vendor/knplabs/github-api/lib/Github/Api/GraphQL.php b/vendor/knplabs/github-api/lib/Github/Api/GraphQL.php
new file mode 100644
index 00000000..9d66b512
--- /dev/null
+++ b/vendor/knplabs/github-api/lib/Github/Api/GraphQL.php
@@ -0,0 +1,47 @@
+
+ */
+class GraphQL extends AbstractApi
+{
+ use AcceptHeaderTrait;
+
+ /**
+ * @param string $query
+ * @param array $variables
+ *
+ * @return array
+ */
+ public function execute($query, array $variables = [])
+ {
+ $this->acceptHeaderValue = 'application/vnd.github.v4+json';
+ $params = [
+ 'query' => $query,
+ ];
+ if (!empty($variables)) {
+ $params['variables'] = json_encode($variables);
+ }
+
+ return $this->post('/graphql', $params);
+ }
+
+ /**
+ * @param string $file
+ * @param array $variables
+ *
+ * @return array
+ */
+ public function fromFile($file, array $variables = [])
+ {
+ return $this->execute(file_get_contents($file), $variables);
+ }
+}
diff --git a/vendor/knplabs/github-api/lib/Github/Api/Integrations.php b/vendor/knplabs/github-api/lib/Github/Api/Integrations.php
new file mode 100644
index 00000000..bd4f78a4
--- /dev/null
+++ b/vendor/knplabs/github-api/lib/Github/Api/Integrations.php
@@ -0,0 +1,26 @@
+
+ */
+class Integrations extends Apps
+{
+ /**
+ * @deprecated
+ * Configure the accept header for Early Access to the integrations api (DEPRECATED)
+ * @see https://developer.github.com/v3/apps/
+ *
+ * @return self
+ */
+ public function configure()
+ {
+ return $this;
+ }
+}
diff --git a/vendor/knplabs/github-api/lib/Github/Api/Issue.php b/vendor/knplabs/github-api/lib/Github/Api/Issue.php
new file mode 100644
index 00000000..9b7d1d04
--- /dev/null
+++ b/vendor/knplabs/github-api/lib/Github/Api/Issue.php
@@ -0,0 +1,263 @@
+
+ * @author Joseph Bielawski
+ */
+class Issue extends AbstractApi
+{
+ use AcceptHeaderTrait;
+
+ /**
+ * Configure the body type.
+ *
+ * @link https://developer.github.com/v3/issues/#custom-media-types
+ *
+ * @param string|null $bodyType
+ *
+ * @return self
+ */
+ public function configure($bodyType = null)
+ {
+ if (!in_array($bodyType, ['text', 'html', 'full'])) {
+ $bodyType = 'raw';
+ }
+
+ $this->acceptHeaderValue = sprintf('application/vnd.github.%s.%s+json', $this->client->getApiVersion(), $bodyType);
+
+ return $this;
+ }
+
+ /**
+ * List issues by username, repo and state.
+ *
+ * @link http://developer.github.com/v3/issues/
+ *
+ * @param string $username the username
+ * @param string $repository the repository
+ * @param array $params the additional parameters like milestone, assignees, labels, sort, direction
+ *
+ * @return array list of issues found
+ */
+ public function all($username, $repository, array $params = [])
+ {
+ return $this->get('/repos/'.rawurlencode($username).'/'.rawurlencode($repository).'/issues', array_merge(['page' => 1], $params));
+ }
+
+ /**
+ * Search issues by username, repo, state and keyword.
+ *
+ * @deprecated This method is deprecated use the Search api instead. See https://developer.github.com/v3/search/legacy/#legacy-search-api-is-deprecated
+ * @link http://developer.github.com/v3/search/#search-issues
+ *
+ * @param string $username the username
+ * @param string $repository the repository
+ * @param string $state the issue state, can be open or closed
+ * @param string $keyword the keyword to filter issues by
+ *
+ * @return array list of issues found
+ */
+ public function find($username, $repository, $state, $keyword)
+ {
+ if (!in_array($state, ['open', 'closed'])) {
+ $state = 'open';
+ }
+
+ return $this->get('/legacy/issues/search/'.rawurlencode($username).'/'.rawurlencode($repository).'/'.rawurlencode($state).'/'.rawurlencode($keyword));
+ }
+
+ /**
+ * List issues by organization.
+ *
+ * @link http://developer.github.com/v3/issues/
+ *
+ * @param string $organization the organization
+ * @param string $state the issue state, can be open or closed
+ * @param array $params the additional parameters like milestone, assignees, labels, sort, direction
+ *
+ * @return array list of issues found
+ */
+ public function org($organization, $state, array $params = [])
+ {
+ if (!in_array($state, ['open', 'closed'])) {
+ $state = 'open';
+ }
+
+ return $this->get('/orgs/'.rawurlencode($organization).'/issues', array_merge(['page' => 1, 'state' => $state], $params));
+ }
+
+ /**
+ * Get extended information about an issue by its username, repo and number.
+ *
+ * @link http://developer.github.com/v3/issues/
+ *
+ * @param string $username the username
+ * @param string $repository the repository
+ * @param int $id the issue number
+ *
+ * @return array information about the issue
+ */
+ public function show($username, $repository, $id)
+ {
+ return $this->get('/repos/'.rawurlencode($username).'/'.rawurlencode($repository).'/issues/'.rawurlencode($id));
+ }
+
+ /**
+ * Create a new issue for the given username and repo.
+ * The issue is assigned to the authenticated user. Requires authentication.
+ *
+ * @link http://developer.github.com/v3/issues/
+ *
+ * @param string $username the username
+ * @param string $repository the repository
+ * @param array $params the new issue data
+ *
+ * @throws MissingArgumentException
+ *
+ * @return array information about the issue
+ */
+ public function create($username, $repository, array $params)
+ {
+ if (!isset($params['title'])) {
+ throw new MissingArgumentException(['title']);
+ }
+
+ return $this->post('/repos/'.rawurlencode($username).'/'.rawurlencode($repository).'/issues', $params);
+ }
+
+ /**
+ * Update issue information's by username, repo and issue number. Requires authentication.
+ *
+ * @link http://developer.github.com/v3/issues/
+ *
+ * @param string $username the username
+ * @param string $repository the repository
+ * @param int $id the issue number
+ * @param array $params key=>value user attributes to update.
+ * key can be title or body
+ *
+ * @return array information about the issue
+ */
+ public function update($username, $repository, $id, array $params)
+ {
+ return $this->patch('/repos/'.rawurlencode($username).'/'.rawurlencode($repository).'/issues/'.rawurlencode($id), $params);
+ }
+
+ /**
+ * Lock an issue. Users with push access can lock an issue's conversation.
+ *
+ * @link https://developer.github.com/v3/issues/#lock-an-issue
+ *
+ * @param string $username
+ * @param string $repository
+ * @param int $id
+ *
+ * @return string
+ */
+ public function lock($username, $repository, $id)
+ {
+ return $this->put('/repos/'.rawurlencode($username).'/'.rawurlencode($repository).'/issues/'.rawurlencode($id).'/lock');
+ }
+
+ /**
+ * Unlock an issue. Users with push access can unlock an issue's conversation.
+ *
+ * @link https://developer.github.com/v3/issues/#lock-an-issue
+ *
+ * @param string $username
+ * @param string $repository
+ * @param int $id
+ *
+ * @return string
+ */
+ public function unlock($username, $repository, $id)
+ {
+ return $this->delete('/repos/'.rawurlencode($username).'/'.rawurlencode($repository).'/issues/'.rawurlencode($id).'/lock');
+ }
+
+ /**
+ * List an issue comments.
+ *
+ * @link http://developer.github.com/v3/issues/comments/
+ *
+ * @return Comments
+ */
+ public function comments()
+ {
+ return new Comments($this->client);
+ }
+
+ /**
+ * List all project events.
+ *
+ * @link http://developer.github.com/v3/issues/events/
+ *
+ * @return Events
+ */
+ public function events()
+ {
+ return new Events($this->client);
+ }
+
+ /**
+ * List all project labels.
+ *
+ * @link http://developer.github.com/v3/issues/labels/
+ *
+ * @return Labels
+ */
+ public function labels()
+ {
+ return new Labels($this->client);
+ }
+
+ /**
+ * List all project milestones.
+ *
+ * @link http://developer.github.com/v3/issues/milestones/
+ *
+ * @return Milestones
+ */
+ public function milestones()
+ {
+ return new Milestones($this->client);
+ }
+
+ /**
+ * List all assignees.
+ *
+ * @link https://developer.github.com/v3/issues/assignees/
+ *
+ * @return Assignees
+ */
+ public function assignees()
+ {
+ return new Assignees($this->client);
+ }
+
+ /**
+ * List all events.
+ *
+ * @link https://developer.github.com/v3/issues/timeline/
+ *
+ * @return Timeline
+ */
+ public function timeline()
+ {
+ return new Timeline($this->client);
+ }
+}
diff --git a/vendor/knplabs/github-api/lib/Github/Api/Issue/Assignees.php b/vendor/knplabs/github-api/lib/Github/Api/Issue/Assignees.php
new file mode 100644
index 00000000..1e5e0b72
--- /dev/null
+++ b/vendor/knplabs/github-api/lib/Github/Api/Issue/Assignees.php
@@ -0,0 +1,91 @@
+get('/repos/'.rawurlencode($username).'/'.rawurlencode($repository).'/assignees', $parameters);
+ }
+
+ /**
+ * Check to see if a particular user is an assignee for a repository.
+ *
+ * @link https://developer.github.com/v3/issues/assignees/#check-assignee
+ *
+ * @param string $username
+ * @param string $repository
+ * @param string $assignee
+ *
+ * @return array
+ */
+ public function check($username, $repository, $assignee)
+ {
+ return $this->get('/repos/'.rawurlencode($username).'/'.rawurlencode($repository).'/assignees/'.rawurlencode($assignee));
+ }
+
+ /**
+ * Add assignees to an Issue.
+ *
+ * @link https://developer.github.com/v3/issues/assignees/#add-assignees-to-an-issue
+ *
+ * @param string $username
+ * @param string $repository
+ * @param string $issue
+ * @param array $parameters
+ *
+ * @throws MissingArgumentException
+ *
+ * @return string
+ */
+ public function add($username, $repository, $issue, array $parameters)
+ {
+ if (!isset($parameters['assignees'])) {
+ throw new MissingArgumentException('assignees');
+ }
+
+ if (!is_array($parameters['assignees'])) {
+ @trigger_error(sprintf('Passing the "assignees" parameter as a string in "%s" is deprecated and will throw an exception in php-github-api version 3.0. Pass an array of strings instead', __METHOD__), E_USER_DEPRECATED);
+
+ $parameters['assignees'] = [$parameters['assignees']];
+ }
+
+ return $this->post('/repos/'.rawurlencode($username).'/'.rawurlencode($repository).'/issues/'.rawurlencode($issue).'/assignees', $parameters);
+ }
+
+ /**
+ * Remove assignees from an Issue.
+ *
+ * @link https://developer.github.com/v3/issues/assignees/#remove-assignees-from-an-issue
+ *
+ * @param string $username
+ * @param string $repository
+ * @param string $issue
+ * @param array $parameters
+ *
+ * @throws MissingArgumentException
+ *
+ * @return string
+ */
+ public function remove($username, $repository, $issue, array $parameters)
+ {
+ if (!isset($parameters['assignees'])) {
+ throw new MissingArgumentException('assignees');
+ }
+
+ return $this->delete('/repos/'.rawurlencode($username).'/'.rawurlencode($repository).'/issues/'.rawurlencode($issue).'/assignees', $parameters);
+ }
+}
diff --git a/vendor/knplabs/github-api/lib/Github/Api/Issue/Comments.php b/vendor/knplabs/github-api/lib/Github/Api/Issue/Comments.php
new file mode 100644
index 00000000..a664d533
--- /dev/null
+++ b/vendor/knplabs/github-api/lib/Github/Api/Issue/Comments.php
@@ -0,0 +1,135 @@
+
+ * @author Tobias Nyholm
+ */
+class Comments extends AbstractApi
+{
+ use AcceptHeaderTrait;
+
+ /**
+ * Configure the body type.
+ *
+ * @link https://developer.github.com/v3/issues/comments/#custom-media-types
+ *
+ * @param string|null $bodyType
+ *
+ * @return self
+ */
+ public function configure($bodyType = null)
+ {
+ if (!in_array($bodyType, ['raw', 'text', 'html'])) {
+ $bodyType = 'full';
+ }
+
+ $this->acceptHeaderValue = sprintf('application/vnd.github.%s.%s+json', $this->client->getApiVersion(), $bodyType);
+
+ return $this;
+ }
+
+ /**
+ * Get all comments for an issue.
+ *
+ * @link https://developer.github.com/v3/issues/comments/#list-comments-on-an-issue
+ *
+ * @param string $username
+ * @param string $repository
+ * @param int $issue
+ * @param int $page
+ *
+ * @return array
+ */
+ public function all($username, $repository, $issue, $page = 1)
+ {
+ return $this->get('/repos/'.rawurlencode($username).'/'.rawurlencode($repository).'/issues/'.rawurlencode($issue).'/comments', [
+ 'page' => $page,
+ ]);
+ }
+
+ /**
+ * Get a comment for an issue.
+ *
+ * @link https://developer.github.com/v3/issues/comments/#get-a-single-comment
+ *
+ * @param string $username
+ * @param string $repository
+ * @param int $comment
+ *
+ * @return array
+ */
+ public function show($username, $repository, $comment)
+ {
+ return $this->get('/repos/'.rawurlencode($username).'/'.rawurlencode($repository).'/issues/comments/'.rawurlencode($comment));
+ }
+
+ /**
+ * Create a comment for an issue.
+ *
+ * @link https://developer.github.com/v3/issues/comments/#create-a-comment
+ *
+ * @param string $username
+ * @param string $repository
+ * @param int $issue
+ * @param array $params
+ *
+ * @throws \Github\Exception\MissingArgumentException
+ *
+ * @return array
+ */
+ public function create($username, $repository, $issue, array $params)
+ {
+ if (!isset($params['body'])) {
+ throw new MissingArgumentException('body');
+ }
+
+ return $this->post('/repos/'.rawurlencode($username).'/'.rawurlencode($repository).'/issues/'.rawurlencode($issue).'/comments', $params);
+ }
+
+ /**
+ * Update a comment for an issue.
+ *
+ * @link https://developer.github.com/v3/issues/comments/#edit-a-comment
+ *
+ * @param string $username
+ * @param string $repository
+ * @param int $comment
+ * @param array $params
+ *
+ * @throws \Github\Exception\MissingArgumentException
+ *
+ * @return array
+ */
+ public function update($username, $repository, $comment, array $params)
+ {
+ if (!isset($params['body'])) {
+ throw new MissingArgumentException('body');
+ }
+
+ return $this->patch('/repos/'.rawurlencode($username).'/'.rawurlencode($repository).'/issues/comments/'.rawurlencode($comment), $params);
+ }
+
+ /**
+ * Delete a comment for an issue.
+ *
+ * @link https://developer.github.com/v3/issues/comments/#delete-a-comment
+ *
+ * @param string $username
+ * @param string $repository
+ * @param int $comment
+ *
+ * @return array
+ */
+ public function remove($username, $repository, $comment)
+ {
+ return $this->delete('/repos/'.rawurlencode($username).'/'.rawurlencode($repository).'/issues/comments/'.rawurlencode($comment));
+ }
+}
diff --git a/vendor/knplabs/github-api/lib/Github/Api/Issue/Events.php b/vendor/knplabs/github-api/lib/Github/Api/Issue/Events.php
new file mode 100644
index 00000000..8b70ec79
--- /dev/null
+++ b/vendor/knplabs/github-api/lib/Github/Api/Issue/Events.php
@@ -0,0 +1,54 @@
+
+ */
+class Events extends AbstractApi
+{
+ /**
+ * Get all events for an issue.
+ *
+ * @link https://developer.github.com/v3/issues/events/#list-events-for-an-issue
+ *
+ * @param string $username
+ * @param string $repository
+ * @param int|null $issue
+ * @param int $page
+ *
+ * @return array
+ */
+ public function all($username, $repository, $issue = null, $page = 1)
+ {
+ if (null !== $issue) {
+ $path = '/repos/'.rawurlencode($username).'/'.rawurlencode($repository).'/issues/'.rawurlencode($issue).'/events';
+ } else {
+ $path = '/repos/'.rawurlencode($username).'/'.rawurlencode($repository).'/issues/events';
+ }
+
+ return $this->get($path, [
+ 'page' => $page,
+ ]);
+ }
+
+ /**
+ * Display an event for an issue.
+ *
+ * @link https://developer.github.com/v3/issues/events/#get-a-single-event
+ *
+ * @param $username
+ * @param $repository
+ * @param $event
+ *
+ * @return array
+ */
+ public function show($username, $repository, $event)
+ {
+ return $this->get('/repos/'.rawurlencode($username).'/'.rawurlencode($repository).'/issues/events/'.rawurlencode($event));
+ }
+}
diff --git a/vendor/knplabs/github-api/lib/Github/Api/Issue/Labels.php b/vendor/knplabs/github-api/lib/Github/Api/Issue/Labels.php
new file mode 100644
index 00000000..1159e153
--- /dev/null
+++ b/vendor/knplabs/github-api/lib/Github/Api/Issue/Labels.php
@@ -0,0 +1,192 @@
+
+ */
+class Labels extends AbstractApi
+{
+ /**
+ * Get all labels for a repository or the labels for a specific issue.
+ *
+ * @link https://developer.github.com/v3/issues/labels/#list-labels-on-an-issue
+ *
+ * @param string $username
+ * @param string $repository
+ * @param int|null $issue
+ *
+ * @return array
+ */
+ public function all($username, $repository, $issue = null)
+ {
+ if ($issue === null) {
+ $path = '/repos/'.rawurlencode($username).'/'.rawurlencode($repository).'/labels';
+ } else {
+ $path = '/repos/'.rawurlencode($username).'/'.rawurlencode($repository).'/issues/'.rawurlencode($issue).'/labels';
+ }
+
+ return $this->get($path);
+ }
+
+ /**
+ * Get a single label.
+ *
+ * @link https://developer.github.com/v3/issues/labels/#get-a-single-label
+ *
+ * @param string $username
+ * @param string $repository
+ * @param string $label
+ *
+ * @return array
+ */
+ public function show($username, $repository, $label)
+ {
+ return $this->get('/repos/'.rawurlencode($username).'/'.rawurlencode($repository).'/labels/'.rawurlencode($label));
+ }
+
+ /**
+ * Create a label for a repository.
+ *
+ * @link https://developer.github.com/v3/issues/labels/#create-a-label
+ *
+ * @param string $username
+ * @param string $repository
+ * @param array $params
+ *
+ * @throws \Github\Exception\MissingArgumentException
+ *
+ * @return array
+ */
+ public function create($username, $repository, array $params)
+ {
+ if (!isset($params['name'])) {
+ throw new MissingArgumentException('name');
+ }
+ if (!isset($params['color'])) {
+ $params['color'] = 'FFFFFF';
+ }
+
+ return $this->post('/repos/'.rawurlencode($username).'/'.rawurlencode($repository).'/labels', $params);
+ }
+
+ /**
+ * Delete a label for a repository.
+ *
+ * @link https://developer.github.com/v3/issues/labels/#remove-a-label-from-an-issue
+ *
+ * @param string $username
+ * @param string $repository
+ * @param string $label
+ *
+ * @return array
+ */
+ public function deleteLabel($username, $repository, $label)
+ {
+ return $this->delete('/repos/'.rawurlencode($username).'/'.rawurlencode($repository).'/labels/'.rawurlencode($label));
+ }
+
+ /**
+ * Edit a label for a repository.
+ *
+ * @link https://developer.github.com/v3/issues/labels/#update-a-label
+ *
+ * @param string $username
+ * @param string $repository
+ * @param string $label
+ * @param string $newName
+ * @param string $color
+ *
+ * @return array
+ */
+ public function update($username, $repository, $label, $newName, $color)
+ {
+ $params = [
+ 'name' => $newName,
+ 'color' => $color,
+ ];
+
+ return $this->patch('/repos/'.rawurlencode($username).'/'.rawurlencode($repository).'/labels/'.rawurlencode($label), $params);
+ }
+
+ /**
+ * Add a label to an issue.
+ *
+ * @link https://developer.github.com/v3/issues/labels/#remove-a-label-from-an-issue
+ *
+ * @param string $username
+ * @param string $repository
+ * @param int $issue
+ * @param string $labels
+ *
+ * @return array
+ *
+ * @thorws \Github\Exception\InvalidArgumentException
+ */
+ public function add($username, $repository, $issue, $labels)
+ {
+ if (is_string($labels)) {
+ $labels = [$labels];
+ } elseif (0 === count($labels)) {
+ throw new InvalidArgumentException();
+ }
+
+ return $this->post('/repos/'.rawurlencode($username).'/'.rawurlencode($repository).'/issues/'.rawurlencode($issue).'/labels', $labels);
+ }
+
+ /**
+ * Replace labels for an issue.
+ *
+ * @link https://developer.github.com/v3/issues/labels/#replace-all-labels-for-an-issue
+ *
+ * @param string $username
+ * @param string $repository
+ * @param int $issue
+ * @param array $params
+ *
+ * @return array
+ */
+ public function replace($username, $repository, $issue, array $params)
+ {
+ return $this->put('/repos/'.rawurlencode($username).'/'.rawurlencode($repository).'/issues/'.rawurlencode($issue).'/labels', $params);
+ }
+
+ /**
+ * Remove a label for an issue.
+ *
+ * @link https://developer.github.com/v3/issues/labels/#remove-a-label-from-an-issue
+ *
+ * @param string $username
+ * @param string $repository
+ * @param string $issue
+ * @param string $label
+ *
+ * @return null
+ */
+ public function remove($username, $repository, $issue, $label)
+ {
+ return $this->delete('/repos/'.rawurlencode($username).'/'.rawurlencode($repository).'/issues/'.rawurlencode($issue).'/labels/'.rawurlencode($label));
+ }
+
+ /**
+ * Remove all labels from an issue.
+ *
+ * @link https://developer.github.com/v3/issues/labels/#replace-all-labels-for-an-issue
+ *
+ * @param string $username
+ * @param string $repository
+ * @param string $issue
+ *
+ * @return null
+ */
+ public function clear($username, $repository, $issue)
+ {
+ return $this->delete('/repos/'.rawurlencode($username).'/'.rawurlencode($repository).'/issues/'.rawurlencode($issue).'/labels');
+ }
+}
diff --git a/vendor/knplabs/github-api/lib/Github/Api/Issue/Milestones.php b/vendor/knplabs/github-api/lib/Github/Api/Issue/Milestones.php
new file mode 100644
index 00000000..11f0e94c
--- /dev/null
+++ b/vendor/knplabs/github-api/lib/Github/Api/Issue/Milestones.php
@@ -0,0 +1,139 @@
+
+ */
+class Milestones extends AbstractApi
+{
+ /**
+ * Get all milestones for a repository.
+ *
+ * @link https://developer.github.com/v3/issues/milestones/#list-milestones-for-a-repository
+ *
+ * @param string $username
+ * @param string $repository
+ * @param array $params
+ *
+ * @return array
+ */
+ public function all($username, $repository, array $params = [])
+ {
+ if (isset($params['state']) && !in_array($params['state'], ['open', 'closed', 'all'])) {
+ $params['state'] = 'open';
+ }
+ if (isset($params['sort']) && !in_array($params['sort'], ['due_date', 'completeness'])) {
+ $params['sort'] = 'due_date';
+ }
+ if (isset($params['direction']) && !in_array($params['direction'], ['asc', 'desc'])) {
+ $params['direction'] = 'asc';
+ }
+
+ return $this->get('/repos/'.rawurlencode($username).'/'.rawurlencode($repository).'/milestones', array_merge([
+ 'page' => 1,
+ 'state' => 'open',
+ 'sort' => 'due_date',
+ 'direction' => 'asc',
+ ], $params));
+ }
+
+ /**
+ * Get a milestone for a repository.
+ *
+ * @link https://developer.github.com/v3/issues/milestones/#get-a-single-milestone
+ *
+ * @param string $username
+ * @param string $repository
+ * @param int $id
+ *
+ * @return array
+ */
+ public function show($username, $repository, $id)
+ {
+ return $this->get('/repos/'.rawurlencode($username).'/'.rawurlencode($repository).'/milestones/'.rawurlencode($id));
+ }
+
+ /**
+ * Create a milestone for a repository.
+ *
+ * @link https://developer.github.com/v3/issues/milestones/#create-a-milestone
+ *
+ * @param string $username
+ * @param string $repository
+ * @param array $params
+ *
+ * @throws \Github\Exception\MissingArgumentException
+ *
+ * @return array
+ */
+ public function create($username, $repository, array $params)
+ {
+ if (!isset($params['title'])) {
+ throw new MissingArgumentException('title');
+ }
+ if (isset($params['state']) && !in_array($params['state'], ['open', 'closed'])) {
+ $params['state'] = 'open';
+ }
+
+ return $this->post('/repos/'.rawurlencode($username).'/'.rawurlencode($repository).'/milestones', $params);
+ }
+
+ /**
+ * Update a milestone for a repository.
+ *
+ * @link https://developer.github.com/v3/issues/milestones/#update-a-milestone
+ *
+ * @param string $username
+ * @param string $repository
+ * @param int $id
+ * @param array $params
+ *
+ * @return array
+ */
+ public function update($username, $repository, $id, array $params)
+ {
+ if (isset($params['state']) && !in_array($params['state'], ['open', 'closed'])) {
+ $params['state'] = 'open';
+ }
+
+ return $this->patch('/repos/'.rawurlencode($username).'/'.rawurlencode($repository).'/milestones/'.rawurlencode($id), $params);
+ }
+
+ /**
+ * Delete a milestone for a repository.
+ *
+ * @link https://developer.github.com/v3/issues/milestones/#delete-a-milestone
+ *
+ * @param string $username
+ * @param string $repository
+ * @param int $id
+ *
+ * @return null
+ */
+ public function remove($username, $repository, $id)
+ {
+ return $this->delete('/repos/'.rawurlencode($username).'/'.rawurlencode($repository).'/milestones/'.rawurlencode($id));
+ }
+
+ /**
+ * Get the labels of a milestone.
+ *
+ * @link https://developer.github.com/v3/issues/labels/#get-labels-for-every-issue-in-a-milestone
+ *
+ * @param string $username
+ * @param string $repository
+ * @param int $id
+ *
+ * @return array
+ */
+ public function labels($username, $repository, $id)
+ {
+ return $this->get('/repos/'.rawurlencode($username).'/'.rawurlencode($repository).'/milestones/'.rawurlencode($id).'/labels');
+ }
+}
diff --git a/vendor/knplabs/github-api/lib/Github/Api/Issue/Timeline.php b/vendor/knplabs/github-api/lib/Github/Api/Issue/Timeline.php
new file mode 100644
index 00000000..c0f76f2f
--- /dev/null
+++ b/vendor/knplabs/github-api/lib/Github/Api/Issue/Timeline.php
@@ -0,0 +1,34 @@
+acceptHeaderValue = 'application/vnd.github.mockingbird-preview';
+
+ return $this;
+ }
+
+ /**
+ * Get all events for a specific issue.
+ *
+ * @link https://developer.github.com/v3/issues/timeline/#list-events-for-an-issue
+ *
+ * @param string $username
+ * @param string $repository
+ * @param int $issue
+ *
+ * @return array
+ */
+ public function all($username, $repository, $issue)
+ {
+ return $this->get('/repos/'.rawurlencode($username).'/'.rawurlencode($repository).'/issues/'.rawurlencode($issue).'/timeline');
+ }
+}
diff --git a/vendor/knplabs/github-api/lib/Github/Api/Markdown.php b/vendor/knplabs/github-api/lib/Github/Api/Markdown.php
new file mode 100644
index 00000000..977b1d04
--- /dev/null
+++ b/vendor/knplabs/github-api/lib/Github/Api/Markdown.php
@@ -0,0 +1,49 @@
+
+ */
+class Markdown extends AbstractApi
+{
+ /**
+ * @param string $text
+ * @param string $mode
+ * @param string $context
+ *
+ * @return string
+ */
+ public function render($text, $mode = 'markdown', $context = null)
+ {
+ if (!in_array($mode, ['gfm', 'markdown'])) {
+ $mode = 'markdown';
+ }
+
+ $params = [
+ 'text' => $text,
+ 'mode' => $mode,
+ ];
+ if (null !== $context && 'gfm' === $mode) {
+ $params['context'] = $context;
+ }
+
+ return $this->post('/markdown', $params);
+ }
+
+ /**
+ * @param string $file
+ *
+ * @return string
+ */
+ public function renderRaw($file)
+ {
+ return $this->post('/markdown/raw', [
+ 'file' => $file,
+ ]);
+ }
+}
diff --git a/vendor/knplabs/github-api/lib/Github/Api/Meta.php b/vendor/knplabs/github-api/lib/Github/Api/Meta.php
new file mode 100644
index 00000000..0ec81f65
--- /dev/null
+++ b/vendor/knplabs/github-api/lib/Github/Api/Meta.php
@@ -0,0 +1,23 @@
+
+ */
+class Meta extends AbstractApi
+{
+ /**
+ * Get the ip address of the hook and git servers for the GitHub.com service.
+ *
+ * @return array Information about the service of GitHub.com
+ */
+ public function service()
+ {
+ return $this->get('/meta');
+ }
+}
diff --git a/vendor/knplabs/github-api/lib/Github/Api/Miscellaneous/CodeOfConduct.php b/vendor/knplabs/github-api/lib/Github/Api/Miscellaneous/CodeOfConduct.php
new file mode 100644
index 00000000..64bfaa8b
--- /dev/null
+++ b/vendor/knplabs/github-api/lib/Github/Api/Miscellaneous/CodeOfConduct.php
@@ -0,0 +1,44 @@
+acceptHeaderValue = 'application/vnd.github.scarlet-witch-preview+json';
+
+ return $this;
+ }
+
+ /**
+ * List all codes of conduct.
+ *
+ * @link https://developer.github.com/v3/codes_of_conduct/#list-all-codes-of-conduct
+ *
+ * @return array
+ */
+ public function all()
+ {
+ return $this->get('/codes_of_conduct');
+ }
+
+ /**
+ * Get an individual code of conduct.
+ *
+ * @link https://developer.github.com/v3/codes_of_conduct/#get-an-individual-code-of-conduct
+ *
+ * @param string $key
+ *
+ * @return array
+ */
+ public function show($key)
+ {
+ return $this->get('/codes_of_conduct/'.rawurlencode($key));
+ }
+}
diff --git a/vendor/knplabs/github-api/lib/Github/Api/Miscellaneous/Emojis.php b/vendor/knplabs/github-api/lib/Github/Api/Miscellaneous/Emojis.php
new file mode 100644
index 00000000..2a940f6d
--- /dev/null
+++ b/vendor/knplabs/github-api/lib/Github/Api/Miscellaneous/Emojis.php
@@ -0,0 +1,20 @@
+get('/emojis');
+ }
+}
diff --git a/vendor/knplabs/github-api/lib/Github/Api/Miscellaneous/Gitignore.php b/vendor/knplabs/github-api/lib/Github/Api/Miscellaneous/Gitignore.php
new file mode 100644
index 00000000..c5f03e7e
--- /dev/null
+++ b/vendor/knplabs/github-api/lib/Github/Api/Miscellaneous/Gitignore.php
@@ -0,0 +1,34 @@
+get('/gitignore/templates');
+ }
+
+ /**
+ * Get a single template.
+ *
+ * @link https://developer.github.com/v3/gitignore/#get-a-single-template
+ *
+ * @param string $template
+ *
+ * @return array
+ */
+ public function show($template)
+ {
+ return $this->get('/gitignore/templates/'.rawurlencode($template));
+ }
+}
diff --git a/vendor/knplabs/github-api/lib/Github/Api/Notification.php b/vendor/knplabs/github-api/lib/Github/Api/Notification.php
new file mode 100644
index 00000000..0d73d607
--- /dev/null
+++ b/vendor/knplabs/github-api/lib/Github/Api/Notification.php
@@ -0,0 +1,90 @@
+
+ */
+class Notification extends AbstractApi
+{
+ /**
+ * Get a listing of notifications.
+ *
+ * @link https://developer.github.com/v3/activity/notifications/
+ *
+ * @param bool $includingRead
+ * @param bool $participating
+ * @param DateTime|null $since
+ *
+ * @return array array of notifications
+ */
+ public function all($includingRead = false, $participating = false, DateTime $since = null, DateTime $before = null)
+ {
+ $parameters = [
+ 'all' => $includingRead,
+ 'participating' => $participating,
+ ];
+
+ if ($since !== null) {
+ $parameters['since'] = $since->format(DateTime::ISO8601);
+ }
+
+ if ($before !== null) {
+ $parameters['before'] = $before->format(DateTime::ISO8601);
+ }
+
+ return $this->get('/notifications', $parameters);
+ }
+
+ /**
+ * Marks all notifications as read from the current date.
+ *
+ * Optionally give DateTime to mark as read before that date.
+ *
+ * @link https://developer.github.com/v3/activity/notifications/#mark-as-read
+ *
+ * @param DateTime|null $since
+ */
+ public function markRead(DateTime $since = null)
+ {
+ $parameters = [];
+
+ if ($since !== null) {
+ $parameters['last_read_at'] = $since->format(DateTime::ISO8601);
+ }
+
+ $this->put('/notifications', $parameters);
+ }
+
+ /**
+ * Mark a single thread as read using its ID.
+ *
+ * @link https://developer.github.com/v3/activity/notifications/#mark-a-thread-as-read
+ *
+ * @param int $id
+ */
+ public function markThreadRead($id)
+ {
+ $this->patch('/notifications/threads/'.$id);
+ }
+
+ /**
+ * Gets a single thread using its ID.
+ *
+ * @link https://developer.github.com/v3/activity/notifications/#view-a-single-thread
+ *
+ * @param int $id
+ */
+ public function id($id)
+ {
+ return $this->get('/notifications/threads/'.$id);
+ }
+}
diff --git a/vendor/knplabs/github-api/lib/Github/Api/Organization.php b/vendor/knplabs/github-api/lib/Github/Api/Organization.php
new file mode 100644
index 00000000..49ca5c91
--- /dev/null
+++ b/vendor/knplabs/github-api/lib/Github/Api/Organization.php
@@ -0,0 +1,104 @@
+
+ * @author Joseph Bielawski
+ */
+class Organization extends AbstractApi
+{
+ /**
+ * @link https://developer.github.com/v3/orgs/#list-all-organizations
+ *
+ * @return array the organizations
+ */
+ public function all($since = '')
+ {
+ return $this->get('/organizations?since='.rawurlencode($since));
+ }
+
+ /**
+ * Get extended information about an organization by its name.
+ *
+ * @link http://developer.github.com/v3/orgs/#get
+ *
+ * @param string $organization the organization to show
+ *
+ * @return array information about the organization
+ */
+ public function show($organization)
+ {
+ return $this->get('/orgs/'.rawurlencode($organization));
+ }
+
+ public function update($organization, array $params)
+ {
+ return $this->patch('/orgs/'.rawurlencode($organization), $params);
+ }
+
+ /**
+ * List all repositories across all the organizations that you can access.
+ *
+ * @link http://developer.github.com/v3/repos/#list-organization-repositories
+ *
+ * @param string $organization the user name
+ * @param string $type the type of repositories
+ * @param int $page the page
+ *
+ * @return array the repositories
+ */
+ public function repositories($organization, $type = 'all', $page = 1)
+ {
+ return $this->get('/orgs/'.rawurlencode($organization).'/repos', [
+ 'type' => $type,
+ 'page' => $page,
+ ]);
+ }
+
+ /**
+ * @return Members
+ */
+ public function members()
+ {
+ return new Members($this->client);
+ }
+
+ /**
+ * @return Hooks
+ */
+ public function hooks()
+ {
+ return new Hooks($this->client);
+ }
+
+ /**
+ * @return Teams
+ */
+ public function teams()
+ {
+ return new Teams($this->client);
+ }
+
+ /**
+ * @link http://developer.github.com/v3/issues/#list-issues
+ *
+ * @param $organization
+ * @param array $params
+ * @param int $page
+ *
+ * @return array
+ */
+ public function issues($organization, array $params = [], $page = 1)
+ {
+ return $this->get('/orgs/'.rawurlencode($organization).'/issues', array_merge(['page' => $page], $params));
+ }
+}
diff --git a/vendor/knplabs/github-api/lib/Github/Api/Organization/Hooks.php b/vendor/knplabs/github-api/lib/Github/Api/Organization/Hooks.php
new file mode 100644
index 00000000..b68a43bc
--- /dev/null
+++ b/vendor/knplabs/github-api/lib/Github/Api/Organization/Hooks.php
@@ -0,0 +1,111 @@
+get('/orgs/'.rawurlencode($organization).'/hooks');
+ }
+
+ /**
+ * Get a single hook.
+ *
+ * @link https://developer.github.com/v3/orgs/hooks/#get-single-hook
+ *
+ * @param string $organization
+ * @param int $id
+ *
+ * @return array
+ */
+ public function show($organization, $id)
+ {
+ return $this->get('/orgs/'.rawurlencode($organization).'/hooks/'.rawurlencode($id));
+ }
+
+ /**
+ * Create a hook.
+ *
+ * @link https://developer.github.com/v3/orgs/hooks/#create-a-hook
+ *
+ * @param string $organization
+ * @param array $params
+ *
+ * @throws \Github\Exception\MissingArgumentException
+ *
+ * @return array
+ */
+ public function create($organization, array $params)
+ {
+ if (!isset($params['name'], $params['config'])) {
+ throw new MissingArgumentException(['name', 'config']);
+ }
+
+ return $this->post('/orgs/'.rawurlencode($organization).'/hooks', $params);
+ }
+
+ /**
+ * Edit a hook.
+ *
+ * @link https://developer.github.com/v3/orgs/hooks/#edit-a-hook
+ *
+ * @param string $organization
+ * @param int $id
+ * @param array $params
+ *
+ * @throws \Github\Exception\MissingArgumentException
+ *
+ * @return array
+ */
+ public function update($organization, $id, array $params)
+ {
+ if (!isset($params['config'])) {
+ throw new MissingArgumentException(['config']);
+ }
+
+ return $this->patch('/orgs/'.rawurlencode($organization).'/hooks/'.rawurlencode($id), $params);
+ }
+
+ /**
+ * Ping a hook.
+ *
+ * @link https://developer.github.com/v3/orgs/hooks/#ping-a-hook
+ *
+ * @param string $organization
+ * @param int $id
+ *
+ * @return null
+ */
+ public function ping($organization, $id)
+ {
+ return $this->post('/orgs/'.rawurlencode($organization).'/hooks/'.rawurlencode($id).'/pings');
+ }
+
+ /**
+ * Delete a hook.
+ *
+ * @link https://developer.github.com/v3/orgs/hooks/#delete-a-hook
+ *
+ * @param string $organization
+ * @param int $id
+ *
+ * @return null
+ */
+ public function remove($organization, $id)
+ {
+ return $this->delete('/orgs/'.rawurlencode($organization).'/hooks/'.rawurlencode($id));
+ }
+}
diff --git a/vendor/knplabs/github-api/lib/Github/Api/Organization/Members.php b/vendor/knplabs/github-api/lib/Github/Api/Organization/Members.php
new file mode 100644
index 00000000..3639e3ba
--- /dev/null
+++ b/vendor/knplabs/github-api/lib/Github/Api/Organization/Members.php
@@ -0,0 +1,75 @@
+
+ */
+class Members extends AbstractApi
+{
+ public function all($organization, $type = null, $filter = 'all', $role = null)
+ {
+ $parameters = [];
+ $path = '/orgs/'.rawurlencode($organization).'/';
+ if (null === $type) {
+ $path .= 'members';
+ if (null !== $filter) {
+ $parameters['filter'] = $filter;
+ }
+ if (null !== $role) {
+ $parameters['role'] = $role;
+ }
+ } else {
+ $path .= 'public_members';
+ }
+
+ return $this->get($path, $parameters);
+ }
+
+ public function show($organization, $username)
+ {
+ return $this->get('/orgs/'.rawurlencode($organization).'/members/'.rawurlencode($username));
+ }
+
+ public function member($organization, $username)
+ {
+ return $this->get('/orgs/'.rawurlencode($organization).'/memberships/'.rawurlencode($username));
+ }
+
+ public function check($organization, $username)
+ {
+ return $this->get('/orgs/'.rawurlencode($organization).'/public_members/'.rawurlencode($username));
+ }
+
+ public function publicize($organization, $username)
+ {
+ return $this->put('/orgs/'.rawurlencode($organization).'/public_members/'.rawurlencode($username));
+ }
+
+ public function conceal($organization, $username)
+ {
+ return $this->delete('/orgs/'.rawurlencode($organization).'/public_members/'.rawurlencode($username));
+ }
+
+ /*
+ * Add user to organization
+ */
+ public function add($organization, $username)
+ {
+ return $this->put('/orgs/'.rawurlencode($organization).'/memberships/'.rawurlencode($username));
+ }
+
+ public function addMember($organization, $username)
+ {
+ return $this->add($organization, $username);
+ }
+
+ public function remove($organization, $username)
+ {
+ return $this->delete('/orgs/'.rawurlencode($organization).'/members/'.rawurlencode($username));
+ }
+}
diff --git a/vendor/knplabs/github-api/lib/Github/Api/Organization/Projects.php b/vendor/knplabs/github-api/lib/Github/Api/Organization/Projects.php
new file mode 100644
index 00000000..2bb7196e
--- /dev/null
+++ b/vendor/knplabs/github-api/lib/Github/Api/Organization/Projects.php
@@ -0,0 +1,23 @@
+get('/orgs/'.rawurlencode($organization).'/projects', array_merge(['page' => 1], $params));
+ }
+
+ public function create($organization, array $params)
+ {
+ if (!isset($params['name'])) {
+ throw new MissingArgumentException(['name']);
+ }
+
+ return $this->post('/orgs/'.rawurlencode($organization).'/projects', $params);
+ }
+}
diff --git a/vendor/knplabs/github-api/lib/Github/Api/Organization/Teams.php b/vendor/knplabs/github-api/lib/Github/Api/Organization/Teams.php
new file mode 100644
index 00000000..401dbe4f
--- /dev/null
+++ b/vendor/knplabs/github-api/lib/Github/Api/Organization/Teams.php
@@ -0,0 +1,100 @@
+
+ */
+class Teams extends AbstractApi
+{
+ public function all($organization)
+ {
+ return $this->get('/orgs/'.rawurlencode($organization).'/teams');
+ }
+
+ public function create($organization, array $params)
+ {
+ if (!isset($params['name'])) {
+ throw new MissingArgumentException('name');
+ }
+ if (isset($params['repo_names']) && !is_array($params['repo_names'])) {
+ $params['repo_names'] = [$params['repo_names']];
+ }
+ if (isset($params['permission']) && !in_array($params['permission'], ['pull', 'push', 'admin'])) {
+ $params['permission'] = 'pull';
+ }
+
+ return $this->post('/orgs/'.rawurlencode($organization).'/teams', $params);
+ }
+
+ public function show($team)
+ {
+ return $this->get('/teams/'.rawurlencode($team));
+ }
+
+ public function update($team, array $params)
+ {
+ if (!isset($params['name'])) {
+ throw new MissingArgumentException('name');
+ }
+ if (isset($params['permission']) && !in_array($params['permission'], ['pull', 'push', 'admin'])) {
+ $params['permission'] = 'pull';
+ }
+
+ return $this->patch('/teams/'.rawurlencode($team), $params);
+ }
+
+ public function remove($team)
+ {
+ return $this->delete('/teams/'.rawurlencode($team));
+ }
+
+ public function members($team)
+ {
+ return $this->get('/teams/'.rawurlencode($team).'/members');
+ }
+
+ public function check($team, $username)
+ {
+ return $this->get('/teams/'.rawurlencode($team).'/memberships/'.rawurlencode($username));
+ }
+
+ public function addMember($team, $username)
+ {
+ return $this->put('/teams/'.rawurlencode($team).'/memberships/'.rawurlencode($username));
+ }
+
+ public function removeMember($team, $username)
+ {
+ return $this->delete('/teams/'.rawurlencode($team).'/memberships/'.rawurlencode($username));
+ }
+
+ public function repositories($team)
+ {
+ return $this->get('/teams/'.rawurlencode($team).'/repos');
+ }
+
+ public function repository($team, $organization, $repository)
+ {
+ return $this->get('/teams/'.rawurlencode($team).'/repos/'.rawurlencode($organization).'/'.rawurlencode($repository));
+ }
+
+ public function addRepository($team, $organization, $repository, $params = [])
+ {
+ if (isset($params['permission']) && !in_array($params['permission'], ['pull', 'push', 'admin'])) {
+ $params['permission'] = 'pull';
+ }
+
+ return $this->put('/teams/'.rawurlencode($team).'/repos/'.rawurlencode($organization).'/'.rawurlencode($repository), $params);
+ }
+
+ public function removeRepository($team, $organization, $repository)
+ {
+ return $this->delete('/teams/'.rawurlencode($team).'/repos/'.rawurlencode($organization).'/'.rawurlencode($repository));
+ }
+}
diff --git a/vendor/knplabs/github-api/lib/Github/Api/Project/AbstractProjectApi.php b/vendor/knplabs/github-api/lib/Github/Api/Project/AbstractProjectApi.php
new file mode 100644
index 00000000..15274d7d
--- /dev/null
+++ b/vendor/knplabs/github-api/lib/Github/Api/Project/AbstractProjectApi.php
@@ -0,0 +1,45 @@
+acceptHeaderValue = 'application/vnd.github.inertia-preview+json';
+
+ return $this;
+ }
+
+ public function show($id, array $params = [])
+ {
+ return $this->get('/projects/'.rawurlencode($id), array_merge(['page' => 1], $params));
+ }
+
+ public function update($id, array $params)
+ {
+ return $this->patch('/projects/'.rawurlencode($id), $params);
+ }
+
+ public function deleteProject($id)
+ {
+ return $this->delete('/projects/'.rawurlencode($id));
+ }
+
+ public function columns()
+ {
+ return new Columns($this->client);
+ }
+}
diff --git a/vendor/knplabs/github-api/lib/Github/Api/Project/Cards.php b/vendor/knplabs/github-api/lib/Github/Api/Project/Cards.php
new file mode 100644
index 00000000..758e7708
--- /dev/null
+++ b/vendor/knplabs/github-api/lib/Github/Api/Project/Cards.php
@@ -0,0 +1,60 @@
+acceptHeaderValue = 'application/vnd.github.inertia-preview+json';
+
+ return $this;
+ }
+
+ public function all($columnId, array $params = [])
+ {
+ return $this->get('/projects/columns/'.rawurlencode($columnId).'/cards', array_merge(['page' => 1], $params));
+ }
+
+ public function show($id)
+ {
+ return $this->get('/projects/columns/cards/'.rawurlencode($id));
+ }
+
+ public function create($columnId, array $params)
+ {
+ return $this->post('/projects/columns/'.rawurlencode($columnId).'/cards', $params);
+ }
+
+ public function update($id, array $params)
+ {
+ return $this->patch('/projects/columns/cards/'.rawurlencode($id), $params);
+ }
+
+ public function deleteCard($id)
+ {
+ return $this->delete('/projects/columns/cards/'.rawurlencode($id));
+ }
+
+ public function move($id, array $params)
+ {
+ if (!isset($params['position'])) {
+ throw new MissingArgumentException(['position']);
+ }
+
+ return $this->post('/projects/columns/cards/'.rawurlencode($id).'/moves', $params);
+ }
+}
diff --git a/vendor/knplabs/github-api/lib/Github/Api/Project/Columns.php b/vendor/knplabs/github-api/lib/Github/Api/Project/Columns.php
new file mode 100644
index 00000000..22e5cbaf
--- /dev/null
+++ b/vendor/knplabs/github-api/lib/Github/Api/Project/Columns.php
@@ -0,0 +1,73 @@
+acceptHeaderValue = 'application/vnd.github.inertia-preview+json';
+
+ return $this;
+ }
+
+ public function all($projectId, array $params = [])
+ {
+ return $this->get('/projects/'.rawurlencode($projectId).'/columns', array_merge(['page' => 1], $params));
+ }
+
+ public function show($id)
+ {
+ return $this->get('/projects/columns/'.rawurlencode($id));
+ }
+
+ public function create($projectId, array $params)
+ {
+ if (!isset($params['name'])) {
+ throw new MissingArgumentException(['name']);
+ }
+
+ return $this->post('/projects/'.rawurlencode($projectId).'/columns', $params);
+ }
+
+ public function update($id, array $params)
+ {
+ if (!isset($params['name'])) {
+ throw new MissingArgumentException(['name']);
+ }
+
+ return $this->patch('/projects/columns/'.rawurlencode($id), $params);
+ }
+
+ public function deleteColumn($id)
+ {
+ return $this->delete('/projects/columns/'.rawurlencode($id));
+ }
+
+ public function move($id, array $params)
+ {
+ if (!isset($params['position'])) {
+ throw new MissingArgumentException(['position']);
+ }
+
+ return $this->post('/projects/columns/'.rawurlencode($id).'/moves', $params);
+ }
+
+ public function cards()
+ {
+ return new Cards($this->client);
+ }
+}
diff --git a/vendor/knplabs/github-api/lib/Github/Api/PullRequest.php b/vendor/knplabs/github-api/lib/Github/Api/PullRequest.php
new file mode 100644
index 00000000..933dd7b1
--- /dev/null
+++ b/vendor/knplabs/github-api/lib/Github/Api/PullRequest.php
@@ -0,0 +1,203 @@
+
+ */
+class PullRequest extends AbstractApi
+{
+ use AcceptHeaderTrait;
+
+ /**
+ * Configure the body type.
+ *
+ * @link https://developer.github.com/v3/pulls/#custom-media-types
+ *
+ * @param string|null $bodyType
+ * @param string|null $apiVersion
+ *
+ * @return self
+ */
+ public function configure($bodyType = null, $apiVersion = null)
+ {
+ if (!in_array($apiVersion, [])) {
+ $apiVersion = $this->client->getApiVersion();
+ }
+
+ if (!in_array($bodyType, ['text', 'html', 'full', 'diff', 'patch'])) {
+ $bodyType = 'raw';
+ }
+
+ if (!in_array($bodyType, ['diff', 'patch'])) {
+ $bodyType .= '+json';
+ }
+
+ $this->acceptHeaderValue = sprintf('application/vnd.github.%s.%s', $apiVersion, $bodyType);
+
+ return $this;
+ }
+
+ /**
+ * Get a listing of a project's pull requests by the username, repository and (optionally) state.
+ *
+ * @link http://developer.github.com/v3/pulls/
+ *
+ * @param string $username the username
+ * @param string $repository the repository
+ * @param array $params a list of extra parameters.
+ *
+ * @return array array of pull requests for the project
+ */
+ public function all($username, $repository, array $params = [])
+ {
+ $parameters = array_merge([
+ 'page' => 1,
+ 'per_page' => 30,
+ ], $params);
+
+ return $this->get('/repos/'.rawurlencode($username).'/'.rawurlencode($repository).'/pulls', $parameters);
+ }
+
+ /**
+ * Show all details of a pull request, including the discussions.
+ *
+ * @link http://developer.github.com/v3/pulls/
+ *
+ * @param string $username the username
+ * @param string $repository the repository
+ * @param int $id the ID of the pull request for which details are retrieved
+ *
+ * @return array|string pull request details
+ */
+ public function show($username, $repository, $id)
+ {
+ return $this->get('/repos/'.rawurlencode($username).'/'.rawurlencode($repository).'/pulls/'.rawurlencode($id));
+ }
+
+ public function commits($username, $repository, $id)
+ {
+ return $this->get('/repos/'.rawurlencode($username).'/'.rawurlencode($repository).'/pulls/'.rawurlencode($id).'/commits');
+ }
+
+ public function files($username, $repository, $id)
+ {
+ return $this->get('/repos/'.rawurlencode($username).'/'.rawurlencode($repository).'/pulls/'.rawurlencode($id).'/files');
+ }
+
+ /**
+ * All statuses which are the statuses of its head branch.
+ *
+ * @see http://developer.github.com/v3/pulls/
+ *
+ * @param string $username the username
+ * @param string $repository the repository
+ * @param int $id the ID of the pull request for which statuses are retrieved
+ *
+ * @return array array of statuses for the project
+ */
+ public function status($username, $repository, $id)
+ {
+ $link = $this->show($username, $repository, $id)['_links']['statuses']['href'];
+
+ return $this->get($link);
+ }
+
+ public function comments()
+ {
+ return new Comments($this->client);
+ }
+
+ public function reviews()
+ {
+ return new Review($this->client);
+ }
+
+ public function reviewRequests()
+ {
+ return new ReviewRequest($this->client);
+ }
+
+ /**
+ * Create a pull request.
+ *
+ * @link http://developer.github.com/v3/pulls/
+ *
+ * @param string $username the username
+ * @param string $repository the repository
+ * @param array $params A String of the branch or commit SHA that you want your changes to be pulled to.
+ * A String of the branch or commit SHA of your changes. Typically this will be a branch.
+ * If the branch is in a fork of the original repository, specify the username first:
+ * "my-user:some-branch". The String title of the Pull Request. The String body of
+ * the Pull Request. The issue number. Used when title and body is not set.
+ *
+ * @throws MissingArgumentException
+ *
+ * @return array
+ */
+ public function create($username, $repository, array $params)
+ {
+ // Two ways to create PR, using issue or title
+ if (!isset($params['issue']) && !isset($params['title'])) {
+ throw new MissingArgumentException(['issue', 'title']);
+ }
+
+ if (!isset($params['base'], $params['head'])) {
+ throw new MissingArgumentException(['base', 'head']);
+ }
+
+ // If `issue` is not sent, then `body` must be sent
+ if (!isset($params['issue']) && !isset($params['body'])) {
+ throw new MissingArgumentException(['issue', 'body']);
+ }
+
+ return $this->post('/repos/'.rawurlencode($username).'/'.rawurlencode($repository).'/pulls', $params);
+ }
+
+ public function update($username, $repository, $id, array $params)
+ {
+ if (isset($params['state']) && !in_array($params['state'], ['open', 'closed'])) {
+ $params['state'] = 'open';
+ }
+
+ return $this->patch('/repos/'.rawurlencode($username).'/'.rawurlencode($repository).'/pulls/'.rawurlencode($id), $params);
+ }
+
+ public function merged($username, $repository, $id)
+ {
+ return $this->get('/repos/'.rawurlencode($username).'/'.rawurlencode($repository).'/pulls/'.rawurlencode($id).'/merge');
+ }
+
+ public function merge($username, $repository, $id, $message, $sha, $mergeMethod = 'merge', $title = null)
+ {
+ if (is_bool($mergeMethod)) {
+ $mergeMethod = $mergeMethod ? 'squash' : 'merge';
+ }
+
+ if (!in_array($mergeMethod, ['merge', 'squash', 'rebase'], true)) {
+ throw new InvalidArgumentException(sprintf('"$mergeMethod" must be one of ["merge", "squash", "rebase"] ("%s" given).', $mergeMethod));
+ }
+
+ $params = [
+ 'commit_message' => $message,
+ 'sha' => $sha,
+ 'merge_method' => $mergeMethod,
+ ];
+
+ if (is_string($title)) {
+ $params['commit_title'] = $title;
+ }
+
+ return $this->put('/repos/'.rawurlencode($username).'/'.rawurlencode($repository).'/pulls/'.rawurlencode($id).'/merge', $params);
+ }
+}
diff --git a/vendor/knplabs/github-api/lib/Github/Api/PullRequest/Comments.php b/vendor/knplabs/github-api/lib/Github/Api/PullRequest/Comments.php
new file mode 100644
index 00000000..183bfbe0
--- /dev/null
+++ b/vendor/knplabs/github-api/lib/Github/Api/PullRequest/Comments.php
@@ -0,0 +1,153 @@
+
+ */
+class Comments extends AbstractApi
+{
+ use AcceptHeaderTrait;
+
+ /**
+ * Configure the body type.
+ *
+ * @link https://developer.github.com/v3/pulls/comments/#custom-media-types
+ *
+ * @param string|null $bodyType
+ * @param string|null @apiVersion
+ *
+ * @return self
+ */
+ public function configure($bodyType = null, $apiVersion = null)
+ {
+ if (!in_array($apiVersion, ['squirrel-girl-preview'])) {
+ $apiVersion = $this->client->getApiVersion();
+ }
+
+ if (!in_array($bodyType, ['text', 'html', 'full'])) {
+ $bodyType = 'raw';
+ }
+
+ $this->acceptHeaderValue = sprintf('application/vnd.github.%s.%s+json', $apiVersion, $bodyType);
+
+ return $this;
+ }
+
+ /**
+ * Get a listing of a pull request's comments by the username, repository and pull request number
+ * or all repository comments by the username and repository.
+ *
+ * @link https://developer.github.com/v3/pulls/comments/#list-comments-on-a-pull-request
+ * @link https://developer.github.com/v3/pulls/comments/#list-comments-in-a-repository
+ *
+ * @param string $username the username
+ * @param string $repository the repository
+ * @param int|null $pullRequest the pull request number
+ * @param array $params a list of extra parameters.
+ *
+ * @return array
+ */
+ public function all($username, $repository, $pullRequest = null, array $params = [])
+ {
+ if (null !== $pullRequest) {
+ return $this->get('/repos/'.rawurlencode($username).'/'.rawurlencode($repository).'/pulls/'.rawurlencode($pullRequest).'/comments');
+ }
+
+ $parameters = array_merge([
+ 'page' => 1,
+ 'per_page' => 30,
+ ], $params);
+
+ return $this->get('/repos/'.rawurlencode($username).'/'.rawurlencode($repository).'/pulls/comments', $parameters);
+ }
+
+ /**
+ * Get a single pull request comment by the username, repository and comment id.
+ *
+ * @link https://developer.github.com/v3/pulls/comments/#get-a-single-comment
+ *
+ * @param string $username the username
+ * @param string $repository the repository
+ * @param int $comment the comment id
+ *
+ * @return array
+ */
+ public function show($username, $repository, $comment)
+ {
+ return $this->get('/repos/'.rawurlencode($username).'/'.rawurlencode($repository).'/pulls/comments/'.rawurlencode($comment));
+ }
+
+ /**
+ * Create a pull request comment by the username, repository and pull request number.
+ *
+ * @link https://developer.github.com/v3/pulls/comments/#create-a-comment
+ *
+ * @param string $username the username
+ * @param string $repository the repository
+ * @param int $pullRequest the pull request number
+ * @param array $params a list of extra parameters.
+ *
+ * @throws MissingArgumentException
+ *
+ * @return array
+ */
+ public function create($username, $repository, $pullRequest, array $params)
+ {
+ if (!isset($params['body'])) {
+ throw new MissingArgumentException('body');
+ }
+
+ // If `in_reply_to` is set, other options are not necessary anymore
+ if (!isset($params['in_reply_to']) && !isset($params['commit_id'], $params['path'], $params['position'])) {
+ throw new MissingArgumentException(['commit_id', 'path', 'position']);
+ }
+
+ return $this->post('/repos/'.rawurlencode($username).'/'.rawurlencode($repository).'/pulls/'.rawurlencode($pullRequest).'/comments', $params);
+ }
+
+ /**
+ * Update a pull request comment by the username, repository and comment id.
+ *
+ * @link https://developer.github.com/v3/pulls/comments/#edit-a-comment
+ *
+ * @param string $username the username
+ * @param string $repository the repository
+ * @param int $comment the comment id
+ * @param array $params a list of extra parameters.
+ *
+ * @throws MissingArgumentException
+ *
+ * @return array
+ */
+ public function update($username, $repository, $comment, array $params)
+ {
+ if (!isset($params['body'])) {
+ throw new MissingArgumentException('body');
+ }
+
+ return $this->patch('/repos/'.rawurlencode($username).'/'.rawurlencode($repository).'/pulls/comments/'.rawurlencode($comment), $params);
+ }
+
+ /**
+ * Delete a pull request comment by the username, repository and comment id.
+ *
+ * @link https://developer.github.com/v3/pulls/comments/#delete-a-comment
+ *
+ * @param string $username the username
+ * @param string $repository the repository
+ * @param int $comment the comment id
+ *
+ * @return string
+ */
+ public function remove($username, $repository, $comment)
+ {
+ return $this->delete('/repos/'.rawurlencode($username).'/'.rawurlencode($repository).'/pulls/comments/'.rawurlencode($comment));
+ }
+}
diff --git a/vendor/knplabs/github-api/lib/Github/Api/PullRequest/Review.php b/vendor/knplabs/github-api/lib/Github/Api/PullRequest/Review.php
new file mode 100644
index 00000000..f9795de0
--- /dev/null
+++ b/vendor/knplabs/github-api/lib/Github/Api/PullRequest/Review.php
@@ -0,0 +1,177 @@
+
+ */
+class Review extends AbstractApi
+{
+ use AcceptHeaderTrait;
+
+ public function configure()
+ {
+ return $this;
+ }
+
+ /**
+ * Get a listing of a pull request's reviews by the username, repository and pull request number.
+ *
+ * @link https://developer.github.com/v3/pulls/reviews/#list-reviews-on-a-pull-request
+ *
+ * @param string $username the username
+ * @param string $repository the repository
+ * @param int $pullRequest the pull request number
+ * @param array $params a list of extra parameters.
+ *
+ * @return array array of pull request reviews for the pull request
+ */
+ public function all($username, $repository, $pullRequest, array $params = [])
+ {
+ $parameters = array_merge([
+ 'page' => 1,
+ 'per_page' => 30,
+ ], $params);
+
+ return $this->get('/repos/'.rawurlencode($username).'/'.rawurlencode($repository).'/pulls/'.$pullRequest.'/reviews', $parameters);
+ }
+
+ /**
+ * Get a single pull request review by the username, repository, pull request number and the review id.
+ *
+ * @link https://developer.github.com/v3/pulls/reviews/#get-a-single-review
+ *
+ * @param string $username the username
+ * @param string $repository the repository
+ * @param int $pullRequest the pull request number
+ * @param int $id the review id
+ *
+ * @return array the pull request review
+ */
+ public function show($username, $repository, $pullRequest, $id)
+ {
+ return $this->get('/repos/'.rawurlencode($username).'/'.rawurlencode($repository).'/pulls/'.$pullRequest.'/reviews/'.$id);
+ }
+
+ /**
+ * Delete a single pull request review by the username, repository, pull request number and the review id.
+ *
+ * @link https://developer.github.com/v3/pulls/reviews/#delete-a-pending-review
+ *
+ * @param string $username the username
+ * @param string $repository the repository
+ * @param int $pullRequest the pull request number
+ * @param int $id the review id
+ *
+ * @return array|string
+ */
+ public function remove($username, $repository, $pullRequest, $id)
+ {
+ return $this->delete('/repos/'.rawurlencode($username).'/'.rawurlencode($repository).'/pulls/'.$pullRequest.'/reviews/'.$id);
+ }
+
+ /**
+ * Get comments for a single pull request review.
+ *
+ * @link https://developer.github.com/v3/pulls/reviews/#get-comments-for-a-single-review
+ *
+ * @param string $username the username
+ * @param string $repository the repository
+ * @param int $pullRequest the pull request number
+ * @param int $id the review id
+ *
+ * @return array|string
+ */
+ public function comments($username, $repository, $pullRequest, $id)
+ {
+ return $this->get('/repos/'.rawurlencode($username).'/'.rawurlencode($repository).'/pulls/'.rawurlencode($pullRequest).'/reviews/'.rawurlencode($id).'/comments');
+ }
+
+ /**
+ * Create a pull request review by the username, repository and pull request number.
+ *
+ * @link https://developer.github.com/v3/pulls/reviews/#create-a-pull-request-review
+ *
+ * @param string $username the username
+ * @param string $repository the repository
+ * @param int $pullRequest the pull request number
+ * @param array $params a list of extra parameters.
+ *
+ * @throws MissingArgumentException
+ *
+ * @return array the pull request review
+ */
+ public function create($username, $repository, $pullRequest, array $params = [])
+ {
+ if (array_key_exists('event', $params) && !in_array($params['event'], ['APPROVE', 'REQUEST_CHANGES', 'COMMENT'], true)) {
+ throw new InvalidArgumentException(sprintf('"event" must be one of ["APPROVE", "REQUEST_CHANGES", "COMMENT"] ("%s" given).', $params['event']));
+ }
+
+ return $this->post('/repos/'.rawurlencode($username).'/'.rawurlencode($repository).'/pulls/'.$pullRequest.'/reviews', $params);
+ }
+
+ /**
+ * Submit a pull request review by the username, repository, pull request number and the review id.
+ *
+ * @link https://developer.github.com/v3/pulls/reviews/#submit-a-pull-request-review
+ *
+ * @param string $username the username
+ * @param string $repository the repository
+ * @param int $pullRequest the pull request number
+ * @param int $id the review id
+ * @param array $params a list of extra parameters.
+ *
+ * @throws MissingArgumentException
+ *
+ * @return array the pull request review
+ */
+ public function submit($username, $repository, $pullRequest, $id, array $params = [])
+ {
+ if (!isset($params['event'])) {
+ throw new MissingArgumentException('event');
+ }
+
+ if (!in_array($params['event'], ['APPROVE', 'REQUEST_CHANGES', 'COMMENT'], true)) {
+ throw new InvalidArgumentException(sprintf('"event" must be one of ["APPROVE", "REQUEST_CHANGES", "COMMENT"] ("%s" given).', $params['event']));
+ }
+
+ return $this->post('/repos/'.rawurlencode($username).'/'.rawurlencode($repository).'/pulls/'.$pullRequest.'/reviews/'.$id.'/events', $params);
+ }
+
+ /**
+ * Dismiss a pull request review by the username, repository, pull request number and the review id.
+ *
+ * @link https://developer.github.com/v3/pulls/reviews/#dismiss-a-pull-request-review
+ *
+ * @param string $username the username
+ * @param string $repository the repository
+ * @param int $pullRequest the pull request number
+ * @param int $id the review id
+ * @param string $message a mandatory dismissal message
+ *
+ * @return array|string
+ */
+ public function dismiss($username, $repository, $pullRequest, $id, $message)
+ {
+ if (!is_string($message)) {
+ throw new InvalidArgumentException(sprintf('"message" must be a valid string ("%s" given).', gettype($message)));
+ }
+
+ if (empty($message)) {
+ throw new InvalidArgumentException('"message" is mandatory and cannot be empty');
+ }
+
+ return $this->put('/repos/'.rawurlencode($username).'/'.rawurlencode($repository).'/pulls/'.$pullRequest.'/reviews/'.$id.'/dismissals', [
+ 'message' => $message,
+ ]);
+ }
+}
diff --git a/vendor/knplabs/github-api/lib/Github/Api/PullRequest/ReviewRequest.php b/vendor/knplabs/github-api/lib/Github/Api/PullRequest/ReviewRequest.php
new file mode 100644
index 00000000..8321fd27
--- /dev/null
+++ b/vendor/knplabs/github-api/lib/Github/Api/PullRequest/ReviewRequest.php
@@ -0,0 +1,64 @@
+get('/repos/'.rawurlencode($username).'/'.rawurlencode($repository).'/pulls/'.$pullRequest.'/requested_reviewers', $params);
+ }
+
+ /**
+ * @link https://developer.github.com/v3/pulls/review_requests/#create-a-review-request
+ *
+ * @param string $username
+ * @param string $repository
+ * @param int $pullRequest
+ * @param array $reviewers
+ *
+ * @return string
+ */
+ public function create($username, $repository, $pullRequest, array $reviewers)
+ {
+ return $this->post('/repos/'.rawurlencode($username).'/'.rawurlencode($repository).'/pulls/'.$pullRequest.'/requested_reviewers', ['reviewers' => $reviewers]);
+ }
+
+ /**
+ * @link https://developer.github.com/v3/pulls/review_requests/#delete-a-review-request
+ *
+ * @param string $username
+ * @param string $repository
+ * @param int $pullRequest
+ * @param array $reviewers
+ *
+ * @return string
+ */
+ public function remove($username, $repository, $pullRequest, array $reviewers)
+ {
+ return $this->delete('/repos/'.rawurlencode($username).'/'.rawurlencode($repository).'/pulls/'.$pullRequest.'/requested_reviewers', ['reviewers' => $reviewers]);
+ }
+}
diff --git a/vendor/knplabs/github-api/lib/Github/Api/RateLimit.php b/vendor/knplabs/github-api/lib/Github/Api/RateLimit.php
new file mode 100644
index 00000000..ba5301df
--- /dev/null
+++ b/vendor/knplabs/github-api/lib/Github/Api/RateLimit.php
@@ -0,0 +1,47 @@
+
+ */
+class RateLimit extends AbstractApi
+{
+ /**
+ * Get rate limits.
+ *
+ * @return array
+ */
+ public function getRateLimits()
+ {
+ return $this->get('/rate_limit');
+ }
+
+ /**
+ * Get core rate limit.
+ *
+ * @return int
+ */
+ public function getCoreLimit()
+ {
+ $response = $this->getRateLimits();
+
+ return $response['resources']['core']['limit'];
+ }
+
+ /**
+ * Get search rate limit.
+ *
+ * @return int
+ */
+ public function getSearchLimit()
+ {
+ $response = $this->getRateLimits();
+
+ return $response['resources']['search']['limit'];
+ }
+}
diff --git a/vendor/knplabs/github-api/lib/Github/Api/Repo.php b/vendor/knplabs/github-api/lib/Github/Api/Repo.php
new file mode 100644
index 00000000..c0c54a95
--- /dev/null
+++ b/vendor/knplabs/github-api/lib/Github/Api/Repo.php
@@ -0,0 +1,682 @@
+
+ * @author Thibault Duplessis
+ */
+class Repo extends AbstractApi
+{
+ use AcceptHeaderTrait;
+
+ /**
+ * Search repositories by keyword.
+ *
+ * @deprecated This method is deprecated use the Search api instead. See https://developer.github.com/v3/search/legacy/#legacy-search-api-is-deprecated
+ * @link http://developer.github.com/v3/search/#search-repositories
+ *
+ * @param string $keyword the search query
+ * @param array $params
+ *
+ * @return array list of found repositories
+ */
+ public function find($keyword, array $params = [])
+ {
+ return $this->get('/legacy/repos/search/'.rawurlencode($keyword), array_merge(['start_page' => 1], $params));
+ }
+
+ /**
+ * List all public repositories.
+ *
+ * @link https://developer.github.com/v3/repos/#list-all-public-repositories
+ *
+ * @param int|null $id The integer ID of the last Repository that you’ve seen.
+ *
+ * @return array list of users found
+ */
+ public function all($id = null)
+ {
+ if (!is_int($id)) {
+ return $this->get('/repositories');
+ }
+
+ return $this->get('/repositories?since='.rawurldecode($id));
+ }
+
+ /**
+ * Get the last year of commit activity for a repository grouped by week.
+ *
+ * @link http://developer.github.com/v3/repos/statistics/#commit-activity
+ *
+ * @param string $username the user who owns the repository
+ * @param string $repository the name of the repository
+ *
+ * @return array commit activity grouped by week
+ */
+ public function activity($username, $repository)
+ {
+ return $this->get('/repos/'.rawurlencode($username).'/'.rawurlencode($repository).'/stats/commit_activity');
+ }
+
+ /**
+ * Get contributor commit statistics for a repository.
+ *
+ * @link http://developer.github.com/v3/repos/statistics/#contributors
+ *
+ * @param string $username the user who owns the repository
+ * @param string $repository the name of the repository
+ *
+ * @return array list of contributors and their commit statistics
+ */
+ public function statistics($username, $repository)
+ {
+ return $this->get('/repos/'.rawurlencode($username).'/'.rawurlencode($repository).'/stats/contributors');
+ }
+
+ /**
+ * Get a weekly aggregate of the number of additions and deletions pushed to a repository.
+ *
+ * @link http://developer.github.com/v3/repos/statistics/#code-frequency
+ *
+ * @param string $username the user who owns the repository
+ * @param string $repository the name of the repository
+ *
+ * @return array list of weeks and their commit statistics
+ */
+ public function frequency($username, $repository)
+ {
+ return $this->get('/repos/'.rawurlencode($username).'/'.rawurlencode($repository).'/stats/code_frequency');
+ }
+
+ /**
+ * Get the weekly commit count for the repository owner and everyone else.
+ *
+ * @link http://developer.github.com/v3/repos/statistics/#participation
+ *
+ * @param string $username the user who owns the repository
+ * @param string $repository the name of the repository
+ *
+ * @return array list of weekly commit count grouped by 'all' and 'owner'
+ */
+ public function participation($username, $repository)
+ {
+ return $this->get('/repos/'.rawurlencode($username).'/'.rawurlencode($repository).'/stats/participation');
+ }
+
+ /**
+ * List all repositories for an organization.
+ *
+ * @link http://developer.github.com/v3/repos/#list-organization-repositories
+ *
+ * @param string $organization the name of the organization
+ * @param array $params
+ *
+ * @return array list of organization repositories
+ */
+ public function org($organization, array $params = [])
+ {
+ return $this->get('/orgs/'.$organization.'/repos', array_merge(['start_page' => 1], $params));
+ }
+
+ /**
+ * Get extended information about a repository by its username and repository name.
+ *
+ * @link http://developer.github.com/v3/repos/
+ *
+ * @param string $username the user who owns the repository
+ * @param string $repository the name of the repository
+ *
+ * @return array information about the repository
+ */
+ public function show($username, $repository)
+ {
+ return $this->get('/repos/'.rawurlencode($username).'/'.rawurlencode($repository));
+ }
+
+ /**
+ * Get extended information about a repository by its id.
+ * Note: at time of writing this is an undocumented feature but GitHub support have advised that it can be relied on.
+ *
+ * @link http://developer.github.com/v3/repos/
+ * @link https://github.com/piotrmurach/github/issues/283
+ * @link https://github.com/piotrmurach/github/issues/282
+ *
+ * @param int $id the id of the repository
+ *
+ * @return array information about the repository
+ */
+ public function showById($id)
+ {
+ return $this->get('/repositories/'.rawurlencode($id));
+ }
+
+ /**
+ * Create repository.
+ *
+ * @link http://developer.github.com/v3/repos/
+ *
+ * @param string $name name of the repository
+ * @param string $description repository description
+ * @param string $homepage homepage url
+ * @param bool $public `true` for public, `false` for private
+ * @param null|string $organization username of organization if applicable
+ * @param bool $hasIssues `true` to enable issues for this repository, `false` to disable them
+ * @param bool $hasWiki `true` to enable the wiki for this repository, `false` to disable it
+ * @param bool $hasDownloads `true` to enable downloads for this repository, `false` to disable them
+ * @param int $teamId The id of the team that will be granted access to this repository. This is only valid when creating a repo in an organization.
+ * @param bool $autoInit `true` to create an initial commit with empty README, `false` for no initial commit
+ *
+ * @return array returns repository data
+ */
+ public function create(
+ $name,
+ $description = '',
+ $homepage = '',
+ $public = true,
+ $organization = null,
+ $hasIssues = false,
+ $hasWiki = false,
+ $hasDownloads = false,
+ $teamId = null,
+ $autoInit = false
+ ) {
+ $path = null !== $organization ? '/orgs/'.$organization.'/repos' : '/user/repos';
+
+ $parameters = [
+ 'name' => $name,
+ 'description' => $description,
+ 'homepage' => $homepage,
+ 'private' => !$public,
+ 'has_issues' => $hasIssues,
+ 'has_wiki' => $hasWiki,
+ 'has_downloads' => $hasDownloads,
+ 'auto_init' => $autoInit,
+ ];
+
+ if ($organization && $teamId) {
+ $parameters['team_id'] = $teamId;
+ }
+
+ return $this->post($path, $parameters);
+ }
+
+ /**
+ * Set information of a repository.
+ *
+ * @link http://developer.github.com/v3/repos/
+ *
+ * @param string $username the user who owns the repository
+ * @param string $repository the name of the repository
+ * @param array $values the key => value pairs to post
+ *
+ * @return array information about the repository
+ */
+ public function update($username, $repository, array $values)
+ {
+ return $this->patch('/repos/'.rawurlencode($username).'/'.rawurlencode($repository), $values);
+ }
+
+ /**
+ * Delete a repository.
+ *
+ * @link http://developer.github.com/v3/repos/
+ *
+ * @param string $username the user who owns the repository
+ * @param string $repository the name of the repository
+ *
+ * @return mixed null on success, array on error with 'message'
+ */
+ public function remove($username, $repository)
+ {
+ return $this->delete('/repos/'.rawurlencode($username).'/'.rawurlencode($repository));
+ }
+
+ /**
+ * Get the readme content for a repository by its username and repository name.
+ *
+ * @link http://developer.github.com/v3/repos/contents/#get-the-readme
+ *
+ * @param string $username the user who owns the repository
+ * @param string $repository the name of the repository
+ * @param string $format one of formats: "raw", "html", or "v3+json"
+ *
+ * @return string|array the readme content
+ */
+ public function readme($username, $repository, $format = 'raw')
+ {
+ return $this->get('/repos/'.rawurlencode($username).'/'.rawurlencode($repository).'/readme', [], [
+ 'Accept' => "application/vnd.github.$format",
+ ]);
+ }
+
+ /**
+ * Manage the collaborators of a repository.
+ *
+ * @link http://developer.github.com/v3/repos/collaborators/
+ *
+ * @return Collaborators
+ */
+ public function collaborators()
+ {
+ return new Collaborators($this->client);
+ }
+
+ /**
+ * Manage the comments of a repository.
+ *
+ * @link http://developer.github.com/v3/repos/comments/
+ *
+ * @return Comments
+ */
+ public function comments()
+ {
+ return new Comments($this->client);
+ }
+
+ /**
+ * Manage the commits of a repository.
+ *
+ * @link http://developer.github.com/v3/repos/commits/
+ *
+ * @return Commits
+ */
+ public function commits()
+ {
+ return new Commits($this->client);
+ }
+
+ /**
+ * Manage the content of a repository.
+ *
+ * @link http://developer.github.com/v3/repos/contents/
+ *
+ * @return Contents
+ */
+ public function contents()
+ {
+ return new Contents($this->client);
+ }
+
+ /**
+ * Manage the content of a repository.
+ *
+ * @link http://developer.github.com/v3/repos/downloads/
+ *
+ * @return Downloads
+ */
+ public function downloads()
+ {
+ return new Downloads($this->client);
+ }
+
+ /**
+ * Manage the releases of a repository (Currently Undocumented).
+ *
+ * @link http://developer.github.com/v3/repos/
+ *
+ * @return Releases
+ */
+ public function releases()
+ {
+ return new Releases($this->client);
+ }
+
+ /**
+ * Manage the deploy keys of a repository.
+ *
+ * @link http://developer.github.com/v3/repos/keys/
+ *
+ * @return DeployKeys
+ */
+ public function keys()
+ {
+ return new DeployKeys($this->client);
+ }
+
+ /**
+ * Manage the forks of a repository.
+ *
+ * @link http://developer.github.com/v3/repos/forks/
+ *
+ * @return Forks
+ */
+ public function forks()
+ {
+ return new Forks($this->client);
+ }
+
+ /**
+ * Manage the stargazers of a repository.
+ *
+ * @link https://developer.github.com/v3/activity/starring/#list-stargazers
+ *
+ * @return Stargazers
+ */
+ public function stargazers()
+ {
+ return new Stargazers($this->client);
+ }
+
+ /**
+ * Manage the hooks of a repository.
+ *
+ * @link http://developer.github.com/v3/issues/jooks/
+ *
+ * @return Hooks
+ */
+ public function hooks()
+ {
+ return new Hooks($this->client);
+ }
+
+ /**
+ * Manage the labels of a repository.
+ *
+ * @link http://developer.github.com/v3/issues/labels/
+ *
+ * @return Labels
+ */
+ public function labels()
+ {
+ return new Labels($this->client);
+ }
+
+ /**
+ * Manage the statuses of a repository.
+ *
+ * @link http://developer.github.com/v3/repos/statuses/
+ *
+ * @return Statuses
+ */
+ public function statuses()
+ {
+ return new Statuses($this->client);
+ }
+
+ /**
+ * Get the branch(es) of a repository.
+ *
+ * @link http://developer.github.com/v3/repos/
+ *
+ * @param string $username the username
+ * @param string $repository the name of the repository
+ * @param string $branch the name of the branch
+ *
+ * @return array list of the repository branches
+ */
+ public function branches($username, $repository, $branch = null)
+ {
+ $url = '/repos/'.rawurlencode($username).'/'.rawurlencode($repository).'/branches';
+ if (null !== $branch) {
+ $url .= '/'.rawurlencode($branch);
+ }
+
+ return $this->get($url);
+ }
+
+ /**
+ * Manage the protection of a repository branch.
+ *
+ * @link https://developer.github.com/v3/repos/branches/#get-branch-protection
+ *
+ * @return Protection
+ */
+ public function protection()
+ {
+ return new Protection($this->client);
+ }
+
+ /**
+ * Get the contributors of a repository.
+ *
+ * @link http://developer.github.com/v3/repos/
+ *
+ * @param string $username the user who owns the repository
+ * @param string $repository the name of the repository
+ * @param bool $includingAnonymous by default, the list only shows GitHub users.
+ * You can include non-users too by setting this to true
+ *
+ * @return array list of the repo contributors
+ */
+ public function contributors($username, $repository, $includingAnonymous = false)
+ {
+ return $this->get('/repos/'.rawurlencode($username).'/'.rawurlencode($repository).'/contributors', [
+ 'anon' => $includingAnonymous ?: null,
+ ]);
+ }
+
+ /**
+ * Get the language breakdown of a repository.
+ *
+ * @link http://developer.github.com/v3/repos/
+ *
+ * @param string $username the user who owns the repository
+ * @param string $repository the name of the repository
+ *
+ * @return array list of the languages
+ */
+ public function languages($username, $repository)
+ {
+ return $this->get('/repos/'.rawurlencode($username).'/'.rawurlencode($repository).'/languages');
+ }
+
+ /**
+ * Get the tags of a repository.
+ *
+ * @link http://developer.github.com/v3/repos/
+ *
+ * @param string $username the user who owns the repository
+ * @param string $repository the name of the repository
+ * @param array $params the additional parameters like milestone, assignees, labels, sort, direction
+ *
+ * @return array list of the repository tags
+ */
+ public function tags($username, $repository, array $params = [])
+ {
+ return $this->get('/repos/'.rawurlencode($username).'/'.rawurlencode($repository).'/tags', $params);
+ }
+
+ /**
+ * Get the teams of a repository.
+ *
+ * @link http://developer.github.com/v3/repos/
+ *
+ * @param string $username the user who owns the repo
+ * @param string $repository the name of the repo
+ *
+ * @return array list of the languages
+ */
+ public function teams($username, $repository)
+ {
+ return $this->get('/repos/'.rawurlencode($username).'/'.rawurlencode($repository).'/teams');
+ }
+
+ /**
+ * @deprecated see subscribers method
+ *
+ * @param string $username
+ * @param string $repository
+ * @param int $page
+ *
+ * @return array
+ */
+ public function watchers($username, $repository, $page = 1)
+ {
+ return $this->get('/repos/'.rawurlencode($username).'/'.rawurlencode($repository).'/watchers', [
+ 'page' => $page,
+ ]);
+ }
+
+ /**
+ * @param string $username
+ * @param string $repository
+ * @param int $page
+ *
+ * @return array
+ */
+ public function subscribers($username, $repository, $page = 1)
+ {
+ return $this->get('/repos/'.rawurlencode($username).'/'.rawurlencode($repository).'/subscribers', [
+ 'page' => $page,
+ ]);
+ }
+
+ /**
+ * Perform a merge.
+ *
+ * @link http://developer.github.com/v3/repos/merging/
+ *
+ * @param string $username
+ * @param string $repository
+ * @param string $base The name of the base branch that the head will be merged into.
+ * @param string $head The head to merge. This can be a branch name or a commit SHA1.
+ * @param string $message Commit message to use for the merge commit. If omitted, a default message will be used.
+ *
+ * @return array|null
+ */
+ public function merge($username, $repository, $base, $head, $message = null)
+ {
+ $parameters = [
+ 'base' => $base,
+ 'head' => $head,
+ ];
+
+ if (is_string($message)) {
+ $parameters['commit_message'] = $message;
+ }
+
+ return $this->post('/repos/'.rawurlencode($username).'/'.rawurlencode($repository).'/merges', $parameters);
+ }
+
+ /**
+ * @param string $username
+ * @param string $repository
+ *
+ * @return array
+ */
+ public function milestones($username, $repository)
+ {
+ return $this->get('/repos/'.rawurldecode($username).'/'.rawurldecode($repository).'/milestones');
+ }
+
+ public function projects()
+ {
+ return new Projects($this->client);
+ }
+
+ public function traffic()
+ {
+ return new Traffic($this->client);
+ }
+
+ /**
+ * @param string $username
+ * @param string $repository
+ * @param int $page
+ *
+ * @return array|string
+ *
+ * @see https://developer.github.com/v3/activity/events/#list-repository-events
+ */
+ public function events($username, $repository, $page = 1)
+ {
+ return $this->get('/repos/'.rawurldecode($username).'/'.rawurldecode($repository).'/events', ['page' => $page]);
+ }
+
+ /**
+ * Get the contents of a repository's code of conduct.
+ *
+ * @link https://developer.github.com/v3/codes_of_conduct/#get-the-contents-of-a-repositorys-code-of-conduct
+ *
+ * @param string $username
+ * @param string $repository
+ *
+ * @return array
+ */
+ public function codeOfConduct($username, $repository)
+ {
+ //This api is in preview mode, so set the correct accept-header
+ $this->acceptHeaderValue = 'application/vnd.github.scarlet-witch-preview+json';
+
+ return $this->get('/repos/'.rawurldecode($username).'/'.rawurldecode($repository).'/community/code_of_conduct');
+ }
+
+ /**
+ * List all topics for a repository.
+ *
+ * @link https://developer.github.com/v3/repos/#list-all-topics-for-a-repository
+ *
+ * @param string $username
+ * @param string $repository
+ *
+ * @return array
+ */
+ public function topics($username, $repository)
+ {
+ //This api is in preview mode, so set the correct accept-header
+ $this->acceptHeaderValue = 'application/vnd.github.mercy-preview+json';
+
+ return $this->get('/repos/'.rawurldecode($username).'/'.rawurldecode($repository).'/topics');
+ }
+
+ /**
+ * Replace all topics for a repository.
+ *
+ * @link https://developer.github.com/v3/repos/#replace-all-topics-for-a-repository
+ *
+ * @param string $username
+ * @param string $repository
+ * @param array $topics
+ *
+ * @return array
+ */
+ public function replaceTopics($username, $repository, array $topics)
+ {
+ //This api is in preview mode, so set the correct accept-header
+ $this->acceptHeaderValue = 'application/vnd.github.mercy-preview+json';
+
+ return $this->put('/repos/'.rawurldecode($username).'/'.rawurldecode($repository).'/topics', ['names' => $topics]);
+ }
+
+ /**
+ * Transfer a repository.
+ *
+ * @link https://developer.github.com/v3/repos/#transfer-a-repository
+ *
+ * @param string $username
+ * @param string $repository
+ * @param string $newOwner
+ * @param array $teamId
+ *
+ * @return array
+ */
+ public function transfer($username, $repository, $newOwner, $teamId = [])
+ {
+ //This api is in preview mode, so set the correct accept-header
+ $this->acceptHeaderValue = 'application/vnd.github.nightshade-preview+json';
+
+ return $this->post('/repos/'.rawurldecode($username).'/'.rawurldecode($repository).'/transfer', ['new_owner' => $newOwner, 'team_id' => $teamId]);
+ }
+}
diff --git a/vendor/knplabs/github-api/lib/Github/Api/Repository/Assets.php b/vendor/knplabs/github-api/lib/Github/Api/Repository/Assets.php
new file mode 100644
index 00000000..dbe6da22
--- /dev/null
+++ b/vendor/knplabs/github-api/lib/Github/Api/Repository/Assets.php
@@ -0,0 +1,118 @@
+
+ */
+class Assets extends AbstractApi
+{
+ /**
+ * Get all release's assets in selected repository
+ * GET /repos/:owner/:repo/releases/:id/assets.
+ *
+ * @param string $username the user who owns the repo
+ * @param string $repository the name of the repo
+ * @param int $id the id of the release
+ *
+ * @return array
+ */
+ public function all($username, $repository, $id)
+ {
+ return $this->get('/repos/'.rawurlencode($username).'/'.rawurlencode($repository).'/releases/'.rawurlencode($id).'/assets');
+ }
+
+ /**
+ * Get an asset in selected repository's release
+ * GET /repos/:owner/:repo/releases/assets/:id.
+ *
+ * @param string $username the user who owns the repo
+ * @param string $repository the name of the repo
+ * @param int $id the id of the asset
+ *
+ * @return array
+ */
+ public function show($username, $repository, $id)
+ {
+ return $this->get('/repos/'.rawurlencode($username).'/'.rawurlencode($repository).'/releases/assets/'.rawurlencode($id));
+ }
+
+ /**
+ * Create an asset for selected repository's release
+ * POST /repos/:owner/:repo/releases/:id/assets?name=:filename.
+ *
+ * Creating an asset requires support for server name indentification (SNI)
+ * so this must be supported by your PHP version.
+ *
+ * @see http://developer.github.com/v3/repos/releases/#upload-a-release-asset
+ * @see http://php.net/manual/en/openssl.constsni.php
+ *
+ * @param string $username the user who owns the repo
+ * @param string $repository the name of the repo
+ * @param int $id the id of the release
+ * @param string $name the filename for the asset
+ * @param string $contentType the content type for the asset
+ * @param string $content the content of the asset
+ *
+ * @throws MissingArgumentException
+ * @throws ErrorException
+ *
+ * @return array
+ */
+ public function create($username, $repository, $id, $name, $contentType, $content)
+ {
+ if (!defined('OPENSSL_TLSEXT_SERVER_NAME') || !OPENSSL_TLSEXT_SERVER_NAME) {
+ throw new ErrorException('Asset upload support requires Server Name Indication. This is not supported by your PHP version. See http://php.net/manual/en/openssl.constsni.php.');
+ }
+
+ // Asset creation requires a separate endpoint, uploads.github.com.
+ // Change the base url for the HTTP client temporarily while we execute
+ // this request.
+ $response = $this->postRaw('https://uploads.github.com/repos/'.rawurlencode($username).'/'.rawurlencode($repository).'/releases/'.rawurlencode($id).'/assets?name='.$name, $content, ['Content-Type' => $contentType]);
+
+ return $response;
+ }
+
+ /**
+ * Edit an asset in selected repository's release
+ * PATCH /repos/:owner/:repo/releases/assets/:id.
+ *
+ * @param string $username the user who owns the repo
+ * @param string $repository the name of the repo
+ * @param int $id the id of the asset
+ * @param array $params request parameters
+ *
+ * @throws MissingArgumentException
+ *
+ * @return array
+ */
+ public function edit($username, $repository, $id, array $params)
+ {
+ if (!isset($params['name'])) {
+ throw new MissingArgumentException('name');
+ }
+
+ return $this->patch('/repos/'.rawurlencode($username).'/'.rawurlencode($repository).'/releases/assets/'.rawurlencode($id), $params);
+ }
+
+ /**
+ * Delete an asset in selected repository's release
+ * DELETE /repos/:owner/:repo/releases/assets/:id.
+ *
+ * @param string $username the user who owns the repo
+ * @param string $repository the name of the repo
+ * @param int $id the id of the asset
+ *
+ * @return array
+ */
+ public function remove($username, $repository, $id)
+ {
+ return $this->delete('/repos/'.rawurlencode($username).'/'.rawurlencode($repository).'/releases/assets/'.rawurlencode($id));
+ }
+}
diff --git a/vendor/knplabs/github-api/lib/Github/Api/Repository/Collaborators.php b/vendor/knplabs/github-api/lib/Github/Api/Repository/Collaborators.php
new file mode 100644
index 00000000..34411884
--- /dev/null
+++ b/vendor/knplabs/github-api/lib/Github/Api/Repository/Collaborators.php
@@ -0,0 +1,84 @@
+
+ */
+class Collaborators extends AbstractApi
+{
+ /**
+ * @link https://developer.github.com/v3/repos/collaborators/#list-collaborators
+ *
+ * @param $username
+ * @param $repository
+ * @param array $params
+ *
+ * @return array|string
+ */
+ public function all($username, $repository, array $params = [])
+ {
+ return $this->get('/repos/'.rawurlencode($username).'/'.rawurlencode($repository).'/collaborators', $params);
+ }
+
+ /**
+ * @link https://developer.github.com/v3/repos/collaborators/#check-if-a-user-is-a-collaborator
+ *
+ * @param $username
+ * @param $repository
+ * @param $collaborator
+ *
+ * @return array|string
+ */
+ public function check($username, $repository, $collaborator)
+ {
+ return $this->get('/repos/'.rawurlencode($username).'/'.rawurlencode($repository).'/collaborators/'.rawurlencode($collaborator));
+ }
+
+ /**
+ * @link https://developer.github.com/v3/repos/collaborators/#add-user-as-a-collaborator
+ *
+ * @param $username
+ * @param $repository
+ * @param $collaborator
+ * @param array $params
+ *
+ * @return array|string
+ */
+ public function add($username, $repository, $collaborator, array $params = [])
+ {
+ return $this->put('/repos/'.rawurlencode($username).'/'.rawurlencode($repository).'/collaborators/'.rawurlencode($collaborator), $params);
+ }
+
+ /**
+ * @link https://developer.github.com/v3/repos/collaborators/#remove-user-as-a-collaborator
+ *
+ * @param $username
+ * @param $repository
+ * @param $collaborator
+ *
+ * @return array|string
+ */
+ public function remove($username, $repository, $collaborator)
+ {
+ return $this->delete('/repos/'.rawurlencode($username).'/'.rawurlencode($repository).'/collaborators/'.rawurlencode($collaborator));
+ }
+
+ /**
+ * @link https://developer.github.com/v3/repos/collaborators/#review-a-users-permission-level
+ *
+ * @param $username
+ * @param $repository
+ * @param $collaborator
+ *
+ * @return array|string
+ */
+ public function permission($username, $repository, $collaborator)
+ {
+ return $this->get('/repos/'.rawurlencode($username).'/'.rawurlencode($repository).'/collaborators/'.rawurlencode($collaborator).'/permission');
+ }
+}
diff --git a/vendor/knplabs/github-api/lib/Github/Api/Repository/Comments.php b/vendor/knplabs/github-api/lib/Github/Api/Repository/Comments.php
new file mode 100644
index 00000000..53feb72c
--- /dev/null
+++ b/vendor/knplabs/github-api/lib/Github/Api/Repository/Comments.php
@@ -0,0 +1,75 @@
+
+ * @author Tobias Nyholm
+ */
+class Comments extends AbstractApi
+{
+ use AcceptHeaderTrait;
+
+ /**
+ * Configure the body type.
+ *
+ * @link https://developer.github.com/v3/repos/comments/#custom-media-types
+ *
+ * @param string|null $bodyType
+ *
+ * @return self
+ */
+ public function configure($bodyType = null)
+ {
+ if (!in_array($bodyType, ['raw', 'text', 'html'])) {
+ $bodyType = 'full';
+ }
+
+ $this->acceptHeaderValue = sprintf('application/vnd.github.%s.%s+json', $this->client->getApiVersion(), $bodyType);
+
+ return $this;
+ }
+
+ public function all($username, $repository, $sha = null)
+ {
+ if (null === $sha) {
+ return $this->get('/repos/'.rawurlencode($username).'/'.rawurlencode($repository).'/comments');
+ }
+
+ return $this->get('/repos/'.rawurlencode($username).'/'.rawurlencode($repository).'/commits/'.rawurlencode($sha).'/comments');
+ }
+
+ public function show($username, $repository, $comment)
+ {
+ return $this->get('/repos/'.rawurlencode($username).'/'.rawurlencode($repository).'/comments/'.rawurlencode($comment));
+ }
+
+ public function create($username, $repository, $sha, array $params)
+ {
+ if (!isset($params['body'])) {
+ throw new MissingArgumentException('body');
+ }
+
+ return $this->post('/repos/'.rawurlencode($username).'/'.rawurlencode($repository).'/commits/'.rawurlencode($sha).'/comments', $params);
+ }
+
+ public function update($username, $repository, $comment, array $params)
+ {
+ if (!isset($params['body'])) {
+ throw new MissingArgumentException('body');
+ }
+
+ return $this->patch('/repos/'.rawurlencode($username).'/'.rawurlencode($repository).'/comments/'.rawurlencode($comment), $params);
+ }
+
+ public function remove($username, $repository, $comment)
+ {
+ return $this->delete('/repos/'.rawurlencode($username).'/'.rawurlencode($repository).'/comments/'.rawurlencode($comment));
+ }
+}
diff --git a/vendor/knplabs/github-api/lib/Github/Api/Repository/Commits.php b/vendor/knplabs/github-api/lib/Github/Api/Repository/Commits.php
new file mode 100644
index 00000000..8195e4ba
--- /dev/null
+++ b/vendor/knplabs/github-api/lib/Github/Api/Repository/Commits.php
@@ -0,0 +1,33 @@
+
+ */
+class Commits extends AbstractApi
+{
+ public function all($username, $repository, array $params)
+ {
+ return $this->get('/repos/'.rawurlencode($username).'/'.rawurlencode($repository).'/commits', $params);
+ }
+
+ public function compare($username, $repository, $base, $head, $mediaType = null)
+ {
+ $headers = [];
+ if (null !== $mediaType) {
+ $headers['Accept'] = $mediaType;
+ }
+
+ return $this->get('/repos/'.rawurlencode($username).'/'.rawurlencode($repository).'/compare/'.rawurlencode($base).'...'.rawurlencode($head), [], $headers);
+ }
+
+ public function show($username, $repository, $sha)
+ {
+ return $this->get('/repos/'.rawurlencode($username).'/'.rawurlencode($repository).'/commits/'.rawurlencode($sha));
+ }
+}
diff --git a/vendor/knplabs/github-api/lib/Github/Api/Repository/Contents.php b/vendor/knplabs/github-api/lib/Github/Api/Repository/Contents.php
new file mode 100644
index 00000000..64346e62
--- /dev/null
+++ b/vendor/knplabs/github-api/lib/Github/Api/Repository/Contents.php
@@ -0,0 +1,297 @@
+
+ */
+class Contents extends AbstractApi
+{
+ use AcceptHeaderTrait;
+
+ /**
+ * Configure the body type.
+ *
+ * @link https://developer.github.com/v3/repo/contents/#custom-media-types
+ *
+ * @param string|null $bodyType
+ *
+ * @return self
+ */
+ public function configure($bodyType = null)
+ {
+ if (!in_array($bodyType, ['html', 'object'])) {
+ $bodyType = 'raw';
+ }
+
+ $this->acceptHeaderValue = sprintf('application/vnd.github.%s.%s', $this->client->getApiVersion(), $bodyType);
+
+ return $this;
+ }
+
+ /**
+ * Get content of README file in a repository.
+ *
+ * @link http://developer.github.com/v3/repos/contents/
+ *
+ * @param string $username the user who owns the repository
+ * @param string $repository the name of the repository
+ * @param null|string $reference reference to a branch or commit
+ *
+ * @return array information for README file
+ */
+ public function readme($username, $repository, $reference = null)
+ {
+ return $this->get('/repos/'.rawurlencode($username).'/'.rawurlencode($repository).'/readme', [
+ 'ref' => $reference,
+ ]);
+ }
+
+ /**
+ * Get contents of any file or directory in a repository.
+ *
+ * @link http://developer.github.com/v3/repos/contents/
+ *
+ * @param string $username the user who owns the repository
+ * @param string $repository the name of the repository
+ * @param null|string $path path to file or directory
+ * @param null|string $reference reference to a branch or commit
+ *
+ * @return array|string information for file | information for each item in directory
+ */
+ public function show($username, $repository, $path = null, $reference = null)
+ {
+ $url = '/repos/'.rawurlencode($username).'/'.rawurlencode($repository).'/contents';
+ if (null !== $path) {
+ $url .= '/'.rawurlencode($path);
+ }
+
+ return $this->get($url, [
+ 'ref' => $reference,
+ ]);
+ }
+
+ /**
+ * Creates a new file in a repository.
+ *
+ * @link http://developer.github.com/v3/repos/contents/#create-a-file
+ *
+ * @param string $username the user who owns the repository
+ * @param string $repository the name of the repository
+ * @param string $path path to file
+ * @param string $content contents of the new file
+ * @param string $message the commit message
+ * @param null|string $branch name of a branch
+ * @param null|array $committer information about the committer
+ *
+ * @throws MissingArgumentException
+ *
+ * @return array information about the new file
+ */
+ public function create($username, $repository, $path, $content, $message, $branch = null, array $committer = null)
+ {
+ $url = '/repos/'.rawurlencode($username).'/'.rawurlencode($repository).'/contents/'.rawurlencode($path);
+
+ $parameters = [
+ 'content' => base64_encode($content),
+ 'message' => $message,
+ ];
+
+ if (null !== $branch) {
+ $parameters['branch'] = $branch;
+ }
+
+ if (null !== $committer) {
+ if (!isset($committer['name'], $committer['email'])) {
+ throw new MissingArgumentException(['name', 'email']);
+ }
+ $parameters['committer'] = $committer;
+ }
+
+ return $this->put($url, $parameters);
+ }
+
+ /**
+ * Checks that a given path exists in a repository.
+ *
+ * @param string $username the user who owns the repository
+ * @param string $repository the name of the repository
+ * @param string $path path of file to check
+ * @param null|string $reference reference to a branch or commit
+ *
+ * @return bool
+ */
+ public function exists($username, $repository, $path, $reference = null)
+ {
+ $url = '/repos/'.rawurlencode($username).'/'.rawurlencode($repository).'/contents';
+
+ if (null !== $path) {
+ $url .= '/'.rawurlencode($path);
+ }
+
+ try {
+ $response = $this->head($url, [
+ 'ref' => $reference,
+ ]);
+
+ if ($response->getStatusCode() != 200) {
+ return false;
+ }
+ } catch (TwoFactorAuthenticationRequiredException $ex) {
+ throw $ex;
+ } catch (\Exception $ex) {
+ return false;
+ }
+
+ return true;
+ }
+
+ /**
+ * Updates the contents of a file in a repository.
+ *
+ * @link http://developer.github.com/v3/repos/contents/#update-a-file
+ *
+ * @param string $username the user who owns the repository
+ * @param string $repository the name of the repository
+ * @param string $path path to file
+ * @param string $content contents of the new file
+ * @param string $message the commit message
+ * @param string $sha blob SHA of the file being replaced
+ * @param null|string $branch name of a branch
+ * @param null|array $committer information about the committer
+ *
+ * @throws MissingArgumentException
+ *
+ * @return array information about the updated file
+ */
+ public function update($username, $repository, $path, $content, $message, $sha, $branch = null, array $committer = null)
+ {
+ $url = '/repos/'.rawurlencode($username).'/'.rawurlencode($repository).'/contents/'.rawurlencode($path);
+
+ $parameters = [
+ 'content' => base64_encode($content),
+ 'message' => $message,
+ 'sha' => $sha,
+ ];
+
+ if (null !== $branch) {
+ $parameters['branch'] = $branch;
+ }
+
+ if (null !== $committer) {
+ if (!isset($committer['name'], $committer['email'])) {
+ throw new MissingArgumentException(['name', 'email']);
+ }
+ $parameters['committer'] = $committer;
+ }
+
+ return $this->put($url, $parameters);
+ }
+
+ /**
+ * Deletes a file from a repository.
+ *
+ * @link http://developer.github.com/v3/repos/contents/#delete-a-file
+ *
+ * @param string $username the user who owns the repository
+ * @param string $repository the name of the repository
+ * @param string $path path to file
+ * @param string $message the commit message
+ * @param string $sha blob SHA of the file being deleted
+ * @param null|string $branch name of a branch
+ * @param null|array $committer information about the committer
+ *
+ * @throws MissingArgumentException
+ *
+ * @return array information about the updated file
+ */
+ public function rm($username, $repository, $path, $message, $sha, $branch = null, array $committer = null)
+ {
+ $url = '/repos/'.rawurlencode($username).'/'.rawurlencode($repository).'/contents/'.rawurlencode($path);
+
+ $parameters = [
+ 'message' => $message,
+ 'sha' => $sha,
+ ];
+
+ if (null !== $branch) {
+ $parameters['branch'] = $branch;
+ }
+
+ if (null !== $committer) {
+ if (!isset($committer['name'], $committer['email'])) {
+ throw new MissingArgumentException(['name', 'email']);
+ }
+ $parameters['committer'] = $committer;
+ }
+
+ return $this->delete($url, $parameters);
+ }
+
+ /**
+ * Get content of archives in a repository.
+ *
+ * @link http://developer.github.com/v3/repos/contents/
+ *
+ * @param string $username the user who owns the repository
+ * @param string $repository the name of the repository
+ * @param string $format format of archive: tarball or zipball
+ * @param null|string $reference reference to a branch or commit
+ *
+ * @return string repository archive binary data
+ */
+ public function archive($username, $repository, $format, $reference = null)
+ {
+ if (!in_array($format, ['tarball', 'zipball'])) {
+ $format = 'tarball';
+ }
+
+ return $this->get('/repos/'.rawurlencode($username).'/'.rawurlencode($repository).'/'.rawurlencode($format).
+ ((null !== $reference) ? ('/'.rawurlencode($reference)) : ''));
+ }
+
+ /**
+ * Get the contents of a file in a repository.
+ *
+ * @param string $username the user who owns the repository
+ * @param string $repository the name of the repository
+ * @param string $path path to file
+ * @param null|string $reference reference to a branch or commit
+ *
+ * @throws InvalidArgumentException If $path is not a file or if its encoding is different from base64
+ * @throws ErrorException If $path doesn't include a 'content' index
+ *
+ * @return null|string content of file, or null in case of base64_decode failure
+ */
+ public function download($username, $repository, $path, $reference = null)
+ {
+ $file = $this->show($username, $repository, $path, $reference);
+
+ if (!isset($file['type']) || 'file' !== $file['type']) {
+ throw new InvalidArgumentException(sprintf('Path "%s" is not a file.', $path));
+ }
+
+ if (!isset($file['content'])) {
+ throw new ErrorException(sprintf('Unable to access "content" for file "%s" (possible keys: "%s").', $path, implode(', ', array_keys($file))));
+ }
+
+ if (!isset($file['encoding'])) {
+ throw new InvalidArgumentException(sprintf('Can\'t decode content of file "%s", as no encoding is defined.', $path));
+ }
+
+ if ('base64' !== $file['encoding']) {
+ throw new InvalidArgumentException(sprintf('Encoding "%s" of file "%s" is not supported.', $file['encoding'], $path));
+ }
+
+ return base64_decode($file['content']) ?: null;
+ }
+}
diff --git a/vendor/knplabs/github-api/lib/Github/Api/Repository/DeployKeys.php b/vendor/knplabs/github-api/lib/Github/Api/Repository/DeployKeys.php
new file mode 100644
index 00000000..c6c8a2ce
--- /dev/null
+++ b/vendor/knplabs/github-api/lib/Github/Api/Repository/DeployKeys.php
@@ -0,0 +1,47 @@
+
+ */
+class DeployKeys extends AbstractApi
+{
+ public function all($username, $repository)
+ {
+ return $this->get('/repos/'.rawurlencode($username).'/'.rawurlencode($repository).'/keys');
+ }
+
+ public function show($username, $repository, $id)
+ {
+ return $this->get('/repos/'.rawurlencode($username).'/'.rawurlencode($repository).'/keys/'.rawurlencode($id));
+ }
+
+ public function create($username, $repository, array $params)
+ {
+ if (!isset($params['title'], $params['key'])) {
+ throw new MissingArgumentException(['title', 'key']);
+ }
+
+ return $this->post('/repos/'.rawurlencode($username).'/'.rawurlencode($repository).'/keys', $params);
+ }
+
+ public function update($username, $repository, $id, array $params)
+ {
+ if (!isset($params['title'], $params['key'])) {
+ throw new MissingArgumentException(['title', 'key']);
+ }
+
+ return $this->patch('/repos/'.rawurlencode($username).'/'.rawurlencode($repository).'/keys/'.rawurlencode($id), $params);
+ }
+
+ public function remove($username, $repository, $id)
+ {
+ return $this->delete('/repos/'.rawurlencode($username).'/'.rawurlencode($repository).'/keys/'.rawurlencode($id));
+ }
+}
diff --git a/vendor/knplabs/github-api/lib/Github/Api/Repository/Downloads.php b/vendor/knplabs/github-api/lib/Github/Api/Repository/Downloads.php
new file mode 100644
index 00000000..ed4c42f2
--- /dev/null
+++ b/vendor/knplabs/github-api/lib/Github/Api/Repository/Downloads.php
@@ -0,0 +1,60 @@
+
+ */
+class Downloads extends AbstractApi
+{
+ /**
+ * List downloads in selected repository.
+ *
+ * @link http://developer.github.com/v3/repos/downloads/#list-downloads-for-a-repository
+ *
+ * @param string $username the user who owns the repo
+ * @param string $repository the name of the repo
+ *
+ * @return array
+ */
+ public function all($username, $repository)
+ {
+ return $this->get('/repos/'.rawurlencode($username).'/'.rawurlencode($repository).'/downloads');
+ }
+
+ /**
+ * Get a download in selected repository.
+ *
+ * @link http://developer.github.com/v3/repos/downloads/#get-a-single-download
+ *
+ * @param string $username the user who owns the repo
+ * @param string $repository the name of the repo
+ * @param int $id the id of the download file
+ *
+ * @return array
+ */
+ public function show($username, $repository, $id)
+ {
+ return $this->get('/repos/'.rawurlencode($username).'/'.rawurlencode($repository).'/downloads/'.rawurlencode($id));
+ }
+
+ /**
+ * Delete a download in selected repository.
+ *
+ * @link http://developer.github.com/v3/repos/downloads/#delete-a-download
+ *
+ * @param string $username the user who owns the repo
+ * @param string $repository the name of the repo
+ * @param int $id the id of the download file
+ *
+ * @return array
+ */
+ public function remove($username, $repository, $id)
+ {
+ return $this->delete('/repos/'.rawurlencode($username).'/'.rawurlencode($repository).'/downloads/'.rawurlencode($id));
+ }
+}
diff --git a/vendor/knplabs/github-api/lib/Github/Api/Repository/Forks.php b/vendor/knplabs/github-api/lib/Github/Api/Repository/Forks.php
new file mode 100644
index 00000000..961dc649
--- /dev/null
+++ b/vendor/knplabs/github-api/lib/Github/Api/Repository/Forks.php
@@ -0,0 +1,27 @@
+
+ */
+class Forks extends AbstractApi
+{
+ public function all($username, $repository, array $params = [])
+ {
+ if (isset($params['sort']) && !in_array($params['sort'], ['newest', 'oldest', 'watchers'])) {
+ $params['sort'] = 'newest';
+ }
+
+ return $this->get('/repos/'.rawurlencode($username).'/'.rawurlencode($repository).'/forks', array_merge(['page' => 1], $params));
+ }
+
+ public function create($username, $repository, array $params = [])
+ {
+ return $this->post('/repos/'.rawurlencode($username).'/'.rawurlencode($repository).'/forks', $params);
+ }
+}
diff --git a/vendor/knplabs/github-api/lib/Github/Api/Repository/Hooks.php b/vendor/knplabs/github-api/lib/Github/Api/Repository/Hooks.php
new file mode 100644
index 00000000..db67f402
--- /dev/null
+++ b/vendor/knplabs/github-api/lib/Github/Api/Repository/Hooks.php
@@ -0,0 +1,57 @@
+
+ */
+class Hooks extends AbstractApi
+{
+ public function all($username, $repository)
+ {
+ return $this->get('/repos/'.rawurlencode($username).'/'.rawurlencode($repository).'/hooks');
+ }
+
+ public function show($username, $repository, $id)
+ {
+ return $this->get('/repos/'.rawurlencode($username).'/'.rawurlencode($repository).'/hooks/'.rawurlencode($id));
+ }
+
+ public function create($username, $repository, array $params)
+ {
+ if (!isset($params['name'], $params['config'])) {
+ throw new MissingArgumentException(['name', 'config']);
+ }
+
+ return $this->post('/repos/'.rawurlencode($username).'/'.rawurlencode($repository).'/hooks', $params);
+ }
+
+ public function update($username, $repository, $id, array $params)
+ {
+ if (!isset($params['config'])) {
+ throw new MissingArgumentException(['config']);
+ }
+
+ return $this->patch('/repos/'.rawurlencode($username).'/'.rawurlencode($repository).'/hooks/'.rawurlencode($id), $params);
+ }
+
+ public function ping($username, $repository, $id)
+ {
+ return $this->post('/repos/'.rawurlencode($username).'/'.rawurlencode($repository).'/hooks/'.rawurlencode($id).'/pings');
+ }
+
+ public function test($username, $repository, $id)
+ {
+ return $this->post('/repos/'.rawurlencode($username).'/'.rawurlencode($repository).'/hooks/'.rawurlencode($id).'/test');
+ }
+
+ public function remove($username, $repository, $id)
+ {
+ return $this->delete('/repos/'.rawurlencode($username).'/'.rawurlencode($repository).'/hooks/'.rawurlencode($id));
+ }
+}
diff --git a/vendor/knplabs/github-api/lib/Github/Api/Repository/Labels.php b/vendor/knplabs/github-api/lib/Github/Api/Repository/Labels.php
new file mode 100644
index 00000000..7829e551
--- /dev/null
+++ b/vendor/knplabs/github-api/lib/Github/Api/Repository/Labels.php
@@ -0,0 +1,47 @@
+
+ */
+class Labels extends AbstractApi
+{
+ public function all($username, $repository)
+ {
+ return $this->get('/repos/'.rawurlencode($username).'/'.rawurlencode($repository).'/labels');
+ }
+
+ public function show($username, $repository, $label)
+ {
+ return $this->get('/repos/'.rawurlencode($username).'/'.rawurlencode($repository).'/labels/'.rawurlencode($label));
+ }
+
+ public function create($username, $repository, array $params)
+ {
+ if (!isset($params['name'], $params['color'])) {
+ throw new MissingArgumentException(['name', 'color']);
+ }
+
+ return $this->post('/repos/'.rawurlencode($username).'/'.rawurlencode($repository).'/labels', $params);
+ }
+
+ public function update($username, $repository, $label, array $params)
+ {
+ if (!isset($params['name'], $params['color'])) {
+ throw new MissingArgumentException(['name', 'color']);
+ }
+
+ return $this->patch('/repos/'.rawurlencode($username).'/'.rawurlencode($repository).'/labels/'.rawurlencode($label), $params);
+ }
+
+ public function remove($username, $repository, $label)
+ {
+ return $this->delete('/repos/'.rawurlencode($username).'/'.rawurlencode($repository).'/labels/'.rawurlencode($label));
+ }
+}
diff --git a/vendor/knplabs/github-api/lib/Github/Api/Repository/Projects.php b/vendor/knplabs/github-api/lib/Github/Api/Repository/Projects.php
new file mode 100644
index 00000000..9db29f55
--- /dev/null
+++ b/vendor/knplabs/github-api/lib/Github/Api/Repository/Projects.php
@@ -0,0 +1,23 @@
+get('/repos/'.rawurlencode($username).'/'.rawurlencode($repository).'/projects', array_merge(['page' => 1], $params));
+ }
+
+ public function create($username, $repository, array $params)
+ {
+ if (!isset($params['name'])) {
+ throw new MissingArgumentException(['name']);
+ }
+
+ return $this->post('/repos/'.rawurlencode($username).'/'.rawurlencode($repository).'/projects', $params);
+ }
+}
diff --git a/vendor/knplabs/github-api/lib/Github/Api/Repository/Protection.php b/vendor/knplabs/github-api/lib/Github/Api/Repository/Protection.php
new file mode 100644
index 00000000..73d1e6b5
--- /dev/null
+++ b/vendor/knplabs/github-api/lib/Github/Api/Repository/Protection.php
@@ -0,0 +1,441 @@
+
+ */
+class Protection extends AbstractApi
+{
+ use AcceptHeaderTrait;
+
+ public function configure()
+ {
+ $this->acceptHeaderValue = 'application/vnd.github.loki-preview+json';
+
+ return $this;
+ }
+
+ /**
+ * Retrieves configured protection for the provided branch.
+ *
+ * @link https://developer.github.com/v3/repos/branches/#get-branch-protection
+ *
+ * @param string $username The user who owns the repository
+ * @param string $repository The name of the repo
+ * @param string $branch The name of the branch
+ *
+ * @return array The branch protection information
+ */
+ public function show($username, $repository, $branch)
+ {
+ return $this->get('/repos/'.rawurlencode($username).'/'.rawurlencode($repository).'/branches/'.rawurlencode($branch).'/protection');
+ }
+
+ /**
+ * Updates the repo's branch protection.
+ *
+ * @link https://developer.github.com/v3/repos/branches/#update-branch-protection
+ *
+ * @param string $username The user who owns the repository
+ * @param string $repository The name of the repo
+ * @param string $branch The name of the branch
+ * @param array $params The branch protection information
+ *
+ * @return array The updated branch protection information
+ */
+ public function update($username, $repository, $branch, array $params = [])
+ {
+ return $this->put('/repos/'.rawurlencode($username).'/'.rawurlencode($repository).'/branches/'.rawurlencode($branch).'/protection', $params);
+ }
+
+ /**
+ * Remove the repo's branch protection.
+ *
+ * @link https://developer.github.com/v3/repos/branches/#remove-branch-protection
+ *
+ * @param string $username The user who owns the repository
+ * @param string $repository The name of the repo
+ * @param string $branch The name of the branch
+ */
+ public function remove($username, $repository, $branch)
+ {
+ return $this->delete('/repos/'.rawurlencode($username).'/'.rawurlencode($repository).'/branches/'.rawurlencode($branch).'/protection');
+ }
+
+ /**
+ * Get required status checks of protected branch.
+ *
+ * @link https://developer.github.com/v3/repos/branches/#get-required-status-checks-of-protected-branch
+ *
+ * @param string $username The user who owns the repository
+ * @param string $repository The name of the repo
+ * @param string $branch The name of the branch
+ *
+ * @return array The required status checks information
+ */
+ public function showStatusChecks($username, $repository, $branch)
+ {
+ return $this->get('/repos/'.rawurlencode($username).'/'.rawurlencode($repository).'/branches/'.rawurlencode($branch).'/protection/required_status_checks');
+ }
+
+ /**
+ * Update required status checks of protected branch.
+ *
+ * @link https://developer.github.com/v3/repos/branches/#update-required-status-checks-of-protected-branch
+ *
+ * @param string $username The user who owns the repository
+ * @param string $repository The name of the repo
+ * @param string $branch The name of the branch
+ * @param array $params The branch status checks information
+ *
+ * @return array The updated branch status checks information
+ */
+ public function updateStatusChecks($username, $repository, $branch, array $params = [])
+ {
+ return $this->patch('/repos/'.rawurlencode($username).'/'.rawurlencode($repository).'/branches/'.rawurlencode($branch).'/protection/required_status_checks', $params);
+ }
+
+ /**
+ * Remove required status checks of protected branch.
+ *
+ * @link https://developer.github.com/v3/repos/branches/#remove-required-status-checks-of-protected-branch
+ *
+ * @param string $username The user who owns the repository
+ * @param string $repository The name of the repo
+ * @param string $branch The name of the branch
+ */
+ public function removeStatusChecks($username, $repository, $branch)
+ {
+ return $this->delete('/repos/'.rawurlencode($username).'/'.rawurlencode($repository).'/branches/'.rawurlencode($branch).'/protection/required_status_checks');
+ }
+
+ /**
+ * List required status checks contexts of protected branch.
+ *
+ * @link https://developer.github.com/v3/repos/branches/#list-required-status-checks-contexts-of-protected-branch
+ *
+ * @param string $username The user who owns the repository
+ * @param string $repository The name of the repo
+ * @param string $branch The name of the branch
+ *
+ * @return array The required status checks contexts information
+ */
+ public function showStatusChecksContexts($username, $repository, $branch)
+ {
+ return $this->get('/repos/'.rawurlencode($username).'/'.rawurlencode($repository).'/branches/'.rawurlencode($branch).'/protection/required_status_checks/contexts');
+ }
+
+ /**
+ * Replace required status checks contexts of protected branch.
+ *
+ * @link https://developer.github.com/v3/repos/branches/#replace-required-status-checks-contexts-of-protected-branch
+ *
+ * @param string $username The user who owns the repository
+ * @param string $repository The name of the repo
+ * @param string $branch The name of the branch
+ * @param array $params The branch status checks contexts information
+ *
+ * @return array The new branch status checks contexts information
+ */
+ public function replaceStatusChecksContexts($username, $repository, $branch, array $params = [])
+ {
+ return $this->put('/repos/'.rawurlencode($username).'/'.rawurlencode($repository).'/branches/'.rawurlencode($branch).'/protection/required_status_checks/contexts', $params);
+ }
+
+ /**
+ * Add required status checks contexts of protected branch.
+ *
+ * @link https://developer.github.com/v3/repos/branches/#add-required-status-checks-contexts-of-protected-branch
+ *
+ * @param string $username The user who owns the repository
+ * @param string $repository The name of the repo
+ * @param string $branch The name of the branch
+ * @param array $params The branch status checks contexts information
+ *
+ * @return array The updated branch status checks contexts information
+ */
+ public function addStatusChecksContexts($username, $repository, $branch, array $params = [])
+ {
+ return $this->post('/repos/'.rawurlencode($username).'/'.rawurlencode($repository).'/branches/'.rawurlencode($branch).'/protection/required_status_checks/contexts', $params);
+ }
+
+ /**
+ * Remove required status checks contexts of protected branch.
+ *
+ * @link https://developer.github.com/v3/repos/branches/#remove-required-status-checks-contexts-of-protected-branch
+ *
+ * @param string $username The user who owns the repository
+ * @param string $repository The name of the repo
+ * @param string $branch The name of the branch
+ * @param array $params The branch status checks contexts information
+ *
+ * @return array The updated branch status checks contexts information
+ */
+ public function removeStatusChecksContexts($username, $repository, $branch, array $params = [])
+ {
+ return $this->delete('/repos/'.rawurlencode($username).'/'.rawurlencode($repository).'/branches/'.rawurlencode($branch).'/protection/required_status_checks/contexts', $params);
+ }
+
+ /**
+ * Get pull request review enforcement of protected branch.
+ *
+ * @link https://developer.github.com/v3/repos/branches/#get-pull-request-review-enforcement-of-protected-branch
+ *
+ * @param string $username The user who owns the repository
+ * @param string $repository The name of the repo
+ * @param string $branch The name of the branch
+ *
+ * @return array The pull request review enforcement information
+ */
+ public function showPullRequestReviewEnforcement($username, $repository, $branch)
+ {
+ return $this->get('/repos/'.rawurlencode($username).'/'.rawurlencode($repository).'/branches/'.rawurlencode($branch).'/protection/required_pull_request_reviews');
+ }
+
+ /**
+ * Update pull request review enforcement of protected branch.
+ *
+ * @link https://developer.github.com/v3/repos/branches/#update-pull-request-review-enforcement-of-protected-branch
+ *
+ * @param string $username The user who owns the repository
+ * @param string $repository The name of the repo
+ * @param string $branch The name of the branch
+ * @param array $params The branch status checks information
+ *
+ * @return array The updated branch status checks information
+ */
+ public function updatePullRequestReviewEnforcement($username, $repository, $branch, array $params = [])
+ {
+ return $this->patch('/repos/'.rawurlencode($username).'/'.rawurlencode($repository).'/branches/'.rawurlencode($branch).'/protection/required_pull_request_reviews', $params);
+ }
+
+ /**
+ * Remove pull request review enforcement of protected branch.
+ *
+ * @link https://developer.github.com/v3/repos/branches/#remove-pull-request-review-enforcement-of-protected-branch
+ *
+ * @param string $username The user who owns the repository
+ * @param string $repository The name of the repo
+ * @param string $branch The name of the branch
+ */
+ public function removePullRequestReviewEnforcement($username, $repository, $branch)
+ {
+ return $this->delete('/repos/'.rawurlencode($username).'/'.rawurlencode($repository).'/branches/'.rawurlencode($branch).'/protection/required_pull_request_reviews');
+ }
+
+ /**
+ * Get admin enforcement of protected branch.
+ *
+ * @link https://developer.github.com/v3/repos/branches/#get-admin-enforcement-of-protected-branch
+ *
+ * @param string $username The user who owns the repository
+ * @param string $repository The name of the repo
+ * @param string $branch The name of the branch
+ *
+ * @return array The admin enforcement information
+ */
+ public function showAdminEnforcement($username, $repository, $branch)
+ {
+ return $this->get('/repos/'.rawurlencode($username).'/'.rawurlencode($repository).'/branches/'.rawurlencode($branch).'/protection/enforce_admins');
+ }
+
+ /**
+ * Add admin enforcement of protected branch.
+ *
+ * @link https://developer.github.com/v3/repos/branches/#add-admin-enforcement-of-protected-branch
+ *
+ * @param string $username The user who owns the repository
+ * @param string $repository The name of the repo
+ * @param string $branch The name of the branch
+ *
+ * @return array The updated admin enforcement information
+ */
+ public function addAdminEnforcement($username, $repository, $branch)
+ {
+ return $this->post('/repos/'.rawurlencode($username).'/'.rawurlencode($repository).'/branches/'.rawurlencode($branch).'/protection/enforce_admins');
+ }
+
+ /**
+ * Remove admin enforcement of protected branch.
+ *
+ * @link https://developer.github.com/v3/repos/branches/#remove-admin-enforcement-of-protected-branch
+ *
+ * @param string $username The user who owns the repository
+ * @param string $repository The name of the repo
+ * @param string $branch The name of the branch
+ */
+ public function removeAdminEnforcement($username, $repository, $branch)
+ {
+ return $this->delete('/repos/'.rawurlencode($username).'/'.rawurlencode($repository).'/branches/'.rawurlencode($branch).'/protection/enforce_admins');
+ }
+
+ /**
+ * Get restrictions of protected branch.
+ *
+ * @link https://developer.github.com/v3/repos/branches/#get-restrictions-of-protected-branch
+ *
+ * @param string $username The user who owns the repository
+ * @param string $repository The name of the repo
+ * @param string $branch The name of the branch
+ *
+ * @return array The branch restrictions information
+ */
+ public function showRestrictions($username, $repository, $branch)
+ {
+ return $this->get('/repos/'.rawurlencode($username).'/'.rawurlencode($repository).'/branches/'.rawurlencode($branch).'/protection/restrictions');
+ }
+
+ /**
+ * Remove restrictions of protected branch.
+ *
+ * @link https://developer.github.com/v3/repos/branches/#remove-restrictions-of-protected-branch
+ *
+ * @param string $username The user who owns the repository
+ * @param string $repository The name of the repo
+ * @param string $branch The name of the branch
+ */
+ public function removeRestrictions($username, $repository, $branch)
+ {
+ return $this->delete('/repos/'.rawurlencode($username).'/'.rawurlencode($repository).'/branches/'.rawurlencode($branch).'/protection/restrictions');
+ }
+
+ /**
+ * List team restrictions of protected branch.
+ *
+ * @link https://developer.github.com/v3/repos/branches/#list-team-restrictions-of-protected-branch
+ *
+ * @param string $username The user who owns the repository
+ * @param string $repository The name of the repo
+ * @param string $branch The name of the branch
+ *
+ * @return array The branch team restrictions information
+ */
+ public function showTeamRestrictions($username, $repository, $branch)
+ {
+ return $this->get('/repos/'.rawurlencode($username).'/'.rawurlencode($repository).'/branches/'.rawurlencode($branch).'/protection/restrictions/teams');
+ }
+
+ /**
+ * Replace team restrictions of protected branch.
+ *
+ * @link https://developer.github.com/v3/repos/branches/#replace-team-restrictions-of-protected-branch
+ *
+ * @param string $username The user who owns the repository
+ * @param string $repository The name of the repo
+ * @param string $branch The name of the branch
+ * @param array $params The list of team slugs with push access
+ *
+ * @return array The new branch team restrictions information
+ */
+ public function replaceTeamRestrictions($username, $repository, $branch, array $params = [])
+ {
+ return $this->put('/repos/'.rawurlencode($username).'/'.rawurlencode($repository).'/branches/'.rawurlencode($branch).'/protection/restrictions/teams', $params);
+ }
+
+ /**
+ * Add team restrictions of protected branch.
+ *
+ * @link https://developer.github.com/v3/repos/branches/#add-team-restrictions-of-protected-branch
+ *
+ * @param string $username The user who owns the repository
+ * @param string $repository The name of the repo
+ * @param string $branch The name of the branch
+ * @param array $params The list of team slugs with push access
+ *
+ * @return array The branch team restrictions information
+ */
+ public function addTeamRestrictions($username, $repository, $branch, array $params = [])
+ {
+ return $this->post('/repos/'.rawurlencode($username).'/'.rawurlencode($repository).'/branches/'.rawurlencode($branch).'/protection/restrictions/teams', $params);
+ }
+
+ /**
+ * Remove team restrictions of protected branch.
+ *
+ * @link https://developer.github.com/v3/repos/branches/#remove-team-restrictions-of-protected-branch
+ *
+ * @param string $username The user who owns the repository
+ * @param string $repository The name of the repo
+ * @param string $branch The name of the branch
+ * @param array $params The list of team slugs with push access
+ *
+ * @return array The updated branch team restrictions information
+ */
+ public function removeTeamRestrictions($username, $repository, $branch, array $params = [])
+ {
+ return $this->delete('/repos/'.rawurlencode($username).'/'.rawurlencode($repository).'/branches/'.rawurlencode($branch).'/protection/restrictions/teams', $params);
+ }
+
+ /**
+ * List user restrictions of protected branch.
+ *
+ * @link https://developer.github.com/v3/repos/branches/#list-user-restrictions-of-protected-branch
+ *
+ * @param string $username The user who owns the repository
+ * @param string $repository The name of the repo
+ * @param string $branch The name of the branch
+ *
+ * @return array The branch user restrictions information
+ */
+ public function showUserRestrictions($username, $repository, $branch)
+ {
+ return $this->get('/repos/'.rawurlencode($username).'/'.rawurlencode($repository).'/branches/'.rawurlencode($branch).'/protection/restrictions/users');
+ }
+
+ /**
+ * Replace user restrictions of protected branch.
+ *
+ * @link https://developer.github.com/v3/repos/branches/#replace-user-restrictions-of-protected-branch
+ *
+ * @param string $username The user who owns the repository
+ * @param string $repository The name of the repo
+ * @param string $branch The name of the branch
+ * @param array $params The list of user logins with push access
+ *
+ * @return array The new branch user restrictions information
+ */
+ public function replaceUserRestrictions($username, $repository, $branch, array $params = [])
+ {
+ return $this->put('/repos/'.rawurlencode($username).'/'.rawurlencode($repository).'/branches/'.rawurlencode($branch).'/protection/restrictions/users', $params);
+ }
+
+ /**
+ * Add user restrictions of protected branch.
+ *
+ * @link https://developer.github.com/v3/repos/branches/#add-user-restrictions-of-protected-branch
+ *
+ * @param string $username The user who owns the repository
+ * @param string $repository The name of the repo
+ * @param string $branch The name of the branch
+ * @param array $params The list of user logins with push access
+ *
+ * @return array The branch user restrictions information
+ */
+ public function addUserRestrictions($username, $repository, $branch, array $params = [])
+ {
+ return $this->post('/repos/'.rawurlencode($username).'/'.rawurlencode($repository).'/branches/'.rawurlencode($branch).'/protection/restrictions/users', $params);
+ }
+
+ /**
+ * Remove user restrictions of protected branch.
+ *
+ * @link https://developer.github.com/v3/repos/branches/#remove-user-restrictions-of-protected-branch
+ *
+ * @param string $username The user who owns the repository
+ * @param string $repository The name of the repo
+ * @param string $branch The name of the branch
+ * @param array $params The list of user logins with push access
+ *
+ * @return array The updated branch user restrictions information
+ */
+ public function removeUserRestrictions($username, $repository, $branch, array $params = [])
+ {
+ return $this->delete('/repos/'.rawurlencode($username).'/'.rawurlencode($repository).'/branches/'.rawurlencode($branch).'/protection/restrictions/users', $params);
+ }
+}
diff --git a/vendor/knplabs/github-api/lib/Github/Api/Repository/Releases.php b/vendor/knplabs/github-api/lib/Github/Api/Repository/Releases.php
new file mode 100644
index 00000000..73fd080b
--- /dev/null
+++ b/vendor/knplabs/github-api/lib/Github/Api/Repository/Releases.php
@@ -0,0 +1,127 @@
+
+ * @author Evgeniy Guseletov
+ */
+class Releases extends AbstractApi
+{
+ /**
+ * Get the latest release.
+ *
+ * @param $username
+ * @param $repository
+ *
+ * @return array
+ */
+ public function latest($username, $repository)
+ {
+ return $this->get('/repos/'.rawurlencode($username).'/'.rawurlencode($repository).'/releases/latest');
+ }
+
+ /**
+ * List releases for a tag.
+ *
+ * @param $username
+ * @param $repository
+ * @param $tag
+ *
+ * @return array
+ */
+ public function tag($username, $repository, $tag)
+ {
+ return $this->get('/repos/'.rawurlencode($username).'/'.rawurlencode($repository).'/releases/tags/'.rawurlencode($tag));
+ }
+
+ /**
+ * List releases in selected repository.
+ *
+ * @param string $username the user who owns the repo
+ * @param string $repository the name of the repo
+ * @param array $params the additional parameters like milestone, assignees, labels, sort, direction
+ *
+ * @return array
+ */
+ public function all($username, $repository, array $params = [])
+ {
+ return $this->get('/repos/'.rawurlencode($username).'/'.rawurlencode($repository).'/releases', $params);
+ }
+
+ /**
+ * Get a release in selected repository.
+ *
+ * @param string $username the user who owns the repo
+ * @param string $repository the name of the repo
+ * @param int $id the id of the release
+ *
+ * @return array
+ */
+ public function show($username, $repository, $id)
+ {
+ return $this->get('/repos/'.rawurlencode($username).'/'.rawurlencode($repository).'/releases/'.rawurlencode($id));
+ }
+
+ /**
+ * Create new release in selected repository.
+ *
+ * @param string $username
+ * @param string $repository
+ * @param array $params
+ *
+ * @throws MissingArgumentException
+ *
+ * @return array
+ */
+ public function create($username, $repository, array $params)
+ {
+ if (!isset($params['tag_name'])) {
+ throw new MissingArgumentException('tag_name');
+ }
+
+ return $this->post('/repos/'.rawurlencode($username).'/'.rawurlencode($repository).'/releases', $params);
+ }
+
+ /**
+ * Edit release in selected repository.
+ *
+ * @param string $username
+ * @param string $repository
+ * @param int $id
+ * @param array $params
+ *
+ * @return array
+ */
+ public function edit($username, $repository, $id, array $params)
+ {
+ return $this->patch('/repos/'.rawurlencode($username).'/'.rawurlencode($repository).'/releases/'.rawurlencode($id), $params);
+ }
+
+ /**
+ * Delete a release in selected repository (Not thoroughly tested!).
+ *
+ * @param string $username the user who owns the repo
+ * @param string $repository the name of the repo
+ * @param int $id the id of the release
+ *
+ * @return array
+ */
+ public function remove($username, $repository, $id)
+ {
+ return $this->delete('/repos/'.rawurlencode($username).'/'.rawurlencode($repository).'/releases/'.rawurlencode($id));
+ }
+
+ /**
+ * @return Assets
+ */
+ public function assets()
+ {
+ return new Assets($this->client);
+ }
+}
diff --git a/vendor/knplabs/github-api/lib/Github/Api/Repository/Stargazers.php b/vendor/knplabs/github-api/lib/Github/Api/Repository/Stargazers.php
new file mode 100644
index 00000000..e7177774
--- /dev/null
+++ b/vendor/knplabs/github-api/lib/Github/Api/Repository/Stargazers.php
@@ -0,0 +1,40 @@
+
+ * @author Tobias Nyholm
+ */
+class Stargazers extends AbstractApi
+{
+ use AcceptHeaderTrait;
+
+ /**
+ * Configure the body type.
+ *
+ * @see https://developer.github.com/v3/activity/starring/#alternative-response-with-star-creation-timestamps
+ *
+ * @param string $bodyType
+ *
+ * @return self
+ */
+ public function configure($bodyType = null)
+ {
+ if ('star' === $bodyType) {
+ $this->acceptHeaderValue = sprintf('application/vnd.github.%s.star+json', $this->client->getApiVersion());
+ }
+
+ return $this;
+ }
+
+ public function all($username, $repository)
+ {
+ return $this->get('/repos/'.rawurlencode($username).'/'.rawurlencode($repository).'/stargazers');
+ }
+}
diff --git a/vendor/knplabs/github-api/lib/Github/Api/Repository/Statuses.php b/vendor/knplabs/github-api/lib/Github/Api/Repository/Statuses.php
new file mode 100644
index 00000000..c62f4edd
--- /dev/null
+++ b/vendor/knplabs/github-api/lib/Github/Api/Repository/Statuses.php
@@ -0,0 +1,63 @@
+
+ */
+class Statuses extends AbstractApi
+{
+ /**
+ * @link http://developer.github.com/v3/repos/statuses/#list-statuses-for-a-specific-sha
+ *
+ * @param string $username
+ * @param string $repository
+ * @param string $sha
+ *
+ * @return array
+ */
+ public function show($username, $repository, $sha)
+ {
+ return $this->get('/repos/'.rawurlencode($username).'/'.rawurlencode($repository).'/commits/'.rawurlencode($sha).'/statuses');
+ }
+
+ /**
+ * @link https://developer.github.com/v3/repos/statuses/#get-the-combined-status-for-a-specific-ref
+ *
+ * @param string $username
+ * @param string $repository
+ * @param string $sha
+ *
+ * @return array
+ */
+ public function combined($username, $repository, $sha)
+ {
+ return $this->get('/repos/'.rawurlencode($username).'/'.rawurlencode($repository).'/commits/'.rawurlencode($sha).'/status');
+ }
+
+ /**
+ * @link http://developer.github.com/v3/repos/statuses/#create-a-status
+ *
+ * @param string $username
+ * @param string $repository
+ * @param string $sha
+ * @param array $params
+ *
+ * @throws MissingArgumentException
+ *
+ * @return array
+ */
+ public function create($username, $repository, $sha, array $params = [])
+ {
+ if (!isset($params['state'])) {
+ throw new MissingArgumentException('state');
+ }
+
+ return $this->post('/repos/'.rawurlencode($username).'/'.rawurlencode($repository).'/statuses/'.rawurlencode($sha), $params);
+ }
+}
diff --git a/vendor/knplabs/github-api/lib/Github/Api/Repository/Traffic.php b/vendor/knplabs/github-api/lib/Github/Api/Repository/Traffic.php
new file mode 100644
index 00000000..d5b550c8
--- /dev/null
+++ b/vendor/knplabs/github-api/lib/Github/Api/Repository/Traffic.php
@@ -0,0 +1,67 @@
+
+ */
+class Traffic extends AbstractApi
+{
+ /**
+ * @link https://developer.github.com/v3/repos/traffic/#list-referrers
+ *
+ * @param string $owner
+ * @param string $repository
+ *
+ * @return array
+ */
+ public function referers($owner, $repository)
+ {
+ return $this->get('/repos/'.rawurlencode($owner).'/'.rawurlencode($repository).'/traffic/popular/referrers');
+ }
+
+ /**
+ * @link https://developer.github.com/v3/repos/traffic/#list-paths
+ *
+ * @param string $owner
+ * @param string $repository
+ *
+ * @return array
+ */
+ public function paths($owner, $repository)
+ {
+ return $this->get('/repos/'.rawurlencode($owner).'/'.rawurlencode($repository).'/traffic/popular/paths');
+ }
+
+ /**
+ * @link https://developer.github.com/v3/repos/traffic/#views
+ *
+ * @param string $owner
+ * @param string $repository
+ * @param string $per
+ *
+ * @return array
+ */
+ public function views($owner, $repository, $per = 'day')
+ {
+ return $this->get('/repos/'.rawurlencode($owner).'/'.rawurlencode($repository).'/traffic/views?per='.rawurlencode($per));
+ }
+
+ /**
+ * @link https://developer.github.com/v3/repos/traffic/#clones
+ *
+ * @param string $owner
+ * @param string $repository
+ * @param string $per
+ *
+ * @return array
+ */
+ public function clones($owner, $repository, $per = 'day')
+ {
+ return $this->get('/repos/'.rawurlencode($owner).'/'.rawurlencode($repository).'/traffic/clones?per='.rawurlencode($per));
+ }
+}
diff --git a/vendor/knplabs/github-api/lib/Github/Api/Search.php b/vendor/knplabs/github-api/lib/Github/Api/Search.php
new file mode 100644
index 00000000..84626ae3
--- /dev/null
+++ b/vendor/knplabs/github-api/lib/Github/Api/Search.php
@@ -0,0 +1,115 @@
+
+ */
+class Search extends AbstractApi
+{
+ use AcceptHeaderTrait;
+
+ /**
+ * Search repositories by filter (q).
+ *
+ * @link https://developer.github.com/v3/search/#search-repositories
+ *
+ * @param string $q the filter
+ * @param string $sort the sort field
+ * @param string $order asc/desc
+ *
+ * @return array list of repositories found
+ */
+ public function repositories($q, $sort = 'updated', $order = 'desc')
+ {
+ return $this->get('/search/repositories', ['q' => $q, 'sort' => $sort, 'order' => $order]);
+ }
+
+ /**
+ * Search issues by filter (q).
+ *
+ * @link https://developer.github.com/v3/search/#search-issues
+ *
+ * @param string $q the filter
+ * @param string $sort the sort field
+ * @param string $order asc/desc
+ *
+ * @return array list of issues found
+ */
+ public function issues($q, $sort = 'updated', $order = 'desc')
+ {
+ return $this->get('/search/issues', ['q' => $q, 'sort' => $sort, 'order' => $order]);
+ }
+
+ /**
+ * Search code by filter (q).
+ *
+ * @link https://developer.github.com/v3/search/#search-code
+ *
+ * @param string $q the filter
+ * @param string $sort the sort field
+ * @param string $order asc/desc
+ *
+ * @return array list of code found
+ */
+ public function code($q, $sort = 'updated', $order = 'desc')
+ {
+ return $this->get('/search/code', ['q' => $q, 'sort' => $sort, 'order' => $order]);
+ }
+
+ /**
+ * Search users by filter (q).
+ *
+ * @link https://developer.github.com/v3/search/#search-users
+ *
+ * @param string $q the filter
+ * @param string $sort the sort field
+ * @param string $order asc/desc
+ *
+ * @return array list of users found
+ */
+ public function users($q, $sort = 'updated', $order = 'desc')
+ {
+ return $this->get('/search/users', ['q' => $q, 'sort' => $sort, 'order' => $order]);
+ }
+
+ /**
+ * Search commits by filter (q).
+ *
+ * @link https://developer.github.com/v3/search/#search-commits
+ *
+ * @param string $q the filter
+ * @param string $sort the sort field
+ * @param string $order sort order. asc/desc
+ *
+ * @return array
+ */
+ public function commits($q, $sort = null, $order = 'desc')
+ {
+ //This api is in preview mode, so set the correct accept-header
+ $this->acceptHeaderValue = 'application/vnd.github.cloak-preview';
+
+ return $this->get('/search/commits', ['q' => $q, 'sort' => $sort, 'order' => $order]);
+ }
+
+ /**
+ * Search commits by filter (q).
+ *
+ * @link https://developer.github.com/v3/search/#search-topics
+ *
+ * @param string $q the filter
+ *
+ * @return array
+ */
+ public function topics($q)
+ {
+ //This api is in preview mode, so set the correct accept-header
+ $this->acceptHeaderValue = 'application/vnd.github.mercy-preview+json';
+
+ return $this->get('/search/topics', ['q' => $q]);
+ }
+}
diff --git a/vendor/knplabs/github-api/lib/Github/Api/User.php b/vendor/knplabs/github-api/lib/Github/Api/User.php
new file mode 100644
index 00000000..c4414c56
--- /dev/null
+++ b/vendor/knplabs/github-api/lib/Github/Api/User.php
@@ -0,0 +1,251 @@
+
+ * @author Thibault Duplessis
+ */
+class User extends AbstractApi
+{
+ /**
+ * Search users by username.
+ *
+ * @deprecated This method is deprecated use the Search api instead. See https://developer.github.com/v3/search/legacy/#legacy-search-api-is-deprecated
+ * @link http://developer.github.com/v3/search/#search-users
+ *
+ * @param string $keyword the keyword to search
+ *
+ * @return array list of users found
+ */
+ public function find($keyword)
+ {
+ return $this->get('/legacy/user/search/'.rawurlencode($keyword));
+ }
+
+ /**
+ * Request all users.
+ *
+ * @link https://developer.github.com/v3/users/#get-all-users
+ *
+ * @param int|null $id ID of the last user that you've seen
+ *
+ * @return array list of users found
+ */
+ public function all($id = null)
+ {
+ if (!is_int($id)) {
+ return $this->get('/users');
+ }
+
+ return $this->get('/users', ['since' => rawurldecode($id)]);
+ }
+
+ /**
+ * Get extended information about a user by its username.
+ *
+ * @link http://developer.github.com/v3/users/
+ *
+ * @param string $username the username to show
+ *
+ * @return array information about the user
+ */
+ public function show($username)
+ {
+ return $this->get('/users/'.rawurlencode($username));
+ }
+
+ /**
+ * Get extended information about a user by its username.
+ *
+ * @link https://developer.github.com/v3/orgs/
+ *
+ * @param string $username the username to show
+ *
+ * @return array information about organizations that user belongs to
+ */
+ public function organizations($username)
+ {
+ return $this->get('/users/'.rawurlencode($username).'/orgs');
+ }
+
+ /**
+ * Get user organizations.
+ *
+ * @link https://developer.github.com/v3/orgs/#list-your-organizations
+ *
+ * @return array information about organizations that authenticated user belongs to
+ */
+ public function orgs()
+ {
+ return $this->get('/user/orgs');
+ }
+
+ /**
+ * Request the users that a specific user is following.
+ *
+ * @link http://developer.github.com/v3/users/followers/
+ *
+ * @param string $username the username
+ * @param array $parameters parameters for the query string
+ * @param array $requestHeaders additional headers to set in the request
+ *
+ * @return array list of followed users
+ */
+ public function following($username, array $parameters = [], array $requestHeaders = [])
+ {
+ return $this->get('/users/'.rawurlencode($username).'/following', $parameters, $requestHeaders);
+ }
+
+ /**
+ * Request the users following a specific user.
+ *
+ * @link http://developer.github.com/v3/users/followers/
+ *
+ * @param string $username the username
+ * @param array $parameters parameters for the query string
+ * @param array $requestHeaders additional headers to set in the request
+ *
+ * @return array list of following users
+ */
+ public function followers($username, array $parameters = [], array $requestHeaders = [])
+ {
+ return $this->get('/users/'.rawurlencode($username).'/followers', $parameters, $requestHeaders);
+ }
+
+ /**
+ * Request the repository that a specific user is watching.
+ *
+ * @deprecated see subscriptions method
+ *
+ * @param string $username the username
+ *
+ * @return array list of watched repositories
+ */
+ public function watched($username)
+ {
+ return $this->get('/users/'.rawurlencode($username).'/watched');
+ }
+
+ /**
+ * Request starred repositories that a specific user has starred.
+ *
+ * @link http://developer.github.com/v3/activity/starring/
+ *
+ * @param string $username the username
+ * @param int $page the page number of the paginated result set
+ * @param int $perPage the number of results per page
+ * @param string $sort sort by (possible values: created, updated)
+ * @param string $direction direction of sort (possible values: asc, desc)
+ *
+ * @return array list of starred repositories
+ */
+ public function starred($username, $page = 1, $perPage = 30, $sort = 'created', $direction = 'desc')
+ {
+ return $this->get('/users/'.rawurlencode($username).'/starred', [
+ 'page' => $page,
+ 'per_page' => $perPage,
+ 'sort' => $sort,
+ 'direction' => $direction,
+ ]);
+ }
+
+ /**
+ * Request the repository that a specific user is watching.
+ *
+ * @link http://developer.github.com/v3/activity/watching/
+ *
+ * @param string $username the username
+ *
+ * @return array list of watched repositories
+ */
+ public function subscriptions($username)
+ {
+ return $this->get('/users/'.rawurlencode($username).'/subscriptions');
+ }
+
+ /**
+ * List public repositories for the specified user.
+ *
+ * @link https://developer.github.com/v3/repos/#list-user-repositories
+ *
+ * @param string $username the username
+ * @param string $type role in the repository
+ * @param string $sort sort by
+ * @param string $direction direction of sort, asc or desc
+ * @param string $visibility visibility of repository
+ * @param string $affiliation relationship to repository
+ *
+ * @return array list of the user repositories
+ */
+ public function repositories($username, $type = 'owner', $sort = 'full_name', $direction = 'asc', $visibility = 'all', $affiliation = 'owner,collaborator,organization_member')
+ {
+ return $this->get('/users/'.rawurlencode($username).'/repos', [
+ 'type' => $type,
+ 'sort' => $sort,
+ 'direction' => $direction,
+ 'visibility' => $visibility,
+ 'affiliation' => $affiliation,
+ ]);
+ }
+
+ /**
+ * List repositories that are accessible to the authenticated user.
+ *
+ * @link https://developer.github.com/v3/repos/#list-your-repositories
+ *
+ * @param array $params visibility, affiliation, type, sort, direction
+ *
+ * @return array list of the user repositories
+ */
+ public function myRepositories(array $params = [])
+ {
+ return $this->get('/user/repos', $params);
+ }
+
+ /**
+ * Get the public gists for a user.
+ *
+ * @link http://developer.github.com/v3/gists/
+ *
+ * @param string $username the username
+ *
+ * @return array list of the user gists
+ */
+ public function gists($username)
+ {
+ return $this->get('/users/'.rawurlencode($username).'/gists');
+ }
+
+ /**
+ * Get the public keys for a user.
+ *
+ * @link http://developer.github.com/v3/users/keys/#list-public-keys-for-a-user
+ *
+ * @param string $username the username
+ *
+ * @return array list of the user public keys
+ */
+ public function keys($username)
+ {
+ return $this->get('/users/'.rawurlencode($username).'/keys');
+ }
+
+ /**
+ * List events performed by a user.
+ *
+ * @link http://developer.github.com/v3/activity/events/#list-public-events-performed-by-a-user
+ *
+ * @param string $username
+ *
+ * @return array
+ */
+ public function publicEvents($username)
+ {
+ return $this->get('/users/'.rawurlencode($username).'/events/public');
+ }
+}
diff --git a/vendor/knplabs/github-api/lib/Github/Client.php b/vendor/knplabs/github-api/lib/Github/Client.php
new file mode 100644
index 00000000..8e2ea275
--- /dev/null
+++ b/vendor/knplabs/github-api/lib/Github/Client.php
@@ -0,0 +1,414 @@
+
+ *
+ * Website: http://github.com/KnpLabs/php-github-api
+ */
+class Client
+{
+ /**
+ * Constant for authentication method. Indicates the default, but deprecated
+ * login with username and token in URL.
+ */
+ const AUTH_URL_TOKEN = 'url_token';
+
+ /**
+ * Constant for authentication method. Not indicates the new login, but allows
+ * usage of unauthenticated rate limited requests for given client_id + client_secret.
+ */
+ const AUTH_URL_CLIENT_ID = 'url_client_id';
+
+ /**
+ * Constant for authentication method. Indicates the new favored login method
+ * with username and password via HTTP Authentication.
+ */
+ const AUTH_HTTP_PASSWORD = 'http_password';
+
+ /**
+ * Constant for authentication method. Indicates the new login method with
+ * with username and token via HTTP Authentication.
+ */
+ const AUTH_HTTP_TOKEN = 'http_token';
+
+ /**
+ * Constant for authentication method. Indicates JSON Web Token
+ * authentication required for integration access to the API.
+ */
+ const AUTH_JWT = 'jwt';
+
+ /**
+ * @var string
+ */
+ private $apiVersion;
+
+ /**
+ * @var Builder
+ */
+ private $httpClientBuilder;
+
+ /**
+ * @var History
+ */
+ private $responseHistory;
+
+ /**
+ * Instantiate a new GitHub client.
+ *
+ * @param Builder|null $httpClientBuilder
+ * @param string|null $apiVersion
+ * @param string|null $enterpriseUrl
+ */
+ public function __construct(Builder $httpClientBuilder = null, $apiVersion = null, $enterpriseUrl = null)
+ {
+ $this->responseHistory = new History();
+ $this->httpClientBuilder = $builder = $httpClientBuilder ?: new Builder();
+
+ $builder->addPlugin(new GithubExceptionThrower());
+ $builder->addPlugin(new Plugin\HistoryPlugin($this->responseHistory));
+ $builder->addPlugin(new Plugin\RedirectPlugin());
+ $builder->addPlugin(new Plugin\AddHostPlugin(UriFactoryDiscovery::find()->createUri('https://api.github.com')));
+ $builder->addPlugin(new Plugin\HeaderDefaultsPlugin([
+ 'User-Agent' => 'php-github-api (http://github.com/KnpLabs/php-github-api)',
+ ]));
+
+ $this->apiVersion = $apiVersion ?: 'v3';
+ $builder->addHeaderValue('Accept', sprintf('application/vnd.github.%s+json', $this->apiVersion));
+
+ if ($enterpriseUrl) {
+ $this->setEnterpriseUrl($enterpriseUrl);
+ }
+ }
+
+ /**
+ * Create a Github\Client using a HttpClient.
+ *
+ * @param HttpClient $httpClient
+ *
+ * @return Client
+ */
+ public static function createWithHttpClient(HttpClient $httpClient)
+ {
+ $builder = new Builder($httpClient);
+
+ return new self($builder);
+ }
+
+ /**
+ * @param string $name
+ *
+ * @throws InvalidArgumentException
+ *
+ * @return ApiInterface
+ */
+ public function api($name)
+ {
+ switch ($name) {
+ case 'me':
+ case 'current_user':
+ case 'currentUser':
+ $api = new Api\CurrentUser($this);
+ break;
+ case 'codeOfConduct':
+ $api = new Api\Miscellaneous\CodeOfConduct($this);
+ break;
+
+ case 'deployment':
+ case 'deployments':
+ $api = new Api\Deployment($this);
+ break;
+
+ case 'ent':
+ case 'enterprise':
+ $api = new Api\Enterprise($this);
+ break;
+
+ case 'emojis':
+ $api = new Api\Miscellaneous\Emojis($this);
+ break;
+
+ case 'git':
+ case 'git_data':
+ case 'gitData':
+ $api = new Api\GitData($this);
+ break;
+
+ case 'gist':
+ case 'gists':
+ $api = new Api\Gists($this);
+ break;
+
+ case 'gitignore':
+ $api = new Api\Miscellaneous\Gitignore($this);
+ break;
+
+ case 'integration':
+ case 'integrations':
+ $api = new Api\Integrations($this);
+ break;
+
+ case 'apps':
+ $api = new Api\Apps($this);
+ break;
+
+ case 'issue':
+ case 'issues':
+ $api = new Api\Issue($this);
+ break;
+
+ case 'markdown':
+ $api = new Api\Markdown($this);
+ break;
+
+ case 'notification':
+ case 'notifications':
+ $api = new Api\Notification($this);
+ break;
+
+ case 'organization':
+ case 'organizations':
+ $api = new Api\Organization($this);
+ break;
+
+ case 'org_project':
+ case 'orgProject':
+ case 'org_projects':
+ case 'orgProjects':
+ case 'organization_project':
+ case 'organizationProject':
+ case 'organization_projects':
+ case 'organizationProjects':
+ $api = new Api\Organization\Projects($this);
+ break;
+
+ case 'pr':
+ case 'pulls':
+ case 'pullRequest':
+ case 'pull_request':
+ case 'pullRequests':
+ case 'pull_requests':
+ $api = new Api\PullRequest($this);
+ break;
+
+ case 'rateLimit':
+ case 'rate_limit':
+ $api = new Api\RateLimit($this);
+ break;
+
+ case 'repo':
+ case 'repos':
+ case 'repository':
+ case 'repositories':
+ $api = new Api\Repo($this);
+ break;
+
+ case 'search':
+ $api = new Api\Search($this);
+ break;
+
+ case 'team':
+ case 'teams':
+ $api = new Api\Organization\Teams($this);
+ break;
+
+ case 'member':
+ case 'members':
+ $api = new Api\Organization\Members($this);
+ break;
+
+ case 'user':
+ case 'users':
+ $api = new Api\User($this);
+ break;
+
+ case 'authorization':
+ case 'authorizations':
+ $api = new Api\Authorizations($this);
+ break;
+
+ case 'meta':
+ $api = new Api\Meta($this);
+ break;
+
+ case 'graphql':
+ $api = new Api\GraphQL($this);
+ break;
+
+ default:
+ throw new InvalidArgumentException(sprintf('Undefined api instance called: "%s"', $name));
+ }
+
+ return $api;
+ }
+
+ /**
+ * Authenticate a user for all next requests.
+ *
+ * @param string $tokenOrLogin GitHub private token/username/client ID
+ * @param null|string $password GitHub password/secret (optionally can contain $authMethod)
+ * @param null|string $authMethod One of the AUTH_* class constants
+ *
+ * @throws InvalidArgumentException If no authentication method was given
+ */
+ public function authenticate($tokenOrLogin, $password = null, $authMethod = null)
+ {
+ if (null === $password && null === $authMethod) {
+ throw new InvalidArgumentException('You need to specify authentication method!');
+ }
+
+ if (null === $authMethod && in_array($password, [self::AUTH_URL_TOKEN, self::AUTH_URL_CLIENT_ID, self::AUTH_HTTP_PASSWORD, self::AUTH_HTTP_TOKEN, self::AUTH_JWT])) {
+ $authMethod = $password;
+ $password = null;
+ }
+
+ if (null === $authMethod) {
+ $authMethod = self::AUTH_HTTP_PASSWORD;
+ }
+
+ $this->getHttpClientBuilder()->removePlugin(Authentication::class);
+ $this->getHttpClientBuilder()->addPlugin(new Authentication($tokenOrLogin, $password, $authMethod));
+ }
+
+ /**
+ * Sets the URL of your GitHub Enterprise instance.
+ *
+ * @param string $enterpriseUrl URL of the API in the form of http(s)://hostname
+ */
+ private function setEnterpriseUrl($enterpriseUrl)
+ {
+ $builder = $this->getHttpClientBuilder();
+ $builder->removePlugin(Plugin\AddHostPlugin::class);
+ $builder->removePlugin(PathPrepend::class);
+
+ $builder->addPlugin(new Plugin\AddHostPlugin(UriFactoryDiscovery::find()->createUri($enterpriseUrl)));
+ $builder->addPlugin(new PathPrepend(sprintf('/api/%s', $this->getApiVersion())));
+ }
+
+ /**
+ * @return string
+ */
+ public function getApiVersion()
+ {
+ return $this->apiVersion;
+ }
+
+ /**
+ * Add a cache plugin to cache responses locally.
+ *
+ * @param CacheItemPoolInterface $cache
+ * @param array $config
+ */
+ public function addCache(CacheItemPoolInterface $cachePool, array $config = [])
+ {
+ $this->getHttpClientBuilder()->addCache($cachePool, $config);
+ }
+
+ /**
+ * Remove the cache plugin.
+ */
+ public function removeCache()
+ {
+ $this->getHttpClientBuilder()->removeCache();
+ }
+
+ /**
+ * @param string $name
+ *
+ * @throws BadMethodCallException
+ *
+ * @return ApiInterface
+ */
+ public function __call($name, $args)
+ {
+ try {
+ return $this->api($name);
+ } catch (InvalidArgumentException $e) {
+ throw new BadMethodCallException(sprintf('Undefined method called: "%s"', $name));
+ }
+ }
+
+ /**
+ * @return null|\Psr\Http\Message\ResponseInterface
+ */
+ public function getLastResponse()
+ {
+ return $this->responseHistory->getLastResponse();
+ }
+
+ /**
+ * @return HttpMethodsClient
+ */
+ public function getHttpClient()
+ {
+ return $this->getHttpClientBuilder()->getHttpClient();
+ }
+
+ /**
+ * @return Builder
+ */
+ protected function getHttpClientBuilder()
+ {
+ return $this->httpClientBuilder;
+ }
+}
diff --git a/vendor/knplabs/github-api/lib/Github/Exception/ApiLimitExceedException.php b/vendor/knplabs/github-api/lib/Github/Exception/ApiLimitExceedException.php
new file mode 100644
index 00000000..0283175f
--- /dev/null
+++ b/vendor/knplabs/github-api/lib/Github/Exception/ApiLimitExceedException.php
@@ -0,0 +1,32 @@
+
+ */
+class ApiLimitExceedException extends RuntimeException
+{
+ private $limit;
+ private $reset;
+
+ public function __construct($limit = 5000, $reset = 1800, $code = 0, $previous = null)
+ {
+ $this->limit = (int) $limit;
+ $this->reset = (int) $reset;
+
+ parent::__construct(sprintf('You have reached GitHub hourly limit! Actual limit is: %d', $limit), $code, $previous);
+ }
+
+ public function getLimit()
+ {
+ return $this->limit;
+ }
+
+ public function getResetTime()
+ {
+ return $this->reset;
+ }
+}
diff --git a/vendor/knplabs/github-api/lib/Github/Exception/BadMethodCallException.php b/vendor/knplabs/github-api/lib/Github/Exception/BadMethodCallException.php
new file mode 100644
index 00000000..83e05437
--- /dev/null
+++ b/vendor/knplabs/github-api/lib/Github/Exception/BadMethodCallException.php
@@ -0,0 +1,12 @@
+
+ */
+class BadMethodCallException extends \BadMethodCallException implements ExceptionInterface
+{
+}
diff --git a/vendor/knplabs/github-api/lib/Github/Exception/ErrorException.php b/vendor/knplabs/github-api/lib/Github/Exception/ErrorException.php
new file mode 100644
index 00000000..61f61f36
--- /dev/null
+++ b/vendor/knplabs/github-api/lib/Github/Exception/ErrorException.php
@@ -0,0 +1,12 @@
+
+ */
+class ErrorException extends \ErrorException implements ExceptionInterface
+{
+}
diff --git a/vendor/knplabs/github-api/lib/Github/Exception/ExceptionInterface.php b/vendor/knplabs/github-api/lib/Github/Exception/ExceptionInterface.php
new file mode 100644
index 00000000..87e6d2f7
--- /dev/null
+++ b/vendor/knplabs/github-api/lib/Github/Exception/ExceptionInterface.php
@@ -0,0 +1,9 @@
+
+ */
+class InvalidArgumentException extends \InvalidArgumentException implements ExceptionInterface
+{
+}
diff --git a/vendor/knplabs/github-api/lib/Github/Exception/MissingArgumentException.php b/vendor/knplabs/github-api/lib/Github/Exception/MissingArgumentException.php
new file mode 100644
index 00000000..96e217ac
--- /dev/null
+++ b/vendor/knplabs/github-api/lib/Github/Exception/MissingArgumentException.php
@@ -0,0 +1,20 @@
+
+ */
+class MissingArgumentException extends ErrorException
+{
+ public function __construct($required, $code = 0, $previous = null)
+ {
+ if (is_string($required)) {
+ $required = [$required];
+ }
+
+ parent::__construct(sprintf('One or more of required ("%s") parameters is missing!', implode('", "', $required)), $code, $previous);
+ }
+}
diff --git a/vendor/knplabs/github-api/lib/Github/Exception/RuntimeException.php b/vendor/knplabs/github-api/lib/Github/Exception/RuntimeException.php
new file mode 100644
index 00000000..676cb957
--- /dev/null
+++ b/vendor/knplabs/github-api/lib/Github/Exception/RuntimeException.php
@@ -0,0 +1,12 @@
+
+ */
+class RuntimeException extends \RuntimeException implements ExceptionInterface
+{
+}
diff --git a/vendor/knplabs/github-api/lib/Github/Exception/TwoFactorAuthenticationRequiredException.php b/vendor/knplabs/github-api/lib/Github/Exception/TwoFactorAuthenticationRequiredException.php
new file mode 100644
index 00000000..6f93fe40
--- /dev/null
+++ b/vendor/knplabs/github-api/lib/Github/Exception/TwoFactorAuthenticationRequiredException.php
@@ -0,0 +1,19 @@
+type = $type;
+ parent::__construct('Two factor authentication is enabled on this account', $code, $previous);
+ }
+
+ public function getType()
+ {
+ return $this->type;
+ }
+}
diff --git a/vendor/knplabs/github-api/lib/Github/Exception/ValidationFailedException.php b/vendor/knplabs/github-api/lib/Github/Exception/ValidationFailedException.php
new file mode 100644
index 00000000..c689e2d6
--- /dev/null
+++ b/vendor/knplabs/github-api/lib/Github/Exception/ValidationFailedException.php
@@ -0,0 +1,12 @@
+
+ */
+class ValidationFailedException extends ErrorException
+{
+}
diff --git a/vendor/knplabs/github-api/lib/Github/HttpClient/Builder.php b/vendor/knplabs/github-api/lib/Github/HttpClient/Builder.php
new file mode 100644
index 00000000..6a635b2d
--- /dev/null
+++ b/vendor/knplabs/github-api/lib/Github/HttpClient/Builder.php
@@ -0,0 +1,200 @@
+
+ */
+class Builder
+{
+ /**
+ * The object that sends HTTP messages.
+ *
+ * @var HttpClient
+ */
+ private $httpClient;
+
+ /**
+ * A HTTP client with all our plugins.
+ *
+ * @var HttpMethodsClient
+ */
+ private $pluginClient;
+
+ /**
+ * @var MessageFactory
+ */
+ private $requestFactory;
+
+ /**
+ * @var StreamFactory
+ */
+ private $streamFactory;
+
+ /**
+ * True if we should create a new Plugin client at next request.
+ *
+ * @var bool
+ */
+ private $httpClientModified = true;
+
+ /**
+ * @var Plugin[]
+ */
+ private $plugins = [];
+
+ /**
+ * This plugin is special treated because it has to be the very last plugin.
+ *
+ * @var Plugin\CachePlugin
+ */
+ private $cachePlugin;
+
+ /**
+ * Http headers.
+ *
+ * @var array
+ */
+ private $headers = [];
+
+ /**
+ * @param HttpClient $httpClient
+ * @param RequestFactory $requestFactory
+ * @param StreamFactory $streamFactory
+ */
+ public function __construct(
+ HttpClient $httpClient = null,
+ RequestFactory $requestFactory = null,
+ StreamFactory $streamFactory = null
+ ) {
+ $this->httpClient = $httpClient ?: HttpClientDiscovery::find();
+ $this->requestFactory = $requestFactory ?: MessageFactoryDiscovery::find();
+ $this->streamFactory = $streamFactory ?: StreamFactoryDiscovery::find();
+ }
+
+ /**
+ * @return HttpMethodsClient
+ */
+ public function getHttpClient()
+ {
+ if ($this->httpClientModified) {
+ $this->httpClientModified = false;
+
+ $plugins = $this->plugins;
+ if ($this->cachePlugin) {
+ $plugins[] = $this->cachePlugin;
+ }
+
+ $this->pluginClient = new HttpMethodsClient(
+ (new PluginClientFactory())->createClient($this->httpClient, $plugins),
+ $this->requestFactory
+ );
+ }
+
+ return $this->pluginClient;
+ }
+
+ /**
+ * Add a new plugin to the end of the plugin chain.
+ *
+ * @param Plugin $plugin
+ */
+ public function addPlugin(Plugin $plugin)
+ {
+ $this->plugins[] = $plugin;
+ $this->httpClientModified = true;
+ }
+
+ /**
+ * Remove a plugin by its fully qualified class name (FQCN).
+ *
+ * @param string $fqcn
+ */
+ public function removePlugin($fqcn)
+ {
+ foreach ($this->plugins as $idx => $plugin) {
+ if ($plugin instanceof $fqcn) {
+ unset($this->plugins[$idx]);
+ $this->httpClientModified = true;
+ }
+ }
+ }
+
+ /**
+ * Clears used headers.
+ */
+ public function clearHeaders()
+ {
+ $this->headers = [];
+
+ $this->removePlugin(Plugin\HeaderAppendPlugin::class);
+ $this->addPlugin(new Plugin\HeaderAppendPlugin($this->headers));
+ }
+
+ /**
+ * @param array $headers
+ */
+ public function addHeaders(array $headers)
+ {
+ $this->headers = array_merge($this->headers, $headers);
+
+ $this->removePlugin(Plugin\HeaderAppendPlugin::class);
+ $this->addPlugin(new Plugin\HeaderAppendPlugin($this->headers));
+ }
+
+ /**
+ * @param string $header
+ * @param string $headerValue
+ */
+ public function addHeaderValue($header, $headerValue)
+ {
+ if (!isset($this->headers[$header])) {
+ $this->headers[$header] = $headerValue;
+ } else {
+ $this->headers[$header] = array_merge((array) $this->headers[$header], [$headerValue]);
+ }
+
+ $this->removePlugin(Plugin\HeaderAppendPlugin::class);
+ $this->addPlugin(new Plugin\HeaderAppendPlugin($this->headers));
+ }
+
+ /**
+ * Add a cache plugin to cache responses locally.
+ *
+ * @param CacheItemPoolInterface $cachePool
+ * @param array $config
+ */
+ public function addCache(CacheItemPoolInterface $cachePool, array $config = [])
+ {
+ if (!isset($config['cache_key_generator'])) {
+ $config['cache_key_generator'] = new HeaderCacheKeyGenerator(['Authorization', 'Cookie', 'Accept', 'Content-type']);
+ }
+ $this->cachePlugin = Plugin\CachePlugin::clientCache($cachePool, $this->streamFactory, $config);
+ $this->httpClientModified = true;
+ }
+
+ /**
+ * Remove the cache plugin.
+ */
+ public function removeCache()
+ {
+ $this->cachePlugin = null;
+ $this->httpClientModified = true;
+ }
+}
diff --git a/vendor/knplabs/github-api/lib/Github/HttpClient/Message/ResponseMediator.php b/vendor/knplabs/github-api/lib/Github/HttpClient/Message/ResponseMediator.php
new file mode 100644
index 00000000..6b944165
--- /dev/null
+++ b/vendor/knplabs/github-api/lib/Github/HttpClient/Message/ResponseMediator.php
@@ -0,0 +1,82 @@
+getBody()->__toString();
+ if (strpos($response->getHeaderLine('Content-Type'), 'application/json') === 0) {
+ $content = json_decode($body, true);
+ if (JSON_ERROR_NONE === json_last_error()) {
+ return $content;
+ }
+ }
+
+ return $body;
+ }
+
+ /**
+ * @param ResponseInterface $response
+ *
+ * @return array|null
+ */
+ public static function getPagination(ResponseInterface $response)
+ {
+ if (!$response->hasHeader('Link')) {
+ return;
+ }
+
+ $header = self::getHeader($response, 'Link');
+ $pagination = [];
+ foreach (explode(',', $header) as $link) {
+ preg_match('/<(.*)>; rel="(.*)"/i', trim($link, ','), $match);
+
+ if (3 === count($match)) {
+ $pagination[$match[2]] = $match[1];
+ }
+ }
+
+ return $pagination;
+ }
+
+ /**
+ * @param ResponseInterface $response
+ *
+ * @return null|string
+ */
+ public static function getApiLimit(ResponseInterface $response)
+ {
+ $remainingCalls = self::getHeader($response, 'X-RateLimit-Remaining');
+
+ if (null !== $remainingCalls && 1 > $remainingCalls) {
+ throw new ApiLimitExceedException($remainingCalls);
+ }
+
+ return $remainingCalls;
+ }
+
+ /**
+ * Get the value for a single header.
+ *
+ * @param ResponseInterface $response
+ * @param string $name
+ *
+ * @return string|null
+ */
+ public static function getHeader(ResponseInterface $response, $name)
+ {
+ $headers = $response->getHeader($name);
+
+ return array_shift($headers);
+ }
+}
diff --git a/vendor/knplabs/github-api/lib/Github/HttpClient/Plugin/Authentication.php b/vendor/knplabs/github-api/lib/Github/HttpClient/Plugin/Authentication.php
new file mode 100644
index 00000000..920d6572
--- /dev/null
+++ b/vendor/knplabs/github-api/lib/Github/HttpClient/Plugin/Authentication.php
@@ -0,0 +1,85 @@
+
+ */
+class Authentication implements Plugin
+{
+ private $tokenOrLogin;
+ private $password;
+ private $method;
+
+ public function __construct($tokenOrLogin, $password, $method)
+ {
+ $this->tokenOrLogin = $tokenOrLogin;
+ $this->password = $password;
+ $this->method = $method;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function handleRequest(RequestInterface $request, callable $next, callable $first)
+ {
+ switch ($this->method) {
+ case Client::AUTH_HTTP_PASSWORD:
+ $request = $request->withHeader(
+ 'Authorization',
+ sprintf('Basic %s', base64_encode($this->tokenOrLogin.':'.$this->password))
+ );
+ break;
+
+ case Client::AUTH_HTTP_TOKEN:
+ $request = $request->withHeader('Authorization', sprintf('token %s', $this->tokenOrLogin));
+ break;
+
+ case Client::AUTH_URL_CLIENT_ID:
+ $uri = $request->getUri();
+ $query = $uri->getQuery();
+
+ $parameters = [
+ 'client_id' => $this->tokenOrLogin,
+ 'client_secret' => $this->password,
+ ];
+
+ $query .= empty($query) ? '' : '&';
+ $query .= utf8_encode(http_build_query($parameters, '', '&'));
+
+ $uri = $uri->withQuery($query);
+ $request = $request->withUri($uri);
+ break;
+
+ case Client::AUTH_URL_TOKEN:
+ $uri = $request->getUri();
+ $query = $uri->getQuery();
+
+ $parameters = ['access_token' => $this->tokenOrLogin];
+
+ $query .= empty($query) ? '' : '&';
+ $query .= utf8_encode(http_build_query($parameters, '', '&'));
+
+ $uri = $uri->withQuery($query);
+ $request = $request->withUri($uri);
+ break;
+
+ case Client::AUTH_JWT:
+ $request = $request->withHeader('Authorization', sprintf('Bearer %s', $this->tokenOrLogin));
+ break;
+
+ default:
+ throw new RuntimeException(sprintf('%s not yet implemented', $this->method));
+ break;
+ }
+
+ return $next($request);
+ }
+}
diff --git a/vendor/knplabs/github-api/lib/Github/HttpClient/Plugin/GithubExceptionThrower.php b/vendor/knplabs/github-api/lib/Github/HttpClient/Plugin/GithubExceptionThrower.php
new file mode 100644
index 00000000..7d46fe9c
--- /dev/null
+++ b/vendor/knplabs/github-api/lib/Github/HttpClient/Plugin/GithubExceptionThrower.php
@@ -0,0 +1,90 @@
+
+ * @author Tobias Nyholm
+ */
+class GithubExceptionThrower implements Plugin
+{
+ /**
+ * {@inheritdoc}
+ */
+ public function handleRequest(RequestInterface $request, callable $next, callable $first)
+ {
+ return $next($request)->then(function (ResponseInterface $response) use ($request) {
+ if ($response->getStatusCode() < 400 || $response->getStatusCode() > 600) {
+ return $response;
+ }
+
+ // If error:
+ $remaining = ResponseMediator::getHeader($response, 'X-RateLimit-Remaining');
+ if (null != $remaining && 1 > $remaining && 'rate_limit' !== substr($request->getRequestTarget(), 1, 10)) {
+ $limit = ResponseMediator::getHeader($response, 'X-RateLimit-Limit');
+ $reset = ResponseMediator::getHeader($response, 'X-RateLimit-Reset');
+
+ throw new ApiLimitExceedException($limit, $reset);
+ }
+
+ if (401 === $response->getStatusCode()) {
+ if ($response->hasHeader('X-GitHub-OTP') && 0 === strpos((string) ResponseMediator::getHeader($response, 'X-GitHub-OTP'), 'required;')) {
+ $type = substr((string) ResponseMediator::getHeader($response, 'X-GitHub-OTP'), 9);
+
+ throw new TwoFactorAuthenticationRequiredException($type);
+ }
+ }
+
+ $content = ResponseMediator::getContent($response);
+ if (is_array($content) && isset($content['message'])) {
+ if (400 == $response->getStatusCode()) {
+ throw new ErrorException($content['message'], 400);
+ } elseif (422 == $response->getStatusCode() && isset($content['errors'])) {
+ $errors = [];
+ foreach ($content['errors'] as $error) {
+ switch ($error['code']) {
+ case 'missing':
+ $errors[] = sprintf('The %s %s does not exist, for resource "%s"', $error['field'], $error['value'], $error['resource']);
+ break;
+
+ case 'missing_field':
+ $errors[] = sprintf('Field "%s" is missing, for resource "%s"', $error['field'], $error['resource']);
+ break;
+
+ case 'invalid':
+ if (isset($error['message'])) {
+ $errors[] = sprintf('Field "%s" is invalid, for resource "%s": "%s"', $error['field'], $error['resource'], $error['message']);
+ } else {
+ $errors[] = sprintf('Field "%s" is invalid, for resource "%s"', $error['field'], $error['resource']);
+ }
+ break;
+
+ case 'already_exists':
+ $errors[] = sprintf('Field "%s" already exists, for resource "%s"', $error['field'], $error['resource']);
+ break;
+
+ default:
+ $errors[] = $error['message'];
+ break;
+
+ }
+ }
+
+ throw new ValidationFailedException('Validation Failed: '.implode(', ', $errors), 422);
+ }
+ }
+
+ throw new RuntimeException(isset($content['message']) ? $content['message'] : $content, $response->getStatusCode());
+ });
+ }
+}
diff --git a/vendor/knplabs/github-api/lib/Github/HttpClient/Plugin/History.php b/vendor/knplabs/github-api/lib/Github/HttpClient/Plugin/History.php
new file mode 100644
index 00000000..303d8140
--- /dev/null
+++ b/vendor/knplabs/github-api/lib/Github/HttpClient/Plugin/History.php
@@ -0,0 +1,38 @@
+
+ */
+class History implements Journal
+{
+ /**
+ * @var ResponseInterface
+ */
+ private $lastResponse;
+
+ /**
+ * @return ResponseInterface|null
+ */
+ public function getLastResponse()
+ {
+ return $this->lastResponse;
+ }
+
+ public function addSuccess(RequestInterface $request, ResponseInterface $response)
+ {
+ $this->lastResponse = $response;
+ }
+
+ public function addFailure(RequestInterface $request, Exception $exception)
+ {
+ }
+}
diff --git a/vendor/knplabs/github-api/lib/Github/HttpClient/Plugin/PathPrepend.php b/vendor/knplabs/github-api/lib/Github/HttpClient/Plugin/PathPrepend.php
new file mode 100644
index 00000000..2c91bf74
--- /dev/null
+++ b/vendor/knplabs/github-api/lib/Github/HttpClient/Plugin/PathPrepend.php
@@ -0,0 +1,38 @@
+
+ */
+class PathPrepend implements Plugin
+{
+ private $path;
+
+ /**
+ * @param string $path
+ */
+ public function __construct($path)
+ {
+ $this->path = $path;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function handleRequest(RequestInterface $request, callable $next, callable $first)
+ {
+ $currentPath = $request->getUri()->getPath();
+ if (strpos($currentPath, $this->path) !== 0) {
+ $uri = $request->getUri()->withPath($this->path.$currentPath);
+ $request = $request->withUri($uri);
+ }
+
+ return $next($request);
+ }
+}
diff --git a/vendor/knplabs/github-api/lib/Github/ResultPager.php b/vendor/knplabs/github-api/lib/Github/ResultPager.php
new file mode 100644
index 00000000..24f2a32f
--- /dev/null
+++ b/vendor/knplabs/github-api/lib/Github/ResultPager.php
@@ -0,0 +1,194 @@
+
+ * @author Mitchel Verschoof
+ */
+class ResultPager implements ResultPagerInterface
+{
+ /**
+ * The GitHub Client to use for pagination.
+ *
+ * @var \Github\Client
+ */
+ protected $client;
+
+ /**
+ * Comes from pagination headers in Github API results.
+ *
+ * @var array
+ */
+ protected $pagination;
+
+ /**
+ * The Github client to use for pagination.
+ *
+ * This must be the same instance that you got the Api instance from.
+ *
+ * Example code:
+ *
+ * $client = new \Github\Client();
+ * $api = $client->api('someApi');
+ * $pager = new \Github\ResultPager($client);
+ *
+ * @param \Github\Client $client
+ */
+ public function __construct(Client $client)
+ {
+ $this->client = $client;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function getPagination()
+ {
+ return $this->pagination;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function fetch(ApiInterface $api, $method, array $parameters = [])
+ {
+ $result = $this->callApi($api, $method, $parameters);
+ $this->postFetch();
+
+ return $result;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function fetchAll(ApiInterface $api, $method, array $parameters = [])
+ {
+ $isSearch = $api instanceof Search;
+
+ // get the perPage from the api
+ $perPage = $api->getPerPage();
+
+ // set parameters per_page to GitHub max to minimize number of requests
+ $api->setPerPage(100);
+
+ try {
+ $result = $this->callApi($api, $method, $parameters);
+ $this->postFetch();
+
+ if ($isSearch) {
+ $result = isset($result['items']) ? $result['items'] : $result;
+ }
+
+ while ($this->hasNext()) {
+ $next = $this->fetchNext();
+
+ if ($isSearch) {
+ $result = array_merge($result, $next['items']);
+ } else {
+ $result = array_merge($result, $next);
+ }
+ }
+ } finally {
+ // restore the perPage
+ $api->setPerPage($perPage);
+ }
+
+ return $result;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function postFetch()
+ {
+ $this->pagination = ResponseMediator::getPagination($this->client->getLastResponse());
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function hasNext()
+ {
+ return $this->has('next');
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function fetchNext()
+ {
+ return $this->get('next');
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function hasPrevious()
+ {
+ return $this->has('prev');
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function fetchPrevious()
+ {
+ return $this->get('prev');
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function fetchFirst()
+ {
+ return $this->get('first');
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function fetchLast()
+ {
+ return $this->get('last');
+ }
+
+ /**
+ * @param string $key
+ */
+ protected function has($key)
+ {
+ return !empty($this->pagination) && isset($this->pagination[$key]);
+ }
+
+ /**
+ * @param string $key
+ */
+ protected function get($key)
+ {
+ if ($this->has($key)) {
+ $result = $this->client->getHttpClient()->get($this->pagination[$key]);
+ $this->postFetch();
+
+ return ResponseMediator::getContent($result);
+ }
+ }
+
+ /**
+ * @param ApiInterface $api
+ * @param string $method
+ * @param array $parameters
+ *
+ * @return mixed
+ */
+ protected function callApi(ApiInterface $api, $method, array $parameters)
+ {
+ return call_user_func_array([$api, $method], $parameters);
+ }
+}
diff --git a/vendor/knplabs/github-api/lib/Github/ResultPagerInterface.php b/vendor/knplabs/github-api/lib/Github/ResultPagerInterface.php
new file mode 100644
index 00000000..c5add903
--- /dev/null
+++ b/vendor/knplabs/github-api/lib/Github/ResultPagerInterface.php
@@ -0,0 +1,90 @@
+
+ * @author Mitchel Verschoof
+ */
+interface ResultPagerInterface
+{
+ /**
+ * @return null|array pagination result of last request
+ */
+ public function getPagination();
+
+ /**
+ * Fetch a single result (page) from an api call.
+ *
+ * @param ApiInterface $api the Api instance
+ * @param string $method the method name to call on the Api instance
+ * @param array $parameters the method parameters in an array
+ *
+ * @return array returns the result of the Api::$method() call
+ */
+ public function fetch(ApiInterface $api, $method, array $parameters = []);
+
+ /**
+ * Fetch all results (pages) from an api call.
+ *
+ * Use with care - there is no maximum.
+ *
+ * @param ApiInterface $api the Api instance
+ * @param string $method the method name to call on the Api instance
+ * @param array $parameters the method parameters in an array
+ *
+ * @return array returns a merge of the results of the Api::$method() call
+ */
+ public function fetchAll(ApiInterface $api, $method, array $parameters = []);
+
+ /**
+ * Method that performs the actual work to refresh the pagination property.
+ */
+ public function postFetch();
+
+ /**
+ * Check to determine the availability of a next page.
+ *
+ * @return bool
+ */
+ public function hasNext();
+
+ /**
+ * Check to determine the availability of a previous page.
+ *
+ * @return bool
+ */
+ public function hasPrevious();
+
+ /**
+ * Fetch the next page.
+ *
+ * @return array
+ */
+ public function fetchNext();
+
+ /**
+ * Fetch the previous page.
+ *
+ * @return array
+ */
+ public function fetchPrevious();
+
+ /**
+ * Fetch the first page.
+ *
+ * @return array
+ */
+ public function fetchFirst();
+
+ /**
+ * Fetch the last page.
+ *
+ * @return array
+ */
+ public function fetchLast();
+}
diff --git a/vendor/php-http/cache-plugin/CHANGELOG.md b/vendor/php-http/cache-plugin/CHANGELOG.md
new file mode 100644
index 00000000..795a3ff7
--- /dev/null
+++ b/vendor/php-http/cache-plugin/CHANGELOG.md
@@ -0,0 +1,74 @@
+# Change Log
+
+## 1.5.0 - 2017-11-29
+
+### Added
+
+* Support for Symfony 4
+
+### Changed
+
+* Removed check if etag is a string. Etag can never be a string, it is always an array.
+
+## 1.4.0 - 2017-04-05
+
+### Added
+
+- `CacheKeyGenerator` interface that allow you to configure how the PSR-6 cache key is created. There are two implementations
+of this interface: `SimpleGenerator` (default) and `HeaderCacheKeyGenerator`.
+
+### Fixed
+
+- Issue where deprecation warning always was triggered. Not it is just triggered if `respect_cache_headers` is used.
+
+## 1.3.0 - 2017-03-28
+
+### Added
+
+- New `methods` option which allows to configure the request methods which can be cached.
+- New `respect_response_cache_directives` option to define specific cache directives to respect when handling responses.
+- Introduced `CachePlugin::clientCache` and `CachePlugin::serverCache` factory methods to easily setup the plugin with
+ the correct config settigns for each usecase.
+
+### Changed
+
+- The `no-cache` directive is now respected by the plugin and will not cache the response. If you need the previous behaviour, configure `respect_response_cache_directives`.
+- We always rewind the stream after loading response from cache.
+
+### Deprecated
+
+- The `respect_cache_headers` option is deprecated and will be removed in 2.0. This option is replaced by the new `respect_response_cache_directives` option.
+ If you had set `respect_cache_headers` to `false`, set the directives to `[]` to ignore all directives.
+
+
+## 1.2.0 - 2016-08-16
+
+### Changed
+
+- The default value for `default_ttl` is changed from `null` to `0`.
+
+### Fixed
+
+- Issue when you use `respect_cache_headers=>false` in combination with `default_ttl=>null`.
+- We allow `cache_lifetime` to be set to `null`.
+
+
+## 1.1.0 - 2016-08-04
+
+### Added
+
+- Support for cache validation with ETag and Last-Modified headers. (Enabled automatically when the server sends the relevant headers.)
+- `hash_algo` config option used for cache key generation (defaults to **sha1**).
+
+### Changed
+
+- Default hash algo used for cache generation (from **md5** to **sha1**).
+
+### Fixed
+
+- Cast max age header to integer in order to get valid expiration value.
+
+
+## 1.0.0 - 2016-05-05
+
+- Initial release
diff --git a/vendor/php-http/cache-plugin/LICENSE b/vendor/php-http/cache-plugin/LICENSE
new file mode 100644
index 00000000..4558d6f0
--- /dev/null
+++ b/vendor/php-http/cache-plugin/LICENSE
@@ -0,0 +1,19 @@
+Copyright (c) 2015-2016 PHP HTTP Team
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is furnished
+to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+THE SOFTWARE.
diff --git a/vendor/php-http/cache-plugin/README.md b/vendor/php-http/cache-plugin/README.md
new file mode 100644
index 00000000..ce07b090
--- /dev/null
+++ b/vendor/php-http/cache-plugin/README.md
@@ -0,0 +1,46 @@
+# Cache Plugin
+
+[![Latest Version](https://img.shields.io/github/release/php-http/cache-plugin.svg?style=flat-square)](https://github.com/php-http/cache-plugin/releases)
+[![Software License](https://img.shields.io/badge/license-MIT-brightgreen.svg?style=flat-square)](LICENSE)
+[![Build Status](https://img.shields.io/travis/php-http/cache-plugin.svg?style=flat-square)](https://travis-ci.org/php-http/cache-plugin)
+[![Code Coverage](https://img.shields.io/scrutinizer/coverage/g/php-http/cache-plugin.svg?style=flat-square)](https://scrutinizer-ci.com/g/php-http/cache-plugin)
+[![Quality Score](https://img.shields.io/scrutinizer/g/php-http/cache-plugin.svg?style=flat-square)](https://scrutinizer-ci.com/g/php-http/cache-plugin)
+[![Total Downloads](https://img.shields.io/packagist/dt/php-http/cache-plugin.svg?style=flat-square)](https://packagist.org/packages/php-http/cache-plugin)
+
+**PSR-6 Cache plugin for HTTPlug.**
+
+
+## Install
+
+Via Composer
+
+``` bash
+$ composer require php-http/cache-plugin
+```
+
+
+## Documentation
+
+Please see the [official documentation](http://docs.php-http.org/en/latest/plugins/cache.html).
+
+
+## Testing
+
+``` bash
+$ composer test
+```
+
+
+## Contributing
+
+Please see our [contributing guide](http://docs.php-http.org/en/latest/development/contributing.html).
+
+
+## Security
+
+If you discover any security related issues, please contact us at [security@php-http.org](mailto:security@php-http.org).
+
+
+## License
+
+The MIT License (MIT). Please see [License File](LICENSE) for more information.
diff --git a/vendor/php-http/cache-plugin/composer.json b/vendor/php-http/cache-plugin/composer.json
new file mode 100644
index 00000000..3ce18bee
--- /dev/null
+++ b/vendor/php-http/cache-plugin/composer.json
@@ -0,0 +1,43 @@
+{
+ "name": "php-http/cache-plugin",
+ "description": "PSR-6 Cache plugin for HTTPlug",
+ "license": "MIT",
+ "keywords": ["cache", "http", "httplug", "plugin"],
+ "homepage": "http://httplug.io",
+ "authors": [
+ {
+ "name": "Márk Sági-Kazár",
+ "email": "mark.sagikazar@gmail.com"
+ }
+ ],
+ "require": {
+ "php": "^5.4 || ^7.0",
+ "psr/cache": "^1.0",
+ "php-http/client-common": "^1.1",
+ "php-http/message-factory": "^1.0",
+ "symfony/options-resolver": "^2.6 || ^3.0 || ^4.0"
+ },
+ "require-dev": {
+ "phpspec/phpspec": "^2.5",
+ "henrikbjorn/phpspec-code-coverage" : "^1.0"
+ },
+ "autoload": {
+ "psr-4": {
+ "Http\\Client\\Common\\Plugin\\": "src/"
+ }
+ },
+ "autoload-dev": {
+ "psr-4": {
+ "spec\\Http\\Client\\Common\\Plugin\\": "spec/"
+ }
+ },
+ "scripts": {
+ "test": "vendor/bin/phpspec run",
+ "test-ci": "vendor/bin/phpspec run -c phpspec.ci.yml"
+ },
+ "extra": {
+ "branch-alias": {
+ "dev-master": "1.5-dev"
+ }
+ }
+}
diff --git a/vendor/php-http/cache-plugin/src/Cache/Generator/CacheKeyGenerator.php b/vendor/php-http/cache-plugin/src/Cache/Generator/CacheKeyGenerator.php
new file mode 100644
index 00000000..d351e57c
--- /dev/null
+++ b/vendor/php-http/cache-plugin/src/Cache/Generator/CacheKeyGenerator.php
@@ -0,0 +1,22 @@
+
+ */
+interface CacheKeyGenerator
+{
+ /**
+ * Generate a cache key from a Request.
+ *
+ * @param RequestInterface $request
+ *
+ * @return string
+ */
+ public function generate(RequestInterface $request);
+}
diff --git a/vendor/php-http/cache-plugin/src/Cache/Generator/HeaderCacheKeyGenerator.php b/vendor/php-http/cache-plugin/src/Cache/Generator/HeaderCacheKeyGenerator.php
new file mode 100644
index 00000000..562ed484
--- /dev/null
+++ b/vendor/php-http/cache-plugin/src/Cache/Generator/HeaderCacheKeyGenerator.php
@@ -0,0 +1,38 @@
+
+ */
+class HeaderCacheKeyGenerator implements CacheKeyGenerator
+{
+ /**
+ * The header names we should take into account when creating the cache key.
+ *
+ * @var array
+ */
+ private $headerNames;
+
+ /**
+ * @param $headerNames
+ */
+ public function __construct(array $headerNames)
+ {
+ $this->headerNames = $headerNames;
+ }
+
+ public function generate(RequestInterface $request)
+ {
+ $concatenatedHeaders = [];
+ foreach ($this->headerNames as $headerName) {
+ $concatenatedHeaders[] = sprintf(' %s:"%s"', $headerName, $request->getHeaderLine($headerName));
+ }
+
+ return $request->getMethod().' '.$request->getUri().implode('', $concatenatedHeaders).' '.$request->getBody();
+ }
+}
diff --git a/vendor/php-http/cache-plugin/src/Cache/Generator/SimpleGenerator.php b/vendor/php-http/cache-plugin/src/Cache/Generator/SimpleGenerator.php
new file mode 100644
index 00000000..4f0ee903
--- /dev/null
+++ b/vendor/php-http/cache-plugin/src/Cache/Generator/SimpleGenerator.php
@@ -0,0 +1,23 @@
+
+ */
+class SimpleGenerator implements CacheKeyGenerator
+{
+ public function generate(RequestInterface $request)
+ {
+ $body = (string) $request->getBody();
+ if (!empty($body)) {
+ $body = ' '.$body;
+ }
+
+ return $request->getMethod().' '.$request->getUri().$body;
+ }
+}
diff --git a/vendor/php-http/cache-plugin/src/CachePlugin.php b/vendor/php-http/cache-plugin/src/CachePlugin.php
new file mode 100644
index 00000000..166cf9be
--- /dev/null
+++ b/vendor/php-http/cache-plugin/src/CachePlugin.php
@@ -0,0 +1,444 @@
+
+ */
+final class CachePlugin implements Plugin
+{
+ /**
+ * @var CacheItemPoolInterface
+ */
+ private $pool;
+
+ /**
+ * @var StreamFactory
+ */
+ private $streamFactory;
+
+ /**
+ * @var array
+ */
+ private $config;
+
+ /**
+ * Cache directives indicating if a response can not be cached.
+ *
+ * @var array
+ */
+ private $noCacheFlags = ['no-cache', 'private', 'no-store'];
+
+ /**
+ * @param CacheItemPoolInterface $pool
+ * @param StreamFactory $streamFactory
+ * @param array $config {
+ *
+ * @var bool $respect_cache_headers Whether to look at the cache directives or ignore them
+ * @var int $default_ttl (seconds) If we do not respect cache headers or can't calculate a good ttl, use this
+ * value
+ * @var string $hash_algo The hashing algorithm to use when generating cache keys
+ * @var int $cache_lifetime (seconds) To support serving a previous stale response when the server answers 304
+ * we have to store the cache for a longer time than the server originally says it is valid for.
+ * We store a cache item for $cache_lifetime + max age of the response.
+ * @var array $methods list of request methods which can be cached
+ * @var array $respect_response_cache_directives list of cache directives this plugin will respect while caching responses
+ * @var CacheKeyGenerator $cache_key_generator an object to generate the cache key. Defaults to a new instance of SimpleGenerator
+ * }
+ */
+ public function __construct(CacheItemPoolInterface $pool, StreamFactory $streamFactory, array $config = [])
+ {
+ $this->pool = $pool;
+ $this->streamFactory = $streamFactory;
+
+ if (isset($config['respect_cache_headers']) && isset($config['respect_response_cache_directives'])) {
+ throw new \InvalidArgumentException(
+ 'You can\'t provide config option "respect_cache_headers" and "respect_response_cache_directives". '.
+ 'Use "respect_response_cache_directives" instead.'
+ );
+ }
+
+ $optionsResolver = new OptionsResolver();
+ $this->configureOptions($optionsResolver);
+ $this->config = $optionsResolver->resolve($config);
+
+ if (null === $this->config['cache_key_generator']) {
+ $this->config['cache_key_generator'] = new SimpleGenerator();
+ }
+ }
+
+ /**
+ * This method will setup the cachePlugin in client cache mode. When using the client cache mode the plugin will
+ * cache responses with `private` cache directive.
+ *
+ * @param CacheItemPoolInterface $pool
+ * @param StreamFactory $streamFactory
+ * @param array $config For all possible config options see the constructor docs
+ *
+ * @return CachePlugin
+ */
+ public static function clientCache(CacheItemPoolInterface $pool, StreamFactory $streamFactory, array $config = [])
+ {
+ // Allow caching of private requests
+ if (isset($config['respect_response_cache_directives'])) {
+ $config['respect_response_cache_directives'][] = 'no-cache';
+ $config['respect_response_cache_directives'][] = 'max-age';
+ $config['respect_response_cache_directives'] = array_unique($config['respect_response_cache_directives']);
+ } else {
+ $config['respect_response_cache_directives'] = ['no-cache', 'max-age'];
+ }
+
+ return new self($pool, $streamFactory, $config);
+ }
+
+ /**
+ * This method will setup the cachePlugin in server cache mode. This is the default caching behavior it refuses to
+ * cache responses with the `private`or `no-cache` directives.
+ *
+ * @param CacheItemPoolInterface $pool
+ * @param StreamFactory $streamFactory
+ * @param array $config For all possible config options see the constructor docs
+ *
+ * @return CachePlugin
+ */
+ public static function serverCache(CacheItemPoolInterface $pool, StreamFactory $streamFactory, array $config = [])
+ {
+ return new self($pool, $streamFactory, $config);
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function handleRequest(RequestInterface $request, callable $next, callable $first)
+ {
+ $method = strtoupper($request->getMethod());
+ // if the request not is cachable, move to $next
+ if (!in_array($method, $this->config['methods'])) {
+ return $next($request);
+ }
+
+ // If we can cache the request
+ $key = $this->createCacheKey($request);
+ $cacheItem = $this->pool->getItem($key);
+
+ if ($cacheItem->isHit()) {
+ $data = $cacheItem->get();
+ // The array_key_exists() is to be removed in 2.0.
+ if (array_key_exists('expiresAt', $data) && (null === $data['expiresAt'] || time() < $data['expiresAt'])) {
+ // This item is still valid according to previous cache headers
+ return new FulfilledPromise($this->createResponseFromCacheItem($cacheItem));
+ }
+
+ // Add headers to ask the server if this cache is still valid
+ if ($modifiedSinceValue = $this->getModifiedSinceHeaderValue($cacheItem)) {
+ $request = $request->withHeader('If-Modified-Since', $modifiedSinceValue);
+ }
+
+ if ($etag = $this->getETag($cacheItem)) {
+ $request = $request->withHeader('If-None-Match', $etag);
+ }
+ }
+
+ return $next($request)->then(function (ResponseInterface $response) use ($cacheItem) {
+ if (304 === $response->getStatusCode()) {
+ if (!$cacheItem->isHit()) {
+ /*
+ * We do not have the item in cache. This plugin did not add If-Modified-Since
+ * or If-None-Match headers. Return the response from server.
+ */
+ return $response;
+ }
+
+ // The cached response we have is still valid
+ $data = $cacheItem->get();
+ $maxAge = $this->getMaxAge($response);
+ $data['expiresAt'] = $this->calculateResponseExpiresAt($maxAge);
+ $cacheItem->set($data)->expiresAfter($this->calculateCacheItemExpiresAfter($maxAge));
+ $this->pool->save($cacheItem);
+
+ return $this->createResponseFromCacheItem($cacheItem);
+ }
+
+ if ($this->isCacheable($response)) {
+ $bodyStream = $response->getBody();
+ $body = $bodyStream->__toString();
+ if ($bodyStream->isSeekable()) {
+ $bodyStream->rewind();
+ } else {
+ $response = $response->withBody($this->streamFactory->createStream($body));
+ }
+
+ $maxAge = $this->getMaxAge($response);
+ $cacheItem
+ ->expiresAfter($this->calculateCacheItemExpiresAfter($maxAge))
+ ->set([
+ 'response' => $response,
+ 'body' => $body,
+ 'expiresAt' => $this->calculateResponseExpiresAt($maxAge),
+ 'createdAt' => time(),
+ 'etag' => $response->getHeader('ETag'),
+ ]);
+ $this->pool->save($cacheItem);
+ }
+
+ return $response;
+ });
+ }
+
+ /**
+ * Calculate the timestamp when this cache item should be dropped from the cache. The lowest value that can be
+ * returned is $maxAge.
+ *
+ * @param int|null $maxAge
+ *
+ * @return int|null Unix system time passed to the PSR-6 cache
+ */
+ private function calculateCacheItemExpiresAfter($maxAge)
+ {
+ if (null === $this->config['cache_lifetime'] && null === $maxAge) {
+ return;
+ }
+
+ return $this->config['cache_lifetime'] + $maxAge;
+ }
+
+ /**
+ * Calculate the timestamp when a response expires. After that timestamp, we need to send a
+ * If-Modified-Since / If-None-Match request to validate the response.
+ *
+ * @param int|null $maxAge
+ *
+ * @return int|null Unix system time. A null value means that the response expires when the cache item expires
+ */
+ private function calculateResponseExpiresAt($maxAge)
+ {
+ if (null === $maxAge) {
+ return;
+ }
+
+ return time() + $maxAge;
+ }
+
+ /**
+ * Verify that we can cache this response.
+ *
+ * @param ResponseInterface $response
+ *
+ * @return bool
+ */
+ protected function isCacheable(ResponseInterface $response)
+ {
+ if (!in_array($response->getStatusCode(), [200, 203, 300, 301, 302, 404, 410])) {
+ return false;
+ }
+
+ $nocacheDirectives = array_intersect($this->config['respect_response_cache_directives'], $this->noCacheFlags);
+ foreach ($nocacheDirectives as $nocacheDirective) {
+ if ($this->getCacheControlDirective($response, $nocacheDirective)) {
+ return false;
+ }
+ }
+
+ return true;
+ }
+
+ /**
+ * Get the value of a parameter in the cache control header.
+ *
+ * @param ResponseInterface $response
+ * @param string $name The field of Cache-Control to fetch
+ *
+ * @return bool|string The value of the directive, true if directive without value, false if directive not present
+ */
+ private function getCacheControlDirective(ResponseInterface $response, $name)
+ {
+ $headers = $response->getHeader('Cache-Control');
+ foreach ($headers as $header) {
+ if (preg_match(sprintf('|%s=?([0-9]+)?|i', $name), $header, $matches)) {
+ // return the value for $name if it exists
+ if (isset($matches[1])) {
+ return $matches[1];
+ }
+
+ return true;
+ }
+ }
+
+ return false;
+ }
+
+ /**
+ * @param RequestInterface $request
+ *
+ * @return string
+ */
+ private function createCacheKey(RequestInterface $request)
+ {
+ $key = $this->config['cache_key_generator']->generate($request);
+
+ return hash($this->config['hash_algo'], $key);
+ }
+
+ /**
+ * Get a ttl in seconds. It could return null if we do not respect cache headers and got no defaultTtl.
+ *
+ * @param ResponseInterface $response
+ *
+ * @return int|null
+ */
+ private function getMaxAge(ResponseInterface $response)
+ {
+ if (!in_array('max-age', $this->config['respect_response_cache_directives'], true)) {
+ return $this->config['default_ttl'];
+ }
+
+ // check for max age in the Cache-Control header
+ $maxAge = $this->getCacheControlDirective($response, 'max-age');
+ if (!is_bool($maxAge)) {
+ $ageHeaders = $response->getHeader('Age');
+ foreach ($ageHeaders as $age) {
+ return $maxAge - ((int) $age);
+ }
+
+ return (int) $maxAge;
+ }
+
+ // check for ttl in the Expires header
+ $headers = $response->getHeader('Expires');
+ foreach ($headers as $header) {
+ return (new \DateTime($header))->getTimestamp() - (new \DateTime())->getTimestamp();
+ }
+
+ return $this->config['default_ttl'];
+ }
+
+ /**
+ * Configure an options resolver.
+ *
+ * @param OptionsResolver $resolver
+ */
+ private function configureOptions(OptionsResolver $resolver)
+ {
+ $resolver->setDefaults([
+ 'cache_lifetime' => 86400 * 30, // 30 days
+ 'default_ttl' => 0,
+ //Deprecated as of v1.3, to be removed in v2.0. Use respect_response_cache_directives instead
+ 'respect_cache_headers' => null,
+ 'hash_algo' => 'sha1',
+ 'methods' => ['GET', 'HEAD'],
+ 'respect_response_cache_directives' => ['no-cache', 'private', 'max-age', 'no-store'],
+ 'cache_key_generator' => null,
+ ]);
+
+ $resolver->setAllowedTypes('cache_lifetime', ['int', 'null']);
+ $resolver->setAllowedTypes('default_ttl', ['int', 'null']);
+ $resolver->setAllowedTypes('respect_cache_headers', ['bool', 'null']);
+ $resolver->setAllowedTypes('methods', 'array');
+ $resolver->setAllowedTypes('cache_key_generator', ['null', 'Http\Client\Common\Plugin\Cache\Generator\CacheKeyGenerator']);
+ $resolver->setAllowedValues('hash_algo', hash_algos());
+ $resolver->setAllowedValues('methods', function ($value) {
+ /* RFC7230 sections 3.1.1 and 3.2.6 except limited to uppercase characters. */
+ $matches = preg_grep('/[^A-Z0-9!#$%&\'*+\-.^_`|~]/', $value);
+
+ return empty($matches);
+ });
+
+ $resolver->setNormalizer('respect_cache_headers', function (Options $options, $value) {
+ if (null !== $value) {
+ @trigger_error('The option "respect_cache_headers" is deprecated since version 1.3 and will be removed in 2.0. Use "respect_response_cache_directives" instead.', E_USER_DEPRECATED);
+ }
+
+ return null === $value ? true : $value;
+ });
+
+ $resolver->setNormalizer('respect_response_cache_directives', function (Options $options, $value) {
+ if (false === $options['respect_cache_headers']) {
+ return [];
+ }
+
+ return $value;
+ });
+ }
+
+ /**
+ * @param CacheItemInterface $cacheItem
+ *
+ * @return ResponseInterface
+ */
+ private function createResponseFromCacheItem(CacheItemInterface $cacheItem)
+ {
+ $data = $cacheItem->get();
+
+ /** @var ResponseInterface $response */
+ $response = $data['response'];
+ $stream = $this->streamFactory->createStream($data['body']);
+
+ try {
+ $stream->rewind();
+ } catch (\Exception $e) {
+ throw new RewindStreamException('Cannot rewind stream.', 0, $e);
+ }
+
+ $response = $response->withBody($stream);
+
+ return $response;
+ }
+
+ /**
+ * Get the value of the "If-Modified-Since" header.
+ *
+ * @param CacheItemInterface $cacheItem
+ *
+ * @return string|null
+ */
+ private function getModifiedSinceHeaderValue(CacheItemInterface $cacheItem)
+ {
+ $data = $cacheItem->get();
+ // The isset() is to be removed in 2.0.
+ if (!isset($data['createdAt'])) {
+ return;
+ }
+
+ $modified = new \DateTime('@'.$data['createdAt']);
+ $modified->setTimezone(new \DateTimeZone('GMT'));
+
+ return sprintf('%s GMT', $modified->format('l, d-M-y H:i:s'));
+ }
+
+ /**
+ * Get the ETag from the cached response.
+ *
+ * @param CacheItemInterface $cacheItem
+ *
+ * @return string|null
+ */
+ private function getETag(CacheItemInterface $cacheItem)
+ {
+ $data = $cacheItem->get();
+ // The isset() is to be removed in 2.0.
+ if (!isset($data['etag'])) {
+ return;
+ }
+
+ foreach ($data['etag'] as $etag) {
+ if (!empty($etag)) {
+ return $etag;
+ }
+ }
+ }
+}
diff --git a/vendor/php-http/cache-plugin/src/Exception/RewindStreamException.php b/vendor/php-http/cache-plugin/src/Exception/RewindStreamException.php
new file mode 100644
index 00000000..1b9eaee3
--- /dev/null
+++ b/vendor/php-http/cache-plugin/src/Exception/RewindStreamException.php
@@ -0,0 +1,12 @@
+
+ */
+class RewindStreamException extends \RuntimeException implements Exception
+{
+}
diff --git a/vendor/php-http/client-common/CHANGELOG.md b/vendor/php-http/client-common/CHANGELOG.md
new file mode 100644
index 00000000..ff6b43cb
--- /dev/null
+++ b/vendor/php-http/client-common/CHANGELOG.md
@@ -0,0 +1,179 @@
+# Change Log
+
+## 1.9.0 - 2019-01-03
+
+### Added
+
+- Support for PSR-18 clients
+- Added traits `VersionBridgePlugin` and `VersionBridgeClient` to help plugins and clients to support both
+ 1.x and 2.x version of `php-http/client-common` and `php-http/httplug`.
+
+### Changed
+
+- [RetryPlugin] Renamed the configuration options for the exception retry callback from `decider` to `exception_decider`
+ and `delay` to `exception_delay`. The old names still work but are deprecated.
+
+## 1.8.2 - 2018-12-14
+
+### Changed
+
+- When multiple cookies exist, a single header with all cookies is sent as per RFC 6265 Section 5.4
+- AddPathPlugin will now trim of ending slashes in paths
+
+## 1.8.1 - 2018-10-09
+
+### Fixed
+
+- Reverted change to RetryPlugin so it again waits when retrying to avoid "can only throw objects" error.
+
+## 1.8.0 - 2018-09-21
+
+### Added
+
+ - Add an option on ErrorPlugin to only throw exception on response with 5XX status code.
+
+### Changed
+
+- AddPathPlugin no longer add prefix multiple times if a request is restarted - it now only adds the prefix if that request chain has not yet passed through the AddPathPlugin
+- RetryPlugin no longer wait for retried requests and use a deferred promise instead
+
+### Fixed
+
+- Decoder plugin will now remove header when there is no more encoding, instead of setting to an empty array
+
+
+## 1.7.0 - 2017-11-30
+
+### Added
+
+- Symfony 4 support
+
+### Changed
+
+- Strict comparison in DecoderPlugin
+
+## 1.6.0 - 2017-10-16
+
+### Added
+
+- Add HttpClientPool client to leverage load balancing and fallback mechanism [see the documentation](http://docs.php-http.org/en/latest/components/client-common.html) for more details.
+- `PluginClientFactory` to create `PluginClient` instances.
+- Added new option 'delay' for `RetryPlugin`.
+- Added new option 'decider' for `RetryPlugin`.
+- Supports more cookie date formats in the Cookie Plugin
+
+### Changed
+
+- The `RetryPlugin` does now wait between retries. To disable/change this feature you must write something like:
+
+```php
+$plugin = new RetryPlugin(['delay' => function(RequestInterface $request, Exception $e, $retries) {
+ return 0;
+});
+```
+
+### Deprecated
+
+- The `debug_plugins` option for `PluginClient` is deprecated and will be removed in 2.0. Use the decorator design pattern instead like in [ProfilePlugin](https://github.com/php-http/HttplugBundle/blob/de33f9c14252f22093a5ec7d84f17535ab31a384/Collector/ProfilePlugin.php).
+
+## 1.5.0 - 2017-03-30
+
+### Added
+
+- `QueryDefaultsPlugin` to add default query parameters.
+
+## 1.4.2 - 2017-03-18
+
+### Deprecated
+
+- `DecoderPlugin` does not longer claim to support `compress` content encoding
+
+### Fixed
+
+- `CookiePlugin` allows main domain cookies to be sent/stored for subdomains
+- `DecoderPlugin` uses the right `FilteredStream` to handle `deflate` content encoding
+
+
+## 1.4.1 - 2017-02-20
+
+### Fixed
+
+- Cast return value of `StreamInterface::getSize` to string in `ContentLengthPlugin`
+
+
+## 1.4.0 - 2016-11-04
+
+### Added
+
+- Add Path plugin
+- Base URI plugin that combines Add Host and Add Path plugins
+
+
+## 1.3.0 - 2016-10-16
+
+### Changed
+
+- Fix Emulated Trait to use Http based promise which respect the HttpAsyncClient interface
+- Require Httplug 1.1 where we use HTTP specific promises.
+- RedirectPlugin: use the full URL instead of the URI to properly keep track of redirects
+- Add AddPathPlugin for API URLs with base path
+- Add BaseUriPlugin that combines AddHostPlugin and AddPathPlugin
+
+
+## 1.2.1 - 2016-07-26
+
+### Changed
+
+- AddHostPlugin also sets the port if specified
+
+
+## 1.2.0 - 2016-07-14
+
+### Added
+
+- Suggest separate plugins in composer.json
+- Introduced `debug_plugins` option for `PluginClient`
+
+
+## 1.1.0 - 2016-05-04
+
+### Added
+
+- Add a flexible http client providing both contract, and only emulating what's necessary
+- HTTP Client Router: route requests to underlying clients
+- Plugin client and core plugins moved here from `php-http/plugins`
+
+### Deprecated
+
+- Extending client classes, they will be made final in version 2.0
+
+
+## 1.0.0 - 2016-01-27
+
+### Changed
+
+- Remove useless interface in BatchException
+
+
+## 0.2.0 - 2016-01-12
+
+### Changed
+
+- Updated package files
+- Updated HTTPlug to RC1
+
+
+## 0.1.1 - 2015-12-26
+
+### Added
+
+- Emulated clients
+
+
+## 0.1.0 - 2015-12-25
+
+### Added
+
+- Batch client from utils
+- Methods client from utils
+- Emulators and decorators from client-tools
diff --git a/vendor/php-http/client-common/LICENSE b/vendor/php-http/client-common/LICENSE
new file mode 100644
index 00000000..4558d6f0
--- /dev/null
+++ b/vendor/php-http/client-common/LICENSE
@@ -0,0 +1,19 @@
+Copyright (c) 2015-2016 PHP HTTP Team
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is furnished
+to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+THE SOFTWARE.
diff --git a/vendor/php-http/client-common/README.md b/vendor/php-http/client-common/README.md
new file mode 100644
index 00000000..017bfcec
--- /dev/null
+++ b/vendor/php-http/client-common/README.md
@@ -0,0 +1,55 @@
+# HTTP Client Common
+
+[![Latest Version](https://img.shields.io/github/release/php-http/client-common.svg?style=flat-square)](https://github.com/php-http/client-common/releases)
+[![Software License](https://img.shields.io/badge/license-MIT-brightgreen.svg?style=flat-square)](LICENSE)
+[![Build Status](https://img.shields.io/travis/php-http/client-common.svg?style=flat-square)](https://travis-ci.org/php-http/client-common)
+[![Code Coverage](https://img.shields.io/scrutinizer/coverage/g/php-http/client-common.svg?style=flat-square)](https://scrutinizer-ci.com/g/php-http/client-common)
+[![Quality Score](https://img.shields.io/scrutinizer/g/php-http/client-common.svg?style=flat-square)](https://scrutinizer-ci.com/g/php-http/client-common)
+[![Total Downloads](https://img.shields.io/packagist/dt/php-http/client-common.svg?style=flat-square)](https://packagist.org/packages/php-http/client-common)
+
+**Common HTTP Client implementations and tools for HTTPlug.**
+
+
+## Install
+
+Via Composer
+
+``` bash
+$ composer require php-http/client-common
+```
+
+
+## Usage
+
+This package provides common tools for HTTP Clients:
+
+- BatchClient to handle sending requests in parallel
+- A convenience client with HTTP method names as class methods
+- Emulator, decorator layers for sync/async clients
+
+
+## Documentation
+
+Please see the [official documentation](http://docs.php-http.org/en/latest/components/client-common.html).
+
+
+## Testing
+
+``` bash
+$ composer test
+```
+
+
+## Contributing
+
+Please see our [contributing guide](http://docs.php-http.org/en/latest/development/contributing.html).
+
+
+## Security
+
+If you discover any security related issues, please contact us at [security@php-http.org](mailto:security@php-http.org).
+
+
+## License
+
+The MIT License (MIT). Please see [License File](LICENSE) for more information.
diff --git a/vendor/php-http/client-common/composer.json b/vendor/php-http/client-common/composer.json
new file mode 100644
index 00000000..c502f778
--- /dev/null
+++ b/vendor/php-http/client-common/composer.json
@@ -0,0 +1,43 @@
+{
+ "name": "php-http/client-common",
+ "description": "Common HTTP Client implementations and tools for HTTPlug",
+ "license": "MIT",
+ "keywords": ["http", "client", "httplug", "common"],
+ "homepage": "http://httplug.io",
+ "authors": [
+ {
+ "name": "Márk Sági-Kazár",
+ "email": "mark.sagikazar@gmail.com"
+ }
+ ],
+ "require": {
+ "php": "^5.4 || ^7.0",
+ "php-http/httplug": "^1.1",
+ "php-http/message-factory": "^1.0",
+ "php-http/message": "^1.6",
+ "symfony/options-resolver": "^2.6 || ^3.0 || ^4.0"
+ },
+ "require-dev": {
+ "phpspec/phpspec": "^2.5 || ^3.4 || ^4.2",
+ "guzzlehttp/psr7": "^1.4"
+ },
+ "suggest": {
+ "php-http/logger-plugin": "PSR-3 Logger plugin",
+ "php-http/cache-plugin": "PSR-6 Cache plugin",
+ "php-http/stopwatch-plugin": "Symfony Stopwatch plugin"
+ },
+ "autoload": {
+ "psr-4": {
+ "Http\\Client\\Common\\": "src/"
+ }
+ },
+ "scripts": {
+ "test": "vendor/bin/phpspec run",
+ "test-ci": "vendor/bin/phpspec run -c phpspec.ci.yml"
+ },
+ "extra": {
+ "branch-alias": {
+ "dev-master": "1.9.x-dev"
+ }
+ }
+}
diff --git a/vendor/php-http/client-common/src/BatchClient.php b/vendor/php-http/client-common/src/BatchClient.php
new file mode 100644
index 00000000..1aa99507
--- /dev/null
+++ b/vendor/php-http/client-common/src/BatchClient.php
@@ -0,0 +1,78 @@
+
+ */
+class BatchClient implements HttpClient
+{
+ /**
+ * @var HttpClient|ClientInterface
+ */
+ private $client;
+
+ /**
+ * @param HttpClient|ClientInterface $client
+ */
+ public function __construct($client)
+ {
+ if (!($client instanceof HttpClient) && !($client instanceof ClientInterface)) {
+ throw new \LogicException('Client must be an instance of Http\\Client\\HttpClient or Psr\\Http\\Client\\ClientInterface');
+ }
+
+ $this->client = $client;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function sendRequest(RequestInterface $request)
+ {
+ return $this->client->sendRequest($request);
+ }
+
+ /**
+ * Send several requests.
+ *
+ * You may not assume that the requests are executed in a particular order. If the order matters
+ * for your application, use sendRequest sequentially.
+ *
+ * @param RequestInterface[] The requests to send
+ *
+ * @return BatchResult Containing one result per request
+ *
+ * @throws BatchException If one or more requests fails. The exception gives access to the
+ * BatchResult with a map of request to result for success, request to
+ * exception for failures
+ */
+ public function sendRequests(array $requests)
+ {
+ $batchResult = new BatchResult();
+
+ foreach ($requests as $request) {
+ try {
+ $response = $this->sendRequest($request);
+ $batchResult = $batchResult->addResponse($request, $response);
+ } catch (Exception $e) {
+ $batchResult = $batchResult->addException($request, $e);
+ }
+ }
+
+ if ($batchResult->hasExceptions()) {
+ throw new BatchException($batchResult);
+ }
+
+ return $batchResult;
+ }
+}
diff --git a/vendor/php-http/client-common/src/BatchResult.php b/vendor/php-http/client-common/src/BatchResult.php
new file mode 100644
index 00000000..710611d6
--- /dev/null
+++ b/vendor/php-http/client-common/src/BatchResult.php
@@ -0,0 +1,181 @@
+
+ */
+final class BatchResult
+{
+ /**
+ * @var \SplObjectStorage
+ */
+ private $responses;
+
+ /**
+ * @var \SplObjectStorage
+ */
+ private $exceptions;
+
+ public function __construct()
+ {
+ $this->responses = new \SplObjectStorage();
+ $this->exceptions = new \SplObjectStorage();
+ }
+
+ /**
+ * Checks if there are any successful responses at all.
+ *
+ * @return bool
+ */
+ public function hasResponses()
+ {
+ return $this->responses->count() > 0;
+ }
+
+ /**
+ * Returns all successful responses.
+ *
+ * @return ResponseInterface[]
+ */
+ public function getResponses()
+ {
+ $responses = [];
+
+ foreach ($this->responses as $request) {
+ $responses[] = $this->responses[$request];
+ }
+
+ return $responses;
+ }
+
+ /**
+ * Checks if there is a successful response for a request.
+ *
+ * @param RequestInterface $request
+ *
+ * @return bool
+ */
+ public function isSuccessful(RequestInterface $request)
+ {
+ return $this->responses->contains($request);
+ }
+
+ /**
+ * Returns the response for a successful request.
+ *
+ * @param RequestInterface $request
+ *
+ * @return ResponseInterface
+ *
+ * @throws \UnexpectedValueException If request was not part of the batch or failed
+ */
+ public function getResponseFor(RequestInterface $request)
+ {
+ try {
+ return $this->responses[$request];
+ } catch (\UnexpectedValueException $e) {
+ throw new \UnexpectedValueException('Request not found', $e->getCode(), $e);
+ }
+ }
+
+ /**
+ * Adds a response in an immutable way.
+ *
+ * @param RequestInterface $request
+ * @param ResponseInterface $response
+ *
+ * @return BatchResult the new BatchResult with this request-response pair added to it
+ */
+ public function addResponse(RequestInterface $request, ResponseInterface $response)
+ {
+ $new = clone $this;
+ $new->responses->attach($request, $response);
+
+ return $new;
+ }
+
+ /**
+ * Checks if there are any unsuccessful requests at all.
+ *
+ * @return bool
+ */
+ public function hasExceptions()
+ {
+ return $this->exceptions->count() > 0;
+ }
+
+ /**
+ * Returns all exceptions for the unsuccessful requests.
+ *
+ * @return Exception[]
+ */
+ public function getExceptions()
+ {
+ $exceptions = [];
+
+ foreach ($this->exceptions as $request) {
+ $exceptions[] = $this->exceptions[$request];
+ }
+
+ return $exceptions;
+ }
+
+ /**
+ * Checks if there is an exception for a request, meaning the request failed.
+ *
+ * @param RequestInterface $request
+ *
+ * @return bool
+ */
+ public function isFailed(RequestInterface $request)
+ {
+ return $this->exceptions->contains($request);
+ }
+
+ /**
+ * Returns the exception for a failed request.
+ *
+ * @param RequestInterface $request
+ *
+ * @return Exception
+ *
+ * @throws \UnexpectedValueException If request was not part of the batch or was successful
+ */
+ public function getExceptionFor(RequestInterface $request)
+ {
+ try {
+ return $this->exceptions[$request];
+ } catch (\UnexpectedValueException $e) {
+ throw new \UnexpectedValueException('Request not found', $e->getCode(), $e);
+ }
+ }
+
+ /**
+ * Adds an exception in an immutable way.
+ *
+ * @param RequestInterface $request
+ * @param Exception $exception
+ *
+ * @return BatchResult the new BatchResult with this request-exception pair added to it
+ */
+ public function addException(RequestInterface $request, Exception $exception)
+ {
+ $new = clone $this;
+ $new->exceptions->attach($request, $exception);
+
+ return $new;
+ }
+
+ public function __clone()
+ {
+ $this->responses = clone $this->responses;
+ $this->exceptions = clone $this->exceptions;
+ }
+}
diff --git a/vendor/php-http/client-common/src/Deferred.php b/vendor/php-http/client-common/src/Deferred.php
new file mode 100644
index 00000000..075a30e1
--- /dev/null
+++ b/vendor/php-http/client-common/src/Deferred.php
@@ -0,0 +1,131 @@
+waitCallback = $waitCallback;
+ $this->state = Promise::PENDING;
+ $this->onFulfilledCallbacks = [];
+ $this->onRejectedCallbacks = [];
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function then(callable $onFulfilled = null, callable $onRejected = null)
+ {
+ $deferred = new self($this->waitCallback);
+
+ $this->onFulfilledCallbacks[] = function (ResponseInterface $response) use ($onFulfilled, $deferred) {
+ try {
+ if (null !== $onFulfilled) {
+ $response = $onFulfilled($response);
+ }
+ $deferred->resolve($response);
+ } catch (Exception $exception) {
+ $deferred->reject($exception);
+ }
+ };
+
+ $this->onRejectedCallbacks[] = function (Exception $exception) use ($onRejected, $deferred) {
+ try {
+ if (null !== $onRejected) {
+ $response = $onRejected($exception);
+ $deferred->resolve($response);
+
+ return;
+ }
+ $deferred->reject($exception);
+ } catch (Exception $newException) {
+ $deferred->reject($newException);
+ }
+ };
+
+ return $deferred;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function getState()
+ {
+ return $this->state;
+ }
+
+ /**
+ * Resolve this deferred with a Response.
+ */
+ public function resolve(ResponseInterface $response)
+ {
+ if (self::PENDING !== $this->state) {
+ return;
+ }
+
+ $this->value = $response;
+ $this->state = self::FULFILLED;
+
+ foreach ($this->onFulfilledCallbacks as $onFulfilledCallback) {
+ $onFulfilledCallback($response);
+ }
+ }
+
+ /**
+ * Reject this deferred with an Exception.
+ */
+ public function reject(Exception $exception)
+ {
+ if (self::PENDING !== $this->state) {
+ return;
+ }
+
+ $this->failure = $exception;
+ $this->state = self::REJECTED;
+
+ foreach ($this->onRejectedCallbacks as $onRejectedCallback) {
+ $onRejectedCallback($exception);
+ }
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function wait($unwrap = true)
+ {
+ if (self::PENDING === $this->state) {
+ $callback = $this->waitCallback;
+ $callback();
+ }
+
+ if (!$unwrap) {
+ return;
+ }
+
+ if (self::FULFILLED === $this->state) {
+ return $this->value;
+ }
+
+ throw $this->failure;
+ }
+}
diff --git a/vendor/php-http/client-common/src/EmulatedHttpAsyncClient.php b/vendor/php-http/client-common/src/EmulatedHttpAsyncClient.php
new file mode 100644
index 00000000..39f89cc0
--- /dev/null
+++ b/vendor/php-http/client-common/src/EmulatedHttpAsyncClient.php
@@ -0,0 +1,32 @@
+
+ */
+class EmulatedHttpAsyncClient implements HttpClient, HttpAsyncClient
+{
+ use HttpAsyncClientEmulator;
+ use HttpClientDecorator;
+
+ /**
+ * @param HttpClient|ClientInterface $httpClient
+ */
+ public function __construct($httpClient)
+ {
+ if (!($httpClient instanceof HttpClient) && !($httpClient instanceof ClientInterface)) {
+ throw new \LogicException('Client must be an instance of Http\\Client\\HttpClient or Psr\\Http\\Client\\ClientInterface');
+ }
+
+ $this->httpClient = $httpClient;
+ }
+}
diff --git a/vendor/php-http/client-common/src/EmulatedHttpClient.php b/vendor/php-http/client-common/src/EmulatedHttpClient.php
new file mode 100644
index 00000000..01046c83
--- /dev/null
+++ b/vendor/php-http/client-common/src/EmulatedHttpClient.php
@@ -0,0 +1,27 @@
+
+ */
+class EmulatedHttpClient implements HttpClient, HttpAsyncClient
+{
+ use HttpAsyncClientDecorator;
+ use HttpClientEmulator;
+
+ /**
+ * @param HttpAsyncClient $httpAsyncClient
+ */
+ public function __construct(HttpAsyncClient $httpAsyncClient)
+ {
+ $this->httpAsyncClient = $httpAsyncClient;
+ }
+}
diff --git a/vendor/php-http/client-common/src/Exception/BatchException.php b/vendor/php-http/client-common/src/Exception/BatchException.php
new file mode 100644
index 00000000..66a92719
--- /dev/null
+++ b/vendor/php-http/client-common/src/Exception/BatchException.php
@@ -0,0 +1,39 @@
+
+ */
+final class BatchException extends TransferException
+{
+ /**
+ * @var BatchResult
+ */
+ private $result;
+
+ /**
+ * @param BatchResult $result
+ */
+ public function __construct(BatchResult $result)
+ {
+ $this->result = $result;
+ }
+
+ /**
+ * Returns the BatchResult that contains all responses and exceptions.
+ *
+ * @return BatchResult
+ */
+ public function getResult()
+ {
+ return $this->result;
+ }
+}
diff --git a/vendor/php-http/client-common/src/Exception/CircularRedirectionException.php b/vendor/php-http/client-common/src/Exception/CircularRedirectionException.php
new file mode 100644
index 00000000..73ec521e
--- /dev/null
+++ b/vendor/php-http/client-common/src/Exception/CircularRedirectionException.php
@@ -0,0 +1,14 @@
+
+ */
+class CircularRedirectionException extends HttpException
+{
+}
diff --git a/vendor/php-http/client-common/src/Exception/ClientErrorException.php b/vendor/php-http/client-common/src/Exception/ClientErrorException.php
new file mode 100644
index 00000000..b1f6cc85
--- /dev/null
+++ b/vendor/php-http/client-common/src/Exception/ClientErrorException.php
@@ -0,0 +1,14 @@
+
+ */
+class ClientErrorException extends HttpException
+{
+}
diff --git a/vendor/php-http/client-common/src/Exception/HttpClientNotFoundException.php b/vendor/php-http/client-common/src/Exception/HttpClientNotFoundException.php
new file mode 100644
index 00000000..5d33f983
--- /dev/null
+++ b/vendor/php-http/client-common/src/Exception/HttpClientNotFoundException.php
@@ -0,0 +1,14 @@
+
+ */
+class HttpClientNotFoundException extends TransferException
+{
+}
diff --git a/vendor/php-http/client-common/src/Exception/LoopException.php b/vendor/php-http/client-common/src/Exception/LoopException.php
new file mode 100644
index 00000000..e834124d
--- /dev/null
+++ b/vendor/php-http/client-common/src/Exception/LoopException.php
@@ -0,0 +1,14 @@
+
+ */
+class LoopException extends RequestException
+{
+}
diff --git a/vendor/php-http/client-common/src/Exception/MultipleRedirectionException.php b/vendor/php-http/client-common/src/Exception/MultipleRedirectionException.php
new file mode 100644
index 00000000..ae514cd7
--- /dev/null
+++ b/vendor/php-http/client-common/src/Exception/MultipleRedirectionException.php
@@ -0,0 +1,14 @@
+
+ */
+class MultipleRedirectionException extends HttpException
+{
+}
diff --git a/vendor/php-http/client-common/src/Exception/ServerErrorException.php b/vendor/php-http/client-common/src/Exception/ServerErrorException.php
new file mode 100644
index 00000000..665d7241
--- /dev/null
+++ b/vendor/php-http/client-common/src/Exception/ServerErrorException.php
@@ -0,0 +1,14 @@
+
+ */
+class ServerErrorException extends HttpException
+{
+}
diff --git a/vendor/php-http/client-common/src/FlexibleHttpClient.php b/vendor/php-http/client-common/src/FlexibleHttpClient.php
new file mode 100644
index 00000000..d0a6e88c
--- /dev/null
+++ b/vendor/php-http/client-common/src/FlexibleHttpClient.php
@@ -0,0 +1,40 @@
+
+ */
+final class FlexibleHttpClient implements HttpClient, HttpAsyncClient
+{
+ use HttpClientDecorator;
+ use HttpAsyncClientDecorator;
+
+ /**
+ * @param HttpClient|HttpAsyncClient|ClientInterface $client
+ */
+ public function __construct($client)
+ {
+ if (!($client instanceof HttpClient) && !($client instanceof HttpAsyncClient) && !($client instanceof ClientInterface)) {
+ throw new \LogicException('Client must be an instance of Http\\Client\\HttpClient or Http\\Client\\HttpAsyncClient');
+ }
+
+ $this->httpClient = $client;
+ $this->httpAsyncClient = $client;
+
+ if (!($this->httpClient instanceof HttpClient) && !($client instanceof ClientInterface)) {
+ $this->httpClient = new EmulatedHttpClient($this->httpClient);
+ }
+
+ if (!($this->httpAsyncClient instanceof HttpAsyncClient)) {
+ $this->httpAsyncClient = new EmulatedHttpAsyncClient($this->httpAsyncClient);
+ }
+ }
+}
diff --git a/vendor/php-http/client-common/src/HttpAsyncClientDecorator.php b/vendor/php-http/client-common/src/HttpAsyncClientDecorator.php
new file mode 100644
index 00000000..6eb576cd
--- /dev/null
+++ b/vendor/php-http/client-common/src/HttpAsyncClientDecorator.php
@@ -0,0 +1,29 @@
+
+ */
+trait HttpAsyncClientDecorator
+{
+ /**
+ * @var HttpAsyncClient
+ */
+ protected $httpAsyncClient;
+
+ /**
+ * {@inheritdoc}
+ *
+ * @see HttpAsyncClient::sendAsyncRequest
+ */
+ public function sendAsyncRequest(RequestInterface $request)
+ {
+ return $this->httpAsyncClient->sendAsyncRequest($request);
+ }
+}
diff --git a/vendor/php-http/client-common/src/HttpAsyncClientEmulator.php b/vendor/php-http/client-common/src/HttpAsyncClientEmulator.php
new file mode 100644
index 00000000..c0ba354f
--- /dev/null
+++ b/vendor/php-http/client-common/src/HttpAsyncClientEmulator.php
@@ -0,0 +1,36 @@
+
+ */
+trait HttpAsyncClientEmulator
+{
+ /**
+ * {@inheritdoc}
+ *
+ * @see HttpClient::sendRequest
+ */
+ abstract public function sendRequest(RequestInterface $request);
+
+ /**
+ * {@inheritdoc}
+ *
+ * @see HttpAsyncClient::sendAsyncRequest
+ */
+ public function sendAsyncRequest(RequestInterface $request)
+ {
+ try {
+ return new Promise\HttpFulfilledPromise($this->sendRequest($request));
+ } catch (Exception $e) {
+ return new Promise\HttpRejectedPromise($e);
+ }
+ }
+}
diff --git a/vendor/php-http/client-common/src/HttpClientDecorator.php b/vendor/php-http/client-common/src/HttpClientDecorator.php
new file mode 100644
index 00000000..0b7e48e5
--- /dev/null
+++ b/vendor/php-http/client-common/src/HttpClientDecorator.php
@@ -0,0 +1,30 @@
+
+ */
+trait HttpClientDecorator
+{
+ /**
+ * @var HttpClient|ClientInterface
+ */
+ protected $httpClient;
+
+ /**
+ * {@inheritdoc}
+ *
+ * @see HttpClient::sendRequest
+ */
+ public function sendRequest(RequestInterface $request)
+ {
+ return $this->httpClient->sendRequest($request);
+ }
+}
diff --git a/vendor/php-http/client-common/src/HttpClientEmulator.php b/vendor/php-http/client-common/src/HttpClientEmulator.php
new file mode 100644
index 00000000..dbec1aba
--- /dev/null
+++ b/vendor/php-http/client-common/src/HttpClientEmulator.php
@@ -0,0 +1,32 @@
+
+ */
+trait HttpClientEmulator
+{
+ /**
+ * {@inheritdoc}
+ *
+ * @see HttpClient::sendRequest
+ */
+ public function sendRequest(RequestInterface $request)
+ {
+ $promise = $this->sendAsyncRequest($request);
+
+ return $promise->wait();
+ }
+
+ /**
+ * {@inheritdoc}
+ *
+ * @see HttpAsyncClient::sendAsyncRequest
+ */
+ abstract public function sendAsyncRequest(RequestInterface $request);
+}
diff --git a/vendor/php-http/client-common/src/HttpClientPool.php b/vendor/php-http/client-common/src/HttpClientPool.php
new file mode 100644
index 00000000..7ac292ca
--- /dev/null
+++ b/vendor/php-http/client-common/src/HttpClientPool.php
@@ -0,0 +1,59 @@
+clientPool[] = $client;
+ }
+
+ /**
+ * Return an http client given a specific strategy.
+ *
+ * @throws HttpClientNotFoundException When no http client has been found into the pool
+ *
+ * @return HttpClientPoolItem Return a http client that can do both sync or async
+ */
+ abstract protected function chooseHttpClient();
+
+ /**
+ * {@inheritdoc}
+ */
+ public function sendAsyncRequest(RequestInterface $request)
+ {
+ return $this->chooseHttpClient()->sendAsyncRequest($request);
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function sendRequest(RequestInterface $request)
+ {
+ return $this->chooseHttpClient()->sendRequest($request);
+ }
+}
diff --git a/vendor/php-http/client-common/src/HttpClientPool/LeastUsedClientPool.php b/vendor/php-http/client-common/src/HttpClientPool/LeastUsedClientPool.php
new file mode 100644
index 00000000..6299cceb
--- /dev/null
+++ b/vendor/php-http/client-common/src/HttpClientPool/LeastUsedClientPool.php
@@ -0,0 +1,45 @@
+
+ */
+final class LeastUsedClientPool extends HttpClientPool
+{
+ /**
+ * {@inheritdoc}
+ */
+ protected function chooseHttpClient()
+ {
+ $clientPool = array_filter($this->clientPool, function (HttpClientPoolItem $clientPoolItem) {
+ return !$clientPoolItem->isDisabled();
+ });
+
+ if (0 === count($clientPool)) {
+ throw new HttpClientNotFoundException('Cannot choose a http client as there is no one present in the pool');
+ }
+
+ usort($clientPool, function (HttpClientPoolItem $clientA, HttpClientPoolItem $clientB) {
+ if ($clientA->getSendingRequestCount() === $clientB->getSendingRequestCount()) {
+ return 0;
+ }
+
+ if ($clientA->getSendingRequestCount() < $clientB->getSendingRequestCount()) {
+ return -1;
+ }
+
+ return 1;
+ });
+
+ return reset($clientPool);
+ }
+}
diff --git a/vendor/php-http/client-common/src/HttpClientPool/RandomClientPool.php b/vendor/php-http/client-common/src/HttpClientPool/RandomClientPool.php
new file mode 100644
index 00000000..3255f865
--- /dev/null
+++ b/vendor/php-http/client-common/src/HttpClientPool/RandomClientPool.php
@@ -0,0 +1,31 @@
+
+ */
+final class RandomClientPool extends HttpClientPool
+{
+ /**
+ * {@inheritdoc}
+ */
+ protected function chooseHttpClient()
+ {
+ $clientPool = array_filter($this->clientPool, function (HttpClientPoolItem $clientPoolItem) {
+ return !$clientPoolItem->isDisabled();
+ });
+
+ if (0 === count($clientPool)) {
+ throw new HttpClientNotFoundException('Cannot choose a http client as there is no one present in the pool');
+ }
+
+ return $clientPool[array_rand($clientPool)];
+ }
+}
diff --git a/vendor/php-http/client-common/src/HttpClientPool/RoundRobinClientPool.php b/vendor/php-http/client-common/src/HttpClientPool/RoundRobinClientPool.php
new file mode 100644
index 00000000..8d8e40a0
--- /dev/null
+++ b/vendor/php-http/client-common/src/HttpClientPool/RoundRobinClientPool.php
@@ -0,0 +1,41 @@
+
+ */
+final class RoundRobinClientPool extends HttpClientPool
+{
+ /**
+ * {@inheritdoc}
+ */
+ protected function chooseHttpClient()
+ {
+ $last = current($this->clientPool);
+
+ do {
+ $client = next($this->clientPool);
+
+ if (false === $client) {
+ $client = reset($this->clientPool);
+
+ if (false === $client) {
+ throw new HttpClientNotFoundException('Cannot choose a http client as there is no one present in the pool');
+ }
+ }
+
+ // Case when there is only one and the last one has been disabled
+ if ($last === $client && $client->isDisabled()) {
+ throw new HttpClientNotFoundException('Cannot choose a http client as there is no one enabled in the pool');
+ }
+ } while ($client->isDisabled());
+
+ return $client;
+ }
+}
diff --git a/vendor/php-http/client-common/src/HttpClientPoolItem.php b/vendor/php-http/client-common/src/HttpClientPoolItem.php
new file mode 100644
index 00000000..09cd6ddf
--- /dev/null
+++ b/vendor/php-http/client-common/src/HttpClientPoolItem.php
@@ -0,0 +1,178 @@
+
+ */
+class HttpClientPoolItem implements HttpClient, HttpAsyncClient
+{
+ /**
+ * @var int Number of request this client is currently sending
+ */
+ private $sendingRequestCount = 0;
+
+ /**
+ * @var \DateTime|null Time when this client has been disabled or null if enable
+ */
+ private $disabledAt;
+
+ /**
+ * @var int|null Number of seconds after this client is reenable, by default null: never reenable this client
+ */
+ private $reenableAfter;
+
+ /**
+ * @var FlexibleHttpClient A http client responding to async and sync request
+ */
+ private $client;
+
+ /**
+ * @param HttpClient|HttpAsyncClient $client
+ * @param null|int $reenableAfter Number of seconds after this client is reenable
+ */
+ public function __construct($client, $reenableAfter = null)
+ {
+ $this->client = new FlexibleHttpClient($client);
+ $this->reenableAfter = $reenableAfter;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function sendRequest(RequestInterface $request)
+ {
+ if ($this->isDisabled()) {
+ throw new Exception\RequestException('Cannot send the request as this client has been disabled', $request);
+ }
+
+ try {
+ $this->incrementRequestCount();
+ $response = $this->client->sendRequest($request);
+ $this->decrementRequestCount();
+ } catch (Exception $e) {
+ $this->disable();
+ $this->decrementRequestCount();
+
+ throw $e;
+ }
+
+ return $response;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function sendAsyncRequest(RequestInterface $request)
+ {
+ if ($this->isDisabled()) {
+ throw new Exception\RequestException('Cannot send the request as this client has been disabled', $request);
+ }
+
+ $this->incrementRequestCount();
+
+ return $this->client->sendAsyncRequest($request)->then(function ($response) {
+ $this->decrementRequestCount();
+
+ return $response;
+ }, function ($exception) {
+ $this->disable();
+ $this->decrementRequestCount();
+
+ throw $exception;
+ });
+ }
+
+ /**
+ * Whether this client is disabled or not.
+ *
+ * Will also reactivate this client if possible
+ *
+ * @internal
+ *
+ * @return bool
+ */
+ public function isDisabled()
+ {
+ $disabledAt = $this->getDisabledAt();
+
+ if (null !== $this->reenableAfter && null !== $disabledAt) {
+ // Reenable after a certain time
+ $now = new \DateTime();
+
+ if (($now->getTimestamp() - $disabledAt->getTimestamp()) >= $this->reenableAfter) {
+ $this->enable();
+
+ return false;
+ }
+
+ return true;
+ }
+
+ return null !== $disabledAt;
+ }
+
+ /**
+ * Get current number of request that is send by the underlying http client.
+ *
+ * @internal
+ *
+ * @return int
+ */
+ public function getSendingRequestCount()
+ {
+ return $this->sendingRequestCount;
+ }
+
+ /**
+ * Return when this client has been disabled or null if it's enabled.
+ *
+ * @return \DateTime|null
+ */
+ private function getDisabledAt()
+ {
+ return $this->disabledAt;
+ }
+
+ /**
+ * Increment the request count.
+ */
+ private function incrementRequestCount()
+ {
+ ++$this->sendingRequestCount;
+ }
+
+ /**
+ * Decrement the request count.
+ */
+ private function decrementRequestCount()
+ {
+ --$this->sendingRequestCount;
+ }
+
+ /**
+ * Enable the current client.
+ */
+ private function enable()
+ {
+ $this->disabledAt = null;
+ }
+
+ /**
+ * Disable the current client.
+ */
+ private function disable()
+ {
+ $this->disabledAt = new \DateTime('now');
+ }
+}
diff --git a/vendor/php-http/client-common/src/HttpClientRouter.php b/vendor/php-http/client-common/src/HttpClientRouter.php
new file mode 100644
index 00000000..9f721336
--- /dev/null
+++ b/vendor/php-http/client-common/src/HttpClientRouter.php
@@ -0,0 +1,74 @@
+
+ */
+final class HttpClientRouter implements HttpClient, HttpAsyncClient
+{
+ /**
+ * @var array
+ */
+ private $clients = [];
+
+ /**
+ * {@inheritdoc}
+ */
+ public function sendRequest(RequestInterface $request)
+ {
+ $client = $this->chooseHttpClient($request);
+
+ return $client->sendRequest($request);
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function sendAsyncRequest(RequestInterface $request)
+ {
+ $client = $this->chooseHttpClient($request);
+
+ return $client->sendAsyncRequest($request);
+ }
+
+ /**
+ * Add a client to the router.
+ *
+ * @param HttpClient|HttpAsyncClient $client
+ * @param RequestMatcher $requestMatcher
+ */
+ public function addClient($client, RequestMatcher $requestMatcher)
+ {
+ $this->clients[] = [
+ 'matcher' => $requestMatcher,
+ 'client' => new FlexibleHttpClient($client),
+ ];
+ }
+
+ /**
+ * Choose an HTTP client given a specific request.
+ *
+ * @param RequestInterface $request
+ *
+ * @return HttpClient|HttpAsyncClient
+ */
+ protected function chooseHttpClient(RequestInterface $request)
+ {
+ foreach ($this->clients as $client) {
+ if ($client['matcher']->matches($request)) {
+ return $client['client'];
+ }
+ }
+
+ throw new RequestException('No client found for the specified request', $request);
+ }
+}
diff --git a/vendor/php-http/client-common/src/HttpMethodsClient.php b/vendor/php-http/client-common/src/HttpMethodsClient.php
new file mode 100644
index 00000000..c462c107
--- /dev/null
+++ b/vendor/php-http/client-common/src/HttpMethodsClient.php
@@ -0,0 +1,210 @@
+get('/foo')
+ * ->post('/bar')
+ * ;
+ *
+ * The client also exposes the sendRequest methods of the wrapped HttpClient.
+ *
+ * @author Márk Sági-Kazár
+ * @author David Buchmann
+ */
+class HttpMethodsClient implements HttpClient
+{
+ /**
+ * @var HttpClient|ClientInterface
+ */
+ private $httpClient;
+
+ /**
+ * @var RequestFactory
+ */
+ private $requestFactory;
+
+ /**
+ * @param HttpClient|ClientInterface $httpClient The client to send requests with
+ * @param RequestFactory $requestFactory The message factory to create requests
+ */
+ public function __construct($httpClient, RequestFactory $requestFactory)
+ {
+ if (!($httpClient instanceof HttpClient) && !($httpClient instanceof ClientInterface)) {
+ throw new \LogicException('Client must be an instance of Http\\Client\\HttpClient or Psr\\Http\\Client\\ClientInterface');
+ }
+
+ $this->httpClient = $httpClient;
+ $this->requestFactory = $requestFactory;
+ }
+
+ /**
+ * Sends a GET request.
+ *
+ * @param string|UriInterface $uri
+ * @param array $headers
+ *
+ * @throws Exception
+ *
+ * @return ResponseInterface
+ */
+ public function get($uri, array $headers = [])
+ {
+ return $this->send('GET', $uri, $headers, null);
+ }
+
+ /**
+ * Sends an HEAD request.
+ *
+ * @param string|UriInterface $uri
+ * @param array $headers
+ *
+ * @throws Exception
+ *
+ * @return ResponseInterface
+ */
+ public function head($uri, array $headers = [])
+ {
+ return $this->send('HEAD', $uri, $headers, null);
+ }
+
+ /**
+ * Sends a TRACE request.
+ *
+ * @param string|UriInterface $uri
+ * @param array $headers
+ *
+ * @throws Exception
+ *
+ * @return ResponseInterface
+ */
+ public function trace($uri, array $headers = [])
+ {
+ return $this->send('TRACE', $uri, $headers, null);
+ }
+
+ /**
+ * Sends a POST request.
+ *
+ * @param string|UriInterface $uri
+ * @param array $headers
+ * @param string|StreamInterface|null $body
+ *
+ * @throws Exception
+ *
+ * @return ResponseInterface
+ */
+ public function post($uri, array $headers = [], $body = null)
+ {
+ return $this->send('POST', $uri, $headers, $body);
+ }
+
+ /**
+ * Sends a PUT request.
+ *
+ * @param string|UriInterface $uri
+ * @param array $headers
+ * @param string|StreamInterface|null $body
+ *
+ * @throws Exception
+ *
+ * @return ResponseInterface
+ */
+ public function put($uri, array $headers = [], $body = null)
+ {
+ return $this->send('PUT', $uri, $headers, $body);
+ }
+
+ /**
+ * Sends a PATCH request.
+ *
+ * @param string|UriInterface $uri
+ * @param array $headers
+ * @param string|StreamInterface|null $body
+ *
+ * @throws Exception
+ *
+ * @return ResponseInterface
+ */
+ public function patch($uri, array $headers = [], $body = null)
+ {
+ return $this->send('PATCH', $uri, $headers, $body);
+ }
+
+ /**
+ * Sends a DELETE request.
+ *
+ * @param string|UriInterface $uri
+ * @param array $headers
+ * @param string|StreamInterface|null $body
+ *
+ * @throws Exception
+ *
+ * @return ResponseInterface
+ */
+ public function delete($uri, array $headers = [], $body = null)
+ {
+ return $this->send('DELETE', $uri, $headers, $body);
+ }
+
+ /**
+ * Sends an OPTIONS request.
+ *
+ * @param string|UriInterface $uri
+ * @param array $headers
+ * @param string|StreamInterface|null $body
+ *
+ * @throws Exception
+ *
+ * @return ResponseInterface
+ */
+ public function options($uri, array $headers = [], $body = null)
+ {
+ return $this->send('OPTIONS', $uri, $headers, $body);
+ }
+
+ /**
+ * Sends a request with any HTTP method.
+ *
+ * @param string $method HTTP method to use
+ * @param string|UriInterface $uri
+ * @param array $headers
+ * @param string|StreamInterface|null $body
+ *
+ * @throws Exception
+ *
+ * @return ResponseInterface
+ */
+ public function send($method, $uri, array $headers = [], $body = null)
+ {
+ return $this->sendRequest($this->requestFactory->createRequest(
+ $method,
+ $uri,
+ $headers,
+ $body
+ ));
+ }
+
+ /**
+ * Forward to the underlying HttpClient.
+ *
+ * {@inheritdoc}
+ */
+ public function sendRequest(RequestInterface $request)
+ {
+ return $this->httpClient->sendRequest($request);
+ }
+}
diff --git a/vendor/php-http/client-common/src/Plugin.php b/vendor/php-http/client-common/src/Plugin.php
new file mode 100644
index 00000000..89a2a622
--- /dev/null
+++ b/vendor/php-http/client-common/src/Plugin.php
@@ -0,0 +1,32 @@
+
+ */
+interface Plugin
+{
+ /**
+ * Handle the request and return the response coming from the next callable.
+ *
+ * @see http://docs.php-http.org/en/latest/plugins/build-your-own.html
+ *
+ * @param RequestInterface $request
+ * @param callable $next Next middleware in the chain, the request is passed as the first argument
+ * @param callable $first First middleware in the chain, used to to restart a request
+ *
+ * @return Promise Resolves a PSR-7 Response or fails with an Http\Client\Exception (The same as HttpAsyncClient).
+ */
+ public function handleRequest(RequestInterface $request, callable $next, callable $first);
+}
diff --git a/vendor/php-http/client-common/src/Plugin/AddHostPlugin.php b/vendor/php-http/client-common/src/Plugin/AddHostPlugin.php
new file mode 100644
index 00000000..29ab8ae8
--- /dev/null
+++ b/vendor/php-http/client-common/src/Plugin/AddHostPlugin.php
@@ -0,0 +1,77 @@
+
+ */
+final class AddHostPlugin implements Plugin
+{
+ /**
+ * @var UriInterface
+ */
+ private $host;
+
+ /**
+ * @var bool
+ */
+ private $replace;
+
+ /**
+ * @param UriInterface $host
+ * @param array $config {
+ *
+ * @var bool $replace True will replace all hosts, false will only add host when none is specified.
+ * }
+ */
+ public function __construct(UriInterface $host, array $config = [])
+ {
+ if ('' === $host->getHost()) {
+ throw new \LogicException('Host can not be empty');
+ }
+
+ $this->host = $host;
+
+ $resolver = new OptionsResolver();
+ $this->configureOptions($resolver);
+ $options = $resolver->resolve($config);
+
+ $this->replace = $options['replace'];
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function handleRequest(RequestInterface $request, callable $next, callable $first)
+ {
+ if ($this->replace || '' === $request->getUri()->getHost()) {
+ $uri = $request->getUri()
+ ->withHost($this->host->getHost())
+ ->withScheme($this->host->getScheme())
+ ->withPort($this->host->getPort())
+ ;
+
+ $request = $request->withUri($uri);
+ }
+
+ return $next($request);
+ }
+
+ /**
+ * @param OptionsResolver $resolver
+ */
+ private function configureOptions(OptionsResolver $resolver)
+ {
+ $resolver->setDefaults([
+ 'replace' => false,
+ ]);
+ $resolver->setAllowedTypes('replace', 'bool');
+ }
+}
diff --git a/vendor/php-http/client-common/src/Plugin/AddPathPlugin.php b/vendor/php-http/client-common/src/Plugin/AddPathPlugin.php
new file mode 100644
index 00000000..0a1bca2b
--- /dev/null
+++ b/vendor/php-http/client-common/src/Plugin/AddPathPlugin.php
@@ -0,0 +1,60 @@
+
+ */
+final class AddPathPlugin implements Plugin
+{
+ /**
+ * @var UriInterface
+ */
+ private $uri;
+
+ /**
+ * Stores identifiers of the already altered requests.
+ *
+ * @var array
+ */
+ private $alteredRequests = [];
+
+ /**
+ * @param UriInterface $uri
+ */
+ public function __construct(UriInterface $uri)
+ {
+ if ('' === $uri->getPath()) {
+ throw new \LogicException('URI path cannot be empty');
+ }
+
+ if ('/' === substr($uri->getPath(), -1)) {
+ $uri = $uri->withPath(rtrim($uri->getPath(), '/'));
+ }
+
+ $this->uri = $uri;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function handleRequest(RequestInterface $request, callable $next, callable $first)
+ {
+ $identifier = spl_object_hash((object) $first);
+
+ if (!array_key_exists($identifier, $this->alteredRequests)) {
+ $request = $request->withUri($request->getUri()
+ ->withPath($this->uri->getPath().$request->getUri()->getPath())
+ );
+ $this->alteredRequests[$identifier] = $identifier;
+ }
+
+ return $next($request);
+ }
+}
diff --git a/vendor/php-http/client-common/src/Plugin/AuthenticationPlugin.php b/vendor/php-http/client-common/src/Plugin/AuthenticationPlugin.php
new file mode 100644
index 00000000..194712fc
--- /dev/null
+++ b/vendor/php-http/client-common/src/Plugin/AuthenticationPlugin.php
@@ -0,0 +1,38 @@
+
+ */
+final class AuthenticationPlugin implements Plugin
+{
+ /**
+ * @var Authentication An authentication system
+ */
+ private $authentication;
+
+ /**
+ * @param Authentication $authentication
+ */
+ public function __construct(Authentication $authentication)
+ {
+ $this->authentication = $authentication;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function handleRequest(RequestInterface $request, callable $next, callable $first)
+ {
+ $request = $this->authentication->authenticate($request);
+
+ return $next($request);
+ }
+}
diff --git a/vendor/php-http/client-common/src/Plugin/BaseUriPlugin.php b/vendor/php-http/client-common/src/Plugin/BaseUriPlugin.php
new file mode 100644
index 00000000..2c2a7752
--- /dev/null
+++ b/vendor/php-http/client-common/src/Plugin/BaseUriPlugin.php
@@ -0,0 +1,54 @@
+
+ */
+final class BaseUriPlugin implements Plugin
+{
+ /**
+ * @var AddHostPlugin
+ */
+ private $addHostPlugin;
+
+ /**
+ * @var AddPathPlugin|null
+ */
+ private $addPathPlugin = null;
+
+ /**
+ * @param UriInterface $uri Has to contain a host name and cans have a path.
+ * @param array $hostConfig Config for AddHostPlugin. @see AddHostPlugin::configureOptions
+ */
+ public function __construct(UriInterface $uri, array $hostConfig = [])
+ {
+ $this->addHostPlugin = new AddHostPlugin($uri, $hostConfig);
+
+ if (rtrim($uri->getPath(), '/')) {
+ $this->addPathPlugin = new AddPathPlugin($uri);
+ }
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function handleRequest(RequestInterface $request, callable $next, callable $first)
+ {
+ $addHostNext = function (RequestInterface $request) use ($next, $first) {
+ return $this->addHostPlugin->handleRequest($request, $next, $first);
+ };
+
+ if ($this->addPathPlugin) {
+ return $this->addPathPlugin->handleRequest($request, $addHostNext, $first);
+ }
+
+ return $addHostNext($request);
+ }
+}
diff --git a/vendor/php-http/client-common/src/Plugin/ContentLengthPlugin.php b/vendor/php-http/client-common/src/Plugin/ContentLengthPlugin.php
new file mode 100644
index 00000000..0f7aafae
--- /dev/null
+++ b/vendor/php-http/client-common/src/Plugin/ContentLengthPlugin.php
@@ -0,0 +1,36 @@
+
+ */
+final class ContentLengthPlugin implements Plugin
+{
+ /**
+ * {@inheritdoc}
+ */
+ public function handleRequest(RequestInterface $request, callable $next, callable $first)
+ {
+ if (!$request->hasHeader('Content-Length')) {
+ $stream = $request->getBody();
+
+ // Cannot determine the size so we use a chunk stream
+ if (null === $stream->getSize()) {
+ $stream = new ChunkStream($stream);
+ $request = $request->withBody($stream);
+ $request = $request->withAddedHeader('Transfer-Encoding', 'chunked');
+ } else {
+ $request = $request->withHeader('Content-Length', (string) $stream->getSize());
+ }
+ }
+
+ return $next($request);
+ }
+}
diff --git a/vendor/php-http/client-common/src/Plugin/ContentTypePlugin.php b/vendor/php-http/client-common/src/Plugin/ContentTypePlugin.php
new file mode 100644
index 00000000..8ef1d62b
--- /dev/null
+++ b/vendor/php-http/client-common/src/Plugin/ContentTypePlugin.php
@@ -0,0 +1,123 @@
+
+ */
+final class ContentTypePlugin implements Plugin
+{
+ /**
+ * Allow to disable the content type detection when stream is too large (as it can consume a lot of resource).
+ *
+ * @var bool
+ *
+ * true skip the content type detection
+ * false detect the content type (default value)
+ */
+ protected $skipDetection;
+
+ /**
+ * Determine the size stream limit for which the detection as to be skipped (default to 16Mb).
+ *
+ * @var int
+ */
+ protected $sizeLimit;
+
+ /**
+ * @param array $config {
+ *
+ * @var bool $skip_detection True skip detection if stream size is bigger than $size_limit.
+ * @var int $size_limit size stream limit for which the detection as to be skipped.
+ * }
+ */
+ public function __construct(array $config = [])
+ {
+ $resolver = new OptionsResolver();
+ $resolver->setDefaults([
+ 'skip_detection' => false,
+ 'size_limit' => 16000000,
+ ]);
+ $resolver->setAllowedTypes('skip_detection', 'bool');
+ $resolver->setAllowedTypes('size_limit', 'int');
+
+ $options = $resolver->resolve($config);
+
+ $this->skipDetection = $options['skip_detection'];
+ $this->sizeLimit = $options['size_limit'];
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function handleRequest(RequestInterface $request, callable $next, callable $first)
+ {
+ if (!$request->hasHeader('Content-Type')) {
+ $stream = $request->getBody();
+ $streamSize = $stream->getSize();
+
+ if (!$stream->isSeekable()) {
+ return $next($request);
+ }
+
+ if (0 === $streamSize) {
+ return $next($request);
+ }
+
+ if ($this->skipDetection && (null === $streamSize || $streamSize >= $this->sizeLimit)) {
+ return $next($request);
+ }
+
+ if ($this->isJson($stream)) {
+ $request = $request->withHeader('Content-Type', 'application/json');
+
+ return $next($request);
+ }
+
+ if ($this->isXml($stream)) {
+ $request = $request->withHeader('Content-Type', 'application/xml');
+
+ return $next($request);
+ }
+ }
+
+ return $next($request);
+ }
+
+ /**
+ * @param $stream StreamInterface
+ *
+ * @return bool
+ */
+ private function isJson($stream)
+ {
+ $stream->rewind();
+
+ json_decode($stream->getContents());
+
+ return JSON_ERROR_NONE === json_last_error();
+ }
+
+ /**
+ * @param $stream StreamInterface
+ *
+ * @return \SimpleXMLElement|false
+ */
+ private function isXml($stream)
+ {
+ $stream->rewind();
+
+ $previousValue = libxml_use_internal_errors(true);
+ $isXml = simplexml_load_string($stream->getContents());
+ libxml_use_internal_errors($previousValue);
+
+ return $isXml;
+ }
+}
diff --git a/vendor/php-http/client-common/src/Plugin/CookiePlugin.php b/vendor/php-http/client-common/src/Plugin/CookiePlugin.php
new file mode 100644
index 00000000..156532ab
--- /dev/null
+++ b/vendor/php-http/client-common/src/Plugin/CookiePlugin.php
@@ -0,0 +1,185 @@
+
+ */
+final class CookiePlugin implements Plugin
+{
+ /**
+ * Cookie storage.
+ *
+ * @var CookieJar
+ */
+ private $cookieJar;
+
+ /**
+ * @param CookieJar $cookieJar
+ */
+ public function __construct(CookieJar $cookieJar)
+ {
+ $this->cookieJar = $cookieJar;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function handleRequest(RequestInterface $request, callable $next, callable $first)
+ {
+ $cookies = [];
+ foreach ($this->cookieJar->getCookies() as $cookie) {
+ if ($cookie->isExpired()) {
+ continue;
+ }
+
+ if (!$cookie->matchDomain($request->getUri()->getHost())) {
+ continue;
+ }
+
+ if (!$cookie->matchPath($request->getUri()->getPath())) {
+ continue;
+ }
+
+ if ($cookie->isSecure() && ('https' !== $request->getUri()->getScheme())) {
+ continue;
+ }
+
+ $cookies[] = sprintf('%s=%s', $cookie->getName(), $cookie->getValue());
+ }
+
+ if (!empty($cookies)) {
+ $request = $request->withAddedHeader('Cookie', implode('; ', array_unique($cookies)));
+ }
+
+ return $next($request)->then(function (ResponseInterface $response) use ($request) {
+ if ($response->hasHeader('Set-Cookie')) {
+ $setCookies = $response->getHeader('Set-Cookie');
+
+ foreach ($setCookies as $setCookie) {
+ $cookie = $this->createCookie($request, $setCookie);
+
+ // Cookie invalid do not use it
+ if (null === $cookie) {
+ continue;
+ }
+
+ // Restrict setting cookie from another domain
+ if (!preg_match("/\.{$cookie->getDomain()}$/", '.'.$request->getUri()->getHost())) {
+ continue;
+ }
+
+ $this->cookieJar->addCookie($cookie);
+ }
+ }
+
+ return $response;
+ });
+ }
+
+ /**
+ * Creates a cookie from a string.
+ *
+ * @param RequestInterface $request
+ * @param $setCookie
+ *
+ * @return Cookie|null
+ *
+ * @throws TransferException
+ */
+ private function createCookie(RequestInterface $request, $setCookie)
+ {
+ $parts = array_map('trim', explode(';', $setCookie));
+
+ if (empty($parts) || !strpos($parts[0], '=')) {
+ return;
+ }
+
+ list($name, $cookieValue) = $this->createValueKey(array_shift($parts));
+
+ $maxAge = null;
+ $expires = null;
+ $domain = $request->getUri()->getHost();
+ $path = $request->getUri()->getPath();
+ $secure = false;
+ $httpOnly = false;
+
+ // Add the cookie pieces into the parsed data array
+ foreach ($parts as $part) {
+ list($key, $value) = $this->createValueKey($part);
+
+ switch (strtolower($key)) {
+ case 'expires':
+ try {
+ $expires = CookieUtil::parseDate($value);
+ } catch (UnexpectedValueException $e) {
+ throw new TransferException(
+ sprintf(
+ 'Cookie header `%s` expires value `%s` could not be converted to date',
+ $name,
+ $value
+ ),
+ 0,
+ $e
+ );
+ }
+
+ break;
+
+ case 'max-age':
+ $maxAge = (int) $value;
+
+ break;
+
+ case 'domain':
+ $domain = $value;
+
+ break;
+
+ case 'path':
+ $path = $value;
+
+ break;
+
+ case 'secure':
+ $secure = true;
+
+ break;
+
+ case 'httponly':
+ $httpOnly = true;
+
+ break;
+ }
+ }
+
+ return new Cookie($name, $cookieValue, $maxAge, $domain, $path, $secure, $httpOnly, $expires);
+ }
+
+ /**
+ * Separates key/value pair from cookie.
+ *
+ * @param $part
+ *
+ * @return array
+ */
+ private function createValueKey($part)
+ {
+ $parts = explode('=', $part, 2);
+ $key = trim($parts[0]);
+ $value = isset($parts[1]) ? trim($parts[1]) : true;
+
+ return [$key, $value];
+ }
+}
diff --git a/vendor/php-http/client-common/src/Plugin/DecoderPlugin.php b/vendor/php-http/client-common/src/Plugin/DecoderPlugin.php
new file mode 100644
index 00000000..0239d402
--- /dev/null
+++ b/vendor/php-http/client-common/src/Plugin/DecoderPlugin.php
@@ -0,0 +1,144 @@
+
+ */
+final class DecoderPlugin implements Plugin
+{
+ /**
+ * @var bool Whether this plugin decode stream with value in the Content-Encoding header (default to true).
+ *
+ * If set to false only the Transfer-Encoding header will be used
+ */
+ private $useContentEncoding;
+
+ /**
+ * @param array $config {
+ *
+ * @var bool $use_content_encoding Whether this plugin should look at the Content-Encoding header first or only at the Transfer-Encoding (defaults to true).
+ * }
+ */
+ public function __construct(array $config = [])
+ {
+ $resolver = new OptionsResolver();
+ $resolver->setDefaults([
+ 'use_content_encoding' => true,
+ ]);
+ $resolver->setAllowedTypes('use_content_encoding', 'bool');
+ $options = $resolver->resolve($config);
+
+ $this->useContentEncoding = $options['use_content_encoding'];
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function handleRequest(RequestInterface $request, callable $next, callable $first)
+ {
+ $encodings = extension_loaded('zlib') ? ['gzip', 'deflate'] : ['identity'];
+
+ if ($this->useContentEncoding) {
+ $request = $request->withHeader('Accept-Encoding', $encodings);
+ }
+ $encodings[] = 'chunked';
+ $request = $request->withHeader('TE', $encodings);
+
+ return $next($request)->then(function (ResponseInterface $response) {
+ return $this->decodeResponse($response);
+ });
+ }
+
+ /**
+ * Decode a response body given its Transfer-Encoding or Content-Encoding value.
+ *
+ * @param ResponseInterface $response Response to decode
+ *
+ * @return ResponseInterface New response decoded
+ */
+ private function decodeResponse(ResponseInterface $response)
+ {
+ $response = $this->decodeOnEncodingHeader('Transfer-Encoding', $response);
+
+ if ($this->useContentEncoding) {
+ $response = $this->decodeOnEncodingHeader('Content-Encoding', $response);
+ }
+
+ return $response;
+ }
+
+ /**
+ * Decode a response on a specific header (content encoding or transfer encoding mainly).
+ *
+ * @param string $headerName Name of the header
+ * @param ResponseInterface $response Response
+ *
+ * @return ResponseInterface A new instance of the response decoded
+ */
+ private function decodeOnEncodingHeader($headerName, ResponseInterface $response)
+ {
+ if ($response->hasHeader($headerName)) {
+ $encodings = $response->getHeader($headerName);
+ $newEncodings = [];
+
+ while ($encoding = array_pop($encodings)) {
+ $stream = $this->decorateStream($encoding, $response->getBody());
+
+ if (false === $stream) {
+ array_unshift($newEncodings, $encoding);
+
+ continue;
+ }
+
+ $response = $response->withBody($stream);
+ }
+
+ if (\count($newEncodings) > 0) {
+ $response = $response->withHeader($headerName, $newEncodings);
+ } else {
+ $response = $response->withoutHeader($headerName);
+ }
+ }
+
+ return $response;
+ }
+
+ /**
+ * Decorate a stream given an encoding.
+ *
+ * @param string $encoding
+ * @param StreamInterface $stream
+ *
+ * @return StreamInterface|false A new stream interface or false if encoding is not supported
+ */
+ private function decorateStream($encoding, StreamInterface $stream)
+ {
+ if ('chunked' === strtolower($encoding)) {
+ return new Encoding\DechunkStream($stream);
+ }
+
+ if ('deflate' === strtolower($encoding)) {
+ return new Encoding\DecompressStream($stream);
+ }
+
+ if ('gzip' === strtolower($encoding)) {
+ return new Encoding\GzipDecodeStream($stream);
+ }
+
+ return false;
+ }
+}
diff --git a/vendor/php-http/client-common/src/Plugin/ErrorPlugin.php b/vendor/php-http/client-common/src/Plugin/ErrorPlugin.php
new file mode 100644
index 00000000..bcc1c676
--- /dev/null
+++ b/vendor/php-http/client-common/src/Plugin/ErrorPlugin.php
@@ -0,0 +1,81 @@
+
+ */
+final class ErrorPlugin implements Plugin
+{
+ /**
+ * @var bool Whether this plugin should only throw 5XX Exceptions (default to false).
+ *
+ * If set to true 4XX Responses code will never throw an exception
+ */
+ private $onlyServerException;
+
+ /**
+ * @param array $config {
+ *
+ * @var bool only_server_exception Whether this plugin should only throw 5XX Exceptions (default to false).
+ * }
+ */
+ public function __construct(array $config = [])
+ {
+ $resolver = new OptionsResolver();
+ $resolver->setDefaults([
+ 'only_server_exception' => false,
+ ]);
+ $resolver->setAllowedTypes('only_server_exception', 'bool');
+ $options = $resolver->resolve($config);
+
+ $this->onlyServerException = $options['only_server_exception'];
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function handleRequest(RequestInterface $request, callable $next, callable $first)
+ {
+ $promise = $next($request);
+
+ return $promise->then(function (ResponseInterface $response) use ($request) {
+ return $this->transformResponseToException($request, $response);
+ });
+ }
+
+ /**
+ * Transform response to an error if possible.
+ *
+ * @param RequestInterface $request Request of the call
+ * @param ResponseInterface $response Response of the call
+ *
+ * @throws ClientErrorException If response status code is a 4xx
+ * @throws ServerErrorException If response status code is a 5xx
+ *
+ * @return ResponseInterface If status code is not in 4xx or 5xx return response
+ */
+ protected function transformResponseToException(RequestInterface $request, ResponseInterface $response)
+ {
+ if (!$this->onlyServerException && $response->getStatusCode() >= 400 && $response->getStatusCode() < 500) {
+ throw new ClientErrorException($response->getReasonPhrase(), $request, $response);
+ }
+
+ if ($response->getStatusCode() >= 500 && $response->getStatusCode() < 600) {
+ throw new ServerErrorException($response->getReasonPhrase(), $request, $response);
+ }
+
+ return $response;
+ }
+}
diff --git a/vendor/php-http/client-common/src/Plugin/HeaderAppendPlugin.php b/vendor/php-http/client-common/src/Plugin/HeaderAppendPlugin.php
new file mode 100644
index 00000000..26fd8134
--- /dev/null
+++ b/vendor/php-http/client-common/src/Plugin/HeaderAppendPlugin.php
@@ -0,0 +1,45 @@
+
+ */
+final class HeaderAppendPlugin implements Plugin
+{
+ /**
+ * @var array
+ */
+ private $headers = [];
+
+ /**
+ * @param array $headers Hashmap of header name to header value
+ */
+ public function __construct(array $headers)
+ {
+ $this->headers = $headers;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function handleRequest(RequestInterface $request, callable $next, callable $first)
+ {
+ foreach ($this->headers as $header => $headerValue) {
+ $request = $request->withAddedHeader($header, $headerValue);
+ }
+
+ return $next($request);
+ }
+}
diff --git a/vendor/php-http/client-common/src/Plugin/HeaderDefaultsPlugin.php b/vendor/php-http/client-common/src/Plugin/HeaderDefaultsPlugin.php
new file mode 100644
index 00000000..6dfc1115
--- /dev/null
+++ b/vendor/php-http/client-common/src/Plugin/HeaderDefaultsPlugin.php
@@ -0,0 +1,43 @@
+
+ */
+final class HeaderDefaultsPlugin implements Plugin
+{
+ /**
+ * @var array
+ */
+ private $headers = [];
+
+ /**
+ * @param array $headers Hashmap of header name to header value
+ */
+ public function __construct(array $headers)
+ {
+ $this->headers = $headers;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function handleRequest(RequestInterface $request, callable $next, callable $first)
+ {
+ foreach ($this->headers as $header => $headerValue) {
+ if (!$request->hasHeader($header)) {
+ $request = $request->withHeader($header, $headerValue);
+ }
+ }
+
+ return $next($request);
+ }
+}
diff --git a/vendor/php-http/client-common/src/Plugin/HeaderRemovePlugin.php b/vendor/php-http/client-common/src/Plugin/HeaderRemovePlugin.php
new file mode 100644
index 00000000..fc9c19d1
--- /dev/null
+++ b/vendor/php-http/client-common/src/Plugin/HeaderRemovePlugin.php
@@ -0,0 +1,41 @@
+
+ */
+final class HeaderRemovePlugin implements Plugin
+{
+ /**
+ * @var array
+ */
+ private $headers = [];
+
+ /**
+ * @param array $headers List of header names to remove from the request
+ */
+ public function __construct(array $headers)
+ {
+ $this->headers = $headers;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function handleRequest(RequestInterface $request, callable $next, callable $first)
+ {
+ foreach ($this->headers as $header) {
+ if ($request->hasHeader($header)) {
+ $request = $request->withoutHeader($header);
+ }
+ }
+
+ return $next($request);
+ }
+}
diff --git a/vendor/php-http/client-common/src/Plugin/HeaderSetPlugin.php b/vendor/php-http/client-common/src/Plugin/HeaderSetPlugin.php
new file mode 100644
index 00000000..75f11d40
--- /dev/null
+++ b/vendor/php-http/client-common/src/Plugin/HeaderSetPlugin.php
@@ -0,0 +1,41 @@
+
+ */
+final class HeaderSetPlugin implements Plugin
+{
+ /**
+ * @var array
+ */
+ private $headers = [];
+
+ /**
+ * @param array $headers Hashmap of header name to header value
+ */
+ public function __construct(array $headers)
+ {
+ $this->headers = $headers;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function handleRequest(RequestInterface $request, callable $next, callable $first)
+ {
+ foreach ($this->headers as $header => $headerValue) {
+ $request = $request->withHeader($header, $headerValue);
+ }
+
+ return $next($request);
+ }
+}
diff --git a/vendor/php-http/client-common/src/Plugin/HistoryPlugin.php b/vendor/php-http/client-common/src/Plugin/HistoryPlugin.php
new file mode 100644
index 00000000..5abddbd8
--- /dev/null
+++ b/vendor/php-http/client-common/src/Plugin/HistoryPlugin.php
@@ -0,0 +1,49 @@
+
+ */
+final class HistoryPlugin implements Plugin
+{
+ /**
+ * Journal use to store request / responses / exception.
+ *
+ * @var Journal
+ */
+ private $journal;
+
+ /**
+ * @param Journal $journal
+ */
+ public function __construct(Journal $journal)
+ {
+ $this->journal = $journal;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function handleRequest(RequestInterface $request, callable $next, callable $first)
+ {
+ $journal = $this->journal;
+
+ return $next($request)->then(function (ResponseInterface $response) use ($request, $journal) {
+ $journal->addSuccess($request, $response);
+
+ return $response;
+ }, function (Exception $exception) use ($request, $journal) {
+ $journal->addFailure($request, $exception);
+
+ throw $exception;
+ });
+ }
+}
diff --git a/vendor/php-http/client-common/src/Plugin/Journal.php b/vendor/php-http/client-common/src/Plugin/Journal.php
new file mode 100644
index 00000000..15f30956
--- /dev/null
+++ b/vendor/php-http/client-common/src/Plugin/Journal.php
@@ -0,0 +1,31 @@
+
+ */
+interface Journal
+{
+ /**
+ * Record a successful call.
+ *
+ * @param RequestInterface $request Request use to make the call
+ * @param ResponseInterface $response Response returned by the call
+ */
+ public function addSuccess(RequestInterface $request, ResponseInterface $response);
+
+ /**
+ * Record a failed call.
+ *
+ * @param RequestInterface $request Request use to make the call
+ * @param Exception $exception Exception returned by the call
+ */
+ public function addFailure(RequestInterface $request, Exception $exception);
+}
diff --git a/vendor/php-http/client-common/src/Plugin/QueryDefaultsPlugin.php b/vendor/php-http/client-common/src/Plugin/QueryDefaultsPlugin.php
new file mode 100644
index 00000000..d9c06d6b
--- /dev/null
+++ b/vendor/php-http/client-common/src/Plugin/QueryDefaultsPlugin.php
@@ -0,0 +1,47 @@
+
+ */
+final class QueryDefaultsPlugin implements Plugin
+{
+ /**
+ * @var array
+ */
+ private $queryParams = [];
+
+ /**
+ * @param array $queryParams Hashmap of query name to query value. Names and values must not be url encoded as
+ * this plugin will encode them
+ */
+ public function __construct(array $queryParams)
+ {
+ $this->queryParams = $queryParams;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function handleRequest(RequestInterface $request, callable $next, callable $first)
+ {
+ $uri = $request->getUri();
+
+ parse_str($uri->getQuery(), $query);
+ $query += $this->queryParams;
+
+ $request = $request->withUri(
+ $uri->withQuery(http_build_query($query))
+ );
+
+ return $next($request);
+ }
+}
diff --git a/vendor/php-http/client-common/src/Plugin/RedirectPlugin.php b/vendor/php-http/client-common/src/Plugin/RedirectPlugin.php
new file mode 100644
index 00000000..d2f442ed
--- /dev/null
+++ b/vendor/php-http/client-common/src/Plugin/RedirectPlugin.php
@@ -0,0 +1,270 @@
+
+ */
+class RedirectPlugin implements Plugin
+{
+ /**
+ * Rule on how to redirect, change method for the new request.
+ *
+ * @var array
+ */
+ protected $redirectCodes = [
+ 300 => [
+ 'switch' => [
+ 'unless' => ['GET', 'HEAD'],
+ 'to' => 'GET',
+ ],
+ 'multiple' => true,
+ 'permanent' => false,
+ ],
+ 301 => [
+ 'switch' => [
+ 'unless' => ['GET', 'HEAD'],
+ 'to' => 'GET',
+ ],
+ 'multiple' => false,
+ 'permanent' => true,
+ ],
+ 302 => [
+ 'switch' => [
+ 'unless' => ['GET', 'HEAD'],
+ 'to' => 'GET',
+ ],
+ 'multiple' => false,
+ 'permanent' => false,
+ ],
+ 303 => [
+ 'switch' => [
+ 'unless' => ['GET', 'HEAD'],
+ 'to' => 'GET',
+ ],
+ 'multiple' => false,
+ 'permanent' => false,
+ ],
+ 307 => [
+ 'switch' => false,
+ 'multiple' => false,
+ 'permanent' => false,
+ ],
+ 308 => [
+ 'switch' => false,
+ 'multiple' => false,
+ 'permanent' => true,
+ ],
+ ];
+
+ /**
+ * Determine how header should be preserved from old request.
+ *
+ * @var bool|array
+ *
+ * true will keep all previous headers (default value)
+ * false will ditch all previous headers
+ * string[] will keep only headers with the specified names
+ */
+ protected $preserveHeader;
+
+ /**
+ * Store all previous redirect from 301 / 308 status code.
+ *
+ * @var array
+ */
+ protected $redirectStorage = [];
+
+ /**
+ * Whether the location header must be directly used for a multiple redirection status code (300).
+ *
+ * @var bool
+ */
+ protected $useDefaultForMultiple;
+
+ /**
+ * @var array
+ */
+ protected $circularDetection = [];
+
+ /**
+ * @param array $config {
+ *
+ * @var bool|string[] $preserve_header True keeps all headers, false remove all of them, an array is interpreted as a list of header names to keep
+ * @var bool $use_default_for_multiple Whether the location header must be directly used for a multiple redirection status code (300).
+ * }
+ */
+ public function __construct(array $config = [])
+ {
+ $resolver = new OptionsResolver();
+ $resolver->setDefaults([
+ 'preserve_header' => true,
+ 'use_default_for_multiple' => true,
+ ]);
+ $resolver->setAllowedTypes('preserve_header', ['bool', 'array']);
+ $resolver->setAllowedTypes('use_default_for_multiple', 'bool');
+ $resolver->setNormalizer('preserve_header', function (OptionsResolver $resolver, $value) {
+ if (is_bool($value) && false === $value) {
+ return [];
+ }
+
+ return $value;
+ });
+ $options = $resolver->resolve($config);
+
+ $this->preserveHeader = $options['preserve_header'];
+ $this->useDefaultForMultiple = $options['use_default_for_multiple'];
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function handleRequest(RequestInterface $request, callable $next, callable $first)
+ {
+ // Check in storage
+ if (array_key_exists((string) $request->getUri(), $this->redirectStorage)) {
+ $uri = $this->redirectStorage[(string) $request->getUri()]['uri'];
+ $statusCode = $this->redirectStorage[(string) $request->getUri()]['status'];
+ $redirectRequest = $this->buildRedirectRequest($request, $uri, $statusCode);
+
+ return $first($redirectRequest);
+ }
+
+ return $next($request)->then(function (ResponseInterface $response) use ($request, $first) {
+ $statusCode = $response->getStatusCode();
+
+ if (!array_key_exists($statusCode, $this->redirectCodes)) {
+ return $response;
+ }
+
+ $uri = $this->createUri($response, $request);
+ $redirectRequest = $this->buildRedirectRequest($request, $uri, $statusCode);
+ $chainIdentifier = spl_object_hash((object) $first);
+
+ if (!array_key_exists($chainIdentifier, $this->circularDetection)) {
+ $this->circularDetection[$chainIdentifier] = [];
+ }
+
+ $this->circularDetection[$chainIdentifier][] = (string) $request->getUri();
+
+ if (in_array((string) $redirectRequest->getUri(), $this->circularDetection[$chainIdentifier])) {
+ throw new CircularRedirectionException('Circular redirection detected', $request, $response);
+ }
+
+ if ($this->redirectCodes[$statusCode]['permanent']) {
+ $this->redirectStorage[(string) $request->getUri()] = [
+ 'uri' => $uri,
+ 'status' => $statusCode,
+ ];
+ }
+
+ // Call redirect request in synchrone
+ $redirectPromise = $first($redirectRequest);
+
+ return $redirectPromise->wait();
+ });
+ }
+
+ /**
+ * Builds the redirect request.
+ *
+ * @param RequestInterface $request Original request
+ * @param UriInterface $uri New uri
+ * @param int $statusCode Status code from the redirect response
+ *
+ * @return MessageInterface|RequestInterface
+ */
+ protected function buildRedirectRequest(RequestInterface $request, UriInterface $uri, $statusCode)
+ {
+ $request = $request->withUri($uri);
+
+ if (false !== $this->redirectCodes[$statusCode]['switch'] && !in_array($request->getMethod(), $this->redirectCodes[$statusCode]['switch']['unless'])) {
+ $request = $request->withMethod($this->redirectCodes[$statusCode]['switch']['to']);
+ }
+
+ if (is_array($this->preserveHeader)) {
+ $headers = array_keys($request->getHeaders());
+
+ foreach ($headers as $name) {
+ if (!in_array($name, $this->preserveHeader)) {
+ $request = $request->withoutHeader($name);
+ }
+ }
+ }
+
+ return $request;
+ }
+
+ /**
+ * Creates a new Uri from the old request and the location header.
+ *
+ * @param ResponseInterface $response The redirect response
+ * @param RequestInterface $request The original request
+ *
+ * @throws HttpException If location header is not usable (missing or incorrect)
+ * @throws MultipleRedirectionException If a 300 status code is received and default location cannot be resolved (doesn't use the location header or not present)
+ *
+ * @return UriInterface
+ */
+ private function createUri(ResponseInterface $response, RequestInterface $request)
+ {
+ if ($this->redirectCodes[$response->getStatusCode()]['multiple'] && (!$this->useDefaultForMultiple || !$response->hasHeader('Location'))) {
+ throw new MultipleRedirectionException('Cannot choose a redirection', $request, $response);
+ }
+
+ if (!$response->hasHeader('Location')) {
+ throw new HttpException('Redirect status code, but no location header present in the response', $request, $response);
+ }
+
+ $location = $response->getHeaderLine('Location');
+ $parsedLocation = parse_url($location);
+
+ if (false === $parsedLocation) {
+ throw new HttpException(sprintf('Location %s could not be parsed', $location), $request, $response);
+ }
+
+ $uri = $request->getUri();
+
+ if (array_key_exists('scheme', $parsedLocation)) {
+ $uri = $uri->withScheme($parsedLocation['scheme']);
+ }
+
+ if (array_key_exists('host', $parsedLocation)) {
+ $uri = $uri->withHost($parsedLocation['host']);
+ }
+
+ if (array_key_exists('port', $parsedLocation)) {
+ $uri = $uri->withPort($parsedLocation['port']);
+ }
+
+ if (array_key_exists('path', $parsedLocation)) {
+ $uri = $uri->withPath($parsedLocation['path']);
+ }
+
+ if (array_key_exists('query', $parsedLocation)) {
+ $uri = $uri->withQuery($parsedLocation['query']);
+ } else {
+ $uri = $uri->withQuery('');
+ }
+
+ if (array_key_exists('fragment', $parsedLocation)) {
+ $uri = $uri->withFragment($parsedLocation['fragment']);
+ } else {
+ $uri = $uri->withFragment('');
+ }
+
+ return $uri;
+ }
+}
diff --git a/vendor/php-http/client-common/src/Plugin/RequestMatcherPlugin.php b/vendor/php-http/client-common/src/Plugin/RequestMatcherPlugin.php
new file mode 100644
index 00000000..5f72b02d
--- /dev/null
+++ b/vendor/php-http/client-common/src/Plugin/RequestMatcherPlugin.php
@@ -0,0 +1,47 @@
+
+ */
+final class RequestMatcherPlugin implements Plugin
+{
+ /**
+ * @var RequestMatcher
+ */
+ private $requestMatcher;
+
+ /**
+ * @var Plugin
+ */
+ private $delegatedPlugin;
+
+ /**
+ * @param RequestMatcher $requestMatcher
+ * @param Plugin $delegatedPlugin
+ */
+ public function __construct(RequestMatcher $requestMatcher, Plugin $delegatedPlugin)
+ {
+ $this->requestMatcher = $requestMatcher;
+ $this->delegatedPlugin = $delegatedPlugin;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function handleRequest(RequestInterface $request, callable $next, callable $first)
+ {
+ if ($this->requestMatcher->matches($request)) {
+ return $this->delegatedPlugin->handleRequest($request, $next, $first);
+ }
+
+ return $next($request);
+ }
+}
diff --git a/vendor/php-http/client-common/src/Plugin/RetryPlugin.php b/vendor/php-http/client-common/src/Plugin/RetryPlugin.php
new file mode 100644
index 00000000..3d09265f
--- /dev/null
+++ b/vendor/php-http/client-common/src/Plugin/RetryPlugin.php
@@ -0,0 +1,139 @@
+
+ */
+final class RetryPlugin implements Plugin
+{
+ /**
+ * Number of retry before sending an exception.
+ *
+ * @var int
+ */
+ private $retry;
+
+ /**
+ * @var callable
+ */
+ private $exceptionDelay;
+
+ /**
+ * @var callable
+ */
+ private $exceptionDecider;
+
+ /**
+ * Store the retry counter for each request.
+ *
+ * @var array
+ */
+ private $retryStorage = [];
+
+ /**
+ * @param array $config {
+ *
+ * @var int $retries Number of retries to attempt if an exception occurs before letting the exception bubble up.
+ * @var callable $exception_decider A callback that gets a request and an exception to decide after a failure whether the request should be retried.
+ * @var callable $exception_delay A callback that gets a request, an exception and the number of retries and returns how many microseconds we should wait before trying again.
+ * }
+ */
+ public function __construct(array $config = [])
+ {
+ if (array_key_exists('decider', $config)) {
+ if (array_key_exists('exception_decider', $config)) {
+ throw new \InvalidArgumentException('Do not set both the old "decider" and new "exception_decider" options');
+ }
+ trigger_error('The "decider" option has been deprecated in favour of "exception_decider"', E_USER_DEPRECATED);
+ $config['exception_decider'] = $config['decider'];
+ unset($config['decider']);
+ }
+ if (array_key_exists('delay', $config)) {
+ if (array_key_exists('exception_delay', $config)) {
+ throw new \InvalidArgumentException('Do not set both the old "delay" and new "exception_delay" options');
+ }
+ trigger_error('The "delay" option has been deprecated in favour of "exception_delay"', E_USER_DEPRECATED);
+ $config['exception_delay'] = $config['delay'];
+ unset($config['delay']);
+ }
+
+ $resolver = new OptionsResolver();
+ $resolver->setDefaults([
+ 'retries' => 1,
+ 'exception_decider' => function (RequestInterface $request, Exception $e) {
+ return true;
+ },
+ 'exception_delay' => __CLASS__.'::defaultDelay',
+ ]);
+ $resolver->setAllowedTypes('retries', 'int');
+ $resolver->setAllowedTypes('exception_decider', 'callable');
+ $resolver->setAllowedTypes('exception_delay', 'callable');
+ $options = $resolver->resolve($config);
+
+ $this->retry = $options['retries'];
+ $this->exceptionDecider = $options['exception_decider'];
+ $this->exceptionDelay = $options['exception_delay'];
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function handleRequest(RequestInterface $request, callable $next, callable $first)
+ {
+ $chainIdentifier = spl_object_hash((object) $first);
+
+ return $next($request)->then(function (ResponseInterface $response) use ($request, $chainIdentifier) {
+ if (array_key_exists($chainIdentifier, $this->retryStorage)) {
+ unset($this->retryStorage[$chainIdentifier]);
+ }
+
+ return $response;
+ }, function (Exception $exception) use ($request, $next, $first, $chainIdentifier) {
+ if (!array_key_exists($chainIdentifier, $this->retryStorage)) {
+ $this->retryStorage[$chainIdentifier] = 0;
+ }
+
+ if ($this->retryStorage[$chainIdentifier] >= $this->retry) {
+ unset($this->retryStorage[$chainIdentifier]);
+
+ throw $exception;
+ }
+
+ if (!call_user_func($this->exceptionDecider, $request, $exception)) {
+ throw $exception;
+ }
+
+ $time = call_user_func($this->exceptionDelay, $request, $exception, $this->retryStorage[$chainIdentifier]);
+ usleep($time);
+
+ // Retry in synchrone
+ ++$this->retryStorage[$chainIdentifier];
+ $promise = $this->handleRequest($request, $next, $first);
+
+ return $promise->wait();
+ });
+ }
+
+ /**
+ * @param RequestInterface $request
+ * @param Exception $e
+ * @param int $retries The number of retries we made before. First time this get called it will be 0.
+ *
+ * @return int
+ */
+ public static function defaultDelay(RequestInterface $request, Exception $e, $retries)
+ {
+ return pow(2, $retries) * 500000;
+ }
+}
diff --git a/vendor/php-http/client-common/src/Plugin/VersionBridgePlugin.php b/vendor/php-http/client-common/src/Plugin/VersionBridgePlugin.php
new file mode 100644
index 00000000..f3891e5c
--- /dev/null
+++ b/vendor/php-http/client-common/src/Plugin/VersionBridgePlugin.php
@@ -0,0 +1,21 @@
+
+ */
+trait VersionBridgePlugin
+{
+ abstract protected function doHandleRequest(RequestInterface $request, callable $next, callable $first);
+
+ public function handleRequest(RequestInterface $request, callable $next, callable $first)
+ {
+ return $this->doHandleRequest($request, $next, $first);
+ }
+}
diff --git a/vendor/php-http/client-common/src/PluginClient.php b/vendor/php-http/client-common/src/PluginClient.php
new file mode 100644
index 00000000..8cedcf65
--- /dev/null
+++ b/vendor/php-http/client-common/src/PluginClient.php
@@ -0,0 +1,180 @@
+
+ */
+final class PluginClient implements HttpClient, HttpAsyncClient
+{
+ /**
+ * An HTTP async client.
+ *
+ * @var HttpAsyncClient
+ */
+ private $client;
+
+ /**
+ * The plugin chain.
+ *
+ * @var Plugin[]
+ */
+ private $plugins;
+
+ /**
+ * A list of options.
+ *
+ * @var array
+ */
+ private $options;
+
+ /**
+ * @param HttpClient|HttpAsyncClient $client
+ * @param Plugin[] $plugins
+ * @param array $options {
+ *
+ * @var int $max_restarts
+ * @var Plugin[] $debug_plugins an array of plugins that are injected between each normal plugin
+ * }
+ *
+ * @throws \RuntimeException if client is not an instance of HttpClient or HttpAsyncClient
+ */
+ public function __construct($client, array $plugins = [], array $options = [])
+ {
+ if ($client instanceof HttpAsyncClient) {
+ $this->client = $client;
+ } elseif ($client instanceof HttpClient || $client instanceof ClientInterface) {
+ $this->client = new EmulatedHttpAsyncClient($client);
+ } else {
+ throw new \RuntimeException('Client must be an instance of Http\\Client\\HttpClient or Http\\Client\\HttpAsyncClient');
+ }
+
+ $this->plugins = $plugins;
+ $this->options = $this->configure($options);
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function sendRequest(RequestInterface $request)
+ {
+ // If we don't have an http client, use the async call
+ if (!($this->client instanceof HttpClient)) {
+ return $this->sendAsyncRequest($request)->wait();
+ }
+
+ // Else we want to use the synchronous call of the underlying client, and not the async one in the case
+ // we have both an async and sync call
+ $pluginChain = $this->createPluginChain($this->plugins, function (RequestInterface $request) {
+ try {
+ return new HttpFulfilledPromise($this->client->sendRequest($request));
+ } catch (HttplugException $exception) {
+ return new HttpRejectedPromise($exception);
+ }
+ });
+
+ return $pluginChain($request)->wait();
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function sendAsyncRequest(RequestInterface $request)
+ {
+ $pluginChain = $this->createPluginChain($this->plugins, function (RequestInterface $request) {
+ return $this->client->sendAsyncRequest($request);
+ });
+
+ return $pluginChain($request);
+ }
+
+ /**
+ * Configure the plugin client.
+ *
+ * @param array $options
+ *
+ * @return array
+ */
+ private function configure(array $options = [])
+ {
+ if (isset($options['debug_plugins'])) {
+ @trigger_error('The "debug_plugins" option is deprecated since 1.5 and will be removed in 2.0.', E_USER_DEPRECATED);
+ }
+
+ $resolver = new OptionsResolver();
+ $resolver->setDefaults([
+ 'max_restarts' => 10,
+ 'debug_plugins' => [],
+ ]);
+
+ $resolver
+ ->setAllowedTypes('debug_plugins', 'array')
+ ->setAllowedValues('debug_plugins', function (array $plugins) {
+ foreach ($plugins as $plugin) {
+ // Make sure each object passed with the `debug_plugins` is an instance of Plugin.
+ if (!$plugin instanceof Plugin) {
+ return false;
+ }
+ }
+
+ return true;
+ });
+
+ return $resolver->resolve($options);
+ }
+
+ /**
+ * Create the plugin chain.
+ *
+ * @param Plugin[] $pluginList A list of plugins
+ * @param callable $clientCallable Callable making the HTTP call
+ *
+ * @return callable
+ */
+ private function createPluginChain($pluginList, callable $clientCallable)
+ {
+ $firstCallable = $lastCallable = $clientCallable;
+
+ /*
+ * Inject debug plugins between each plugin.
+ */
+ $pluginListWithDebug = $this->options['debug_plugins'];
+ foreach ($pluginList as $plugin) {
+ $pluginListWithDebug[] = $plugin;
+ $pluginListWithDebug = array_merge($pluginListWithDebug, $this->options['debug_plugins']);
+ }
+
+ while ($plugin = array_pop($pluginListWithDebug)) {
+ $lastCallable = function (RequestInterface $request) use ($plugin, $lastCallable, &$firstCallable) {
+ return $plugin->handleRequest($request, $lastCallable, $firstCallable);
+ };
+
+ $firstCallable = $lastCallable;
+ }
+
+ $firstCalls = 0;
+ $firstCallable = function (RequestInterface $request) use ($lastCallable, &$firstCalls) {
+ if ($firstCalls > $this->options['max_restarts']) {
+ throw new LoopException('Too many restarts in plugin client', $request);
+ }
+
+ ++$firstCalls;
+
+ return $lastCallable($request);
+ };
+
+ return $firstCallable;
+ }
+}
diff --git a/vendor/php-http/client-common/src/PluginClientFactory.php b/vendor/php-http/client-common/src/PluginClientFactory.php
new file mode 100644
index 00000000..cbea3e13
--- /dev/null
+++ b/vendor/php-http/client-common/src/PluginClientFactory.php
@@ -0,0 +1,63 @@
+
+ */
+final class PluginClientFactory
+{
+ /**
+ * @var callable
+ */
+ private static $factory;
+
+ /**
+ * Set the factory to use.
+ * The callable to provide must have the same arguments and return type as PluginClientFactory::createClient.
+ * This is used by the HTTPlugBundle to provide a better Symfony integration.
+ * Unlike the createClient method, this one is static to allow zero configuration profiling by hooking into early
+ * application execution.
+ *
+ * @internal
+ *
+ * @param callable $factory
+ */
+ public static function setFactory(callable $factory)
+ {
+ static::$factory = $factory;
+ }
+
+ /**
+ * @param HttpClient|HttpAsyncClient|ClientInterface $client
+ * @param Plugin[] $plugins
+ * @param array $options {
+ *
+ * @var string $client_name to give client a name which may be used when displaying client information like in
+ * the HTTPlugBundle profiler.
+ * }
+ *
+ * @see PluginClient constructor for PluginClient specific $options.
+ *
+ * @return PluginClient
+ */
+ public function createClient($client, array $plugins = [], array $options = [])
+ {
+ if (static::$factory) {
+ $factory = static::$factory;
+
+ return $factory($client, $plugins, $options);
+ }
+
+ unset($options['client_name']);
+
+ return new PluginClient($client, $plugins, $options);
+ }
+}
diff --git a/vendor/php-http/client-common/src/VersionBridgeClient.php b/vendor/php-http/client-common/src/VersionBridgeClient.php
new file mode 100644
index 00000000..048eeae3
--- /dev/null
+++ b/vendor/php-http/client-common/src/VersionBridgeClient.php
@@ -0,0 +1,21 @@
+
+ */
+trait VersionBridgeClient
+{
+ abstract protected function doSendRequest(RequestInterface $request);
+
+ public function sendRequest(RequestInterface $request)
+ {
+ return $this->doSendRequest($request);
+ }
+}
diff --git a/vendor/php-http/discovery/CHANGELOG.md b/vendor/php-http/discovery/CHANGELOG.md
new file mode 100644
index 00000000..ee43ffd2
--- /dev/null
+++ b/vendor/php-http/discovery/CHANGELOG.md
@@ -0,0 +1,221 @@
+# Change Log
+
+## 1.5.2 - 2018-12-31
+
+Corrected mistakes in 1.5.1. The different between 1.5.2 and 1.5.0 is that
+we removed some PHP 7 code.
+
+## 1.5.1 - 2018-12-31
+
+This version added new features by mistake. These are reverted in 1.5.2.
+
+Do not use 1.5.1.
+
+### Fixed
+
+- Removed PHP 7 code
+
+## 1.5.0 - 2018-12-30
+
+### Added
+
+- Support for `nyholm/psr7` version 1.0.
+- `ClassDiscovery::safeClassExists` which will help Magento users.
+- Support for HTTPlug 2.0
+- Support for Buzz 1.0
+- Better error message when nothing found by introducing a new exception: `NoCandidateFoundException`.
+
+### Fixed
+
+- Fixed condition evaluation, it should stop after first invalid condition.
+
+## 1.4.0 - 2018-02-06
+
+### Added
+
+- Discovery support for nyholm/psr7
+
+## 1.3.0 - 2017-08-03
+
+### Added
+
+- Discovery support for CakePHP adapter
+- Discovery support for Zend adapter
+- Discovery support for Artax adapter
+
+## 1.2.1 - 2017-03-02
+
+### Fixed
+
+- Fixed minor issue with `MockClientStrategy`, also added more tests.
+
+## 1.2.0 - 2017-02-12
+
+### Added
+
+- MockClientStrategy class.
+
+## 1.1.1 - 2016-11-27
+
+### Changed
+
+- Made exception messages clearer. `StrategyUnavailableException` is no longer the previous exception to `DiscoveryFailedException`.
+- `CommonClassesStrategy` is using `self` instead of `static`. Using `static` makes no sense when `CommonClassesStrategy` is final.
+
+## 1.1.0 - 2016-10-20
+
+### Added
+
+- Discovery support for Slim Framework factories
+
+## 1.0.0 - 2016-07-18
+
+### Added
+
+- Added back `Http\Discovery\NotFoundException` to preserve BC with 0.8 version. You may upgrade from 0.8.x and 0.9.x to 1.0.0 without any BC breaks.
+- Added interface `Http\Discovery\Exception` which is implemented by all our exceptions
+
+### Changed
+
+- Puli strategy renamed to Puli Beta strategy to prevent incompatibility with a future Puli stable
+
+### Deprecated
+
+- For BC reasons, the old `Http\Discovery\NotFoundException` (extending the new exception) will be thrown until version 2.0
+
+
+## 0.9.1 - 2016-06-28
+
+### Changed
+
+- Dropping PHP 5.4 support because we use the ::class constant.
+
+
+## 0.9.0 - 2016-06-25
+
+### Added
+
+- Discovery strategies to find classes
+
+### Changed
+
+- [Puli](http://puli.io) made optional
+- Improved exceptions
+- **[BC] `NotFoundException` moved to `Http\Discovery\Exception\NotFoundException`**
+
+
+## 0.8.0 - 2016-02-11
+
+### Changed
+
+- Puli composer plugin must be installed separately
+
+
+## 0.7.0 - 2016-01-15
+
+### Added
+
+- Temporary puli.phar (Beta 10) executable
+
+### Changed
+
+- Updated HTTPlug dependencies
+- Updated Puli dependencies
+- Local configuration to make tests passing
+
+### Removed
+
+- Puli CLI dependency
+
+
+## 0.6.4 - 2016-01-07
+
+### Fixed
+
+- Puli [not working](https://twitter.com/PuliPHP/status/685132540588507137) with the latest json-schema
+
+
+## 0.6.3 - 2016-01-04
+
+### Changed
+
+- Adjust Puli dependencies
+
+
+## 0.6.2 - 2016-01-04
+
+### Changed
+
+- Make Puli CLI a requirement
+
+
+## 0.6.1 - 2016-01-03
+
+### Changed
+
+- More flexible Puli requirement
+
+
+## 0.6.0 - 2015-12-30
+
+### Changed
+
+- Use [Puli](http://puli.io) for discovery
+- Improved exception messages
+
+
+## 0.5.0 - 2015-12-25
+
+### Changed
+
+- Updated message factory dependency (php-http/message)
+
+
+## 0.4.0 - 2015-12-17
+
+### Added
+
+- Array condition evaluation in the Class Discovery
+
+### Removed
+
+- Message factories (moved to php-http/utils)
+
+
+## 0.3.0 - 2015-11-18
+
+### Added
+
+- HTTP Async Client Discovery
+- Stream factories
+
+### Changed
+
+- Discoveries and Factories are final
+- Message and Uri factories have the type in their names
+- Diactoros Message factory uses Stream factory internally
+
+### Fixed
+
+- Improved docblocks for API documentation generation
+
+
+## 0.2.0 - 2015-10-31
+
+### Changed
+
+- Renamed AdapterDiscovery to ClientDiscovery
+
+
+## 0.1.1 - 2015-06-13
+
+### Fixed
+
+- Bad HTTP Adapter class name for Guzzle 5
+
+
+## 0.1.0 - 2015-06-12
+
+### Added
+
+- Initial release
diff --git a/vendor/php-http/discovery/LICENSE b/vendor/php-http/discovery/LICENSE
new file mode 100644
index 00000000..4558d6f0
--- /dev/null
+++ b/vendor/php-http/discovery/LICENSE
@@ -0,0 +1,19 @@
+Copyright (c) 2015-2016 PHP HTTP Team
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is furnished
+to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+THE SOFTWARE.
diff --git a/vendor/php-http/discovery/README.md b/vendor/php-http/discovery/README.md
new file mode 100644
index 00000000..7c5151ea
--- /dev/null
+++ b/vendor/php-http/discovery/README.md
@@ -0,0 +1,46 @@
+# HTTPlug Discovery
+
+[![Latest Version](https://img.shields.io/github/release/php-http/discovery.svg?style=flat-square)](https://github.com/php-http/discovery/releases)
+[![Software License](https://img.shields.io/badge/license-MIT-brightgreen.svg?style=flat-square)](LICENSE)
+[![Build Status](https://img.shields.io/travis/php-http/discovery.svg?style=flat-square)](https://travis-ci.org/php-http/discovery)
+[![Code Coverage](https://img.shields.io/scrutinizer/coverage/g/php-http/discovery.svg?style=flat-square)](https://scrutinizer-ci.com/g/php-http/discovery)
+[![Quality Score](https://img.shields.io/scrutinizer/g/php-http/discovery.svg?style=flat-square)](https://scrutinizer-ci.com/g/php-http/discovery)
+[![Total Downloads](https://img.shields.io/packagist/dt/php-http/discovery.svg?style=flat-square)](https://packagist.org/packages/php-http/discovery)
+
+**Finds installed HTTPlug implementations and PSR-7 message factories.**
+
+
+## Install
+
+Via Composer
+
+``` bash
+$ composer require php-http/discovery
+```
+
+
+## Documentation
+
+Please see the [official documentation](http://php-http.readthedocs.org/en/latest/discovery.html).
+
+
+## Testing
+
+``` bash
+$ composer test
+```
+
+
+## Contributing
+
+Please see our [contributing guide](http://docs.php-http.org/en/latest/development/contributing.html).
+
+
+## Security
+
+If you discover any security related issues, please contact us at [security@php-http.org](mailto:security@php-http.org).
+
+
+## License
+
+The MIT License (MIT). Please see [License File](LICENSE) for more information.
diff --git a/vendor/php-http/discovery/composer.json b/vendor/php-http/discovery/composer.json
new file mode 100644
index 00000000..97141f3b
--- /dev/null
+++ b/vendor/php-http/discovery/composer.json
@@ -0,0 +1,51 @@
+{
+ "name": "php-http/discovery",
+ "description": "Finds installed HTTPlug implementations and PSR-7 message factories",
+ "license": "MIT",
+ "keywords": ["http", "discovery", "client", "adapter", "message", "factory", "psr7"],
+ "homepage": "http://php-http.org",
+ "authors": [
+ {
+ "name": "Márk Sági-Kazár",
+ "email": "mark.sagikazar@gmail.com"
+ }
+ ],
+ "require": {
+ "php": "^5.5 || ^7.0"
+ },
+ "require-dev": {
+ "php-http/httplug": "^1.0|^2.0",
+ "php-http/message-factory": "^1.0",
+ "puli/composer-plugin": "1.0.0-beta10",
+ "phpspec/phpspec": "^2.4",
+ "henrikbjorn/phpspec-code-coverage" : "^2.0.2"
+ },
+ "suggest": {
+ "puli/composer-plugin": "Sets up Puli which is recommended for Discovery to work. Check http://docs.php-http.org/en/latest/discovery.html for more details.",
+ "php-http/message": "Allow to use Guzzle, Diactoros or Slim Framework factories"
+ },
+ "autoload": {
+ "psr-4": {
+ "Http\\Discovery\\": "src/"
+ }
+ },
+ "autoload-dev": {
+ "psr-4": {
+ "spec\\Http\\Discovery\\": "spec/"
+ }
+ },
+ "scripts": {
+ "test": "vendor/bin/phpspec run",
+ "test-ci": "vendor/bin/phpspec run -c phpspec.ci.yml"
+ },
+ "extra": {
+ "branch-alias": {
+ "dev-master": "1.5-dev"
+ }
+ },
+ "conflict": {
+ "nyholm/psr7": "<1.0"
+ },
+ "prefer-stable": true,
+ "minimum-stability": "beta"
+}
diff --git a/vendor/php-http/discovery/src/ClassDiscovery.php b/vendor/php-http/discovery/src/ClassDiscovery.php
new file mode 100644
index 00000000..40e2d1c5
--- /dev/null
+++ b/vendor/php-http/discovery/src/ClassDiscovery.php
@@ -0,0 +1,235 @@
+
+ * @author Márk Sági-Kazár
+ * @author Tobias Nyholm
+ */
+abstract class ClassDiscovery
+{
+ /**
+ * A list of strategies to find classes.
+ *
+ * @var array
+ */
+ private static $strategies = [
+ Strategy\PuliBetaStrategy::class,
+ Strategy\CommonClassesStrategy::class,
+ ];
+
+ /**
+ * Discovery cache to make the second time we use discovery faster.
+ *
+ * @var array
+ */
+ private static $cache = [];
+
+ /**
+ * Finds a class.
+ *
+ * @param string $type
+ *
+ * @return string|\Closure
+ *
+ * @throws DiscoveryFailedException
+ */
+ protected static function findOneByType($type)
+ {
+ // Look in the cache
+ if (null !== ($class = self::getFromCache($type))) {
+ return $class;
+ }
+
+ $exceptions = [];
+ foreach (self::$strategies as $strategy) {
+ try {
+ $candidates = call_user_func($strategy.'::getCandidates', $type);
+ } catch (StrategyUnavailableException $e) {
+ $exceptions[] = $e;
+
+ continue;
+ }
+
+ foreach ($candidates as $candidate) {
+ if (isset($candidate['condition'])) {
+ if (!self::evaluateCondition($candidate['condition'])) {
+ continue;
+ }
+ }
+
+ // save the result for later use
+ self::storeInCache($type, $candidate);
+
+ return $candidate['class'];
+ }
+
+ $exceptions[] = new NoCandidateFoundException($strategy, $candidates);
+ }
+
+ throw DiscoveryFailedException::create($exceptions);
+ }
+
+ /**
+ * Get a value from cache.
+ *
+ * @param string $type
+ *
+ * @return string|null
+ */
+ private static function getFromCache($type)
+ {
+ if (!isset(self::$cache[$type])) {
+ return;
+ }
+
+ $candidate = self::$cache[$type];
+ if (isset($candidate['condition'])) {
+ if (!self::evaluateCondition($candidate['condition'])) {
+ return;
+ }
+ }
+
+ return $candidate['class'];
+ }
+
+ /**
+ * Store a value in cache.
+ *
+ * @param string $type
+ * @param string $class
+ */
+ private static function storeInCache($type, $class)
+ {
+ self::$cache[$type] = $class;
+ }
+
+ /**
+ * Set new strategies and clear the cache.
+ *
+ * @param array $strategies string array of fully qualified class name to a DiscoveryStrategy
+ */
+ public static function setStrategies(array $strategies)
+ {
+ self::$strategies = $strategies;
+ self::clearCache();
+ }
+
+ /**
+ * Append a strategy at the end of the strategy queue.
+ *
+ * @param string $strategy Fully qualified class name to a DiscoveryStrategy
+ */
+ public static function appendStrategy($strategy)
+ {
+ self::$strategies[] = $strategy;
+ self::clearCache();
+ }
+
+ /**
+ * Prepend a strategy at the beginning of the strategy queue.
+ *
+ * @param string $strategy Fully qualified class name to a DiscoveryStrategy
+ */
+ public static function prependStrategy($strategy)
+ {
+ array_unshift(self::$strategies, $strategy);
+ self::clearCache();
+ }
+
+ /**
+ * Clear the cache.
+ */
+ public static function clearCache()
+ {
+ self::$cache = [];
+ }
+
+ /**
+ * Evaluates conditions to boolean.
+ *
+ * @param mixed $condition
+ *
+ * @return bool
+ */
+ protected static function evaluateCondition($condition)
+ {
+ if (is_string($condition)) {
+ // Should be extended for functions, extensions???
+ return self::safeClassExists($condition);
+ }
+ if (is_callable($condition)) {
+ return (bool) $condition();
+ }
+ if (is_bool($condition)) {
+ return $condition;
+ }
+ if (is_array($condition)) {
+ foreach ($condition as $c) {
+ if (false === static::evaluateCondition($c)) {
+ // Immediately stop execution if the condition is false
+ return false;
+ }
+ }
+
+ return true;
+ }
+
+ return false;
+ }
+
+ /**
+ * Get an instance of the $class.
+ *
+ * @param string|\Closure $class A FQCN of a class or a closure that instantiate the class.
+ *
+ * @return object
+ *
+ * @throws ClassInstantiationFailedException
+ */
+ protected static function instantiateClass($class)
+ {
+ try {
+ if (is_string($class)) {
+ return new $class();
+ }
+
+ if (is_callable($class)) {
+ return $class();
+ }
+ } catch (\Exception $e) {
+ throw new ClassInstantiationFailedException('Unexpected exception when instantiating class.', 0, $e);
+ }
+
+ throw new ClassInstantiationFailedException('Could not instantiate class because parameter is neither a callable nor a string');
+ }
+
+ /**
+ * We want to do a "safe" version of PHP's "class_exists" because Magento has a bug
+ * (or they call it a "feature"). Magento is throwing an exception if you do class_exists()
+ * on a class that ends with "Factory" and if that file does not exits.
+ *
+ * This function will catch all potential exceptions and make sure it returns a boolean.
+ *
+ * @param string $class
+ * @param bool $autoload
+ *
+ * @return bool
+ */
+ public static function safeClassExists($class)
+ {
+ try {
+ return class_exists($class);
+ } catch (\Exception $e) {
+ return false;
+ }
+ }
+}
diff --git a/vendor/php-http/discovery/src/Exception.php b/vendor/php-http/discovery/src/Exception.php
new file mode 100644
index 00000000..973c9087
--- /dev/null
+++ b/vendor/php-http/discovery/src/Exception.php
@@ -0,0 +1,12 @@
+
+ */
+interface Exception
+{
+}
diff --git a/vendor/php-http/discovery/src/Exception/ClassInstantiationFailedException.php b/vendor/php-http/discovery/src/Exception/ClassInstantiationFailedException.php
new file mode 100644
index 00000000..e95bf5d8
--- /dev/null
+++ b/vendor/php-http/discovery/src/Exception/ClassInstantiationFailedException.php
@@ -0,0 +1,14 @@
+
+ */
+final class ClassInstantiationFailedException extends \RuntimeException implements Exception
+{
+}
diff --git a/vendor/php-http/discovery/src/Exception/DiscoveryFailedException.php b/vendor/php-http/discovery/src/Exception/DiscoveryFailedException.php
new file mode 100644
index 00000000..304b7276
--- /dev/null
+++ b/vendor/php-http/discovery/src/Exception/DiscoveryFailedException.php
@@ -0,0 +1,51 @@
+
+ */
+final class DiscoveryFailedException extends \Exception implements Exception
+{
+ /**
+ * @var \Exception[]
+ */
+ private $exceptions;
+
+ /**
+ * @param string $message
+ * @param \Exception[] $exceptions
+ */
+ public function __construct($message, array $exceptions = [])
+ {
+ $this->exceptions = $exceptions;
+
+ parent::__construct($message);
+ }
+
+ /**
+ * @param \Exception[] $exceptions
+ */
+ public static function create($exceptions)
+ {
+ $message = 'Could not find resource using any discovery strategy. Find more information at http://docs.php-http.org/en/latest/discovery.html#common-errors';
+ foreach ($exceptions as $e) {
+ $message .= "\n - ".$e->getMessage();
+ }
+ $message .= "\n\n";
+
+ return new self($message, $exceptions);
+ }
+
+ /**
+ * @return \Exception[]
+ */
+ public function getExceptions()
+ {
+ return $this->exceptions;
+ }
+}
diff --git a/vendor/php-http/discovery/src/Exception/NoCandidateFoundException.php b/vendor/php-http/discovery/src/Exception/NoCandidateFoundException.php
new file mode 100644
index 00000000..240b5688
--- /dev/null
+++ b/vendor/php-http/discovery/src/Exception/NoCandidateFoundException.php
@@ -0,0 +1,35 @@
+
+ */
+final class NoCandidateFoundException extends \Exception implements Exception
+{
+ /**
+ * @param string $strategy
+ * @param array $candidates
+ */
+ public function __construct($strategy, array $candidates)
+ {
+ $classes = array_map(
+ function ($a) {
+ return $a['class'];
+ },
+ $candidates
+ );
+
+ $message = sprintf(
+ 'No valid candidate found using strategy "%s". We tested the following candidates: %s.',
+ $strategy,
+ implode(', ', $classes)
+ );
+
+ parent::__construct($message);
+ }
+}
diff --git a/vendor/php-http/discovery/src/Exception/NotFoundException.php b/vendor/php-http/discovery/src/Exception/NotFoundException.php
new file mode 100644
index 00000000..befbf488
--- /dev/null
+++ b/vendor/php-http/discovery/src/Exception/NotFoundException.php
@@ -0,0 +1,16 @@
+
+ */
+/*final */class NotFoundException extends \RuntimeException implements Exception
+{
+}
diff --git a/vendor/php-http/discovery/src/Exception/PuliUnavailableException.php b/vendor/php-http/discovery/src/Exception/PuliUnavailableException.php
new file mode 100644
index 00000000..a6ade733
--- /dev/null
+++ b/vendor/php-http/discovery/src/Exception/PuliUnavailableException.php
@@ -0,0 +1,12 @@
+
+ */
+final class PuliUnavailableException extends StrategyUnavailableException
+{
+}
diff --git a/vendor/php-http/discovery/src/Exception/StrategyUnavailableException.php b/vendor/php-http/discovery/src/Exception/StrategyUnavailableException.php
new file mode 100644
index 00000000..89ecf352
--- /dev/null
+++ b/vendor/php-http/discovery/src/Exception/StrategyUnavailableException.php
@@ -0,0 +1,15 @@
+
+ */
+class StrategyUnavailableException extends \RuntimeException implements Exception
+{
+}
diff --git a/vendor/php-http/discovery/src/HttpAsyncClientDiscovery.php b/vendor/php-http/discovery/src/HttpAsyncClientDiscovery.php
new file mode 100644
index 00000000..6ef60e70
--- /dev/null
+++ b/vendor/php-http/discovery/src/HttpAsyncClientDiscovery.php
@@ -0,0 +1,36 @@
+
+ */
+final class HttpAsyncClientDiscovery extends ClassDiscovery
+{
+ /**
+ * Finds an HTTP Async Client.
+ *
+ * @return HttpAsyncClient
+ *
+ * @throws Exception\NotFoundException
+ */
+ public static function find()
+ {
+ try {
+ $asyncClient = static::findOneByType(HttpAsyncClient::class);
+ } catch (DiscoveryFailedException $e) {
+ throw new NotFoundException(
+ 'No HTTPlug async clients found. Make sure to install a package providing "php-http/async-client-implementation". Example: "php-http/guzzle6-adapter".',
+ 0,
+ $e
+ );
+ }
+
+ return static::instantiateClass($asyncClient);
+ }
+}
diff --git a/vendor/php-http/discovery/src/HttpClientDiscovery.php b/vendor/php-http/discovery/src/HttpClientDiscovery.php
new file mode 100644
index 00000000..2654b7ed
--- /dev/null
+++ b/vendor/php-http/discovery/src/HttpClientDiscovery.php
@@ -0,0 +1,36 @@
+
+ */
+final class HttpClientDiscovery extends ClassDiscovery
+{
+ /**
+ * Finds an HTTP Client.
+ *
+ * @return HttpClient
+ *
+ * @throws Exception\NotFoundException
+ */
+ public static function find()
+ {
+ try {
+ $client = static::findOneByType(HttpClient::class);
+ } catch (DiscoveryFailedException $e) {
+ throw new NotFoundException(
+ 'No HTTPlug clients found. Make sure to install a package providing "php-http/client-implementation". Example: "php-http/guzzle6-adapter".',
+ 0,
+ $e
+ );
+ }
+
+ return static::instantiateClass($client);
+ }
+}
diff --git a/vendor/php-http/discovery/src/MessageFactoryDiscovery.php b/vendor/php-http/discovery/src/MessageFactoryDiscovery.php
new file mode 100644
index 00000000..c21b9bf4
--- /dev/null
+++ b/vendor/php-http/discovery/src/MessageFactoryDiscovery.php
@@ -0,0 +1,36 @@
+
+ */
+final class MessageFactoryDiscovery extends ClassDiscovery
+{
+ /**
+ * Finds a Message Factory.
+ *
+ * @return MessageFactory
+ *
+ * @throws Exception\NotFoundException
+ */
+ public static function find()
+ {
+ try {
+ $messageFactory = static::findOneByType(MessageFactory::class);
+ } catch (DiscoveryFailedException $e) {
+ throw new NotFoundException(
+ 'No message factories found. To use Guzzle, Diactoros or Slim Framework factories install php-http/message and the chosen message implementation.',
+ 0,
+ $e
+ );
+ }
+
+ return static::instantiateClass($messageFactory);
+ }
+}
diff --git a/vendor/php-http/discovery/src/NotFoundException.php b/vendor/php-http/discovery/src/NotFoundException.php
new file mode 100644
index 00000000..d59dadbf
--- /dev/null
+++ b/vendor/php-http/discovery/src/NotFoundException.php
@@ -0,0 +1,14 @@
+
+ *
+ * @deprecated since since version 1.0, and will be removed in 2.0. Use {@link \Http\Discovery\Exception\NotFoundException} instead.
+ */
+final class NotFoundException extends \Http\Discovery\Exception\NotFoundException
+{
+}
diff --git a/vendor/php-http/discovery/src/Strategy/CommonClassesStrategy.php b/vendor/php-http/discovery/src/Strategy/CommonClassesStrategy.php
new file mode 100644
index 00000000..eff2c0a6
--- /dev/null
+++ b/vendor/php-http/discovery/src/Strategy/CommonClassesStrategy.php
@@ -0,0 +1,101 @@
+
+ */
+final class CommonClassesStrategy implements DiscoveryStrategy
+{
+ /**
+ * @var array
+ */
+ private static $classes = [
+ MessageFactory::class => [
+ ['class' => NyholmHttplugFactory::class, 'condition' => [NyholmHttplugFactory::class]],
+ ['class' => GuzzleMessageFactory::class, 'condition' => [GuzzleRequest::class, GuzzleMessageFactory::class]],
+ ['class' => DiactorosMessageFactory::class, 'condition' => [DiactorosRequest::class, DiactorosMessageFactory::class]],
+ ['class' => SlimMessageFactory::class, 'condition' => [SlimRequest::class, SlimMessageFactory::class]],
+ ],
+ StreamFactory::class => [
+ ['class' => NyholmHttplugFactory::class, 'condition' => [NyholmHttplugFactory::class]],
+ ['class' => GuzzleStreamFactory::class, 'condition' => [GuzzleRequest::class, GuzzleStreamFactory::class]],
+ ['class' => DiactorosStreamFactory::class, 'condition' => [DiactorosRequest::class, DiactorosStreamFactory::class]],
+ ['class' => SlimStreamFactory::class, 'condition' => [SlimRequest::class, SlimStreamFactory::class]],
+ ],
+ UriFactory::class => [
+ ['class' => NyholmHttplugFactory::class, 'condition' => [NyholmHttplugFactory::class]],
+ ['class' => GuzzleUriFactory::class, 'condition' => [GuzzleRequest::class, GuzzleUriFactory::class]],
+ ['class' => DiactorosUriFactory::class, 'condition' => [DiactorosRequest::class, DiactorosUriFactory::class]],
+ ['class' => SlimUriFactory::class, 'condition' => [SlimRequest::class, SlimUriFactory::class]],
+ ],
+ HttpAsyncClient::class => [
+ ['class' => Guzzle6::class, 'condition' => Guzzle6::class],
+ ['class' => Curl::class, 'condition' => Curl::class],
+ ['class' => React::class, 'condition' => React::class],
+ ],
+ HttpClient::class => [
+ ['class' => Guzzle6::class, 'condition' => Guzzle6::class],
+ ['class' => Guzzle5::class, 'condition' => Guzzle5::class],
+ ['class' => Curl::class, 'condition' => Curl::class],
+ ['class' => Socket::class, 'condition' => Socket::class],
+ ['class' => Buzz::class, 'condition' => Buzz::class],
+ ['class' => React::class, 'condition' => React::class],
+ ['class' => Cake::class, 'condition' => Cake::class],
+ ['class' => Zend::class, 'condition' => Zend::class],
+ ['class' => Artax::class, 'condition' => Artax::class],
+ [
+ 'class' => [self::class, 'buzzInstantiate'],
+ 'condition' => [\Buzz\Client\FileGetContents::class, \Buzz\Message\ResponseBuilder::class],
+ ],
+ ],
+ ];
+
+ /**
+ * {@inheritdoc}
+ */
+ public static function getCandidates($type)
+ {
+ if (isset(self::$classes[$type])) {
+ return self::$classes[$type];
+ }
+
+ return [];
+ }
+
+ public static function buzzInstantiate()
+ {
+ return new \Buzz\Client\FileGetContents(MessageFactoryDiscovery::find());
+ }
+}
diff --git a/vendor/php-http/discovery/src/Strategy/DiscoveryStrategy.php b/vendor/php-http/discovery/src/Strategy/DiscoveryStrategy.php
new file mode 100644
index 00000000..641485a6
--- /dev/null
+++ b/vendor/php-http/discovery/src/Strategy/DiscoveryStrategy.php
@@ -0,0 +1,23 @@
+
+ */
+interface DiscoveryStrategy
+{
+ /**
+ * Find a resource of a specific type.
+ *
+ * @param string $type
+ *
+ * @return array The return value is always an array with zero or more elements. Each
+ * element is an array with two keys ['class' => string, 'condition' => mixed].
+ *
+ * @throws StrategyUnavailableException if we cannot use this strategy.
+ */
+ public static function getCandidates($type);
+}
diff --git a/vendor/php-http/discovery/src/Strategy/MockClientStrategy.php b/vendor/php-http/discovery/src/Strategy/MockClientStrategy.php
new file mode 100644
index 00000000..57447877
--- /dev/null
+++ b/vendor/php-http/discovery/src/Strategy/MockClientStrategy.php
@@ -0,0 +1,24 @@
+
+ */
+final class MockClientStrategy implements DiscoveryStrategy
+{
+ /**
+ * {@inheritdoc}
+ */
+ public static function getCandidates($type)
+ {
+ return (HttpClient::class === $type)
+ ? [['class' => Mock::class, 'condition' => Mock::class]]
+ : [];
+ }
+}
diff --git a/vendor/php-http/discovery/src/Strategy/PuliBetaStrategy.php b/vendor/php-http/discovery/src/Strategy/PuliBetaStrategy.php
new file mode 100644
index 00000000..c1e1fb7e
--- /dev/null
+++ b/vendor/php-http/discovery/src/Strategy/PuliBetaStrategy.php
@@ -0,0 +1,92 @@
+
+ * @author Márk Sági-Kazár
+ */
+class PuliBetaStrategy implements DiscoveryStrategy
+{
+ /**
+ * @var GeneratedPuliFactory
+ */
+ protected static $puliFactory;
+
+ /**
+ * @var Discovery
+ */
+ protected static $puliDiscovery;
+
+ /**
+ * @return GeneratedPuliFactory
+ *
+ * @throws PuliUnavailableException
+ */
+ private static function getPuliFactory()
+ {
+ if (null === self::$puliFactory) {
+ if (!defined('PULI_FACTORY_CLASS')) {
+ throw new PuliUnavailableException('Puli Factory is not available');
+ }
+
+ $puliFactoryClass = PULI_FACTORY_CLASS;
+
+ if (!ClassDiscovery::safeClassExists($puliFactoryClass)) {
+ throw new PuliUnavailableException('Puli Factory class does not exist');
+ }
+
+ self::$puliFactory = new $puliFactoryClass();
+ }
+
+ return self::$puliFactory;
+ }
+
+ /**
+ * Returns the Puli discovery layer.
+ *
+ * @return Discovery
+ *
+ * @throws PuliUnavailableException
+ */
+ private static function getPuliDiscovery()
+ {
+ if (!isset(self::$puliDiscovery)) {
+ $factory = self::getPuliFactory();
+ $repository = $factory->createRepository();
+
+ self::$puliDiscovery = $factory->createDiscovery($repository);
+ }
+
+ return self::$puliDiscovery;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public static function getCandidates($type)
+ {
+ $returnData = [];
+ $bindings = self::getPuliDiscovery()->findBindings($type);
+
+ foreach ($bindings as $binding) {
+ $condition = true;
+ if ($binding->hasParameterValue('depends')) {
+ $condition = $binding->getParameterValue('depends');
+ }
+ $returnData[] = ['class' => $binding->getClassName(), 'condition' => $condition];
+ }
+
+ return $returnData;
+ }
+}
diff --git a/vendor/php-http/discovery/src/StreamFactoryDiscovery.php b/vendor/php-http/discovery/src/StreamFactoryDiscovery.php
new file mode 100644
index 00000000..7bcc8ce1
--- /dev/null
+++ b/vendor/php-http/discovery/src/StreamFactoryDiscovery.php
@@ -0,0 +1,36 @@
+
+ */
+final class StreamFactoryDiscovery extends ClassDiscovery
+{
+ /**
+ * Finds a Stream Factory.
+ *
+ * @return StreamFactory
+ *
+ * @throws Exception\NotFoundException
+ */
+ public static function find()
+ {
+ try {
+ $streamFactory = static::findOneByType(StreamFactory::class);
+ } catch (DiscoveryFailedException $e) {
+ throw new NotFoundException(
+ 'No stream factories found. To use Guzzle, Diactoros or Slim Framework factories install php-http/message and the chosen message implementation.',
+ 0,
+ $e
+ );
+ }
+
+ return static::instantiateClass($streamFactory);
+ }
+}
diff --git a/vendor/php-http/discovery/src/UriFactoryDiscovery.php b/vendor/php-http/discovery/src/UriFactoryDiscovery.php
new file mode 100644
index 00000000..1eef1e6f
--- /dev/null
+++ b/vendor/php-http/discovery/src/UriFactoryDiscovery.php
@@ -0,0 +1,36 @@
+
+ */
+final class UriFactoryDiscovery extends ClassDiscovery
+{
+ /**
+ * Finds a URI Factory.
+ *
+ * @return UriFactory
+ *
+ * @throws Exception\NotFoundException
+ */
+ public static function find()
+ {
+ try {
+ $uriFactory = static::findOneByType(UriFactory::class);
+ } catch (DiscoveryFailedException $e) {
+ throw new NotFoundException(
+ 'No uri factories found. To use Guzzle, Diactoros or Slim Framework factories install php-http/message and the chosen message implementation.',
+ 0,
+ $e
+ );
+ }
+
+ return static::instantiateClass($uriFactory);
+ }
+}
diff --git a/vendor/php-http/guzzle6-adapter/CHANGELOG.md b/vendor/php-http/guzzle6-adapter/CHANGELOG.md
new file mode 100644
index 00000000..0fdb5069
--- /dev/null
+++ b/vendor/php-http/guzzle6-adapter/CHANGELOG.md
@@ -0,0 +1,66 @@
+# Change Log
+
+
+## 1.1.1 - 2016-05-10
+
+### Fixed
+
+- Adapter can again be instantiated without a guzzle client.
+
+## 1.1.0 - 2016-05-09
+
+### Added
+
+- Factory method Client::createWithConfig to create an adapter with custom
+ configuration for the underlying guzzle client.
+
+
+## 1.0.0 - 2016-01-26
+
+
+## 0.4.1 - 2016-01-13
+
+### Changed
+
+- Updated integration tests
+
+### Removed
+
+- Client common dependency
+
+
+## 0.4.0 - 2016-01-12
+
+### Changed
+
+- Updated package files
+- Updated HTTPlug to RC1
+
+
+## 0.2.1 - 2015-12-17
+
+### Added
+
+- Puli configuration and bindings
+
+### Changed
+
+- Guzzle setup conforms to HTTPlug requirement now: Minimal functionality in client
+
+
+## 0.2.0 - 2015-12-15
+
+### Added
+
+- Async client capabalities
+
+### Changed
+
+- HTTPlug instead of HTTP Adapter
+
+
+## 0.1.0 - 2015-06-12
+
+### Added
+
+- Initial release
diff --git a/vendor/php-http/guzzle6-adapter/LICENSE b/vendor/php-http/guzzle6-adapter/LICENSE
new file mode 100644
index 00000000..48741e41
--- /dev/null
+++ b/vendor/php-http/guzzle6-adapter/LICENSE
@@ -0,0 +1,20 @@
+Copyright (c) 2014-2015 Eric GELOEN
+Copyright (c) 2015-2016 PHP HTTP Team
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is furnished
+to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+THE SOFTWARE.
diff --git a/vendor/php-http/guzzle6-adapter/README.md b/vendor/php-http/guzzle6-adapter/README.md
new file mode 100644
index 00000000..623eb2f8
--- /dev/null
+++ b/vendor/php-http/guzzle6-adapter/README.md
@@ -0,0 +1,59 @@
+# Guzzle 6 HTTP Adapter
+
+[![Latest Version](https://img.shields.io/github/release/php-http/guzzle6-adapter.svg?style=flat-square)](https://github.com/php-http/guzzle6-adapter/releases)
+[![Software License](https://img.shields.io/badge/license-MIT-brightgreen.svg?style=flat-square)](LICENSE)
+[![Build Status](https://img.shields.io/travis/php-http/guzzle6-adapter.svg?style=flat-square)](https://travis-ci.org/php-http/guzzle6-adapter)
+[![Code Coverage](https://img.shields.io/scrutinizer/coverage/g/php-http/guzzle6-adapter.svg?style=flat-square)](https://scrutinizer-ci.com/g/php-http/guzzle6-adapter)
+[![Quality Score](https://img.shields.io/scrutinizer/g/php-http/guzzle6-adapter.svg?style=flat-square)](https://scrutinizer-ci.com/g/php-http/guzzle6-adapter)
+[![Total Downloads](https://img.shields.io/packagist/dt/php-http/guzzle6-adapter.svg?style=flat-square)](https://packagist.org/packages/php-http/guzzle6-adapter)
+
+**Guzzle 6 HTTP Adapter.**
+
+
+## Install
+
+Via Composer
+
+``` bash
+$ composer require php-http/guzzle6-adapter
+```
+
+
+## Documentation
+
+Please see the [official documentation](http://docs.php-http.org/en/latest/clients/guzzle6-adapter.html).
+
+
+## Testing
+
+First launch the http server:
+
+```bash
+$ ./vendor/bin/http_test_server > /dev/null 2>&1 &
+```
+
+Then the test suite:
+
+``` bash
+$ composer test
+```
+
+
+## Contributing
+
+Please see our [contributing guide](http://docs.php-http.org/en/latest/development/contributing.html).
+
+
+## Security
+
+If you discover any security related issues, please contact us at [security@php-http.org](mailto:security@php-http.org).
+
+
+## Credits
+
+Thanks to [David de Boer](https://github.com/ddeboer) for implementing this adapter.
+
+
+## License
+
+The MIT License (MIT). Please see [License File](LICENSE) for more information.
diff --git a/vendor/php-http/guzzle6-adapter/composer.json b/vendor/php-http/guzzle6-adapter/composer.json
new file mode 100644
index 00000000..2f01d1a4
--- /dev/null
+++ b/vendor/php-http/guzzle6-adapter/composer.json
@@ -0,0 +1,49 @@
+{
+ "name": "php-http/guzzle6-adapter",
+ "description": "Guzzle 6 HTTP Adapter",
+ "license": "MIT",
+ "keywords": ["guzzle", "http"],
+ "homepage": "http://httplug.io",
+ "authors": [
+ {
+ "name": "David de Boer",
+ "email": "david@ddeboer.nl"
+ },
+ {
+ "name": "Márk Sági-Kazár",
+ "email": "mark.sagikazar@gmail.com"
+ }
+ ],
+ "require": {
+ "php": ">=5.5.0",
+ "php-http/httplug": "^1.0",
+ "guzzlehttp/guzzle": "^6.0"
+ },
+ "require-dev": {
+ "ext-curl": "*",
+ "php-http/adapter-integration-tests": "^0.4"
+ },
+ "provide": {
+ "php-http/client-implementation": "1.0",
+ "php-http/async-client-implementation": "1.0"
+ },
+ "autoload": {
+ "psr-4": {
+ "Http\\Adapter\\Guzzle6\\": "src/"
+ }
+ },
+ "autoload-dev": {
+ "psr-4": {
+ "Http\\Adapter\\Guzzle6\\Tests\\": "tests/"
+ }
+ },
+ "scripts": {
+ "test": "vendor/bin/phpunit",
+ "test-ci": "vendor/bin/phpunit --coverage-text --coverage-clover=build/coverage.xml"
+ },
+ "extra": {
+ "branch-alias": {
+ "dev-master": "1.2-dev"
+ }
+ }
+}
diff --git a/vendor/php-http/guzzle6-adapter/puli.json b/vendor/php-http/guzzle6-adapter/puli.json
new file mode 100644
index 00000000..bd296146
--- /dev/null
+++ b/vendor/php-http/guzzle6-adapter/puli.json
@@ -0,0 +1,16 @@
+{
+ "version": "1.0",
+ "name": "php-http/guzzle6-adapter",
+ "bindings": {
+ "04b5a002-71a8-473d-a8df-75671551b84a": {
+ "_class": "Puli\\Discovery\\Binding\\ClassBinding",
+ "class": "Http\\Adapter\\Guzzle6\\Client",
+ "type": "Http\\Client\\HttpClient"
+ },
+ "9c856476-7f6b-43df-a740-15420a5f839c": {
+ "_class": "Puli\\Discovery\\Binding\\ClassBinding",
+ "class": "Http\\Adapter\\Guzzle6\\Client",
+ "type": "Http\\Client\\HttpAsyncClient"
+ }
+ }
+}
diff --git a/vendor/php-http/guzzle6-adapter/src/Client.php b/vendor/php-http/guzzle6-adapter/src/Client.php
new file mode 100644
index 00000000..ded7494b
--- /dev/null
+++ b/vendor/php-http/guzzle6-adapter/src/Client.php
@@ -0,0 +1,84 @@
+
+ */
+class Client implements HttpClient, HttpAsyncClient
+{
+ /**
+ * @var ClientInterface
+ */
+ private $client;
+
+ /**
+ * @param ClientInterface|null $client
+ */
+ public function __construct(ClientInterface $client = null)
+ {
+ if (!$client) {
+ $client = static::buildClient();
+ }
+
+ $this->client = $client;
+ }
+
+ /**
+ * Factory method to create the guzzle 6 adapter with custom configuration for guzzle.
+ *
+ * @param array $config Configuration to create guzzle with.
+ *
+ * @return Client
+ */
+ public static function createWithConfig(array $config)
+ {
+ return new self(static::buildClient($config));
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function sendRequest(RequestInterface $request)
+ {
+ $promise = $this->sendAsyncRequest($request);
+
+ return $promise->wait();
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function sendAsyncRequest(RequestInterface $request)
+ {
+ $promise = $this->client->sendAsync($request);
+
+ return new Promise($promise, $request);
+ }
+
+ /**
+ * Build the guzzle client instance.
+ *
+ * @param array $config Additional configuration
+ *
+ * @return GuzzleClient
+ */
+ private static function buildClient(array $config = [])
+ {
+ $handlerStack = new HandlerStack(\GuzzleHttp\choose_handler());
+ $handlerStack->push(Middleware::prepareBody(), 'prepare_body');
+ $config = array_merge(['handler' => $handlerStack], $config);
+
+ return new GuzzleClient($config);
+ }
+}
diff --git a/vendor/php-http/guzzle6-adapter/src/Promise.php b/vendor/php-http/guzzle6-adapter/src/Promise.php
new file mode 100644
index 00000000..4d5eb8df
--- /dev/null
+++ b/vendor/php-http/guzzle6-adapter/src/Promise.php
@@ -0,0 +1,140 @@
+
+ */
+class Promise implements HttpPromise
+{
+ /**
+ * @var PromiseInterface
+ */
+ private $promise;
+
+ /**
+ * @var string State of the promise
+ */
+ private $state;
+
+ /**
+ * @var ResponseInterface
+ */
+ private $response;
+
+ /**
+ * @var HttplugException
+ */
+ private $exception;
+
+ /**
+ * @var RequestInterface
+ */
+ private $request;
+
+ /**
+ * @param PromiseInterface $promise
+ * @param RequestInterface $request
+ */
+ public function __construct(PromiseInterface $promise, RequestInterface $request)
+ {
+ $this->request = $request;
+ $this->state = self::PENDING;
+ $this->promise = $promise->then(function ($response) {
+ $this->response = $response;
+ $this->state = self::FULFILLED;
+
+ return $response;
+ }, function ($reason) use ($request) {
+ $this->state = self::REJECTED;
+
+ if ($reason instanceof HttplugException) {
+ $this->exception = $reason;
+ } elseif ($reason instanceof GuzzleExceptions\GuzzleException) {
+ $this->exception = $this->handleException($reason, $request);
+ } elseif ($reason instanceof \Exception) {
+ $this->exception = new \RuntimeException('Invalid exception returned from Guzzle6', 0, $reason);
+ } else {
+ $this->exception = new \UnexpectedValueException('Reason returned from Guzzle6 must be an Exception', 0, $reason);
+ }
+
+ throw $this->exception;
+ });
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function then(callable $onFulfilled = null, callable $onRejected = null)
+ {
+ return new static($this->promise->then($onFulfilled, $onRejected), $this->request);
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function getState()
+ {
+ return $this->state;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function wait($unwrap = true)
+ {
+ $this->promise->wait(false);
+
+ if ($unwrap) {
+ if ($this->getState() == self::REJECTED) {
+ throw $this->exception;
+ }
+
+ return $this->response;
+ }
+ }
+
+ /**
+ * Converts a Guzzle exception into an Httplug exception.
+ *
+ * @param GuzzleExceptions\GuzzleException $exception
+ * @param RequestInterface $request
+ *
+ * @return HttplugException
+ */
+ private function handleException(GuzzleExceptions\GuzzleException $exception, RequestInterface $request)
+ {
+ if ($exception instanceof GuzzleExceptions\SeekException) {
+ return new HttplugException\RequestException($exception->getMessage(), $request, $exception);
+ }
+
+ if ($exception instanceof GuzzleExceptions\ConnectException) {
+ return new HttplugException\NetworkException($exception->getMessage(), $exception->getRequest(), $exception);
+ }
+
+ if ($exception instanceof GuzzleExceptions\RequestException) {
+ // Make sure we have a response for the HttpException
+ if ($exception->hasResponse()) {
+ return new HttplugException\HttpException(
+ $exception->getMessage(),
+ $exception->getRequest(),
+ $exception->getResponse(),
+ $exception
+ );
+ }
+
+ return new HttplugException\RequestException($exception->getMessage(), $exception->getRequest(), $exception);
+ }
+
+ return new HttplugException\TransferException($exception->getMessage(), 0, $exception);
+ }
+}
diff --git a/vendor/php-http/httplug/CHANGELOG.md b/vendor/php-http/httplug/CHANGELOG.md
new file mode 100644
index 00000000..8478966a
--- /dev/null
+++ b/vendor/php-http/httplug/CHANGELOG.md
@@ -0,0 +1,72 @@
+# Change Log
+
+## 1.1.0 - 2016-08-31
+
+- Added HttpFulfilledPromise and HttpRejectedPromise which respect the HttpAsyncClient interface
+
+## 1.0.0 - 2016-01-26
+
+### Removed
+
+- Stability configuration from composer
+
+
+## 1.0.0-RC1 - 2016-01-12
+
+### Changed
+
+- Updated package files
+- Updated promise dependency to RC1
+
+
+## 1.0.0-beta - 2015-12-17
+
+### Added
+
+- Puli configuration and binding types
+
+### Changed
+
+- Exception concept
+
+
+## 1.0.0-alpha3 - 2015-12-13
+
+### Changed
+
+- Async client does not throw exceptions
+
+### Removed
+
+- Promise interface moved to its own repository: [php-http/promise](https://github.com/php-http/promise)
+
+
+## 1.0.0-alpha2 - 2015-11-16
+
+### Added
+
+- Async client and Promise interface
+
+
+## 1.0.0-alpha - 2015-10-26
+
+### Added
+
+- Better domain exceptions.
+
+### Changed
+
+- Purpose of the library: general HTTP CLient abstraction.
+
+### Removed
+
+- Request options: they should be configured at construction time.
+- Multiple request sending: should be done asynchronously using Async Client.
+- `getName` method
+
+
+## 0.1.0 - 2015-06-03
+
+### Added
+
+- Initial release
diff --git a/vendor/php-http/httplug/LICENSE b/vendor/php-http/httplug/LICENSE
new file mode 100644
index 00000000..48741e41
--- /dev/null
+++ b/vendor/php-http/httplug/LICENSE
@@ -0,0 +1,20 @@
+Copyright (c) 2014-2015 Eric GELOEN
+Copyright (c) 2015-2016 PHP HTTP Team
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is furnished
+to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+THE SOFTWARE.
diff --git a/vendor/php-http/httplug/README.md b/vendor/php-http/httplug/README.md
new file mode 100644
index 00000000..f46212bc
--- /dev/null
+++ b/vendor/php-http/httplug/README.md
@@ -0,0 +1,57 @@
+# HTTPlug
+
+[![Latest Version](https://img.shields.io/github/release/php-http/httplug.svg?style=flat-square)](https://github.com/php-http/httplug/releases)
+[![Software License](https://img.shields.io/badge/license-MIT-brightgreen.svg?style=flat-square)](LICENSE)
+[![Build Status](https://img.shields.io/travis/php-http/httplug.svg?style=flat-square)](https://travis-ci.org/php-http/httplug)
+[![Code Coverage](https://img.shields.io/scrutinizer/coverage/g/php-http/httplug.svg?style=flat-square)](https://scrutinizer-ci.com/g/php-http/httplug)
+[![Quality Score](https://img.shields.io/scrutinizer/g/php-http/httplug.svg?style=flat-square)](https://scrutinizer-ci.com/g/php-http/httplug)
+[![Total Downloads](https://img.shields.io/packagist/dt/php-http/httplug.svg?style=flat-square)](https://packagist.org/packages/php-http/httplug)
+
+[![Slack Status](http://slack.httplug.io/badge.svg)](http://slack.httplug.io)
+[![Email](https://img.shields.io/badge/email-team@httplug.io-blue.svg?style=flat-square)](mailto:team@httplug.io)
+
+**HTTPlug, the HTTP client abstraction for PHP.**
+
+
+## Install
+
+Via Composer
+
+``` bash
+$ composer require php-http/httplug
+```
+
+
+## Intro
+
+This is the contract package for HTTP Client.
+Use it to create HTTP Clients which are interoperable and compatible with [PSR-7](http://www.php-fig.org/psr/psr-7/).
+
+This library is the official successor of the [ivory http adapter](https://github.com/egeloen/ivory-http-adapter).
+
+
+## Documentation
+
+Please see the [official documentation](http://docs.php-http.org).
+
+
+## Testing
+
+``` bash
+$ composer test
+```
+
+
+## Contributing
+
+Please see our [contributing guide](http://docs.php-http.org/en/latest/development/contributing.html).
+
+
+## Security
+
+If you discover any security related issues, please contact us at [security@php-http.org](mailto:security@php-http.org).
+
+
+## License
+
+The MIT License (MIT). Please see [License File](LICENSE) for more information.
diff --git a/vendor/php-http/httplug/composer.json b/vendor/php-http/httplug/composer.json
new file mode 100644
index 00000000..f74c4d30
--- /dev/null
+++ b/vendor/php-http/httplug/composer.json
@@ -0,0 +1,40 @@
+{
+ "name": "php-http/httplug",
+ "description": "HTTPlug, the HTTP client abstraction for PHP",
+ "license": "MIT",
+ "keywords": ["http", "client"],
+ "homepage": "http://httplug.io",
+ "authors": [
+ {
+ "name": "Eric GELOEN",
+ "email": "geloen.eric@gmail.com"
+ },
+ {
+ "name": "Márk Sági-Kazár",
+ "email": "mark.sagikazar@gmail.com"
+ }
+ ],
+ "require": {
+ "php": ">=5.4",
+ "psr/http-message": "^1.0",
+ "php-http/promise": "^1.0"
+ },
+ "require-dev": {
+ "phpspec/phpspec": "^2.4",
+ "henrikbjorn/phpspec-code-coverage" : "^1.0"
+ },
+ "autoload": {
+ "psr-4": {
+ "Http\\Client\\": "src/"
+ }
+ },
+ "scripts": {
+ "test": "vendor/bin/phpspec run",
+ "test-ci": "vendor/bin/phpspec run -c phpspec.ci.yml"
+ },
+ "extra": {
+ "branch-alias": {
+ "dev-master": "1.1-dev"
+ }
+ }
+}
diff --git a/vendor/php-http/httplug/puli.json b/vendor/php-http/httplug/puli.json
new file mode 100644
index 00000000..41683315
--- /dev/null
+++ b/vendor/php-http/httplug/puli.json
@@ -0,0 +1,12 @@
+{
+ "version": "1.0",
+ "name": "php-http/httplug",
+ "binding-types": {
+ "Http\\Client\\HttpAsyncClient": {
+ "description": "Async HTTP Client"
+ },
+ "Http\\Client\\HttpClient": {
+ "description": "HTTP Client"
+ }
+ }
+}
diff --git a/vendor/php-http/httplug/src/Exception.php b/vendor/php-http/httplug/src/Exception.php
new file mode 100644
index 00000000..e7382c3c
--- /dev/null
+++ b/vendor/php-http/httplug/src/Exception.php
@@ -0,0 +1,12 @@
+
+ */
+interface Exception
+{
+}
diff --git a/vendor/php-http/httplug/src/Exception/HttpException.php b/vendor/php-http/httplug/src/Exception/HttpException.php
new file mode 100644
index 00000000..f4f32a4d
--- /dev/null
+++ b/vendor/php-http/httplug/src/Exception/HttpException.php
@@ -0,0 +1,74 @@
+
+ */
+class HttpException extends RequestException
+{
+ /**
+ * @var ResponseInterface
+ */
+ protected $response;
+
+ /**
+ * @param string $message
+ * @param RequestInterface $request
+ * @param ResponseInterface $response
+ * @param \Exception|null $previous
+ */
+ public function __construct(
+ $message,
+ RequestInterface $request,
+ ResponseInterface $response,
+ \Exception $previous = null
+ ) {
+ parent::__construct($message, $request, $previous);
+
+ $this->response = $response;
+ $this->code = $response->getStatusCode();
+ }
+
+ /**
+ * Returns the response.
+ *
+ * @return ResponseInterface
+ */
+ public function getResponse()
+ {
+ return $this->response;
+ }
+
+ /**
+ * Factory method to create a new exception with a normalized error message.
+ *
+ * @param RequestInterface $request
+ * @param ResponseInterface $response
+ * @param \Exception|null $previous
+ *
+ * @return HttpException
+ */
+ public static function create(
+ RequestInterface $request,
+ ResponseInterface $response,
+ \Exception $previous = null
+ ) {
+ $message = sprintf(
+ '[url] %s [http method] %s [status code] %s [reason phrase] %s',
+ $request->getRequestTarget(),
+ $request->getMethod(),
+ $response->getStatusCode(),
+ $response->getReasonPhrase()
+ );
+
+ return new self($message, $request, $response, $previous);
+ }
+}
diff --git a/vendor/php-http/httplug/src/Exception/NetworkException.php b/vendor/php-http/httplug/src/Exception/NetworkException.php
new file mode 100644
index 00000000..f2198e5b
--- /dev/null
+++ b/vendor/php-http/httplug/src/Exception/NetworkException.php
@@ -0,0 +1,14 @@
+
+ */
+class NetworkException extends RequestException
+{
+}
diff --git a/vendor/php-http/httplug/src/Exception/RequestException.php b/vendor/php-http/httplug/src/Exception/RequestException.php
new file mode 100644
index 00000000..cdce14bd
--- /dev/null
+++ b/vendor/php-http/httplug/src/Exception/RequestException.php
@@ -0,0 +1,43 @@
+
+ */
+class RequestException extends TransferException
+{
+ /**
+ * @var RequestInterface
+ */
+ private $request;
+
+ /**
+ * @param string $message
+ * @param RequestInterface $request
+ * @param \Exception|null $previous
+ */
+ public function __construct($message, RequestInterface $request, \Exception $previous = null)
+ {
+ $this->request = $request;
+
+ parent::__construct($message, 0, $previous);
+ }
+
+ /**
+ * Returns the request.
+ *
+ * @return RequestInterface
+ */
+ public function getRequest()
+ {
+ return $this->request;
+ }
+}
diff --git a/vendor/php-http/httplug/src/Exception/TransferException.php b/vendor/php-http/httplug/src/Exception/TransferException.php
new file mode 100644
index 00000000..a858cf5e
--- /dev/null
+++ b/vendor/php-http/httplug/src/Exception/TransferException.php
@@ -0,0 +1,14 @@
+
+ */
+class TransferException extends \RuntimeException implements Exception
+{
+}
diff --git a/vendor/php-http/httplug/src/HttpAsyncClient.php b/vendor/php-http/httplug/src/HttpAsyncClient.php
new file mode 100644
index 00000000..492e511b
--- /dev/null
+++ b/vendor/php-http/httplug/src/HttpAsyncClient.php
@@ -0,0 +1,27 @@
+
+ */
+interface HttpAsyncClient
+{
+ /**
+ * Sends a PSR-7 request in an asynchronous way.
+ *
+ * Exceptions related to processing the request are available from the returned Promise.
+ *
+ * @param RequestInterface $request
+ *
+ * @return Promise Resolves a PSR-7 Response or fails with an Http\Client\Exception.
+ *
+ * @throws \Exception If processing the request is impossible (eg. bad configuration).
+ */
+ public function sendAsyncRequest(RequestInterface $request);
+}
diff --git a/vendor/php-http/httplug/src/HttpClient.php b/vendor/php-http/httplug/src/HttpClient.php
new file mode 100644
index 00000000..0e51749f
--- /dev/null
+++ b/vendor/php-http/httplug/src/HttpClient.php
@@ -0,0 +1,28 @@
+
+ * @author Márk Sági-Kazár
+ * @author David Buchmann
+ */
+interface HttpClient
+{
+ /**
+ * Sends a PSR-7 request.
+ *
+ * @param RequestInterface $request
+ *
+ * @return ResponseInterface
+ *
+ * @throws \Http\Client\Exception If an error happens during processing the request.
+ * @throws \Exception If processing the request is impossible (eg. bad configuration).
+ */
+ public function sendRequest(RequestInterface $request);
+}
diff --git a/vendor/php-http/httplug/src/Promise/HttpFulfilledPromise.php b/vendor/php-http/httplug/src/Promise/HttpFulfilledPromise.php
new file mode 100644
index 00000000..6779e447
--- /dev/null
+++ b/vendor/php-http/httplug/src/Promise/HttpFulfilledPromise.php
@@ -0,0 +1,57 @@
+response = $response;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function then(callable $onFulfilled = null, callable $onRejected = null)
+ {
+ if (null === $onFulfilled) {
+ return $this;
+ }
+
+ try {
+ return new self($onFulfilled($this->response));
+ } catch (Exception $e) {
+ return new HttpRejectedPromise($e);
+ }
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function getState()
+ {
+ return Promise::FULFILLED;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function wait($unwrap = true)
+ {
+ if ($unwrap) {
+ return $this->response;
+ }
+ }
+}
diff --git a/vendor/php-http/httplug/src/Promise/HttpRejectedPromise.php b/vendor/php-http/httplug/src/Promise/HttpRejectedPromise.php
new file mode 100644
index 00000000..bfb0738f
--- /dev/null
+++ b/vendor/php-http/httplug/src/Promise/HttpRejectedPromise.php
@@ -0,0 +1,56 @@
+exception = $exception;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function then(callable $onFulfilled = null, callable $onRejected = null)
+ {
+ if (null === $onRejected) {
+ return $this;
+ }
+
+ try {
+ return new HttpFulfilledPromise($onRejected($this->exception));
+ } catch (Exception $e) {
+ return new self($e);
+ }
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function getState()
+ {
+ return Promise::REJECTED;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function wait($unwrap = true)
+ {
+ if ($unwrap) {
+ throw $this->exception;
+ }
+ }
+}
diff --git a/vendor/php-http/message-factory/CHANGELOG.md b/vendor/php-http/message-factory/CHANGELOG.md
new file mode 100644
index 00000000..4711924c
--- /dev/null
+++ b/vendor/php-http/message-factory/CHANGELOG.md
@@ -0,0 +1,65 @@
+# Change Log
+
+
+## 1.0.2 - 2015-12-19
+
+### Added
+
+- Request and Response factory binding types to Puli
+
+
+## 1.0.1 - 2015-12-17
+
+### Added
+
+- Puli configuration and binding types
+
+
+## 1.0.0 - 2015-12-15
+
+### Added
+
+- Response Factory in order to be reused in Message and Server Message factories
+- Request Factory
+
+### Changed
+
+- Message Factory extends Request and Response factories
+
+
+## 1.0.0-RC1 - 2015-12-14
+
+### Added
+
+- CS check
+
+### Changed
+
+- RuntimeException is thrown when the StreamFactory cannot write to the underlying stream
+
+
+## 0.3.0 - 2015-11-16
+
+### Removed
+
+- Client Context Factory
+- Factory Awares and Templates
+
+
+## 0.2.0 - 2015-11-16
+
+### Changed
+
+- Reordered the parameters when creating a message to have the protocol last,
+as its the least likely to need to be changed.
+
+
+## 0.1.0 - 2015-06-01
+
+### Added
+
+- Initial release
+
+### Changed
+
+- Helpers are renamed to templates
diff --git a/vendor/php-http/message-factory/LICENSE b/vendor/php-http/message-factory/LICENSE
new file mode 100644
index 00000000..8e2c4a0b
--- /dev/null
+++ b/vendor/php-http/message-factory/LICENSE
@@ -0,0 +1,19 @@
+Copyright (c) 2015 PHP HTTP Team
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is furnished
+to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+THE SOFTWARE.
diff --git a/vendor/php-http/message-factory/README.md b/vendor/php-http/message-factory/README.md
new file mode 100644
index 00000000..4654495a
--- /dev/null
+++ b/vendor/php-http/message-factory/README.md
@@ -0,0 +1,36 @@
+# PSR-7 Message Factory
+
+[![Latest Version](https://img.shields.io/github/release/php-http/message-factory.svg?style=flat-square)](https://github.com/php-http/message-factory/releases)
+[![Software License](https://img.shields.io/badge/license-MIT-brightgreen.svg?style=flat-square)](LICENSE)
+[![Total Downloads](https://img.shields.io/packagist/dt/php-http/message-factory.svg?style=flat-square)](https://packagist.org/packages/php-http/message-factory)
+
+**Factory interfaces for PSR-7 HTTP Message.**
+
+
+## Install
+
+Via Composer
+
+``` bash
+$ composer require php-http/message-factory
+```
+
+
+## Documentation
+
+Please see the [official documentation](http://php-http.readthedocs.org/en/latest/message-factory/).
+
+
+## Contributing
+
+Please see [CONTRIBUTING](CONTRIBUTING.md) and [CONDUCT](CONDUCT.md) for details.
+
+
+## Security
+
+If you discover any security related issues, please contact us at [security@php-http.org](mailto:security@php-http.org).
+
+
+## License
+
+The MIT License (MIT). Please see [License File](LICENSE) for more information.
diff --git a/vendor/php-http/message-factory/composer.json b/vendor/php-http/message-factory/composer.json
new file mode 100644
index 00000000..7c72febe
--- /dev/null
+++ b/vendor/php-http/message-factory/composer.json
@@ -0,0 +1,27 @@
+{
+ "name": "php-http/message-factory",
+ "description": "Factory interfaces for PSR-7 HTTP Message",
+ "license": "MIT",
+ "keywords": ["http", "factory", "message", "stream", "uri"],
+ "homepage": "http://php-http.org",
+ "authors": [
+ {
+ "name": "Márk Sági-Kazár",
+ "email": "mark.sagikazar@gmail.com"
+ }
+ ],
+ "require": {
+ "php": ">=5.4",
+ "psr/http-message": "^1.0"
+ },
+ "autoload": {
+ "psr-4": {
+ "Http\\Message\\": "src/"
+ }
+ },
+ "extra": {
+ "branch-alias": {
+ "dev-master": "1.0-dev"
+ }
+ }
+}
diff --git a/vendor/php-http/message-factory/puli.json b/vendor/php-http/message-factory/puli.json
new file mode 100644
index 00000000..08d37627
--- /dev/null
+++ b/vendor/php-http/message-factory/puli.json
@@ -0,0 +1,43 @@
+{
+ "version": "1.0",
+ "binding-types": {
+ "Http\\Message\\MessageFactory": {
+ "description": "PSR-7 Message Factory",
+ "parameters": {
+ "depends": {
+ "description": "Optional class dependency which can be checked by consumers"
+ }
+ }
+ },
+ "Http\\Message\\RequestFactory": {
+ "parameters": {
+ "depends": {
+ "description": "Optional class dependency which can be checked by consumers"
+ }
+ }
+ },
+ "Http\\Message\\ResponseFactory": {
+ "parameters": {
+ "depends": {
+ "description": "Optional class dependency which can be checked by consumers"
+ }
+ }
+ },
+ "Http\\Message\\StreamFactory": {
+ "description": "PSR-7 Stream Factory",
+ "parameters": {
+ "depends": {
+ "description": "Optional class dependency which can be checked by consumers"
+ }
+ }
+ },
+ "Http\\Message\\UriFactory": {
+ "description": "PSR-7 URI Factory",
+ "parameters": {
+ "depends": {
+ "description": "Optional class dependency which can be checked by consumers"
+ }
+ }
+ }
+ }
+}
diff --git a/vendor/php-http/message-factory/src/MessageFactory.php b/vendor/php-http/message-factory/src/MessageFactory.php
new file mode 100644
index 00000000..965aaa80
--- /dev/null
+++ b/vendor/php-http/message-factory/src/MessageFactory.php
@@ -0,0 +1,12 @@
+
+ */
+interface MessageFactory extends RequestFactory, ResponseFactory
+{
+}
diff --git a/vendor/php-http/message-factory/src/RequestFactory.php b/vendor/php-http/message-factory/src/RequestFactory.php
new file mode 100644
index 00000000..624e82f3
--- /dev/null
+++ b/vendor/php-http/message-factory/src/RequestFactory.php
@@ -0,0 +1,34 @@
+
+ */
+interface RequestFactory
+{
+ /**
+ * Creates a new PSR-7 request.
+ *
+ * @param string $method
+ * @param string|UriInterface $uri
+ * @param array $headers
+ * @param resource|string|StreamInterface|null $body
+ * @param string $protocolVersion
+ *
+ * @return RequestInterface
+ */
+ public function createRequest(
+ $method,
+ $uri,
+ array $headers = [],
+ $body = null,
+ $protocolVersion = '1.1'
+ );
+}
diff --git a/vendor/php-http/message-factory/src/ResponseFactory.php b/vendor/php-http/message-factory/src/ResponseFactory.php
new file mode 100644
index 00000000..2411ed3a
--- /dev/null
+++ b/vendor/php-http/message-factory/src/ResponseFactory.php
@@ -0,0 +1,35 @@
+
+ */
+interface ResponseFactory
+{
+ /**
+ * Creates a new PSR-7 response.
+ *
+ * @param int $statusCode
+ * @param string|null $reasonPhrase
+ * @param array $headers
+ * @param resource|string|StreamInterface|null $body
+ * @param string $protocolVersion
+ *
+ * @return ResponseInterface
+ */
+ public function createResponse(
+ $statusCode = 200,
+ $reasonPhrase = null,
+ array $headers = [],
+ $body = null,
+ $protocolVersion = '1.1'
+ );
+}
diff --git a/vendor/php-http/message-factory/src/StreamFactory.php b/vendor/php-http/message-factory/src/StreamFactory.php
new file mode 100644
index 00000000..327a902f
--- /dev/null
+++ b/vendor/php-http/message-factory/src/StreamFactory.php
@@ -0,0 +1,25 @@
+
+ */
+interface StreamFactory
+{
+ /**
+ * Creates a new PSR-7 stream.
+ *
+ * @param string|resource|StreamInterface|null $body
+ *
+ * @return StreamInterface
+ *
+ * @throws \InvalidArgumentException If the stream body is invalid.
+ * @throws \RuntimeException If creating the stream from $body fails.
+ */
+ public function createStream($body = null);
+}
diff --git a/vendor/php-http/message-factory/src/UriFactory.php b/vendor/php-http/message-factory/src/UriFactory.php
new file mode 100644
index 00000000..f05e6252
--- /dev/null
+++ b/vendor/php-http/message-factory/src/UriFactory.php
@@ -0,0 +1,24 @@
+
+ */
+interface UriFactory
+{
+ /**
+ * Creates an PSR-7 URI.
+ *
+ * @param string|UriInterface $uri
+ *
+ * @return UriInterface
+ *
+ * @throws \InvalidArgumentException If the $uri argument can not be converted into a valid URI.
+ */
+ public function createUri($uri);
+}
diff --git a/vendor/php-http/message/CHANGELOG.md b/vendor/php-http/message/CHANGELOG.md
new file mode 100644
index 00000000..48319fd0
--- /dev/null
+++ b/vendor/php-http/message/CHANGELOG.md
@@ -0,0 +1,194 @@
+# Change Log
+
+
+All notable changes to this project will be documented in this file.
+
+The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/)
+and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.html).
+
+
+## Unreleased
+
+## [1.7.2] - 2018-10-30
+
+### Fixed
+
+- FilteredStream uses `@trigger_error` instead of throwing exceptions to not
+ break careless users. You still need to fix your stream code to respect
+ `isSeekable`. Seeking does not work as expected, and we will add exceptions
+ in version 2.
+
+## [1.7.1] - 2018-10-29
+
+### Fixed
+
+- FilteredStream is not actually seekable
+
+
+## [1.7.0] - 2018-08-15
+
+### Fixed
+
+- Fix CurlCommandFormatter for binary request payloads
+- Fix QueryParam authentication to assemble proper URL regardless of PHP `arg_separator.output` directive
+- Do not pass `null` parameters to `Clue\StreamFilter\fun`
+
+### Changed
+
+- Dropped tests on HHVM
+
+
+## [1.6.0] - 2017-07-05
+
+### Added
+
+- CookieUtil::parseDate to create a date from cookie date string
+
+### Fixed
+
+- Fix curl command of CurlFormatter when there is an user-agent header
+
+
+## [1.5.0] - 2017-02-14
+
+### Added
+
+- Check for empty string in Stream factories
+- Cookie::createWithoutValidation Static constructor to create a cookie. Will not perform any attribute validation during instantiation.
+- Cookie::isValid Method to check if cookie attributes are valid.
+
+### Fixed
+
+- FilteredStream::getSize returns null because the contents size is unknown.
+- Stream factories does not rewinds streams. The previous behavior was not coherent between factories and inputs.
+
+### Deprecated
+
+- FilteredStream::getReadFilter The read filter is internal and should never be used by consuming code.
+- FilteredStream::getWriteFilter We did not implement writing to the streams at all. And if we do, the filter is an internal information and should not be used by consuming code.
+
+
+## [1.4.1] - 2016-12-16
+
+### Fixed
+
+- Cookie::matchPath Cookie with root path (`/`) will not match sub path (e.g. `/cookie`).
+
+
+## [1.4.0] - 2016-10-20
+
+### Added
+
+- Message, stream and URI factories for [Slim Framework](https://github.com/slimphp/Slim)
+- BufferedStream that allow you to decorate a non-seekable stream with a seekable one.
+- cUrlFormatter to be able to redo the request with a cURL command
+
+
+## [1.3.1] - 2016-07-15
+
+### Fixed
+
+- FullHttpMessageFormatter will not read from streams that you cannot rewind (non-seekable)
+- FullHttpMessageFormatter will not read from the stream if $maxBodyLength is zero
+- FullHttpMessageFormatter rewinds streams after they are read
+
+
+## [1.3.0] - 2016-07-14
+
+### Added
+
+- FullHttpMessageFormatter to include headers and body in the formatted message
+
+### Fixed
+
+- #41: Response builder broke header value
+
+
+## [1.2.0] - 2016-03-29
+
+### Added
+
+- The RequestMatcher is built after the Symfony RequestMatcher and separates
+ scheme, host and path expressions and provides an option to filter on the
+ method
+- New RequestConditional authentication method using request matchers
+- Add automatic basic auth info detection based on the URL
+
+### Changed
+
+- Improved ResponseBuilder
+
+### Deprecated
+
+- RegexRequestMatcher, use RequestMatcher instead
+- Matching authenitcation method, use RequestConditional instead
+
+
+## [1.1.0] - 2016-02-25
+
+### Added
+
+ - Add a request matcher interface and regex implementation
+ - Add a callback request matcher implementation
+ - Add a ResponseBuilder, to create PSR7 Response from a string
+
+### Fixed
+
+ - Fix casting string on a FilteredStream not filtering the output
+
+
+## [1.0.0] - 2016-01-27
+
+
+## [0.2.0] - 2015-12-29
+
+### Added
+
+- Autoregistration of stream filters using Composer autoload
+- Cookie
+- [Apigen](http://www.apigen.org/) configuration
+
+
+## [0.1.2] - 2015-12-26
+
+### Added
+
+- Request and response factory bindings
+
+### Fixed
+
+- Chunk filter namespace in Dechunk stream
+
+
+## [0.1.1] - 2015-12-25
+
+### Added
+
+- Formatter
+
+
+## 0.1.0 - 2015-12-24
+
+### Added
+
+- Authentication
+- Encoding
+- Message decorator
+- Message factory (Guzzle, Diactoros)
+
+
+[Unreleased]: https://github.com/php-http/message/compare/v1.7.1...HEAD
+[1.7.1]: https://github.com/php-http/message/compare/1.7.0...v1.7.1
+[1.7.0]: https://github.com/php-http/message/compare/1.6.0...1.7.0
+[1.6.0]: https://github.com/php-http/message/compare/1.5.0...1.6.0
+[1.5.0]: https://github.com/php-http/message/compare/v1.4.1...1.5.0
+[1.4.1]: https://github.com/php-http/message/compare/v1.4.0...v1.4.1
+[1.4.0]: https://github.com/php-http/message/compare/v1.3.1...v1.4.0
+[1.3.1]: https://github.com/php-http/message/compare/v1.3.0...v1.3.1
+[1.3.0]: https://github.com/php-http/message/compare/v1.2.0...v1.3.0
+[1.2.0]: https://github.com/php-http/message/compare/v1.1.0...v1.2.0
+[1.1.0]: https://github.com/php-http/message/compare/v1.0.0...v1.1.0
+[1.0.0]: https://github.com/php-http/message/compare/0.2.0...v1.0.0
+[0.2.0]: https://github.com/php-http/message/compare/v0.1.2...0.2.0
+[0.1.2]: https://github.com/php-http/message/compare/v0.1.1...v0.1.2
+[0.1.1]: https://github.com/php-http/message/compare/v0.1.0...v0.1.1
diff --git a/vendor/php-http/message/LICENSE b/vendor/php-http/message/LICENSE
new file mode 100644
index 00000000..4558d6f0
--- /dev/null
+++ b/vendor/php-http/message/LICENSE
@@ -0,0 +1,19 @@
+Copyright (c) 2015-2016 PHP HTTP Team
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is furnished
+to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+THE SOFTWARE.
diff --git a/vendor/php-http/message/README.md b/vendor/php-http/message/README.md
new file mode 100644
index 00000000..f6f6e197
--- /dev/null
+++ b/vendor/php-http/message/README.md
@@ -0,0 +1,61 @@
+# HTTP Message
+
+[![Latest Version](https://img.shields.io/github/release/php-http/message.svg?style=flat-square)](https://github.com/php-http/message/releases)
+[![Software License](https://img.shields.io/badge/license-MIT-brightgreen.svg?style=flat-square)](LICENSE)
+[![Build Status](https://img.shields.io/travis/php-http/message.svg?style=flat-square)](https://travis-ci.org/php-http/message)
+[![Code Coverage](https://img.shields.io/scrutinizer/coverage/g/php-http/message.svg?style=flat-square)](https://scrutinizer-ci.com/g/php-http/message)
+[![Quality Score](https://img.shields.io/scrutinizer/g/php-http/message.svg?style=flat-square)](https://scrutinizer-ci.com/g/php-http/message)
+[![Total Downloads](https://img.shields.io/packagist/dt/php-http/message.svg?style=flat-square)](https://packagist.org/packages/php-http/message)
+
+**HTTP Message related tools.**
+
+
+## Install
+
+Via Composer
+
+``` bash
+$ composer require php-http/message
+```
+
+
+## Intro
+
+This package contains various PSR-7 tools which might be useful in an HTTP workflow:
+
+- Authentication method implementations
+- Various Stream encoding tools
+- Message decorators
+- Message factory implementations for Guzzle PSR-7 and Diactoros
+- Cookie implementation
+- Request matchers
+
+
+## Documentation
+
+Please see the [official documentation](http://docs.php-http.org/en/latest/message.html).
+
+
+## Testing
+
+``` bash
+$ composer test
+```
+
+
+## Contributing
+
+Please see our [contributing guide](http://docs.php-http.org/en/latest/development/contributing.html).
+
+## Credits
+
+Thanks to [Cuzzle](https://github.com/namshi/cuzzle) for inpiration for the `CurlCommandFormatter`.
+
+## Security
+
+If you discover any security related issues, please contact us at [security@php-http.org](mailto:security@php-http.org).
+
+
+## License
+
+The MIT License (MIT). Please see [License File](LICENSE) for more information.
diff --git a/vendor/php-http/message/apigen.neon b/vendor/php-http/message/apigen.neon
new file mode 100644
index 00000000..0ba1a185
--- /dev/null
+++ b/vendor/php-http/message/apigen.neon
@@ -0,0 +1,6 @@
+source:
+ - src/
+
+destination: build/api/
+
+templateTheme: bootstrap
diff --git a/vendor/php-http/message/composer.json b/vendor/php-http/message/composer.json
new file mode 100644
index 00000000..3a9b3ed4
--- /dev/null
+++ b/vendor/php-http/message/composer.json
@@ -0,0 +1,60 @@
+{
+ "name": "php-http/message",
+ "description": "HTTP Message related tools",
+ "license": "MIT",
+ "keywords": ["message", "http", "psr-7"],
+ "homepage": "http://php-http.org",
+ "authors": [
+ {
+ "name": "Márk Sági-Kazár",
+ "email": "mark.sagikazar@gmail.com"
+ }
+ ],
+ "require": {
+ "php": "^5.4 || ^7.0",
+ "psr/http-message": "^1.0",
+ "php-http/message-factory": "^1.0.2",
+ "clue/stream-filter": "^1.4"
+ },
+ "provide": {
+ "php-http/message-factory-implementation": "1.0"
+ },
+ "require-dev": {
+ "zendframework/zend-diactoros": "^1.0",
+ "guzzlehttp/psr7": "^1.0",
+ "ext-zlib": "*",
+ "phpspec/phpspec": "^2.4",
+ "henrikbjorn/phpspec-code-coverage" : "^1.0",
+ "coduo/phpspec-data-provider-extension": "^1.0",
+ "akeneo/phpspec-skip-example-extension": "^1.0",
+ "slim/slim": "^3.0"
+ },
+ "suggest": {
+ "zendframework/zend-diactoros": "Used with Diactoros Factories",
+ "guzzlehttp/psr7": "Used with Guzzle PSR-7 Factories",
+ "slim/slim": "Used with Slim Framework PSR-7 implementation",
+ "ext-zlib": "Used with compressor/decompressor streams"
+ },
+ "autoload": {
+ "psr-4": {
+ "Http\\Message\\": "src/"
+ },
+ "files": [
+ "src/filters.php"
+ ]
+ },
+ "autoload-dev": {
+ "psr-4": {
+ "spec\\Http\\Message\\": "spec/"
+ }
+ },
+ "scripts": {
+ "test": "vendor/bin/phpspec run",
+ "test-ci": "vendor/bin/phpspec run -c phpspec.ci.yml"
+ },
+ "extra": {
+ "branch-alias": {
+ "dev-master": "1.6-dev"
+ }
+ }
+}
diff --git a/vendor/php-http/message/puli.json b/vendor/php-http/message/puli.json
new file mode 100644
index 00000000..024a85d9
--- /dev/null
+++ b/vendor/php-http/message/puli.json
@@ -0,0 +1,111 @@
+{
+ "version": "1.0",
+ "name": "php-http/message",
+ "bindings": {
+ "064d003d-78a1-48c4-8f3b-1f92ff25da69": {
+ "_class": "Puli\\Discovery\\Binding\\ClassBinding",
+ "class": "Http\\Message\\MessageFactory\\DiactorosMessageFactory",
+ "type": "Http\\Message\\MessageFactory",
+ "parameters": {
+ "depends": "Zend\\Diactoros\\Request"
+ }
+ },
+ "0836751e-6558-4d1b-8993-4a52012947c3": {
+ "_class": "Puli\\Discovery\\Binding\\ClassBinding",
+ "class": "Http\\Message\\MessageFactory\\SlimMessageFactory",
+ "type": "Http\\Message\\ResponseFactory"
+ },
+ "1d127622-dc61-4bfa-b9da-d221548d72c3": {
+ "_class": "Puli\\Discovery\\Binding\\ClassBinding",
+ "class": "Http\\Message\\MessageFactory\\SlimMessageFactory",
+ "type": "Http\\Message\\RequestFactory"
+ },
+ "2438c2d0-0658-441f-8855-ddaf0f87d54d": {
+ "_class": "Puli\\Discovery\\Binding\\ClassBinding",
+ "class": "Http\\Message\\MessageFactory\\GuzzleMessageFactory",
+ "type": "Http\\Message\\MessageFactory",
+ "parameters": {
+ "depends": "GuzzleHttp\\Psr7\\Request"
+ }
+ },
+ "253aa08c-d705-46e7-b1d2-e28c97eef792": {
+ "_class": "Puli\\Discovery\\Binding\\ClassBinding",
+ "class": "Http\\Message\\MessageFactory\\GuzzleMessageFactory",
+ "type": "Http\\Message\\RequestFactory",
+ "parameters": {
+ "depends": "GuzzleHttp\\Psr7\\Request"
+ }
+ },
+ "273a34f9-62f4-4ba1-9801-b1284d49ff89": {
+ "_class": "Puli\\Discovery\\Binding\\ClassBinding",
+ "class": "Http\\Message\\StreamFactory\\GuzzleStreamFactory",
+ "type": "Http\\Message\\StreamFactory",
+ "parameters": {
+ "depends": "GuzzleHttp\\Psr7\\Stream"
+ }
+ },
+ "304b83db-b594-4d83-ae75-1f633adf92f7": {
+ "_class": "Puli\\Discovery\\Binding\\ClassBinding",
+ "class": "Http\\Message\\UriFactory\\GuzzleUriFactory",
+ "type": "Http\\Message\\UriFactory",
+ "parameters": {
+ "depends": "GuzzleHttp\\Psr7\\Uri"
+ }
+ },
+ "3f4bc1cd-aa95-4702-9fa7-65408e471691": {
+ "_class": "Puli\\Discovery\\Binding\\ClassBinding",
+ "class": "Http\\Message\\UriFactory\\DiactorosUriFactory",
+ "type": "Http\\Message\\UriFactory",
+ "parameters": {
+ "depends": "Zend\\Diactoros\\Uri"
+ }
+ },
+ "4672a6ee-ad9e-4109-a5d1-b7d46f26c7a1": {
+ "_class": "Puli\\Discovery\\Binding\\ClassBinding",
+ "class": "Http\\Message\\MessageFactory\\SlimMessageFactory",
+ "type": "Http\\Message\\MessageFactory"
+ },
+ "6234e947-d3bd-43eb-97d5-7f9e22e6bb1b": {
+ "_class": "Puli\\Discovery\\Binding\\ClassBinding",
+ "class": "Http\\Message\\MessageFactory\\DiactorosMessageFactory",
+ "type": "Http\\Message\\ResponseFactory",
+ "parameters": {
+ "depends": "Zend\\Diactoros\\Response"
+ }
+ },
+ "6a9ad6ce-d82c-470f-8e30-60f21d9d95bf": {
+ "_class": "Puli\\Discovery\\Binding\\ClassBinding",
+ "class": "Http\\Message\\UriFactory\\SlimUriFactory",
+ "type": "Http\\Message\\UriFactory"
+ },
+ "72c2afa0-ea56-4d03-adb6-a9f241a8a734": {
+ "_class": "Puli\\Discovery\\Binding\\ClassBinding",
+ "class": "Http\\Message\\StreamFactory\\SlimStreamFactory",
+ "type": "Http\\Message\\StreamFactory"
+ },
+ "95c1be8f-39fe-4abd-8351-92cb14379a75": {
+ "_class": "Puli\\Discovery\\Binding\\ClassBinding",
+ "class": "Http\\Message\\StreamFactory\\DiactorosStreamFactory",
+ "type": "Http\\Message\\StreamFactory",
+ "parameters": {
+ "depends": "Zend\\Diactoros\\Stream"
+ }
+ },
+ "a018af27-7590-4dcf-83a1-497f95604cd6": {
+ "_class": "Puli\\Discovery\\Binding\\ClassBinding",
+ "class": "Http\\Message\\MessageFactory\\GuzzleMessageFactory",
+ "type": "Http\\Message\\ResponseFactory",
+ "parameters": {
+ "depends": "GuzzleHttp\\Psr7\\Response"
+ }
+ },
+ "c07955b1-de46-43db-923b-d07fae9382cb": {
+ "_class": "Puli\\Discovery\\Binding\\ClassBinding",
+ "class": "Http\\Message\\MessageFactory\\DiactorosMessageFactory",
+ "type": "Http\\Message\\RequestFactory",
+ "parameters": {
+ "depends": "Zend\\Diactoros\\Request"
+ }
+ }
+ }
+}
diff --git a/vendor/php-http/message/src/Authentication.php b/vendor/php-http/message/src/Authentication.php
new file mode 100644
index 00000000..b50366ff
--- /dev/null
+++ b/vendor/php-http/message/src/Authentication.php
@@ -0,0 +1,22 @@
+
+ */
+interface Authentication
+{
+ /**
+ * Authenticates a request.
+ *
+ * @param RequestInterface $request
+ *
+ * @return RequestInterface
+ */
+ public function authenticate(RequestInterface $request);
+}
diff --git a/vendor/php-http/message/src/Authentication/AutoBasicAuth.php b/vendor/php-http/message/src/Authentication/AutoBasicAuth.php
new file mode 100644
index 00000000..7b6a4294
--- /dev/null
+++ b/vendor/php-http/message/src/Authentication/AutoBasicAuth.php
@@ -0,0 +1,48 @@
+
+ */
+final class AutoBasicAuth implements Authentication
+{
+ /**
+ * Whether user info should be removed from the URI.
+ *
+ * @var bool
+ */
+ private $shouldRemoveUserInfo;
+
+ /**
+ * @param bool|true $shouldRremoveUserInfo
+ */
+ public function __construct($shouldRremoveUserInfo = true)
+ {
+ $this->shouldRemoveUserInfo = (bool) $shouldRremoveUserInfo;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function authenticate(RequestInterface $request)
+ {
+ $uri = $request->getUri();
+ $userInfo = $uri->getUserInfo();
+
+ if (!empty($userInfo)) {
+ if ($this->shouldRemoveUserInfo) {
+ $request = $request->withUri($uri->withUserInfo(''));
+ }
+
+ $request = $request->withHeader('Authorization', sprintf('Basic %s', base64_encode($userInfo)));
+ }
+
+ return $request;
+ }
+}
diff --git a/vendor/php-http/message/src/Authentication/BasicAuth.php b/vendor/php-http/message/src/Authentication/BasicAuth.php
new file mode 100644
index 00000000..23618a53
--- /dev/null
+++ b/vendor/php-http/message/src/Authentication/BasicAuth.php
@@ -0,0 +1,44 @@
+
+ */
+final class BasicAuth implements Authentication
+{
+ /**
+ * @var string
+ */
+ private $username;
+
+ /**
+ * @var string
+ */
+ private $password;
+
+ /**
+ * @param string $username
+ * @param string $password
+ */
+ public function __construct($username, $password)
+ {
+ $this->username = $username;
+ $this->password = $password;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function authenticate(RequestInterface $request)
+ {
+ $header = sprintf('Basic %s', base64_encode(sprintf('%s:%s', $this->username, $this->password)));
+
+ return $request->withHeader('Authorization', $header);
+ }
+}
diff --git a/vendor/php-http/message/src/Authentication/Bearer.php b/vendor/php-http/message/src/Authentication/Bearer.php
new file mode 100644
index 00000000..a8fb21a1
--- /dev/null
+++ b/vendor/php-http/message/src/Authentication/Bearer.php
@@ -0,0 +1,37 @@
+
+ */
+final class Bearer implements Authentication
+{
+ /**
+ * @var string
+ */
+ private $token;
+
+ /**
+ * @param string $token
+ */
+ public function __construct($token)
+ {
+ $this->token = $token;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function authenticate(RequestInterface $request)
+ {
+ $header = sprintf('Bearer %s', $this->token);
+
+ return $request->withHeader('Authorization', $header);
+ }
+}
diff --git a/vendor/php-http/message/src/Authentication/Chain.php b/vendor/php-http/message/src/Authentication/Chain.php
new file mode 100644
index 00000000..71002bb1
--- /dev/null
+++ b/vendor/php-http/message/src/Authentication/Chain.php
@@ -0,0 +1,47 @@
+
+ */
+final class Chain implements Authentication
+{
+ /**
+ * @var Authentication[]
+ */
+ private $authenticationChain = [];
+
+ /**
+ * @param Authentication[] $authenticationChain
+ */
+ public function __construct(array $authenticationChain = [])
+ {
+ foreach ($authenticationChain as $authentication) {
+ if (!$authentication instanceof Authentication) {
+ throw new \InvalidArgumentException(
+ 'Members of the authentication chain must be of type Http\Message\Authentication'
+ );
+ }
+ }
+
+ $this->authenticationChain = $authenticationChain;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function authenticate(RequestInterface $request)
+ {
+ foreach ($this->authenticationChain as $authentication) {
+ $request = $authentication->authenticate($request);
+ }
+
+ return $request;
+ }
+}
diff --git a/vendor/php-http/message/src/Authentication/Matching.php b/vendor/php-http/message/src/Authentication/Matching.php
new file mode 100644
index 00000000..4b89b501
--- /dev/null
+++ b/vendor/php-http/message/src/Authentication/Matching.php
@@ -0,0 +1,74 @@
+
+ *
+ * @deprecated since since version 1.2, and will be removed in 2.0. Use {@link RequestConditional} instead.
+ */
+final class Matching implements Authentication
+{
+ /**
+ * @var Authentication
+ */
+ private $authentication;
+
+ /**
+ * @var CallbackRequestMatcher
+ */
+ private $matcher;
+
+ /**
+ * @param Authentication $authentication
+ * @param callable|null $matcher
+ */
+ public function __construct(Authentication $authentication, callable $matcher = null)
+ {
+ if (is_null($matcher)) {
+ $matcher = function () {
+ return true;
+ };
+ }
+
+ $this->authentication = $authentication;
+ $this->matcher = new CallbackRequestMatcher($matcher);
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function authenticate(RequestInterface $request)
+ {
+ if ($this->matcher->matches($request)) {
+ return $this->authentication->authenticate($request);
+ }
+
+ return $request;
+ }
+
+ /**
+ * Creates a matching authentication for an URL.
+ *
+ * @param Authentication $authentication
+ * @param string $url
+ *
+ * @return self
+ */
+ public static function createUrlMatcher(Authentication $authentication, $url)
+ {
+ $matcher = function (RequestInterface $request) use ($url) {
+ return preg_match($url, $request->getRequestTarget());
+ };
+
+ return new static($authentication, $matcher);
+ }
+}
diff --git a/vendor/php-http/message/src/Authentication/QueryParam.php b/vendor/php-http/message/src/Authentication/QueryParam.php
new file mode 100644
index 00000000..650cac72
--- /dev/null
+++ b/vendor/php-http/message/src/Authentication/QueryParam.php
@@ -0,0 +1,50 @@
+
+ */
+final class QueryParam implements Authentication
+{
+ /**
+ * @var array
+ */
+ private $params = [];
+
+ /**
+ * @param array $params
+ */
+ public function __construct(array $params)
+ {
+ $this->params = $params;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function authenticate(RequestInterface $request)
+ {
+ $uri = $request->getUri();
+ $query = $uri->getQuery();
+ $params = [];
+
+ parse_str($query, $params);
+
+ $params = array_merge($params, $this->params);
+
+ $query = http_build_query($params, null, '&');
+
+ $uri = $uri->withQuery($query);
+
+ return $request->withUri($uri);
+ }
+}
diff --git a/vendor/php-http/message/src/Authentication/RequestConditional.php b/vendor/php-http/message/src/Authentication/RequestConditional.php
new file mode 100644
index 00000000..54774400
--- /dev/null
+++ b/vendor/php-http/message/src/Authentication/RequestConditional.php
@@ -0,0 +1,47 @@
+
+ */
+final class RequestConditional implements Authentication
+{
+ /**
+ * @var RequestMatcher
+ */
+ private $requestMatcher;
+
+ /**
+ * @var Authentication
+ */
+ private $authentication;
+
+ /**
+ * @param RequestMatcher $requestMatcher
+ * @param Authentication $authentication
+ */
+ public function __construct(RequestMatcher $requestMatcher, Authentication $authentication)
+ {
+ $this->requestMatcher = $requestMatcher;
+ $this->authentication = $authentication;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function authenticate(RequestInterface $request)
+ {
+ if ($this->requestMatcher->matches($request)) {
+ return $this->authentication->authenticate($request);
+ }
+
+ return $request;
+ }
+}
diff --git a/vendor/php-http/message/src/Authentication/Wsse.php b/vendor/php-http/message/src/Authentication/Wsse.php
new file mode 100644
index 00000000..fbbde336
--- /dev/null
+++ b/vendor/php-http/message/src/Authentication/Wsse.php
@@ -0,0 +1,58 @@
+
+ */
+final class Wsse implements Authentication
+{
+ /**
+ * @var string
+ */
+ private $username;
+
+ /**
+ * @var string
+ */
+ private $password;
+
+ /**
+ * @param string $username
+ * @param string $password
+ */
+ public function __construct($username, $password)
+ {
+ $this->username = $username;
+ $this->password = $password;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function authenticate(RequestInterface $request)
+ {
+ // TODO: generate better nonce?
+ $nonce = substr(md5(uniqid(uniqid().'_', true)), 0, 16);
+ $created = date('c');
+ $digest = base64_encode(sha1(base64_decode($nonce).$created.$this->password, true));
+
+ $wsse = sprintf(
+ 'UsernameToken Username="%s", PasswordDigest="%s", Nonce="%s", Created="%s"',
+ $this->username,
+ $digest,
+ $nonce,
+ $created
+ );
+
+ return $request
+ ->withHeader('Authorization', 'WSSE profile="UsernameToken"')
+ ->withHeader('X-WSSE', $wsse)
+ ;
+ }
+}
diff --git a/vendor/php-http/message/src/Builder/ResponseBuilder.php b/vendor/php-http/message/src/Builder/ResponseBuilder.php
new file mode 100644
index 00000000..de2e8828
--- /dev/null
+++ b/vendor/php-http/message/src/Builder/ResponseBuilder.php
@@ -0,0 +1,148 @@
+response = $response;
+ }
+
+ /**
+ * Return response.
+ *
+ * @return ResponseInterface
+ */
+ public function getResponse()
+ {
+ return $this->response;
+ }
+
+ /**
+ * Add headers represented by an array of header lines.
+ *
+ * @param string[] $headers Response headers as array of header lines.
+ *
+ * @return $this
+ *
+ * @throws \UnexpectedValueException For invalid header values.
+ * @throws \InvalidArgumentException For invalid status code arguments.
+ */
+ public function setHeadersFromArray(array $headers)
+ {
+ $status = array_shift($headers);
+ $this->setStatus($status);
+
+ foreach ($headers as $headerLine) {
+ $headerLine = trim($headerLine);
+ if ('' === $headerLine) {
+ continue;
+ }
+
+ $this->addHeader($headerLine);
+ }
+
+ return $this;
+ }
+
+ /**
+ * Add headers represented by a single string.
+ *
+ * @param string $headers Response headers as single string.
+ *
+ * @return $this
+ *
+ * @throws \InvalidArgumentException if $headers is not a string on object with __toString()
+ * @throws \UnexpectedValueException For invalid header values.
+ */
+ public function setHeadersFromString($headers)
+ {
+ if (!(is_string($headers)
+ || (is_object($headers) && method_exists($headers, '__toString')))
+ ) {
+ throw new \InvalidArgumentException(
+ sprintf(
+ '%s expects parameter 1 to be a string, %s given',
+ __METHOD__,
+ is_object($headers) ? get_class($headers) : gettype($headers)
+ )
+ );
+ }
+
+ $this->setHeadersFromArray(explode("\r\n", $headers));
+
+ return $this;
+ }
+
+ /**
+ * Set response status from a status string.
+ *
+ * @param string $statusLine Response status as a string.
+ *
+ * @return $this
+ *
+ * @throws \InvalidArgumentException For invalid status line.
+ */
+ public function setStatus($statusLine)
+ {
+ $parts = explode(' ', $statusLine, 3);
+ if (count($parts) < 2 || 0 !== strpos(strtolower($parts[0]), 'http/')) {
+ throw new \InvalidArgumentException(
+ sprintf('"%s" is not a valid HTTP status line', $statusLine)
+ );
+ }
+
+ $reasonPhrase = count($parts) > 2 ? $parts[2] : '';
+ $this->response = $this->response
+ ->withStatus((int) $parts[1], $reasonPhrase)
+ ->withProtocolVersion(substr($parts[0], 5));
+
+ return $this;
+ }
+
+ /**
+ * Add header represented by a string.
+ *
+ * @param string $headerLine Response header as a string.
+ *
+ * @return $this
+ *
+ * @throws \InvalidArgumentException For invalid header names or values.
+ */
+ public function addHeader($headerLine)
+ {
+ $parts = explode(':', $headerLine, 2);
+ if (2 !== count($parts)) {
+ throw new \InvalidArgumentException(
+ sprintf('"%s" is not a valid HTTP header line', $headerLine)
+ );
+ }
+ $name = trim($parts[0]);
+ $value = trim($parts[1]);
+ if ($this->response->hasHeader($name)) {
+ $this->response = $this->response->withAddedHeader($name, $value);
+ } else {
+ $this->response = $this->response->withHeader($name, $value);
+ }
+
+ return $this;
+ }
+}
diff --git a/vendor/php-http/message/src/Cookie.php b/vendor/php-http/message/src/Cookie.php
new file mode 100644
index 00000000..98ac57c0
--- /dev/null
+++ b/vendor/php-http/message/src/Cookie.php
@@ -0,0 +1,526 @@
+
+ *
+ * @see http://tools.ietf.org/search/rfc6265
+ */
+final class Cookie
+{
+ /**
+ * @var string
+ */
+ private $name;
+
+ /**
+ * @var string|null
+ */
+ private $value;
+
+ /**
+ * @var int|null
+ */
+ private $maxAge;
+
+ /**
+ * @var string|null
+ */
+ private $domain;
+
+ /**
+ * @var string
+ */
+ private $path;
+
+ /**
+ * @var bool
+ */
+ private $secure;
+
+ /**
+ * @var bool
+ */
+ private $httpOnly;
+
+ /**
+ * Expires attribute is HTTP 1.0 only and should be avoided.
+ *
+ * @var \DateTime|null
+ */
+ private $expires;
+
+ /**
+ * @param string $name
+ * @param string|null $value
+ * @param int|null $maxAge
+ * @param string|null $domain
+ * @param string|null $path
+ * @param bool $secure
+ * @param bool $httpOnly
+ * @param \DateTime|null $expires Expires attribute is HTTP 1.0 only and should be avoided.
+ *
+ * @throws \InvalidArgumentException If name, value or max age is not valid.
+ */
+ public function __construct(
+ $name,
+ $value = null,
+ $maxAge = null,
+ $domain = null,
+ $path = null,
+ $secure = false,
+ $httpOnly = false,
+ \DateTime $expires = null
+ ) {
+ $this->validateName($name);
+ $this->validateValue($value);
+ $this->validateMaxAge($maxAge);
+
+ $this->name = $name;
+ $this->value = $value;
+ $this->maxAge = $maxAge;
+ $this->expires = $expires;
+ $this->domain = $this->normalizeDomain($domain);
+ $this->path = $this->normalizePath($path);
+ $this->secure = (bool) $secure;
+ $this->httpOnly = (bool) $httpOnly;
+ }
+
+ /**
+ * Creates a new cookie without any attribute validation.
+ *
+ * @param string $name
+ * @param string|null $value
+ * @param int $maxAge
+ * @param string|null $domain
+ * @param string|null $path
+ * @param bool $secure
+ * @param bool $httpOnly
+ * @param \DateTime|null $expires Expires attribute is HTTP 1.0 only and should be avoided.
+ */
+ public static function createWithoutValidation(
+ $name,
+ $value = null,
+ $maxAge = null,
+ $domain = null,
+ $path = null,
+ $secure = false,
+ $httpOnly = false,
+ \DateTime $expires = null
+ ) {
+ $cookie = new self('name', null, null, $domain, $path, $secure, $httpOnly, $expires);
+ $cookie->name = $name;
+ $cookie->value = $value;
+ $cookie->maxAge = $maxAge;
+
+ return $cookie;
+ }
+
+ /**
+ * Returns the name.
+ *
+ * @return string
+ */
+ public function getName()
+ {
+ return $this->name;
+ }
+
+ /**
+ * Returns the value.
+ *
+ * @return string|null
+ */
+ public function getValue()
+ {
+ return $this->value;
+ }
+
+ /**
+ * Checks if there is a value.
+ *
+ * @return bool
+ */
+ public function hasValue()
+ {
+ return isset($this->value);
+ }
+
+ /**
+ * Sets the value.
+ *
+ * @param string|null $value
+ *
+ * @return Cookie
+ */
+ public function withValue($value)
+ {
+ $this->validateValue($value);
+
+ $new = clone $this;
+ $new->value = $value;
+
+ return $new;
+ }
+
+ /**
+ * Returns the max age.
+ *
+ * @return int|null
+ */
+ public function getMaxAge()
+ {
+ return $this->maxAge;
+ }
+
+ /**
+ * Checks if there is a max age.
+ *
+ * @return bool
+ */
+ public function hasMaxAge()
+ {
+ return isset($this->maxAge);
+ }
+
+ /**
+ * Sets the max age.
+ *
+ * @param int|null $maxAge
+ *
+ * @return Cookie
+ */
+ public function withMaxAge($maxAge)
+ {
+ $this->validateMaxAge($maxAge);
+
+ $new = clone $this;
+ $new->maxAge = $maxAge;
+
+ return $new;
+ }
+
+ /**
+ * Returns the expiration time.
+ *
+ * @return \DateTime|null
+ */
+ public function getExpires()
+ {
+ return $this->expires;
+ }
+
+ /**
+ * Checks if there is an expiration time.
+ *
+ * @return bool
+ */
+ public function hasExpires()
+ {
+ return isset($this->expires);
+ }
+
+ /**
+ * Sets the expires.
+ *
+ * @param \DateTime|null $expires
+ *
+ * @return Cookie
+ */
+ public function withExpires(\DateTime $expires = null)
+ {
+ $new = clone $this;
+ $new->expires = $expires;
+
+ return $new;
+ }
+
+ /**
+ * Checks if the cookie is expired.
+ *
+ * @return bool
+ */
+ public function isExpired()
+ {
+ return isset($this->expires) and $this->expires < new \DateTime();
+ }
+
+ /**
+ * Returns the domain.
+ *
+ * @return string|null
+ */
+ public function getDomain()
+ {
+ return $this->domain;
+ }
+
+ /**
+ * Checks if there is a domain.
+ *
+ * @return bool
+ */
+ public function hasDomain()
+ {
+ return isset($this->domain);
+ }
+
+ /**
+ * Sets the domain.
+ *
+ * @param string|null $domain
+ *
+ * @return Cookie
+ */
+ public function withDomain($domain)
+ {
+ $new = clone $this;
+ $new->domain = $this->normalizeDomain($domain);
+
+ return $new;
+ }
+
+ /**
+ * Checks whether this cookie is meant for this domain.
+ *
+ * @see http://tools.ietf.org/html/rfc6265#section-5.1.3
+ *
+ * @param string $domain
+ *
+ * @return bool
+ */
+ public function matchDomain($domain)
+ {
+ // Domain is not set or exact match
+ if (!$this->hasDomain() || 0 === strcasecmp($domain, $this->domain)) {
+ return true;
+ }
+
+ // Domain is not an IP address
+ if (filter_var($domain, FILTER_VALIDATE_IP)) {
+ return false;
+ }
+
+ return (bool) preg_match(sprintf('/\b%s$/i', preg_quote($this->domain)), $domain);
+ }
+
+ /**
+ * Returns the path.
+ *
+ * @return string
+ */
+ public function getPath()
+ {
+ return $this->path;
+ }
+
+ /**
+ * Sets the path.
+ *
+ * @param string|null $path
+ *
+ * @return Cookie
+ */
+ public function withPath($path)
+ {
+ $new = clone $this;
+ $new->path = $this->normalizePath($path);
+
+ return $new;
+ }
+
+ /**
+ * Checks whether this cookie is meant for this path.
+ *
+ * @see http://tools.ietf.org/html/rfc6265#section-5.1.4
+ *
+ * @param string $path
+ *
+ * @return bool
+ */
+ public function matchPath($path)
+ {
+ return $this->path === $path || (0 === strpos($path, rtrim($this->path, '/').'/'));
+ }
+
+ /**
+ * Checks whether this cookie may only be sent over HTTPS.
+ *
+ * @return bool
+ */
+ public function isSecure()
+ {
+ return $this->secure;
+ }
+
+ /**
+ * Sets whether this cookie should only be sent over HTTPS.
+ *
+ * @param bool $secure
+ *
+ * @return Cookie
+ */
+ public function withSecure($secure)
+ {
+ $new = clone $this;
+ $new->secure = (bool) $secure;
+
+ return $new;
+ }
+
+ /**
+ * Check whether this cookie may not be accessed through Javascript.
+ *
+ * @return bool
+ */
+ public function isHttpOnly()
+ {
+ return $this->httpOnly;
+ }
+
+ /**
+ * Sets whether this cookie may not be accessed through Javascript.
+ *
+ * @param bool $httpOnly
+ *
+ * @return Cookie
+ */
+ public function withHttpOnly($httpOnly)
+ {
+ $new = clone $this;
+ $new->httpOnly = (bool) $httpOnly;
+
+ return $new;
+ }
+
+ /**
+ * Checks if this cookie represents the same cookie as $cookie.
+ *
+ * This does not compare the values, only name, domain and path.
+ *
+ * @param Cookie $cookie
+ *
+ * @return bool
+ */
+ public function match(self $cookie)
+ {
+ return $this->name === $cookie->name && $this->domain === $cookie->domain and $this->path === $cookie->path;
+ }
+
+ /**
+ * Validates cookie attributes.
+ *
+ * @return bool
+ */
+ public function isValid()
+ {
+ try {
+ $this->validateName($this->name);
+ $this->validateValue($this->value);
+ $this->validateMaxAge($this->maxAge);
+ } catch (\InvalidArgumentException $e) {
+ return false;
+ }
+
+ return true;
+ }
+
+ /**
+ * Validates the name attribute.
+ *
+ * @see http://tools.ietf.org/search/rfc2616#section-2.2
+ *
+ * @param string $name
+ *
+ * @throws \InvalidArgumentException If the name is empty or contains invalid characters.
+ */
+ private function validateName($name)
+ {
+ if (strlen($name) < 1) {
+ throw new \InvalidArgumentException('The name cannot be empty');
+ }
+
+ // Name attribute is a token as per spec in RFC 2616
+ if (preg_match('/[\x00-\x20\x22\x28-\x29\x2C\x2F\x3A-\x40\x5B-\x5D\x7B\x7D\x7F]/', $name)) {
+ throw new \InvalidArgumentException(sprintf('The cookie name "%s" contains invalid characters.', $name));
+ }
+ }
+
+ /**
+ * Validates a value.
+ *
+ * @see http://tools.ietf.org/html/rfc6265#section-4.1.1
+ *
+ * @param string|null $value
+ *
+ * @throws \InvalidArgumentException If the value contains invalid characters.
+ */
+ private function validateValue($value)
+ {
+ if (isset($value)) {
+ if (preg_match('/[^\x21\x23-\x2B\x2D-\x3A\x3C-\x5B\x5D-\x7E]/', $value)) {
+ throw new \InvalidArgumentException(sprintf('The cookie value "%s" contains invalid characters.', $value));
+ }
+ }
+ }
+
+ /**
+ * Validates a Max-Age attribute.
+ *
+ * @param int|null $maxAge
+ *
+ * @throws \InvalidArgumentException If the Max-Age is not an empty or integer value.
+ */
+ private function validateMaxAge($maxAge)
+ {
+ if (isset($maxAge)) {
+ if (!is_int($maxAge)) {
+ throw new \InvalidArgumentException('Max-Age must be integer');
+ }
+ }
+ }
+
+ /**
+ * Remove the leading '.' and lowercase the domain as per spec in RFC 6265.
+ *
+ * @see http://tools.ietf.org/html/rfc6265#section-4.1.2.3
+ * @see http://tools.ietf.org/html/rfc6265#section-5.1.3
+ * @see http://tools.ietf.org/html/rfc6265#section-5.2.3
+ *
+ * @param string|null $domain
+ *
+ * @return string
+ */
+ private function normalizeDomain($domain)
+ {
+ if (isset($domain)) {
+ $domain = ltrim(strtolower($domain), '.');
+ }
+
+ return $domain;
+ }
+
+ /**
+ * Processes path as per spec in RFC 6265.
+ *
+ * @see http://tools.ietf.org/html/rfc6265#section-5.1.4
+ * @see http://tools.ietf.org/html/rfc6265#section-5.2.4
+ *
+ * @param string|null $path
+ *
+ * @return string
+ */
+ private function normalizePath($path)
+ {
+ $path = rtrim($path, '/');
+
+ if (empty($path) or '/' !== substr($path, 0, 1)) {
+ $path = '/';
+ }
+
+ return $path;
+ }
+}
diff --git a/vendor/php-http/message/src/CookieJar.php b/vendor/php-http/message/src/CookieJar.php
new file mode 100644
index 00000000..ab267d33
--- /dev/null
+++ b/vendor/php-http/message/src/CookieJar.php
@@ -0,0 +1,220 @@
+
+ */
+final class CookieJar implements \Countable, \IteratorAggregate
+{
+ /**
+ * @var \SplObjectStorage
+ */
+ protected $cookies;
+
+ public function __construct()
+ {
+ $this->cookies = new \SplObjectStorage();
+ }
+
+ /**
+ * Checks if there is a cookie.
+ *
+ * @param Cookie $cookie
+ *
+ * @return bool
+ */
+ public function hasCookie(Cookie $cookie)
+ {
+ return $this->cookies->contains($cookie);
+ }
+
+ /**
+ * Adds a cookie.
+ *
+ * @param Cookie $cookie
+ */
+ public function addCookie(Cookie $cookie)
+ {
+ if (!$this->hasCookie($cookie)) {
+ $cookies = $this->getMatchingCookies($cookie);
+
+ foreach ($cookies as $matchingCookie) {
+ if ($cookie->getValue() !== $matchingCookie->getValue() || $cookie->getMaxAge() > $matchingCookie->getMaxAge()) {
+ $this->removeCookie($matchingCookie);
+
+ continue;
+ }
+ }
+
+ if ($cookie->hasValue()) {
+ $this->cookies->attach($cookie);
+ }
+ }
+ }
+
+ /**
+ * Removes a cookie.
+ *
+ * @param Cookie $cookie
+ */
+ public function removeCookie(Cookie $cookie)
+ {
+ $this->cookies->detach($cookie);
+ }
+
+ /**
+ * Returns the cookies.
+ *
+ * @return Cookie[]
+ */
+ public function getCookies()
+ {
+ $match = function ($matchCookie) {
+ return true;
+ };
+
+ return $this->findMatchingCookies($match);
+ }
+
+ /**
+ * Returns all matching cookies.
+ *
+ * @param Cookie $cookie
+ *
+ * @return Cookie[]
+ */
+ public function getMatchingCookies(Cookie $cookie)
+ {
+ $match = function ($matchCookie) use ($cookie) {
+ return $matchCookie->match($cookie);
+ };
+
+ return $this->findMatchingCookies($match);
+ }
+
+ /**
+ * Finds matching cookies based on a callable.
+ *
+ * @param callable $match
+ *
+ * @return Cookie[]
+ */
+ protected function findMatchingCookies(callable $match)
+ {
+ $cookies = [];
+
+ foreach ($this->cookies as $cookie) {
+ if ($match($cookie)) {
+ $cookies[] = $cookie;
+ }
+ }
+
+ return $cookies;
+ }
+
+ /**
+ * Checks if there are cookies.
+ *
+ * @return bool
+ */
+ public function hasCookies()
+ {
+ return $this->cookies->count() > 0;
+ }
+
+ /**
+ * Sets the cookies and removes any previous one.
+ *
+ * @param Cookie[] $cookies
+ */
+ public function setCookies(array $cookies)
+ {
+ $this->clear();
+ $this->addCookies($cookies);
+ }
+
+ /**
+ * Adds some cookies.
+ *
+ * @param Cookie[] $cookies
+ */
+ public function addCookies(array $cookies)
+ {
+ foreach ($cookies as $cookie) {
+ $this->addCookie($cookie);
+ }
+ }
+
+ /**
+ * Removes some cookies.
+ *
+ * @param Cookie[] $cookies
+ */
+ public function removeCookies(array $cookies)
+ {
+ foreach ($cookies as $cookie) {
+ $this->removeCookie($cookie);
+ }
+ }
+
+ /**
+ * Removes cookies which match the given parameters.
+ *
+ * Null means that parameter should not be matched
+ *
+ * @param string|null $name
+ * @param string|null $domain
+ * @param string|null $path
+ */
+ public function removeMatchingCookies($name = null, $domain = null, $path = null)
+ {
+ $match = function ($cookie) use ($name, $domain, $path) {
+ $match = true;
+
+ if (isset($name)) {
+ $match = $match && ($cookie->getName() === $name);
+ }
+
+ if (isset($domain)) {
+ $match = $match && $cookie->matchDomain($domain);
+ }
+
+ if (isset($path)) {
+ $match = $match && $cookie->matchPath($path);
+ }
+
+ return $match;
+ };
+
+ $cookies = $this->findMatchingCookies($match);
+
+ $this->removeCookies($cookies);
+ }
+
+ /**
+ * Removes all cookies.
+ */
+ public function clear()
+ {
+ $this->cookies = new \SplObjectStorage();
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function count()
+ {
+ return $this->cookies->count();
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function getIterator()
+ {
+ return clone $this->cookies;
+ }
+}
diff --git a/vendor/php-http/message/src/CookieUtil.php b/vendor/php-http/message/src/CookieUtil.php
new file mode 100644
index 00000000..5c670d46
--- /dev/null
+++ b/vendor/php-http/message/src/CookieUtil.php
@@ -0,0 +1,53 @@
+
+ */
+trait MessageDecorator
+{
+ /**
+ * @var MessageInterface
+ */
+ private $message;
+
+ /**
+ * Returns the decorated message.
+ *
+ * Since the underlying Message is immutable as well
+ * exposing it is not an issue, because it's state cannot be altered
+ *
+ * @return MessageInterface
+ */
+ public function getMessage()
+ {
+ return $this->message;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function getProtocolVersion()
+ {
+ return $this->message->getProtocolVersion();
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function withProtocolVersion($version)
+ {
+ $new = clone $this;
+ $new->message = $this->message->withProtocolVersion($version);
+
+ return $new;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function getHeaders()
+ {
+ return $this->message->getHeaders();
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function hasHeader($header)
+ {
+ return $this->message->hasHeader($header);
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function getHeader($header)
+ {
+ return $this->message->getHeader($header);
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function getHeaderLine($header)
+ {
+ return $this->message->getHeaderLine($header);
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function withHeader($header, $value)
+ {
+ $new = clone $this;
+ $new->message = $this->message->withHeader($header, $value);
+
+ return $new;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function withAddedHeader($header, $value)
+ {
+ $new = clone $this;
+ $new->message = $this->message->withAddedHeader($header, $value);
+
+ return $new;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function withoutHeader($header)
+ {
+ $new = clone $this;
+ $new->message = $this->message->withoutHeader($header);
+
+ return $new;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function getBody()
+ {
+ return $this->message->getBody();
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function withBody(StreamInterface $body)
+ {
+ $new = clone $this;
+ $new->message = $this->message->withBody($body);
+
+ return $new;
+ }
+}
diff --git a/vendor/php-http/message/src/Decorator/RequestDecorator.php b/vendor/php-http/message/src/Decorator/RequestDecorator.php
new file mode 100644
index 00000000..7c50e588
--- /dev/null
+++ b/vendor/php-http/message/src/Decorator/RequestDecorator.php
@@ -0,0 +1,88 @@
+
+ */
+trait RequestDecorator
+{
+ use MessageDecorator {
+ getMessage as getRequest;
+ }
+
+ /**
+ * Exchanges the underlying request with another.
+ *
+ * @param RequestInterface $request
+ *
+ * @return self
+ */
+ public function withRequest(RequestInterface $request)
+ {
+ $new = clone $this;
+ $new->message = $request;
+
+ return $new;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function getRequestTarget()
+ {
+ return $this->message->getRequestTarget();
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function withRequestTarget($requestTarget)
+ {
+ $new = clone $this;
+ $new->message = $this->message->withRequestTarget($requestTarget);
+
+ return $new;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function getMethod()
+ {
+ return $this->message->getMethod();
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function withMethod($method)
+ {
+ $new = clone $this;
+ $new->message = $this->message->withMethod($method);
+
+ return $new;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function getUri()
+ {
+ return $this->message->getUri();
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function withUri(UriInterface $uri, $preserveHost = false)
+ {
+ $new = clone $this;
+ $new->message = $this->message->withUri($uri, $preserveHost);
+
+ return $new;
+ }
+}
diff --git a/vendor/php-http/message/src/Decorator/ResponseDecorator.php b/vendor/php-http/message/src/Decorator/ResponseDecorator.php
new file mode 100644
index 00000000..82d9ae08
--- /dev/null
+++ b/vendor/php-http/message/src/Decorator/ResponseDecorator.php
@@ -0,0 +1,57 @@
+
+ */
+trait ResponseDecorator
+{
+ use MessageDecorator {
+ getMessage as getResponse;
+ }
+
+ /**
+ * Exchanges the underlying response with another.
+ *
+ * @param ResponseInterface $response
+ *
+ * @return self
+ */
+ public function withResponse(ResponseInterface $response)
+ {
+ $new = clone $this;
+ $new->message = $response;
+
+ return $new;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function getStatusCode()
+ {
+ return $this->message->getStatusCode();
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function withStatus($code, $reasonPhrase = '')
+ {
+ $new = clone $this;
+ $new->message = $this->message->withStatus($code, $reasonPhrase);
+
+ return $new;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function getReasonPhrase()
+ {
+ return $this->message->getReasonPhrase();
+ }
+}
diff --git a/vendor/php-http/message/src/Decorator/StreamDecorator.php b/vendor/php-http/message/src/Decorator/StreamDecorator.php
new file mode 100644
index 00000000..f405c7af
--- /dev/null
+++ b/vendor/php-http/message/src/Decorator/StreamDecorator.php
@@ -0,0 +1,138 @@
+
+ */
+trait StreamDecorator
+{
+ /**
+ * @var StreamInterface
+ */
+ protected $stream;
+
+ /**
+ * {@inheritdoc}
+ */
+ public function __toString()
+ {
+ return $this->stream->__toString();
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function close()
+ {
+ $this->stream->close();
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function detach()
+ {
+ return $this->stream->detach();
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function getSize()
+ {
+ return $this->stream->getSize();
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function tell()
+ {
+ return $this->stream->tell();
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function eof()
+ {
+ return $this->stream->eof();
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function isSeekable()
+ {
+ return $this->stream->isSeekable();
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function seek($offset, $whence = SEEK_SET)
+ {
+ $this->stream->seek($offset, $whence);
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function rewind()
+ {
+ $this->stream->rewind();
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function isWritable()
+ {
+ return $this->stream->isWritable();
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function write($string)
+ {
+ return $this->stream->write($string);
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function isReadable()
+ {
+ return $this->stream->isReadable();
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function read($length)
+ {
+ return $this->stream->read($length);
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function getContents()
+ {
+ return $this->stream->getContents();
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function getMetadata($key = null)
+ {
+ return $this->stream->getMetadata($key);
+ }
+}
diff --git a/vendor/php-http/message/src/Encoding/ChunkStream.php b/vendor/php-http/message/src/Encoding/ChunkStream.php
new file mode 100644
index 00000000..74c2fbd0
--- /dev/null
+++ b/vendor/php-http/message/src/Encoding/ChunkStream.php
@@ -0,0 +1,39 @@
+
+ */
+class ChunkStream extends FilteredStream
+{
+ /**
+ * {@inheritdoc}
+ */
+ protected function readFilter()
+ {
+ return 'chunk';
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ protected function writeFilter()
+ {
+ return 'dechunk';
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ protected function fill()
+ {
+ parent::fill();
+
+ if ($this->stream->eof()) {
+ $this->buffer .= "0\r\n\r\n";
+ }
+ }
+}
diff --git a/vendor/php-http/message/src/Encoding/CompressStream.php b/vendor/php-http/message/src/Encoding/CompressStream.php
new file mode 100644
index 00000000..eeec6e03
--- /dev/null
+++ b/vendor/php-http/message/src/Encoding/CompressStream.php
@@ -0,0 +1,46 @@
+
+ */
+class CompressStream extends FilteredStream
+{
+ /**
+ * @param StreamInterface $stream
+ * @param int $level
+ */
+ public function __construct(StreamInterface $stream, $level = -1)
+ {
+ if (!extension_loaded('zlib')) {
+ throw new \RuntimeException('The zlib extension must be enabled to use this stream');
+ }
+
+ parent::__construct($stream, ['window' => 15, 'level' => $level]);
+
+ // @deprecated will be removed in 2.0
+ $this->writeFilterCallback = Filter\fun($this->writeFilter(), ['window' => 15]);
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ protected function readFilter()
+ {
+ return 'zlib.deflate';
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ protected function writeFilter()
+ {
+ return 'zlib.inflate';
+ }
+}
diff --git a/vendor/php-http/message/src/Encoding/DechunkStream.php b/vendor/php-http/message/src/Encoding/DechunkStream.php
new file mode 100644
index 00000000..4cade835
--- /dev/null
+++ b/vendor/php-http/message/src/Encoding/DechunkStream.php
@@ -0,0 +1,29 @@
+
+ */
+class DechunkStream extends FilteredStream
+{
+ /**
+ * {@inheritdoc}
+ */
+ protected function readFilter()
+ {
+ return 'dechunk';
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ protected function writeFilter()
+ {
+ return 'chunk';
+ }
+}
diff --git a/vendor/php-http/message/src/Encoding/DecompressStream.php b/vendor/php-http/message/src/Encoding/DecompressStream.php
new file mode 100644
index 00000000..9a950ea9
--- /dev/null
+++ b/vendor/php-http/message/src/Encoding/DecompressStream.php
@@ -0,0 +1,46 @@
+
+ */
+class DecompressStream extends FilteredStream
+{
+ /**
+ * @param StreamInterface $stream
+ * @param int $level
+ */
+ public function __construct(StreamInterface $stream, $level = -1)
+ {
+ if (!extension_loaded('zlib')) {
+ throw new \RuntimeException('The zlib extension must be enabled to use this stream');
+ }
+
+ parent::__construct($stream, ['window' => 15]);
+
+ // @deprecated will be removed in 2.0
+ $this->writeFilterCallback = Filter\fun($this->writeFilter(), ['window' => 15, 'level' => $level]);
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ protected function readFilter()
+ {
+ return 'zlib.inflate';
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ protected function writeFilter()
+ {
+ return 'zlib.deflate';
+ }
+}
diff --git a/vendor/php-http/message/src/Encoding/DeflateStream.php b/vendor/php-http/message/src/Encoding/DeflateStream.php
new file mode 100644
index 00000000..f7ce8e2d
--- /dev/null
+++ b/vendor/php-http/message/src/Encoding/DeflateStream.php
@@ -0,0 +1,42 @@
+
+ */
+class DeflateStream extends FilteredStream
+{
+ /**
+ * @param StreamInterface $stream
+ * @param int $level
+ */
+ public function __construct(StreamInterface $stream, $level = -1)
+ {
+ parent::__construct($stream, ['window' => -15, 'level' => $level]);
+
+ // @deprecated will be removed in 2.0
+ $this->writeFilterCallback = Filter\fun($this->writeFilter(), ['window' => -15]);
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ protected function readFilter()
+ {
+ return 'zlib.deflate';
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ protected function writeFilter()
+ {
+ return 'zlib.inflate';
+ }
+}
diff --git a/vendor/php-http/message/src/Encoding/Filter/Chunk.php b/vendor/php-http/message/src/Encoding/Filter/Chunk.php
new file mode 100644
index 00000000..0f8f53b3
--- /dev/null
+++ b/vendor/php-http/message/src/Encoding/Filter/Chunk.php
@@ -0,0 +1,30 @@
+
+ */
+class Chunk extends \php_user_filter
+{
+ /**
+ * {@inheritdoc}
+ */
+ public function filter($in, $out, &$consumed, $closing)
+ {
+ while ($bucket = stream_bucket_make_writeable($in)) {
+ $lenbucket = stream_bucket_new($this->stream, dechex($bucket->datalen)."\r\n");
+ stream_bucket_append($out, $lenbucket);
+
+ $consumed += $bucket->datalen;
+ stream_bucket_append($out, $bucket);
+
+ $lenbucket = stream_bucket_new($this->stream, "\r\n");
+ stream_bucket_append($out, $lenbucket);
+ }
+
+ return PSFS_PASS_ON;
+ }
+}
diff --git a/vendor/php-http/message/src/Encoding/FilteredStream.php b/vendor/php-http/message/src/Encoding/FilteredStream.php
new file mode 100644
index 00000000..7e5713e8
--- /dev/null
+++ b/vendor/php-http/message/src/Encoding/FilteredStream.php
@@ -0,0 +1,236 @@
+
+ */
+abstract class FilteredStream implements StreamInterface
+{
+ const BUFFER_SIZE = 8192;
+
+ use StreamDecorator {
+ rewind as private doRewind;
+ seek as private doSeek;
+ }
+
+ /**
+ * @var callable
+ */
+ protected $readFilterCallback;
+
+ /**
+ * @var resource
+ *
+ * @deprecated since version 1.5, will be removed in 2.0
+ */
+ protected $readFilter;
+
+ /**
+ * @var callable
+ *
+ * @deprecated since version 1.5, will be removed in 2.0
+ */
+ protected $writeFilterCallback;
+
+ /**
+ * @var resource
+ *
+ * @deprecated since version 1.5, will be removed in 2.0
+ */
+ protected $writeFilter;
+
+ /**
+ * Internal buffer.
+ *
+ * @var string
+ */
+ protected $buffer = '';
+
+ /**
+ * @param StreamInterface $stream
+ * @param mixed|null $readFilterOptions
+ * @param mixed|null $writeFilterOptions deprecated since 1.5, will be removed in 2.0
+ */
+ public function __construct(StreamInterface $stream, $readFilterOptions = null, $writeFilterOptions = null)
+ {
+ if (null !== $readFilterOptions) {
+ $this->readFilterCallback = Filter\fun($this->readFilter(), $readFilterOptions);
+ } else {
+ $this->readFilterCallback = Filter\fun($this->readFilter());
+ }
+
+ if (null !== $writeFilterOptions) {
+ $this->writeFilterCallback = Filter\fun($this->writeFilter(), $writeFilterOptions);
+
+ @trigger_error('The $writeFilterOptions argument is deprecated since version 1.5 and will be removed in 2.0.', E_USER_DEPRECATED);
+ } else {
+ $this->writeFilterCallback = Filter\fun($this->writeFilter());
+ }
+
+ $this->stream = $stream;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function read($length)
+ {
+ if (strlen($this->buffer) >= $length) {
+ $read = substr($this->buffer, 0, $length);
+ $this->buffer = substr($this->buffer, $length);
+
+ return $read;
+ }
+
+ if ($this->stream->eof()) {
+ $buffer = $this->buffer;
+ $this->buffer = '';
+
+ return $buffer;
+ }
+
+ $read = $this->buffer;
+ $this->buffer = '';
+ $this->fill();
+
+ return $read.$this->read($length - strlen($read));
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function eof()
+ {
+ return $this->stream->eof() && '' === $this->buffer;
+ }
+
+ /**
+ * Buffer is filled by reading underlying stream.
+ *
+ * Callback is reading once more even if the stream is ended.
+ * This allow to get last data in the PHP buffer otherwise this
+ * bug is present : https://bugs.php.net/bug.php?id=48725
+ */
+ protected function fill()
+ {
+ $readFilterCallback = $this->readFilterCallback;
+ $this->buffer .= $readFilterCallback($this->stream->read(self::BUFFER_SIZE));
+
+ if ($this->stream->eof()) {
+ $this->buffer .= $readFilterCallback();
+ }
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function getContents()
+ {
+ $buffer = '';
+
+ while (!$this->eof()) {
+ $buf = $this->read(self::BUFFER_SIZE);
+ // Using a loose equality here to match on '' and false.
+ if (null == $buf) {
+ break;
+ }
+
+ $buffer .= $buf;
+ }
+
+ return $buffer;
+ }
+
+ /**
+ * Always returns null because we can't tell the size of a stream when we filter.
+ */
+ public function getSize()
+ {
+ return null;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function __toString()
+ {
+ return $this->getContents();
+ }
+
+ /**
+ * Filtered streams are not seekable.
+ *
+ * We would need to buffer and process everything to allow seeking.
+ */
+ public function isSeekable()
+ {
+ return false;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function rewind()
+ {
+ @trigger_error('Filtered streams are not seekable. This method will start raising an exception in the next major version', E_USER_DEPRECATED);
+ $this->doRewind();
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function seek($offset, $whence = SEEK_SET)
+ {
+ @trigger_error('Filtered streams are not seekable. This method will start raising an exception in the next major version', E_USER_DEPRECATED);
+ $this->doSeek($offset, $whence);
+ }
+
+ /**
+ * Returns the read filter name.
+ *
+ * @return string
+ *
+ * @deprecated since version 1.5, will be removed in 2.0
+ */
+ public function getReadFilter()
+ {
+ @trigger_error('The '.__CLASS__.'::'.__METHOD__.' method is deprecated since version 1.5 and will be removed in 2.0.', E_USER_DEPRECATED);
+
+ return $this->readFilter();
+ }
+
+ /**
+ * Returns the write filter name.
+ *
+ * @return string
+ */
+ abstract protected function readFilter();
+
+ /**
+ * Returns the write filter name.
+ *
+ * @return string
+ *
+ * @deprecated since version 1.5, will be removed in 2.0
+ */
+ public function getWriteFilter()
+ {
+ @trigger_error('The '.__CLASS__.'::'.__METHOD__.' method is deprecated since version 1.5 and will be removed in 2.0.', E_USER_DEPRECATED);
+
+ return $this->writeFilter();
+ }
+
+ /**
+ * Returns the write filter name.
+ *
+ * @return string
+ */
+ abstract protected function writeFilter();
+}
diff --git a/vendor/php-http/message/src/Encoding/GzipDecodeStream.php b/vendor/php-http/message/src/Encoding/GzipDecodeStream.php
new file mode 100644
index 00000000..19a2b8f0
--- /dev/null
+++ b/vendor/php-http/message/src/Encoding/GzipDecodeStream.php
@@ -0,0 +1,46 @@
+
+ */
+class GzipDecodeStream extends FilteredStream
+{
+ /**
+ * @param StreamInterface $stream
+ * @param int $level
+ */
+ public function __construct(StreamInterface $stream, $level = -1)
+ {
+ if (!extension_loaded('zlib')) {
+ throw new \RuntimeException('The zlib extension must be enabled to use this stream');
+ }
+
+ parent::__construct($stream, ['window' => 31]);
+
+ // @deprecated will be removed in 2.0
+ $this->writeFilterCallback = Filter\fun($this->writeFilter(), ['window' => 31, 'level' => $level]);
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ protected function readFilter()
+ {
+ return 'zlib.inflate';
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ protected function writeFilter()
+ {
+ return 'zlib.deflate';
+ }
+}
diff --git a/vendor/php-http/message/src/Encoding/GzipEncodeStream.php b/vendor/php-http/message/src/Encoding/GzipEncodeStream.php
new file mode 100644
index 00000000..8555d4ae
--- /dev/null
+++ b/vendor/php-http/message/src/Encoding/GzipEncodeStream.php
@@ -0,0 +1,46 @@
+
+ */
+class GzipEncodeStream extends FilteredStream
+{
+ /**
+ * @param StreamInterface $stream
+ * @param int $level
+ */
+ public function __construct(StreamInterface $stream, $level = -1)
+ {
+ if (!extension_loaded('zlib')) {
+ throw new \RuntimeException('The zlib extension must be enabled to use this stream');
+ }
+
+ parent::__construct($stream, ['window' => 31, 'level' => $level]);
+
+ // @deprecated will be removed in 2.0
+ $this->writeFilterCallback = Filter\fun($this->writeFilter(), ['window' => 31]);
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ protected function readFilter()
+ {
+ return 'zlib.deflate';
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ protected function writeFilter()
+ {
+ return 'zlib.inflate';
+ }
+}
diff --git a/vendor/php-http/message/src/Encoding/InflateStream.php b/vendor/php-http/message/src/Encoding/InflateStream.php
new file mode 100644
index 00000000..863b73de
--- /dev/null
+++ b/vendor/php-http/message/src/Encoding/InflateStream.php
@@ -0,0 +1,46 @@
+
+ */
+class InflateStream extends FilteredStream
+{
+ /**
+ * @param StreamInterface $stream
+ * @param int $level
+ */
+ public function __construct(StreamInterface $stream, $level = -1)
+ {
+ if (!extension_loaded('zlib')) {
+ throw new \RuntimeException('The zlib extension must be enabled to use this stream');
+ }
+
+ parent::__construct($stream, ['window' => -15]);
+
+ // @deprecated will be removed in 2.0
+ $this->writeFilterCallback = Filter\fun($this->writeFilter(), ['window' => -15, 'level' => $level]);
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ protected function readFilter()
+ {
+ return 'zlib.inflate';
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ protected function writeFilter()
+ {
+ return 'zlib.deflate';
+ }
+}
diff --git a/vendor/php-http/message/src/Exception.php b/vendor/php-http/message/src/Exception.php
new file mode 100644
index 00000000..80d4cd9d
--- /dev/null
+++ b/vendor/php-http/message/src/Exception.php
@@ -0,0 +1,10 @@
+
+ */
+interface Formatter
+{
+ /**
+ * Formats a request.
+ *
+ * @param RequestInterface $request
+ *
+ * @return string
+ */
+ public function formatRequest(RequestInterface $request);
+
+ /**
+ * Formats a response.
+ *
+ * @param ResponseInterface $response
+ *
+ * @return string
+ */
+ public function formatResponse(ResponseInterface $response);
+}
diff --git a/vendor/php-http/message/src/Formatter/CurlCommandFormatter.php b/vendor/php-http/message/src/Formatter/CurlCommandFormatter.php
new file mode 100644
index 00000000..80602508
--- /dev/null
+++ b/vendor/php-http/message/src/Formatter/CurlCommandFormatter.php
@@ -0,0 +1,91 @@
+
+ */
+class CurlCommandFormatter implements Formatter
+{
+ /**
+ * {@inheritdoc}
+ */
+ public function formatRequest(RequestInterface $request)
+ {
+ $command = sprintf('curl %s', escapeshellarg((string) $request->getUri()->withFragment('')));
+ if ('1.0' === $request->getProtocolVersion()) {
+ $command .= ' --http1.0';
+ } elseif ('2.0' === $request->getProtocolVersion()) {
+ $command .= ' --http2';
+ }
+
+ $method = strtoupper($request->getMethod());
+ if ('HEAD' === $method) {
+ $command .= ' --head';
+ } elseif ('GET' !== $method) {
+ $command .= ' --request '.$method;
+ }
+
+ $command .= $this->getHeadersAsCommandOptions($request);
+
+ $body = $request->getBody();
+ if ($body->getSize() > 0) {
+ if ($body->isSeekable()) {
+ $data = $body->__toString();
+ $body->rewind();
+ if (preg_match('/[\x00-\x1F\x7F]/', $data)) {
+ $data = '[binary stream omitted]';
+ }
+ } else {
+ $data = '[non-seekable stream omitted]';
+ }
+ $escapedData = @escapeshellarg($data);
+ if (empty($escapedData)) {
+ $escapedData = 'We couldn\'t not escape the data properly';
+ }
+
+ $command .= sprintf(' --data %s', $escapedData);
+ }
+
+ return $command;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function formatResponse(ResponseInterface $response)
+ {
+ return '';
+ }
+
+ /**
+ * @param RequestInterface $request
+ *
+ * @return string
+ */
+ private function getHeadersAsCommandOptions(RequestInterface $request)
+ {
+ $command = '';
+ foreach ($request->getHeaders() as $name => $values) {
+ if ('host' === strtolower($name) && $values[0] === $request->getUri()->getHost()) {
+ continue;
+ }
+
+ if ('user-agent' === strtolower($name)) {
+ $command .= sprintf(' -A %s', escapeshellarg($values[0]));
+
+ continue;
+ }
+
+ $command .= sprintf(' -H %s', escapeshellarg($name.': '.$request->getHeaderLine($name)));
+ }
+
+ return $command;
+ }
+}
diff --git a/vendor/php-http/message/src/Formatter/FullHttpMessageFormatter.php b/vendor/php-http/message/src/Formatter/FullHttpMessageFormatter.php
new file mode 100644
index 00000000..1918c597
--- /dev/null
+++ b/vendor/php-http/message/src/Formatter/FullHttpMessageFormatter.php
@@ -0,0 +1,91 @@
+
+ */
+class FullHttpMessageFormatter implements Formatter
+{
+ /**
+ * The maximum length of the body.
+ *
+ * @var int
+ */
+ private $maxBodyLength;
+
+ /**
+ * @param int $maxBodyLength
+ */
+ public function __construct($maxBodyLength = 1000)
+ {
+ $this->maxBodyLength = $maxBodyLength;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function formatRequest(RequestInterface $request)
+ {
+ $message = sprintf(
+ "%s %s HTTP/%s\n",
+ $request->getMethod(),
+ $request->getRequestTarget(),
+ $request->getProtocolVersion()
+ );
+
+ foreach ($request->getHeaders() as $name => $values) {
+ $message .= $name.': '.implode(', ', $values)."\n";
+ }
+
+ return $this->addBody($request, $message);
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function formatResponse(ResponseInterface $response)
+ {
+ $message = sprintf(
+ "HTTP/%s %s %s\n",
+ $response->getProtocolVersion(),
+ $response->getStatusCode(),
+ $response->getReasonPhrase()
+ );
+
+ foreach ($response->getHeaders() as $name => $values) {
+ $message .= $name.': '.implode(', ', $values)."\n";
+ }
+
+ return $this->addBody($response, $message);
+ }
+
+ /**
+ * Add the message body if the stream is seekable.
+ *
+ * @param MessageInterface $request
+ * @param string $message
+ *
+ * @return string
+ */
+ private function addBody(MessageInterface $request, $message)
+ {
+ $stream = $request->getBody();
+ if (!$stream->isSeekable() || 0 === $this->maxBodyLength) {
+ // Do not read the stream
+ $message .= "\n";
+ } else {
+ $message .= "\n".mb_substr($stream->__toString(), 0, $this->maxBodyLength);
+ $stream->rewind();
+ }
+
+ return $message;
+ }
+}
diff --git a/vendor/php-http/message/src/Formatter/SimpleFormatter.php b/vendor/php-http/message/src/Formatter/SimpleFormatter.php
new file mode 100644
index 00000000..b1fcabdb
--- /dev/null
+++ b/vendor/php-http/message/src/Formatter/SimpleFormatter.php
@@ -0,0 +1,42 @@
+
+ * @author Márk Sági-Kazár
+ */
+class SimpleFormatter implements Formatter
+{
+ /**
+ * {@inheritdoc}
+ */
+ public function formatRequest(RequestInterface $request)
+ {
+ return sprintf(
+ '%s %s %s',
+ $request->getMethod(),
+ $request->getUri()->__toString(),
+ $request->getProtocolVersion()
+ );
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function formatResponse(ResponseInterface $response)
+ {
+ return sprintf(
+ '%s %s %s',
+ $response->getStatusCode(),
+ $response->getReasonPhrase(),
+ $response->getProtocolVersion()
+ );
+ }
+}
diff --git a/vendor/php-http/message/src/MessageFactory/DiactorosMessageFactory.php b/vendor/php-http/message/src/MessageFactory/DiactorosMessageFactory.php
new file mode 100644
index 00000000..53f08ae0
--- /dev/null
+++ b/vendor/php-http/message/src/MessageFactory/DiactorosMessageFactory.php
@@ -0,0 +1,61 @@
+
+ */
+final class DiactorosMessageFactory implements MessageFactory
+{
+ /**
+ * @var DiactorosStreamFactory
+ */
+ private $streamFactory;
+
+ public function __construct()
+ {
+ $this->streamFactory = new DiactorosStreamFactory();
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function createRequest(
+ $method,
+ $uri,
+ array $headers = [],
+ $body = null,
+ $protocolVersion = '1.1'
+ ) {
+ return (new Request(
+ $uri,
+ $method,
+ $this->streamFactory->createStream($body),
+ $headers
+ ))->withProtocolVersion($protocolVersion);
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function createResponse(
+ $statusCode = 200,
+ $reasonPhrase = null,
+ array $headers = [],
+ $body = null,
+ $protocolVersion = '1.1'
+ ) {
+ return (new Response(
+ $this->streamFactory->createStream($body),
+ $statusCode,
+ $headers
+ ))->withProtocolVersion($protocolVersion);
+ }
+}
diff --git a/vendor/php-http/message/src/MessageFactory/GuzzleMessageFactory.php b/vendor/php-http/message/src/MessageFactory/GuzzleMessageFactory.php
new file mode 100644
index 00000000..59eb6551
--- /dev/null
+++ b/vendor/php-http/message/src/MessageFactory/GuzzleMessageFactory.php
@@ -0,0 +1,53 @@
+
+ */
+final class GuzzleMessageFactory implements MessageFactory
+{
+ /**
+ * {@inheritdoc}
+ */
+ public function createRequest(
+ $method,
+ $uri,
+ array $headers = [],
+ $body = null,
+ $protocolVersion = '1.1'
+ ) {
+ return new Request(
+ $method,
+ $uri,
+ $headers,
+ $body,
+ $protocolVersion
+ );
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function createResponse(
+ $statusCode = 200,
+ $reasonPhrase = null,
+ array $headers = [],
+ $body = null,
+ $protocolVersion = '1.1'
+ ) {
+ return new Response(
+ $statusCode,
+ $headers,
+ $body,
+ $protocolVersion,
+ $reasonPhrase
+ );
+ }
+}
diff --git a/vendor/php-http/message/src/MessageFactory/SlimMessageFactory.php b/vendor/php-http/message/src/MessageFactory/SlimMessageFactory.php
new file mode 100644
index 00000000..cdad2ec2
--- /dev/null
+++ b/vendor/php-http/message/src/MessageFactory/SlimMessageFactory.php
@@ -0,0 +1,72 @@
+
+ */
+final class SlimMessageFactory implements MessageFactory
+{
+ /**
+ * @var SlimStreamFactory
+ */
+ private $streamFactory;
+
+ /**
+ * @var SlimUriFactory
+ */
+ private $uriFactory;
+
+ public function __construct()
+ {
+ $this->streamFactory = new SlimStreamFactory();
+ $this->uriFactory = new SlimUriFactory();
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function createRequest(
+ $method,
+ $uri,
+ array $headers = [],
+ $body = null,
+ $protocolVersion = '1.1'
+ ) {
+ return (new Request(
+ $method,
+ $this->uriFactory->createUri($uri),
+ new Headers($headers),
+ [],
+ [],
+ $this->streamFactory->createStream($body),
+ []
+ ))->withProtocolVersion($protocolVersion);
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function createResponse(
+ $statusCode = 200,
+ $reasonPhrase = null,
+ array $headers = [],
+ $body = null,
+ $protocolVersion = '1.1'
+ ) {
+ return (new Response(
+ $statusCode,
+ new Headers($headers),
+ $this->streamFactory->createStream($body)
+ ))->withProtocolVersion($protocolVersion);
+ }
+}
diff --git a/vendor/php-http/message/src/RequestMatcher.php b/vendor/php-http/message/src/RequestMatcher.php
new file mode 100644
index 00000000..94fe5324
--- /dev/null
+++ b/vendor/php-http/message/src/RequestMatcher.php
@@ -0,0 +1,26 @@
+
+ */
+interface RequestMatcher
+{
+ /**
+ * Decides whether the rule(s) implemented by the strategy matches the supplied request.
+ *
+ * @param RequestInterface $request The PSR7 request to check for a match
+ *
+ * @return bool true if the request matches, false otherwise
+ */
+ public function matches(RequestInterface $request);
+}
diff --git a/vendor/php-http/message/src/RequestMatcher/CallbackRequestMatcher.php b/vendor/php-http/message/src/RequestMatcher/CallbackRequestMatcher.php
new file mode 100644
index 00000000..4d45e32e
--- /dev/null
+++ b/vendor/php-http/message/src/RequestMatcher/CallbackRequestMatcher.php
@@ -0,0 +1,35 @@
+
+ */
+final class CallbackRequestMatcher implements RequestMatcher
+{
+ /**
+ * @var callable
+ */
+ private $callback;
+
+ /**
+ * @param callable $callback
+ */
+ public function __construct(callable $callback)
+ {
+ $this->callback = $callback;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function matches(RequestInterface $request)
+ {
+ return (bool) call_user_func($this->callback, $request);
+ }
+}
diff --git a/vendor/php-http/message/src/RequestMatcher/RegexRequestMatcher.php b/vendor/php-http/message/src/RequestMatcher/RegexRequestMatcher.php
new file mode 100644
index 00000000..91f3729e
--- /dev/null
+++ b/vendor/php-http/message/src/RequestMatcher/RegexRequestMatcher.php
@@ -0,0 +1,41 @@
+
+ *
+ * @deprecated since version 1.2 and will be removed in 2.0. Use {@link RequestMatcher} instead.
+ */
+final class RegexRequestMatcher implements RequestMatcher
+{
+ /**
+ * Matching regex.
+ *
+ * @var string
+ */
+ private $regex;
+
+ /**
+ * @param string $regex
+ */
+ public function __construct($regex)
+ {
+ $this->regex = $regex;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function matches(RequestInterface $request)
+ {
+ return (bool) preg_match($this->regex, (string) $request->getUri());
+ }
+}
diff --git a/vendor/php-http/message/src/RequestMatcher/RequestMatcher.php b/vendor/php-http/message/src/RequestMatcher/RequestMatcher.php
new file mode 100644
index 00000000..e2aa0212
--- /dev/null
+++ b/vendor/php-http/message/src/RequestMatcher/RequestMatcher.php
@@ -0,0 +1,78 @@
+
+ * @author Joel Wurtz
+ */
+final class RequestMatcher implements RequestMatcherInterface
+{
+ /**
+ * @var string
+ */
+ private $path;
+
+ /**
+ * @var string
+ */
+ private $host;
+
+ /**
+ * @var array
+ */
+ private $methods = [];
+
+ /**
+ * @var string[]
+ */
+ private $schemes = [];
+
+ /**
+ * The regular expressions used for path or host must be specified without delimiter.
+ * You do not need to escape the forward slash / to match it.
+ *
+ * @param string|null $path Regular expression for the path
+ * @param string|null $host Regular expression for the hostname
+ * @param string|string[]|null $methods Method or list of methods to match
+ * @param string|string[]|null $schemes Scheme or list of schemes to match (e.g. http or https)
+ */
+ public function __construct($path = null, $host = null, $methods = [], $schemes = [])
+ {
+ $this->path = $path;
+ $this->host = $host;
+ $this->methods = array_map('strtoupper', (array) $methods);
+ $this->schemes = array_map('strtolower', (array) $schemes);
+ }
+
+ /**
+ * {@inheritdoc}
+ *
+ * @api
+ */
+ public function matches(RequestInterface $request)
+ {
+ if ($this->schemes && !in_array($request->getUri()->getScheme(), $this->schemes)) {
+ return false;
+ }
+
+ if ($this->methods && !in_array($request->getMethod(), $this->methods)) {
+ return false;
+ }
+
+ if (null !== $this->path && !preg_match('{'.$this->path.'}', rawurldecode($request->getUri()->getPath()))) {
+ return false;
+ }
+
+ if (null !== $this->host && !preg_match('{'.$this->host.'}i', $request->getUri()->getHost())) {
+ return false;
+ }
+
+ return true;
+ }
+}
diff --git a/vendor/php-http/message/src/Stream/BufferedStream.php b/vendor/php-http/message/src/Stream/BufferedStream.php
new file mode 100644
index 00000000..1eac9747
--- /dev/null
+++ b/vendor/php-http/message/src/Stream/BufferedStream.php
@@ -0,0 +1,270 @@
+stream = $stream;
+ $this->size = $stream->getSize();
+
+ if ($useFileBuffer) {
+ $this->resource = fopen('php://temp/maxmemory:'.$memoryBuffer, 'rw+');
+ } else {
+ $this->resource = fopen('php://memory', 'rw+');
+ }
+
+ if (false === $this->resource) {
+ throw new \RuntimeException('Cannot create a resource over temp or memory implementation');
+ }
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function __toString()
+ {
+ try {
+ $this->rewind();
+
+ return $this->getContents();
+ } catch (\Throwable $throwable) {
+ return '';
+ } catch (\Exception $exception) { // Layer to be BC with PHP 5, remove this when we only support PHP 7+
+ return '';
+ }
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function close()
+ {
+ if (null === $this->resource) {
+ throw new \RuntimeException('Cannot close on a detached stream');
+ }
+
+ $this->stream->close();
+ fclose($this->resource);
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function detach()
+ {
+ if (null === $this->resource) {
+ return;
+ }
+
+ // Force reading the remaining data of the stream
+ $this->getContents();
+
+ $resource = $this->resource;
+ $this->stream->close();
+ $this->stream = null;
+ $this->resource = null;
+
+ return $resource;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function getSize()
+ {
+ if (null === $this->resource) {
+ return;
+ }
+
+ if (null === $this->size && $this->stream->eof()) {
+ return $this->written;
+ }
+
+ return $this->size;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function tell()
+ {
+ if (null === $this->resource) {
+ throw new \RuntimeException('Cannot tell on a detached stream');
+ }
+
+ return ftell($this->resource);
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function eof()
+ {
+ if (null === $this->resource) {
+ throw new \RuntimeException('Cannot call eof on a detached stream');
+ }
+
+ // We are at the end only when both our resource and underlying stream are at eof
+ return $this->stream->eof() && (ftell($this->resource) === $this->written);
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function isSeekable()
+ {
+ return null !== $this->resource;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function seek($offset, $whence = SEEK_SET)
+ {
+ if (null === $this->resource) {
+ throw new \RuntimeException('Cannot seek on a detached stream');
+ }
+
+ fseek($this->resource, $offset, $whence);
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function rewind()
+ {
+ if (null === $this->resource) {
+ throw new \RuntimeException('Cannot rewind on a detached stream');
+ }
+
+ rewind($this->resource);
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function isWritable()
+ {
+ return false;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function write($string)
+ {
+ throw new \RuntimeException('Cannot write on this stream');
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function isReadable()
+ {
+ return null !== $this->resource;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function read($length)
+ {
+ if (null === $this->resource) {
+ throw new \RuntimeException('Cannot read on a detached stream');
+ }
+
+ $read = '';
+
+ // First read from the resource
+ if (ftell($this->resource) !== $this->written) {
+ $read = fread($this->resource, $length);
+ }
+
+ $bytesRead = strlen($read);
+
+ if ($bytesRead < $length) {
+ $streamRead = $this->stream->read($length - $bytesRead);
+
+ // Write on the underlying stream what we read
+ $this->written += fwrite($this->resource, $streamRead);
+ $read .= $streamRead;
+ }
+
+ return $read;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function getContents()
+ {
+ if (null === $this->resource) {
+ throw new \RuntimeException('Cannot read on a detached stream');
+ }
+
+ $read = '';
+
+ while (!$this->eof()) {
+ $read .= $this->read(8192);
+ }
+
+ return $read;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function getMetadata($key = null)
+ {
+ if (null === $this->resource) {
+ if (null === $key) {
+ return [];
+ }
+
+ return;
+ }
+
+ $metadata = stream_get_meta_data($this->resource);
+
+ if (null === $key) {
+ return $metadata;
+ }
+
+ if (!array_key_exists($key, $metadata)) {
+ return;
+ }
+
+ return $metadata[$key];
+ }
+}
diff --git a/vendor/php-http/message/src/StreamFactory/DiactorosStreamFactory.php b/vendor/php-http/message/src/StreamFactory/DiactorosStreamFactory.php
new file mode 100644
index 00000000..a75ec988
--- /dev/null
+++ b/vendor/php-http/message/src/StreamFactory/DiactorosStreamFactory.php
@@ -0,0 +1,36 @@
+
+ */
+final class DiactorosStreamFactory implements StreamFactory
+{
+ /**
+ * {@inheritdoc}
+ */
+ public function createStream($body = null)
+ {
+ if ($body instanceof StreamInterface) {
+ return $body;
+ }
+
+ if (is_resource($body)) {
+ return new Stream($body);
+ }
+
+ $stream = new Stream('php://memory', 'rw');
+ if (null !== $body && '' !== $body) {
+ $stream->write((string) $body);
+ }
+
+ return $stream;
+ }
+}
diff --git a/vendor/php-http/message/src/StreamFactory/GuzzleStreamFactory.php b/vendor/php-http/message/src/StreamFactory/GuzzleStreamFactory.php
new file mode 100644
index 00000000..10f4d3f9
--- /dev/null
+++ b/vendor/php-http/message/src/StreamFactory/GuzzleStreamFactory.php
@@ -0,0 +1,21 @@
+
+ */
+final class GuzzleStreamFactory implements StreamFactory
+{
+ /**
+ * {@inheritdoc}
+ */
+ public function createStream($body = null)
+ {
+ return \GuzzleHttp\Psr7\stream_for($body);
+ }
+}
diff --git a/vendor/php-http/message/src/StreamFactory/SlimStreamFactory.php b/vendor/php-http/message/src/StreamFactory/SlimStreamFactory.php
new file mode 100644
index 00000000..efcadc43
--- /dev/null
+++ b/vendor/php-http/message/src/StreamFactory/SlimStreamFactory.php
@@ -0,0 +1,37 @@
+
+ */
+final class SlimStreamFactory implements StreamFactory
+{
+ /**
+ * {@inheritdoc}
+ */
+ public function createStream($body = null)
+ {
+ if ($body instanceof StreamInterface) {
+ return $body;
+ }
+
+ if (is_resource($body)) {
+ return new Stream($body);
+ }
+
+ $resource = fopen('php://memory', 'r+');
+ $stream = new Stream($resource);
+ if (null !== $body && '' !== $body) {
+ $stream->write((string) $body);
+ }
+
+ return $stream;
+ }
+}
diff --git a/vendor/php-http/message/src/UriFactory/DiactorosUriFactory.php b/vendor/php-http/message/src/UriFactory/DiactorosUriFactory.php
new file mode 100644
index 00000000..268c361d
--- /dev/null
+++ b/vendor/php-http/message/src/UriFactory/DiactorosUriFactory.php
@@ -0,0 +1,29 @@
+
+ */
+final class DiactorosUriFactory implements UriFactory
+{
+ /**
+ * {@inheritdoc}
+ */
+ public function createUri($uri)
+ {
+ if ($uri instanceof UriInterface) {
+ return $uri;
+ } elseif (is_string($uri)) {
+ return new Uri($uri);
+ }
+
+ throw new \InvalidArgumentException('URI must be a string or UriInterface');
+ }
+}
diff --git a/vendor/php-http/message/src/UriFactory/GuzzleUriFactory.php b/vendor/php-http/message/src/UriFactory/GuzzleUriFactory.php
new file mode 100644
index 00000000..4c1c286c
--- /dev/null
+++ b/vendor/php-http/message/src/UriFactory/GuzzleUriFactory.php
@@ -0,0 +1,22 @@
+
+ */
+final class GuzzleUriFactory implements UriFactory
+{
+ /**
+ * {@inheritdoc}
+ */
+ public function createUri($uri)
+ {
+ return Psr7\uri_for($uri);
+ }
+}
diff --git a/vendor/php-http/message/src/UriFactory/SlimUriFactory.php b/vendor/php-http/message/src/UriFactory/SlimUriFactory.php
new file mode 100644
index 00000000..c013d542
--- /dev/null
+++ b/vendor/php-http/message/src/UriFactory/SlimUriFactory.php
@@ -0,0 +1,31 @@
+
+ */
+final class SlimUriFactory implements UriFactory
+{
+ /**
+ * {@inheritdoc}
+ */
+ public function createUri($uri)
+ {
+ if ($uri instanceof UriInterface) {
+ return $uri;
+ }
+
+ if (is_string($uri)) {
+ return Uri::createFromString($uri);
+ }
+
+ throw new \InvalidArgumentException('URI must be a string or UriInterface');
+ }
+}
diff --git a/vendor/php-http/message/src/filters.php b/vendor/php-http/message/src/filters.php
new file mode 100644
index 00000000..15ed73de
--- /dev/null
+++ b/vendor/php-http/message/src/filters.php
@@ -0,0 +1,6 @@
+
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is furnished
+to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+THE SOFTWARE.
diff --git a/vendor/php-http/promise/README.md b/vendor/php-http/promise/README.md
new file mode 100644
index 00000000..adda2aeb
--- /dev/null
+++ b/vendor/php-http/promise/README.md
@@ -0,0 +1,49 @@
+# Promise
+
+[![Latest Version](https://img.shields.io/github/release/php-http/promise.svg?style=flat-square)](https://github.com/php-http/promise/releases)
+[![Software License](https://img.shields.io/badge/license-MIT-brightgreen.svg?style=flat-square)](LICENSE)
+[![Build Status](https://img.shields.io/travis/php-http/promise.svg?style=flat-square)](https://travis-ci.org/php-http/promise)
+[![Code Coverage](https://img.shields.io/scrutinizer/coverage/g/php-http/promise.svg?style=flat-square)](https://scrutinizer-ci.com/g/php-http/promise)
+[![Quality Score](https://img.shields.io/scrutinizer/g/php-http/promise.svg?style=flat-square)](https://scrutinizer-ci.com/g/php-http/promise)
+[![Total Downloads](https://img.shields.io/packagist/dt/php-http/promise.svg?style=flat-square)](https://packagist.org/packages/php-http/promise)
+
+**Promise used for asynchronous HTTP requests.**
+
+**Note:** This will eventually be removed/deprecated and replaced with the upcoming Promise PSR.
+
+
+## Install
+
+Via Composer
+
+``` bash
+$ composer require php-http/promise
+```
+
+
+## Documentation
+
+Please see the [official documentation](http://docs.php-http.org).
+
+
+## Testing
+
+``` bash
+$ composer test
+```
+
+
+## Contributing
+
+Please see our [contributing guide](http://docs.php-http.org/en/latest/development/contributing.html).
+
+
+## Security
+
+If you discover any security related issues, please contact us at [security@httplug.io](mailto:security@httplug.io)
+or [security@php-http.org](mailto:security@php-http.org).
+
+
+## License
+
+The MIT License (MIT). Please see [License File](LICENSE) for more information.
diff --git a/vendor/php-http/promise/composer.json b/vendor/php-http/promise/composer.json
new file mode 100644
index 00000000..ff1d2cee
--- /dev/null
+++ b/vendor/php-http/promise/composer.json
@@ -0,0 +1,35 @@
+{
+ "name": "php-http/promise",
+ "description": "Promise used for asynchronous HTTP requests",
+ "license": "MIT",
+ "keywords": ["promise"],
+ "homepage": "http://httplug.io",
+ "authors": [
+ {
+ "name": "Joel Wurtz",
+ "email": "joel.wurtz@gmail.com"
+ },
+ {
+ "name": "Márk Sági-Kazár",
+ "email": "mark.sagikazar@gmail.com"
+ }
+ ],
+ "require-dev": {
+ "phpspec/phpspec": "^2.4",
+ "henrikbjorn/phpspec-code-coverage" : "^1.0"
+ },
+ "autoload": {
+ "psr-4": {
+ "Http\\Promise\\": "src/"
+ }
+ },
+ "scripts": {
+ "test": "vendor/bin/phpspec run",
+ "test-ci": "vendor/bin/phpspec run -c phpspec.yml.ci"
+ },
+ "extra": {
+ "branch-alias": {
+ "dev-master": "1.1-dev"
+ }
+ }
+}
diff --git a/vendor/php-http/promise/src/FulfilledPromise.php b/vendor/php-http/promise/src/FulfilledPromise.php
new file mode 100644
index 00000000..f60f686a
--- /dev/null
+++ b/vendor/php-http/promise/src/FulfilledPromise.php
@@ -0,0 +1,58 @@
+
+ */
+final class FulfilledPromise implements Promise
+{
+ /**
+ * @var mixed
+ */
+ private $result;
+
+ /**
+ * @param $result
+ */
+ public function __construct($result)
+ {
+ $this->result = $result;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function then(callable $onFulfilled = null, callable $onRejected = null)
+ {
+ if (null === $onFulfilled) {
+ return $this;
+ }
+
+ try {
+ return new self($onFulfilled($this->result));
+ } catch (\Exception $e) {
+ return new RejectedPromise($e);
+ }
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function getState()
+ {
+ return Promise::FULFILLED;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function wait($unwrap = true)
+ {
+ if ($unwrap) {
+ return $this->result;
+ }
+ }
+}
diff --git a/vendor/php-http/promise/src/Promise.php b/vendor/php-http/promise/src/Promise.php
new file mode 100644
index 00000000..e2cf5f89
--- /dev/null
+++ b/vendor/php-http/promise/src/Promise.php
@@ -0,0 +1,69 @@
+
+ * @author Márk Sági-Kazár
+ */
+interface Promise
+{
+ /**
+ * Promise has not been fulfilled or rejected.
+ */
+ const PENDING = 'pending';
+
+ /**
+ * Promise has been fulfilled.
+ */
+ const FULFILLED = 'fulfilled';
+
+ /**
+ * Promise has been rejected.
+ */
+ const REJECTED = 'rejected';
+
+ /**
+ * Adds behavior for when the promise is resolved or rejected (response will be available, or error happens).
+ *
+ * If you do not care about one of the cases, you can set the corresponding callable to null
+ * The callback will be called when the value arrived and never more than once.
+ *
+ * @param callable $onFulfilled Called when a response will be available.
+ * @param callable $onRejected Called when an exception occurs.
+ *
+ * @return Promise A new resolved promise with value of the executed callback (onFulfilled / onRejected).
+ */
+ public function then(callable $onFulfilled = null, callable $onRejected = null);
+
+ /**
+ * Returns the state of the promise, one of PENDING, FULFILLED or REJECTED.
+ *
+ * @return string
+ */
+ public function getState();
+
+ /**
+ * Wait for the promise to be fulfilled or rejected.
+ *
+ * When this method returns, the request has been resolved and if callables have been
+ * specified, the appropriate one has terminated.
+ *
+ * When $unwrap is true (the default), the response is returned, or the exception thrown
+ * on failure. Otherwise, nothing is returned or thrown.
+ *
+ * @param bool $unwrap Whether to return resolved value / throw reason or not
+ *
+ * @return mixed Resolved value, null if $unwrap is set to false
+ *
+ * @throws \Exception The rejection reason if $unwrap is set to true and the request failed.
+ */
+ public function wait($unwrap = true);
+}
diff --git a/vendor/php-http/promise/src/RejectedPromise.php b/vendor/php-http/promise/src/RejectedPromise.php
new file mode 100644
index 00000000..e396a40f
--- /dev/null
+++ b/vendor/php-http/promise/src/RejectedPromise.php
@@ -0,0 +1,58 @@
+
+ */
+final class RejectedPromise implements Promise
+{
+ /**
+ * @var \Exception
+ */
+ private $exception;
+
+ /**
+ * @param \Exception $exception
+ */
+ public function __construct(\Exception $exception)
+ {
+ $this->exception = $exception;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function then(callable $onFulfilled = null, callable $onRejected = null)
+ {
+ if (null === $onRejected) {
+ return $this;
+ }
+
+ try {
+ return new FulfilledPromise($onRejected($this->exception));
+ } catch (\Exception $e) {
+ return new self($e);
+ }
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function getState()
+ {
+ return Promise::REJECTED;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function wait($unwrap = true)
+ {
+ if ($unwrap) {
+ throw $this->exception;
+ }
+ }
+}
diff --git a/vendor/psr/cache/CHANGELOG.md b/vendor/psr/cache/CHANGELOG.md
new file mode 100644
index 00000000..58ddab05
--- /dev/null
+++ b/vendor/psr/cache/CHANGELOG.md
@@ -0,0 +1,16 @@
+# Changelog
+
+All notable changes to this project will be documented in this file, in reverse chronological order by release.
+
+## 1.0.1 - 2016-08-06
+
+### Fixed
+
+- Make spacing consistent in phpdoc annotations php-fig/cache#9 - chalasr
+- Fix grammar in phpdoc annotations php-fig/cache#10 - chalasr
+- Be more specific in docblocks that `getItems()` and `deleteItems()` take an array of strings (`string[]`) compared to just `array` php-fig/cache#8 - GrahamCampbell
+- For `expiresAt()` and `expiresAfter()` in CacheItemInterface fix docblock to specify null as a valid parameters as well as an implementation of DateTimeInterface php-fig/cache#7 - GrahamCampbell
+
+## 1.0.0 - 2015-12-11
+
+Initial stable release; reflects accepted PSR-6 specification
diff --git a/vendor/psr/cache/LICENSE.txt b/vendor/psr/cache/LICENSE.txt
new file mode 100644
index 00000000..b1c2c97b
--- /dev/null
+++ b/vendor/psr/cache/LICENSE.txt
@@ -0,0 +1,19 @@
+Copyright (c) 2015 PHP Framework Interoperability Group
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+THE SOFTWARE.
diff --git a/vendor/psr/cache/README.md b/vendor/psr/cache/README.md
new file mode 100644
index 00000000..c8706cee
--- /dev/null
+++ b/vendor/psr/cache/README.md
@@ -0,0 +1,9 @@
+PSR Cache
+=========
+
+This repository holds all interfaces defined by
+[PSR-6](http://www.php-fig.org/psr/psr-6/).
+
+Note that this is not a Cache implementation of its own. It is merely an
+interface that describes a Cache implementation. See the specification for more
+details.
diff --git a/vendor/psr/cache/composer.json b/vendor/psr/cache/composer.json
new file mode 100644
index 00000000..e828fec9
--- /dev/null
+++ b/vendor/psr/cache/composer.json
@@ -0,0 +1,25 @@
+{
+ "name": "psr/cache",
+ "description": "Common interface for caching libraries",
+ "keywords": ["psr", "psr-6", "cache"],
+ "license": "MIT",
+ "authors": [
+ {
+ "name": "PHP-FIG",
+ "homepage": "http://www.php-fig.org/"
+ }
+ ],
+ "require": {
+ "php": ">=5.3.0"
+ },
+ "autoload": {
+ "psr-4": {
+ "Psr\\Cache\\": "src/"
+ }
+ },
+ "extra": {
+ "branch-alias": {
+ "dev-master": "1.0.x-dev"
+ }
+ }
+}
diff --git a/vendor/psr/cache/src/CacheException.php b/vendor/psr/cache/src/CacheException.php
new file mode 100644
index 00000000..e27f22f8
--- /dev/null
+++ b/vendor/psr/cache/src/CacheException.php
@@ -0,0 +1,10 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\OptionsResolver\Debug;
+
+use Symfony\Component\OptionsResolver\Exception\NoConfigurationException;
+use Symfony\Component\OptionsResolver\Exception\UndefinedOptionsException;
+use Symfony\Component\OptionsResolver\OptionsResolver;
+
+/**
+ * @author Maxime Steinhausser
+ *
+ * @final
+ */
+class OptionsResolverIntrospector
+{
+ private $get;
+
+ public function __construct(OptionsResolver $optionsResolver)
+ {
+ $this->get = \Closure::bind(function ($property, $option, $message) {
+ /** @var OptionsResolver $this */
+ if (!$this->isDefined($option)) {
+ throw new UndefinedOptionsException(sprintf('The option "%s" does not exist.', $option));
+ }
+
+ if (!array_key_exists($option, $this->{$property})) {
+ throw new NoConfigurationException($message);
+ }
+
+ return $this->{$property}[$option];
+ }, $optionsResolver, $optionsResolver);
+ }
+
+ /**
+ * @return mixed
+ *
+ * @throws NoConfigurationException on no configured value
+ */
+ public function getDefault(string $option)
+ {
+ return ($this->get)('defaults', $option, sprintf('No default value was set for the "%s" option.', $option));
+ }
+
+ /**
+ * @return \Closure[]
+ *
+ * @throws NoConfigurationException on no configured closures
+ */
+ public function getLazyClosures(string $option): array
+ {
+ return ($this->get)('lazy', $option, sprintf('No lazy closures were set for the "%s" option.', $option));
+ }
+
+ /**
+ * @return string[]
+ *
+ * @throws NoConfigurationException on no configured types
+ */
+ public function getAllowedTypes(string $option): array
+ {
+ return ($this->get)('allowedTypes', $option, sprintf('No allowed types were set for the "%s" option.', $option));
+ }
+
+ /**
+ * @return mixed[]
+ *
+ * @throws NoConfigurationException on no configured values
+ */
+ public function getAllowedValues(string $option): array
+ {
+ return ($this->get)('allowedValues', $option, sprintf('No allowed values were set for the "%s" option.', $option));
+ }
+
+ /**
+ * @throws NoConfigurationException on no configured normalizer
+ */
+ public function getNormalizer(string $option): \Closure
+ {
+ return ($this->get)('normalizers', $option, sprintf('No normalizer was set for the "%s" option.', $option));
+ }
+
+ /**
+ * @return string|\Closure
+ *
+ * @throws NoConfigurationException on no configured deprecation
+ */
+ public function getDeprecationMessage(string $option)
+ {
+ return ($this->get)('deprecated', $option, sprintf('No deprecation was set for the "%s" option.', $option));
+ }
+}
diff --git a/vendor/symfony/options-resolver/Exception/AccessException.php b/vendor/symfony/options-resolver/Exception/AccessException.php
new file mode 100644
index 00000000..c12b6806
--- /dev/null
+++ b/vendor/symfony/options-resolver/Exception/AccessException.php
@@ -0,0 +1,22 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\OptionsResolver\Exception;
+
+/**
+ * Thrown when trying to read an option outside of or write it inside of
+ * {@link \Symfony\Component\OptionsResolver\Options::resolve()}.
+ *
+ * @author Bernhard Schussek
+ */
+class AccessException extends \LogicException implements ExceptionInterface
+{
+}
diff --git a/vendor/symfony/options-resolver/Exception/ExceptionInterface.php b/vendor/symfony/options-resolver/Exception/ExceptionInterface.php
new file mode 100644
index 00000000..ea99d050
--- /dev/null
+++ b/vendor/symfony/options-resolver/Exception/ExceptionInterface.php
@@ -0,0 +1,21 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\OptionsResolver\Exception;
+
+/**
+ * Marker interface for all exceptions thrown by the OptionsResolver component.
+ *
+ * @author Bernhard Schussek
+ */
+interface ExceptionInterface extends \Throwable
+{
+}
diff --git a/vendor/symfony/options-resolver/Exception/InvalidArgumentException.php b/vendor/symfony/options-resolver/Exception/InvalidArgumentException.php
new file mode 100644
index 00000000..6d421d68
--- /dev/null
+++ b/vendor/symfony/options-resolver/Exception/InvalidArgumentException.php
@@ -0,0 +1,21 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\OptionsResolver\Exception;
+
+/**
+ * Thrown when an argument is invalid.
+ *
+ * @author Bernhard Schussek
+ */
+class InvalidArgumentException extends \InvalidArgumentException implements ExceptionInterface
+{
+}
diff --git a/vendor/symfony/options-resolver/Exception/InvalidOptionsException.php b/vendor/symfony/options-resolver/Exception/InvalidOptionsException.php
new file mode 100644
index 00000000..6fd4f125
--- /dev/null
+++ b/vendor/symfony/options-resolver/Exception/InvalidOptionsException.php
@@ -0,0 +1,23 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\OptionsResolver\Exception;
+
+/**
+ * Thrown when the value of an option does not match its validation rules.
+ *
+ * You should make sure a valid value is passed to the option.
+ *
+ * @author Bernhard Schussek
+ */
+class InvalidOptionsException extends InvalidArgumentException
+{
+}
diff --git a/vendor/symfony/options-resolver/Exception/MissingOptionsException.php b/vendor/symfony/options-resolver/Exception/MissingOptionsException.php
new file mode 100644
index 00000000..faa487f1
--- /dev/null
+++ b/vendor/symfony/options-resolver/Exception/MissingOptionsException.php
@@ -0,0 +1,23 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\OptionsResolver\Exception;
+
+/**
+ * Exception thrown when a required option is missing.
+ *
+ * Add the option to the passed options array.
+ *
+ * @author Bernhard Schussek
+ */
+class MissingOptionsException extends InvalidArgumentException
+{
+}
diff --git a/vendor/symfony/options-resolver/Exception/NoConfigurationException.php b/vendor/symfony/options-resolver/Exception/NoConfigurationException.php
new file mode 100644
index 00000000..6693ec14
--- /dev/null
+++ b/vendor/symfony/options-resolver/Exception/NoConfigurationException.php
@@ -0,0 +1,26 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\OptionsResolver\Exception;
+
+use Symfony\Component\OptionsResolver\Debug\OptionsResolverIntrospector;
+
+/**
+ * Thrown when trying to introspect an option definition property
+ * for which no value was configured inside the OptionsResolver instance.
+ *
+ * @see OptionsResolverIntrospector
+ *
+ * @author Maxime Steinhausser
+ */
+class NoConfigurationException extends \RuntimeException implements ExceptionInterface
+{
+}
diff --git a/vendor/symfony/options-resolver/Exception/NoSuchOptionException.php b/vendor/symfony/options-resolver/Exception/NoSuchOptionException.php
new file mode 100644
index 00000000..4c3280f4
--- /dev/null
+++ b/vendor/symfony/options-resolver/Exception/NoSuchOptionException.php
@@ -0,0 +1,26 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\OptionsResolver\Exception;
+
+/**
+ * Thrown when trying to read an option that has no value set.
+ *
+ * When accessing optional options from within a lazy option or normalizer you should first
+ * check whether the optional option is set. You can do this with `isset($options['optional'])`.
+ * In contrast to the {@link UndefinedOptionsException}, this is a runtime exception that can
+ * occur when evaluating lazy options.
+ *
+ * @author Tobias Schultze
+ */
+class NoSuchOptionException extends \OutOfBoundsException implements ExceptionInterface
+{
+}
diff --git a/vendor/symfony/options-resolver/Exception/OptionDefinitionException.php b/vendor/symfony/options-resolver/Exception/OptionDefinitionException.php
new file mode 100644
index 00000000..e8e339d4
--- /dev/null
+++ b/vendor/symfony/options-resolver/Exception/OptionDefinitionException.php
@@ -0,0 +1,21 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\OptionsResolver\Exception;
+
+/**
+ * Thrown when two lazy options have a cyclic dependency.
+ *
+ * @author Bernhard Schussek
+ */
+class OptionDefinitionException extends \LogicException implements ExceptionInterface
+{
+}
diff --git a/vendor/symfony/options-resolver/Exception/UndefinedOptionsException.php b/vendor/symfony/options-resolver/Exception/UndefinedOptionsException.php
new file mode 100644
index 00000000..6ca3fce4
--- /dev/null
+++ b/vendor/symfony/options-resolver/Exception/UndefinedOptionsException.php
@@ -0,0 +1,24 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\OptionsResolver\Exception;
+
+/**
+ * Exception thrown when an undefined option is passed.
+ *
+ * You should remove the options in question from your code or define them
+ * beforehand.
+ *
+ * @author Bernhard Schussek
+ */
+class UndefinedOptionsException extends InvalidArgumentException
+{
+}
diff --git a/vendor/symfony/options-resolver/LICENSE b/vendor/symfony/options-resolver/LICENSE
new file mode 100644
index 00000000..a677f437
--- /dev/null
+++ b/vendor/symfony/options-resolver/LICENSE
@@ -0,0 +1,19 @@
+Copyright (c) 2004-2019 Fabien Potencier
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is furnished
+to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+THE SOFTWARE.
diff --git a/vendor/symfony/options-resolver/Options.php b/vendor/symfony/options-resolver/Options.php
new file mode 100644
index 00000000..d18374cb
--- /dev/null
+++ b/vendor/symfony/options-resolver/Options.php
@@ -0,0 +1,24 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\OptionsResolver;
+
+/**
+ * Contains resolved option values.
+ *
+ * @author Bernhard Schussek
+ * @author Tobias Schultze
+ *
+ * @method mixed offsetGet(string $option, bool $triggerDeprecation = true)
+ */
+interface Options extends \ArrayAccess, \Countable
+{
+}
diff --git a/vendor/symfony/options-resolver/OptionsResolver.php b/vendor/symfony/options-resolver/OptionsResolver.php
new file mode 100644
index 00000000..5d238f50
--- /dev/null
+++ b/vendor/symfony/options-resolver/OptionsResolver.php
@@ -0,0 +1,1149 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\OptionsResolver;
+
+use Symfony\Component\OptionsResolver\Exception\AccessException;
+use Symfony\Component\OptionsResolver\Exception\InvalidArgumentException;
+use Symfony\Component\OptionsResolver\Exception\InvalidOptionsException;
+use Symfony\Component\OptionsResolver\Exception\MissingOptionsException;
+use Symfony\Component\OptionsResolver\Exception\NoSuchOptionException;
+use Symfony\Component\OptionsResolver\Exception\OptionDefinitionException;
+use Symfony\Component\OptionsResolver\Exception\UndefinedOptionsException;
+
+/**
+ * Validates options and merges them with default values.
+ *
+ * @author Bernhard Schussek
+ * @author Tobias Schultze
+ */
+class OptionsResolver implements Options
+{
+ /**
+ * The names of all defined options.
+ */
+ private $defined = array();
+
+ /**
+ * The default option values.
+ */
+ private $defaults = array();
+
+ /**
+ * A list of closure for nested options.
+ *
+ * @var \Closure[][]
+ */
+ private $nested = array();
+
+ /**
+ * The names of required options.
+ */
+ private $required = array();
+
+ /**
+ * The resolved option values.
+ */
+ private $resolved = array();
+
+ /**
+ * A list of normalizer closures.
+ *
+ * @var \Closure[]
+ */
+ private $normalizers = array();
+
+ /**
+ * A list of accepted values for each option.
+ */
+ private $allowedValues = array();
+
+ /**
+ * A list of accepted types for each option.
+ */
+ private $allowedTypes = array();
+
+ /**
+ * A list of closures for evaluating lazy options.
+ */
+ private $lazy = array();
+
+ /**
+ * A list of lazy options whose closure is currently being called.
+ *
+ * This list helps detecting circular dependencies between lazy options.
+ */
+ private $calling = array();
+
+ /**
+ * A list of deprecated options.
+ */
+ private $deprecated = array();
+
+ /**
+ * The list of options provided by the user.
+ */
+ private $given = array();
+
+ /**
+ * Whether the instance is locked for reading.
+ *
+ * Once locked, the options cannot be changed anymore. This is
+ * necessary in order to avoid inconsistencies during the resolving
+ * process. If any option is changed after being read, all evaluated
+ * lazy options that depend on this option would become invalid.
+ */
+ private $locked = false;
+
+ private static $typeAliases = array(
+ 'boolean' => 'bool',
+ 'integer' => 'int',
+ 'double' => 'float',
+ );
+
+ /**
+ * Sets the default value of a given option.
+ *
+ * If the default value should be set based on other options, you can pass
+ * a closure with the following signature:
+ *
+ * function (Options $options) {
+ * // ...
+ * }
+ *
+ * The closure will be evaluated when {@link resolve()} is called. The
+ * closure has access to the resolved values of other options through the
+ * passed {@link Options} instance:
+ *
+ * function (Options $options) {
+ * if (isset($options['port'])) {
+ * // ...
+ * }
+ * }
+ *
+ * If you want to access the previously set default value, add a second
+ * argument to the closure's signature:
+ *
+ * $options->setDefault('name', 'Default Name');
+ *
+ * $options->setDefault('name', function (Options $options, $previousValue) {
+ * // 'Default Name' === $previousValue
+ * });
+ *
+ * This is mostly useful if the configuration of the {@link Options} object
+ * is spread across different locations of your code, such as base and
+ * sub-classes.
+ *
+ * If you want to define nested options, you can pass a closure with the
+ * following signature:
+ *
+ * $options->setDefault('database', function (OptionsResolver $resolver) {
+ * $resolver->setDefined(array('dbname', 'host', 'port', 'user', 'pass'));
+ * }
+ *
+ * To get access to the parent options, add a second argument to the closure's
+ * signature:
+ *
+ * function (OptionsResolver $resolver, Options $parent) {
+ * // 'default' === $parent['connection']
+ * }
+ *
+ * @param string $option The name of the option
+ * @param mixed $value The default value of the option
+ *
+ * @return $this
+ *
+ * @throws AccessException If called from a lazy option or normalizer
+ */
+ public function setDefault($option, $value)
+ {
+ // Setting is not possible once resolving starts, because then lazy
+ // options could manipulate the state of the object, leading to
+ // inconsistent results.
+ if ($this->locked) {
+ throw new AccessException('Default values cannot be set from a lazy option or normalizer.');
+ }
+
+ // If an option is a closure that should be evaluated lazily, store it
+ // in the "lazy" property.
+ if ($value instanceof \Closure) {
+ $reflClosure = new \ReflectionFunction($value);
+ $params = $reflClosure->getParameters();
+
+ if (isset($params[0]) && null !== ($class = $params[0]->getClass()) && Options::class === $class->name) {
+ // Initialize the option if no previous value exists
+ if (!isset($this->defaults[$option])) {
+ $this->defaults[$option] = null;
+ }
+
+ // Ignore previous lazy options if the closure has no second parameter
+ if (!isset($this->lazy[$option]) || !isset($params[1])) {
+ $this->lazy[$option] = array();
+ }
+
+ // Store closure for later evaluation
+ $this->lazy[$option][] = $value;
+ $this->defined[$option] = true;
+
+ // Make sure the option is processed and is not nested anymore
+ unset($this->resolved[$option], $this->nested[$option]);
+
+ return $this;
+ }
+
+ if (isset($params[0]) && null !== ($class = $params[0]->getClass()) && self::class === $class->name && (!isset($params[1]) || (null !== ($class = $params[1]->getClass()) && Options::class === $class->name))) {
+ // Store closure for later evaluation
+ $this->nested[$option][] = $value;
+ $this->defaults[$option] = array();
+ $this->defined[$option] = true;
+
+ // Make sure the option is processed and is not lazy anymore
+ unset($this->resolved[$option], $this->lazy[$option]);
+
+ return $this;
+ }
+ }
+
+ // This option is not lazy nor nested anymore
+ unset($this->lazy[$option], $this->nested[$option]);
+
+ // Yet undefined options can be marked as resolved, because we only need
+ // to resolve options with lazy closures, normalizers or validation
+ // rules, none of which can exist for undefined options
+ // If the option was resolved before, update the resolved value
+ if (!isset($this->defined[$option]) || array_key_exists($option, $this->resolved)) {
+ $this->resolved[$option] = $value;
+ }
+
+ $this->defaults[$option] = $value;
+ $this->defined[$option] = true;
+
+ return $this;
+ }
+
+ /**
+ * Sets a list of default values.
+ *
+ * @param array $defaults The default values to set
+ *
+ * @return $this
+ *
+ * @throws AccessException If called from a lazy option or normalizer
+ */
+ public function setDefaults(array $defaults)
+ {
+ foreach ($defaults as $option => $value) {
+ $this->setDefault($option, $value);
+ }
+
+ return $this;
+ }
+
+ /**
+ * Returns whether a default value is set for an option.
+ *
+ * Returns true if {@link setDefault()} was called for this option.
+ * An option is also considered set if it was set to null.
+ *
+ * @param string $option The option name
+ *
+ * @return bool Whether a default value is set
+ */
+ public function hasDefault($option)
+ {
+ return array_key_exists($option, $this->defaults);
+ }
+
+ /**
+ * Marks one or more options as required.
+ *
+ * @param string|string[] $optionNames One or more option names
+ *
+ * @return $this
+ *
+ * @throws AccessException If called from a lazy option or normalizer
+ */
+ public function setRequired($optionNames)
+ {
+ if ($this->locked) {
+ throw new AccessException('Options cannot be made required from a lazy option or normalizer.');
+ }
+
+ foreach ((array) $optionNames as $option) {
+ $this->defined[$option] = true;
+ $this->required[$option] = true;
+ }
+
+ return $this;
+ }
+
+ /**
+ * Returns whether an option is required.
+ *
+ * An option is required if it was passed to {@link setRequired()}.
+ *
+ * @param string $option The name of the option
+ *
+ * @return bool Whether the option is required
+ */
+ public function isRequired($option)
+ {
+ return isset($this->required[$option]);
+ }
+
+ /**
+ * Returns the names of all required options.
+ *
+ * @return string[] The names of the required options
+ *
+ * @see isRequired()
+ */
+ public function getRequiredOptions()
+ {
+ return array_keys($this->required);
+ }
+
+ /**
+ * Returns whether an option is missing a default value.
+ *
+ * An option is missing if it was passed to {@link setRequired()}, but not
+ * to {@link setDefault()}. This option must be passed explicitly to
+ * {@link resolve()}, otherwise an exception will be thrown.
+ *
+ * @param string $option The name of the option
+ *
+ * @return bool Whether the option is missing
+ */
+ public function isMissing($option)
+ {
+ return isset($this->required[$option]) && !array_key_exists($option, $this->defaults);
+ }
+
+ /**
+ * Returns the names of all options missing a default value.
+ *
+ * @return string[] The names of the missing options
+ *
+ * @see isMissing()
+ */
+ public function getMissingOptions()
+ {
+ return array_keys(array_diff_key($this->required, $this->defaults));
+ }
+
+ /**
+ * Defines a valid option name.
+ *
+ * Defines an option name without setting a default value. The option will
+ * be accepted when passed to {@link resolve()}. When not passed, the
+ * option will not be included in the resolved options.
+ *
+ * @param string|string[] $optionNames One or more option names
+ *
+ * @return $this
+ *
+ * @throws AccessException If called from a lazy option or normalizer
+ */
+ public function setDefined($optionNames)
+ {
+ if ($this->locked) {
+ throw new AccessException('Options cannot be defined from a lazy option or normalizer.');
+ }
+
+ foreach ((array) $optionNames as $option) {
+ $this->defined[$option] = true;
+ }
+
+ return $this;
+ }
+
+ /**
+ * Returns whether an option is defined.
+ *
+ * Returns true for any option passed to {@link setDefault()},
+ * {@link setRequired()} or {@link setDefined()}.
+ *
+ * @param string $option The option name
+ *
+ * @return bool Whether the option is defined
+ */
+ public function isDefined($option)
+ {
+ return isset($this->defined[$option]);
+ }
+
+ /**
+ * Returns the names of all defined options.
+ *
+ * @return string[] The names of the defined options
+ *
+ * @see isDefined()
+ */
+ public function getDefinedOptions()
+ {
+ return array_keys($this->defined);
+ }
+
+ public function isNested(string $option): bool
+ {
+ return isset($this->nested[$option]);
+ }
+
+ /**
+ * Deprecates an option, allowed types or values.
+ *
+ * Instead of passing the message, you may also pass a closure with the
+ * following signature:
+ *
+ * function (Options $options, $value): string {
+ * // ...
+ * }
+ *
+ * The closure receives the value as argument and should return a string.
+ * Return an empty string to ignore the option deprecation.
+ *
+ * The closure is invoked when {@link resolve()} is called. The parameter
+ * passed to the closure is the value of the option after validating it
+ * and before normalizing it.
+ *
+ * @param string|\Closure $deprecationMessage
+ */
+ public function setDeprecated(string $option, $deprecationMessage = 'The option "%name%" is deprecated.'): self
+ {
+ if ($this->locked) {
+ throw new AccessException('Options cannot be deprecated from a lazy option or normalizer.');
+ }
+
+ if (!isset($this->defined[$option])) {
+ throw new UndefinedOptionsException(sprintf('The option "%s" does not exist, defined options are: "%s".', $option, implode('", "', array_keys($this->defined))));
+ }
+
+ if (!\is_string($deprecationMessage) && !$deprecationMessage instanceof \Closure) {
+ throw new InvalidArgumentException(sprintf('Invalid type for deprecation message argument, expected string or \Closure, but got "%s".', \gettype($deprecationMessage)));
+ }
+
+ // ignore if empty string
+ if ('' === $deprecationMessage) {
+ return $this;
+ }
+
+ $this->deprecated[$option] = $deprecationMessage;
+
+ // Make sure the option is processed
+ unset($this->resolved[$option]);
+
+ return $this;
+ }
+
+ public function isDeprecated(string $option): bool
+ {
+ return isset($this->deprecated[$option]);
+ }
+
+ /**
+ * Sets the normalizer for an option.
+ *
+ * The normalizer should be a closure with the following signature:
+ *
+ * function (Options $options, $value) {
+ * // ...
+ * }
+ *
+ * The closure is invoked when {@link resolve()} is called. The closure
+ * has access to the resolved values of other options through the passed
+ * {@link Options} instance.
+ *
+ * The second parameter passed to the closure is the value of
+ * the option.
+ *
+ * The resolved option value is set to the return value of the closure.
+ *
+ * @param string $option The option name
+ * @param \Closure $normalizer The normalizer
+ *
+ * @return $this
+ *
+ * @throws UndefinedOptionsException If the option is undefined
+ * @throws AccessException If called from a lazy option or normalizer
+ */
+ public function setNormalizer($option, \Closure $normalizer)
+ {
+ if ($this->locked) {
+ throw new AccessException('Normalizers cannot be set from a lazy option or normalizer.');
+ }
+
+ if (!isset($this->defined[$option])) {
+ throw new UndefinedOptionsException(sprintf('The option "%s" does not exist. Defined options are: "%s".', $option, implode('", "', array_keys($this->defined))));
+ }
+
+ $this->normalizers[$option] = $normalizer;
+
+ // Make sure the option is processed
+ unset($this->resolved[$option]);
+
+ return $this;
+ }
+
+ /**
+ * Sets allowed values for an option.
+ *
+ * Instead of passing values, you may also pass a closures with the
+ * following signature:
+ *
+ * function ($value) {
+ * // return true or false
+ * }
+ *
+ * The closure receives the value as argument and should return true to
+ * accept the value and false to reject the value.
+ *
+ * @param string $option The option name
+ * @param mixed $allowedValues One or more acceptable values/closures
+ *
+ * @return $this
+ *
+ * @throws UndefinedOptionsException If the option is undefined
+ * @throws AccessException If called from a lazy option or normalizer
+ */
+ public function setAllowedValues($option, $allowedValues)
+ {
+ if ($this->locked) {
+ throw new AccessException('Allowed values cannot be set from a lazy option or normalizer.');
+ }
+
+ if (!isset($this->defined[$option])) {
+ throw new UndefinedOptionsException(sprintf('The option "%s" does not exist. Defined options are: "%s".', $option, implode('", "', array_keys($this->defined))));
+ }
+
+ $this->allowedValues[$option] = \is_array($allowedValues) ? $allowedValues : array($allowedValues);
+
+ // Make sure the option is processed
+ unset($this->resolved[$option]);
+
+ return $this;
+ }
+
+ /**
+ * Adds allowed values for an option.
+ *
+ * The values are merged with the allowed values defined previously.
+ *
+ * Instead of passing values, you may also pass a closures with the
+ * following signature:
+ *
+ * function ($value) {
+ * // return true or false
+ * }
+ *
+ * The closure receives the value as argument and should return true to
+ * accept the value and false to reject the value.
+ *
+ * @param string $option The option name
+ * @param mixed $allowedValues One or more acceptable values/closures
+ *
+ * @return $this
+ *
+ * @throws UndefinedOptionsException If the option is undefined
+ * @throws AccessException If called from a lazy option or normalizer
+ */
+ public function addAllowedValues($option, $allowedValues)
+ {
+ if ($this->locked) {
+ throw new AccessException('Allowed values cannot be added from a lazy option or normalizer.');
+ }
+
+ if (!isset($this->defined[$option])) {
+ throw new UndefinedOptionsException(sprintf('The option "%s" does not exist. Defined options are: "%s".', $option, implode('", "', array_keys($this->defined))));
+ }
+
+ if (!\is_array($allowedValues)) {
+ $allowedValues = array($allowedValues);
+ }
+
+ if (!isset($this->allowedValues[$option])) {
+ $this->allowedValues[$option] = $allowedValues;
+ } else {
+ $this->allowedValues[$option] = array_merge($this->allowedValues[$option], $allowedValues);
+ }
+
+ // Make sure the option is processed
+ unset($this->resolved[$option]);
+
+ return $this;
+ }
+
+ /**
+ * Sets allowed types for an option.
+ *
+ * Any type for which a corresponding is_() function exists is
+ * acceptable. Additionally, fully-qualified class or interface names may
+ * be passed.
+ *
+ * @param string $option The option name
+ * @param string|string[] $allowedTypes One or more accepted types
+ *
+ * @return $this
+ *
+ * @throws UndefinedOptionsException If the option is undefined
+ * @throws AccessException If called from a lazy option or normalizer
+ */
+ public function setAllowedTypes($option, $allowedTypes)
+ {
+ if ($this->locked) {
+ throw new AccessException('Allowed types cannot be set from a lazy option or normalizer.');
+ }
+
+ if (!isset($this->defined[$option])) {
+ throw new UndefinedOptionsException(sprintf('The option "%s" does not exist. Defined options are: "%s".', $option, implode('", "', array_keys($this->defined))));
+ }
+
+ $this->allowedTypes[$option] = (array) $allowedTypes;
+
+ // Make sure the option is processed
+ unset($this->resolved[$option]);
+
+ return $this;
+ }
+
+ /**
+ * Adds allowed types for an option.
+ *
+ * The types are merged with the allowed types defined previously.
+ *
+ * Any type for which a corresponding is_() function exists is
+ * acceptable. Additionally, fully-qualified class or interface names may
+ * be passed.
+ *
+ * @param string $option The option name
+ * @param string|string[] $allowedTypes One or more accepted types
+ *
+ * @return $this
+ *
+ * @throws UndefinedOptionsException If the option is undefined
+ * @throws AccessException If called from a lazy option or normalizer
+ */
+ public function addAllowedTypes($option, $allowedTypes)
+ {
+ if ($this->locked) {
+ throw new AccessException('Allowed types cannot be added from a lazy option or normalizer.');
+ }
+
+ if (!isset($this->defined[$option])) {
+ throw new UndefinedOptionsException(sprintf('The option "%s" does not exist. Defined options are: "%s".', $option, implode('", "', array_keys($this->defined))));
+ }
+
+ if (!isset($this->allowedTypes[$option])) {
+ $this->allowedTypes[$option] = (array) $allowedTypes;
+ } else {
+ $this->allowedTypes[$option] = array_merge($this->allowedTypes[$option], (array) $allowedTypes);
+ }
+
+ // Make sure the option is processed
+ unset($this->resolved[$option]);
+
+ return $this;
+ }
+
+ /**
+ * Removes the option with the given name.
+ *
+ * Undefined options are ignored.
+ *
+ * @param string|string[] $optionNames One or more option names
+ *
+ * @return $this
+ *
+ * @throws AccessException If called from a lazy option or normalizer
+ */
+ public function remove($optionNames)
+ {
+ if ($this->locked) {
+ throw new AccessException('Options cannot be removed from a lazy option or normalizer.');
+ }
+
+ foreach ((array) $optionNames as $option) {
+ unset($this->defined[$option], $this->defaults[$option], $this->required[$option], $this->resolved[$option]);
+ unset($this->lazy[$option], $this->normalizers[$option], $this->allowedTypes[$option], $this->allowedValues[$option]);
+ }
+
+ return $this;
+ }
+
+ /**
+ * Removes all options.
+ *
+ * @return $this
+ *
+ * @throws AccessException If called from a lazy option or normalizer
+ */
+ public function clear()
+ {
+ if ($this->locked) {
+ throw new AccessException('Options cannot be cleared from a lazy option or normalizer.');
+ }
+
+ $this->defined = array();
+ $this->defaults = array();
+ $this->nested = array();
+ $this->required = array();
+ $this->resolved = array();
+ $this->lazy = array();
+ $this->normalizers = array();
+ $this->allowedTypes = array();
+ $this->allowedValues = array();
+ $this->deprecated = array();
+
+ return $this;
+ }
+
+ /**
+ * Merges options with the default values stored in the container and
+ * validates them.
+ *
+ * Exceptions are thrown if:
+ *
+ * - Undefined options are passed;
+ * - Required options are missing;
+ * - Options have invalid types;
+ * - Options have invalid values.
+ *
+ * @param array $options A map of option names to values
+ *
+ * @return array The merged and validated options
+ *
+ * @throws UndefinedOptionsException If an option name is undefined
+ * @throws InvalidOptionsException If an option doesn't fulfill the
+ * specified validation rules
+ * @throws MissingOptionsException If a required option is missing
+ * @throws OptionDefinitionException If there is a cyclic dependency between
+ * lazy options and/or normalizers
+ * @throws NoSuchOptionException If a lazy option reads an unavailable option
+ * @throws AccessException If called from a lazy option or normalizer
+ */
+ public function resolve(array $options = array())
+ {
+ if ($this->locked) {
+ throw new AccessException('Options cannot be resolved from a lazy option or normalizer.');
+ }
+
+ // Allow this method to be called multiple times
+ $clone = clone $this;
+
+ // Make sure that no unknown options are passed
+ $diff = array_diff_key($options, $clone->defined);
+
+ if (\count($diff) > 0) {
+ ksort($clone->defined);
+ ksort($diff);
+
+ throw new UndefinedOptionsException(sprintf((\count($diff) > 1 ? 'The options "%s" do not exist.' : 'The option "%s" does not exist.').' Defined options are: "%s".', implode('", "', array_keys($diff)), implode('", "', array_keys($clone->defined))));
+ }
+
+ // Override options set by the user
+ foreach ($options as $option => $value) {
+ $clone->given[$option] = true;
+ $clone->defaults[$option] = $value;
+ unset($clone->resolved[$option], $clone->lazy[$option]);
+ }
+
+ // Check whether any required option is missing
+ $diff = array_diff_key($clone->required, $clone->defaults);
+
+ if (\count($diff) > 0) {
+ ksort($diff);
+
+ throw new MissingOptionsException(sprintf(\count($diff) > 1 ? 'The required options "%s" are missing.' : 'The required option "%s" is missing.', implode('", "', array_keys($diff))));
+ }
+
+ // Lock the container
+ $clone->locked = true;
+
+ // Now process the individual options. Use offsetGet(), which resolves
+ // the option itself and any options that the option depends on
+ foreach ($clone->defaults as $option => $_) {
+ $clone->offsetGet($option);
+ }
+
+ return $clone->resolved;
+ }
+
+ /**
+ * Returns the resolved value of an option.
+ *
+ * @param string $option The option name
+ * @param bool $triggerDeprecation Whether to trigger the deprecation or not (true by default)
+ *
+ * @return mixed The option value
+ *
+ * @throws AccessException If accessing this method outside of
+ * {@link resolve()}
+ * @throws NoSuchOptionException If the option is not set
+ * @throws InvalidOptionsException If the option doesn't fulfill the
+ * specified validation rules
+ * @throws OptionDefinitionException If there is a cyclic dependency between
+ * lazy options and/or normalizers
+ */
+ public function offsetGet($option/*, bool $triggerDeprecation = true*/)
+ {
+ if (!$this->locked) {
+ throw new AccessException('Array access is only supported within closures of lazy options and normalizers.');
+ }
+
+ $triggerDeprecation = 1 === \func_num_args() || \func_get_arg(1);
+
+ // Shortcut for resolved options
+ if (isset($this->resolved[$option]) || array_key_exists($option, $this->resolved)) {
+ if ($triggerDeprecation && isset($this->deprecated[$option]) && (isset($this->given[$option]) || $this->calling) && \is_string($this->deprecated[$option])) {
+ @trigger_error(strtr($this->deprecated[$option], array('%name%' => $option)), E_USER_DEPRECATED);
+ }
+
+ return $this->resolved[$option];
+ }
+
+ // Check whether the option is set at all
+ if (!isset($this->defaults[$option]) && !array_key_exists($option, $this->defaults)) {
+ if (!isset($this->defined[$option])) {
+ throw new NoSuchOptionException(sprintf('The option "%s" does not exist. Defined options are: "%s".', $option, implode('", "', array_keys($this->defined))));
+ }
+
+ throw new NoSuchOptionException(sprintf('The optional option "%s" has no value set. You should make sure it is set with "isset" before reading it.', $option));
+ }
+
+ $value = $this->defaults[$option];
+
+ // Resolve the option if it is a nested definition
+ if (isset($this->nested[$option])) {
+ // If the closure is already being called, we have a cyclic dependency
+ if (isset($this->calling[$option])) {
+ throw new OptionDefinitionException(sprintf('The options "%s" have a cyclic dependency.', implode('", "', array_keys($this->calling))));
+ }
+
+ if (!\is_array($value)) {
+ throw new InvalidOptionsException(sprintf('The nested option "%s" with value %s is expected to be of type array, but is of type "%s".', $option, $this->formatValue($value), $this->formatTypeOf($value)));
+ }
+
+ // The following section must be protected from cyclic calls.
+ $this->calling[$option] = true;
+ try {
+ $resolver = new self();
+ foreach ($this->nested[$option] as $closure) {
+ $closure($resolver, $this);
+ }
+ $value = $resolver->resolve($value);
+ } finally {
+ unset($this->calling[$option]);
+ }
+ }
+
+ // Resolve the option if the default value is lazily evaluated
+ if (isset($this->lazy[$option])) {
+ // If the closure is already being called, we have a cyclic
+ // dependency
+ if (isset($this->calling[$option])) {
+ throw new OptionDefinitionException(sprintf('The options "%s" have a cyclic dependency.', implode('", "', array_keys($this->calling))));
+ }
+
+ // The following section must be protected from cyclic
+ // calls. Set $calling for the current $option to detect a cyclic
+ // dependency
+ // BEGIN
+ $this->calling[$option] = true;
+ try {
+ foreach ($this->lazy[$option] as $closure) {
+ $value = $closure($this, $value);
+ }
+ } finally {
+ unset($this->calling[$option]);
+ }
+ // END
+ }
+
+ // Validate the type of the resolved option
+ if (isset($this->allowedTypes[$option])) {
+ $valid = false;
+ $invalidTypes = array();
+
+ foreach ($this->allowedTypes[$option] as $type) {
+ $type = self::$typeAliases[$type] ?? $type;
+
+ if ($valid = $this->verifyTypes($type, $value, $invalidTypes)) {
+ break;
+ }
+ }
+
+ if (!$valid) {
+ $keys = array_keys($invalidTypes);
+
+ if (1 === \count($keys) && '[]' === substr($keys[0], -2)) {
+ throw new InvalidOptionsException(sprintf('The option "%s" with value %s is expected to be of type "%s", but one of the elements is of type "%s".', $option, $this->formatValue($value), implode('" or "', $this->allowedTypes[$option]), $keys[0]));
+ }
+
+ throw new InvalidOptionsException(sprintf('The option "%s" with value %s is expected to be of type "%s", but is of type "%s".', $option, $this->formatValue($value), implode('" or "', $this->allowedTypes[$option]), implode('|', array_keys($invalidTypes))));
+ }
+ }
+
+ // Validate the value of the resolved option
+ if (isset($this->allowedValues[$option])) {
+ $success = false;
+ $printableAllowedValues = array();
+
+ foreach ($this->allowedValues[$option] as $allowedValue) {
+ if ($allowedValue instanceof \Closure) {
+ if ($allowedValue($value)) {
+ $success = true;
+ break;
+ }
+
+ // Don't include closures in the exception message
+ continue;
+ }
+
+ if ($value === $allowedValue) {
+ $success = true;
+ break;
+ }
+
+ $printableAllowedValues[] = $allowedValue;
+ }
+
+ if (!$success) {
+ $message = sprintf(
+ 'The option "%s" with value %s is invalid.',
+ $option,
+ $this->formatValue($value)
+ );
+
+ if (\count($printableAllowedValues) > 0) {
+ $message .= sprintf(
+ ' Accepted values are: %s.',
+ $this->formatValues($printableAllowedValues)
+ );
+ }
+
+ throw new InvalidOptionsException($message);
+ }
+ }
+
+ // Check whether the option is deprecated
+ // and it is provided by the user or is being called from a lazy evaluation
+ if ($triggerDeprecation && isset($this->deprecated[$option]) && (isset($this->given[$option]) || ($this->calling && \is_string($this->deprecated[$option])))) {
+ $deprecationMessage = $this->deprecated[$option];
+
+ if ($deprecationMessage instanceof \Closure) {
+ // If the closure is already being called, we have a cyclic dependency
+ if (isset($this->calling[$option])) {
+ throw new OptionDefinitionException(sprintf('The options "%s" have a cyclic dependency.', implode('", "', array_keys($this->calling))));
+ }
+
+ $this->calling[$option] = true;
+ try {
+ if (!\is_string($deprecationMessage = $deprecationMessage($this, $value))) {
+ throw new InvalidOptionsException(sprintf('Invalid type for deprecation message, expected string but got "%s", return an empty string to ignore.', \gettype($deprecationMessage)));
+ }
+ } finally {
+ unset($this->calling[$option]);
+ }
+ }
+
+ if ('' !== $deprecationMessage) {
+ @trigger_error(strtr($deprecationMessage, array('%name%' => $option)), E_USER_DEPRECATED);
+ }
+ }
+
+ // Normalize the validated option
+ if (isset($this->normalizers[$option])) {
+ // If the closure is already being called, we have a cyclic
+ // dependency
+ if (isset($this->calling[$option])) {
+ throw new OptionDefinitionException(sprintf('The options "%s" have a cyclic dependency.', implode('", "', array_keys($this->calling))));
+ }
+
+ $normalizer = $this->normalizers[$option];
+
+ // The following section must be protected from cyclic
+ // calls. Set $calling for the current $option to detect a cyclic
+ // dependency
+ // BEGIN
+ $this->calling[$option] = true;
+ try {
+ $value = $normalizer($this, $value);
+ } finally {
+ unset($this->calling[$option]);
+ }
+ // END
+ }
+
+ // Mark as resolved
+ $this->resolved[$option] = $value;
+
+ return $value;
+ }
+
+ private function verifyTypes(string $type, $value, array &$invalidTypes, int $level = 0): bool
+ {
+ if (\is_array($value) && '[]' === substr($type, -2)) {
+ $type = substr($type, 0, -2);
+
+ foreach ($value as $val) {
+ if (!$this->verifyTypes($type, $val, $invalidTypes, $level + 1)) {
+ return false;
+ }
+ }
+
+ return true;
+ }
+
+ if (('null' === $type && null === $value) || (\function_exists($func = 'is_'.$type) && $func($value)) || $value instanceof $type) {
+ return true;
+ }
+
+ if (!$invalidTypes) {
+ $suffix = '';
+ while (\strlen($suffix) < $level * 2) {
+ $suffix .= '[]';
+ }
+ $invalidTypes[$this->formatTypeOf($value).$suffix] = true;
+ }
+
+ return false;
+ }
+
+ /**
+ * Returns whether a resolved option with the given name exists.
+ *
+ * @param string $option The option name
+ *
+ * @return bool Whether the option is set
+ *
+ * @throws AccessException If accessing this method outside of {@link resolve()}
+ *
+ * @see \ArrayAccess::offsetExists()
+ */
+ public function offsetExists($option)
+ {
+ if (!$this->locked) {
+ throw new AccessException('Array access is only supported within closures of lazy options and normalizers.');
+ }
+
+ return array_key_exists($option, $this->defaults);
+ }
+
+ /**
+ * Not supported.
+ *
+ * @throws AccessException
+ */
+ public function offsetSet($option, $value)
+ {
+ throw new AccessException('Setting options via array access is not supported. Use setDefault() instead.');
+ }
+
+ /**
+ * Not supported.
+ *
+ * @throws AccessException
+ */
+ public function offsetUnset($option)
+ {
+ throw new AccessException('Removing options via array access is not supported. Use remove() instead.');
+ }
+
+ /**
+ * Returns the number of set options.
+ *
+ * This may be only a subset of the defined options.
+ *
+ * @return int Number of options
+ *
+ * @throws AccessException If accessing this method outside of {@link resolve()}
+ *
+ * @see \Countable::count()
+ */
+ public function count()
+ {
+ if (!$this->locked) {
+ throw new AccessException('Counting is only supported within closures of lazy options and normalizers.');
+ }
+
+ return \count($this->defaults);
+ }
+
+ /**
+ * Returns a string representation of the type of the value.
+ *
+ * @param mixed $value The value to return the type of
+ *
+ * @return string The type of the value
+ */
+ private function formatTypeOf($value): string
+ {
+ return \is_object($value) ? \get_class($value) : \gettype($value);
+ }
+
+ /**
+ * Returns a string representation of the value.
+ *
+ * This method returns the equivalent PHP tokens for most scalar types
+ * (i.e. "false" for false, "1" for 1 etc.). Strings are always wrapped
+ * in double quotes (").
+ *
+ * @param mixed $value The value to format as string
+ */
+ private function formatValue($value): string
+ {
+ if (\is_object($value)) {
+ return \get_class($value);
+ }
+
+ if (\is_array($value)) {
+ return 'array';
+ }
+
+ if (\is_string($value)) {
+ return '"'.$value.'"';
+ }
+
+ if (\is_resource($value)) {
+ return 'resource';
+ }
+
+ if (null === $value) {
+ return 'null';
+ }
+
+ if (false === $value) {
+ return 'false';
+ }
+
+ if (true === $value) {
+ return 'true';
+ }
+
+ return (string) $value;
+ }
+
+ /**
+ * Returns a string representation of a list of values.
+ *
+ * Each of the values is converted to a string using
+ * {@link formatValue()}. The values are then concatenated with commas.
+ *
+ * @see formatValue()
+ */
+ private function formatValues(array $values): string
+ {
+ foreach ($values as $key => $value) {
+ $values[$key] = $this->formatValue($value);
+ }
+
+ return implode(', ', $values);
+ }
+}
diff --git a/vendor/symfony/options-resolver/README.md b/vendor/symfony/options-resolver/README.md
new file mode 100644
index 00000000..245e69b5
--- /dev/null
+++ b/vendor/symfony/options-resolver/README.md
@@ -0,0 +1,15 @@
+OptionsResolver Component
+=========================
+
+The OptionsResolver component is `array_replace` on steroids. It allows you to
+create an options system with required options, defaults, validation (type,
+value), normalization and more.
+
+Resources
+---------
+
+ * [Documentation](https://symfony.com/doc/current/components/options_resolver.html)
+ * [Contributing](https://symfony.com/doc/current/contributing/index.html)
+ * [Report issues](https://github.com/symfony/symfony/issues) and
+ [send Pull Requests](https://github.com/symfony/symfony/pulls)
+ in the [main Symfony repository](https://github.com/symfony/symfony)
diff --git a/vendor/symfony/options-resolver/Tests/Debug/OptionsResolverIntrospectorTest.php b/vendor/symfony/options-resolver/Tests/Debug/OptionsResolverIntrospectorTest.php
new file mode 100644
index 00000000..4bdce6f8
--- /dev/null
+++ b/vendor/symfony/options-resolver/Tests/Debug/OptionsResolverIntrospectorTest.php
@@ -0,0 +1,248 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\OptionsResolver\Tests\Debug;
+
+use PHPUnit\Framework\TestCase;
+use Symfony\Component\OptionsResolver\Debug\OptionsResolverIntrospector;
+use Symfony\Component\OptionsResolver\Options;
+use Symfony\Component\OptionsResolver\OptionsResolver;
+
+class OptionsResolverIntrospectorTest extends TestCase
+{
+ public function testGetDefault()
+ {
+ $resolver = new OptionsResolver();
+ $resolver->setDefault($option = 'foo', 'bar');
+
+ $debug = new OptionsResolverIntrospector($resolver);
+ $this->assertSame('bar', $debug->getDefault($option));
+ }
+
+ public function testGetDefaultNull()
+ {
+ $resolver = new OptionsResolver();
+ $resolver->setDefault($option = 'foo', null);
+
+ $debug = new OptionsResolverIntrospector($resolver);
+ $this->assertNull($debug->getDefault($option));
+ }
+
+ /**
+ * @expectedException \Symfony\Component\OptionsResolver\Exception\NoConfigurationException
+ * @expectedExceptionMessage No default value was set for the "foo" option.
+ */
+ public function testGetDefaultThrowsOnNoConfiguredValue()
+ {
+ $resolver = new OptionsResolver();
+ $resolver->setDefined($option = 'foo');
+
+ $debug = new OptionsResolverIntrospector($resolver);
+ $this->assertSame('bar', $debug->getDefault($option));
+ }
+
+ /**
+ * @expectedException \Symfony\Component\OptionsResolver\Exception\UndefinedOptionsException
+ * @expectedExceptionMessage The option "foo" does not exist.
+ */
+ public function testGetDefaultThrowsOnNotDefinedOption()
+ {
+ $resolver = new OptionsResolver();
+
+ $debug = new OptionsResolverIntrospector($resolver);
+ $this->assertSame('bar', $debug->getDefault('foo'));
+ }
+
+ public function testGetLazyClosures()
+ {
+ $resolver = new OptionsResolver();
+ $closures = array();
+ $resolver->setDefault($option = 'foo', $closures[] = function (Options $options) {});
+
+ $debug = new OptionsResolverIntrospector($resolver);
+ $this->assertSame($closures, $debug->getLazyClosures($option));
+ }
+
+ /**
+ * @expectedException \Symfony\Component\OptionsResolver\Exception\NoConfigurationException
+ * @expectedExceptionMessage No lazy closures were set for the "foo" option.
+ */
+ public function testGetLazyClosuresThrowsOnNoConfiguredValue()
+ {
+ $resolver = new OptionsResolver();
+ $resolver->setDefined($option = 'foo');
+
+ $debug = new OptionsResolverIntrospector($resolver);
+ $this->assertSame('bar', $debug->getLazyClosures($option));
+ }
+
+ /**
+ * @expectedException \Symfony\Component\OptionsResolver\Exception\UndefinedOptionsException
+ * @expectedExceptionMessage The option "foo" does not exist.
+ */
+ public function testGetLazyClosuresThrowsOnNotDefinedOption()
+ {
+ $resolver = new OptionsResolver();
+
+ $debug = new OptionsResolverIntrospector($resolver);
+ $this->assertSame('bar', $debug->getLazyClosures('foo'));
+ }
+
+ public function testGetAllowedTypes()
+ {
+ $resolver = new OptionsResolver();
+ $resolver->setDefined($option = 'foo');
+ $resolver->setAllowedTypes($option = 'foo', $allowedTypes = array('string', 'bool'));
+
+ $debug = new OptionsResolverIntrospector($resolver);
+ $this->assertSame($allowedTypes, $debug->getAllowedTypes($option));
+ }
+
+ /**
+ * @expectedException \Symfony\Component\OptionsResolver\Exception\NoConfigurationException
+ * @expectedExceptionMessage No allowed types were set for the "foo" option.
+ */
+ public function testGetAllowedTypesThrowsOnNoConfiguredValue()
+ {
+ $resolver = new OptionsResolver();
+ $resolver->setDefined($option = 'foo');
+
+ $debug = new OptionsResolverIntrospector($resolver);
+ $this->assertSame('bar', $debug->getAllowedTypes($option));
+ }
+
+ /**
+ * @expectedException \Symfony\Component\OptionsResolver\Exception\UndefinedOptionsException
+ * @expectedExceptionMessage The option "foo" does not exist.
+ */
+ public function testGetAllowedTypesThrowsOnNotDefinedOption()
+ {
+ $resolver = new OptionsResolver();
+
+ $debug = new OptionsResolverIntrospector($resolver);
+ $this->assertSame('bar', $debug->getAllowedTypes('foo'));
+ }
+
+ public function testGetAllowedValues()
+ {
+ $resolver = new OptionsResolver();
+ $resolver->setDefined($option = 'foo');
+ $resolver->setAllowedValues($option = 'foo', $allowedValues = array('bar', 'baz'));
+
+ $debug = new OptionsResolverIntrospector($resolver);
+ $this->assertSame($allowedValues, $debug->getAllowedValues($option));
+ }
+
+ /**
+ * @expectedException \Symfony\Component\OptionsResolver\Exception\NoConfigurationException
+ * @expectedExceptionMessage No allowed values were set for the "foo" option.
+ */
+ public function testGetAllowedValuesThrowsOnNoConfiguredValue()
+ {
+ $resolver = new OptionsResolver();
+ $resolver->setDefined($option = 'foo');
+
+ $debug = new OptionsResolverIntrospector($resolver);
+ $this->assertSame('bar', $debug->getAllowedValues($option));
+ }
+
+ /**
+ * @expectedException \Symfony\Component\OptionsResolver\Exception\UndefinedOptionsException
+ * @expectedExceptionMessage The option "foo" does not exist.
+ */
+ public function testGetAllowedValuesThrowsOnNotDefinedOption()
+ {
+ $resolver = new OptionsResolver();
+
+ $debug = new OptionsResolverIntrospector($resolver);
+ $this->assertSame('bar', $debug->getAllowedValues('foo'));
+ }
+
+ public function testGetNormalizer()
+ {
+ $resolver = new OptionsResolver();
+ $resolver->setDefined($option = 'foo');
+ $resolver->setNormalizer($option = 'foo', $normalizer = function () {});
+
+ $debug = new OptionsResolverIntrospector($resolver);
+ $this->assertSame($normalizer, $debug->getNormalizer($option));
+ }
+
+ /**
+ * @expectedException \Symfony\Component\OptionsResolver\Exception\NoConfigurationException
+ * @expectedExceptionMessage No normalizer was set for the "foo" option.
+ */
+ public function testGetNormalizerThrowsOnNoConfiguredValue()
+ {
+ $resolver = new OptionsResolver();
+ $resolver->setDefined($option = 'foo');
+
+ $debug = new OptionsResolverIntrospector($resolver);
+ $this->assertSame('bar', $debug->getNormalizer($option));
+ }
+
+ /**
+ * @expectedException \Symfony\Component\OptionsResolver\Exception\UndefinedOptionsException
+ * @expectedExceptionMessage The option "foo" does not exist.
+ */
+ public function testGetNormalizerThrowsOnNotDefinedOption()
+ {
+ $resolver = new OptionsResolver();
+
+ $debug = new OptionsResolverIntrospector($resolver);
+ $this->assertSame('bar', $debug->getNormalizer('foo'));
+ }
+
+ public function testGetDeprecationMessage()
+ {
+ $resolver = new OptionsResolver();
+ $resolver->setDefined('foo');
+ $resolver->setDeprecated('foo', 'The option "foo" is deprecated.');
+
+ $debug = new OptionsResolverIntrospector($resolver);
+ $this->assertSame('The option "foo" is deprecated.', $debug->getDeprecationMessage('foo'));
+ }
+
+ public function testGetClosureDeprecationMessage()
+ {
+ $resolver = new OptionsResolver();
+ $resolver->setDefined('foo');
+ $resolver->setDeprecated('foo', $closure = function (Options $options, $value) {});
+
+ $debug = new OptionsResolverIntrospector($resolver);
+ $this->assertSame($closure, $debug->getDeprecationMessage('foo'));
+ }
+
+ /**
+ * @expectedException \Symfony\Component\OptionsResolver\Exception\NoConfigurationException
+ * @expectedExceptionMessage No deprecation was set for the "foo" option.
+ */
+ public function testGetDeprecationMessageThrowsOnNoConfiguredValue()
+ {
+ $resolver = new OptionsResolver();
+ $resolver->setDefined('foo');
+
+ $debug = new OptionsResolverIntrospector($resolver);
+ $this->assertSame('bar', $debug->getDeprecationMessage('foo'));
+ }
+
+ /**
+ * @expectedException \Symfony\Component\OptionsResolver\Exception\UndefinedOptionsException
+ * @expectedExceptionMessage The option "foo" does not exist.
+ */
+ public function testGetDeprecationMessageThrowsOnNotDefinedOption()
+ {
+ $resolver = new OptionsResolver();
+
+ $debug = new OptionsResolverIntrospector($resolver);
+ $this->assertSame('bar', $debug->getDeprecationMessage('foo'));
+ }
+}
diff --git a/vendor/symfony/options-resolver/Tests/OptionsResolverTest.php b/vendor/symfony/options-resolver/Tests/OptionsResolverTest.php
new file mode 100644
index 00000000..213c471a
--- /dev/null
+++ b/vendor/symfony/options-resolver/Tests/OptionsResolverTest.php
@@ -0,0 +1,2466 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\OptionsResolver\Tests;
+
+use PHPUnit\Framework\Assert;
+use PHPUnit\Framework\TestCase;
+use Symfony\Component\OptionsResolver\Exception\InvalidOptionsException;
+use Symfony\Component\OptionsResolver\Options;
+use Symfony\Component\OptionsResolver\OptionsResolver;
+
+class OptionsResolverTest extends TestCase
+{
+ /**
+ * @var OptionsResolver
+ */
+ private $resolver;
+
+ protected function setUp()
+ {
+ $this->resolver = new OptionsResolver();
+ }
+
+ /**
+ * @expectedException \Symfony\Component\OptionsResolver\Exception\UndefinedOptionsException
+ * @expectedExceptionMessage The option "foo" does not exist. Defined options are: "a", "z".
+ */
+ public function testResolveFailsIfNonExistingOption()
+ {
+ $this->resolver->setDefault('z', '1');
+ $this->resolver->setDefault('a', '2');
+
+ $this->resolver->resolve(array('foo' => 'bar'));
+ }
+
+ /**
+ * @expectedException \Symfony\Component\OptionsResolver\Exception\UndefinedOptionsException
+ * @expectedExceptionMessage The options "baz", "foo", "ping" do not exist. Defined options are: "a", "z".
+ */
+ public function testResolveFailsIfMultipleNonExistingOptions()
+ {
+ $this->resolver->setDefault('z', '1');
+ $this->resolver->setDefault('a', '2');
+
+ $this->resolver->resolve(array('ping' => 'pong', 'foo' => 'bar', 'baz' => 'bam'));
+ }
+
+ /**
+ * @expectedException \Symfony\Component\OptionsResolver\Exception\AccessException
+ */
+ public function testResolveFailsFromLazyOption()
+ {
+ $this->resolver->setDefault('foo', function (Options $options) {
+ $options->resolve(array());
+ });
+
+ $this->resolver->resolve();
+ }
+
+ public function testSetDefaultReturnsThis()
+ {
+ $this->assertSame($this->resolver, $this->resolver->setDefault('foo', 'bar'));
+ }
+
+ public function testSetDefault()
+ {
+ $this->resolver->setDefault('one', '1');
+ $this->resolver->setDefault('two', '20');
+
+ $this->assertEquals(array(
+ 'one' => '1',
+ 'two' => '20',
+ ), $this->resolver->resolve());
+ }
+
+ /**
+ * @expectedException \Symfony\Component\OptionsResolver\Exception\AccessException
+ */
+ public function testFailIfSetDefaultFromLazyOption()
+ {
+ $this->resolver->setDefault('lazy', function (Options $options) {
+ $options->setDefault('default', 42);
+ });
+
+ $this->resolver->resolve();
+ }
+
+ public function testHasDefault()
+ {
+ $this->assertFalse($this->resolver->hasDefault('foo'));
+ $this->resolver->setDefault('foo', 42);
+ $this->assertTrue($this->resolver->hasDefault('foo'));
+ }
+
+ public function testHasDefaultWithNullValue()
+ {
+ $this->assertFalse($this->resolver->hasDefault('foo'));
+ $this->resolver->setDefault('foo', null);
+ $this->assertTrue($this->resolver->hasDefault('foo'));
+ }
+
+ public function testSetLazyReturnsThis()
+ {
+ $this->assertSame($this->resolver, $this->resolver->setDefault('foo', function (Options $options) {}));
+ }
+
+ public function testSetLazyClosure()
+ {
+ $this->resolver->setDefault('foo', function (Options $options) {
+ return 'lazy';
+ });
+
+ $this->assertEquals(array('foo' => 'lazy'), $this->resolver->resolve());
+ }
+
+ public function testClosureWithoutTypeHintNotInvoked()
+ {
+ $closure = function ($options) {
+ Assert::fail('Should not be called');
+ };
+
+ $this->resolver->setDefault('foo', $closure);
+
+ $this->assertSame(array('foo' => $closure), $this->resolver->resolve());
+ }
+
+ public function testClosureWithoutParametersNotInvoked()
+ {
+ $closure = function () {
+ Assert::fail('Should not be called');
+ };
+
+ $this->resolver->setDefault('foo', $closure);
+
+ $this->assertSame(array('foo' => $closure), $this->resolver->resolve());
+ }
+
+ public function testAccessPreviousDefaultValue()
+ {
+ // defined by superclass
+ $this->resolver->setDefault('foo', 'bar');
+
+ // defined by subclass
+ $this->resolver->setDefault('foo', function (Options $options, $previousValue) {
+ Assert::assertEquals('bar', $previousValue);
+
+ return 'lazy';
+ });
+
+ $this->assertEquals(array('foo' => 'lazy'), $this->resolver->resolve());
+ }
+
+ public function testAccessPreviousLazyDefaultValue()
+ {
+ // defined by superclass
+ $this->resolver->setDefault('foo', function (Options $options) {
+ return 'bar';
+ });
+
+ // defined by subclass
+ $this->resolver->setDefault('foo', function (Options $options, $previousValue) {
+ Assert::assertEquals('bar', $previousValue);
+
+ return 'lazy';
+ });
+
+ $this->assertEquals(array('foo' => 'lazy'), $this->resolver->resolve());
+ }
+
+ public function testPreviousValueIsNotEvaluatedIfNoSecondArgument()
+ {
+ // defined by superclass
+ $this->resolver->setDefault('foo', function () {
+ Assert::fail('Should not be called');
+ });
+
+ // defined by subclass, no $previousValue argument defined!
+ $this->resolver->setDefault('foo', function (Options $options) {
+ return 'lazy';
+ });
+
+ $this->assertEquals(array('foo' => 'lazy'), $this->resolver->resolve());
+ }
+
+ public function testOverwrittenLazyOptionNotEvaluated()
+ {
+ $this->resolver->setDefault('foo', function (Options $options) {
+ Assert::fail('Should not be called');
+ });
+
+ $this->resolver->setDefault('foo', 'bar');
+
+ $this->assertSame(array('foo' => 'bar'), $this->resolver->resolve());
+ }
+
+ public function testInvokeEachLazyOptionOnlyOnce()
+ {
+ $calls = 0;
+
+ $this->resolver->setDefault('lazy1', function (Options $options) use (&$calls) {
+ Assert::assertSame(1, ++$calls);
+
+ $options['lazy2'];
+ });
+
+ $this->resolver->setDefault('lazy2', function (Options $options) use (&$calls) {
+ Assert::assertSame(2, ++$calls);
+ });
+
+ $this->resolver->resolve();
+
+ $this->assertSame(2, $calls);
+ }
+
+ public function testSetRequiredReturnsThis()
+ {
+ $this->assertSame($this->resolver, $this->resolver->setRequired('foo'));
+ }
+
+ /**
+ * @expectedException \Symfony\Component\OptionsResolver\Exception\AccessException
+ */
+ public function testFailIfSetRequiredFromLazyOption()
+ {
+ $this->resolver->setDefault('foo', function (Options $options) {
+ $options->setRequired('bar');
+ });
+
+ $this->resolver->resolve();
+ }
+
+ /**
+ * @expectedException \Symfony\Component\OptionsResolver\Exception\MissingOptionsException
+ */
+ public function testResolveFailsIfRequiredOptionMissing()
+ {
+ $this->resolver->setRequired('foo');
+
+ $this->resolver->resolve();
+ }
+
+ public function testResolveSucceedsIfRequiredOptionSet()
+ {
+ $this->resolver->setRequired('foo');
+ $this->resolver->setDefault('foo', 'bar');
+
+ $this->assertNotEmpty($this->resolver->resolve());
+ }
+
+ public function testResolveSucceedsIfRequiredOptionPassed()
+ {
+ $this->resolver->setRequired('foo');
+
+ $this->assertNotEmpty($this->resolver->resolve(array('foo' => 'bar')));
+ }
+
+ public function testIsRequired()
+ {
+ $this->assertFalse($this->resolver->isRequired('foo'));
+ $this->resolver->setRequired('foo');
+ $this->assertTrue($this->resolver->isRequired('foo'));
+ }
+
+ public function testRequiredIfSetBefore()
+ {
+ $this->assertFalse($this->resolver->isRequired('foo'));
+
+ $this->resolver->setDefault('foo', 'bar');
+ $this->resolver->setRequired('foo');
+
+ $this->assertTrue($this->resolver->isRequired('foo'));
+ }
+
+ public function testStillRequiredAfterSet()
+ {
+ $this->assertFalse($this->resolver->isRequired('foo'));
+
+ $this->resolver->setRequired('foo');
+ $this->resolver->setDefault('foo', 'bar');
+
+ $this->assertTrue($this->resolver->isRequired('foo'));
+ }
+
+ public function testIsNotRequiredAfterRemove()
+ {
+ $this->assertFalse($this->resolver->isRequired('foo'));
+ $this->resolver->setRequired('foo');
+ $this->resolver->remove('foo');
+ $this->assertFalse($this->resolver->isRequired('foo'));
+ }
+
+ public function testIsNotRequiredAfterClear()
+ {
+ $this->assertFalse($this->resolver->isRequired('foo'));
+ $this->resolver->setRequired('foo');
+ $this->resolver->clear();
+ $this->assertFalse($this->resolver->isRequired('foo'));
+ }
+
+ public function testGetRequiredOptions()
+ {
+ $this->resolver->setRequired(array('foo', 'bar'));
+ $this->resolver->setDefault('bam', 'baz');
+ $this->resolver->setDefault('foo', 'boo');
+
+ $this->assertSame(array('foo', 'bar'), $this->resolver->getRequiredOptions());
+ }
+
+ public function testIsMissingIfNotSet()
+ {
+ $this->assertFalse($this->resolver->isMissing('foo'));
+ $this->resolver->setRequired('foo');
+ $this->assertTrue($this->resolver->isMissing('foo'));
+ }
+
+ public function testIsNotMissingIfSet()
+ {
+ $this->resolver->setDefault('foo', 'bar');
+
+ $this->assertFalse($this->resolver->isMissing('foo'));
+ $this->resolver->setRequired('foo');
+ $this->assertFalse($this->resolver->isMissing('foo'));
+ }
+
+ public function testIsNotMissingAfterRemove()
+ {
+ $this->resolver->setRequired('foo');
+ $this->resolver->remove('foo');
+ $this->assertFalse($this->resolver->isMissing('foo'));
+ }
+
+ public function testIsNotMissingAfterClear()
+ {
+ $this->resolver->setRequired('foo');
+ $this->resolver->clear();
+ $this->assertFalse($this->resolver->isRequired('foo'));
+ }
+
+ public function testGetMissingOptions()
+ {
+ $this->resolver->setRequired(array('foo', 'bar'));
+ $this->resolver->setDefault('bam', 'baz');
+ $this->resolver->setDefault('foo', 'boo');
+
+ $this->assertSame(array('bar'), $this->resolver->getMissingOptions());
+ }
+
+ /**
+ * @expectedException \Symfony\Component\OptionsResolver\Exception\AccessException
+ */
+ public function testFailIfSetDefinedFromLazyOption()
+ {
+ $this->resolver->setDefault('foo', function (Options $options) {
+ $options->setDefined('bar');
+ });
+
+ $this->resolver->resolve();
+ }
+
+ public function testDefinedOptionsNotIncludedInResolvedOptions()
+ {
+ $this->resolver->setDefined('foo');
+
+ $this->assertSame(array(), $this->resolver->resolve());
+ }
+
+ public function testDefinedOptionsIncludedIfDefaultSetBefore()
+ {
+ $this->resolver->setDefault('foo', 'bar');
+ $this->resolver->setDefined('foo');
+
+ $this->assertSame(array('foo' => 'bar'), $this->resolver->resolve());
+ }
+
+ public function testDefinedOptionsIncludedIfDefaultSetAfter()
+ {
+ $this->resolver->setDefined('foo');
+ $this->resolver->setDefault('foo', 'bar');
+
+ $this->assertSame(array('foo' => 'bar'), $this->resolver->resolve());
+ }
+
+ public function testDefinedOptionsIncludedIfPassedToResolve()
+ {
+ $this->resolver->setDefined('foo');
+
+ $this->assertSame(array('foo' => 'bar'), $this->resolver->resolve(array('foo' => 'bar')));
+ }
+
+ public function testIsDefined()
+ {
+ $this->assertFalse($this->resolver->isDefined('foo'));
+ $this->resolver->setDefined('foo');
+ $this->assertTrue($this->resolver->isDefined('foo'));
+ }
+
+ public function testLazyOptionsAreDefined()
+ {
+ $this->assertFalse($this->resolver->isDefined('foo'));
+ $this->resolver->setDefault('foo', function (Options $options) {});
+ $this->assertTrue($this->resolver->isDefined('foo'));
+ }
+
+ public function testRequiredOptionsAreDefined()
+ {
+ $this->assertFalse($this->resolver->isDefined('foo'));
+ $this->resolver->setRequired('foo');
+ $this->assertTrue($this->resolver->isDefined('foo'));
+ }
+
+ public function testSetOptionsAreDefined()
+ {
+ $this->assertFalse($this->resolver->isDefined('foo'));
+ $this->resolver->setDefault('foo', 'bar');
+ $this->assertTrue($this->resolver->isDefined('foo'));
+ }
+
+ public function testGetDefinedOptions()
+ {
+ $this->resolver->setDefined(array('foo', 'bar'));
+ $this->resolver->setDefault('baz', 'bam');
+ $this->resolver->setRequired('boo');
+
+ $this->assertSame(array('foo', 'bar', 'baz', 'boo'), $this->resolver->getDefinedOptions());
+ }
+
+ public function testRemovedOptionsAreNotDefined()
+ {
+ $this->assertFalse($this->resolver->isDefined('foo'));
+ $this->resolver->setDefined('foo');
+ $this->assertTrue($this->resolver->isDefined('foo'));
+ $this->resolver->remove('foo');
+ $this->assertFalse($this->resolver->isDefined('foo'));
+ }
+
+ public function testClearedOptionsAreNotDefined()
+ {
+ $this->assertFalse($this->resolver->isDefined('foo'));
+ $this->resolver->setDefined('foo');
+ $this->assertTrue($this->resolver->isDefined('foo'));
+ $this->resolver->clear();
+ $this->assertFalse($this->resolver->isDefined('foo'));
+ }
+
+ /**
+ * @expectedException \Symfony\Component\OptionsResolver\Exception\AccessException
+ */
+ public function testFailIfSetDeprecatedFromLazyOption()
+ {
+ $this->resolver
+ ->setDefault('bar', 'baz')
+ ->setDefault('foo', function (Options $options) {
+ $options->setDeprecated('bar');
+ })
+ ->resolve()
+ ;
+ }
+
+ /**
+ * @expectedException \Symfony\Component\OptionsResolver\Exception\UndefinedOptionsException
+ */
+ public function testSetDeprecatedFailsIfUnknownOption()
+ {
+ $this->resolver->setDeprecated('foo');
+ }
+
+ /**
+ * @expectedException \Symfony\Component\OptionsResolver\Exception\InvalidArgumentException
+ * @expectedExceptionMessage Invalid type for deprecation message argument, expected string or \Closure, but got "boolean".
+ */
+ public function testSetDeprecatedFailsIfInvalidDeprecationMessageType()
+ {
+ $this->resolver
+ ->setDefined('foo')
+ ->setDeprecated('foo', true)
+ ;
+ }
+
+ /**
+ * @expectedException \Symfony\Component\OptionsResolver\Exception\InvalidArgumentException
+ * @expectedExceptionMessage Invalid type for deprecation message, expected string but got "boolean", return an empty string to ignore.
+ */
+ public function testLazyDeprecationFailsIfInvalidDeprecationMessageType()
+ {
+ $this->resolver
+ ->setDefined('foo')
+ ->setDeprecated('foo', function (Options $options, $value) {
+ return false;
+ })
+ ;
+ $this->resolver->resolve(array('foo' => null));
+ }
+
+ /**
+ * @expectedException \Symfony\Component\OptionsResolver\Exception\OptionDefinitionException
+ * @expectedExceptionMessage The options "foo", "bar" have a cyclic dependency.
+ */
+ public function testFailsIfCyclicDependencyBetweenDeprecation()
+ {
+ $this->resolver
+ ->setDefined(array('foo', 'bar'))
+ ->setDeprecated('foo', function (Options $options, $value) {
+ $options['bar'];
+ })
+ ->setDeprecated('bar', function (Options $options, $value) {
+ $options['foo'];
+ })
+ ;
+ $this->resolver->resolve(array('foo' => null, 'bar' => null));
+ }
+
+ public function testIsDeprecated()
+ {
+ $this->resolver
+ ->setDefined('foo')
+ ->setDeprecated('foo')
+ ;
+ $this->assertTrue($this->resolver->isDeprecated('foo'));
+ }
+
+ public function testIsNotDeprecatedIfEmptyString()
+ {
+ $this->resolver
+ ->setDefined('foo')
+ ->setDeprecated('foo', '')
+ ;
+ $this->assertFalse($this->resolver->isDeprecated('foo'));
+ }
+
+ /**
+ * @dataProvider provideDeprecationData
+ */
+ public function testDeprecationMessages(\Closure $configureOptions, array $options, ?array $expectedError, int $expectedCount)
+ {
+ $count = 0;
+ error_clear_last();
+ set_error_handler(function () use (&$count) {
+ ++$count;
+
+ return false;
+ });
+ $e = error_reporting(0);
+
+ $configureOptions($this->resolver);
+ $this->resolver->resolve($options);
+
+ error_reporting($e);
+ restore_error_handler();
+
+ $lastError = error_get_last();
+ unset($lastError['file'], $lastError['line']);
+
+ $this->assertSame($expectedError, $lastError);
+ $this->assertSame($expectedCount, $count);
+ }
+
+ public function provideDeprecationData()
+ {
+ yield 'It deprecates an option with default message' => array(
+ function (OptionsResolver $resolver) {
+ $resolver
+ ->setDefined(array('foo', 'bar'))
+ ->setDeprecated('foo')
+ ;
+ },
+ array('foo' => 'baz'),
+ array(
+ 'type' => E_USER_DEPRECATED,
+ 'message' => 'The option "foo" is deprecated.',
+ ),
+ 1,
+ );
+
+ yield 'It deprecates an option with custom message' => array(
+ function (OptionsResolver $resolver) {
+ $resolver
+ ->setDefined('foo')
+ ->setDefault('bar', function (Options $options) {
+ return $options['foo'];
+ })
+ ->setDeprecated('foo', 'The option "foo" is deprecated, use "bar" option instead.')
+ ;
+ },
+ array('foo' => 'baz'),
+ array(
+ 'type' => E_USER_DEPRECATED,
+ 'message' => 'The option "foo" is deprecated, use "bar" option instead.',
+ ),
+ 2,
+ );
+
+ yield 'It deprecates an option evaluated in another definition' => array(
+ function (OptionsResolver $resolver) {
+ // defined by superclass
+ $resolver
+ ->setDefault('foo', null)
+ ->setDeprecated('foo')
+ ;
+ // defined by subclass
+ $resolver->setDefault('bar', function (Options $options) {
+ return $options['foo']; // It triggers a deprecation
+ });
+ },
+ array(),
+ array(
+ 'type' => E_USER_DEPRECATED,
+ 'message' => 'The option "foo" is deprecated.',
+ ),
+ 1,
+ );
+
+ yield 'It deprecates allowed type and value' => array(
+ function (OptionsResolver $resolver) {
+ $resolver
+ ->setDefault('foo', null)
+ ->setAllowedTypes('foo', array('null', 'string', \stdClass::class))
+ ->setDeprecated('foo', function (Options $options, $value) {
+ if ($value instanceof \stdClass) {
+ return sprintf('Passing an instance of "%s" to option "foo" is deprecated, pass its FQCN instead.', \stdClass::class);
+ }
+
+ return '';
+ })
+ ;
+ },
+ array('foo' => new \stdClass()),
+ array(
+ 'type' => E_USER_DEPRECATED,
+ 'message' => 'Passing an instance of "stdClass" to option "foo" is deprecated, pass its FQCN instead.',
+ ),
+ 1,
+ );
+
+ yield 'It triggers a deprecation based on the value only if option is provided by the user' => array(
+ function (OptionsResolver $resolver) {
+ $resolver
+ ->setDefined('foo')
+ ->setAllowedTypes('foo', array('null', 'bool'))
+ ->setDeprecated('foo', function (Options $options, $value) {
+ if (!\is_bool($value)) {
+ return 'Passing a value different than true or false is deprecated.';
+ }
+
+ return '';
+ })
+ ->setDefault('baz', null)
+ ->setAllowedTypes('baz', array('null', 'int'))
+ ->setDeprecated('baz', function (Options $options, $value) {
+ if (!\is_int($value)) {
+ return 'Not passing an integer is deprecated.';
+ }
+
+ return '';
+ })
+ ->setDefault('bar', function (Options $options) {
+ $options['baz']; // It does not triggers a deprecation
+
+ return $options['foo']; // It does not triggers a deprecation
+ })
+ ;
+ },
+ array('foo' => null), // It triggers a deprecation
+ array(
+ 'type' => E_USER_DEPRECATED,
+ 'message' => 'Passing a value different than true or false is deprecated.',
+ ),
+ 1,
+ );
+
+ yield 'It ignores a deprecation if closure returns an empty string' => array(
+ function (OptionsResolver $resolver) {
+ $resolver
+ ->setDefault('foo', null)
+ ->setDeprecated('foo', function (Options $options, $value) {
+ return '';
+ })
+ ;
+ },
+ array('foo' => Bar::class),
+ null,
+ 0,
+ );
+
+ yield 'It deprecates value depending on other option value' => array(
+ function (OptionsResolver $resolver) {
+ $resolver
+ ->setDefault('widget', null)
+ ->setDefault('date_format', null)
+ ->setDeprecated('date_format', function (Options $options, $dateFormat) {
+ if (null !== $dateFormat && 'single_text' === $options['widget']) {
+ return 'Using the "date_format" option when the "widget" option is set to "single_text" is deprecated.';
+ }
+
+ return '';
+ })
+ ;
+ },
+ array('widget' => 'single_text', 'date_format' => 2),
+ array(
+ 'type' => E_USER_DEPRECATED,
+ 'message' => 'Using the "date_format" option when the "widget" option is set to "single_text" is deprecated.',
+ ),
+ 1,
+ );
+
+ yield 'It triggers a deprecation for each evaluation' => array(
+ function (OptionsResolver $resolver) {
+ $resolver
+ // defined by superclass
+ ->setDefined('foo')
+ ->setDeprecated('foo')
+ // defined by subclass
+ ->setDefault('bar', function (Options $options) {
+ return $options['foo']; // It triggers a deprecation
+ })
+ ->setNormalizer('bar', function (Options $options, $value) {
+ $options['foo']; // It triggers a deprecation
+ $options['foo']; // It triggers a deprecation
+
+ return $value;
+ })
+ ;
+ },
+ array('foo' => 'baz'), // It triggers a deprecation
+ array(
+ 'type' => E_USER_DEPRECATED,
+ 'message' => 'The option "foo" is deprecated.',
+ ),
+ 4,
+ );
+
+ yield 'It ignores a deprecation if no option is provided by the user' => array(
+ function (OptionsResolver $resolver) {
+ $resolver
+ ->setDefined('foo')
+ ->setDefault('bar', null)
+ ->setDeprecated('foo')
+ ->setDeprecated('bar')
+ ;
+ },
+ array(),
+ null,
+ 0,
+ );
+
+ yield 'It explicitly ignores a depreciation' => array(
+ function (OptionsResolver $resolver) {
+ $resolver
+ ->setDefault('baz', function (Options $options) {
+ return $options->offsetGet('foo', false);
+ })
+ ->setDefault('foo', null)
+ ->setDeprecated('foo')
+ ->setDefault('bar', function (Options $options) {
+ return $options->offsetGet('foo', false);
+ })
+ ;
+ },
+ array(),
+ null,
+ 0,
+ );
+ }
+
+ /**
+ * @expectedException \Symfony\Component\OptionsResolver\Exception\UndefinedOptionsException
+ */
+ public function testSetAllowedTypesFailsIfUnknownOption()
+ {
+ $this->resolver->setAllowedTypes('foo', 'string');
+ }
+
+ public function testResolveTypedArray()
+ {
+ $this->resolver->setDefined('foo');
+ $this->resolver->setAllowedTypes('foo', 'string[]');
+ $options = $this->resolver->resolve(array('foo' => array('bar', 'baz')));
+
+ $this->assertSame(array('foo' => array('bar', 'baz')), $options);
+ }
+
+ /**
+ * @expectedException \Symfony\Component\OptionsResolver\Exception\AccessException
+ */
+ public function testFailIfSetAllowedTypesFromLazyOption()
+ {
+ $this->resolver->setDefault('foo', function (Options $options) {
+ $options->setAllowedTypes('bar', 'string');
+ });
+
+ $this->resolver->setDefault('bar', 'baz');
+
+ $this->resolver->resolve();
+ }
+
+ /**
+ * @expectedException \Symfony\Component\OptionsResolver\Exception\InvalidOptionsException
+ * @expectedExceptionMessage The option "foo" with value array is expected to be of type "int[]", but one of the elements is of type "DateTime[]".
+ */
+ public function testResolveFailsIfInvalidTypedArray()
+ {
+ $this->resolver->setDefined('foo');
+ $this->resolver->setAllowedTypes('foo', 'int[]');
+
+ $this->resolver->resolve(array('foo' => array(new \DateTime())));
+ }
+
+ /**
+ * @expectedException \Symfony\Component\OptionsResolver\Exception\InvalidOptionsException
+ * @expectedExceptionMessage The option "foo" with value "bar" is expected to be of type "int[]", but is of type "string".
+ */
+ public function testResolveFailsWithNonArray()
+ {
+ $this->resolver->setDefined('foo');
+ $this->resolver->setAllowedTypes('foo', 'int[]');
+
+ $this->resolver->resolve(array('foo' => 'bar'));
+ }
+
+ /**
+ * @expectedException \Symfony\Component\OptionsResolver\Exception\InvalidOptionsException
+ * @expectedExceptionMessage The option "foo" with value array is expected to be of type "int[]", but one of the elements is of type "stdClass[]".
+ */
+ public function testResolveFailsIfTypedArrayContainsInvalidTypes()
+ {
+ $this->resolver->setDefined('foo');
+ $this->resolver->setAllowedTypes('foo', 'int[]');
+ $values = range(1, 5);
+ $values[] = new \stdClass();
+ $values[] = array();
+ $values[] = new \DateTime();
+ $values[] = 123;
+
+ $this->resolver->resolve(array('foo' => $values));
+ }
+
+ /**
+ * @expectedException \Symfony\Component\OptionsResolver\Exception\InvalidOptionsException
+ * @expectedExceptionMessage The option "foo" with value array is expected to be of type "int[][]", but one of the elements is of type "double[][]".
+ */
+ public function testResolveFailsWithCorrectLevelsButWrongScalar()
+ {
+ $this->resolver->setDefined('foo');
+ $this->resolver->setAllowedTypes('foo', 'int[][]');
+
+ $this->resolver->resolve(
+ array(
+ 'foo' => array(
+ array(1.2),
+ ),
+ )
+ );
+ }
+
+ /**
+ * @dataProvider provideInvalidTypes
+ */
+ public function testResolveFailsIfInvalidType($actualType, $allowedType, $exceptionMessage)
+ {
+ $this->resolver->setDefined('option');
+ $this->resolver->setAllowedTypes('option', $allowedType);
+
+ if (method_exists($this, 'expectException')) {
+ $this->expectException('Symfony\Component\OptionsResolver\Exception\InvalidOptionsException');
+ $this->expectExceptionMessage($exceptionMessage);
+ } else {
+ $this->setExpectedException('Symfony\Component\OptionsResolver\Exception\InvalidOptionsException', $exceptionMessage);
+ }
+
+ $this->resolver->resolve(array('option' => $actualType));
+ }
+
+ public function provideInvalidTypes()
+ {
+ return array(
+ array(true, 'string', 'The option "option" with value true is expected to be of type "string", but is of type "boolean".'),
+ array(false, 'string', 'The option "option" with value false is expected to be of type "string", but is of type "boolean".'),
+ array(fopen(__FILE__, 'r'), 'string', 'The option "option" with value resource is expected to be of type "string", but is of type "resource".'),
+ array(array(), 'string', 'The option "option" with value array is expected to be of type "string", but is of type "array".'),
+ array(new OptionsResolver(), 'string', 'The option "option" with value Symfony\Component\OptionsResolver\OptionsResolver is expected to be of type "string", but is of type "Symfony\Component\OptionsResolver\OptionsResolver".'),
+ array(42, 'string', 'The option "option" with value 42 is expected to be of type "string", but is of type "integer".'),
+ array(null, 'string', 'The option "option" with value null is expected to be of type "string", but is of type "NULL".'),
+ array('bar', '\stdClass', 'The option "option" with value "bar" is expected to be of type "\stdClass", but is of type "string".'),
+ );
+ }
+
+ public function testResolveSucceedsIfValidType()
+ {
+ $this->resolver->setDefault('foo', 'bar');
+ $this->resolver->setAllowedTypes('foo', 'string');
+
+ $this->assertNotEmpty($this->resolver->resolve());
+ }
+
+ /**
+ * @expectedException \Symfony\Component\OptionsResolver\Exception\InvalidOptionsException
+ * @expectedExceptionMessage The option "foo" with value 42 is expected to be of type "string" or "bool", but is of type "integer".
+ */
+ public function testResolveFailsIfInvalidTypeMultiple()
+ {
+ $this->resolver->setDefault('foo', 42);
+ $this->resolver->setAllowedTypes('foo', array('string', 'bool'));
+
+ $this->resolver->resolve();
+ }
+
+ public function testResolveSucceedsIfValidTypeMultiple()
+ {
+ $this->resolver->setDefault('foo', true);
+ $this->resolver->setAllowedTypes('foo', array('string', 'bool'));
+
+ $this->assertNotEmpty($this->resolver->resolve());
+ }
+
+ public function testResolveSucceedsIfInstanceOfClass()
+ {
+ $this->resolver->setDefault('foo', new \stdClass());
+ $this->resolver->setAllowedTypes('foo', '\stdClass');
+
+ $this->assertNotEmpty($this->resolver->resolve());
+ }
+
+ public function testResolveSucceedsIfTypedArray()
+ {
+ $this->resolver->setDefault('foo', null);
+ $this->resolver->setAllowedTypes('foo', array('null', 'DateTime[]'));
+
+ $data = array(
+ 'foo' => array(
+ new \DateTime(),
+ new \DateTime(),
+ ),
+ );
+ $result = $this->resolver->resolve($data);
+ $this->assertEquals($data, $result);
+ }
+
+ /**
+ * @expectedException \Symfony\Component\OptionsResolver\Exception\InvalidOptionsException
+ */
+ public function testResolveFailsIfNotInstanceOfClass()
+ {
+ $this->resolver->setDefault('foo', 'bar');
+ $this->resolver->setAllowedTypes('foo', '\stdClass');
+
+ $this->resolver->resolve();
+ }
+
+ /**
+ * @expectedException \Symfony\Component\OptionsResolver\Exception\UndefinedOptionsException
+ */
+ public function testAddAllowedTypesFailsIfUnknownOption()
+ {
+ $this->resolver->addAllowedTypes('foo', 'string');
+ }
+
+ /**
+ * @expectedException \Symfony\Component\OptionsResolver\Exception\AccessException
+ */
+ public function testFailIfAddAllowedTypesFromLazyOption()
+ {
+ $this->resolver->setDefault('foo', function (Options $options) {
+ $options->addAllowedTypes('bar', 'string');
+ });
+
+ $this->resolver->setDefault('bar', 'baz');
+
+ $this->resolver->resolve();
+ }
+
+ /**
+ * @expectedException \Symfony\Component\OptionsResolver\Exception\InvalidOptionsException
+ */
+ public function testResolveFailsIfInvalidAddedType()
+ {
+ $this->resolver->setDefault('foo', 42);
+ $this->resolver->addAllowedTypes('foo', 'string');
+
+ $this->resolver->resolve();
+ }
+
+ public function testResolveSucceedsIfValidAddedType()
+ {
+ $this->resolver->setDefault('foo', 'bar');
+ $this->resolver->addAllowedTypes('foo', 'string');
+
+ $this->assertNotEmpty($this->resolver->resolve());
+ }
+
+ /**
+ * @expectedException \Symfony\Component\OptionsResolver\Exception\InvalidOptionsException
+ */
+ public function testResolveFailsIfInvalidAddedTypeMultiple()
+ {
+ $this->resolver->setDefault('foo', 42);
+ $this->resolver->addAllowedTypes('foo', array('string', 'bool'));
+
+ $this->resolver->resolve();
+ }
+
+ public function testResolveSucceedsIfValidAddedTypeMultiple()
+ {
+ $this->resolver->setDefault('foo', 'bar');
+ $this->resolver->addAllowedTypes('foo', array('string', 'bool'));
+
+ $this->assertNotEmpty($this->resolver->resolve());
+ }
+
+ public function testAddAllowedTypesDoesNotOverwrite()
+ {
+ $this->resolver->setDefault('foo', 'bar');
+ $this->resolver->setAllowedTypes('foo', 'string');
+ $this->resolver->addAllowedTypes('foo', 'bool');
+
+ $this->resolver->setDefault('foo', 'bar');
+
+ $this->assertNotEmpty($this->resolver->resolve());
+ }
+
+ public function testAddAllowedTypesDoesNotOverwrite2()
+ {
+ $this->resolver->setDefault('foo', 'bar');
+ $this->resolver->setAllowedTypes('foo', 'string');
+ $this->resolver->addAllowedTypes('foo', 'bool');
+
+ $this->resolver->setDefault('foo', false);
+
+ $this->assertNotEmpty($this->resolver->resolve());
+ }
+
+ /**
+ * @expectedException \Symfony\Component\OptionsResolver\Exception\UndefinedOptionsException
+ */
+ public function testSetAllowedValuesFailsIfUnknownOption()
+ {
+ $this->resolver->setAllowedValues('foo', 'bar');
+ }
+
+ /**
+ * @expectedException \Symfony\Component\OptionsResolver\Exception\AccessException
+ */
+ public function testFailIfSetAllowedValuesFromLazyOption()
+ {
+ $this->resolver->setDefault('foo', function (Options $options) {
+ $options->setAllowedValues('bar', 'baz');
+ });
+
+ $this->resolver->setDefault('bar', 'baz');
+
+ $this->resolver->resolve();
+ }
+
+ /**
+ * @expectedException \Symfony\Component\OptionsResolver\Exception\InvalidOptionsException
+ * @expectedExceptionMessage The option "foo" with value 42 is invalid. Accepted values are: "bar".
+ */
+ public function testResolveFailsIfInvalidValue()
+ {
+ $this->resolver->setDefined('foo');
+ $this->resolver->setAllowedValues('foo', 'bar');
+
+ $this->resolver->resolve(array('foo' => 42));
+ }
+
+ /**
+ * @expectedException \Symfony\Component\OptionsResolver\Exception\InvalidOptionsException
+ * @expectedExceptionMessage The option "foo" with value null is invalid. Accepted values are: "bar".
+ */
+ public function testResolveFailsIfInvalidValueIsNull()
+ {
+ $this->resolver->setDefault('foo', null);
+ $this->resolver->setAllowedValues('foo', 'bar');
+
+ $this->resolver->resolve();
+ }
+
+ /**
+ * @expectedException \Symfony\Component\OptionsResolver\Exception\InvalidOptionsException
+ */
+ public function testResolveFailsIfInvalidValueStrict()
+ {
+ $this->resolver->setDefault('foo', 42);
+ $this->resolver->setAllowedValues('foo', '42');
+
+ $this->resolver->resolve();
+ }
+
+ public function testResolveSucceedsIfValidValue()
+ {
+ $this->resolver->setDefault('foo', 'bar');
+ $this->resolver->setAllowedValues('foo', 'bar');
+
+ $this->assertEquals(array('foo' => 'bar'), $this->resolver->resolve());
+ }
+
+ public function testResolveSucceedsIfValidValueIsNull()
+ {
+ $this->resolver->setDefault('foo', null);
+ $this->resolver->setAllowedValues('foo', null);
+
+ $this->assertEquals(array('foo' => null), $this->resolver->resolve());
+ }
+
+ /**
+ * @expectedException \Symfony\Component\OptionsResolver\Exception\InvalidOptionsException
+ * @expectedExceptionMessage The option "foo" with value 42 is invalid. Accepted values are: "bar", false, null.
+ */
+ public function testResolveFailsIfInvalidValueMultiple()
+ {
+ $this->resolver->setDefault('foo', 42);
+ $this->resolver->setAllowedValues('foo', array('bar', false, null));
+
+ $this->resolver->resolve();
+ }
+
+ public function testResolveSucceedsIfValidValueMultiple()
+ {
+ $this->resolver->setDefault('foo', 'baz');
+ $this->resolver->setAllowedValues('foo', array('bar', 'baz'));
+
+ $this->assertEquals(array('foo' => 'baz'), $this->resolver->resolve());
+ }
+
+ public function testResolveFailsIfClosureReturnsFalse()
+ {
+ $this->resolver->setDefault('foo', 42);
+ $this->resolver->setAllowedValues('foo', function ($value) use (&$passedValue) {
+ $passedValue = $value;
+
+ return false;
+ });
+
+ try {
+ $this->resolver->resolve();
+ $this->fail('Should fail');
+ } catch (InvalidOptionsException $e) {
+ }
+
+ $this->assertSame(42, $passedValue);
+ }
+
+ public function testResolveSucceedsIfClosureReturnsTrue()
+ {
+ $this->resolver->setDefault('foo', 'bar');
+ $this->resolver->setAllowedValues('foo', function ($value) use (&$passedValue) {
+ $passedValue = $value;
+
+ return true;
+ });
+
+ $this->assertEquals(array('foo' => 'bar'), $this->resolver->resolve());
+ $this->assertSame('bar', $passedValue);
+ }
+
+ /**
+ * @expectedException \Symfony\Component\OptionsResolver\Exception\InvalidOptionsException
+ */
+ public function testResolveFailsIfAllClosuresReturnFalse()
+ {
+ $this->resolver->setDefault('foo', 42);
+ $this->resolver->setAllowedValues('foo', array(
+ function () { return false; },
+ function () { return false; },
+ function () { return false; },
+ ));
+
+ $this->resolver->resolve();
+ }
+
+ public function testResolveSucceedsIfAnyClosureReturnsTrue()
+ {
+ $this->resolver->setDefault('foo', 'bar');
+ $this->resolver->setAllowedValues('foo', array(
+ function () { return false; },
+ function () { return true; },
+ function () { return false; },
+ ));
+
+ $this->assertEquals(array('foo' => 'bar'), $this->resolver->resolve());
+ }
+
+ /**
+ * @expectedException \Symfony\Component\OptionsResolver\Exception\UndefinedOptionsException
+ */
+ public function testAddAllowedValuesFailsIfUnknownOption()
+ {
+ $this->resolver->addAllowedValues('foo', 'bar');
+ }
+
+ /**
+ * @expectedException \Symfony\Component\OptionsResolver\Exception\AccessException
+ */
+ public function testFailIfAddAllowedValuesFromLazyOption()
+ {
+ $this->resolver->setDefault('foo', function (Options $options) {
+ $options->addAllowedValues('bar', 'baz');
+ });
+
+ $this->resolver->setDefault('bar', 'baz');
+
+ $this->resolver->resolve();
+ }
+
+ /**
+ * @expectedException \Symfony\Component\OptionsResolver\Exception\InvalidOptionsException
+ */
+ public function testResolveFailsIfInvalidAddedValue()
+ {
+ $this->resolver->setDefault('foo', 42);
+ $this->resolver->addAllowedValues('foo', 'bar');
+
+ $this->resolver->resolve();
+ }
+
+ public function testResolveSucceedsIfValidAddedValue()
+ {
+ $this->resolver->setDefault('foo', 'bar');
+ $this->resolver->addAllowedValues('foo', 'bar');
+
+ $this->assertEquals(array('foo' => 'bar'), $this->resolver->resolve());
+ }
+
+ public function testResolveSucceedsIfValidAddedValueIsNull()
+ {
+ $this->resolver->setDefault('foo', null);
+ $this->resolver->addAllowedValues('foo', null);
+
+ $this->assertEquals(array('foo' => null), $this->resolver->resolve());
+ }
+
+ /**
+ * @expectedException \Symfony\Component\OptionsResolver\Exception\InvalidOptionsException
+ */
+ public function testResolveFailsIfInvalidAddedValueMultiple()
+ {
+ $this->resolver->setDefault('foo', 42);
+ $this->resolver->addAllowedValues('foo', array('bar', 'baz'));
+
+ $this->resolver->resolve();
+ }
+
+ public function testResolveSucceedsIfValidAddedValueMultiple()
+ {
+ $this->resolver->setDefault('foo', 'bar');
+ $this->resolver->addAllowedValues('foo', array('bar', 'baz'));
+
+ $this->assertEquals(array('foo' => 'bar'), $this->resolver->resolve());
+ }
+
+ public function testAddAllowedValuesDoesNotOverwrite()
+ {
+ $this->resolver->setDefault('foo', 'bar');
+ $this->resolver->setAllowedValues('foo', 'bar');
+ $this->resolver->addAllowedValues('foo', 'baz');
+
+ $this->assertEquals(array('foo' => 'bar'), $this->resolver->resolve());
+ }
+
+ public function testAddAllowedValuesDoesNotOverwrite2()
+ {
+ $this->resolver->setDefault('foo', 'baz');
+ $this->resolver->setAllowedValues('foo', 'bar');
+ $this->resolver->addAllowedValues('foo', 'baz');
+
+ $this->assertEquals(array('foo' => 'baz'), $this->resolver->resolve());
+ }
+
+ /**
+ * @expectedException \Symfony\Component\OptionsResolver\Exception\InvalidOptionsException
+ */
+ public function testResolveFailsIfAllAddedClosuresReturnFalse()
+ {
+ $this->resolver->setDefault('foo', 42);
+ $this->resolver->setAllowedValues('foo', function () { return false; });
+ $this->resolver->addAllowedValues('foo', function () { return false; });
+
+ $this->resolver->resolve();
+ }
+
+ public function testResolveSucceedsIfAnyAddedClosureReturnsTrue()
+ {
+ $this->resolver->setDefault('foo', 'bar');
+ $this->resolver->setAllowedValues('foo', function () { return false; });
+ $this->resolver->addAllowedValues('foo', function () { return true; });
+
+ $this->assertEquals(array('foo' => 'bar'), $this->resolver->resolve());
+ }
+
+ public function testResolveSucceedsIfAnyAddedClosureReturnsTrue2()
+ {
+ $this->resolver->setDefault('foo', 'bar');
+ $this->resolver->setAllowedValues('foo', function () { return true; });
+ $this->resolver->addAllowedValues('foo', function () { return false; });
+
+ $this->assertEquals(array('foo' => 'bar'), $this->resolver->resolve());
+ }
+
+ public function testSetNormalizerReturnsThis()
+ {
+ $this->resolver->setDefault('foo', 'bar');
+ $this->assertSame($this->resolver, $this->resolver->setNormalizer('foo', function () {}));
+ }
+
+ public function testSetNormalizerClosure()
+ {
+ $this->resolver->setDefault('foo', 'bar');
+ $this->resolver->setNormalizer('foo', function () {
+ return 'normalized';
+ });
+
+ $this->assertEquals(array('foo' => 'normalized'), $this->resolver->resolve());
+ }
+
+ /**
+ * @expectedException \Symfony\Component\OptionsResolver\Exception\UndefinedOptionsException
+ */
+ public function testSetNormalizerFailsIfUnknownOption()
+ {
+ $this->resolver->setNormalizer('foo', function () {});
+ }
+
+ /**
+ * @expectedException \Symfony\Component\OptionsResolver\Exception\AccessException
+ */
+ public function testFailIfSetNormalizerFromLazyOption()
+ {
+ $this->resolver->setDefault('foo', function (Options $options) {
+ $options->setNormalizer('foo', function () {});
+ });
+
+ $this->resolver->setDefault('bar', 'baz');
+
+ $this->resolver->resolve();
+ }
+
+ public function testNormalizerReceivesSetOption()
+ {
+ $this->resolver->setDefault('foo', 'bar');
+
+ $this->resolver->setNormalizer('foo', function (Options $options, $value) {
+ return 'normalized['.$value.']';
+ });
+
+ $this->assertEquals(array('foo' => 'normalized[bar]'), $this->resolver->resolve());
+ }
+
+ public function testNormalizerReceivesPassedOption()
+ {
+ $this->resolver->setDefault('foo', 'bar');
+
+ $this->resolver->setNormalizer('foo', function (Options $options, $value) {
+ return 'normalized['.$value.']';
+ });
+
+ $resolved = $this->resolver->resolve(array('foo' => 'baz'));
+
+ $this->assertEquals(array('foo' => 'normalized[baz]'), $resolved);
+ }
+
+ /**
+ * @expectedException \Symfony\Component\OptionsResolver\Exception\InvalidOptionsException
+ */
+ public function testValidateTypeBeforeNormalization()
+ {
+ $this->resolver->setDefault('foo', 'bar');
+
+ $this->resolver->setAllowedTypes('foo', 'int');
+
+ $this->resolver->setNormalizer('foo', function () {
+ Assert::fail('Should not be called.');
+ });
+
+ $this->resolver->resolve();
+ }
+
+ /**
+ * @expectedException \Symfony\Component\OptionsResolver\Exception\InvalidOptionsException
+ */
+ public function testValidateValueBeforeNormalization()
+ {
+ $this->resolver->setDefault('foo', 'bar');
+
+ $this->resolver->setAllowedValues('foo', 'baz');
+
+ $this->resolver->setNormalizer('foo', function () {
+ Assert::fail('Should not be called.');
+ });
+
+ $this->resolver->resolve();
+ }
+
+ public function testNormalizerCanAccessOtherOptions()
+ {
+ $this->resolver->setDefault('default', 'bar');
+ $this->resolver->setDefault('norm', 'baz');
+
+ $this->resolver->setNormalizer('norm', function (Options $options) {
+ /* @var TestCase $test */
+ Assert::assertSame('bar', $options['default']);
+
+ return 'normalized';
+ });
+
+ $this->assertEquals(array(
+ 'default' => 'bar',
+ 'norm' => 'normalized',
+ ), $this->resolver->resolve());
+ }
+
+ public function testNormalizerCanAccessLazyOptions()
+ {
+ $this->resolver->setDefault('lazy', function (Options $options) {
+ return 'bar';
+ });
+ $this->resolver->setDefault('norm', 'baz');
+
+ $this->resolver->setNormalizer('norm', function (Options $options) {
+ /* @var TestCase $test */
+ Assert::assertEquals('bar', $options['lazy']);
+
+ return 'normalized';
+ });
+
+ $this->assertEquals(array(
+ 'lazy' => 'bar',
+ 'norm' => 'normalized',
+ ), $this->resolver->resolve());
+ }
+
+ /**
+ * @expectedException \Symfony\Component\OptionsResolver\Exception\OptionDefinitionException
+ */
+ public function testFailIfCyclicDependencyBetweenNormalizers()
+ {
+ $this->resolver->setDefault('norm1', 'bar');
+ $this->resolver->setDefault('norm2', 'baz');
+
+ $this->resolver->setNormalizer('norm1', function (Options $options) {
+ $options['norm2'];
+ });
+
+ $this->resolver->setNormalizer('norm2', function (Options $options) {
+ $options['norm1'];
+ });
+
+ $this->resolver->resolve();
+ }
+
+ /**
+ * @expectedException \Symfony\Component\OptionsResolver\Exception\OptionDefinitionException
+ */
+ public function testFailIfCyclicDependencyBetweenNormalizerAndLazyOption()
+ {
+ $this->resolver->setDefault('lazy', function (Options $options) {
+ $options['norm'];
+ });
+
+ $this->resolver->setDefault('norm', 'baz');
+
+ $this->resolver->setNormalizer('norm', function (Options $options) {
+ $options['lazy'];
+ });
+
+ $this->resolver->resolve();
+ }
+
+ public function testCaughtExceptionFromNormalizerDoesNotCrashOptionResolver()
+ {
+ $throw = true;
+
+ $this->resolver->setDefaults(array('catcher' => null, 'thrower' => null));
+
+ $this->resolver->setNormalizer('catcher', function (Options $options) {
+ try {
+ return $options['thrower'];
+ } catch (\Exception $e) {
+ return false;
+ }
+ });
+
+ $this->resolver->setNormalizer('thrower', function () use (&$throw) {
+ if ($throw) {
+ $throw = false;
+ throw new \UnexpectedValueException('throwing');
+ }
+
+ return true;
+ });
+
+ $this->assertSame(array('catcher' => false, 'thrower' => true), $this->resolver->resolve());
+ }
+
+ public function testCaughtExceptionFromLazyDoesNotCrashOptionResolver()
+ {
+ $throw = true;
+
+ $this->resolver->setDefault('catcher', function (Options $options) {
+ try {
+ return $options['thrower'];
+ } catch (\Exception $e) {
+ return false;
+ }
+ });
+
+ $this->resolver->setDefault('thrower', function (Options $options) use (&$throw) {
+ if ($throw) {
+ $throw = false;
+ throw new \UnexpectedValueException('throwing');
+ }
+
+ return true;
+ });
+
+ $this->assertSame(array('catcher' => false, 'thrower' => true), $this->resolver->resolve());
+ }
+
+ public function testInvokeEachNormalizerOnlyOnce()
+ {
+ $calls = 0;
+
+ $this->resolver->setDefault('norm1', 'bar');
+ $this->resolver->setDefault('norm2', 'baz');
+
+ $this->resolver->setNormalizer('norm1', function ($options) use (&$calls) {
+ Assert::assertSame(1, ++$calls);
+
+ $options['norm2'];
+ });
+ $this->resolver->setNormalizer('norm2', function () use (&$calls) {
+ Assert::assertSame(2, ++$calls);
+ });
+
+ $this->resolver->resolve();
+
+ $this->assertSame(2, $calls);
+ }
+
+ public function testNormalizerNotCalledForUnsetOptions()
+ {
+ $this->resolver->setDefined('norm');
+
+ $this->resolver->setNormalizer('norm', function () {
+ Assert::fail('Should not be called.');
+ });
+
+ $this->assertEmpty($this->resolver->resolve());
+ }
+
+ public function testSetDefaultsReturnsThis()
+ {
+ $this->assertSame($this->resolver, $this->resolver->setDefaults(array('foo', 'bar')));
+ }
+
+ public function testSetDefaults()
+ {
+ $this->resolver->setDefault('one', '1');
+ $this->resolver->setDefault('two', 'bar');
+
+ $this->resolver->setDefaults(array(
+ 'two' => '2',
+ 'three' => '3',
+ ));
+
+ $this->assertEquals(array(
+ 'one' => '1',
+ 'two' => '2',
+ 'three' => '3',
+ ), $this->resolver->resolve());
+ }
+
+ /**
+ * @expectedException \Symfony\Component\OptionsResolver\Exception\AccessException
+ */
+ public function testFailIfSetDefaultsFromLazyOption()
+ {
+ $this->resolver->setDefault('foo', function (Options $options) {
+ $options->setDefaults(array('two' => '2'));
+ });
+
+ $this->resolver->resolve();
+ }
+
+ public function testRemoveReturnsThis()
+ {
+ $this->resolver->setDefault('foo', 'bar');
+
+ $this->assertSame($this->resolver, $this->resolver->remove('foo'));
+ }
+
+ public function testRemoveSingleOption()
+ {
+ $this->resolver->setDefault('foo', 'bar');
+ $this->resolver->setDefault('baz', 'boo');
+ $this->resolver->remove('foo');
+
+ $this->assertSame(array('baz' => 'boo'), $this->resolver->resolve());
+ }
+
+ public function testRemoveMultipleOptions()
+ {
+ $this->resolver->setDefault('foo', 'bar');
+ $this->resolver->setDefault('baz', 'boo');
+ $this->resolver->setDefault('doo', 'dam');
+
+ $this->resolver->remove(array('foo', 'doo'));
+
+ $this->assertSame(array('baz' => 'boo'), $this->resolver->resolve());
+ }
+
+ public function testRemoveLazyOption()
+ {
+ $this->resolver->setDefault('foo', function (Options $options) {
+ return 'lazy';
+ });
+ $this->resolver->remove('foo');
+
+ $this->assertSame(array(), $this->resolver->resolve());
+ }
+
+ public function testRemoveNormalizer()
+ {
+ $this->resolver->setDefault('foo', 'bar');
+ $this->resolver->setNormalizer('foo', function (Options $options, $value) {
+ return 'normalized';
+ });
+ $this->resolver->remove('foo');
+ $this->resolver->setDefault('foo', 'bar');
+
+ $this->assertSame(array('foo' => 'bar'), $this->resolver->resolve());
+ }
+
+ public function testRemoveAllowedTypes()
+ {
+ $this->resolver->setDefault('foo', 'bar');
+ $this->resolver->setAllowedTypes('foo', 'int');
+ $this->resolver->remove('foo');
+ $this->resolver->setDefault('foo', 'bar');
+
+ $this->assertSame(array('foo' => 'bar'), $this->resolver->resolve());
+ }
+
+ public function testRemoveAllowedValues()
+ {
+ $this->resolver->setDefault('foo', 'bar');
+ $this->resolver->setAllowedValues('foo', array('baz', 'boo'));
+ $this->resolver->remove('foo');
+ $this->resolver->setDefault('foo', 'bar');
+
+ $this->assertSame(array('foo' => 'bar'), $this->resolver->resolve());
+ }
+
+ /**
+ * @expectedException \Symfony\Component\OptionsResolver\Exception\AccessException
+ */
+ public function testFailIfRemoveFromLazyOption()
+ {
+ $this->resolver->setDefault('foo', function (Options $options) {
+ $options->remove('bar');
+ });
+
+ $this->resolver->setDefault('bar', 'baz');
+
+ $this->resolver->resolve();
+ }
+
+ public function testRemoveUnknownOptionIgnored()
+ {
+ $this->assertNotNull($this->resolver->remove('foo'));
+ }
+
+ public function testClearReturnsThis()
+ {
+ $this->assertSame($this->resolver, $this->resolver->clear());
+ }
+
+ public function testClearRemovesAllOptions()
+ {
+ $this->resolver->setDefault('one', 1);
+ $this->resolver->setDefault('two', 2);
+
+ $this->resolver->clear();
+
+ $this->assertEmpty($this->resolver->resolve());
+ }
+
+ public function testClearLazyOption()
+ {
+ $this->resolver->setDefault('foo', function (Options $options) {
+ return 'lazy';
+ });
+ $this->resolver->clear();
+
+ $this->assertSame(array(), $this->resolver->resolve());
+ }
+
+ public function testClearNormalizer()
+ {
+ $this->resolver->setDefault('foo', 'bar');
+ $this->resolver->setNormalizer('foo', function (Options $options, $value) {
+ return 'normalized';
+ });
+ $this->resolver->clear();
+ $this->resolver->setDefault('foo', 'bar');
+
+ $this->assertSame(array('foo' => 'bar'), $this->resolver->resolve());
+ }
+
+ public function testClearAllowedTypes()
+ {
+ $this->resolver->setDefault('foo', 'bar');
+ $this->resolver->setAllowedTypes('foo', 'int');
+ $this->resolver->clear();
+ $this->resolver->setDefault('foo', 'bar');
+
+ $this->assertSame(array('foo' => 'bar'), $this->resolver->resolve());
+ }
+
+ public function testClearAllowedValues()
+ {
+ $this->resolver->setDefault('foo', 'bar');
+ $this->resolver->setAllowedValues('foo', 'baz');
+ $this->resolver->clear();
+ $this->resolver->setDefault('foo', 'bar');
+
+ $this->assertSame(array('foo' => 'bar'), $this->resolver->resolve());
+ }
+
+ /**
+ * @expectedException \Symfony\Component\OptionsResolver\Exception\AccessException
+ */
+ public function testFailIfClearFromLazyption()
+ {
+ $this->resolver->setDefault('foo', function (Options $options) {
+ $options->clear();
+ });
+
+ $this->resolver->setDefault('bar', 'baz');
+
+ $this->resolver->resolve();
+ }
+
+ public function testClearOptionAndNormalizer()
+ {
+ $this->resolver->setDefault('foo1', 'bar');
+ $this->resolver->setNormalizer('foo1', function (Options $options) {
+ return '';
+ });
+ $this->resolver->setDefault('foo2', 'bar');
+ $this->resolver->setNormalizer('foo2', function (Options $options) {
+ return '';
+ });
+
+ $this->resolver->clear();
+ $this->assertEmpty($this->resolver->resolve());
+ }
+
+ public function testArrayAccess()
+ {
+ $this->resolver->setDefault('default1', 0);
+ $this->resolver->setDefault('default2', 1);
+ $this->resolver->setRequired('required');
+ $this->resolver->setDefined('defined');
+ $this->resolver->setDefault('lazy1', function (Options $options) {
+ return 'lazy';
+ });
+
+ $this->resolver->setDefault('lazy2', function (Options $options) {
+ Assert::assertArrayHasKey('default1', $options);
+ Assert::assertArrayHasKey('default2', $options);
+ Assert::assertArrayHasKey('required', $options);
+ Assert::assertArrayHasKey('lazy1', $options);
+ Assert::assertArrayHasKey('lazy2', $options);
+ Assert::assertArrayNotHasKey('defined', $options);
+
+ Assert::assertSame(0, $options['default1']);
+ Assert::assertSame(42, $options['default2']);
+ Assert::assertSame('value', $options['required']);
+ Assert::assertSame('lazy', $options['lazy1']);
+
+ // Obviously $options['lazy'] and $options['defined'] cannot be
+ // accessed
+ });
+
+ $this->resolver->resolve(array('default2' => 42, 'required' => 'value'));
+ }
+
+ /**
+ * @expectedException \Symfony\Component\OptionsResolver\Exception\AccessException
+ */
+ public function testArrayAccessGetFailsOutsideResolve()
+ {
+ $this->resolver->setDefault('default', 0);
+
+ $this->resolver['default'];
+ }
+
+ /**
+ * @expectedException \Symfony\Component\OptionsResolver\Exception\AccessException
+ */
+ public function testArrayAccessExistsFailsOutsideResolve()
+ {
+ $this->resolver->setDefault('default', 0);
+
+ isset($this->resolver['default']);
+ }
+
+ /**
+ * @expectedException \Symfony\Component\OptionsResolver\Exception\AccessException
+ */
+ public function testArrayAccessSetNotSupported()
+ {
+ $this->resolver['default'] = 0;
+ }
+
+ /**
+ * @expectedException \Symfony\Component\OptionsResolver\Exception\AccessException
+ */
+ public function testArrayAccessUnsetNotSupported()
+ {
+ $this->resolver->setDefault('default', 0);
+
+ unset($this->resolver['default']);
+ }
+
+ /**
+ * @expectedException \Symfony\Component\OptionsResolver\Exception\NoSuchOptionException
+ * @expectedExceptionMessage The option "undefined" does not exist. Defined options are: "foo", "lazy".
+ */
+ public function testFailIfGetNonExisting()
+ {
+ $this->resolver->setDefault('foo', 'bar');
+
+ $this->resolver->setDefault('lazy', function (Options $options) {
+ $options['undefined'];
+ });
+
+ $this->resolver->resolve();
+ }
+
+ /**
+ * @expectedException \Symfony\Component\OptionsResolver\Exception\NoSuchOptionException
+ * @expectedExceptionMessage The optional option "defined" has no value set. You should make sure it is set with "isset" before reading it.
+ */
+ public function testFailIfGetDefinedButUnset()
+ {
+ $this->resolver->setDefined('defined');
+
+ $this->resolver->setDefault('lazy', function (Options $options) {
+ $options['defined'];
+ });
+
+ $this->resolver->resolve();
+ }
+
+ /**
+ * @expectedException \Symfony\Component\OptionsResolver\Exception\OptionDefinitionException
+ */
+ public function testFailIfCyclicDependency()
+ {
+ $this->resolver->setDefault('lazy1', function (Options $options) {
+ $options['lazy2'];
+ });
+
+ $this->resolver->setDefault('lazy2', function (Options $options) {
+ $options['lazy1'];
+ });
+
+ $this->resolver->resolve();
+ }
+
+ public function testCount()
+ {
+ $this->resolver->setDefault('default', 0);
+ $this->resolver->setRequired('required');
+ $this->resolver->setDefined('defined');
+ $this->resolver->setDefault('lazy1', function () {});
+
+ $this->resolver->setDefault('lazy2', function (Options $options) {
+ Assert::assertCount(4, $options);
+ });
+
+ $this->assertCount(4, $this->resolver->resolve(array('required' => 'value')));
+ }
+
+ /**
+ * In resolve() we count the options that are actually set (which may be
+ * only a subset of the defined options). Outside of resolve(), it's not
+ * clear what is counted.
+ *
+ * @expectedException \Symfony\Component\OptionsResolver\Exception\AccessException
+ */
+ public function testCountFailsOutsideResolve()
+ {
+ $this->resolver->setDefault('foo', 0);
+ $this->resolver->setRequired('bar');
+ $this->resolver->setDefined('bar');
+ $this->resolver->setDefault('lazy1', function () {});
+
+ \count($this->resolver);
+ }
+
+ public function testNestedArrays()
+ {
+ $this->resolver->setDefined('foo');
+ $this->resolver->setAllowedTypes('foo', 'int[][]');
+
+ $this->assertEquals(array(
+ 'foo' => array(
+ array(
+ 1, 2,
+ ),
+ ),
+ ), $this->resolver->resolve(
+ array(
+ 'foo' => array(
+ array(1, 2),
+ ),
+ )
+ ));
+ }
+
+ public function testNested2Arrays()
+ {
+ $this->resolver->setDefined('foo');
+ $this->resolver->setAllowedTypes('foo', 'int[][][][]');
+
+ $this->assertEquals(array(
+ 'foo' => array(
+ array(
+ array(
+ array(
+ 1, 2,
+ ),
+ ),
+ ),
+ ),
+ ), $this->resolver->resolve(
+ array(
+ 'foo' => array(
+ array(
+ array(
+ array(1, 2),
+ ),
+ ),
+ ),
+ )
+ ));
+ }
+
+ /**
+ * @expectedException \Symfony\Component\OptionsResolver\Exception\InvalidOptionsException
+ * @expectedExceptionMessage The option "foo" with value array is expected to be of type "float[][][][]", but one of the elements is of type "integer[][][][]".
+ */
+ public function testNestedArraysException()
+ {
+ $this->resolver->setDefined('foo');
+ $this->resolver->setAllowedTypes('foo', 'float[][][][]');
+
+ $this->resolver->resolve(
+ array(
+ 'foo' => array(
+ array(
+ array(
+ array(1, 2),
+ ),
+ ),
+ ),
+ )
+ );
+ }
+
+ /**
+ * @expectedException \Symfony\Component\OptionsResolver\Exception\InvalidOptionsException
+ * @expectedExceptionMessage The option "foo" with value array is expected to be of type "int[][]", but one of the elements is of type "boolean[][]".
+ */
+ public function testNestedArrayException1()
+ {
+ $this->resolver->setDefined('foo');
+ $this->resolver->setAllowedTypes('foo', 'int[][]');
+ $this->resolver->resolve(array(
+ 'foo' => array(
+ array(1, true, 'str', array(2, 3)),
+ ),
+ ));
+ }
+
+ /**
+ * @expectedException \Symfony\Component\OptionsResolver\Exception\InvalidOptionsException
+ * @expectedExceptionMessage The option "foo" with value array is expected to be of type "int[][]", but one of the elements is of type "boolean[][]".
+ */
+ public function testNestedArrayException2()
+ {
+ $this->resolver->setDefined('foo');
+ $this->resolver->setAllowedTypes('foo', 'int[][]');
+ $this->resolver->resolve(array(
+ 'foo' => array(
+ array(true, 'str', array(2, 3)),
+ ),
+ ));
+ }
+
+ /**
+ * @expectedException \Symfony\Component\OptionsResolver\Exception\InvalidOptionsException
+ * @expectedExceptionMessage The option "foo" with value array is expected to be of type "string[][][]", but one of the elements is of type "string[][]".
+ */
+ public function testNestedArrayException3()
+ {
+ $this->resolver->setDefined('foo');
+ $this->resolver->setAllowedTypes('foo', 'string[][][]');
+ $this->resolver->resolve(array(
+ 'foo' => array(
+ array('str', array(1, 2)),
+ ),
+ ));
+ }
+
+ /**
+ * @expectedException \Symfony\Component\OptionsResolver\Exception\InvalidOptionsException
+ * @expectedExceptionMessage The option "foo" with value array is expected to be of type "string[][][]", but one of the elements is of type "integer[][][]".
+ */
+ public function testNestedArrayException4()
+ {
+ $this->resolver->setDefined('foo');
+ $this->resolver->setAllowedTypes('foo', 'string[][][]');
+ $this->resolver->resolve(array(
+ 'foo' => array(
+ array(
+ array('str'), array(1, 2), ),
+ ),
+ ));
+ }
+
+ /**
+ * @expectedException \Symfony\Component\OptionsResolver\Exception\InvalidOptionsException
+ * @expectedExceptionMessage The option "foo" with value array is expected to be of type "string[]", but one of the elements is of type "array[]".
+ */
+ public function testNestedArrayException5()
+ {
+ $this->resolver->setDefined('foo');
+ $this->resolver->setAllowedTypes('foo', 'string[]');
+ $this->resolver->resolve(array(
+ 'foo' => array(
+ array(
+ array('str'), array(1, 2), ),
+ ),
+ ));
+ }
+
+ public function testIsNestedOption()
+ {
+ $this->resolver->setDefaults(array(
+ 'database' => function (OptionsResolver $resolver) {
+ $resolver->setDefined(array('host', 'port'));
+ },
+ ));
+ $this->assertTrue($this->resolver->isNested('database'));
+ }
+
+ /**
+ * @expectedException \Symfony\Component\OptionsResolver\Exception\UndefinedOptionsException
+ * @expectedExceptionMessage The option "foo" does not exist. Defined options are: "host", "port".
+ */
+ public function testFailsIfUndefinedNestedOption()
+ {
+ $this->resolver->setDefaults(array(
+ 'name' => 'default',
+ 'database' => function (OptionsResolver $resolver) {
+ $resolver->setDefined(array('host', 'port'));
+ },
+ ));
+ $this->resolver->resolve(array(
+ 'database' => array('foo' => 'bar'),
+ ));
+ }
+
+ /**
+ * @expectedException \Symfony\Component\OptionsResolver\Exception\MissingOptionsException
+ * @expectedExceptionMessage The required option "host" is missing.
+ */
+ public function testFailsIfMissingRequiredNestedOption()
+ {
+ $this->resolver->setDefaults(array(
+ 'name' => 'default',
+ 'database' => function (OptionsResolver $resolver) {
+ $resolver->setRequired('host');
+ },
+ ));
+ $this->resolver->resolve(array(
+ 'database' => array(),
+ ));
+ }
+
+ /**
+ * @expectedException \Symfony\Component\OptionsResolver\Exception\InvalidOptionsException
+ * @expectedExceptionMessage The option "logging" with value null is expected to be of type "bool", but is of type "NULL".
+ */
+ public function testFailsIfInvalidTypeNestedOption()
+ {
+ $this->resolver->setDefaults(array(
+ 'name' => 'default',
+ 'database' => function (OptionsResolver $resolver) {
+ $resolver
+ ->setDefined('logging')
+ ->setAllowedTypes('logging', 'bool');
+ },
+ ));
+ $this->resolver->resolve(array(
+ 'database' => array('logging' => null),
+ ));
+ }
+
+ /**
+ * @expectedException \Symfony\Component\OptionsResolver\Exception\InvalidOptionsException
+ * @expectedExceptionMessage The nested option "database" with value null is expected to be of type array, but is of type "NULL".
+ */
+ public function testFailsIfNotArrayIsGivenForNestedOptions()
+ {
+ $this->resolver->setDefaults(array(
+ 'name' => 'default',
+ 'database' => function (OptionsResolver $resolver) {
+ $resolver->setDefined('host');
+ },
+ ));
+ $this->resolver->resolve(array(
+ 'database' => null,
+ ));
+ }
+
+ public function testResolveNestedOptionsWithoutDefault()
+ {
+ $this->resolver->setDefaults(array(
+ 'name' => 'default',
+ 'database' => function (OptionsResolver $resolver) {
+ $resolver->setDefined(array('host', 'port'));
+ },
+ ));
+ $actualOptions = $this->resolver->resolve();
+ $expectedOptions = array(
+ 'name' => 'default',
+ 'database' => array(),
+ );
+ $this->assertSame($expectedOptions, $actualOptions);
+ }
+
+ public function testResolveNestedOptionsWithDefault()
+ {
+ $this->resolver->setDefaults(array(
+ 'name' => 'default',
+ 'database' => function (OptionsResolver $resolver) {
+ $resolver->setDefaults(array(
+ 'host' => 'localhost',
+ 'port' => 3306,
+ ));
+ },
+ ));
+ $actualOptions = $this->resolver->resolve();
+ $expectedOptions = array(
+ 'name' => 'default',
+ 'database' => array(
+ 'host' => 'localhost',
+ 'port' => 3306,
+ ),
+ );
+ $this->assertSame($expectedOptions, $actualOptions);
+ }
+
+ public function testResolveMultipleNestedOptions()
+ {
+ $this->resolver->setDefaults(array(
+ 'name' => 'default',
+ 'database' => function (OptionsResolver $resolver) {
+ $resolver
+ ->setRequired(array('dbname', 'host'))
+ ->setDefaults(array(
+ 'port' => 3306,
+ 'replicas' => function (OptionsResolver $resolver) {
+ $resolver->setDefaults(array(
+ 'host' => 'replica1',
+ 'port' => 3306,
+ ));
+ },
+ ));
+ },
+ ));
+ $actualOptions = $this->resolver->resolve(array(
+ 'name' => 'custom',
+ 'database' => array(
+ 'dbname' => 'test',
+ 'host' => 'localhost',
+ 'port' => null,
+ 'replicas' => array('host' => 'replica2'),
+ ),
+ ));
+ $expectedOptions = array(
+ 'name' => 'custom',
+ 'database' => array(
+ 'port' => null,
+ 'replicas' => array('port' => 3306, 'host' => 'replica2'),
+ 'dbname' => 'test',
+ 'host' => 'localhost',
+ ),
+ );
+ $this->assertSame($expectedOptions, $actualOptions);
+ }
+
+ public function testResolveLazyOptionUsingNestedOption()
+ {
+ $this->resolver->setDefaults(array(
+ 'version' => function (Options $options) {
+ return $options['database']['server_version'];
+ },
+ 'database' => function (OptionsResolver $resolver) {
+ $resolver->setDefault('server_version', '3.15');
+ },
+ ));
+ $actualOptions = $this->resolver->resolve();
+ $expectedOptions = array(
+ 'database' => array('server_version' => '3.15'),
+ 'version' => '3.15',
+ );
+ $this->assertSame($expectedOptions, $actualOptions);
+ }
+
+ public function testNormalizeNestedOptionValue()
+ {
+ $this->resolver
+ ->setDefaults(array(
+ 'database' => function (OptionsResolver $resolver) {
+ $resolver->setDefaults(array(
+ 'port' => 3306,
+ 'host' => 'localhost',
+ 'dbname' => 'demo',
+ ));
+ },
+ ))
+ ->setNormalizer('database', function (Options $options, $value) {
+ ksort($value);
+
+ return $value;
+ });
+ $actualOptions = $this->resolver->resolve(array(
+ 'database' => array('dbname' => 'test'),
+ ));
+ $expectedOptions = array(
+ 'database' => array('dbname' => 'test', 'host' => 'localhost', 'port' => 3306),
+ );
+ $this->assertSame($expectedOptions, $actualOptions);
+ }
+
+ public function testOverwrittenNestedOptionNotEvaluatedIfLazyDefault()
+ {
+ // defined by superclass
+ $this->resolver->setDefault('foo', function (OptionsResolver $resolver) {
+ Assert::fail('Should not be called');
+ });
+ // defined by subclass
+ $this->resolver->setDefault('foo', function (Options $options) {
+ return 'lazy';
+ });
+ $this->assertSame(array('foo' => 'lazy'), $this->resolver->resolve());
+ }
+
+ public function testOverwrittenNestedOptionNotEvaluatedIfScalarDefault()
+ {
+ // defined by superclass
+ $this->resolver->setDefault('foo', function (OptionsResolver $resolver) {
+ Assert::fail('Should not be called');
+ });
+ // defined by subclass
+ $this->resolver->setDefault('foo', 'bar');
+ $this->assertSame(array('foo' => 'bar'), $this->resolver->resolve());
+ }
+
+ public function testOverwrittenLazyOptionNotEvaluatedIfNestedOption()
+ {
+ // defined by superclass
+ $this->resolver->setDefault('foo', function (Options $options) {
+ Assert::fail('Should not be called');
+ });
+ // defined by subclass
+ $this->resolver->setDefault('foo', function (OptionsResolver $resolver) {
+ $resolver->setDefault('bar', 'baz');
+ });
+ $this->assertSame(array('foo' => array('bar' => 'baz')), $this->resolver->resolve());
+ }
+
+ public function testResolveAllNestedOptionDefinitions()
+ {
+ // defined by superclass
+ $this->resolver->setDefault('foo', function (OptionsResolver $resolver) {
+ $resolver->setRequired('bar');
+ });
+ // defined by subclass
+ $this->resolver->setDefault('foo', function (OptionsResolver $resolver) {
+ $resolver->setDefault('bar', 'baz');
+ });
+ // defined by subclass
+ $this->resolver->setDefault('foo', function (OptionsResolver $resolver) {
+ $resolver->setDefault('ping', 'pong');
+ });
+ $this->assertSame(array('foo' => array('ping' => 'pong', 'bar' => 'baz')), $this->resolver->resolve());
+ }
+
+ public function testNormalizeNestedValue()
+ {
+ // defined by superclass
+ $this->resolver->setDefault('foo', function (OptionsResolver $resolver) {
+ $resolver->setDefault('bar', null);
+ });
+ // defined by subclass
+ $this->resolver->setNormalizer('foo', function (Options $options, $resolvedValue) {
+ if (null === $resolvedValue['bar']) {
+ $resolvedValue['bar'] = 'baz';
+ }
+
+ return $resolvedValue;
+ });
+ $this->assertSame(array('foo' => array('bar' => 'baz')), $this->resolver->resolve());
+ }
+
+ /**
+ * @expectedException \Symfony\Component\OptionsResolver\Exception\OptionDefinitionException
+ */
+ public function testFailsIfCyclicDependencyBetweenSameNestedOption()
+ {
+ $this->resolver->setDefault('database', function (OptionsResolver $resolver, Options $parent) {
+ $resolver->setDefault('replicas', $parent['database']);
+ });
+ $this->resolver->resolve();
+ }
+
+ /**
+ * @expectedException \Symfony\Component\OptionsResolver\Exception\OptionDefinitionException
+ */
+ public function testFailsIfCyclicDependencyBetweenNestedOptionAndParentLazyOption()
+ {
+ $this->resolver->setDefaults(array(
+ 'version' => function (Options $options) {
+ return $options['database']['server_version'];
+ },
+ 'database' => function (OptionsResolver $resolver, Options $parent) {
+ $resolver->setDefault('server_version', $parent['version']);
+ },
+ ));
+ $this->resolver->resolve();
+ }
+
+ /**
+ * @expectedException \Symfony\Component\OptionsResolver\Exception\OptionDefinitionException
+ */
+ public function testFailsIfCyclicDependencyBetweenNormalizerAndNestedOption()
+ {
+ $this->resolver
+ ->setDefault('name', 'default')
+ ->setDefault('database', function (OptionsResolver $resolver, Options $parent) {
+ $resolver->setDefault('host', $parent['name']);
+ })
+ ->setNormalizer('name', function (Options $options, $value) {
+ $options['database'];
+ });
+ $this->resolver->resolve();
+ }
+
+ /**
+ * @expectedException \Symfony\Component\OptionsResolver\Exception\OptionDefinitionException
+ */
+ public function testFailsIfCyclicDependencyBetweenNestedOptions()
+ {
+ $this->resolver->setDefault('database', function (OptionsResolver $resolver, Options $parent) {
+ $resolver->setDefault('host', $parent['replica']['host']);
+ });
+ $this->resolver->setDefault('replica', function (OptionsResolver $resolver, Options $parent) {
+ $resolver->setDefault('host', $parent['database']['host']);
+ });
+ $this->resolver->resolve();
+ }
+
+ public function testGetAccessToParentOptionFromNestedOption()
+ {
+ $this->resolver->setDefaults(array(
+ 'version' => 3.15,
+ 'database' => function (OptionsResolver $resolver, Options $parent) {
+ $resolver->setDefault('server_version', $parent['version']);
+ },
+ ));
+ $this->assertSame(array('version' => 3.15, 'database' => array('server_version' => 3.15)), $this->resolver->resolve());
+ }
+
+ public function testNestedClosureWithoutTypeHintNotInvoked()
+ {
+ $closure = function ($resolver) {
+ Assert::fail('Should not be called');
+ };
+ $this->resolver->setDefault('foo', $closure);
+ $this->assertSame(array('foo' => $closure), $this->resolver->resolve());
+ }
+
+ public function testNestedClosureWithoutTypeHint2ndArgumentNotInvoked()
+ {
+ $closure = function (OptionsResolver $resolver, $parent) {
+ Assert::fail('Should not be called');
+ };
+ $this->resolver->setDefault('foo', $closure);
+ $this->assertSame(array('foo' => $closure), $this->resolver->resolve());
+ }
+
+ public function testResolveLazyOptionWithTransitiveDefaultDependency()
+ {
+ $this->resolver->setDefaults(array(
+ 'ip' => null,
+ 'database' => function (OptionsResolver $resolver, Options $parent) {
+ $resolver->setDefault('host', $parent['ip']);
+ $resolver->setDefault('primary_replica', function (OptionsResolver $resolver, Options $parent) {
+ $resolver->setDefault('host', $parent['host']);
+ });
+ },
+ 'secondary_replica' => function (Options $options) {
+ return $options['database']['primary_replica']['host'];
+ },
+ ));
+ $actualOptions = $this->resolver->resolve(array('ip' => '127.0.0.1'));
+ $expectedOptions = array(
+ 'ip' => '127.0.0.1',
+ 'database' => array(
+ 'host' => '127.0.0.1',
+ 'primary_replica' => array('host' => '127.0.0.1'),
+ ),
+ 'secondary_replica' => '127.0.0.1',
+ );
+ $this->assertSame($expectedOptions, $actualOptions);
+ }
+
+ public function testAccessToParentOptionFromNestedNormalizerAndLazyOption()
+ {
+ $this->resolver->setDefaults(array(
+ 'debug' => true,
+ 'database' => function (OptionsResolver $resolver, Options $parent) {
+ $resolver
+ ->setDefined('logging')
+ ->setDefault('profiling', function (Options $options) use ($parent) {
+ return $parent['debug'];
+ })
+ ->setNormalizer('logging', function (Options $options, $value) use ($parent) {
+ return false === $parent['debug'] ? true : $value;
+ });
+ },
+ ));
+ $actualOptions = $this->resolver->resolve(array(
+ 'debug' => false,
+ 'database' => array('logging' => false),
+ ));
+ $expectedOptions = array(
+ 'debug' => false,
+ 'database' => array('profiling' => false, 'logging' => true),
+ );
+ $this->assertSame($expectedOptions, $actualOptions);
+ }
+}
diff --git a/vendor/symfony/options-resolver/composer.json b/vendor/symfony/options-resolver/composer.json
new file mode 100644
index 00000000..1c819495
--- /dev/null
+++ b/vendor/symfony/options-resolver/composer.json
@@ -0,0 +1,33 @@
+{
+ "name": "symfony/options-resolver",
+ "type": "library",
+ "description": "Symfony OptionsResolver Component",
+ "keywords": ["options", "config", "configuration"],
+ "homepage": "https://symfony.com",
+ "license": "MIT",
+ "authors": [
+ {
+ "name": "Fabien Potencier",
+ "email": "fabien@symfony.com"
+ },
+ {
+ "name": "Symfony Community",
+ "homepage": "https://symfony.com/contributors"
+ }
+ ],
+ "require": {
+ "php": "^7.1.3"
+ },
+ "autoload": {
+ "psr-4": { "Symfony\\Component\\OptionsResolver\\": "" },
+ "exclude-from-classmap": [
+ "/Tests/"
+ ]
+ },
+ "minimum-stability": "dev",
+ "extra": {
+ "branch-alias": {
+ "dev-master": "4.2-dev"
+ }
+ }
+}
diff --git a/vendor/symfony/options-resolver/phpunit.xml.dist b/vendor/symfony/options-resolver/phpunit.xml.dist
new file mode 100644
index 00000000..9a2ec111
--- /dev/null
+++ b/vendor/symfony/options-resolver/phpunit.xml.dist
@@ -0,0 +1,31 @@
+
+
+
+
+
+
+
+
+
+ ./Tests/
+
+
+
+
+
+ ./
+
+ ./Resources
+ ./Tests
+ ./vendor
+
+
+
+