From 53108e68cf03e573ed2ab75f5f684082c629d09f Mon Sep 17 00:00:00 2001 From: Damien <damien.burel@maarch.org> Date: Tue, 24 Jul 2018 16:16:37 +0200 Subject: [PATCH] FIX #8177 Vendor again --- composer.json | 5 +- .../resource/controllers/ResController.php | 1 + .../controllers/VersionUpdateController.php | 2 +- vendor/composer/autoload_files.php | 3 + vendor/composer/autoload_namespaces.php | 1 - vendor/composer/autoload_psr4.php | 7 + vendor/composer/autoload_static.php | 45 +- vendor/composer/installed.json | 499 ++++++- vendor/guzzlehttp/guzzle/CHANGELOG.md | 1287 +++++++++++++++++ vendor/guzzlehttp/guzzle/LICENSE | 19 + vendor/guzzlehttp/guzzle/README.md | 91 ++ vendor/guzzlehttp/guzzle/UPGRADING.md | 1203 +++++++++++++++ vendor/guzzlehttp/guzzle/composer.json | 44 + vendor/guzzlehttp/guzzle/src/Client.php | 422 ++++++ .../guzzlehttp/guzzle/src/ClientInterface.php | 84 ++ .../guzzle/src/Cookie/CookieJar.php | 314 ++++ .../guzzle/src/Cookie/CookieJarInterface.php | 84 ++ .../guzzle/src/Cookie/FileCookieJar.php | 90 ++ .../guzzle/src/Cookie/SessionCookieJar.php | 71 + .../guzzle/src/Cookie/SetCookie.php | 403 ++++++ .../src/Exception/BadResponseException.php | 27 + .../guzzle/src/Exception/ClientException.php | 7 + .../guzzle/src/Exception/ConnectException.php | 37 + .../guzzle/src/Exception/GuzzleException.php | 13 + .../guzzle/src/Exception/RequestException.php | 217 +++ .../guzzle/src/Exception/SeekException.php | 27 + .../guzzle/src/Exception/ServerException.php | 7 + .../Exception/TooManyRedirectsException.php | 4 + .../src/Exception/TransferException.php | 4 + .../guzzle/src/Handler/CurlFactory.php | 565 ++++++++ .../src/Handler/CurlFactoryInterface.php | 27 + .../guzzle/src/Handler/CurlHandler.php | 45 + .../guzzle/src/Handler/CurlMultiHandler.php | 199 +++ .../guzzle/src/Handler/EasyHandle.php | 92 ++ .../guzzle/src/Handler/MockHandler.php | 189 +++ .../guzzlehttp/guzzle/src/Handler/Proxy.php | 55 + .../guzzle/src/Handler/StreamHandler.php | 532 +++++++ vendor/guzzlehttp/guzzle/src/HandlerStack.php | 273 ++++ .../guzzle/src/MessageFormatter.php | 180 +++ vendor/guzzlehttp/guzzle/src/Middleware.php | 255 ++++ vendor/guzzlehttp/guzzle/src/Pool.php | 123 ++ .../guzzle/src/PrepareBodyMiddleware.php | 106 ++ .../guzzle/src/RedirectMiddleware.php | 237 +++ .../guzzlehttp/guzzle/src/RequestOptions.php | 255 ++++ .../guzzlehttp/guzzle/src/RetryMiddleware.php | 112 ++ .../guzzlehttp/guzzle/src/TransferStats.php | 126 ++ vendor/guzzlehttp/guzzle/src/UriTemplate.php | 237 +++ vendor/guzzlehttp/guzzle/src/functions.php | 333 +++++ .../guzzle/src/functions_include.php | 6 + vendor/guzzlehttp/promises/CHANGELOG.md | 65 + vendor/guzzlehttp/promises/LICENSE | 19 + vendor/guzzlehttp/promises/Makefile | 13 + vendor/guzzlehttp/promises/README.md | 504 +++++++ vendor/guzzlehttp/promises/composer.json | 34 + .../promises/src/AggregateException.php | 16 + .../promises/src/CancellationException.php | 9 + vendor/guzzlehttp/promises/src/Coroutine.php | 151 ++ .../guzzlehttp/promises/src/EachPromise.php | 229 +++ .../promises/src/FulfilledPromise.php | 82 ++ vendor/guzzlehttp/promises/src/Promise.php | 280 ++++ .../promises/src/PromiseInterface.php | 93 ++ .../promises/src/PromisorInterface.php | 15 + .../promises/src/RejectedPromise.php | 87 ++ .../promises/src/RejectionException.php | 47 + vendor/guzzlehttp/promises/src/TaskQueue.php | 66 + .../promises/src/TaskQueueInterface.php | 25 + vendor/guzzlehttp/promises/src/functions.php | 457 ++++++ .../promises/src/functions_include.php | 6 + vendor/guzzlehttp/psr7/CHANGELOG.md | 110 ++ vendor/guzzlehttp/psr7/LICENSE | 19 + vendor/guzzlehttp/psr7/README.md | 739 ++++++++++ vendor/guzzlehttp/psr7/composer.json | 39 + vendor/guzzlehttp/psr7/src/AppendStream.php | 233 +++ vendor/guzzlehttp/psr7/src/BufferStream.php | 137 ++ vendor/guzzlehttp/psr7/src/CachingStream.php | 138 ++ vendor/guzzlehttp/psr7/src/DroppingStream.php | 42 + vendor/guzzlehttp/psr7/src/FnStream.php | 149 ++ vendor/guzzlehttp/psr7/src/InflateStream.php | 52 + vendor/guzzlehttp/psr7/src/LazyOpenStream.php | 39 + vendor/guzzlehttp/psr7/src/LimitStream.php | 155 ++ vendor/guzzlehttp/psr7/src/MessageTrait.php | 183 +++ .../guzzlehttp/psr7/src/MultipartStream.php | 153 ++ vendor/guzzlehttp/psr7/src/NoSeekStream.php | 22 + vendor/guzzlehttp/psr7/src/PumpStream.php | 165 +++ vendor/guzzlehttp/psr7/src/Request.php | 142 ++ vendor/guzzlehttp/psr7/src/Response.php | 132 ++ vendor/guzzlehttp/psr7/src/ServerRequest.php | 358 +++++ vendor/guzzlehttp/psr7/src/Stream.php | 257 ++++ .../psr7/src/StreamDecoratorTrait.php | 149 ++ vendor/guzzlehttp/psr7/src/StreamWrapper.php | 121 ++ vendor/guzzlehttp/psr7/src/UploadedFile.php | 316 ++++ vendor/guzzlehttp/psr7/src/Uri.php | 702 +++++++++ vendor/guzzlehttp/psr7/src/UriNormalizer.php | 216 +++ vendor/guzzlehttp/psr7/src/UriResolver.php | 219 +++ vendor/guzzlehttp/psr7/src/functions.php | 828 +++++++++++ .../guzzlehttp/psr7/src/functions_include.php | 6 + vendor/m4tthumphrey/php-gitlab-api/.gitignore | 2 - .../m4tthumphrey/php-gitlab-api/.travis.yml | 17 - vendor/m4tthumphrey/php-gitlab-api/LICENSE | 2 +- vendor/m4tthumphrey/php-gitlab-api/README.md | 96 -- .../m4tthumphrey/php-gitlab-api/composer.json | 85 +- .../lib/Gitlab/Api/AbstractApi.php | 140 +- .../lib/Gitlab/Api/DeployKeys.php | 20 +- .../lib/Gitlab/Api/Deployments.php | 25 + .../lib/Gitlab/Api/Environments.php | 48 + .../php-gitlab-api/lib/Gitlab/Api/Groups.php | 142 +- .../lib/Gitlab/Api/IssueBoards.php | 88 ++ .../php-gitlab-api/lib/Gitlab/Api/Issues.php | 143 +- .../php-gitlab-api/lib/Gitlab/Api/Jobs.php | 178 +++ .../lib/Gitlab/Api/MergeRequests.php | 213 +-- .../lib/Gitlab/Api/Milestones.php | 28 +- .../lib/Gitlab/Api/ProjectNamespaces.php | 30 +- .../lib/Gitlab/Api/Projects.php | 465 +++--- .../lib/Gitlab/Api/Repositories.php | 257 ++-- .../lib/Gitlab/Api/RepositoryFiles.php | 135 ++ .../lib/Gitlab/Api/Snippets.php | 11 + .../php-gitlab-api/lib/Gitlab/Api/Users.php | 133 +- .../php-gitlab-api/lib/Gitlab/Api/Version.php | 9 + .../php-gitlab-api/lib/Gitlab/Client.php | 391 +++-- .../lib/Gitlab/Exception/ErrorException.php | 3 +- .../Gitlab/Exception/ExceptionInterface.php | 12 + .../Exception/InvalidArgumentException.php | 3 +- .../Exception/MissingArgumentException.php | 2 +- .../lib/Gitlab/Exception/RuntimeException.php | 3 +- .../Exception/ValidationFailedException.php | 3 +- .../lib/Gitlab/HttpClient/Builder.php | 118 ++ .../lib/Gitlab/HttpClient/HttpClient.php | 231 --- .../Gitlab/HttpClient/HttpClientInterface.php | 98 -- .../HttpClient/Listener/AuthListener.php | 95 -- .../HttpClient/Listener/ErrorListener.php | 97 -- .../Listener/PaginationListener.php | 38 - .../Gitlab/HttpClient/Message/FormRequest.php | 8 - .../HttpClient/Message/QueryStringBuilder.php | 76 + .../lib/Gitlab/HttpClient/Message/Request.php | 8 - .../Gitlab/HttpClient/Message/Response.php | 48 - .../HttpClient/Message/ResponseMediator.php | 73 + .../Gitlab/HttpClient/Plugin/ApiVersion.php | 29 + .../HttpClient/Plugin/Authentication.php | 86 ++ .../Plugin/GitlabExceptionThrower.php | 86 ++ .../lib/Gitlab/HttpClient/Plugin/History.php | 44 + .../lib/Gitlab/Model/Branch.php | 26 +- .../php-gitlab-api/lib/Gitlab/Model/Group.php | 17 +- .../php-gitlab-api/lib/Gitlab/Model/Hook.php | 6 +- .../php-gitlab-api/lib/Gitlab/Model/Issue.php | 16 +- .../php-gitlab-api/lib/Gitlab/Model/Job.php | 83 ++ .../lib/Gitlab/Model/MergeRequest.php | 18 +- .../lib/Gitlab/Model/Milestone.php | 8 +- .../lib/Gitlab/Model/Pipeline.php | 49 + .../lib/Gitlab/Model/Project.php | 313 ++-- .../lib/Gitlab/Model/ProjectHook.php | 12 +- .../lib/Gitlab/Model/Session.php | 6 +- .../lib/Gitlab/Model/Snippet.php | 10 +- .../php-gitlab-api/lib/Gitlab/Model/User.php | 20 +- .../php-gitlab-api/lib/Gitlab/ResultPager.php | 27 +- .../php-gitlab-api/phpunit.xml.dist | 25 - .../test/Gitlab/Tests/Api/AbstractApiTest.php | 177 --- .../test/Gitlab/Tests/Api/ApiTestCase.php | 23 - .../test/Gitlab/Tests/Api/DeployKeysTest.php | 51 - .../test/Gitlab/Tests/Api/GroupsTest.php | 264 ---- .../test/Gitlab/Tests/Api/IssuesTest.php | 279 ---- .../test/Gitlab/Tests/Api/KeysTest.php | 26 - .../Gitlab/Tests/Api/MergeRequestsTest.php | 368 ----- .../test/Gitlab/Tests/Api/MilestonesTest.php | 100 -- .../Tests/Api/ProjectNamespacesTest.php | 70 - .../test/Gitlab/Tests/Api/ProjectsTest.php | 1098 -------------- .../Gitlab/Tests/Api/RepositoriesTest.php | 708 --------- .../test/Gitlab/Tests/Api/SnippetsTest.php | 114 -- .../test/Gitlab/Tests/Api/SystemHooksTest.php | 80 - .../test/Gitlab/Tests/Api/TagsTest.php | 87 -- .../test/Gitlab/Tests/Api/TestCase.php | 38 - .../test/Gitlab/Tests/Api/UsersTest.php | 459 ------ .../php-gitlab-api/test/bootstrap.php | 18 - vendor/php-http/client-common/CHANGELOG.md | 137 ++ vendor/php-http/client-common/LICENSE | 19 + vendor/php-http/client-common/README.md | 55 + vendor/php-http/client-common/composer.json | 43 + .../client-common/src/BatchClient.php | 73 + .../client-common/src/BatchResult.php | 181 +++ .../src/EmulatedHttpAsyncClient.php | 27 + .../client-common/src/EmulatedHttpClient.php | 27 + .../src/Exception/BatchException.php | 39 + .../CircularRedirectionException.php | 14 + .../src/Exception/ClientErrorException.php | 14 + .../Exception/HttpClientNotFoundException.php | 14 + .../src/Exception/LoopException.php | 14 + .../MultipleRedirectionException.php | 14 + .../src/Exception/ServerErrorException.php | 14 + .../client-common/src/FlexibleHttpClient.php | 39 + .../src/HttpAsyncClientDecorator.php | 29 + .../src/HttpAsyncClientEmulator.php | 36 + .../client-common/src/HttpClientDecorator.php | 29 + .../client-common/src/HttpClientEmulator.php | 32 + .../client-common/src/HttpClientPool.php | 59 + .../HttpClientPool/LeastUsedClientPool.php | 45 + .../src/HttpClientPool/RandomClientPool.php | 31 + .../HttpClientPool/RoundRobinClientPool.php | 41 + .../client-common/src/HttpClientPoolItem.php | 178 +++ .../client-common/src/HttpClientRouter.php | 74 + .../client-common/src/HttpMethodsClient.php | 205 +++ vendor/php-http/client-common/src/Plugin.php | 32 + .../src/Plugin/AddHostPlugin.php | 77 + .../src/Plugin/AddPathPlugin.php | 48 + .../src/Plugin/AuthenticationPlugin.php | 38 + .../src/Plugin/BaseUriPlugin.php | 54 + .../src/Plugin/ContentLengthPlugin.php | 36 + .../src/Plugin/ContentTypePlugin.php | 123 ++ .../client-common/src/Plugin/CookiePlugin.php | 180 +++ .../src/Plugin/DecoderPlugin.php | 140 ++ .../client-common/src/Plugin/ErrorPlugin.php | 55 + .../src/Plugin/HeaderAppendPlugin.php | 45 + .../src/Plugin/HeaderDefaultsPlugin.php | 43 + .../src/Plugin/HeaderRemovePlugin.php | 41 + .../src/Plugin/HeaderSetPlugin.php | 41 + .../src/Plugin/HistoryPlugin.php | 49 + .../client-common/src/Plugin/Journal.php | 31 + .../src/Plugin/QueryDefaultsPlugin.php | 54 + .../src/Plugin/RedirectPlugin.php | 270 ++++ .../src/Plugin/RequestMatcherPlugin.php | 47 + .../client-common/src/Plugin/RetryPlugin.php | 122 ++ .../client-common/src/PluginClient.php | 179 +++ .../client-common/src/PluginClientFactory.php | 62 + vendor/php-http/guzzle6-adapter/CHANGELOG.md | 66 + vendor/php-http/guzzle6-adapter/LICENSE | 20 + vendor/php-http/guzzle6-adapter/README.md | 59 + vendor/php-http/guzzle6-adapter/composer.json | 49 + vendor/php-http/guzzle6-adapter/puli.json | 16 + .../php-http/guzzle6-adapter/src/Client.php | 84 ++ .../php-http/guzzle6-adapter/src/Promise.php | 140 ++ .../multipart-stream-builder/CHANGELOG.md | 60 + .../php-http/multipart-stream-builder/LICENSE | 19 + .../multipart-stream-builder/README.md | 37 + .../multipart-stream-builder/appveyor.yml | 39 + .../multipart-stream-builder/composer.json | 43 + .../src/ApacheMimetypeHelper.php | 142 ++ .../src/CustomMimetypeHelper.php | 51 + .../src/MimetypeHelper.php | 27 + .../src/MultipartStreamBuilder.php | 278 ++++ 237 files changed, 24071 insertions(+), 5708 deletions(-) create mode 100644 vendor/guzzlehttp/guzzle/CHANGELOG.md create mode 100644 vendor/guzzlehttp/guzzle/LICENSE create mode 100644 vendor/guzzlehttp/guzzle/README.md create mode 100644 vendor/guzzlehttp/guzzle/UPGRADING.md create mode 100644 vendor/guzzlehttp/guzzle/composer.json create mode 100644 vendor/guzzlehttp/guzzle/src/Client.php create mode 100644 vendor/guzzlehttp/guzzle/src/ClientInterface.php create mode 100644 vendor/guzzlehttp/guzzle/src/Cookie/CookieJar.php create mode 100644 vendor/guzzlehttp/guzzle/src/Cookie/CookieJarInterface.php create mode 100644 vendor/guzzlehttp/guzzle/src/Cookie/FileCookieJar.php create mode 100644 vendor/guzzlehttp/guzzle/src/Cookie/SessionCookieJar.php create mode 100644 vendor/guzzlehttp/guzzle/src/Cookie/SetCookie.php create mode 100644 vendor/guzzlehttp/guzzle/src/Exception/BadResponseException.php create mode 100644 vendor/guzzlehttp/guzzle/src/Exception/ClientException.php create mode 100644 vendor/guzzlehttp/guzzle/src/Exception/ConnectException.php create mode 100644 vendor/guzzlehttp/guzzle/src/Exception/GuzzleException.php create mode 100644 vendor/guzzlehttp/guzzle/src/Exception/RequestException.php create mode 100644 vendor/guzzlehttp/guzzle/src/Exception/SeekException.php create mode 100644 vendor/guzzlehttp/guzzle/src/Exception/ServerException.php create mode 100644 vendor/guzzlehttp/guzzle/src/Exception/TooManyRedirectsException.php create mode 100644 vendor/guzzlehttp/guzzle/src/Exception/TransferException.php create mode 100644 vendor/guzzlehttp/guzzle/src/Handler/CurlFactory.php create mode 100644 vendor/guzzlehttp/guzzle/src/Handler/CurlFactoryInterface.php create mode 100644 vendor/guzzlehttp/guzzle/src/Handler/CurlHandler.php create mode 100644 vendor/guzzlehttp/guzzle/src/Handler/CurlMultiHandler.php create mode 100644 vendor/guzzlehttp/guzzle/src/Handler/EasyHandle.php create mode 100644 vendor/guzzlehttp/guzzle/src/Handler/MockHandler.php create mode 100644 vendor/guzzlehttp/guzzle/src/Handler/Proxy.php create mode 100644 vendor/guzzlehttp/guzzle/src/Handler/StreamHandler.php create mode 100644 vendor/guzzlehttp/guzzle/src/HandlerStack.php create mode 100644 vendor/guzzlehttp/guzzle/src/MessageFormatter.php create mode 100644 vendor/guzzlehttp/guzzle/src/Middleware.php create mode 100644 vendor/guzzlehttp/guzzle/src/Pool.php create mode 100644 vendor/guzzlehttp/guzzle/src/PrepareBodyMiddleware.php create mode 100644 vendor/guzzlehttp/guzzle/src/RedirectMiddleware.php create mode 100644 vendor/guzzlehttp/guzzle/src/RequestOptions.php create mode 100644 vendor/guzzlehttp/guzzle/src/RetryMiddleware.php create mode 100644 vendor/guzzlehttp/guzzle/src/TransferStats.php create mode 100644 vendor/guzzlehttp/guzzle/src/UriTemplate.php create mode 100644 vendor/guzzlehttp/guzzle/src/functions.php create mode 100644 vendor/guzzlehttp/guzzle/src/functions_include.php create mode 100644 vendor/guzzlehttp/promises/CHANGELOG.md create mode 100644 vendor/guzzlehttp/promises/LICENSE create mode 100644 vendor/guzzlehttp/promises/Makefile create mode 100644 vendor/guzzlehttp/promises/README.md create mode 100644 vendor/guzzlehttp/promises/composer.json create mode 100644 vendor/guzzlehttp/promises/src/AggregateException.php create mode 100644 vendor/guzzlehttp/promises/src/CancellationException.php create mode 100644 vendor/guzzlehttp/promises/src/Coroutine.php create mode 100644 vendor/guzzlehttp/promises/src/EachPromise.php create mode 100644 vendor/guzzlehttp/promises/src/FulfilledPromise.php create mode 100644 vendor/guzzlehttp/promises/src/Promise.php create mode 100644 vendor/guzzlehttp/promises/src/PromiseInterface.php create mode 100644 vendor/guzzlehttp/promises/src/PromisorInterface.php create mode 100644 vendor/guzzlehttp/promises/src/RejectedPromise.php create mode 100644 vendor/guzzlehttp/promises/src/RejectionException.php create mode 100644 vendor/guzzlehttp/promises/src/TaskQueue.php create mode 100644 vendor/guzzlehttp/promises/src/TaskQueueInterface.php create mode 100644 vendor/guzzlehttp/promises/src/functions.php create mode 100644 vendor/guzzlehttp/promises/src/functions_include.php create mode 100644 vendor/guzzlehttp/psr7/CHANGELOG.md create mode 100644 vendor/guzzlehttp/psr7/LICENSE create mode 100644 vendor/guzzlehttp/psr7/README.md create mode 100644 vendor/guzzlehttp/psr7/composer.json create mode 100644 vendor/guzzlehttp/psr7/src/AppendStream.php create mode 100644 vendor/guzzlehttp/psr7/src/BufferStream.php create mode 100644 vendor/guzzlehttp/psr7/src/CachingStream.php create mode 100644 vendor/guzzlehttp/psr7/src/DroppingStream.php create mode 100644 vendor/guzzlehttp/psr7/src/FnStream.php create mode 100644 vendor/guzzlehttp/psr7/src/InflateStream.php create mode 100644 vendor/guzzlehttp/psr7/src/LazyOpenStream.php create mode 100644 vendor/guzzlehttp/psr7/src/LimitStream.php create mode 100644 vendor/guzzlehttp/psr7/src/MessageTrait.php create mode 100644 vendor/guzzlehttp/psr7/src/MultipartStream.php create mode 100644 vendor/guzzlehttp/psr7/src/NoSeekStream.php create mode 100644 vendor/guzzlehttp/psr7/src/PumpStream.php create mode 100644 vendor/guzzlehttp/psr7/src/Request.php create mode 100644 vendor/guzzlehttp/psr7/src/Response.php create mode 100644 vendor/guzzlehttp/psr7/src/ServerRequest.php create mode 100644 vendor/guzzlehttp/psr7/src/Stream.php create mode 100644 vendor/guzzlehttp/psr7/src/StreamDecoratorTrait.php create mode 100644 vendor/guzzlehttp/psr7/src/StreamWrapper.php create mode 100644 vendor/guzzlehttp/psr7/src/UploadedFile.php create mode 100644 vendor/guzzlehttp/psr7/src/Uri.php create mode 100644 vendor/guzzlehttp/psr7/src/UriNormalizer.php create mode 100644 vendor/guzzlehttp/psr7/src/UriResolver.php create mode 100644 vendor/guzzlehttp/psr7/src/functions.php create mode 100644 vendor/guzzlehttp/psr7/src/functions_include.php delete mode 100644 vendor/m4tthumphrey/php-gitlab-api/.gitignore delete mode 100644 vendor/m4tthumphrey/php-gitlab-api/.travis.yml delete mode 100644 vendor/m4tthumphrey/php-gitlab-api/README.md create mode 100644 vendor/m4tthumphrey/php-gitlab-api/lib/Gitlab/Api/Deployments.php create mode 100644 vendor/m4tthumphrey/php-gitlab-api/lib/Gitlab/Api/Environments.php create mode 100644 vendor/m4tthumphrey/php-gitlab-api/lib/Gitlab/Api/IssueBoards.php create mode 100644 vendor/m4tthumphrey/php-gitlab-api/lib/Gitlab/Api/Jobs.php create mode 100644 vendor/m4tthumphrey/php-gitlab-api/lib/Gitlab/Api/RepositoryFiles.php create mode 100644 vendor/m4tthumphrey/php-gitlab-api/lib/Gitlab/Api/Version.php create mode 100644 vendor/m4tthumphrey/php-gitlab-api/lib/Gitlab/Exception/ExceptionInterface.php create mode 100644 vendor/m4tthumphrey/php-gitlab-api/lib/Gitlab/HttpClient/Builder.php delete mode 100644 vendor/m4tthumphrey/php-gitlab-api/lib/Gitlab/HttpClient/HttpClient.php delete mode 100644 vendor/m4tthumphrey/php-gitlab-api/lib/Gitlab/HttpClient/HttpClientInterface.php delete mode 100644 vendor/m4tthumphrey/php-gitlab-api/lib/Gitlab/HttpClient/Listener/AuthListener.php delete mode 100644 vendor/m4tthumphrey/php-gitlab-api/lib/Gitlab/HttpClient/Listener/ErrorListener.php delete mode 100644 vendor/m4tthumphrey/php-gitlab-api/lib/Gitlab/HttpClient/Listener/PaginationListener.php delete mode 100644 vendor/m4tthumphrey/php-gitlab-api/lib/Gitlab/HttpClient/Message/FormRequest.php create mode 100644 vendor/m4tthumphrey/php-gitlab-api/lib/Gitlab/HttpClient/Message/QueryStringBuilder.php delete mode 100644 vendor/m4tthumphrey/php-gitlab-api/lib/Gitlab/HttpClient/Message/Request.php delete mode 100644 vendor/m4tthumphrey/php-gitlab-api/lib/Gitlab/HttpClient/Message/Response.php create mode 100644 vendor/m4tthumphrey/php-gitlab-api/lib/Gitlab/HttpClient/Message/ResponseMediator.php create mode 100644 vendor/m4tthumphrey/php-gitlab-api/lib/Gitlab/HttpClient/Plugin/ApiVersion.php create mode 100644 vendor/m4tthumphrey/php-gitlab-api/lib/Gitlab/HttpClient/Plugin/Authentication.php create mode 100644 vendor/m4tthumphrey/php-gitlab-api/lib/Gitlab/HttpClient/Plugin/GitlabExceptionThrower.php create mode 100644 vendor/m4tthumphrey/php-gitlab-api/lib/Gitlab/HttpClient/Plugin/History.php create mode 100644 vendor/m4tthumphrey/php-gitlab-api/lib/Gitlab/Model/Job.php create mode 100644 vendor/m4tthumphrey/php-gitlab-api/lib/Gitlab/Model/Pipeline.php delete mode 100644 vendor/m4tthumphrey/php-gitlab-api/phpunit.xml.dist delete mode 100644 vendor/m4tthumphrey/php-gitlab-api/test/Gitlab/Tests/Api/AbstractApiTest.php delete mode 100644 vendor/m4tthumphrey/php-gitlab-api/test/Gitlab/Tests/Api/ApiTestCase.php delete mode 100644 vendor/m4tthumphrey/php-gitlab-api/test/Gitlab/Tests/Api/DeployKeysTest.php delete mode 100644 vendor/m4tthumphrey/php-gitlab-api/test/Gitlab/Tests/Api/GroupsTest.php delete mode 100644 vendor/m4tthumphrey/php-gitlab-api/test/Gitlab/Tests/Api/IssuesTest.php delete mode 100644 vendor/m4tthumphrey/php-gitlab-api/test/Gitlab/Tests/Api/KeysTest.php delete mode 100644 vendor/m4tthumphrey/php-gitlab-api/test/Gitlab/Tests/Api/MergeRequestsTest.php delete mode 100644 vendor/m4tthumphrey/php-gitlab-api/test/Gitlab/Tests/Api/MilestonesTest.php delete mode 100644 vendor/m4tthumphrey/php-gitlab-api/test/Gitlab/Tests/Api/ProjectNamespacesTest.php delete mode 100644 vendor/m4tthumphrey/php-gitlab-api/test/Gitlab/Tests/Api/ProjectsTest.php delete mode 100644 vendor/m4tthumphrey/php-gitlab-api/test/Gitlab/Tests/Api/RepositoriesTest.php delete mode 100644 vendor/m4tthumphrey/php-gitlab-api/test/Gitlab/Tests/Api/SnippetsTest.php delete mode 100644 vendor/m4tthumphrey/php-gitlab-api/test/Gitlab/Tests/Api/SystemHooksTest.php delete mode 100644 vendor/m4tthumphrey/php-gitlab-api/test/Gitlab/Tests/Api/TagsTest.php delete mode 100644 vendor/m4tthumphrey/php-gitlab-api/test/Gitlab/Tests/Api/TestCase.php delete mode 100644 vendor/m4tthumphrey/php-gitlab-api/test/Gitlab/Tests/Api/UsersTest.php delete mode 100644 vendor/m4tthumphrey/php-gitlab-api/test/bootstrap.php create mode 100644 vendor/php-http/client-common/CHANGELOG.md create mode 100644 vendor/php-http/client-common/LICENSE create mode 100644 vendor/php-http/client-common/README.md create mode 100644 vendor/php-http/client-common/composer.json create mode 100644 vendor/php-http/client-common/src/BatchClient.php create mode 100644 vendor/php-http/client-common/src/BatchResult.php create mode 100644 vendor/php-http/client-common/src/EmulatedHttpAsyncClient.php create mode 100644 vendor/php-http/client-common/src/EmulatedHttpClient.php create mode 100644 vendor/php-http/client-common/src/Exception/BatchException.php create mode 100644 vendor/php-http/client-common/src/Exception/CircularRedirectionException.php create mode 100644 vendor/php-http/client-common/src/Exception/ClientErrorException.php create mode 100644 vendor/php-http/client-common/src/Exception/HttpClientNotFoundException.php create mode 100644 vendor/php-http/client-common/src/Exception/LoopException.php create mode 100644 vendor/php-http/client-common/src/Exception/MultipleRedirectionException.php create mode 100644 vendor/php-http/client-common/src/Exception/ServerErrorException.php create mode 100644 vendor/php-http/client-common/src/FlexibleHttpClient.php create mode 100644 vendor/php-http/client-common/src/HttpAsyncClientDecorator.php create mode 100644 vendor/php-http/client-common/src/HttpAsyncClientEmulator.php create mode 100644 vendor/php-http/client-common/src/HttpClientDecorator.php create mode 100644 vendor/php-http/client-common/src/HttpClientEmulator.php create mode 100644 vendor/php-http/client-common/src/HttpClientPool.php create mode 100644 vendor/php-http/client-common/src/HttpClientPool/LeastUsedClientPool.php create mode 100644 vendor/php-http/client-common/src/HttpClientPool/RandomClientPool.php create mode 100644 vendor/php-http/client-common/src/HttpClientPool/RoundRobinClientPool.php create mode 100644 vendor/php-http/client-common/src/HttpClientPoolItem.php create mode 100644 vendor/php-http/client-common/src/HttpClientRouter.php create mode 100644 vendor/php-http/client-common/src/HttpMethodsClient.php create mode 100644 vendor/php-http/client-common/src/Plugin.php create mode 100644 vendor/php-http/client-common/src/Plugin/AddHostPlugin.php create mode 100644 vendor/php-http/client-common/src/Plugin/AddPathPlugin.php create mode 100644 vendor/php-http/client-common/src/Plugin/AuthenticationPlugin.php create mode 100644 vendor/php-http/client-common/src/Plugin/BaseUriPlugin.php create mode 100644 vendor/php-http/client-common/src/Plugin/ContentLengthPlugin.php create mode 100644 vendor/php-http/client-common/src/Plugin/ContentTypePlugin.php create mode 100644 vendor/php-http/client-common/src/Plugin/CookiePlugin.php create mode 100644 vendor/php-http/client-common/src/Plugin/DecoderPlugin.php create mode 100644 vendor/php-http/client-common/src/Plugin/ErrorPlugin.php create mode 100644 vendor/php-http/client-common/src/Plugin/HeaderAppendPlugin.php create mode 100644 vendor/php-http/client-common/src/Plugin/HeaderDefaultsPlugin.php create mode 100644 vendor/php-http/client-common/src/Plugin/HeaderRemovePlugin.php create mode 100644 vendor/php-http/client-common/src/Plugin/HeaderSetPlugin.php create mode 100644 vendor/php-http/client-common/src/Plugin/HistoryPlugin.php create mode 100644 vendor/php-http/client-common/src/Plugin/Journal.php create mode 100644 vendor/php-http/client-common/src/Plugin/QueryDefaultsPlugin.php create mode 100644 vendor/php-http/client-common/src/Plugin/RedirectPlugin.php create mode 100644 vendor/php-http/client-common/src/Plugin/RequestMatcherPlugin.php create mode 100644 vendor/php-http/client-common/src/Plugin/RetryPlugin.php create mode 100644 vendor/php-http/client-common/src/PluginClient.php create mode 100644 vendor/php-http/client-common/src/PluginClientFactory.php create mode 100644 vendor/php-http/guzzle6-adapter/CHANGELOG.md create mode 100644 vendor/php-http/guzzle6-adapter/LICENSE create mode 100644 vendor/php-http/guzzle6-adapter/README.md create mode 100644 vendor/php-http/guzzle6-adapter/composer.json create mode 100644 vendor/php-http/guzzle6-adapter/puli.json create mode 100644 vendor/php-http/guzzle6-adapter/src/Client.php create mode 100644 vendor/php-http/guzzle6-adapter/src/Promise.php create mode 100644 vendor/php-http/multipart-stream-builder/CHANGELOG.md create mode 100644 vendor/php-http/multipart-stream-builder/LICENSE create mode 100644 vendor/php-http/multipart-stream-builder/README.md create mode 100644 vendor/php-http/multipart-stream-builder/appveyor.yml create mode 100644 vendor/php-http/multipart-stream-builder/composer.json create mode 100644 vendor/php-http/multipart-stream-builder/src/ApacheMimetypeHelper.php create mode 100644 vendor/php-http/multipart-stream-builder/src/CustomMimetypeHelper.php create mode 100644 vendor/php-http/multipart-stream-builder/src/MimetypeHelper.php create mode 100644 vendor/php-http/multipart-stream-builder/src/MultipartStreamBuilder.php diff --git a/composer.json b/composer.json index 9f08a564f89..88a5674a9c8 100755 --- a/composer.json +++ b/composer.json @@ -34,9 +34,10 @@ "slim/slim": "^3.8", "respect/validation": "^1.1", "php-http/curl-client": "^1.7", - "m4tthumphrey/php-gitlab-api": "8", + "m4tthumphrey/php-gitlab-api": "^9.10", "jumbojett/openid-connect-php": "^0.3.0", "zf1/zend-search-lucene": "~1.12", - "setasign/fpdi-tcpdf": "2.0.0" + "setasign/fpdi-tcpdf": "2.0.0", + "php-http/guzzle6-adapter": "^1.1" } } diff --git a/src/app/resource/controllers/ResController.php b/src/app/resource/controllers/ResController.php index 67ba464ceab..e9f32e34fef 100755 --- a/src/app/resource/controllers/ResController.php +++ b/src/app/resource/controllers/ResController.php @@ -265,6 +265,7 @@ class ResController try { $pdf = new TcpdfFpdi('P', 'pt'); $nbPages = $pdf->setSourceFile($pathToDocument); + $pdf->setPrintHeader(false); for ($i = 1; $i <= $nbPages; $i++) { $page = $pdf->importPage($i); $size = $pdf->getTemplateSize($page); diff --git a/src/app/versionUpdate/controllers/VersionUpdateController.php b/src/app/versionUpdate/controllers/VersionUpdateController.php index 5fbe2a8e191..801e9d59123 100644 --- a/src/app/versionUpdate/controllers/VersionUpdateController.php +++ b/src/app/versionUpdate/controllers/VersionUpdateController.php @@ -28,7 +28,7 @@ class VersionUpdateController return $response->withStatus(403)->withJson(['errors' => 'Service forbidden']); } - $client = new Client('https://labs.maarch.org/api/v4/'); + $client = Client::create('https://labs.maarch.org/api/v4/'); try { $tags = $client->api('tags')->all('12'); } catch (\Exception $e) { diff --git a/vendor/composer/autoload_files.php b/vendor/composer/autoload_files.php index a5479ea55c6..9b45ff26b0f 100644 --- a/vendor/composer/autoload_files.php +++ b/vendor/composer/autoload_files.php @@ -6,8 +6,11 @@ $vendorDir = dirname(dirname(__FILE__)); $baseDir = dirname($vendorDir); return array( + 'a0edc8309cc5e1d60e3047b5df6b7052' => $vendorDir . '/guzzlehttp/psr7/src/functions_include.php', '253c157292f75eb38082b5acb06f3f01' => $vendorDir . '/nikic/fast-route/src/functions.php', 'ddc0a4d7e61c0286f0f8593b1903e894' => $vendorDir . '/clue/stream-filter/src/functions.php', '8cff32064859f4559445b89279f3199c' => $vendorDir . '/php-http/message/src/filters.php', + 'c964ee0ededf28c96ebd9db5099ef910' => $vendorDir . '/guzzlehttp/promises/src/functions_include.php', + '37a3dc5111fe8f707ab4c132ef1dbc62' => $vendorDir . '/guzzlehttp/guzzle/src/functions_include.php', '0e6d7bf4a5811bfa5cf40c5ccd6fae6a' => $vendorDir . '/symfony/polyfill-mbstring/bootstrap.php', ); diff --git a/vendor/composer/autoload_namespaces.php b/vendor/composer/autoload_namespaces.php index daae60bfc1e..3225edbdd0e 100644 --- a/vendor/composer/autoload_namespaces.php +++ b/vendor/composer/autoload_namespaces.php @@ -11,5 +11,4 @@ return array( 'Zend_Search' => array($vendorDir . '/zf1/zend-search/library'), 'Zend_Exception' => array($vendorDir . '/zf1/zend-exception/library'), 'Pimple' => array($vendorDir . '/pimple/pimple/src'), - 'Gitlab\\' => array($vendorDir . '/m4tthumphrey/php-gitlab-api/lib'), ); diff --git a/vendor/composer/autoload_psr4.php b/vendor/composer/autoload_psr4.php index d8a0b24e916..2abc338813c 100644 --- a/vendor/composer/autoload_psr4.php +++ b/vendor/composer/autoload_psr4.php @@ -33,12 +33,19 @@ return array( 'Interop\\Http\\Factory\\' => array($vendorDir . '/http-interop/http-factory/src'), 'Interop\\Container\\' => array($vendorDir . '/container-interop/container-interop/src/Interop/Container'), 'Http\\Promise\\' => array($vendorDir . '/php-http/promise/src'), + 'Http\\Message\\MultipartStream\\' => array($vendorDir . '/php-http/multipart-stream-builder/src'), 'Http\\Message\\' => array($vendorDir . '/php-http/message-factory/src', $vendorDir . '/php-http/message/src'), 'Http\\Discovery\\' => array($vendorDir . '/php-http/discovery/src'), 'Http\\Client\\Curl\\' => array($vendorDir . '/php-http/curl-client/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'), 'History\\' => array($baseDir . '/src/app/history'), + 'GuzzleHttp\\Psr7\\' => array($vendorDir . '/guzzlehttp/psr7/src'), + 'GuzzleHttp\\Promise\\' => array($vendorDir . '/guzzlehttp/promises/src'), + 'GuzzleHttp\\' => array($vendorDir . '/guzzlehttp/guzzle/src'), 'Group\\' => array($baseDir . '/src/app/group'), + 'Gitlab\\' => array($vendorDir . '/m4tthumphrey/php-gitlab-api/lib/Gitlab'), 'Folder\\' => array($baseDir . '/src/app/folder'), 'FastRoute\\' => array($vendorDir . '/nikic/fast-route/src'), 'Entity\\' => array($baseDir . '/src/app/entity'), diff --git a/vendor/composer/autoload_static.php b/vendor/composer/autoload_static.php index 5c2c107f54e..8bcb68eaa5f 100644 --- a/vendor/composer/autoload_static.php +++ b/vendor/composer/autoload_static.php @@ -7,9 +7,12 @@ namespace Composer\Autoload; class ComposerStaticInit3cf41326f2ce1ccc88c4676c4e39ea47 { public static $files = array ( + 'a0edc8309cc5e1d60e3047b5df6b7052' => __DIR__ . '/..' . '/guzzlehttp/psr7/src/functions_include.php', '253c157292f75eb38082b5acb06f3f01' => __DIR__ . '/..' . '/nikic/fast-route/src/functions.php', 'ddc0a4d7e61c0286f0f8593b1903e894' => __DIR__ . '/..' . '/clue/stream-filter/src/functions.php', '8cff32064859f4559445b89279f3199c' => __DIR__ . '/..' . '/php-http/message/src/filters.php', + 'c964ee0ededf28c96ebd9db5099ef910' => __DIR__ . '/..' . '/guzzlehttp/promises/src/functions_include.php', + '37a3dc5111fe8f707ab4c132ef1dbc62' => __DIR__ . '/..' . '/guzzlehttp/guzzle/src/functions_include.php', '0e6d7bf4a5811bfa5cf40c5ccd6fae6a' => __DIR__ . '/..' . '/symfony/polyfill-mbstring/bootstrap.php', ); @@ -76,15 +79,22 @@ class ComposerStaticInit3cf41326f2ce1ccc88c4676c4e39ea47 'H' => array ( 'Http\\Promise\\' => 13, + 'Http\\Message\\MultipartStream\\' => 29, 'Http\\Message\\' => 13, 'Http\\Discovery\\' => 15, 'Http\\Client\\Curl\\' => 17, + 'Http\\Client\\Common\\' => 19, 'Http\\Client\\' => 12, + 'Http\\Adapter\\Guzzle6\\' => 21, 'History\\' => 8, ), 'G' => array ( + 'GuzzleHttp\\Psr7\\' => 16, + 'GuzzleHttp\\Promise\\' => 19, + 'GuzzleHttp\\' => 11, 'Group\\' => 6, + 'Gitlab\\' => 7, ), 'F' => array ( @@ -228,6 +238,10 @@ class ComposerStaticInit3cf41326f2ce1ccc88c4676c4e39ea47 array ( 0 => __DIR__ . '/..' . '/php-http/promise/src', ), + 'Http\\Message\\MultipartStream\\' => + array ( + 0 => __DIR__ . '/..' . '/php-http/multipart-stream-builder/src', + ), 'Http\\Message\\' => array ( 0 => __DIR__ . '/..' . '/php-http/message-factory/src', @@ -241,18 +255,42 @@ class ComposerStaticInit3cf41326f2ce1ccc88c4676c4e39ea47 array ( 0 => __DIR__ . '/..' . '/php-http/curl-client/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', + ), 'History\\' => array ( 0 => __DIR__ . '/../..' . '/src/app/history', ), + 'GuzzleHttp\\Psr7\\' => + array ( + 0 => __DIR__ . '/..' . '/guzzlehttp/psr7/src', + ), + 'GuzzleHttp\\Promise\\' => + array ( + 0 => __DIR__ . '/..' . '/guzzlehttp/promises/src', + ), + 'GuzzleHttp\\' => + array ( + 0 => __DIR__ . '/..' . '/guzzlehttp/guzzle/src', + ), 'Group\\' => array ( 0 => __DIR__ . '/../..' . '/src/app/group', ), + 'Gitlab\\' => + array ( + 0 => __DIR__ . '/..' . '/m4tthumphrey/php-gitlab-api/lib/Gitlab', + ), 'Folder\\' => array ( 0 => __DIR__ . '/../..' . '/src/app/folder', @@ -334,13 +372,6 @@ class ComposerStaticInit3cf41326f2ce1ccc88c4676c4e39ea47 0 => __DIR__ . '/..' . '/pimple/pimple/src', ), ), - 'G' => - array ( - 'Gitlab\\' => - array ( - 0 => __DIR__ . '/..' . '/m4tthumphrey/php-gitlab-api/lib', - ), - ), ); public static $classMap = array ( diff --git a/vendor/composer/installed.json b/vendor/composer/installed.json index 950519a81b0..b110d3d9201 100644 --- a/vendor/composer/installed.json +++ b/vendor/composer/installed.json @@ -174,64 +174,6 @@ "notification-url": "https://packagist.org/downloads/", "description": "Bare-bones OpenID Connect client" }, - { - "name": "m4tthumphrey/php-gitlab-api", - "version": "8.0.0", - "version_normalized": "8.0.0.0", - "source": { - "type": "git", - "url": "https://github.com/m4tthumphrey/php-gitlab-api.git", - "reference": "51242d88b68d30a0f17662e481e96561bcf846d0" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/m4tthumphrey/php-gitlab-api/zipball/51242d88b68d30a0f17662e481e96561bcf846d0", - "reference": "51242d88b68d30a0f17662e481e96561bcf846d0", - "shasum": "" - }, - "require": { - "ext-curl": "*", - "ext-xml": "*", - "kriswallsmith/buzz": ">=0.7", - "php": ">=5.3.2" - }, - "require-dev": { - "phpunit/phpunit": "~4.5" - }, - "time": "2017-06-06T07:40:46+00:00", - "type": "library", - "installation-source": "dist", - "autoload": { - "psr-0": { - "Gitlab\\": "lib/" - } - }, - "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" - }, - { - "name": "Matt Humphrey", - "homepage": "http://m4tt.io" - } - ], - "description": "GitLab API client", - "homepage": "https://github.com/m4tthumphrey/php-gitlab-api", - "keywords": [ - "api", - "gitlab" - ] - }, { "name": "php-http/message-factory", "version": "v1.0.2", @@ -1637,5 +1579,446 @@ "fpdi", "pdf" ] + }, + { + "name": "php-http/multipart-stream-builder", + "version": "1.0.0", + "version_normalized": "1.0.0.0", + "source": { + "type": "git", + "url": "https://github.com/php-http/multipart-stream-builder.git", + "reference": "1fa3c623fc813a43b39494b2a1612174e36e0fb0" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/php-http/multipart-stream-builder/zipball/1fa3c623fc813a43b39494b2a1612174e36e0fb0", + "reference": "1fa3c623fc813a43b39494b2a1612174e36e0fb0", + "shasum": "" + }, + "require": { + "php": "^5.5 || ^7.0", + "php-http/discovery": "^1.0", + "php-http/message-factory": "^1.0.2", + "psr/http-message": "^1.0" + }, + "require-dev": { + "php-http/message": "^1.5", + "phpunit/phpunit": "^4.8 || ^5.4", + "zendframework/zend-diactoros": "^1.3.5" + }, + "time": "2017-05-21T17:45:25+00:00", + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "0.3-dev" + } + }, + "installation-source": "dist", + "autoload": { + "psr-4": { + "Http\\Message\\MultipartStream\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Tobias Nyholm", + "email": "tobias.nyholm@gmail.com" + } + ], + "description": "A builder class that help you create a multipart stream", + "homepage": "http://php-http.org", + "keywords": [ + "factory", + "http", + "message", + "multipart stream", + "stream" + ] + }, + { + "name": "php-http/client-common", + "version": "1.7.0", + "version_normalized": "1.7.0.0", + "source": { + "type": "git", + "url": "https://github.com/php-http/client-common.git", + "reference": "9accb4a082eb06403747c0ffd444112eda41a0fd" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/php-http/client-common/zipball/9accb4a082eb06403747c0ffd444112eda41a0fd", + "reference": "9accb4a082eb06403747c0ffd444112eda41a0fd", + "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": "2017-11-30T11:06:59+00:00", + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.7-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": "m4tthumphrey/php-gitlab-api", + "version": "9.10.0", + "version_normalized": "9.10.0.0", + "source": { + "type": "git", + "url": "https://github.com/m4tthumphrey/php-gitlab-api.git", + "reference": "c946569ae75eff6317a5691ab590b6eb071bdb26" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/m4tthumphrey/php-gitlab-api/zipball/c946569ae75eff6317a5691ab590b6eb071bdb26", + "reference": "c946569ae75eff6317a5691ab590b6eb071bdb26", + "shasum": "" + }, + "require": { + "ext-xml": "*", + "php": "^5.6 || ^7.0", + "php-http/client-common": "^1.6", + "php-http/client-implementation": "^1.0", + "php-http/discovery": "^1.2", + "php-http/httplug": "^1.1", + "php-http/multipart-stream-builder": "^1.0", + "symfony/options-resolver": "^2.6 || ^3.0 || ^4.0" + }, + "require-dev": { + "guzzlehttp/psr7": "^1.2", + "php-http/guzzle6-adapter": "^1.0", + "php-http/mock-client": "^1.0", + "phpunit/phpunit": "^5.0" + }, + "time": "2018-06-15T13:59:39+00:00", + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "9.8.x-dev" + } + }, + "installation-source": "dist", + "autoload": { + "psr-4": { + "Gitlab\\": "lib/Gitlab/" + } + }, + "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" + }, + { + "name": "Matt Humphrey", + "homepage": "http://m4tt.io" + } + ], + "description": "GitLab API client", + "homepage": "https://github.com/m4tthumphrey/php-gitlab-api", + "keywords": [ + "api", + "gitlab" + ] + }, + { + "name": "guzzlehttp/promises", + "version": "v1.3.1", + "version_normalized": "1.3.1.0", + "source": { + "type": "git", + "url": "https://github.com/guzzle/promises.git", + "reference": "a59da6cf61d80060647ff4d3eb2c03a2bc694646" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/guzzle/promises/zipball/a59da6cf61d80060647ff4d3eb2c03a2bc694646", + "reference": "a59da6cf61d80060647ff4d3eb2c03a2bc694646", + "shasum": "" + }, + "require": { + "php": ">=5.5.0" + }, + "require-dev": { + "phpunit/phpunit": "^4.0" + }, + "time": "2016-12-20T10:07:11+00:00", + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.4-dev" + } + }, + "installation-source": "dist", + "autoload": { + "psr-4": { + "GuzzleHttp\\Promise\\": "src/" + }, + "files": [ + "src/functions_include.php" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Michael Dowling", + "email": "mtdowling@gmail.com", + "homepage": "https://github.com/mtdowling" + } + ], + "description": "Guzzle promises library", + "keywords": [ + "promise" + ] + }, + { + "name": "guzzlehttp/psr7", + "version": "1.4.2", + "version_normalized": "1.4.2.0", + "source": { + "type": "git", + "url": "https://github.com/guzzle/psr7.git", + "reference": "f5b8a8512e2b58b0071a7280e39f14f72e05d87c" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/guzzle/psr7/zipball/f5b8a8512e2b58b0071a7280e39f14f72e05d87c", + "reference": "f5b8a8512e2b58b0071a7280e39f14f72e05d87c", + "shasum": "" + }, + "require": { + "php": ">=5.4.0", + "psr/http-message": "~1.0" + }, + "provide": { + "psr/http-message-implementation": "1.0" + }, + "require-dev": { + "phpunit/phpunit": "~4.0" + }, + "time": "2017-03-20T17:10:46+00:00", + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.4-dev" + } + }, + "installation-source": "dist", + "autoload": { + "psr-4": { + "GuzzleHttp\\Psr7\\": "src/" + }, + "files": [ + "src/functions_include.php" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Michael Dowling", + "email": "mtdowling@gmail.com", + "homepage": "https://github.com/mtdowling" + }, + { + "name": "Tobias Schultze", + "homepage": "https://github.com/Tobion" + } + ], + "description": "PSR-7 message implementation that also provides common utility methods", + "keywords": [ + "http", + "message", + "request", + "response", + "stream", + "uri", + "url" + ] + }, + { + "name": "guzzlehttp/guzzle", + "version": "6.3.3", + "version_normalized": "6.3.3.0", + "source": { + "type": "git", + "url": "https://github.com/guzzle/guzzle.git", + "reference": "407b0cb880ace85c9b63c5f9551db498cb2d50ba" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/guzzle/guzzle/zipball/407b0cb880ace85c9b63c5f9551db498cb2d50ba", + "reference": "407b0cb880ace85c9b63c5f9551db498cb2d50ba", + "shasum": "" + }, + "require": { + "guzzlehttp/promises": "^1.0", + "guzzlehttp/psr7": "^1.4", + "php": ">=5.5" + }, + "require-dev": { + "ext-curl": "*", + "phpunit/phpunit": "^4.8.35 || ^5.7 || ^6.4 || ^7.0", + "psr/log": "^1.0" + }, + "suggest": { + "psr/log": "Required for using the Log middleware" + }, + "time": "2018-04-22T15:46:56+00:00", + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "6.3-dev" + } + }, + "installation-source": "dist", + "autoload": { + "files": [ + "src/functions_include.php" + ], + "psr-4": { + "GuzzleHttp\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Michael Dowling", + "email": "mtdowling@gmail.com", + "homepage": "https://github.com/mtdowling" + } + ], + "description": "Guzzle is a PHP HTTP client library", + "homepage": "http://guzzlephp.org/", + "keywords": [ + "client", + "curl", + "framework", + "http", + "http client", + "rest", + "web service" + ] + }, + { + "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" + ] } ] diff --git a/vendor/guzzlehttp/guzzle/CHANGELOG.md b/vendor/guzzlehttp/guzzle/CHANGELOG.md new file mode 100644 index 00000000000..17badd75606 --- /dev/null +++ b/vendor/guzzlehttp/guzzle/CHANGELOG.md @@ -0,0 +1,1287 @@ +# Change Log + +## 6.3.3 - 2018-04-22 + +* Fix: Default headers when decode_content is specified + + +## 6.3.2 - 2018-03-26 + +* Fix: Release process + + +## 6.3.1 - 2018-03-26 + +* Bug fix: Parsing 0 epoch expiry times in cookies [#2014](https://github.com/guzzle/guzzle/pull/2014) +* Improvement: Better ConnectException detection [#2012](https://github.com/guzzle/guzzle/pull/2012) +* Bug fix: Malformed domain that contains a "/" [#1999](https://github.com/guzzle/guzzle/pull/1999) +* Bug fix: Undefined offset when a cookie has no first key-value pair [#1998](https://github.com/guzzle/guzzle/pull/1998) +* Improvement: Support PHPUnit 6 [#1953](https://github.com/guzzle/guzzle/pull/1953) +* Bug fix: Support empty headers [#1915](https://github.com/guzzle/guzzle/pull/1915) +* Bug fix: Ignore case during header modifications [#1916](https://github.com/guzzle/guzzle/pull/1916) + ++ Minor code cleanups, documentation fixes and clarifications. + + +## 6.3.0 - 2017-06-22 + +* Feature: force IP resolution (ipv4 or ipv6) [#1608](https://github.com/guzzle/guzzle/pull/1608), [#1659](https://github.com/guzzle/guzzle/pull/1659) +* Improvement: Don't include summary in exception message when body is empty [#1621](https://github.com/guzzle/guzzle/pull/1621) +* Improvement: Handle `on_headers` option in MockHandler [#1580](https://github.com/guzzle/guzzle/pull/1580) +* Improvement: Added SUSE Linux CA path [#1609](https://github.com/guzzle/guzzle/issues/1609) +* Improvement: Use class reference for getting the name of the class instead of using hardcoded strings [#1641](https://github.com/guzzle/guzzle/pull/1641) +* Feature: Added `read_timeout` option [#1611](https://github.com/guzzle/guzzle/pull/1611) +* Bug fix: PHP 7.x fixes [#1685](https://github.com/guzzle/guzzle/pull/1685), [#1686](https://github.com/guzzle/guzzle/pull/1686), [#1811](https://github.com/guzzle/guzzle/pull/1811) +* Deprecation: BadResponseException instantiation without a response [#1642](https://github.com/guzzle/guzzle/pull/1642) +* Feature: Added NTLM auth [#1569](https://github.com/guzzle/guzzle/pull/1569) +* Feature: Track redirect HTTP status codes [#1711](https://github.com/guzzle/guzzle/pull/1711) +* Improvement: Check handler type during construction [#1745](https://github.com/guzzle/guzzle/pull/1745) +* Improvement: Always include the Content-Length if there's a body [#1721](https://github.com/guzzle/guzzle/pull/1721) +* Feature: Added convenience method to access a cookie by name [#1318](https://github.com/guzzle/guzzle/pull/1318) +* Bug fix: Fill `CURLOPT_CAPATH` and `CURLOPT_CAINFO` properly [#1684](https://github.com/guzzle/guzzle/pull/1684) +* Improvement: Use `\GuzzleHttp\Promise\rejection_for` function instead of object init [#1827](https://github.com/guzzle/guzzle/pull/1827) + + ++ Minor code cleanups, documentation fixes and clarifications. + +## 6.2.3 - 2017-02-28 + +* Fix deprecations with guzzle/psr7 version 1.4 + +## 6.2.2 - 2016-10-08 + +* Allow to pass nullable Response to delay callable +* Only add scheme when host is present +* Fix drain case where content-length is the literal string zero +* Obfuscate in-URL credentials in exceptions + +## 6.2.1 - 2016-07-18 + +* Address HTTP_PROXY security vulnerability, CVE-2016-5385: + https://httpoxy.org/ +* Fixing timeout bug with StreamHandler: + https://github.com/guzzle/guzzle/pull/1488 +* Only read up to `Content-Length` in PHP StreamHandler to avoid timeouts when + a server does not honor `Connection: close`. +* Ignore URI fragment when sending requests. + +## 6.2.0 - 2016-03-21 + +* Feature: added `GuzzleHttp\json_encode` and `GuzzleHttp\json_decode`. + https://github.com/guzzle/guzzle/pull/1389 +* Bug fix: Fix sleep calculation when waiting for delayed requests. + https://github.com/guzzle/guzzle/pull/1324 +* Feature: More flexible history containers. + https://github.com/guzzle/guzzle/pull/1373 +* Bug fix: defer sink stream opening in StreamHandler. + https://github.com/guzzle/guzzle/pull/1377 +* Bug fix: do not attempt to escape cookie values. + https://github.com/guzzle/guzzle/pull/1406 +* Feature: report original content encoding and length on decoded responses. + https://github.com/guzzle/guzzle/pull/1409 +* Bug fix: rewind seekable request bodies before dispatching to cURL. + https://github.com/guzzle/guzzle/pull/1422 +* Bug fix: provide an empty string to `http_build_query` for HHVM workaround. + https://github.com/guzzle/guzzle/pull/1367 + +## 6.1.1 - 2015-11-22 + +* Bug fix: Proxy::wrapSync() now correctly proxies to the appropriate handler + https://github.com/guzzle/guzzle/commit/911bcbc8b434adce64e223a6d1d14e9a8f63e4e4 +* Feature: HandlerStack is now more generic. + https://github.com/guzzle/guzzle/commit/f2102941331cda544745eedd97fc8fd46e1ee33e +* Bug fix: setting verify to false in the StreamHandler now disables peer + verification. https://github.com/guzzle/guzzle/issues/1256 +* Feature: Middleware now uses an exception factory, including more error + context. https://github.com/guzzle/guzzle/pull/1282 +* Feature: better support for disabled functions. + https://github.com/guzzle/guzzle/pull/1287 +* Bug fix: fixed regression where MockHandler was not using `sink`. + https://github.com/guzzle/guzzle/pull/1292 + +## 6.1.0 - 2015-09-08 + +* Feature: Added the `on_stats` request option to provide access to transfer + statistics for requests. https://github.com/guzzle/guzzle/pull/1202 +* Feature: Added the ability to persist session cookies in CookieJars. + https://github.com/guzzle/guzzle/pull/1195 +* Feature: Some compatibility updates for Google APP Engine + https://github.com/guzzle/guzzle/pull/1216 +* Feature: Added support for NO_PROXY to prevent the use of a proxy based on + a simple set of rules. https://github.com/guzzle/guzzle/pull/1197 +* Feature: Cookies can now contain square brackets. + https://github.com/guzzle/guzzle/pull/1237 +* Bug fix: Now correctly parsing `=` inside of quotes in Cookies. + https://github.com/guzzle/guzzle/pull/1232 +* Bug fix: Cusotm cURL options now correctly override curl options of the + same name. https://github.com/guzzle/guzzle/pull/1221 +* Bug fix: Content-Type header is now added when using an explicitly provided + multipart body. https://github.com/guzzle/guzzle/pull/1218 +* Bug fix: Now ignoring Set-Cookie headers that have no name. +* Bug fix: Reason phrase is no longer cast to an int in some cases in the + cURL handler. https://github.com/guzzle/guzzle/pull/1187 +* Bug fix: Remove the Authorization header when redirecting if the Host + header changes. https://github.com/guzzle/guzzle/pull/1207 +* Bug fix: Cookie path matching fixes + https://github.com/guzzle/guzzle/issues/1129 +* Bug fix: Fixing the cURL `body_as_string` setting + https://github.com/guzzle/guzzle/pull/1201 +* Bug fix: quotes are no longer stripped when parsing cookies. + https://github.com/guzzle/guzzle/issues/1172 +* Bug fix: `form_params` and `query` now always uses the `&` separator. + https://github.com/guzzle/guzzle/pull/1163 +* Bug fix: Adding a Content-Length to PHP stream wrapper requests if not set. + https://github.com/guzzle/guzzle/pull/1189 + +## 6.0.2 - 2015-07-04 + +* Fixed a memory leak in the curl handlers in which references to callbacks + were not being removed by `curl_reset`. +* Cookies are now extracted properly before redirects. +* Cookies now allow more character ranges. +* Decoded Content-Encoding responses are now modified to correctly reflect + their state if the encoding was automatically removed by a handler. This + means that the `Content-Encoding` header may be removed an the + `Content-Length` modified to reflect the message size after removing the + encoding. +* Added a more explicit error message when trying to use `form_params` and + `multipart` in the same request. +* Several fixes for HHVM support. +* Functions are now conditionally required using an additional level of + indirection to help with global Composer installations. + +## 6.0.1 - 2015-05-27 + +* Fixed a bug with serializing the `query` request option where the `&` + separator was missing. +* Added a better error message for when `body` is provided as an array. Please + use `form_params` or `multipart` instead. +* Various doc fixes. + +## 6.0.0 - 2015-05-26 + +* See the UPGRADING.md document for more information. +* Added `multipart` and `form_params` request options. +* Added `synchronous` request option. +* Added the `on_headers` request option. +* Fixed `expect` handling. +* No longer adding default middlewares in the client ctor. These need to be + present on the provided handler in order to work. +* Requests are no longer initiated when sending async requests with the + CurlMultiHandler. This prevents unexpected recursion from requests completing + while ticking the cURL loop. +* Removed the semantics of setting `default` to `true`. This is no longer + required now that the cURL loop is not ticked for async requests. +* Added request and response logging middleware. +* No longer allowing self signed certificates when using the StreamHandler. +* Ensuring that `sink` is valid if saving to a file. +* Request exceptions now include a "handler context" which provides handler + specific contextual information. +* Added `GuzzleHttp\RequestOptions` to allow request options to be applied + using constants. +* `$maxHandles` has been removed from CurlMultiHandler. +* `MultipartPostBody` is now part of the `guzzlehttp/psr7` package. + +## 5.3.0 - 2015-05-19 + +* Mock now supports `save_to` +* Marked `AbstractRequestEvent::getTransaction()` as public. +* Fixed a bug in which multiple headers using different casing would overwrite + previous headers in the associative array. +* Added `Utils::getDefaultHandler()` +* Marked `GuzzleHttp\Client::getDefaultUserAgent` as deprecated. +* URL scheme is now always lowercased. + +## 6.0.0-beta.1 + +* Requires PHP >= 5.5 +* Updated to use PSR-7 + * Requires immutable messages, which basically means an event based system + owned by a request instance is no longer possible. + * Utilizing the [Guzzle PSR-7 package](https://github.com/guzzle/psr7). + * Removed the dependency on `guzzlehttp/streams`. These stream abstractions + are available in the `guzzlehttp/psr7` package under the `GuzzleHttp\Psr7` + namespace. +* Added middleware and handler system + * Replaced the Guzzle event and subscriber system with a middleware system. + * No longer depends on RingPHP, but rather places the HTTP handlers directly + in Guzzle, operating on PSR-7 messages. + * Retry logic is now encapsulated in `GuzzleHttp\Middleware::retry`, which + means the `guzzlehttp/retry-subscriber` is now obsolete. + * Mocking responses is now handled using `GuzzleHttp\Handler\MockHandler`. +* Asynchronous responses + * No longer supports the `future` request option to send an async request. + Instead, use one of the `*Async` methods of a client (e.g., `requestAsync`, + `getAsync`, etc.). + * Utilizing `GuzzleHttp\Promise` instead of React's promise library to avoid + recursion required by chaining and forwarding react promises. See + https://github.com/guzzle/promises + * Added `requestAsync` and `sendAsync` to send request asynchronously. + * Added magic methods for `getAsync()`, `postAsync()`, etc. to send requests + asynchronously. +* Request options + * POST and form updates + * Added the `form_fields` and `form_files` request options. + * Removed the `GuzzleHttp\Post` namespace. + * The `body` request option no longer accepts an array for POST requests. + * The `exceptions` request option has been deprecated in favor of the + `http_errors` request options. + * The `save_to` request option has been deprecated in favor of `sink` request + option. +* Clients no longer accept an array of URI template string and variables for + URI variables. You will need to expand URI templates before passing them + into a client constructor or request method. +* Client methods `get()`, `post()`, `put()`, `patch()`, `options()`, etc. are + now magic methods that will send synchronous requests. +* Replaced `Utils.php` with plain functions in `functions.php`. +* Removed `GuzzleHttp\Collection`. +* Removed `GuzzleHttp\BatchResults`. Batched pool results are now returned as + an array. +* Removed `GuzzleHttp\Query`. Query string handling is now handled using an + associative array passed into the `query` request option. The query string + is serialized using PHP's `http_build_query`. If you need more control, you + can pass the query string in as a string. +* `GuzzleHttp\QueryParser` has been replaced with the + `GuzzleHttp\Psr7\parse_query`. + +## 5.2.0 - 2015-01-27 + +* Added `AppliesHeadersInterface` to make applying headers to a request based + on the body more generic and not specific to `PostBodyInterface`. +* Reduced the number of stack frames needed to send requests. +* Nested futures are now resolved in the client rather than the RequestFsm +* Finishing state transitions is now handled in the RequestFsm rather than the + RingBridge. +* Added a guard in the Pool class to not use recursion for request retries. + +## 5.1.0 - 2014-12-19 + +* Pool class no longer uses recursion when a request is intercepted. +* The size of a Pool can now be dynamically adjusted using a callback. + See https://github.com/guzzle/guzzle/pull/943. +* Setting a request option to `null` when creating a request with a client will + ensure that the option is not set. This allows you to overwrite default + request options on a per-request basis. + See https://github.com/guzzle/guzzle/pull/937. +* Added the ability to limit which protocols are allowed for redirects by + specifying a `protocols` array in the `allow_redirects` request option. +* Nested futures due to retries are now resolved when waiting for synchronous + responses. See https://github.com/guzzle/guzzle/pull/947. +* `"0"` is now an allowed URI path. See + https://github.com/guzzle/guzzle/pull/935. +* `Query` no longer typehints on the `$query` argument in the constructor, + allowing for strings and arrays. +* Exceptions thrown in the `end` event are now correctly wrapped with Guzzle + specific exceptions if necessary. + +## 5.0.3 - 2014-11-03 + +This change updates query strings so that they are treated as un-encoded values +by default where the value represents an un-encoded value to send over the +wire. A Query object then encodes the value before sending over the wire. This +means that even value query string values (e.g., ":") are url encoded. This +makes the Query class match PHP's http_build_query function. However, if you +want to send requests over the wire using valid query string characters that do +not need to be encoded, then you can provide a string to Url::setQuery() and +pass true as the second argument to specify that the query string is a raw +string that should not be parsed or encoded (unless a call to getQuery() is +subsequently made, forcing the query-string to be converted into a Query +object). + +## 5.0.2 - 2014-10-30 + +* Added a trailing `\r\n` to multipart/form-data payloads. See + https://github.com/guzzle/guzzle/pull/871 +* Added a `GuzzleHttp\Pool::send()` convenience method to match the docs. +* Status codes are now returned as integers. See + https://github.com/guzzle/guzzle/issues/881 +* No longer overwriting an existing `application/x-www-form-urlencoded` header + when sending POST requests, allowing for customized headers. See + https://github.com/guzzle/guzzle/issues/877 +* Improved path URL serialization. + + * No longer double percent-encoding characters in the path or query string if + they are already encoded. + * Now properly encoding the supplied path to a URL object, instead of only + encoding ' ' and '?'. + * Note: This has been changed in 5.0.3 to now encode query string values by + default unless the `rawString` argument is provided when setting the query + string on a URL: Now allowing many more characters to be present in the + query string without being percent encoded. See http://tools.ietf.org/html/rfc3986#appendix-A + +## 5.0.1 - 2014-10-16 + +Bugfix release. + +* Fixed an issue where connection errors still returned response object in + error and end events event though the response is unusable. This has been + corrected so that a response is not returned in the `getResponse` method of + these events if the response did not complete. https://github.com/guzzle/guzzle/issues/867 +* Fixed an issue where transfer statistics were not being populated in the + RingBridge. https://github.com/guzzle/guzzle/issues/866 + +## 5.0.0 - 2014-10-12 + +Adding support for non-blocking responses and some minor API cleanup. + +### New Features + +* Added support for non-blocking responses based on `guzzlehttp/guzzle-ring`. +* Added a public API for creating a default HTTP adapter. +* Updated the redirect plugin to be non-blocking so that redirects are sent + concurrently. Other plugins like this can now be updated to be non-blocking. +* Added a "progress" event so that you can get upload and download progress + events. +* Added `GuzzleHttp\Pool` which implements FutureInterface and transfers + requests concurrently using a capped pool size as efficiently as possible. +* Added `hasListeners()` to EmitterInterface. +* Removed `GuzzleHttp\ClientInterface::sendAll` and marked + `GuzzleHttp\Client::sendAll` as deprecated (it's still there, just not the + recommended way). + +### Breaking changes + +The breaking changes in this release are relatively minor. The biggest thing to +look out for is that request and response objects no longer implement fluent +interfaces. + +* Removed the fluent interfaces (i.e., `return $this`) from requests, + responses, `GuzzleHttp\Collection`, `GuzzleHttp\Url`, + `GuzzleHttp\Query`, `GuzzleHttp\Post\PostBody`, and + `GuzzleHttp\Cookie\SetCookie`. This blog post provides a good outline of + why I did this: http://ocramius.github.io/blog/fluent-interfaces-are-evil/. + This also makes the Guzzle message interfaces compatible with the current + PSR-7 message proposal. +* Removed "functions.php", so that Guzzle is truly PSR-4 compliant. Except + for the HTTP request functions from function.php, these functions are now + implemented in `GuzzleHttp\Utils` using camelCase. `GuzzleHttp\json_decode` + moved to `GuzzleHttp\Utils::jsonDecode`. `GuzzleHttp\get_path` moved to + `GuzzleHttp\Utils::getPath`. `GuzzleHttp\set_path` moved to + `GuzzleHttp\Utils::setPath`. `GuzzleHttp\batch` should now be + `GuzzleHttp\Pool::batch`, which returns an `objectStorage`. Using functions.php + caused problems for many users: they aren't PSR-4 compliant, require an + explicit include, and needed an if-guard to ensure that the functions are not + declared multiple times. +* Rewrote adapter layer. + * Removing all classes from `GuzzleHttp\Adapter`, these are now + implemented as callables that are stored in `GuzzleHttp\Ring\Client`. + * Removed the concept of "parallel adapters". Sending requests serially or + concurrently is now handled using a single adapter. + * Moved `GuzzleHttp\Adapter\Transaction` to `GuzzleHttp\Transaction`. The + Transaction object now exposes the request, response, and client as public + properties. The getters and setters have been removed. +* Removed the "headers" event. This event was only useful for changing the + body a response once the headers of the response were known. You can implement + a similar behavior in a number of ways. One example might be to use a + FnStream that has access to the transaction being sent. For example, when the + first byte is written, you could check if the response headers match your + expectations, and if so, change the actual stream body that is being + written to. +* Removed the `asArray` parameter from + `GuzzleHttp\Message\MessageInterface::getHeader`. If you want to get a header + value as an array, then use the newly added `getHeaderAsArray()` method of + `MessageInterface`. This change makes the Guzzle interfaces compatible with + the PSR-7 interfaces. +* `GuzzleHttp\Message\MessageFactory` no longer allows subclasses to add + custom request options using double-dispatch (this was an implementation + detail). Instead, you should now provide an associative array to the + constructor which is a mapping of the request option name mapping to a + function that applies the option value to a request. +* Removed the concept of "throwImmediately" from exceptions and error events. + This control mechanism was used to stop a transfer of concurrent requests + from completing. This can now be handled by throwing the exception or by + cancelling a pool of requests or each outstanding future request individually. +* Updated to "GuzzleHttp\Streams" 3.0. + * `GuzzleHttp\Stream\StreamInterface::getContents()` no longer accepts a + `maxLen` parameter. This update makes the Guzzle streams project + compatible with the current PSR-7 proposal. + * `GuzzleHttp\Stream\Stream::__construct`, + `GuzzleHttp\Stream\Stream::factory`, and + `GuzzleHttp\Stream\Utils::create` no longer accept a size in the second + argument. They now accept an associative array of options, including the + "size" key and "metadata" key which can be used to provide custom metadata. + +## 4.2.2 - 2014-09-08 + +* Fixed a memory leak in the CurlAdapter when reusing cURL handles. +* No longer using `request_fulluri` in stream adapter proxies. +* Relative redirects are now based on the last response, not the first response. + +## 4.2.1 - 2014-08-19 + +* Ensuring that the StreamAdapter does not always add a Content-Type header +* Adding automated github releases with a phar and zip + +## 4.2.0 - 2014-08-17 + +* Now merging in default options using a case-insensitive comparison. + Closes https://github.com/guzzle/guzzle/issues/767 +* Added the ability to automatically decode `Content-Encoding` response bodies + using the `decode_content` request option. This is set to `true` by default + to decode the response body if it comes over the wire with a + `Content-Encoding`. Set this value to `false` to disable decoding the + response content, and pass a string to provide a request `Accept-Encoding` + header and turn on automatic response decoding. This feature now allows you + to pass an `Accept-Encoding` header in the headers of a request but still + disable automatic response decoding. + Closes https://github.com/guzzle/guzzle/issues/764 +* Added the ability to throw an exception immediately when transferring + requests in parallel. Closes https://github.com/guzzle/guzzle/issues/760 +* Updating guzzlehttp/streams dependency to ~2.1 +* No longer utilizing the now deprecated namespaced methods from the stream + package. + +## 4.1.8 - 2014-08-14 + +* Fixed an issue in the CurlFactory that caused setting the `stream=false` + request option to throw an exception. + See: https://github.com/guzzle/guzzle/issues/769 +* TransactionIterator now calls rewind on the inner iterator. + See: https://github.com/guzzle/guzzle/pull/765 +* You can now set the `Content-Type` header to `multipart/form-data` + when creating POST requests to force multipart bodies. + See https://github.com/guzzle/guzzle/issues/768 + +## 4.1.7 - 2014-08-07 + +* Fixed an error in the HistoryPlugin that caused the same request and response + to be logged multiple times when an HTTP protocol error occurs. +* Ensuring that cURL does not add a default Content-Type when no Content-Type + has been supplied by the user. This prevents the adapter layer from modifying + the request that is sent over the wire after any listeners may have already + put the request in a desired state (e.g., signed the request). +* Throwing an exception when you attempt to send requests that have the + "stream" set to true in parallel using the MultiAdapter. +* Only calling curl_multi_select when there are active cURL handles. This was + previously changed and caused performance problems on some systems due to PHP + always selecting until the maximum select timeout. +* Fixed a bug where multipart/form-data POST fields were not correctly + aggregated (e.g., values with "&"). + +## 4.1.6 - 2014-08-03 + +* Added helper methods to make it easier to represent messages as strings, + including getting the start line and getting headers as a string. + +## 4.1.5 - 2014-08-02 + +* Automatically retrying cURL "Connection died, retrying a fresh connect" + errors when possible. +* cURL implementation cleanup +* Allowing multiple event subscriber listeners to be registered per event by + passing an array of arrays of listener configuration. + +## 4.1.4 - 2014-07-22 + +* Fixed a bug that caused multi-part POST requests with more than one field to + serialize incorrectly. +* Paths can now be set to "0" +* `ResponseInterface::xml` now accepts a `libxml_options` option and added a + missing default argument that was required when parsing XML response bodies. +* A `save_to` stream is now created lazily, which means that files are not + created on disk unless a request succeeds. + +## 4.1.3 - 2014-07-15 + +* Various fixes to multipart/form-data POST uploads +* Wrapping function.php in an if-statement to ensure Guzzle can be used + globally and in a Composer install +* Fixed an issue with generating and merging in events to an event array +* POST headers are only applied before sending a request to allow you to change + the query aggregator used before uploading +* Added much more robust query string parsing +* Fixed various parsing and normalization issues with URLs +* Fixing an issue where multi-valued headers were not being utilized correctly + in the StreamAdapter + +## 4.1.2 - 2014-06-18 + +* Added support for sending payloads with GET requests + +## 4.1.1 - 2014-06-08 + +* Fixed an issue related to using custom message factory options in subclasses +* Fixed an issue with nested form fields in a multi-part POST +* Fixed an issue with using the `json` request option for POST requests +* Added `ToArrayInterface` to `GuzzleHttp\Cookie\CookieJar` + +## 4.1.0 - 2014-05-27 + +* Added a `json` request option to easily serialize JSON payloads. +* Added a `GuzzleHttp\json_decode()` wrapper to safely parse JSON. +* Added `setPort()` and `getPort()` to `GuzzleHttp\Message\RequestInterface`. +* Added the ability to provide an emitter to a client in the client constructor. +* Added the ability to persist a cookie session using $_SESSION. +* Added a trait that can be used to add event listeners to an iterator. +* Removed request method constants from RequestInterface. +* Fixed warning when invalid request start-lines are received. +* Updated MessageFactory to work with custom request option methods. +* Updated cacert bundle to latest build. + +4.0.2 (2014-04-16) +------------------ + +* Proxy requests using the StreamAdapter now properly use request_fulluri (#632) +* Added the ability to set scalars as POST fields (#628) + +## 4.0.1 - 2014-04-04 + +* The HTTP status code of a response is now set as the exception code of + RequestException objects. +* 303 redirects will now correctly switch from POST to GET requests. +* The default parallel adapter of a client now correctly uses the MultiAdapter. +* HasDataTrait now initializes the internal data array as an empty array so + that the toArray() method always returns an array. + +## 4.0.0 - 2014-03-29 + +* For more information on the 4.0 transition, see: + http://mtdowling.com/blog/2014/03/15/guzzle-4-rc/ +* For information on changes and upgrading, see: + https://github.com/guzzle/guzzle/blob/master/UPGRADING.md#3x-to-40 +* Added `GuzzleHttp\batch()` as a convenience function for sending requests in + parallel without needing to write asynchronous code. +* Restructured how events are added to `GuzzleHttp\ClientInterface::sendAll()`. + You can now pass a callable or an array of associative arrays where each + associative array contains the "fn", "priority", and "once" keys. + +## 4.0.0.rc-2 - 2014-03-25 + +* Removed `getConfig()` and `setConfig()` from clients to avoid confusion + around whether things like base_url, message_factory, etc. should be able to + be retrieved or modified. +* Added `getDefaultOption()` and `setDefaultOption()` to ClientInterface +* functions.php functions were renamed using snake_case to match PHP idioms +* Added support for `HTTP_PROXY`, `HTTPS_PROXY`, and + `GUZZLE_CURL_SELECT_TIMEOUT` environment variables +* Added the ability to specify custom `sendAll()` event priorities +* Added the ability to specify custom stream context options to the stream + adapter. +* Added a functions.php function for `get_path()` and `set_path()` +* CurlAdapter and MultiAdapter now use a callable to generate curl resources +* MockAdapter now properly reads a body and emits a `headers` event +* Updated Url class to check if a scheme and host are set before adding ":" + and "//". This allows empty Url (e.g., "") to be serialized as "". +* Parsing invalid XML no longer emits warnings +* Curl classes now properly throw AdapterExceptions +* Various performance optimizations +* Streams are created with the faster `Stream\create()` function +* Marked deprecation_proxy() as internal +* Test server is now a collection of static methods on a class + +## 4.0.0-rc.1 - 2014-03-15 + +* See https://github.com/guzzle/guzzle/blob/master/UPGRADING.md#3x-to-40 + +## 3.8.1 - 2014-01-28 + +* Bug: Always using GET requests when redirecting from a 303 response +* Bug: CURLOPT_SSL_VERIFYHOST is now correctly set to false when setting `$certificateAuthority` to false in + `Guzzle\Http\ClientInterface::setSslVerification()` +* Bug: RedirectPlugin now uses strict RFC 3986 compliance when combining a base URL with a relative URL +* Bug: The body of a request can now be set to `"0"` +* Sending PHP stream requests no longer forces `HTTP/1.0` +* Adding more information to ExceptionCollection exceptions so that users have more context, including a stack trace of + each sub-exception +* Updated the `$ref` attribute in service descriptions to merge over any existing parameters of a schema (rather than + clobbering everything). +* Merging URLs will now use the query string object from the relative URL (thus allowing custom query aggregators) +* Query strings are now parsed in a way that they do no convert empty keys with no value to have a dangling `=`. + For example `foo&bar=baz` is now correctly parsed and recognized as `foo&bar=baz` rather than `foo=&bar=baz`. +* Now properly escaping the regular expression delimiter when matching Cookie domains. +* Network access is now disabled when loading XML documents + +## 3.8.0 - 2013-12-05 + +* Added the ability to define a POST name for a file +* JSON response parsing now properly walks additionalProperties +* cURL error code 18 is now retried automatically in the BackoffPlugin +* Fixed a cURL error when URLs contain fragments +* Fixed an issue in the BackoffPlugin retry event where it was trying to access all exceptions as if they were + CurlExceptions +* CURLOPT_PROGRESS function fix for PHP 5.5 (69fcc1e) +* Added the ability for Guzzle to work with older versions of cURL that do not support `CURLOPT_TIMEOUT_MS` +* Fixed a bug that was encountered when parsing empty header parameters +* UriTemplate now has a `setRegex()` method to match the docs +* The `debug` request parameter now checks if it is truthy rather than if it exists +* Setting the `debug` request parameter to true shows verbose cURL output instead of using the LogPlugin +* Added the ability to combine URLs using strict RFC 3986 compliance +* Command objects can now return the validation errors encountered by the command +* Various fixes to cache revalidation (#437 and 29797e5) +* Various fixes to the AsyncPlugin +* Cleaned up build scripts + +## 3.7.4 - 2013-10-02 + +* Bug fix: 0 is now an allowed value in a description parameter that has a default value (#430) +* Bug fix: SchemaFormatter now returns an integer when formatting to a Unix timestamp + (see https://github.com/aws/aws-sdk-php/issues/147) +* Bug fix: Cleaned up and fixed URL dot segment removal to properly resolve internal dots +* Minimum PHP version is now properly specified as 5.3.3 (up from 5.3.2) (#420) +* Updated the bundled cacert.pem (#419) +* OauthPlugin now supports adding authentication to headers or query string (#425) + +## 3.7.3 - 2013-09-08 + +* Added the ability to get the exception associated with a request/command when using `MultiTransferException` and + `CommandTransferException`. +* Setting `additionalParameters` of a response to false is now honored when parsing responses with a service description +* Schemas are only injected into response models when explicitly configured. +* No longer guessing Content-Type based on the path of a request. Content-Type is now only guessed based on the path of + an EntityBody. +* Bug fix: ChunkedIterator can now properly chunk a \Traversable as well as an \Iterator. +* Bug fix: FilterIterator now relies on `\Iterator` instead of `\Traversable`. +* Bug fix: Gracefully handling malformed responses in RequestMediator::writeResponseBody() +* Bug fix: Replaced call to canCache with canCacheRequest in the CallbackCanCacheStrategy of the CachePlugin +* Bug fix: Visiting XML attributes first before visiting XML children when serializing requests +* Bug fix: Properly parsing headers that contain commas contained in quotes +* Bug fix: mimetype guessing based on a filename is now case-insensitive + +## 3.7.2 - 2013-08-02 + +* Bug fix: Properly URL encoding paths when using the PHP-only version of the UriTemplate expander + See https://github.com/guzzle/guzzle/issues/371 +* Bug fix: Cookie domains are now matched correctly according to RFC 6265 + See https://github.com/guzzle/guzzle/issues/377 +* Bug fix: GET parameters are now used when calculating an OAuth signature +* Bug fix: Fixed an issue with cache revalidation where the If-None-Match header was being double quoted +* `Guzzle\Common\AbstractHasDispatcher::dispatch()` now returns the event that was dispatched +* `Guzzle\Http\QueryString::factory()` now guesses the most appropriate query aggregator to used based on the input. + See https://github.com/guzzle/guzzle/issues/379 +* Added a way to add custom domain objects to service description parsing using the `operation.parse_class` event. See + https://github.com/guzzle/guzzle/pull/380 +* cURL multi cleanup and optimizations + +## 3.7.1 - 2013-07-05 + +* Bug fix: Setting default options on a client now works +* Bug fix: Setting options on HEAD requests now works. See #352 +* Bug fix: Moving stream factory before send event to before building the stream. See #353 +* Bug fix: Cookies no longer match on IP addresses per RFC 6265 +* Bug fix: Correctly parsing header parameters that are in `<>` and quotes +* Added `cert` and `ssl_key` as request options +* `Host` header can now diverge from the host part of a URL if the header is set manually +* `Guzzle\Service\Command\LocationVisitor\Request\XmlVisitor` was rewritten to change from using SimpleXML to XMLWriter +* OAuth parameters are only added via the plugin if they aren't already set +* Exceptions are now thrown when a URL cannot be parsed +* Returning `false` if `Guzzle\Http\EntityBody::getContentMd5()` fails +* Not setting a `Content-MD5` on a command if calculating the Content-MD5 fails via the CommandContentMd5Plugin + +## 3.7.0 - 2013-06-10 + +* See UPGRADING.md for more information on how to upgrade. +* Requests now support the ability to specify an array of $options when creating a request to more easily modify a + request. You can pass a 'request.options' configuration setting to a client to apply default request options to + every request created by a client (e.g. default query string variables, headers, curl options, etc.). +* Added a static facade class that allows you to use Guzzle with static methods and mount the class to `\Guzzle`. + See `Guzzle\Http\StaticClient::mount`. +* Added `command.request_options` to `Guzzle\Service\Command\AbstractCommand` to pass request options to requests + created by a command (e.g. custom headers, query string variables, timeout settings, etc.). +* Stream size in `Guzzle\Stream\PhpStreamRequestFactory` will now be set if Content-Length is returned in the + headers of a response +* Added `Guzzle\Common\Collection::setPath($path, $value)` to set a value into an array using a nested key + (e.g. `$collection->setPath('foo/baz/bar', 'test'); echo $collection['foo']['bar']['bar'];`) +* ServiceBuilders now support storing and retrieving arbitrary data +* CachePlugin can now purge all resources for a given URI +* CachePlugin can automatically purge matching cached items when a non-idempotent request is sent to a resource +* CachePlugin now uses the Vary header to determine if a resource is a cache hit +* `Guzzle\Http\Message\Response` now implements `\Serializable` +* Added `Guzzle\Cache\CacheAdapterFactory::fromCache()` to more easily create cache adapters +* `Guzzle\Service\ClientInterface::execute()` now accepts an array, single command, or Traversable +* Fixed a bug in `Guzzle\Http\Message\Header\Link::addLink()` +* Better handling of calculating the size of a stream in `Guzzle\Stream\Stream` using fstat() and caching the size +* `Guzzle\Common\Exception\ExceptionCollection` now creates a more readable exception message +* Fixing BC break: Added back the MonologLogAdapter implementation rather than extending from PsrLog so that older + Symfony users can still use the old version of Monolog. +* Fixing BC break: Added the implementation back in for `Guzzle\Http\Message\AbstractMessage::getTokenizedHeader()`. + Now triggering an E_USER_DEPRECATED warning when used. Use `$message->getHeader()->parseParams()`. +* Several performance improvements to `Guzzle\Common\Collection` +* Added an `$options` argument to the end of the following methods of `Guzzle\Http\ClientInterface`: + createRequest, head, delete, put, patch, post, options, prepareRequest +* Added an `$options` argument to the end of `Guzzle\Http\Message\Request\RequestFactoryInterface::createRequest()` +* Added an `applyOptions()` method to `Guzzle\Http\Message\Request\RequestFactoryInterface` +* Changed `Guzzle\Http\ClientInterface::get($uri = null, $headers = null, $body = null)` to + `Guzzle\Http\ClientInterface::get($uri = null, $headers = null, $options = array())`. You can still pass in a + resource, string, or EntityBody into the $options parameter to specify the download location of the response. +* Changed `Guzzle\Common\Collection::__construct($data)` to no longer accepts a null value for `$data` but a + default `array()` +* Added `Guzzle\Stream\StreamInterface::isRepeatable` +* Removed `Guzzle\Http\ClientInterface::setDefaultHeaders(). Use + $client->getConfig()->setPath('request.options/headers/{header_name}', 'value')`. or + $client->getConfig()->setPath('request.options/headers', array('header_name' => 'value'))`. +* Removed `Guzzle\Http\ClientInterface::getDefaultHeaders(). Use $client->getConfig()->getPath('request.options/headers')`. +* Removed `Guzzle\Http\ClientInterface::expandTemplate()` +* Removed `Guzzle\Http\ClientInterface::setRequestFactory()` +* Removed `Guzzle\Http\ClientInterface::getCurlMulti()` +* Removed `Guzzle\Http\Message\RequestInterface::canCache` +* Removed `Guzzle\Http\Message\RequestInterface::setIsRedirect` +* Removed `Guzzle\Http\Message\RequestInterface::isRedirect` +* Made `Guzzle\Http\Client::expandTemplate` and `getUriTemplate` protected methods. +* You can now enable E_USER_DEPRECATED warnings to see if you are using a deprecated method by setting + `Guzzle\Common\Version::$emitWarnings` to true. +* Marked `Guzzle\Http\Message\Request::isResponseBodyRepeatable()` as deprecated. Use + `$request->getResponseBody()->isRepeatable()` instead. +* Marked `Guzzle\Http\Message\Request::canCache()` as deprecated. Use + `Guzzle\Plugin\Cache\DefaultCanCacheStrategy->canCacheRequest()` instead. +* Marked `Guzzle\Http\Message\Request::canCache()` as deprecated. Use + `Guzzle\Plugin\Cache\DefaultCanCacheStrategy->canCacheRequest()` instead. +* Marked `Guzzle\Http\Message\Request::setIsRedirect()` as deprecated. Use the HistoryPlugin instead. +* Marked `Guzzle\Http\Message\Request::isRedirect()` as deprecated. Use the HistoryPlugin instead. +* Marked `Guzzle\Cache\CacheAdapterFactory::factory()` as deprecated +* Marked 'command.headers', 'command.response_body' and 'command.on_complete' as deprecated for AbstractCommand. + These will work through Guzzle 4.0 +* Marked 'request.params' for `Guzzle\Http\Client` as deprecated. Use [request.options][params]. +* Marked `Guzzle\Service\Client::enableMagicMethods()` as deprecated. Magic methods can no longer be disabled on a Guzzle\Service\Client. +* Marked `Guzzle\Service\Client::getDefaultHeaders()` as deprecated. Use $client->getConfig()->getPath('request.options/headers')`. +* Marked `Guzzle\Service\Client::setDefaultHeaders()` as deprecated. Use $client->getConfig()->setPath('request.options/headers/{header_name}', 'value')`. +* Marked `Guzzle\Parser\Url\UrlParser` as deprecated. Just use PHP's `parse_url()` and percent encode your UTF-8. +* Marked `Guzzle\Common\Collection::inject()` as deprecated. +* Marked `Guzzle\Plugin\CurlAuth\CurlAuthPlugin` as deprecated. Use `$client->getConfig()->setPath('request.options/auth', array('user', 'pass', 'Basic|Digest');` +* CacheKeyProviderInterface and DefaultCacheKeyProvider are no longer used. All of this logic is handled in a + CacheStorageInterface. These two objects and interface will be removed in a future version. +* Always setting X-cache headers on cached responses +* Default cache TTLs are now handled by the CacheStorageInterface of a CachePlugin +* `CacheStorageInterface::cache($key, Response $response, $ttl = null)` has changed to `cache(RequestInterface + $request, Response $response);` +* `CacheStorageInterface::fetch($key)` has changed to `fetch(RequestInterface $request);` +* `CacheStorageInterface::delete($key)` has changed to `delete(RequestInterface $request);` +* Added `CacheStorageInterface::purge($url)` +* `DefaultRevalidation::__construct(CacheKeyProviderInterface $cacheKey, CacheStorageInterface $cache, CachePlugin + $plugin)` has changed to `DefaultRevalidation::__construct(CacheStorageInterface $cache, + CanCacheStrategyInterface $canCache = null)` +* Added `RevalidationInterface::shouldRevalidate(RequestInterface $request, Response $response)` + +## 3.6.0 - 2013-05-29 + +* ServiceDescription now implements ToArrayInterface +* Added command.hidden_params to blacklist certain headers from being treated as additionalParameters +* Guzzle can now correctly parse incomplete URLs +* Mixed casing of headers are now forced to be a single consistent casing across all values for that header. +* Messages internally use a HeaderCollection object to delegate handling case-insensitive header resolution +* Removed the whole changedHeader() function system of messages because all header changes now go through addHeader(). +* Specific header implementations can be created for complex headers. When a message creates a header, it uses a + HeaderFactory which can map specific headers to specific header classes. There is now a Link header and + CacheControl header implementation. +* Removed from interface: Guzzle\Http\ClientInterface::setUriTemplate +* Removed from interface: Guzzle\Http\ClientInterface::setCurlMulti() +* Removed Guzzle\Http\Message\Request::receivedRequestHeader() and implemented this functionality in + Guzzle\Http\Curl\RequestMediator +* Removed the optional $asString parameter from MessageInterface::getHeader(). Just cast the header to a string. +* Removed the optional $tryChunkedTransfer option from Guzzle\Http\Message\EntityEnclosingRequestInterface +* Removed the $asObjects argument from Guzzle\Http\Message\MessageInterface::getHeaders() +* Removed Guzzle\Parser\ParserRegister::get(). Use getParser() +* Removed Guzzle\Parser\ParserRegister::set(). Use registerParser(). +* All response header helper functions return a string rather than mixing Header objects and strings inconsistently +* Removed cURL blacklist support. This is no longer necessary now that Expect, Accept, etc. are managed by Guzzle + directly via interfaces +* Removed the injecting of a request object onto a response object. The methods to get and set a request still exist + but are a no-op until removed. +* Most classes that used to require a `Guzzle\Service\Command\CommandInterface` typehint now request a + `Guzzle\Service\Command\ArrayCommandInterface`. +* Added `Guzzle\Http\Message\RequestInterface::startResponse()` to the RequestInterface to handle injecting a response + on a request while the request is still being transferred +* The ability to case-insensitively search for header values +* Guzzle\Http\Message\Header::hasExactHeader +* Guzzle\Http\Message\Header::raw. Use getAll() +* Deprecated cache control specific methods on Guzzle\Http\Message\AbstractMessage. Use the CacheControl header object + instead. +* `Guzzle\Service\Command\CommandInterface` now extends from ToArrayInterface and ArrayAccess +* Added the ability to cast Model objects to a string to view debug information. + +## 3.5.0 - 2013-05-13 + +* Bug: Fixed a regression so that request responses are parsed only once per oncomplete event rather than multiple times +* Bug: Better cleanup of one-time events across the board (when an event is meant to fire once, it will now remove + itself from the EventDispatcher) +* Bug: `Guzzle\Log\MessageFormatter` now properly writes "total_time" and "connect_time" values +* Bug: Cloning an EntityEnclosingRequest now clones the EntityBody too +* Bug: Fixed an undefined index error when parsing nested JSON responses with a sentAs parameter that reference a + non-existent key +* Bug: All __call() method arguments are now required (helps with mocking frameworks) +* Deprecating Response::getRequest() and now using a shallow clone of a request object to remove a circular reference + to help with refcount based garbage collection of resources created by sending a request +* Deprecating ZF1 cache and log adapters. These will be removed in the next major version. +* Deprecating `Response::getPreviousResponse()` (method signature still exists, but it's deprecated). Use the + HistoryPlugin for a history. +* Added a `responseBody` alias for the `response_body` location +* Refactored internals to no longer rely on Response::getRequest() +* HistoryPlugin can now be cast to a string +* HistoryPlugin now logs transactions rather than requests and responses to more accurately keep track of the requests + and responses that are sent over the wire +* Added `getEffectiveUrl()` and `getRedirectCount()` to Response objects + +## 3.4.3 - 2013-04-30 + +* Bug fix: Fixing bug introduced in 3.4.2 where redirect responses are duplicated on the final redirected response +* Added a check to re-extract the temp cacert bundle from the phar before sending each request + +## 3.4.2 - 2013-04-29 + +* Bug fix: Stream objects now work correctly with "a" and "a+" modes +* Bug fix: Removing `Transfer-Encoding: chunked` header when a Content-Length is present +* Bug fix: AsyncPlugin no longer forces HEAD requests +* Bug fix: DateTime timezones are now properly handled when using the service description schema formatter +* Bug fix: CachePlugin now properly handles stale-if-error directives when a request to the origin server fails +* Setting a response on a request will write to the custom request body from the response body if one is specified +* LogPlugin now writes to php://output when STDERR is undefined +* Added the ability to set multiple POST files for the same key in a single call +* application/x-www-form-urlencoded POSTs now use the utf-8 charset by default +* Added the ability to queue CurlExceptions to the MockPlugin +* Cleaned up how manual responses are queued on requests (removed "queued_response" and now using request.before_send) +* Configuration loading now allows remote files + +## 3.4.1 - 2013-04-16 + +* Large refactoring to how CurlMulti handles work. There is now a proxy that sits in front of a pool of CurlMulti + handles. This greatly simplifies the implementation, fixes a couple bugs, and provides a small performance boost. +* Exceptions are now properly grouped when sending requests in parallel +* Redirects are now properly aggregated when a multi transaction fails +* Redirects now set the response on the original object even in the event of a failure +* Bug fix: Model names are now properly set even when using $refs +* Added support for PHP 5.5's CurlFile to prevent warnings with the deprecated @ syntax +* Added support for oauth_callback in OAuth signatures +* Added support for oauth_verifier in OAuth signatures +* Added support to attempt to retrieve a command first literally, then ucfirst, the with inflection + +## 3.4.0 - 2013-04-11 + +* Bug fix: URLs are now resolved correctly based on http://tools.ietf.org/html/rfc3986#section-5.2. #289 +* Bug fix: Absolute URLs with a path in a service description will now properly override the base URL. #289 +* Bug fix: Parsing a query string with a single PHP array value will now result in an array. #263 +* Bug fix: Better normalization of the User-Agent header to prevent duplicate headers. #264. +* Bug fix: Added `number` type to service descriptions. +* Bug fix: empty parameters are removed from an OAuth signature +* Bug fix: Revalidating a cache entry prefers the Last-Modified over the Date header +* Bug fix: Fixed "array to string" error when validating a union of types in a service description +* Bug fix: Removed code that attempted to determine the size of a stream when data is written to the stream +* Bug fix: Not including an `oauth_token` if the value is null in the OauthPlugin. +* Bug fix: Now correctly aggregating successful requests and failed requests in CurlMulti when a redirect occurs. +* The new default CURLOPT_TIMEOUT setting has been increased to 150 seconds so that Guzzle works on poor connections. +* Added a feature to EntityEnclosingRequest::setBody() that will automatically set the Content-Type of the request if + the Content-Type can be determined based on the entity body or the path of the request. +* Added the ability to overwrite configuration settings in a client when grabbing a throwaway client from a builder. +* Added support for a PSR-3 LogAdapter. +* Added a `command.after_prepare` event +* Added `oauth_callback` parameter to the OauthPlugin +* Added the ability to create a custom stream class when using a stream factory +* Added a CachingEntityBody decorator +* Added support for `additionalParameters` in service descriptions to define how custom parameters are serialized. +* The bundled SSL certificate is now provided in the phar file and extracted when running Guzzle from a phar. +* You can now send any EntityEnclosingRequest with POST fields or POST files and cURL will handle creating bodies +* POST requests using a custom entity body are now treated exactly like PUT requests but with a custom cURL method. This + means that the redirect behavior of POST requests with custom bodies will not be the same as POST requests that use + POST fields or files (the latter is only used when emulating a form POST in the browser). +* Lots of cleanup to CurlHandle::factory and RequestFactory::createRequest + +## 3.3.1 - 2013-03-10 + +* Added the ability to create PHP streaming responses from HTTP requests +* Bug fix: Running any filters when parsing response headers with service descriptions +* Bug fix: OauthPlugin fixes to allow for multi-dimensional array signing, and sorting parameters before signing +* Bug fix: Removed the adding of default empty arrays and false Booleans to responses in order to be consistent across + response location visitors. +* Bug fix: Removed the possibility of creating configuration files with circular dependencies +* RequestFactory::create() now uses the key of a POST file when setting the POST file name +* Added xmlAllowEmpty to serialize an XML body even if no XML specific parameters are set + +## 3.3.0 - 2013-03-03 + +* A large number of performance optimizations have been made +* Bug fix: Added 'wb' as a valid write mode for streams +* Bug fix: `Guzzle\Http\Message\Response::json()` now allows scalar values to be returned +* Bug fix: Fixed bug in `Guzzle\Http\Message\Response` where wrapping quotes were stripped from `getEtag()` +* BC: Removed `Guzzle\Http\Utils` class +* BC: Setting a service description on a client will no longer modify the client's command factories. +* BC: Emitting IO events from a RequestMediator is now a parameter that must be set in a request's curl options using + the 'emit_io' key. This was previously set under a request's parameters using 'curl.emit_io' +* BC: `Guzzle\Stream\Stream::getWrapper()` and `Guzzle\Stream\Stream::getSteamType()` are no longer converted to + lowercase +* Operation parameter objects are now lazy loaded internally +* Added ErrorResponsePlugin that can throw errors for responses defined in service description operations' errorResponses +* Added support for instantiating responseType=class responseClass classes. Classes must implement + `Guzzle\Service\Command\ResponseClassInterface` +* Added support for additionalProperties for top-level parameters in responseType=model responseClasses. These + additional properties also support locations and can be used to parse JSON responses where the outermost part of the + JSON is an array +* Added support for nested renaming of JSON models (rename sentAs to name) +* CachePlugin + * Added support for stale-if-error so that the CachePlugin can now serve stale content from the cache on error + * Debug headers can now added to cached response in the CachePlugin + +## 3.2.0 - 2013-02-14 + +* CurlMulti is no longer reused globally. A new multi object is created per-client. This helps to isolate clients. +* URLs with no path no longer contain a "/" by default +* Guzzle\Http\QueryString does no longer manages the leading "?". This is now handled in Guzzle\Http\Url. +* BadResponseException no longer includes the full request and response message +* Adding setData() to Guzzle\Service\Description\ServiceDescriptionInterface +* Adding getResponseBody() to Guzzle\Http\Message\RequestInterface +* Various updates to classes to use ServiceDescriptionInterface type hints rather than ServiceDescription +* Header values can now be normalized into distinct values when multiple headers are combined with a comma separated list +* xmlEncoding can now be customized for the XML declaration of a XML service description operation +* Guzzle\Http\QueryString now uses Guzzle\Http\QueryAggregator\QueryAggregatorInterface objects to add custom value + aggregation and no longer uses callbacks +* The URL encoding implementation of Guzzle\Http\QueryString can now be customized +* Bug fix: Filters were not always invoked for array service description parameters +* Bug fix: Redirects now use a target response body rather than a temporary response body +* Bug fix: The default exponential backoff BackoffPlugin was not giving when the request threshold was exceeded +* Bug fix: Guzzle now takes the first found value when grabbing Cache-Control directives + +## 3.1.2 - 2013-01-27 + +* Refactored how operation responses are parsed. Visitors now include a before() method responsible for parsing the + response body. For example, the XmlVisitor now parses the XML response into an array in the before() method. +* Fixed an issue where cURL would not automatically decompress responses when the Accept-Encoding header was sent +* CURLOPT_SSL_VERIFYHOST is never set to 1 because it is deprecated (see 5e0ff2ef20f839e19d1eeb298f90ba3598784444) +* Fixed a bug where redirect responses were not chained correctly using getPreviousResponse() +* Setting default headers on a client after setting the user-agent will not erase the user-agent setting + +## 3.1.1 - 2013-01-20 + +* Adding wildcard support to Guzzle\Common\Collection::getPath() +* Adding alias support to ServiceBuilder configs +* Adding Guzzle\Service\Resource\CompositeResourceIteratorFactory and cleaning up factory interface + +## 3.1.0 - 2013-01-12 + +* BC: CurlException now extends from RequestException rather than BadResponseException +* BC: Renamed Guzzle\Plugin\Cache\CanCacheStrategyInterface::canCache() to canCacheRequest() and added CanCacheResponse() +* Added getData to ServiceDescriptionInterface +* Added context array to RequestInterface::setState() +* Bug: Removing hard dependency on the BackoffPlugin from Guzzle\Http +* Bug: Adding required content-type when JSON request visitor adds JSON to a command +* Bug: Fixing the serialization of a service description with custom data +* Made it easier to deal with exceptions thrown when transferring commands or requests in parallel by providing + an array of successful and failed responses +* Moved getPath from Guzzle\Service\Resource\Model to Guzzle\Common\Collection +* Added Guzzle\Http\IoEmittingEntityBody +* Moved command filtration from validators to location visitors +* Added `extends` attributes to service description parameters +* Added getModels to ServiceDescriptionInterface + +## 3.0.7 - 2012-12-19 + +* Fixing phar detection when forcing a cacert to system if null or true +* Allowing filename to be passed to `Guzzle\Http\Message\Request::setResponseBody()` +* Cleaning up `Guzzle\Common\Collection::inject` method +* Adding a response_body location to service descriptions + +## 3.0.6 - 2012-12-09 + +* CurlMulti performance improvements +* Adding setErrorResponses() to Operation +* composer.json tweaks + +## 3.0.5 - 2012-11-18 + +* Bug: Fixing an infinite recursion bug caused from revalidating with the CachePlugin +* Bug: Response body can now be a string containing "0" +* Bug: Using Guzzle inside of a phar uses system by default but now allows for a custom cacert +* Bug: QueryString::fromString now properly parses query string parameters that contain equal signs +* Added support for XML attributes in service description responses +* DefaultRequestSerializer now supports array URI parameter values for URI template expansion +* Added better mimetype guessing to requests and post files + +## 3.0.4 - 2012-11-11 + +* Bug: Fixed a bug when adding multiple cookies to a request to use the correct glue value +* Bug: Cookies can now be added that have a name, domain, or value set to "0" +* Bug: Using the system cacert bundle when using the Phar +* Added json and xml methods to Response to make it easier to parse JSON and XML response data into data structures +* Enhanced cookie jar de-duplication +* Added the ability to enable strict cookie jars that throw exceptions when invalid cookies are added +* Added setStream to StreamInterface to actually make it possible to implement custom rewind behavior for entity bodies +* Added the ability to create any sort of hash for a stream rather than just an MD5 hash + +## 3.0.3 - 2012-11-04 + +* Implementing redirects in PHP rather than cURL +* Added PECL URI template extension and using as default parser if available +* Bug: Fixed Content-Length parsing of Response factory +* Adding rewind() method to entity bodies and streams. Allows for custom rewinding of non-repeatable streams. +* Adding ToArrayInterface throughout library +* Fixing OauthPlugin to create unique nonce values per request + +## 3.0.2 - 2012-10-25 + +* Magic methods are enabled by default on clients +* Magic methods return the result of a command +* Service clients no longer require a base_url option in the factory +* Bug: Fixed an issue with URI templates where null template variables were being expanded + +## 3.0.1 - 2012-10-22 + +* Models can now be used like regular collection objects by calling filter, map, etc. +* Models no longer require a Parameter structure or initial data in the constructor +* Added a custom AppendIterator to get around a PHP bug with the `\AppendIterator` + +## 3.0.0 - 2012-10-15 + +* Rewrote service description format to be based on Swagger + * Now based on JSON schema + * Added nested input structures and nested response models + * Support for JSON and XML input and output models + * Renamed `commands` to `operations` + * Removed dot class notation + * Removed custom types +* Broke the project into smaller top-level namespaces to be more component friendly +* Removed support for XML configs and descriptions. Use arrays or JSON files. +* Removed the Validation component and Inspector +* Moved all cookie code to Guzzle\Plugin\Cookie +* Magic methods on a Guzzle\Service\Client now return the command un-executed. +* Calling getResult() or getResponse() on a command will lazily execute the command if needed. +* Now shipping with cURL's CA certs and using it by default +* Added previousResponse() method to response objects +* No longer sending Accept and Accept-Encoding headers on every request +* Only sending an Expect header by default when a payload is greater than 1MB +* Added/moved client options: + * curl.blacklist to curl.option.blacklist + * Added ssl.certificate_authority +* Added a Guzzle\Iterator component +* Moved plugins from Guzzle\Http\Plugin to Guzzle\Plugin +* Added a more robust backoff retry strategy (replaced the ExponentialBackoffPlugin) +* Added a more robust caching plugin +* Added setBody to response objects +* Updating LogPlugin to use a more flexible MessageFormatter +* Added a completely revamped build process +* Cleaning up Collection class and removing default values from the get method +* Fixed ZF2 cache adapters + +## 2.8.8 - 2012-10-15 + +* Bug: Fixed a cookie issue that caused dot prefixed domains to not match where popular browsers did + +## 2.8.7 - 2012-09-30 + +* Bug: Fixed config file aliases for JSON includes +* Bug: Fixed cookie bug on a request object by using CookieParser to parse cookies on requests +* Bug: Removing the path to a file when sending a Content-Disposition header on a POST upload +* Bug: Hardening request and response parsing to account for missing parts +* Bug: Fixed PEAR packaging +* Bug: Fixed Request::getInfo +* Bug: Fixed cases where CURLM_CALL_MULTI_PERFORM return codes were causing curl transactions to fail +* Adding the ability for the namespace Iterator factory to look in multiple directories +* Added more getters/setters/removers from service descriptions +* Added the ability to remove POST fields from OAuth signatures +* OAuth plugin now supports 2-legged OAuth + +## 2.8.6 - 2012-09-05 + +* Added the ability to modify and build service descriptions +* Added the use of visitors to apply parameters to locations in service descriptions using the dynamic command +* Added a `json` parameter location +* Now allowing dot notation for classes in the CacheAdapterFactory +* Using the union of two arrays rather than an array_merge when extending service builder services and service params +* Ensuring that a service is a string before doing strpos() checks on it when substituting services for references + in service builder config files. +* Services defined in two different config files that include one another will by default replace the previously + defined service, but you can now create services that extend themselves and merge their settings over the previous +* The JsonLoader now supports aliasing filenames with different filenames. This allows you to alias something like + '_default' with a default JSON configuration file. + +## 2.8.5 - 2012-08-29 + +* Bug: Suppressed empty arrays from URI templates +* Bug: Added the missing $options argument from ServiceDescription::factory to enable caching +* Added support for HTTP responses that do not contain a reason phrase in the start-line +* AbstractCommand commands are now invokable +* Added a way to get the data used when signing an Oauth request before a request is sent + +## 2.8.4 - 2012-08-15 + +* Bug: Custom delay time calculations are no longer ignored in the ExponentialBackoffPlugin +* Added the ability to transfer entity bodies as a string rather than streamed. This gets around curl error 65. Set `body_as_string` in a request's curl options to enable. +* Added a StreamInterface, EntityBodyInterface, and added ftell() to Guzzle\Common\Stream +* Added an AbstractEntityBodyDecorator and a ReadLimitEntityBody decorator to transfer only a subset of a decorated stream +* Stream and EntityBody objects will now return the file position to the previous position after a read required operation (e.g. getContentMd5()) +* Added additional response status codes +* Removed SSL information from the default User-Agent header +* DELETE requests can now send an entity body +* Added an EventDispatcher to the ExponentialBackoffPlugin and added an ExponentialBackoffLogger to log backoff retries +* Added the ability of the MockPlugin to consume mocked request bodies +* LogPlugin now exposes request and response objects in the extras array + +## 2.8.3 - 2012-07-30 + +* Bug: Fixed a case where empty POST requests were sent as GET requests +* Bug: Fixed a bug in ExponentialBackoffPlugin that caused fatal errors when retrying an EntityEnclosingRequest that does not have a body +* Bug: Setting the response body of a request to null after completing a request, not when setting the state of a request to new +* Added multiple inheritance to service description commands +* Added an ApiCommandInterface and added `getParamNames()` and `hasParam()` +* Removed the default 2mb size cutoff from the Md5ValidatorPlugin so that it now defaults to validating everything +* Changed CurlMulti::perform to pass a smaller timeout to CurlMulti::executeHandles + +## 2.8.2 - 2012-07-24 + +* Bug: Query string values set to 0 are no longer dropped from the query string +* Bug: A Collection object is no longer created each time a call is made to `Guzzle\Service\Command\AbstractCommand::getRequestHeaders()` +* Bug: `+` is now treated as an encoded space when parsing query strings +* QueryString and Collection performance improvements +* Allowing dot notation for class paths in filters attribute of a service descriptions + +## 2.8.1 - 2012-07-16 + +* Loosening Event Dispatcher dependency +* POST redirects can now be customized using CURLOPT_POSTREDIR + +## 2.8.0 - 2012-07-15 + +* BC: Guzzle\Http\Query + * Query strings with empty variables will always show an equal sign unless the variable is set to QueryString::BLANK (e.g. ?acl= vs ?acl) + * Changed isEncodingValues() and isEncodingFields() to isUrlEncoding() + * Changed setEncodeValues(bool) and setEncodeFields(bool) to useUrlEncoding(bool) + * Changed the aggregation functions of QueryString to be static methods + * Can now use fromString() with querystrings that have a leading ? +* cURL configuration values can be specified in service descriptions using `curl.` prefixed parameters +* Content-Length is set to 0 before emitting the request.before_send event when sending an empty request body +* Cookies are no longer URL decoded by default +* Bug: URI template variables set to null are no longer expanded + +## 2.7.2 - 2012-07-02 + +* BC: Moving things to get ready for subtree splits. Moving Inflection into Common. Moving Guzzle\Http\Parser to Guzzle\Parser. +* BC: Removing Guzzle\Common\Batch\Batch::count() and replacing it with isEmpty() +* CachePlugin now allows for a custom request parameter function to check if a request can be cached +* Bug fix: CachePlugin now only caches GET and HEAD requests by default +* Bug fix: Using header glue when transferring headers over the wire +* Allowing deeply nested arrays for composite variables in URI templates +* Batch divisors can now return iterators or arrays + +## 2.7.1 - 2012-06-26 + +* Minor patch to update version number in UA string +* Updating build process + +## 2.7.0 - 2012-06-25 + +* BC: Inflection classes moved to Guzzle\Inflection. No longer static methods. Can now inject custom inflectors into classes. +* BC: Removed magic setX methods from commands +* BC: Magic methods mapped to service description commands are now inflected in the command factory rather than the client __call() method +* Verbose cURL options are no longer enabled by default. Set curl.debug to true on a client to enable. +* Bug: Now allowing colons in a response start-line (e.g. HTTP/1.1 503 Service Unavailable: Back-end server is at capacity) +* Guzzle\Service\Resource\ResourceIteratorApplyBatched now internally uses the Guzzle\Common\Batch namespace +* Added Guzzle\Service\Plugin namespace and a PluginCollectionPlugin +* Added the ability to set POST fields and files in a service description +* Guzzle\Http\EntityBody::factory() now accepts objects with a __toString() method +* Adding a command.before_prepare event to clients +* Added BatchClosureTransfer and BatchClosureDivisor +* BatchTransferException now includes references to the batch divisor and transfer strategies +* Fixed some tests so that they pass more reliably +* Added Guzzle\Common\Log\ArrayLogAdapter + +## 2.6.6 - 2012-06-10 + +* BC: Removing Guzzle\Http\Plugin\BatchQueuePlugin +* BC: Removing Guzzle\Service\Command\CommandSet +* Adding generic batching system (replaces the batch queue plugin and command set) +* Updating ZF cache and log adapters and now using ZF's composer repository +* Bug: Setting the name of each ApiParam when creating through an ApiCommand +* Adding result_type, result_doc, deprecated, and doc_url to service descriptions +* Bug: Changed the default cookie header casing back to 'Cookie' + +## 2.6.5 - 2012-06-03 + +* BC: Renaming Guzzle\Http\Message\RequestInterface::getResourceUri() to getResource() +* BC: Removing unused AUTH_BASIC and AUTH_DIGEST constants from +* BC: Guzzle\Http\Cookie is now used to manage Set-Cookie data, not Cookie data +* BC: Renaming methods in the CookieJarInterface +* Moving almost all cookie logic out of the CookiePlugin and into the Cookie or CookieJar implementations +* Making the default glue for HTTP headers ';' instead of ',' +* Adding a removeValue to Guzzle\Http\Message\Header +* Adding getCookies() to request interface. +* Making it easier to add event subscribers to HasDispatcherInterface classes. Can now directly call addSubscriber() + +## 2.6.4 - 2012-05-30 + +* BC: Cleaning up how POST files are stored in EntityEnclosingRequest objects. Adding PostFile class. +* BC: Moving ApiCommand specific functionality from the Inspector and on to the ApiCommand +* Bug: Fixing magic method command calls on clients +* Bug: Email constraint only validates strings +* Bug: Aggregate POST fields when POST files are present in curl handle +* Bug: Fixing default User-Agent header +* Bug: Only appending or prepending parameters in commands if they are specified +* Bug: Not requiring response reason phrases or status codes to match a predefined list of codes +* Allowing the use of dot notation for class namespaces when using instance_of constraint +* Added any_match validation constraint +* Added an AsyncPlugin +* Passing request object to the calculateWait method of the ExponentialBackoffPlugin +* Allowing the result of a command object to be changed +* Parsing location and type sub values when instantiating a service description rather than over and over at runtime + +## 2.6.3 - 2012-05-23 + +* [BC] Guzzle\Common\FromConfigInterface no longer requires any config options. +* [BC] Refactoring how POST files are stored on an EntityEnclosingRequest. They are now separate from POST fields. +* You can now use an array of data when creating PUT request bodies in the request factory. +* Removing the requirement that HTTPS requests needed a Cache-Control: public directive to be cacheable. +* [Http] Adding support for Content-Type in multipart POST uploads per upload +* [Http] Added support for uploading multiple files using the same name (foo[0], foo[1]) +* Adding more POST data operations for easier manipulation of POST data. +* You can now set empty POST fields. +* The body of a request is only shown on EntityEnclosingRequest objects that do not use POST files. +* Split the Guzzle\Service\Inspector::validateConfig method into two methods. One to initialize when a command is created, and one to validate. +* CS updates + +## 2.6.2 - 2012-05-19 + +* [Http] Better handling of nested scope requests in CurlMulti. Requests are now always prepares in the send() method rather than the addRequest() method. + +## 2.6.1 - 2012-05-19 + +* [BC] Removing 'path' support in service descriptions. Use 'uri'. +* [BC] Guzzle\Service\Inspector::parseDocBlock is now protected. Adding getApiParamsForClass() with cache. +* [BC] Removing Guzzle\Common\NullObject. Use https://github.com/mtdowling/NullObject if you need it. +* [BC] Removing Guzzle\Common\XmlElement. +* All commands, both dynamic and concrete, have ApiCommand objects. +* Adding a fix for CurlMulti so that if all of the connections encounter some sort of curl error, then the loop exits. +* Adding checks to EntityEnclosingRequest so that empty POST files and fields are ignored. +* Making the method signature of Guzzle\Service\Builder\ServiceBuilder::factory more flexible. + +## 2.6.0 - 2012-05-15 + +* [BC] Moving Guzzle\Service\Builder to Guzzle\Service\Builder\ServiceBuilder +* [BC] Executing a Command returns the result of the command rather than the command +* [BC] Moving all HTTP parsing logic to Guzzle\Http\Parsers. Allows for faster C implementations if needed. +* [BC] Changing the Guzzle\Http\Message\Response::setProtocol() method to accept a protocol and version in separate args. +* [BC] Moving ResourceIterator* to Guzzle\Service\Resource +* [BC] Completely refactored ResourceIterators to iterate over a cloned command object +* [BC] Moved Guzzle\Http\UriTemplate to Guzzle\Http\Parser\UriTemplate\UriTemplate +* [BC] Guzzle\Guzzle is now deprecated +* Moving Guzzle\Common\Guzzle::inject to Guzzle\Common\Collection::inject +* Adding Guzzle\Version class to give version information about Guzzle +* Adding Guzzle\Http\Utils class to provide getDefaultUserAgent() and getHttpDate() +* Adding Guzzle\Curl\CurlVersion to manage caching curl_version() data +* ServiceDescription and ServiceBuilder are now cacheable using similar configs +* Changing the format of XML and JSON service builder configs. Backwards compatible. +* Cleaned up Cookie parsing +* Trimming the default Guzzle User-Agent header +* Adding a setOnComplete() method to Commands that is called when a command completes +* Keeping track of requests that were mocked in the MockPlugin +* Fixed a caching bug in the CacheAdapterFactory +* Inspector objects can be injected into a Command object +* Refactoring a lot of code and tests to be case insensitive when dealing with headers +* Adding Guzzle\Http\Message\HeaderComparison for easy comparison of HTTP headers using a DSL +* Adding the ability to set global option overrides to service builder configs +* Adding the ability to include other service builder config files from within XML and JSON files +* Moving the parseQuery method out of Url and on to QueryString::fromString() as a static factory method. + +## 2.5.0 - 2012-05-08 + +* Major performance improvements +* [BC] Simplifying Guzzle\Common\Collection. Please check to see if you are using features that are now deprecated. +* [BC] Using a custom validation system that allows a flyweight implementation for much faster validation. No longer using Symfony2 Validation component. +* [BC] No longer supporting "{{ }}" for injecting into command or UriTemplates. Use "{}" +* Added the ability to passed parameters to all requests created by a client +* Added callback functionality to the ExponentialBackoffPlugin +* Using microtime in ExponentialBackoffPlugin to allow more granular backoff strategies. +* Rewinding request stream bodies when retrying requests +* Exception is thrown when JSON response body cannot be decoded +* Added configurable magic method calls to clients and commands. This is off by default. +* Fixed a defect that added a hash to every parsed URL part +* Fixed duplicate none generation for OauthPlugin. +* Emitting an event each time a client is generated by a ServiceBuilder +* Using an ApiParams object instead of a Collection for parameters of an ApiCommand +* cache.* request parameters should be renamed to params.cache.* +* Added the ability to set arbitrary curl options on requests (disable_wire, progress, etc.). See CurlHandle. +* Added the ability to disable type validation of service descriptions +* ServiceDescriptions and ServiceBuilders are now Serializable diff --git a/vendor/guzzlehttp/guzzle/LICENSE b/vendor/guzzlehttp/guzzle/LICENSE new file mode 100644 index 00000000000..50a177b0320 --- /dev/null +++ b/vendor/guzzlehttp/guzzle/LICENSE @@ -0,0 +1,19 @@ +Copyright (c) 2011-2018 Michael Dowling, https://github.com/mtdowling <mtdowling@gmail.com> + +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/guzzlehttp/guzzle/README.md b/vendor/guzzlehttp/guzzle/README.md new file mode 100644 index 00000000000..bcd18b8e718 --- /dev/null +++ b/vendor/guzzlehttp/guzzle/README.md @@ -0,0 +1,91 @@ +Guzzle, PHP HTTP client +======================= + +[](https://github.com/guzzle/guzzle/releases) +[](https://travis-ci.org/guzzle/guzzle) +[](https://packagist.org/packages/guzzlehttp/guzzle) + +Guzzle is a PHP HTTP client that makes it easy to send HTTP requests and +trivial to integrate with web services. + +- Simple interface for building query strings, POST requests, streaming large + uploads, streaming large downloads, using HTTP cookies, uploading JSON data, + etc... +- Can send both synchronous and asynchronous requests using the same interface. +- Uses PSR-7 interfaces for requests, responses, and streams. This allows you + to utilize other PSR-7 compatible libraries with Guzzle. +- Abstracts away the underlying HTTP transport, allowing you to write + environment and transport agnostic code; i.e., no hard dependency on cURL, + PHP streams, sockets, or non-blocking event loops. +- Middleware system allows you to augment and compose client behavior. + +```php +$client = new \GuzzleHttp\Client(); +$res = $client->request('GET', 'https://api.github.com/repos/guzzle/guzzle'); +echo $res->getStatusCode(); +// 200 +echo $res->getHeaderLine('content-type'); +// 'application/json; charset=utf8' +echo $res->getBody(); +// '{"id": 1420053, "name": "guzzle", ...}' + +// Send an asynchronous request. +$request = new \GuzzleHttp\Psr7\Request('GET', 'http://httpbin.org'); +$promise = $client->sendAsync($request)->then(function ($response) { + echo 'I completed! ' . $response->getBody(); +}); +$promise->wait(); +``` + +## Help and docs + +- [Documentation](http://guzzlephp.org/) +- [Stack Overflow](http://stackoverflow.com/questions/tagged/guzzle) +- [Gitter](https://gitter.im/guzzle/guzzle) + + +## Installing Guzzle + +The recommended way to install Guzzle is through +[Composer](http://getcomposer.org). + +```bash +# Install Composer +curl -sS https://getcomposer.org/installer | php +``` + +Next, run the Composer command to install the latest stable version of Guzzle: + +```bash +php composer.phar require guzzlehttp/guzzle +``` + +After installing, you need to require Composer's autoloader: + +```php +require 'vendor/autoload.php'; +``` + +You can then later update Guzzle using composer: + + ```bash +composer.phar update + ``` + + +## Version Guidance + +| Version | Status | Packagist | Namespace | Repo | Docs | PSR-7 | PHP Version | +|---------|------------|---------------------|--------------|---------------------|---------------------|-------|-------------| +| 3.x | EOL | `guzzle/guzzle` | `Guzzle` | [v3][guzzle-3-repo] | [v3][guzzle-3-docs] | No | >= 5.3.3 | +| 4.x | EOL | `guzzlehttp/guzzle` | `GuzzleHttp` | [v4][guzzle-4-repo] | N/A | No | >= 5.4 | +| 5.x | Maintained | `guzzlehttp/guzzle` | `GuzzleHttp` | [v5][guzzle-5-repo] | [v5][guzzle-5-docs] | No | >= 5.4 | +| 6.x | Latest | `guzzlehttp/guzzle` | `GuzzleHttp` | [v6][guzzle-6-repo] | [v6][guzzle-6-docs] | Yes | >= 5.5 | + +[guzzle-3-repo]: https://github.com/guzzle/guzzle3 +[guzzle-4-repo]: https://github.com/guzzle/guzzle/tree/4.x +[guzzle-5-repo]: https://github.com/guzzle/guzzle/tree/5.3 +[guzzle-6-repo]: https://github.com/guzzle/guzzle +[guzzle-3-docs]: http://guzzle3.readthedocs.org/en/latest/ +[guzzle-5-docs]: http://guzzle.readthedocs.org/en/5.3/ +[guzzle-6-docs]: http://guzzle.readthedocs.org/en/latest/ diff --git a/vendor/guzzlehttp/guzzle/UPGRADING.md b/vendor/guzzlehttp/guzzle/UPGRADING.md new file mode 100644 index 00000000000..91d1dcc9931 --- /dev/null +++ b/vendor/guzzlehttp/guzzle/UPGRADING.md @@ -0,0 +1,1203 @@ +Guzzle Upgrade Guide +==================== + +5.0 to 6.0 +---------- + +Guzzle now uses [PSR-7](http://www.php-fig.org/psr/psr-7/) for HTTP messages. +Due to the fact that these messages are immutable, this prompted a refactoring +of Guzzle to use a middleware based system rather than an event system. Any +HTTP message interaction (e.g., `GuzzleHttp\Message\Request`) need to be +updated to work with the new immutable PSR-7 request and response objects. Any +event listeners or subscribers need to be updated to become middleware +functions that wrap handlers (or are injected into a +`GuzzleHttp\HandlerStack`). + +- Removed `GuzzleHttp\BatchResults` +- Removed `GuzzleHttp\Collection` +- Removed `GuzzleHttp\HasDataTrait` +- Removed `GuzzleHttp\ToArrayInterface` +- The `guzzlehttp/streams` dependency has been removed. Stream functionality + is now present in the `GuzzleHttp\Psr7` namespace provided by the + `guzzlehttp/psr7` package. +- Guzzle no longer uses ReactPHP promises and now uses the + `guzzlehttp/promises` library. We use a custom promise library for three + significant reasons: + 1. React promises (at the time of writing this) are recursive. Promise + chaining and promise resolution will eventually blow the stack. Guzzle + promises are not recursive as they use a sort of trampolining technique. + Note: there has been movement in the React project to modify promises to + no longer utilize recursion. + 2. Guzzle needs to have the ability to synchronously block on a promise to + wait for a result. Guzzle promises allows this functionality (and does + not require the use of recursion). + 3. Because we need to be able to wait on a result, doing so using React + promises requires wrapping react promises with RingPHP futures. This + overhead is no longer needed, reducing stack sizes, reducing complexity, + and improving performance. +- `GuzzleHttp\Mimetypes` has been moved to a function in + `GuzzleHttp\Psr7\mimetype_from_extension` and + `GuzzleHttp\Psr7\mimetype_from_filename`. +- `GuzzleHttp\Query` and `GuzzleHttp\QueryParser` have been removed. Query + strings must now be passed into request objects as strings, or provided to + the `query` request option when creating requests with clients. The `query` + option uses PHP's `http_build_query` to convert an array to a string. If you + need a different serialization technique, you will need to pass the query + string in as a string. There are a couple helper functions that will make + working with query strings easier: `GuzzleHttp\Psr7\parse_query` and + `GuzzleHttp\Psr7\build_query`. +- Guzzle no longer has a dependency on RingPHP. Due to the use of a middleware + system based on PSR-7, using RingPHP and it's middleware system as well adds + more complexity than the benefits it provides. All HTTP handlers that were + present in RingPHP have been modified to work directly with PSR-7 messages + and placed in the `GuzzleHttp\Handler` namespace. This significantly reduces + complexity in Guzzle, removes a dependency, and improves performance. RingPHP + will be maintained for Guzzle 5 support, but will no longer be a part of + Guzzle 6. +- As Guzzle now uses a middleware based systems the event system and RingPHP + integration has been removed. Note: while the event system has been removed, + it is possible to add your own type of event system that is powered by the + middleware system. + - Removed the `Event` namespace. + - Removed the `Subscriber` namespace. + - Removed `Transaction` class + - Removed `RequestFsm` + - Removed `RingBridge` + - `GuzzleHttp\Subscriber\Cookie` is now provided by + `GuzzleHttp\Middleware::cookies` + - `GuzzleHttp\Subscriber\HttpError` is now provided by + `GuzzleHttp\Middleware::httpError` + - `GuzzleHttp\Subscriber\History` is now provided by + `GuzzleHttp\Middleware::history` + - `GuzzleHttp\Subscriber\Mock` is now provided by + `GuzzleHttp\Handler\MockHandler` + - `GuzzleHttp\Subscriber\Prepare` is now provided by + `GuzzleHttp\PrepareBodyMiddleware` + - `GuzzleHttp\Subscriber\Redirect` is now provided by + `GuzzleHttp\RedirectMiddleware` +- Guzzle now uses `Psr\Http\Message\UriInterface` (implements in + `GuzzleHttp\Psr7\Uri`) for URI support. `GuzzleHttp\Url` is now gone. +- Static functions in `GuzzleHttp\Utils` have been moved to namespaced + functions under the `GuzzleHttp` namespace. This requires either a Composer + based autoloader or you to include functions.php. +- `GuzzleHttp\ClientInterface::getDefaultOption` has been renamed to + `GuzzleHttp\ClientInterface::getConfig`. +- `GuzzleHttp\ClientInterface::setDefaultOption` has been removed. +- The `json` and `xml` methods of response objects has been removed. With the + migration to strictly adhering to PSR-7 as the interface for Guzzle messages, + adding methods to message interfaces would actually require Guzzle messages + to extend from PSR-7 messages rather then work with them directly. + +## Migrating to middleware + +The change to PSR-7 unfortunately required significant refactoring to Guzzle +due to the fact that PSR-7 messages are immutable. Guzzle 5 relied on an event +system from plugins. The event system relied on mutability of HTTP messages and +side effects in order to work. With immutable messages, you have to change your +workflow to become more about either returning a value (e.g., functional +middlewares) or setting a value on an object. Guzzle v6 has chosen the +functional middleware approach. + +Instead of using the event system to listen for things like the `before` event, +you now create a stack based middleware function that intercepts a request on +the way in and the promise of the response on the way out. This is a much +simpler and more predictable approach than the event system and works nicely +with PSR-7 middleware. Due to the use of promises, the middleware system is +also asynchronous. + +v5: + +```php +use GuzzleHttp\Event\BeforeEvent; +$client = new GuzzleHttp\Client(); +// Get the emitter and listen to the before event. +$client->getEmitter()->on('before', function (BeforeEvent $e) { + // Guzzle v5 events relied on mutation + $e->getRequest()->setHeader('X-Foo', 'Bar'); +}); +``` + +v6: + +In v6, you can modify the request before it is sent using the `mapRequest` +middleware. The idiomatic way in v6 to modify the request/response lifecycle is +to setup a handler middleware stack up front and inject the handler into a +client. + +```php +use GuzzleHttp\Middleware; +// Create a handler stack that has all of the default middlewares attached +$handler = GuzzleHttp\HandlerStack::create(); +// Push the handler onto the handler stack +$handler->push(Middleware::mapRequest(function (RequestInterface $request) { + // Notice that we have to return a request object + return $request->withHeader('X-Foo', 'Bar'); +})); +// Inject the handler into the client +$client = new GuzzleHttp\Client(['handler' => $handler]); +``` + +## POST Requests + +This version added the [`form_params`](http://guzzle.readthedocs.org/en/latest/request-options.html#form_params) +and `multipart` request options. `form_params` is an associative array of +strings or array of strings and is used to serialize an +`application/x-www-form-urlencoded` POST request. The +[`multipart`](http://guzzle.readthedocs.org/en/latest/request-options.html#multipart) +option is now used to send a multipart/form-data POST request. + +`GuzzleHttp\Post\PostFile` has been removed. Use the `multipart` option to add +POST files to a multipart/form-data request. + +The `body` option no longer accepts an array to send POST requests. Please use +`multipart` or `form_params` instead. + +The `base_url` option has been renamed to `base_uri`. + +4.x to 5.0 +---------- + +## Rewritten Adapter Layer + +Guzzle now uses [RingPHP](http://ringphp.readthedocs.org/en/latest) to send +HTTP requests. The `adapter` option in a `GuzzleHttp\Client` constructor +is still supported, but it has now been renamed to `handler`. Instead of +passing a `GuzzleHttp\Adapter\AdapterInterface`, you must now pass a PHP +`callable` that follows the RingPHP specification. + +## Removed Fluent Interfaces + +[Fluent interfaces were removed](http://ocramius.github.io/blog/fluent-interfaces-are-evil) +from the following classes: + +- `GuzzleHttp\Collection` +- `GuzzleHttp\Url` +- `GuzzleHttp\Query` +- `GuzzleHttp\Post\PostBody` +- `GuzzleHttp\Cookie\SetCookie` + +## Removed functions.php + +Removed "functions.php", so that Guzzle is truly PSR-4 compliant. The following +functions can be used as replacements. + +- `GuzzleHttp\json_decode` -> `GuzzleHttp\Utils::jsonDecode` +- `GuzzleHttp\get_path` -> `GuzzleHttp\Utils::getPath` +- `GuzzleHttp\Utils::setPath` -> `GuzzleHttp\set_path` +- `GuzzleHttp\Pool::batch` -> `GuzzleHttp\batch`. This function is, however, + deprecated in favor of using `GuzzleHttp\Pool::batch()`. + +The "procedural" global client has been removed with no replacement (e.g., +`GuzzleHttp\get()`, `GuzzleHttp\post()`, etc.). Use a `GuzzleHttp\Client` +object as a replacement. + +## `throwImmediately` has been removed + +The concept of "throwImmediately" has been removed from exceptions and error +events. This control mechanism was used to stop a transfer of concurrent +requests from completing. This can now be handled by throwing the exception or +by cancelling a pool of requests or each outstanding future request +individually. + +## headers event has been removed + +Removed the "headers" event. This event was only useful for changing the +body a response once the headers of the response were known. You can implement +a similar behavior in a number of ways. One example might be to use a +FnStream that has access to the transaction being sent. For example, when the +first byte is written, you could check if the response headers match your +expectations, and if so, change the actual stream body that is being +written to. + +## Updates to HTTP Messages + +Removed the `asArray` parameter from +`GuzzleHttp\Message\MessageInterface::getHeader`. If you want to get a header +value as an array, then use the newly added `getHeaderAsArray()` method of +`MessageInterface`. This change makes the Guzzle interfaces compatible with +the PSR-7 interfaces. + +3.x to 4.0 +---------- + +## Overarching changes: + +- Now requires PHP 5.4 or greater. +- No longer requires cURL to send requests. +- Guzzle no longer wraps every exception it throws. Only exceptions that are + recoverable are now wrapped by Guzzle. +- Various namespaces have been removed or renamed. +- No longer requiring the Symfony EventDispatcher. A custom event dispatcher + based on the Symfony EventDispatcher is + now utilized in `GuzzleHttp\Event\EmitterInterface` (resulting in significant + speed and functionality improvements). + +Changes per Guzzle 3.x namespace are described below. + +## Batch + +The `Guzzle\Batch` namespace has been removed. This is best left to +third-parties to implement on top of Guzzle's core HTTP library. + +## Cache + +The `Guzzle\Cache` namespace has been removed. (Todo: No suitable replacement +has been implemented yet, but hoping to utilize a PSR cache interface). + +## Common + +- Removed all of the wrapped exceptions. It's better to use the standard PHP + library for unrecoverable exceptions. +- `FromConfigInterface` has been removed. +- `Guzzle\Common\Version` has been removed. The VERSION constant can be found + at `GuzzleHttp\ClientInterface::VERSION`. + +### Collection + +- `getAll` has been removed. Use `toArray` to convert a collection to an array. +- `inject` has been removed. +- `keySearch` has been removed. +- `getPath` no longer supports wildcard expressions. Use something better like + JMESPath for this. +- `setPath` now supports appending to an existing array via the `[]` notation. + +### Events + +Guzzle no longer requires Symfony's EventDispatcher component. Guzzle now uses +`GuzzleHttp\Event\Emitter`. + +- `Symfony\Component\EventDispatcher\EventDispatcherInterface` is replaced by + `GuzzleHttp\Event\EmitterInterface`. +- `Symfony\Component\EventDispatcher\EventDispatcher` is replaced by + `GuzzleHttp\Event\Emitter`. +- `Symfony\Component\EventDispatcher\Event` is replaced by + `GuzzleHttp\Event\Event`, and Guzzle now has an EventInterface in + `GuzzleHttp\Event\EventInterface`. +- `AbstractHasDispatcher` has moved to a trait, `HasEmitterTrait`, and + `HasDispatcherInterface` has moved to `HasEmitterInterface`. Retrieving the + event emitter of a request, client, etc. now uses the `getEmitter` method + rather than the `getDispatcher` method. + +#### Emitter + +- Use the `once()` method to add a listener that automatically removes itself + the first time it is invoked. +- Use the `listeners()` method to retrieve a list of event listeners rather than + the `getListeners()` method. +- Use `emit()` instead of `dispatch()` to emit an event from an emitter. +- Use `attach()` instead of `addSubscriber()` and `detach()` instead of + `removeSubscriber()`. + +```php +$mock = new Mock(); +// 3.x +$request->getEventDispatcher()->addSubscriber($mock); +$request->getEventDispatcher()->removeSubscriber($mock); +// 4.x +$request->getEmitter()->attach($mock); +$request->getEmitter()->detach($mock); +``` + +Use the `on()` method to add a listener rather than the `addListener()` method. + +```php +// 3.x +$request->getEventDispatcher()->addListener('foo', function (Event $event) { /* ... */ } ); +// 4.x +$request->getEmitter()->on('foo', function (Event $event, $name) { /* ... */ } ); +``` + +## Http + +### General changes + +- The cacert.pem certificate has been moved to `src/cacert.pem`. +- Added the concept of adapters that are used to transfer requests over the + wire. +- Simplified the event system. +- Sending requests in parallel is still possible, but batching is no longer a + concept of the HTTP layer. Instead, you must use the `complete` and `error` + events to asynchronously manage parallel request transfers. +- `Guzzle\Http\Url` has moved to `GuzzleHttp\Url`. +- `Guzzle\Http\QueryString` has moved to `GuzzleHttp\Query`. +- QueryAggregators have been rewritten so that they are simply callable + functions. +- `GuzzleHttp\StaticClient` has been removed. Use the functions provided in + `functions.php` for an easy to use static client instance. +- Exceptions in `GuzzleHttp\Exception` have been updated to all extend from + `GuzzleHttp\Exception\TransferException`. + +### Client + +Calling methods like `get()`, `post()`, `head()`, etc. no longer create and +return a request, but rather creates a request, sends the request, and returns +the response. + +```php +// 3.0 +$request = $client->get('/'); +$response = $request->send(); + +// 4.0 +$response = $client->get('/'); + +// or, to mirror the previous behavior +$request = $client->createRequest('GET', '/'); +$response = $client->send($request); +``` + +`GuzzleHttp\ClientInterface` has changed. + +- The `send` method no longer accepts more than one request. Use `sendAll` to + send multiple requests in parallel. +- `setUserAgent()` has been removed. Use a default request option instead. You + could, for example, do something like: + `$client->setConfig('defaults/headers/User-Agent', 'Foo/Bar ' . $client::getDefaultUserAgent())`. +- `setSslVerification()` has been removed. Use default request options instead, + like `$client->setConfig('defaults/verify', true)`. + +`GuzzleHttp\Client` has changed. + +- The constructor now accepts only an associative array. You can include a + `base_url` string or array to use a URI template as the base URL of a client. + You can also specify a `defaults` key that is an associative array of default + request options. You can pass an `adapter` to use a custom adapter, + `batch_adapter` to use a custom adapter for sending requests in parallel, or + a `message_factory` to change the factory used to create HTTP requests and + responses. +- The client no longer emits a `client.create_request` event. +- Creating requests with a client no longer automatically utilize a URI + template. You must pass an array into a creational method (e.g., + `createRequest`, `get`, `put`, etc.) in order to expand a URI template. + +### Messages + +Messages no longer have references to their counterparts (i.e., a request no +longer has a reference to it's response, and a response no loger has a +reference to its request). This association is now managed through a +`GuzzleHttp\Adapter\TransactionInterface` object. You can get references to +these transaction objects using request events that are emitted over the +lifecycle of a request. + +#### Requests with a body + +- `GuzzleHttp\Message\EntityEnclosingRequest` and + `GuzzleHttp\Message\EntityEnclosingRequestInterface` have been removed. The + separation between requests that contain a body and requests that do not + contain a body has been removed, and now `GuzzleHttp\Message\RequestInterface` + handles both use cases. +- Any method that previously accepts a `GuzzleHttp\Response` object now accept a + `GuzzleHttp\Message\ResponseInterface`. +- `GuzzleHttp\Message\RequestFactoryInterface` has been renamed to + `GuzzleHttp\Message\MessageFactoryInterface`. This interface is used to create + both requests and responses and is implemented in + `GuzzleHttp\Message\MessageFactory`. +- POST field and file methods have been removed from the request object. You + must now use the methods made available to `GuzzleHttp\Post\PostBodyInterface` + to control the format of a POST body. Requests that are created using a + standard `GuzzleHttp\Message\MessageFactoryInterface` will automatically use + a `GuzzleHttp\Post\PostBody` body if the body was passed as an array or if + the method is POST and no body is provided. + +```php +$request = $client->createRequest('POST', '/'); +$request->getBody()->setField('foo', 'bar'); +$request->getBody()->addFile(new PostFile('file_key', fopen('/path/to/content', 'r'))); +``` + +#### Headers + +- `GuzzleHttp\Message\Header` has been removed. Header values are now simply + represented by an array of values or as a string. Header values are returned + as a string by default when retrieving a header value from a message. You can + pass an optional argument of `true` to retrieve a header value as an array + of strings instead of a single concatenated string. +- `GuzzleHttp\PostFile` and `GuzzleHttp\PostFileInterface` have been moved to + `GuzzleHttp\Post`. This interface has been simplified and now allows the + addition of arbitrary headers. +- Custom headers like `GuzzleHttp\Message\Header\Link` have been removed. Most + of the custom headers are now handled separately in specific + subscribers/plugins, and `GuzzleHttp\Message\HeaderValues::parseParams()` has + been updated to properly handle headers that contain parameters (like the + `Link` header). + +#### Responses + +- `GuzzleHttp\Message\Response::getInfo()` and + `GuzzleHttp\Message\Response::setInfo()` have been removed. Use the event + system to retrieve this type of information. +- `GuzzleHttp\Message\Response::getRawHeaders()` has been removed. +- `GuzzleHttp\Message\Response::getMessage()` has been removed. +- `GuzzleHttp\Message\Response::calculateAge()` and other cache specific + methods have moved to the CacheSubscriber. +- Header specific helper functions like `getContentMd5()` have been removed. + Just use `getHeader('Content-MD5')` instead. +- `GuzzleHttp\Message\Response::setRequest()` and + `GuzzleHttp\Message\Response::getRequest()` have been removed. Use the event + system to work with request and response objects as a transaction. +- `GuzzleHttp\Message\Response::getRedirectCount()` has been removed. Use the + Redirect subscriber instead. +- `GuzzleHttp\Message\Response::isSuccessful()` and other related methods have + been removed. Use `getStatusCode()` instead. + +#### Streaming responses + +Streaming requests can now be created by a client directly, returning a +`GuzzleHttp\Message\ResponseInterface` object that contains a body stream +referencing an open PHP HTTP stream. + +```php +// 3.0 +use Guzzle\Stream\PhpStreamRequestFactory; +$request = $client->get('/'); +$factory = new PhpStreamRequestFactory(); +$stream = $factory->fromRequest($request); +$data = $stream->read(1024); + +// 4.0 +$response = $client->get('/', ['stream' => true]); +// Read some data off of the stream in the response body +$data = $response->getBody()->read(1024); +``` + +#### Redirects + +The `configureRedirects()` method has been removed in favor of a +`allow_redirects` request option. + +```php +// Standard redirects with a default of a max of 5 redirects +$request = $client->createRequest('GET', '/', ['allow_redirects' => true]); + +// Strict redirects with a custom number of redirects +$request = $client->createRequest('GET', '/', [ + 'allow_redirects' => ['max' => 5, 'strict' => true] +]); +``` + +#### EntityBody + +EntityBody interfaces and classes have been removed or moved to +`GuzzleHttp\Stream`. All classes and interfaces that once required +`GuzzleHttp\EntityBodyInterface` now require +`GuzzleHttp\Stream\StreamInterface`. Creating a new body for a request no +longer uses `GuzzleHttp\EntityBody::factory` but now uses +`GuzzleHttp\Stream\Stream::factory` or even better: +`GuzzleHttp\Stream\create()`. + +- `Guzzle\Http\EntityBodyInterface` is now `GuzzleHttp\Stream\StreamInterface` +- `Guzzle\Http\EntityBody` is now `GuzzleHttp\Stream\Stream` +- `Guzzle\Http\CachingEntityBody` is now `GuzzleHttp\Stream\CachingStream` +- `Guzzle\Http\ReadLimitEntityBody` is now `GuzzleHttp\Stream\LimitStream` +- `Guzzle\Http\IoEmittyinEntityBody` has been removed. + +#### Request lifecycle events + +Requests previously submitted a large number of requests. The number of events +emitted over the lifecycle of a request has been significantly reduced to make +it easier to understand how to extend the behavior of a request. All events +emitted during the lifecycle of a request now emit a custom +`GuzzleHttp\Event\EventInterface` object that contains context providing +methods and a way in which to modify the transaction at that specific point in +time (e.g., intercept the request and set a response on the transaction). + +- `request.before_send` has been renamed to `before` and now emits a + `GuzzleHttp\Event\BeforeEvent` +- `request.complete` has been renamed to `complete` and now emits a + `GuzzleHttp\Event\CompleteEvent`. +- `request.sent` has been removed. Use `complete`. +- `request.success` has been removed. Use `complete`. +- `error` is now an event that emits a `GuzzleHttp\Event\ErrorEvent`. +- `request.exception` has been removed. Use `error`. +- `request.receive.status_line` has been removed. +- `curl.callback.progress` has been removed. Use a custom `StreamInterface` to + maintain a status update. +- `curl.callback.write` has been removed. Use a custom `StreamInterface` to + intercept writes. +- `curl.callback.read` has been removed. Use a custom `StreamInterface` to + intercept reads. + +`headers` is a new event that is emitted after the response headers of a +request have been received before the body of the response is downloaded. This +event emits a `GuzzleHttp\Event\HeadersEvent`. + +You can intercept a request and inject a response using the `intercept()` event +of a `GuzzleHttp\Event\BeforeEvent`, `GuzzleHttp\Event\CompleteEvent`, and +`GuzzleHttp\Event\ErrorEvent` event. + +See: http://docs.guzzlephp.org/en/latest/events.html + +## Inflection + +The `Guzzle\Inflection` namespace has been removed. This is not a core concern +of Guzzle. + +## Iterator + +The `Guzzle\Iterator` namespace has been removed. + +- `Guzzle\Iterator\AppendIterator`, `Guzzle\Iterator\ChunkedIterator`, and + `Guzzle\Iterator\MethodProxyIterator` are nice, but not a core requirement of + Guzzle itself. +- `Guzzle\Iterator\FilterIterator` is no longer needed because an equivalent + class is shipped with PHP 5.4. +- `Guzzle\Iterator\MapIterator` is not really needed when using PHP 5.5 because + it's easier to just wrap an iterator in a generator that maps values. + +For a replacement of these iterators, see https://github.com/nikic/iter + +## Log + +The LogPlugin has moved to https://github.com/guzzle/log-subscriber. The +`Guzzle\Log` namespace has been removed. Guzzle now relies on +`Psr\Log\LoggerInterface` for all logging. The MessageFormatter class has been +moved to `GuzzleHttp\Subscriber\Log\Formatter`. + +## Parser + +The `Guzzle\Parser` namespace has been removed. This was previously used to +make it possible to plug in custom parsers for cookies, messages, URI +templates, and URLs; however, this level of complexity is not needed in Guzzle +so it has been removed. + +- Cookie: Cookie parsing logic has been moved to + `GuzzleHttp\Cookie\SetCookie::fromString`. +- Message: Message parsing logic for both requests and responses has been moved + to `GuzzleHttp\Message\MessageFactory::fromMessage`. Message parsing is only + used in debugging or deserializing messages, so it doesn't make sense for + Guzzle as a library to add this level of complexity to parsing messages. +- UriTemplate: URI template parsing has been moved to + `GuzzleHttp\UriTemplate`. The Guzzle library will automatically use the PECL + URI template library if it is installed. +- Url: URL parsing is now performed in `GuzzleHttp\Url::fromString` (previously + it was `Guzzle\Http\Url::factory()`). If custom URL parsing is necessary, + then developers are free to subclass `GuzzleHttp\Url`. + +## Plugin + +The `Guzzle\Plugin` namespace has been renamed to `GuzzleHttp\Subscriber`. +Several plugins are shipping with the core Guzzle library under this namespace. + +- `GuzzleHttp\Subscriber\Cookie`: Replaces the old CookiePlugin. Cookie jar + code has moved to `GuzzleHttp\Cookie`. +- `GuzzleHttp\Subscriber\History`: Replaces the old HistoryPlugin. +- `GuzzleHttp\Subscriber\HttpError`: Throws errors when a bad HTTP response is + received. +- `GuzzleHttp\Subscriber\Mock`: Replaces the old MockPlugin. +- `GuzzleHttp\Subscriber\Prepare`: Prepares the body of a request just before + sending. This subscriber is attached to all requests by default. +- `GuzzleHttp\Subscriber\Redirect`: Replaces the RedirectPlugin. + +The following plugins have been removed (third-parties are free to re-implement +these if needed): + +- `GuzzleHttp\Plugin\Async` has been removed. +- `GuzzleHttp\Plugin\CurlAuth` has been removed. +- `GuzzleHttp\Plugin\ErrorResponse\ErrorResponsePlugin` has been removed. This + functionality should instead be implemented with event listeners that occur + after normal response parsing occurs in the guzzle/command package. + +The following plugins are not part of the core Guzzle package, but are provided +in separate repositories: + +- `Guzzle\Http\Plugin\BackoffPlugin` has been rewritten to be much simpler + to build custom retry policies using simple functions rather than various + chained classes. See: https://github.com/guzzle/retry-subscriber +- `Guzzle\Http\Plugin\Cache\CachePlugin` has moved to + https://github.com/guzzle/cache-subscriber +- `Guzzle\Http\Plugin\Log\LogPlugin` has moved to + https://github.com/guzzle/log-subscriber +- `Guzzle\Http\Plugin\Md5\Md5Plugin` has moved to + https://github.com/guzzle/message-integrity-subscriber +- `Guzzle\Http\Plugin\Mock\MockPlugin` has moved to + `GuzzleHttp\Subscriber\MockSubscriber`. +- `Guzzle\Http\Plugin\Oauth\OauthPlugin` has moved to + https://github.com/guzzle/oauth-subscriber + +## Service + +The service description layer of Guzzle has moved into two separate packages: + +- http://github.com/guzzle/command Provides a high level abstraction over web + services by representing web service operations using commands. +- http://github.com/guzzle/guzzle-services Provides an implementation of + guzzle/command that provides request serialization and response parsing using + Guzzle service descriptions. + +## Stream + +Stream have moved to a separate package available at +https://github.com/guzzle/streams. + +`Guzzle\Stream\StreamInterface` has been given a large update to cleanly take +on the responsibilities of `Guzzle\Http\EntityBody` and +`Guzzle\Http\EntityBodyInterface` now that they have been removed. The number +of methods implemented by the `StreamInterface` has been drastically reduced to +allow developers to more easily extend and decorate stream behavior. + +## Removed methods from StreamInterface + +- `getStream` and `setStream` have been removed to better encapsulate streams. +- `getMetadata` and `setMetadata` have been removed in favor of + `GuzzleHttp\Stream\MetadataStreamInterface`. +- `getWrapper`, `getWrapperData`, `getStreamType`, and `getUri` have all been + removed. This data is accessible when + using streams that implement `GuzzleHttp\Stream\MetadataStreamInterface`. +- `rewind` has been removed. Use `seek(0)` for a similar behavior. + +## Renamed methods + +- `detachStream` has been renamed to `detach`. +- `feof` has been renamed to `eof`. +- `ftell` has been renamed to `tell`. +- `readLine` has moved from an instance method to a static class method of + `GuzzleHttp\Stream\Stream`. + +## Metadata streams + +`GuzzleHttp\Stream\MetadataStreamInterface` has been added to denote streams +that contain additional metadata accessible via `getMetadata()`. +`GuzzleHttp\Stream\StreamInterface::getMetadata` and +`GuzzleHttp\Stream\StreamInterface::setMetadata` have been removed. + +## StreamRequestFactory + +The entire concept of the StreamRequestFactory has been removed. The way this +was used in Guzzle 3 broke the actual interface of sending streaming requests +(instead of getting back a Response, you got a StreamInterface). Streaming +PHP requests are now implemented through the `GuzzleHttp\Adapter\StreamAdapter`. + +3.6 to 3.7 +---------- + +### Deprecations + +- You can now enable E_USER_DEPRECATED warnings to see if you are using any deprecated methods.: + +```php +\Guzzle\Common\Version::$emitWarnings = true; +``` + +The following APIs and options have been marked as deprecated: + +- Marked `Guzzle\Http\Message\Request::isResponseBodyRepeatable()` as deprecated. Use `$request->getResponseBody()->isRepeatable()` instead. +- Marked `Guzzle\Http\Message\Request::canCache()` as deprecated. Use `Guzzle\Plugin\Cache\DefaultCanCacheStrategy->canCacheRequest()` instead. +- Marked `Guzzle\Http\Message\Request::canCache()` as deprecated. Use `Guzzle\Plugin\Cache\DefaultCanCacheStrategy->canCacheRequest()` instead. +- Marked `Guzzle\Http\Message\Request::setIsRedirect()` as deprecated. Use the HistoryPlugin instead. +- Marked `Guzzle\Http\Message\Request::isRedirect()` as deprecated. Use the HistoryPlugin instead. +- Marked `Guzzle\Cache\CacheAdapterFactory::factory()` as deprecated +- Marked `Guzzle\Service\Client::enableMagicMethods()` as deprecated. Magic methods can no longer be disabled on a Guzzle\Service\Client. +- Marked `Guzzle\Parser\Url\UrlParser` as deprecated. Just use PHP's `parse_url()` and percent encode your UTF-8. +- Marked `Guzzle\Common\Collection::inject()` as deprecated. +- Marked `Guzzle\Plugin\CurlAuth\CurlAuthPlugin` as deprecated. Use + `$client->getConfig()->setPath('request.options/auth', array('user', 'pass', 'Basic|Digest|NTLM|Any'));` or + `$client->setDefaultOption('auth', array('user', 'pass', 'Basic|Digest|NTLM|Any'));` + +3.7 introduces `request.options` as a parameter for a client configuration and as an optional argument to all creational +request methods. When paired with a client's configuration settings, these options allow you to specify default settings +for various aspects of a request. Because these options make other previous configuration options redundant, several +configuration options and methods of a client and AbstractCommand have been deprecated. + +- Marked `Guzzle\Service\Client::getDefaultHeaders()` as deprecated. Use `$client->getDefaultOption('headers')`. +- Marked `Guzzle\Service\Client::setDefaultHeaders()` as deprecated. Use `$client->setDefaultOption('headers/{header_name}', 'value')`. +- Marked 'request.params' for `Guzzle\Http\Client` as deprecated. Use `$client->setDefaultOption('params/{param_name}', 'value')` +- Marked 'command.headers', 'command.response_body' and 'command.on_complete' as deprecated for AbstractCommand. These will work through Guzzle 4.0 + + $command = $client->getCommand('foo', array( + 'command.headers' => array('Test' => '123'), + 'command.response_body' => '/path/to/file' + )); + + // Should be changed to: + + $command = $client->getCommand('foo', array( + 'command.request_options' => array( + 'headers' => array('Test' => '123'), + 'save_as' => '/path/to/file' + ) + )); + +### Interface changes + +Additions and changes (you will need to update any implementations or subclasses you may have created): + +- Added an `$options` argument to the end of the following methods of `Guzzle\Http\ClientInterface`: + createRequest, head, delete, put, patch, post, options, prepareRequest +- Added an `$options` argument to the end of `Guzzle\Http\Message\Request\RequestFactoryInterface::createRequest()` +- Added an `applyOptions()` method to `Guzzle\Http\Message\Request\RequestFactoryInterface` +- Changed `Guzzle\Http\ClientInterface::get($uri = null, $headers = null, $body = null)` to + `Guzzle\Http\ClientInterface::get($uri = null, $headers = null, $options = array())`. You can still pass in a + resource, string, or EntityBody into the $options parameter to specify the download location of the response. +- Changed `Guzzle\Common\Collection::__construct($data)` to no longer accepts a null value for `$data` but a + default `array()` +- Added `Guzzle\Stream\StreamInterface::isRepeatable` +- Made `Guzzle\Http\Client::expandTemplate` and `getUriTemplate` protected methods. + +The following methods were removed from interfaces. All of these methods are still available in the concrete classes +that implement them, but you should update your code to use alternative methods: + +- Removed `Guzzle\Http\ClientInterface::setDefaultHeaders(). Use + `$client->getConfig()->setPath('request.options/headers/{header_name}', 'value')`. or + `$client->getConfig()->setPath('request.options/headers', array('header_name' => 'value'))` or + `$client->setDefaultOption('headers/{header_name}', 'value')`. or + `$client->setDefaultOption('headers', array('header_name' => 'value'))`. +- Removed `Guzzle\Http\ClientInterface::getDefaultHeaders(). Use `$client->getConfig()->getPath('request.options/headers')`. +- Removed `Guzzle\Http\ClientInterface::expandTemplate()`. This is an implementation detail. +- Removed `Guzzle\Http\ClientInterface::setRequestFactory()`. This is an implementation detail. +- Removed `Guzzle\Http\ClientInterface::getCurlMulti()`. This is a very specific implementation detail. +- Removed `Guzzle\Http\Message\RequestInterface::canCache`. Use the CachePlugin. +- Removed `Guzzle\Http\Message\RequestInterface::setIsRedirect`. Use the HistoryPlugin. +- Removed `Guzzle\Http\Message\RequestInterface::isRedirect`. Use the HistoryPlugin. + +### Cache plugin breaking changes + +- CacheKeyProviderInterface and DefaultCacheKeyProvider are no longer used. All of this logic is handled in a + CacheStorageInterface. These two objects and interface will be removed in a future version. +- Always setting X-cache headers on cached responses +- Default cache TTLs are now handled by the CacheStorageInterface of a CachePlugin +- `CacheStorageInterface::cache($key, Response $response, $ttl = null)` has changed to `cache(RequestInterface + $request, Response $response);` +- `CacheStorageInterface::fetch($key)` has changed to `fetch(RequestInterface $request);` +- `CacheStorageInterface::delete($key)` has changed to `delete(RequestInterface $request);` +- Added `CacheStorageInterface::purge($url)` +- `DefaultRevalidation::__construct(CacheKeyProviderInterface $cacheKey, CacheStorageInterface $cache, CachePlugin + $plugin)` has changed to `DefaultRevalidation::__construct(CacheStorageInterface $cache, + CanCacheStrategyInterface $canCache = null)` +- Added `RevalidationInterface::shouldRevalidate(RequestInterface $request, Response $response)` + +3.5 to 3.6 +---------- + +* Mixed casing of headers are now forced to be a single consistent casing across all values for that header. +* Messages internally use a HeaderCollection object to delegate handling case-insensitive header resolution +* Removed the whole changedHeader() function system of messages because all header changes now go through addHeader(). + For example, setHeader() first removes the header using unset on a HeaderCollection and then calls addHeader(). + Keeping the Host header and URL host in sync is now handled by overriding the addHeader method in Request. +* Specific header implementations can be created for complex headers. When a message creates a header, it uses a + HeaderFactory which can map specific headers to specific header classes. There is now a Link header and + CacheControl header implementation. +* Moved getLinks() from Response to just be used on a Link header object. + +If you previously relied on Guzzle\Http\Message\Header::raw(), then you will need to update your code to use the +HeaderInterface (e.g. toArray(), getAll(), etc.). + +### Interface changes + +* Removed from interface: Guzzle\Http\ClientInterface::setUriTemplate +* Removed from interface: Guzzle\Http\ClientInterface::setCurlMulti() +* Removed Guzzle\Http\Message\Request::receivedRequestHeader() and implemented this functionality in + Guzzle\Http\Curl\RequestMediator +* Removed the optional $asString parameter from MessageInterface::getHeader(). Just cast the header to a string. +* Removed the optional $tryChunkedTransfer option from Guzzle\Http\Message\EntityEnclosingRequestInterface +* Removed the $asObjects argument from Guzzle\Http\Message\MessageInterface::getHeaders() + +### Removed deprecated functions + +* Removed Guzzle\Parser\ParserRegister::get(). Use getParser() +* Removed Guzzle\Parser\ParserRegister::set(). Use registerParser(). + +### Deprecations + +* The ability to case-insensitively search for header values +* Guzzle\Http\Message\Header::hasExactHeader +* Guzzle\Http\Message\Header::raw. Use getAll() +* Deprecated cache control specific methods on Guzzle\Http\Message\AbstractMessage. Use the CacheControl header object + instead. + +### Other changes + +* All response header helper functions return a string rather than mixing Header objects and strings inconsistently +* Removed cURL blacklist support. This is no longer necessary now that Expect, Accept, etc. are managed by Guzzle + directly via interfaces +* Removed the injecting of a request object onto a response object. The methods to get and set a request still exist + but are a no-op until removed. +* Most classes that used to require a `Guzzle\Service\Command\CommandInterface` typehint now request a + `Guzzle\Service\Command\ArrayCommandInterface`. +* Added `Guzzle\Http\Message\RequestInterface::startResponse()` to the RequestInterface to handle injecting a response + on a request while the request is still being transferred +* `Guzzle\Service\Command\CommandInterface` now extends from ToArrayInterface and ArrayAccess + +3.3 to 3.4 +---------- + +Base URLs of a client now follow the rules of http://tools.ietf.org/html/rfc3986#section-5.2.2 when merging URLs. + +3.2 to 3.3 +---------- + +### Response::getEtag() quote stripping removed + +`Guzzle\Http\Message\Response::getEtag()` no longer strips quotes around the ETag response header + +### Removed `Guzzle\Http\Utils` + +The `Guzzle\Http\Utils` class was removed. This class was only used for testing. + +### Stream wrapper and type + +`Guzzle\Stream\Stream::getWrapper()` and `Guzzle\Stream\Stream::getStreamType()` are no longer converted to lowercase. + +### curl.emit_io became emit_io + +Emitting IO events from a RequestMediator is now a parameter that must be set in a request's curl options using the +'emit_io' key. This was previously set under a request's parameters using 'curl.emit_io' + +3.1 to 3.2 +---------- + +### CurlMulti is no longer reused globally + +Before 3.2, the same CurlMulti object was reused globally for each client. This can cause issue where plugins added +to a single client can pollute requests dispatched from other clients. + +If you still wish to reuse the same CurlMulti object with each client, then you can add a listener to the +ServiceBuilder's `service_builder.create_client` event to inject a custom CurlMulti object into each client as it is +created. + +```php +$multi = new Guzzle\Http\Curl\CurlMulti(); +$builder = Guzzle\Service\Builder\ServiceBuilder::factory('/path/to/config.json'); +$builder->addListener('service_builder.create_client', function ($event) use ($multi) { + $event['client']->setCurlMulti($multi); +} +}); +``` + +### No default path + +URLs no longer have a default path value of '/' if no path was specified. + +Before: + +```php +$request = $client->get('http://www.foo.com'); +echo $request->getUrl(); +// >> http://www.foo.com/ +``` + +After: + +```php +$request = $client->get('http://www.foo.com'); +echo $request->getUrl(); +// >> http://www.foo.com +``` + +### Less verbose BadResponseException + +The exception message for `Guzzle\Http\Exception\BadResponseException` no longer contains the full HTTP request and +response information. You can, however, get access to the request and response object by calling `getRequest()` or +`getResponse()` on the exception object. + +### Query parameter aggregation + +Multi-valued query parameters are no longer aggregated using a callback function. `Guzzle\Http\Query` now has a +setAggregator() method that accepts a `Guzzle\Http\QueryAggregator\QueryAggregatorInterface` object. This object is +responsible for handling the aggregation of multi-valued query string variables into a flattened hash. + +2.8 to 3.x +---------- + +### Guzzle\Service\Inspector + +Change `\Guzzle\Service\Inspector::fromConfig` to `\Guzzle\Common\Collection::fromConfig` + +**Before** + +```php +use Guzzle\Service\Inspector; + +class YourClient extends \Guzzle\Service\Client +{ + public static function factory($config = array()) + { + $default = array(); + $required = array('base_url', 'username', 'api_key'); + $config = Inspector::fromConfig($config, $default, $required); + + $client = new self( + $config->get('base_url'), + $config->get('username'), + $config->get('api_key') + ); + $client->setConfig($config); + + $client->setDescription(ServiceDescription::factory(__DIR__ . DIRECTORY_SEPARATOR . 'client.json')); + + return $client; + } +``` + +**After** + +```php +use Guzzle\Common\Collection; + +class YourClient extends \Guzzle\Service\Client +{ + public static function factory($config = array()) + { + $default = array(); + $required = array('base_url', 'username', 'api_key'); + $config = Collection::fromConfig($config, $default, $required); + + $client = new self( + $config->get('base_url'), + $config->get('username'), + $config->get('api_key') + ); + $client->setConfig($config); + + $client->setDescription(ServiceDescription::factory(__DIR__ . DIRECTORY_SEPARATOR . 'client.json')); + + return $client; + } +``` + +### Convert XML Service Descriptions to JSON + +**Before** + +```xml +<?xml version="1.0" encoding="UTF-8"?> +<client> + <commands> + <!-- Groups --> + <command name="list_groups" method="GET" uri="groups.json"> + <doc>Get a list of groups</doc> + </command> + <command name="search_groups" method="GET" uri='search.json?query="{{query}} type:group"'> + <doc>Uses a search query to get a list of groups</doc> + <param name="query" type="string" required="true" /> + </command> + <command name="create_group" method="POST" uri="groups.json"> + <doc>Create a group</doc> + <param name="data" type="array" location="body" filters="json_encode" doc="Group JSON"/> + <param name="Content-Type" location="header" static="application/json"/> + </command> + <command name="delete_group" method="DELETE" uri="groups/{{id}}.json"> + <doc>Delete a group by ID</doc> + <param name="id" type="integer" required="true"/> + </command> + <command name="get_group" method="GET" uri="groups/{{id}}.json"> + <param name="id" type="integer" required="true"/> + </command> + <command name="update_group" method="PUT" uri="groups/{{id}}.json"> + <doc>Update a group</doc> + <param name="id" type="integer" required="true"/> + <param name="data" type="array" location="body" filters="json_encode" doc="Group JSON"/> + <param name="Content-Type" location="header" static="application/json"/> + </command> + </commands> +</client> +``` + +**After** + +```json +{ + "name": "Zendesk REST API v2", + "apiVersion": "2012-12-31", + "description":"Provides access to Zendesk views, groups, tickets, ticket fields, and users", + "operations": { + "list_groups": { + "httpMethod":"GET", + "uri": "groups.json", + "summary": "Get a list of groups" + }, + "search_groups":{ + "httpMethod":"GET", + "uri": "search.json?query=\"{query} type:group\"", + "summary": "Uses a search query to get a list of groups", + "parameters":{ + "query":{ + "location": "uri", + "description":"Zendesk Search Query", + "type": "string", + "required": true + } + } + }, + "create_group": { + "httpMethod":"POST", + "uri": "groups.json", + "summary": "Create a group", + "parameters":{ + "data": { + "type": "array", + "location": "body", + "description":"Group JSON", + "filters": "json_encode", + "required": true + }, + "Content-Type":{ + "type": "string", + "location":"header", + "static": "application/json" + } + } + }, + "delete_group": { + "httpMethod":"DELETE", + "uri": "groups/{id}.json", + "summary": "Delete a group", + "parameters":{ + "id":{ + "location": "uri", + "description":"Group to delete by ID", + "type": "integer", + "required": true + } + } + }, + "get_group": { + "httpMethod":"GET", + "uri": "groups/{id}.json", + "summary": "Get a ticket", + "parameters":{ + "id":{ + "location": "uri", + "description":"Group to get by ID", + "type": "integer", + "required": true + } + } + }, + "update_group": { + "httpMethod":"PUT", + "uri": "groups/{id}.json", + "summary": "Update a group", + "parameters":{ + "id": { + "location": "uri", + "description":"Group to update by ID", + "type": "integer", + "required": true + }, + "data": { + "type": "array", + "location": "body", + "description":"Group JSON", + "filters": "json_encode", + "required": true + }, + "Content-Type":{ + "type": "string", + "location":"header", + "static": "application/json" + } + } + } +} +``` + +### Guzzle\Service\Description\ServiceDescription + +Commands are now called Operations + +**Before** + +```php +use Guzzle\Service\Description\ServiceDescription; + +$sd = new ServiceDescription(); +$sd->getCommands(); // @returns ApiCommandInterface[] +$sd->hasCommand($name); +$sd->getCommand($name); // @returns ApiCommandInterface|null +$sd->addCommand($command); // @param ApiCommandInterface $command +``` + +**After** + +```php +use Guzzle\Service\Description\ServiceDescription; + +$sd = new ServiceDescription(); +$sd->getOperations(); // @returns OperationInterface[] +$sd->hasOperation($name); +$sd->getOperation($name); // @returns OperationInterface|null +$sd->addOperation($operation); // @param OperationInterface $operation +``` + +### Guzzle\Common\Inflection\Inflector + +Namespace is now `Guzzle\Inflection\Inflector` + +### Guzzle\Http\Plugin + +Namespace is now `Guzzle\Plugin`. Many other changes occur within this namespace and are detailed in their own sections below. + +### Guzzle\Http\Plugin\LogPlugin and Guzzle\Common\Log + +Now `Guzzle\Plugin\Log\LogPlugin` and `Guzzle\Log` respectively. + +**Before** + +```php +use Guzzle\Common\Log\ClosureLogAdapter; +use Guzzle\Http\Plugin\LogPlugin; + +/** @var \Guzzle\Http\Client */ +$client; + +// $verbosity is an integer indicating desired message verbosity level +$client->addSubscriber(new LogPlugin(new ClosureLogAdapter(function($m) { echo $m; }, $verbosity = LogPlugin::LOG_VERBOSE); +``` + +**After** + +```php +use Guzzle\Log\ClosureLogAdapter; +use Guzzle\Log\MessageFormatter; +use Guzzle\Plugin\Log\LogPlugin; + +/** @var \Guzzle\Http\Client */ +$client; + +// $format is a string indicating desired message format -- @see MessageFormatter +$client->addSubscriber(new LogPlugin(new ClosureLogAdapter(function($m) { echo $m; }, $format = MessageFormatter::DEBUG_FORMAT); +``` + +### Guzzle\Http\Plugin\CurlAuthPlugin + +Now `Guzzle\Plugin\CurlAuth\CurlAuthPlugin`. + +### Guzzle\Http\Plugin\ExponentialBackoffPlugin + +Now `Guzzle\Plugin\Backoff\BackoffPlugin`, and other changes. + +**Before** + +```php +use Guzzle\Http\Plugin\ExponentialBackoffPlugin; + +$backoffPlugin = new ExponentialBackoffPlugin($maxRetries, array_merge( + ExponentialBackoffPlugin::getDefaultFailureCodes(), array(429) + )); + +$client->addSubscriber($backoffPlugin); +``` + +**After** + +```php +use Guzzle\Plugin\Backoff\BackoffPlugin; +use Guzzle\Plugin\Backoff\HttpBackoffStrategy; + +// Use convenient factory method instead -- see implementation for ideas of what +// you can do with chaining backoff strategies +$backoffPlugin = BackoffPlugin::getExponentialBackoff($maxRetries, array_merge( + HttpBackoffStrategy::getDefaultFailureCodes(), array(429) + )); +$client->addSubscriber($backoffPlugin); +``` + +### Known Issues + +#### [BUG] Accept-Encoding header behavior changed unintentionally. + +(See #217) (Fixed in 09daeb8c666fb44499a0646d655a8ae36456575e) + +In version 2.8 setting the `Accept-Encoding` header would set the CURLOPT_ENCODING option, which permitted cURL to +properly handle gzip/deflate compressed responses from the server. In versions affected by this bug this does not happen. +See issue #217 for a workaround, or use a version containing the fix. diff --git a/vendor/guzzlehttp/guzzle/composer.json b/vendor/guzzlehttp/guzzle/composer.json new file mode 100644 index 00000000000..1f328e308cd --- /dev/null +++ b/vendor/guzzlehttp/guzzle/composer.json @@ -0,0 +1,44 @@ +{ + "name": "guzzlehttp/guzzle", + "type": "library", + "description": "Guzzle is a PHP HTTP client library", + "keywords": ["framework", "http", "rest", "web service", "curl", "client", "HTTP client"], + "homepage": "http://guzzlephp.org/", + "license": "MIT", + "authors": [ + { + "name": "Michael Dowling", + "email": "mtdowling@gmail.com", + "homepage": "https://github.com/mtdowling" + } + ], + "require": { + "php": ">=5.5", + "guzzlehttp/psr7": "^1.4", + "guzzlehttp/promises": "^1.0" + }, + "require-dev": { + "ext-curl": "*", + "phpunit/phpunit": "^4.8.35 || ^5.7 || ^6.4 || ^7.0", + "psr/log": "^1.0" + }, + "autoload": { + "files": ["src/functions_include.php"], + "psr-4": { + "GuzzleHttp\\": "src/" + } + }, + "autoload-dev": { + "psr-4": { + "GuzzleHttp\\Tests\\": "tests/" + } + }, + "suggest": { + "psr/log": "Required for using the Log middleware" + }, + "extra": { + "branch-alias": { + "dev-master": "6.3-dev" + } + } +} diff --git a/vendor/guzzlehttp/guzzle/src/Client.php b/vendor/guzzlehttp/guzzle/src/Client.php new file mode 100644 index 00000000000..80417918d05 --- /dev/null +++ b/vendor/guzzlehttp/guzzle/src/Client.php @@ -0,0 +1,422 @@ +<?php +namespace GuzzleHttp; + +use GuzzleHttp\Cookie\CookieJar; +use GuzzleHttp\Promise; +use GuzzleHttp\Psr7; +use Psr\Http\Message\UriInterface; +use Psr\Http\Message\RequestInterface; +use Psr\Http\Message\ResponseInterface; + +/** + * @method ResponseInterface get(string|UriInterface $uri, array $options = []) + * @method ResponseInterface head(string|UriInterface $uri, array $options = []) + * @method ResponseInterface put(string|UriInterface $uri, array $options = []) + * @method ResponseInterface post(string|UriInterface $uri, array $options = []) + * @method ResponseInterface patch(string|UriInterface $uri, array $options = []) + * @method ResponseInterface delete(string|UriInterface $uri, array $options = []) + * @method Promise\PromiseInterface getAsync(string|UriInterface $uri, array $options = []) + * @method Promise\PromiseInterface headAsync(string|UriInterface $uri, array $options = []) + * @method Promise\PromiseInterface putAsync(string|UriInterface $uri, array $options = []) + * @method Promise\PromiseInterface postAsync(string|UriInterface $uri, array $options = []) + * @method Promise\PromiseInterface patchAsync(string|UriInterface $uri, array $options = []) + * @method Promise\PromiseInterface deleteAsync(string|UriInterface $uri, array $options = []) + */ +class Client implements ClientInterface +{ + /** @var array Default request options */ + private $config; + + /** + * Clients accept an array of constructor parameters. + * + * Here's an example of creating a client using a base_uri and an array of + * default request options to apply to each request: + * + * $client = new Client([ + * 'base_uri' => 'http://www.foo.com/1.0/', + * 'timeout' => 0, + * 'allow_redirects' => false, + * 'proxy' => '192.168.16.1:10' + * ]); + * + * Client configuration settings include the following options: + * + * - handler: (callable) Function that transfers HTTP requests over the + * wire. The function is called with a Psr7\Http\Message\RequestInterface + * and array of transfer options, and must return a + * GuzzleHttp\Promise\PromiseInterface that is fulfilled with a + * Psr7\Http\Message\ResponseInterface on success. "handler" is a + * constructor only option that cannot be overridden in per/request + * options. If no handler is provided, a default handler will be created + * that enables all of the request options below by attaching all of the + * default middleware to the handler. + * - base_uri: (string|UriInterface) Base URI of the client that is merged + * into relative URIs. Can be a string or instance of UriInterface. + * - **: any request option + * + * @param array $config Client configuration settings. + * + * @see \GuzzleHttp\RequestOptions for a list of available request options. + */ + public function __construct(array $config = []) + { + if (!isset($config['handler'])) { + $config['handler'] = HandlerStack::create(); + } elseif (!is_callable($config['handler'])) { + throw new \InvalidArgumentException('handler must be a callable'); + } + + // Convert the base_uri to a UriInterface + if (isset($config['base_uri'])) { + $config['base_uri'] = Psr7\uri_for($config['base_uri']); + } + + $this->configureDefaults($config); + } + + public function __call($method, $args) + { + if (count($args) < 1) { + throw new \InvalidArgumentException('Magic request methods require a URI and optional options array'); + } + + $uri = $args[0]; + $opts = isset($args[1]) ? $args[1] : []; + + return substr($method, -5) === 'Async' + ? $this->requestAsync(substr($method, 0, -5), $uri, $opts) + : $this->request($method, $uri, $opts); + } + + public function sendAsync(RequestInterface $request, array $options = []) + { + // Merge the base URI into the request URI if needed. + $options = $this->prepareDefaults($options); + + return $this->transfer( + $request->withUri($this->buildUri($request->getUri(), $options), $request->hasHeader('Host')), + $options + ); + } + + public function send(RequestInterface $request, array $options = []) + { + $options[RequestOptions::SYNCHRONOUS] = true; + return $this->sendAsync($request, $options)->wait(); + } + + public function requestAsync($method, $uri = '', array $options = []) + { + $options = $this->prepareDefaults($options); + // Remove request modifying parameter because it can be done up-front. + $headers = isset($options['headers']) ? $options['headers'] : []; + $body = isset($options['body']) ? $options['body'] : null; + $version = isset($options['version']) ? $options['version'] : '1.1'; + // Merge the URI into the base URI. + $uri = $this->buildUri($uri, $options); + if (is_array($body)) { + $this->invalidBody(); + } + $request = new Psr7\Request($method, $uri, $headers, $body, $version); + // Remove the option so that they are not doubly-applied. + unset($options['headers'], $options['body'], $options['version']); + + return $this->transfer($request, $options); + } + + public function request($method, $uri = '', array $options = []) + { + $options[RequestOptions::SYNCHRONOUS] = true; + return $this->requestAsync($method, $uri, $options)->wait(); + } + + public function getConfig($option = null) + { + return $option === null + ? $this->config + : (isset($this->config[$option]) ? $this->config[$option] : null); + } + + private function buildUri($uri, array $config) + { + // for BC we accept null which would otherwise fail in uri_for + $uri = Psr7\uri_for($uri === null ? '' : $uri); + + if (isset($config['base_uri'])) { + $uri = Psr7\UriResolver::resolve(Psr7\uri_for($config['base_uri']), $uri); + } + + return $uri->getScheme() === '' && $uri->getHost() !== '' ? $uri->withScheme('http') : $uri; + } + + /** + * Configures the default options for a client. + * + * @param array $config + */ + private function configureDefaults(array $config) + { + $defaults = [ + 'allow_redirects' => RedirectMiddleware::$defaultSettings, + 'http_errors' => true, + 'decode_content' => true, + 'verify' => true, + 'cookies' => false + ]; + + // Use the standard Linux HTTP_PROXY and HTTPS_PROXY if set. + + // We can only trust the HTTP_PROXY environment variable in a CLI + // process due to the fact that PHP has no reliable mechanism to + // get environment variables that start with "HTTP_". + if (php_sapi_name() == 'cli' && getenv('HTTP_PROXY')) { + $defaults['proxy']['http'] = getenv('HTTP_PROXY'); + } + + if ($proxy = getenv('HTTPS_PROXY')) { + $defaults['proxy']['https'] = $proxy; + } + + if ($noProxy = getenv('NO_PROXY')) { + $cleanedNoProxy = str_replace(' ', '', $noProxy); + $defaults['proxy']['no'] = explode(',', $cleanedNoProxy); + } + + $this->config = $config + $defaults; + + if (!empty($config['cookies']) && $config['cookies'] === true) { + $this->config['cookies'] = new CookieJar(); + } + + // Add the default user-agent header. + if (!isset($this->config['headers'])) { + $this->config['headers'] = ['User-Agent' => default_user_agent()]; + } else { + // Add the User-Agent header if one was not already set. + foreach (array_keys($this->config['headers']) as $name) { + if (strtolower($name) === 'user-agent') { + return; + } + } + $this->config['headers']['User-Agent'] = default_user_agent(); + } + } + + /** + * Merges default options into the array. + * + * @param array $options Options to modify by reference + * + * @return array + */ + private function prepareDefaults($options) + { + $defaults = $this->config; + + if (!empty($defaults['headers'])) { + // Default headers are only added if they are not present. + $defaults['_conditional'] = $defaults['headers']; + unset($defaults['headers']); + } + + // Special handling for headers is required as they are added as + // conditional headers and as headers passed to a request ctor. + if (array_key_exists('headers', $options)) { + // Allows default headers to be unset. + if ($options['headers'] === null) { + $defaults['_conditional'] = null; + unset($options['headers']); + } elseif (!is_array($options['headers'])) { + throw new \InvalidArgumentException('headers must be an array'); + } + } + + // Shallow merge defaults underneath options. + $result = $options + $defaults; + + // Remove null values. + foreach ($result as $k => $v) { + if ($v === null) { + unset($result[$k]); + } + } + + return $result; + } + + /** + * Transfers the given request and applies request options. + * + * The URI of the request is not modified and the request options are used + * as-is without merging in default options. + * + * @param RequestInterface $request + * @param array $options + * + * @return Promise\PromiseInterface + */ + private function transfer(RequestInterface $request, array $options) + { + // save_to -> sink + if (isset($options['save_to'])) { + $options['sink'] = $options['save_to']; + unset($options['save_to']); + } + + // exceptions -> http_errors + if (isset($options['exceptions'])) { + $options['http_errors'] = $options['exceptions']; + unset($options['exceptions']); + } + + $request = $this->applyOptions($request, $options); + $handler = $options['handler']; + + try { + return Promise\promise_for($handler($request, $options)); + } catch (\Exception $e) { + return Promise\rejection_for($e); + } + } + + /** + * Applies the array of request options to a request. + * + * @param RequestInterface $request + * @param array $options + * + * @return RequestInterface + */ + private function applyOptions(RequestInterface $request, array &$options) + { + $modify = [ + 'set_headers' => [], + ]; + + if (isset($options['headers'])) { + $modify['set_headers'] = $options['headers']; + unset($options['headers']); + } + + if (isset($options['form_params'])) { + if (isset($options['multipart'])) { + throw new \InvalidArgumentException('You cannot use ' + . 'form_params and multipart at the same time. Use the ' + . 'form_params option if you want to send application/' + . 'x-www-form-urlencoded requests, and the multipart ' + . 'option to send multipart/form-data requests.'); + } + $options['body'] = http_build_query($options['form_params'], '', '&'); + unset($options['form_params']); + // Ensure that we don't have the header in different case and set the new value. + $options['_conditional'] = Psr7\_caseless_remove(['Content-Type'], $options['_conditional']); + $options['_conditional']['Content-Type'] = 'application/x-www-form-urlencoded'; + } + + if (isset($options['multipart'])) { + $options['body'] = new Psr7\MultipartStream($options['multipart']); + unset($options['multipart']); + } + + if (isset($options['json'])) { + $options['body'] = \GuzzleHttp\json_encode($options['json']); + unset($options['json']); + // Ensure that we don't have the header in different case and set the new value. + $options['_conditional'] = Psr7\_caseless_remove(['Content-Type'], $options['_conditional']); + $options['_conditional']['Content-Type'] = 'application/json'; + } + + if (!empty($options['decode_content']) + && $options['decode_content'] !== true + ) { + // Ensure that we don't have the header in different case and set the new value. + $options['_conditional'] = Psr7\_caseless_remove(['Accept-Encoding'], $options['_conditional']); + $modify['set_headers']['Accept-Encoding'] = $options['decode_content']; + } + + if (isset($options['body'])) { + if (is_array($options['body'])) { + $this->invalidBody(); + } + $modify['body'] = Psr7\stream_for($options['body']); + unset($options['body']); + } + + if (!empty($options['auth']) && is_array($options['auth'])) { + $value = $options['auth']; + $type = isset($value[2]) ? strtolower($value[2]) : 'basic'; + switch ($type) { + case 'basic': + // Ensure that we don't have the header in different case and set the new value. + $modify['set_headers'] = Psr7\_caseless_remove(['Authorization'], $modify['set_headers']); + $modify['set_headers']['Authorization'] = 'Basic ' + . base64_encode("$value[0]:$value[1]"); + break; + case 'digest': + // @todo: Do not rely on curl + $options['curl'][CURLOPT_HTTPAUTH] = CURLAUTH_DIGEST; + $options['curl'][CURLOPT_USERPWD] = "$value[0]:$value[1]"; + break; + case 'ntlm': + $options['curl'][CURLOPT_HTTPAUTH] = CURLAUTH_NTLM; + $options['curl'][CURLOPT_USERPWD] = "$value[0]:$value[1]"; + break; + } + } + + if (isset($options['query'])) { + $value = $options['query']; + if (is_array($value)) { + $value = http_build_query($value, null, '&', PHP_QUERY_RFC3986); + } + if (!is_string($value)) { + throw new \InvalidArgumentException('query must be a string or array'); + } + $modify['query'] = $value; + unset($options['query']); + } + + // Ensure that sink is not an invalid value. + if (isset($options['sink'])) { + // TODO: Add more sink validation? + if (is_bool($options['sink'])) { + throw new \InvalidArgumentException('sink must not be a boolean'); + } + } + + $request = Psr7\modify_request($request, $modify); + if ($request->getBody() instanceof Psr7\MultipartStream) { + // Use a multipart/form-data POST if a Content-Type is not set. + // Ensure that we don't have the header in different case and set the new value. + $options['_conditional'] = Psr7\_caseless_remove(['Content-Type'], $options['_conditional']); + $options['_conditional']['Content-Type'] = 'multipart/form-data; boundary=' + . $request->getBody()->getBoundary(); + } + + // Merge in conditional headers if they are not present. + if (isset($options['_conditional'])) { + // Build up the changes so it's in a single clone of the message. + $modify = []; + foreach ($options['_conditional'] as $k => $v) { + if (!$request->hasHeader($k)) { + $modify['set_headers'][$k] = $v; + } + } + $request = Psr7\modify_request($request, $modify); + // Don't pass this internal value along to middleware/handlers. + unset($options['_conditional']); + } + + return $request; + } + + private function invalidBody() + { + throw new \InvalidArgumentException('Passing in the "body" request ' + . 'option as an array to send a POST request has been deprecated. ' + . 'Please use the "form_params" request option to send a ' + . 'application/x-www-form-urlencoded request, or the "multipart" ' + . 'request option to send a multipart/form-data request.'); + } +} diff --git a/vendor/guzzlehttp/guzzle/src/ClientInterface.php b/vendor/guzzlehttp/guzzle/src/ClientInterface.php new file mode 100644 index 00000000000..2dbcffa492d --- /dev/null +++ b/vendor/guzzlehttp/guzzle/src/ClientInterface.php @@ -0,0 +1,84 @@ +<?php +namespace GuzzleHttp; + +use GuzzleHttp\Promise\PromiseInterface; +use GuzzleHttp\Exception\GuzzleException; +use Psr\Http\Message\RequestInterface; +use Psr\Http\Message\ResponseInterface; +use Psr\Http\Message\UriInterface; + +/** + * Client interface for sending HTTP requests. + */ +interface ClientInterface +{ + const VERSION = '6.3.3'; + + /** + * Send an HTTP request. + * + * @param RequestInterface $request Request to send + * @param array $options Request options to apply to the given + * request and to the transfer. + * + * @return ResponseInterface + * @throws GuzzleException + */ + public function send(RequestInterface $request, array $options = []); + + /** + * Asynchronously send an HTTP request. + * + * @param RequestInterface $request Request to send + * @param array $options Request options to apply to the given + * request and to the transfer. + * + * @return PromiseInterface + */ + public function sendAsync(RequestInterface $request, array $options = []); + + /** + * Create and send an HTTP request. + * + * Use an absolute path to override the base path of the client, or a + * relative path to append to the base path of the client. The URL can + * contain the query string as well. + * + * @param string $method HTTP method. + * @param string|UriInterface $uri URI object or string. + * @param array $options Request options to apply. + * + * @return ResponseInterface + * @throws GuzzleException + */ + public function request($method, $uri, array $options = []); + + /** + * Create and send an asynchronous HTTP request. + * + * Use an absolute path to override the base path of the client, or a + * relative path to append to the base path of the client. The URL can + * contain the query string as well. Use an array to provide a URL + * template and additional variables to use in the URL template expansion. + * + * @param string $method HTTP method + * @param string|UriInterface $uri URI object or string. + * @param array $options Request options to apply. + * + * @return PromiseInterface + */ + public function requestAsync($method, $uri, array $options = []); + + /** + * Get a client configuration option. + * + * These options include default request options of the client, a "handler" + * (if utilized by the concrete client), and a "base_uri" if utilized by + * the concrete client. + * + * @param string|null $option The config option to retrieve. + * + * @return mixed + */ + public function getConfig($option = null); +} diff --git a/vendor/guzzlehttp/guzzle/src/Cookie/CookieJar.php b/vendor/guzzlehttp/guzzle/src/Cookie/CookieJar.php new file mode 100644 index 00000000000..78f2b79fecb --- /dev/null +++ b/vendor/guzzlehttp/guzzle/src/Cookie/CookieJar.php @@ -0,0 +1,314 @@ +<?php +namespace GuzzleHttp\Cookie; + +use Psr\Http\Message\RequestInterface; +use Psr\Http\Message\ResponseInterface; + +/** + * Cookie jar that stores cookies as an array + */ +class CookieJar implements CookieJarInterface +{ + /** @var SetCookie[] Loaded cookie data */ + private $cookies = []; + + /** @var bool */ + private $strictMode; + + /** + * @param bool $strictMode Set to true to throw exceptions when invalid + * cookies are added to the cookie jar. + * @param array $cookieArray Array of SetCookie objects or a hash of + * arrays that can be used with the SetCookie + * constructor + */ + public function __construct($strictMode = false, $cookieArray = []) + { + $this->strictMode = $strictMode; + + foreach ($cookieArray as $cookie) { + if (!($cookie instanceof SetCookie)) { + $cookie = new SetCookie($cookie); + } + $this->setCookie($cookie); + } + } + + /** + * Create a new Cookie jar from an associative array and domain. + * + * @param array $cookies Cookies to create the jar from + * @param string $domain Domain to set the cookies to + * + * @return self + */ + public static function fromArray(array $cookies, $domain) + { + $cookieJar = new self(); + foreach ($cookies as $name => $value) { + $cookieJar->setCookie(new SetCookie([ + 'Domain' => $domain, + 'Name' => $name, + 'Value' => $value, + 'Discard' => true + ])); + } + + return $cookieJar; + } + + /** + * @deprecated + */ + public static function getCookieValue($value) + { + return $value; + } + + /** + * Evaluate if this cookie should be persisted to storage + * that survives between requests. + * + * @param SetCookie $cookie Being evaluated. + * @param bool $allowSessionCookies If we should persist session cookies + * @return bool + */ + public static function shouldPersist( + SetCookie $cookie, + $allowSessionCookies = false + ) { + if ($cookie->getExpires() || $allowSessionCookies) { + if (!$cookie->getDiscard()) { + return true; + } + } + + return false; + } + + /** + * Finds and returns the cookie based on the name + * + * @param string $name cookie name to search for + * @return SetCookie|null cookie that was found or null if not found + */ + public function getCookieByName($name) + { + // don't allow a null name + if ($name === null) { + return null; + } + foreach ($this->cookies as $cookie) { + if ($cookie->getName() !== null && strcasecmp($cookie->getName(), $name) === 0) { + return $cookie; + } + } + } + + public function toArray() + { + return array_map(function (SetCookie $cookie) { + return $cookie->toArray(); + }, $this->getIterator()->getArrayCopy()); + } + + public function clear($domain = null, $path = null, $name = null) + { + if (!$domain) { + $this->cookies = []; + return; + } elseif (!$path) { + $this->cookies = array_filter( + $this->cookies, + function (SetCookie $cookie) use ($path, $domain) { + return !$cookie->matchesDomain($domain); + } + ); + } elseif (!$name) { + $this->cookies = array_filter( + $this->cookies, + function (SetCookie $cookie) use ($path, $domain) { + return !($cookie->matchesPath($path) && + $cookie->matchesDomain($domain)); + } + ); + } else { + $this->cookies = array_filter( + $this->cookies, + function (SetCookie $cookie) use ($path, $domain, $name) { + return !($cookie->getName() == $name && + $cookie->matchesPath($path) && + $cookie->matchesDomain($domain)); + } + ); + } + } + + public function clearSessionCookies() + { + $this->cookies = array_filter( + $this->cookies, + function (SetCookie $cookie) { + return !$cookie->getDiscard() && $cookie->getExpires(); + } + ); + } + + public function setCookie(SetCookie $cookie) + { + // If the name string is empty (but not 0), ignore the set-cookie + // string entirely. + $name = $cookie->getName(); + if (!$name && $name !== '0') { + return false; + } + + // Only allow cookies with set and valid domain, name, value + $result = $cookie->validate(); + if ($result !== true) { + if ($this->strictMode) { + throw new \RuntimeException('Invalid cookie: ' . $result); + } else { + $this->removeCookieIfEmpty($cookie); + return false; + } + } + + // Resolve conflicts with previously set cookies + foreach ($this->cookies as $i => $c) { + + // Two cookies are identical, when their path, and domain are + // identical. + if ($c->getPath() != $cookie->getPath() || + $c->getDomain() != $cookie->getDomain() || + $c->getName() != $cookie->getName() + ) { + continue; + } + + // The previously set cookie is a discard cookie and this one is + // not so allow the new cookie to be set + if (!$cookie->getDiscard() && $c->getDiscard()) { + unset($this->cookies[$i]); + continue; + } + + // If the new cookie's expiration is further into the future, then + // replace the old cookie + if ($cookie->getExpires() > $c->getExpires()) { + unset($this->cookies[$i]); + continue; + } + + // If the value has changed, we better change it + if ($cookie->getValue() !== $c->getValue()) { + unset($this->cookies[$i]); + continue; + } + + // The cookie exists, so no need to continue + return false; + } + + $this->cookies[] = $cookie; + + return true; + } + + public function count() + { + return count($this->cookies); + } + + public function getIterator() + { + return new \ArrayIterator(array_values($this->cookies)); + } + + public function extractCookies( + RequestInterface $request, + ResponseInterface $response + ) { + if ($cookieHeader = $response->getHeader('Set-Cookie')) { + foreach ($cookieHeader as $cookie) { + $sc = SetCookie::fromString($cookie); + if (!$sc->getDomain()) { + $sc->setDomain($request->getUri()->getHost()); + } + if (0 !== strpos($sc->getPath(), '/')) { + $sc->setPath($this->getCookiePathFromRequest($request)); + } + $this->setCookie($sc); + } + } + } + + /** + * Computes cookie path following RFC 6265 section 5.1.4 + * + * @link https://tools.ietf.org/html/rfc6265#section-5.1.4 + * + * @param RequestInterface $request + * @return string + */ + private function getCookiePathFromRequest(RequestInterface $request) + { + $uriPath = $request->getUri()->getPath(); + if ('' === $uriPath) { + return '/'; + } + if (0 !== strpos($uriPath, '/')) { + return '/'; + } + if ('/' === $uriPath) { + return '/'; + } + if (0 === $lastSlashPos = strrpos($uriPath, '/')) { + return '/'; + } + + return substr($uriPath, 0, $lastSlashPos); + } + + public function withCookieHeader(RequestInterface $request) + { + $values = []; + $uri = $request->getUri(); + $scheme = $uri->getScheme(); + $host = $uri->getHost(); + $path = $uri->getPath() ?: '/'; + + foreach ($this->cookies as $cookie) { + if ($cookie->matchesPath($path) && + $cookie->matchesDomain($host) && + !$cookie->isExpired() && + (!$cookie->getSecure() || $scheme === 'https') + ) { + $values[] = $cookie->getName() . '=' + . $cookie->getValue(); + } + } + + return $values + ? $request->withHeader('Cookie', implode('; ', $values)) + : $request; + } + + /** + * If a cookie already exists and the server asks to set it again with a + * null value, the cookie must be deleted. + * + * @param SetCookie $cookie + */ + private function removeCookieIfEmpty(SetCookie $cookie) + { + $cookieValue = $cookie->getValue(); + if ($cookieValue === null || $cookieValue === '') { + $this->clear( + $cookie->getDomain(), + $cookie->getPath(), + $cookie->getName() + ); + } + } +} diff --git a/vendor/guzzlehttp/guzzle/src/Cookie/CookieJarInterface.php b/vendor/guzzlehttp/guzzle/src/Cookie/CookieJarInterface.php new file mode 100644 index 00000000000..2cf298a867b --- /dev/null +++ b/vendor/guzzlehttp/guzzle/src/Cookie/CookieJarInterface.php @@ -0,0 +1,84 @@ +<?php +namespace GuzzleHttp\Cookie; + +use Psr\Http\Message\RequestInterface; +use Psr\Http\Message\ResponseInterface; + +/** + * Stores HTTP cookies. + * + * It extracts cookies from HTTP requests, and returns them in HTTP responses. + * CookieJarInterface instances automatically expire contained cookies when + * necessary. Subclasses are also responsible for storing and retrieving + * cookies from a file, database, etc. + * + * @link http://docs.python.org/2/library/cookielib.html Inspiration + */ +interface CookieJarInterface extends \Countable, \IteratorAggregate +{ + /** + * Create a request with added cookie headers. + * + * If no matching cookies are found in the cookie jar, then no Cookie + * header is added to the request and the same request is returned. + * + * @param RequestInterface $request Request object to modify. + * + * @return RequestInterface returns the modified request. + */ + public function withCookieHeader(RequestInterface $request); + + /** + * Extract cookies from an HTTP response and store them in the CookieJar. + * + * @param RequestInterface $request Request that was sent + * @param ResponseInterface $response Response that was received + */ + public function extractCookies( + RequestInterface $request, + ResponseInterface $response + ); + + /** + * Sets a cookie in the cookie jar. + * + * @param SetCookie $cookie Cookie to set. + * + * @return bool Returns true on success or false on failure + */ + public function setCookie(SetCookie $cookie); + + /** + * Remove cookies currently held in the cookie jar. + * + * Invoking this method without arguments will empty the whole cookie jar. + * If given a $domain argument only cookies belonging to that domain will + * be removed. If given a $domain and $path argument, cookies belonging to + * the specified path within that domain are removed. If given all three + * arguments, then the cookie with the specified name, path and domain is + * removed. + * + * @param string $domain Clears cookies matching a domain + * @param string $path Clears cookies matching a domain and path + * @param string $name Clears cookies matching a domain, path, and name + * + * @return CookieJarInterface + */ + public function clear($domain = null, $path = null, $name = null); + + /** + * Discard all sessions cookies. + * + * Removes cookies that don't have an expire field or a have a discard + * field set to true. To be called when the user agent shuts down according + * to RFC 2965. + */ + public function clearSessionCookies(); + + /** + * Converts the cookie jar to an array. + * + * @return array + */ + public function toArray(); +} diff --git a/vendor/guzzlehttp/guzzle/src/Cookie/FileCookieJar.php b/vendor/guzzlehttp/guzzle/src/Cookie/FileCookieJar.php new file mode 100644 index 00000000000..9887c1d54b2 --- /dev/null +++ b/vendor/guzzlehttp/guzzle/src/Cookie/FileCookieJar.php @@ -0,0 +1,90 @@ +<?php +namespace GuzzleHttp\Cookie; + +/** + * Persists non-session cookies using a JSON formatted file + */ +class FileCookieJar extends CookieJar +{ + /** @var string filename */ + private $filename; + + /** @var bool Control whether to persist session cookies or not. */ + private $storeSessionCookies; + + /** + * Create a new FileCookieJar object + * + * @param string $cookieFile File to store the cookie data + * @param bool $storeSessionCookies Set to true to store session cookies + * in the cookie jar. + * + * @throws \RuntimeException if the file cannot be found or created + */ + public function __construct($cookieFile, $storeSessionCookies = false) + { + $this->filename = $cookieFile; + $this->storeSessionCookies = $storeSessionCookies; + + if (file_exists($cookieFile)) { + $this->load($cookieFile); + } + } + + /** + * Saves the file when shutting down + */ + public function __destruct() + { + $this->save($this->filename); + } + + /** + * Saves the cookies to a file. + * + * @param string $filename File to save + * @throws \RuntimeException if the file cannot be found or created + */ + public function save($filename) + { + $json = []; + foreach ($this as $cookie) { + /** @var SetCookie $cookie */ + if (CookieJar::shouldPersist($cookie, $this->storeSessionCookies)) { + $json[] = $cookie->toArray(); + } + } + + $jsonStr = \GuzzleHttp\json_encode($json); + if (false === file_put_contents($filename, $jsonStr)) { + throw new \RuntimeException("Unable to save file {$filename}"); + } + } + + /** + * Load cookies from a JSON formatted file. + * + * Old cookies are kept unless overwritten by newly loaded ones. + * + * @param string $filename Cookie file to load. + * @throws \RuntimeException if the file cannot be loaded. + */ + public function load($filename) + { + $json = file_get_contents($filename); + if (false === $json) { + throw new \RuntimeException("Unable to load file {$filename}"); + } elseif ($json === '') { + return; + } + + $data = \GuzzleHttp\json_decode($json, true); + if (is_array($data)) { + foreach (json_decode($json, true) as $cookie) { + $this->setCookie(new SetCookie($cookie)); + } + } elseif (strlen($data)) { + throw new \RuntimeException("Invalid cookie file: {$filename}"); + } + } +} diff --git a/vendor/guzzlehttp/guzzle/src/Cookie/SessionCookieJar.php b/vendor/guzzlehttp/guzzle/src/Cookie/SessionCookieJar.php new file mode 100644 index 00000000000..4497bcf03e2 --- /dev/null +++ b/vendor/guzzlehttp/guzzle/src/Cookie/SessionCookieJar.php @@ -0,0 +1,71 @@ +<?php +namespace GuzzleHttp\Cookie; + +/** + * Persists cookies in the client session + */ +class SessionCookieJar extends CookieJar +{ + /** @var string session key */ + private $sessionKey; + + /** @var bool Control whether to persist session cookies or not. */ + private $storeSessionCookies; + + /** + * Create a new SessionCookieJar object + * + * @param string $sessionKey Session key name to store the cookie + * data in session + * @param bool $storeSessionCookies Set to true to store session cookies + * in the cookie jar. + */ + public function __construct($sessionKey, $storeSessionCookies = false) + { + $this->sessionKey = $sessionKey; + $this->storeSessionCookies = $storeSessionCookies; + $this->load(); + } + + /** + * Saves cookies to session when shutting down + */ + public function __destruct() + { + $this->save(); + } + + /** + * Save cookies to the client session + */ + public function save() + { + $json = []; + foreach ($this as $cookie) { + /** @var SetCookie $cookie */ + if (CookieJar::shouldPersist($cookie, $this->storeSessionCookies)) { + $json[] = $cookie->toArray(); + } + } + + $_SESSION[$this->sessionKey] = json_encode($json); + } + + /** + * Load the contents of the client session into the data array + */ + protected function load() + { + if (!isset($_SESSION[$this->sessionKey])) { + return; + } + $data = json_decode($_SESSION[$this->sessionKey], true); + if (is_array($data)) { + foreach ($data as $cookie) { + $this->setCookie(new SetCookie($cookie)); + } + } elseif (strlen($data)) { + throw new \RuntimeException("Invalid cookie data"); + } + } +} diff --git a/vendor/guzzlehttp/guzzle/src/Cookie/SetCookie.php b/vendor/guzzlehttp/guzzle/src/Cookie/SetCookie.php new file mode 100644 index 00000000000..f6993943e74 --- /dev/null +++ b/vendor/guzzlehttp/guzzle/src/Cookie/SetCookie.php @@ -0,0 +1,403 @@ +<?php +namespace GuzzleHttp\Cookie; + +/** + * Set-Cookie object + */ +class SetCookie +{ + /** @var array */ + private static $defaults = [ + 'Name' => null, + 'Value' => null, + 'Domain' => null, + 'Path' => '/', + 'Max-Age' => null, + 'Expires' => null, + 'Secure' => false, + 'Discard' => false, + 'HttpOnly' => false + ]; + + /** @var array Cookie data */ + private $data; + + /** + * Create a new SetCookie object from a string + * + * @param string $cookie Set-Cookie header string + * + * @return self + */ + public static function fromString($cookie) + { + // Create the default return array + $data = self::$defaults; + // Explode the cookie string using a series of semicolons + $pieces = array_filter(array_map('trim', explode(';', $cookie))); + // The name of the cookie (first kvp) must exist and include an equal sign. + if (empty($pieces[0]) || !strpos($pieces[0], '=')) { + return new self($data); + } + + // Add the cookie pieces into the parsed data array + foreach ($pieces as $part) { + $cookieParts = explode('=', $part, 2); + $key = trim($cookieParts[0]); + $value = isset($cookieParts[1]) + ? trim($cookieParts[1], " \n\r\t\0\x0B") + : true; + + // Only check for non-cookies when cookies have been found + if (empty($data['Name'])) { + $data['Name'] = $key; + $data['Value'] = $value; + } else { + foreach (array_keys(self::$defaults) as $search) { + if (!strcasecmp($search, $key)) { + $data[$search] = $value; + continue 2; + } + } + $data[$key] = $value; + } + } + + return new self($data); + } + + /** + * @param array $data Array of cookie data provided by a Cookie parser + */ + public function __construct(array $data = []) + { + $this->data = array_replace(self::$defaults, $data); + // Extract the Expires value and turn it into a UNIX timestamp if needed + if (!$this->getExpires() && $this->getMaxAge()) { + // Calculate the Expires date + $this->setExpires(time() + $this->getMaxAge()); + } elseif ($this->getExpires() && !is_numeric($this->getExpires())) { + $this->setExpires($this->getExpires()); + } + } + + public function __toString() + { + $str = $this->data['Name'] . '=' . $this->data['Value'] . '; '; + foreach ($this->data as $k => $v) { + if ($k !== 'Name' && $k !== 'Value' && $v !== null && $v !== false) { + if ($k === 'Expires') { + $str .= 'Expires=' . gmdate('D, d M Y H:i:s \G\M\T', $v) . '; '; + } else { + $str .= ($v === true ? $k : "{$k}={$v}") . '; '; + } + } + } + + return rtrim($str, '; '); + } + + public function toArray() + { + return $this->data; + } + + /** + * Get the cookie name + * + * @return string + */ + public function getName() + { + return $this->data['Name']; + } + + /** + * Set the cookie name + * + * @param string $name Cookie name + */ + public function setName($name) + { + $this->data['Name'] = $name; + } + + /** + * Get the cookie value + * + * @return string + */ + public function getValue() + { + return $this->data['Value']; + } + + /** + * Set the cookie value + * + * @param string $value Cookie value + */ + public function setValue($value) + { + $this->data['Value'] = $value; + } + + /** + * Get the domain + * + * @return string|null + */ + public function getDomain() + { + return $this->data['Domain']; + } + + /** + * Set the domain of the cookie + * + * @param string $domain + */ + public function setDomain($domain) + { + $this->data['Domain'] = $domain; + } + + /** + * Get the path + * + * @return string + */ + public function getPath() + { + return $this->data['Path']; + } + + /** + * Set the path of the cookie + * + * @param string $path Path of the cookie + */ + public function setPath($path) + { + $this->data['Path'] = $path; + } + + /** + * Maximum lifetime of the cookie in seconds + * + * @return int|null + */ + public function getMaxAge() + { + return $this->data['Max-Age']; + } + + /** + * Set the max-age of the cookie + * + * @param int $maxAge Max age of the cookie in seconds + */ + public function setMaxAge($maxAge) + { + $this->data['Max-Age'] = $maxAge; + } + + /** + * The UNIX timestamp when the cookie Expires + * + * @return mixed + */ + public function getExpires() + { + return $this->data['Expires']; + } + + /** + * Set the unix timestamp for which the cookie will expire + * + * @param int $timestamp Unix timestamp + */ + public function setExpires($timestamp) + { + $this->data['Expires'] = is_numeric($timestamp) + ? (int) $timestamp + : strtotime($timestamp); + } + + /** + * Get whether or not this is a secure cookie + * + * @return null|bool + */ + public function getSecure() + { + return $this->data['Secure']; + } + + /** + * Set whether or not the cookie is secure + * + * @param bool $secure Set to true or false if secure + */ + public function setSecure($secure) + { + $this->data['Secure'] = $secure; + } + + /** + * Get whether or not this is a session cookie + * + * @return null|bool + */ + public function getDiscard() + { + return $this->data['Discard']; + } + + /** + * Set whether or not this is a session cookie + * + * @param bool $discard Set to true or false if this is a session cookie + */ + public function setDiscard($discard) + { + $this->data['Discard'] = $discard; + } + + /** + * Get whether or not this is an HTTP only cookie + * + * @return bool + */ + public function getHttpOnly() + { + return $this->data['HttpOnly']; + } + + /** + * Set whether or not this is an HTTP only cookie + * + * @param bool $httpOnly Set to true or false if this is HTTP only + */ + public function setHttpOnly($httpOnly) + { + $this->data['HttpOnly'] = $httpOnly; + } + + /** + * Check if the cookie matches a path value. + * + * A request-path path-matches a given cookie-path if at least one of + * the following conditions holds: + * + * - The cookie-path and the request-path are identical. + * - The cookie-path is a prefix of the request-path, and the last + * character of the cookie-path is %x2F ("/"). + * - The cookie-path is a prefix of the request-path, and the first + * character of the request-path that is not included in the cookie- + * path is a %x2F ("/") character. + * + * @param string $requestPath Path to check against + * + * @return bool + */ + public function matchesPath($requestPath) + { + $cookiePath = $this->getPath(); + + // Match on exact matches or when path is the default empty "/" + if ($cookiePath === '/' || $cookiePath == $requestPath) { + return true; + } + + // Ensure that the cookie-path is a prefix of the request path. + if (0 !== strpos($requestPath, $cookiePath)) { + return false; + } + + // Match if the last character of the cookie-path is "/" + if (substr($cookiePath, -1, 1) === '/') { + return true; + } + + // Match if the first character not included in cookie path is "/" + return substr($requestPath, strlen($cookiePath), 1) === '/'; + } + + /** + * Check if the cookie matches a domain value + * + * @param string $domain Domain to check against + * + * @return bool + */ + public function matchesDomain($domain) + { + // Remove the leading '.' as per spec in RFC 6265. + // http://tools.ietf.org/html/rfc6265#section-5.2.3 + $cookieDomain = ltrim($this->getDomain(), '.'); + + // Domain not set or exact match. + if (!$cookieDomain || !strcasecmp($domain, $cookieDomain)) { + return true; + } + + // Matching the subdomain according to RFC 6265. + // http://tools.ietf.org/html/rfc6265#section-5.1.3 + if (filter_var($domain, FILTER_VALIDATE_IP)) { + return false; + } + + return (bool) preg_match('/\.' . preg_quote($cookieDomain, '/') . '$/', $domain); + } + + /** + * Check if the cookie is expired + * + * @return bool + */ + public function isExpired() + { + return $this->getExpires() !== null && time() > $this->getExpires(); + } + + /** + * Check if the cookie is valid according to RFC 6265 + * + * @return bool|string Returns true if valid or an error message if invalid + */ + public function validate() + { + // Names must not be empty, but can be 0 + $name = $this->getName(); + if (empty($name) && !is_numeric($name)) { + return 'The cookie name must not be empty'; + } + + // Check if any of the invalid characters are present in the cookie name + if (preg_match( + '/[\x00-\x20\x22\x28-\x29\x2c\x2f\x3a-\x40\x5c\x7b\x7d\x7f]/', + $name + )) { + return 'Cookie name must not contain invalid characters: ASCII ' + . 'Control characters (0-31;127), space, tab and the ' + . 'following characters: ()<>@,;:\"/?={}'; + } + + // Value must not be empty, but can be 0 + $value = $this->getValue(); + if (empty($value) && !is_numeric($value)) { + return 'The cookie value must not be empty'; + } + + // Domains must not be empty, but can be 0 + // A "0" is not a valid internet domain, but may be used as server name + // in a private network. + $domain = $this->getDomain(); + if (empty($domain) && !is_numeric($domain)) { + return 'The cookie domain must not be empty'; + } + + return true; + } +} diff --git a/vendor/guzzlehttp/guzzle/src/Exception/BadResponseException.php b/vendor/guzzlehttp/guzzle/src/Exception/BadResponseException.php new file mode 100644 index 00000000000..427d896fb2f --- /dev/null +++ b/vendor/guzzlehttp/guzzle/src/Exception/BadResponseException.php @@ -0,0 +1,27 @@ +<?php +namespace GuzzleHttp\Exception; + +use Psr\Http\Message\RequestInterface; +use Psr\Http\Message\ResponseInterface; + +/** + * Exception when an HTTP error occurs (4xx or 5xx error) + */ +class BadResponseException extends RequestException +{ + public function __construct( + $message, + RequestInterface $request, + ResponseInterface $response = null, + \Exception $previous = null, + array $handlerContext = [] + ) { + if (null === $response) { + @trigger_error( + 'Instantiating the ' . __CLASS__ . ' class without a Response is deprecated since version 6.3 and will be removed in 7.0.', + E_USER_DEPRECATED + ); + } + parent::__construct($message, $request, $response, $previous, $handlerContext); + } +} diff --git a/vendor/guzzlehttp/guzzle/src/Exception/ClientException.php b/vendor/guzzlehttp/guzzle/src/Exception/ClientException.php new file mode 100644 index 00000000000..f95c09f2b1a --- /dev/null +++ b/vendor/guzzlehttp/guzzle/src/Exception/ClientException.php @@ -0,0 +1,7 @@ +<?php +namespace GuzzleHttp\Exception; + +/** + * Exception when a client error is encountered (4xx codes) + */ +class ClientException extends BadResponseException {} diff --git a/vendor/guzzlehttp/guzzle/src/Exception/ConnectException.php b/vendor/guzzlehttp/guzzle/src/Exception/ConnectException.php new file mode 100644 index 00000000000..d33b0cc19d6 --- /dev/null +++ b/vendor/guzzlehttp/guzzle/src/Exception/ConnectException.php @@ -0,0 +1,37 @@ +<?php +namespace GuzzleHttp\Exception; + +use Psr\Http\Message\RequestInterface; + +/** + * Exception thrown when a connection cannot be established. + * + * Note that no response is present for a ConnectException + */ +class ConnectException extends RequestException +{ + public function __construct( + $message, + RequestInterface $request, + \Exception $previous = null, + array $handlerContext = [] + ) { + parent::__construct($message, $request, null, $previous, $handlerContext); + } + + /** + * @return null + */ + public function getResponse() + { + return null; + } + + /** + * @return bool + */ + public function hasResponse() + { + return false; + } +} diff --git a/vendor/guzzlehttp/guzzle/src/Exception/GuzzleException.php b/vendor/guzzlehttp/guzzle/src/Exception/GuzzleException.php new file mode 100644 index 00000000000..510778f6eb2 --- /dev/null +++ b/vendor/guzzlehttp/guzzle/src/Exception/GuzzleException.php @@ -0,0 +1,13 @@ +<?php +namespace GuzzleHttp\Exception; + +/** + * @method string getMessage() + * @method \Throwable|null getPrevious() + * @method mixed getCode() + * @method string getFile() + * @method int getLine() + * @method array getTrace() + * @method string getTraceAsString() + */ +interface GuzzleException {} diff --git a/vendor/guzzlehttp/guzzle/src/Exception/RequestException.php b/vendor/guzzlehttp/guzzle/src/Exception/RequestException.php new file mode 100644 index 00000000000..39de327e7b6 --- /dev/null +++ b/vendor/guzzlehttp/guzzle/src/Exception/RequestException.php @@ -0,0 +1,217 @@ +<?php +namespace GuzzleHttp\Exception; + +use Psr\Http\Message\RequestInterface; +use Psr\Http\Message\ResponseInterface; +use GuzzleHttp\Promise\PromiseInterface; +use Psr\Http\Message\UriInterface; + +/** + * HTTP Request exception + */ +class RequestException extends TransferException +{ + /** @var RequestInterface */ + private $request; + + /** @var ResponseInterface */ + private $response; + + /** @var array */ + private $handlerContext; + + public function __construct( + $message, + RequestInterface $request, + ResponseInterface $response = null, + \Exception $previous = null, + array $handlerContext = [] + ) { + // Set the code of the exception if the response is set and not future. + $code = $response && !($response instanceof PromiseInterface) + ? $response->getStatusCode() + : 0; + parent::__construct($message, $code, $previous); + $this->request = $request; + $this->response = $response; + $this->handlerContext = $handlerContext; + } + + /** + * Wrap non-RequestExceptions with a RequestException + * + * @param RequestInterface $request + * @param \Exception $e + * + * @return RequestException + */ + public static function wrapException(RequestInterface $request, \Exception $e) + { + return $e instanceof RequestException + ? $e + : new RequestException($e->getMessage(), $request, null, $e); + } + + /** + * Factory method to create a new exception with a normalized error message + * + * @param RequestInterface $request Request + * @param ResponseInterface $response Response received + * @param \Exception $previous Previous exception + * @param array $ctx Optional handler context. + * + * @return self + */ + public static function create( + RequestInterface $request, + ResponseInterface $response = null, + \Exception $previous = null, + array $ctx = [] + ) { + if (!$response) { + return new self( + 'Error completing request', + $request, + null, + $previous, + $ctx + ); + } + + $level = (int) floor($response->getStatusCode() / 100); + if ($level === 4) { + $label = 'Client error'; + $className = ClientException::class; + } elseif ($level === 5) { + $label = 'Server error'; + $className = ServerException::class; + } else { + $label = 'Unsuccessful request'; + $className = __CLASS__; + } + + $uri = $request->getUri(); + $uri = static::obfuscateUri($uri); + + // Client Error: `GET /` resulted in a `404 Not Found` response: + // <html> ... (truncated) + $message = sprintf( + '%s: `%s %s` resulted in a `%s %s` response', + $label, + $request->getMethod(), + $uri, + $response->getStatusCode(), + $response->getReasonPhrase() + ); + + $summary = static::getResponseBodySummary($response); + + if ($summary !== null) { + $message .= ":\n{$summary}\n"; + } + + return new $className($message, $request, $response, $previous, $ctx); + } + + /** + * Get a short summary of the response + * + * Will return `null` if the response is not printable. + * + * @param ResponseInterface $response + * + * @return string|null + */ + public static function getResponseBodySummary(ResponseInterface $response) + { + $body = $response->getBody(); + + if (!$body->isSeekable()) { + return null; + } + + $size = $body->getSize(); + + if ($size === 0) { + return null; + } + + $summary = $body->read(120); + $body->rewind(); + + if ($size > 120) { + $summary .= ' (truncated...)'; + } + + // Matches any printable character, including unicode characters: + // letters, marks, numbers, punctuation, spacing, and separators. + if (preg_match('/[^\pL\pM\pN\pP\pS\pZ\n\r\t]/', $summary)) { + return null; + } + + return $summary; + } + + /** + * Obfuscates URI if there is an username and a password present + * + * @param UriInterface $uri + * + * @return UriInterface + */ + private static function obfuscateUri($uri) + { + $userInfo = $uri->getUserInfo(); + + if (false !== ($pos = strpos($userInfo, ':'))) { + return $uri->withUserInfo(substr($userInfo, 0, $pos), '***'); + } + + return $uri; + } + + /** + * Get the request that caused the exception + * + * @return RequestInterface + */ + public function getRequest() + { + return $this->request; + } + + /** + * Get the associated response + * + * @return ResponseInterface|null + */ + public function getResponse() + { + return $this->response; + } + + /** + * Check if a response was received + * + * @return bool + */ + public function hasResponse() + { + return $this->response !== null; + } + + /** + * Get contextual information about the error from the underlying handler. + * + * The contents of this array will vary depending on which handler you are + * using. It may also be just an empty array. Relying on this data will + * couple you to a specific handler, but can give more debug information + * when needed. + * + * @return array + */ + public function getHandlerContext() + { + return $this->handlerContext; + } +} diff --git a/vendor/guzzlehttp/guzzle/src/Exception/SeekException.php b/vendor/guzzlehttp/guzzle/src/Exception/SeekException.php new file mode 100644 index 00000000000..a77c28926cf --- /dev/null +++ b/vendor/guzzlehttp/guzzle/src/Exception/SeekException.php @@ -0,0 +1,27 @@ +<?php +namespace GuzzleHttp\Exception; + +use Psr\Http\Message\StreamInterface; + +/** + * Exception thrown when a seek fails on a stream. + */ +class SeekException extends \RuntimeException implements GuzzleException +{ + private $stream; + + public function __construct(StreamInterface $stream, $pos = 0, $msg = '') + { + $this->stream = $stream; + $msg = $msg ?: 'Could not seek the stream to position ' . $pos; + parent::__construct($msg); + } + + /** + * @return StreamInterface + */ + public function getStream() + { + return $this->stream; + } +} diff --git a/vendor/guzzlehttp/guzzle/src/Exception/ServerException.php b/vendor/guzzlehttp/guzzle/src/Exception/ServerException.php new file mode 100644 index 00000000000..7cdd340866b --- /dev/null +++ b/vendor/guzzlehttp/guzzle/src/Exception/ServerException.php @@ -0,0 +1,7 @@ +<?php +namespace GuzzleHttp\Exception; + +/** + * Exception when a server error is encountered (5xx codes) + */ +class ServerException extends BadResponseException {} diff --git a/vendor/guzzlehttp/guzzle/src/Exception/TooManyRedirectsException.php b/vendor/guzzlehttp/guzzle/src/Exception/TooManyRedirectsException.php new file mode 100644 index 00000000000..b60a9678d65 --- /dev/null +++ b/vendor/guzzlehttp/guzzle/src/Exception/TooManyRedirectsException.php @@ -0,0 +1,4 @@ +<?php +namespace GuzzleHttp\Exception; + +class TooManyRedirectsException extends RequestException {} diff --git a/vendor/guzzlehttp/guzzle/src/Exception/TransferException.php b/vendor/guzzlehttp/guzzle/src/Exception/TransferException.php new file mode 100644 index 00000000000..b92071ca206 --- /dev/null +++ b/vendor/guzzlehttp/guzzle/src/Exception/TransferException.php @@ -0,0 +1,4 @@ +<?php +namespace GuzzleHttp\Exception; + +class TransferException extends \RuntimeException implements GuzzleException {} diff --git a/vendor/guzzlehttp/guzzle/src/Handler/CurlFactory.php b/vendor/guzzlehttp/guzzle/src/Handler/CurlFactory.php new file mode 100644 index 00000000000..e0923714306 --- /dev/null +++ b/vendor/guzzlehttp/guzzle/src/Handler/CurlFactory.php @@ -0,0 +1,565 @@ +<?php +namespace GuzzleHttp\Handler; + +use GuzzleHttp\Exception\RequestException; +use GuzzleHttp\Exception\ConnectException; +use GuzzleHttp\Promise\FulfilledPromise; +use GuzzleHttp\Psr7; +use GuzzleHttp\Psr7\LazyOpenStream; +use GuzzleHttp\TransferStats; +use Psr\Http\Message\RequestInterface; + +/** + * Creates curl resources from a request + */ +class CurlFactory implements CurlFactoryInterface +{ + /** @var array */ + private $handles = []; + + /** @var int Total number of idle handles to keep in cache */ + private $maxHandles; + + /** + * @param int $maxHandles Maximum number of idle handles. + */ + public function __construct($maxHandles) + { + $this->maxHandles = $maxHandles; + } + + public function create(RequestInterface $request, array $options) + { + if (isset($options['curl']['body_as_string'])) { + $options['_body_as_string'] = $options['curl']['body_as_string']; + unset($options['curl']['body_as_string']); + } + + $easy = new EasyHandle; + $easy->request = $request; + $easy->options = $options; + $conf = $this->getDefaultConf($easy); + $this->applyMethod($easy, $conf); + $this->applyHandlerOptions($easy, $conf); + $this->applyHeaders($easy, $conf); + unset($conf['_headers']); + + // Add handler options from the request configuration options + if (isset($options['curl'])) { + $conf = array_replace($conf, $options['curl']); + } + + $conf[CURLOPT_HEADERFUNCTION] = $this->createHeaderFn($easy); + $easy->handle = $this->handles + ? array_pop($this->handles) + : curl_init(); + curl_setopt_array($easy->handle, $conf); + + return $easy; + } + + public function release(EasyHandle $easy) + { + $resource = $easy->handle; + unset($easy->handle); + + if (count($this->handles) >= $this->maxHandles) { + curl_close($resource); + } else { + // Remove all callback functions as they can hold onto references + // and are not cleaned up by curl_reset. Using curl_setopt_array + // does not work for some reason, so removing each one + // individually. + curl_setopt($resource, CURLOPT_HEADERFUNCTION, null); + curl_setopt($resource, CURLOPT_READFUNCTION, null); + curl_setopt($resource, CURLOPT_WRITEFUNCTION, null); + curl_setopt($resource, CURLOPT_PROGRESSFUNCTION, null); + curl_reset($resource); + $this->handles[] = $resource; + } + } + + /** + * Completes a cURL transaction, either returning a response promise or a + * rejected promise. + * + * @param callable $handler + * @param EasyHandle $easy + * @param CurlFactoryInterface $factory Dictates how the handle is released + * + * @return \GuzzleHttp\Promise\PromiseInterface + */ + public static function finish( + callable $handler, + EasyHandle $easy, + CurlFactoryInterface $factory + ) { + if (isset($easy->options['on_stats'])) { + self::invokeStats($easy); + } + + if (!$easy->response || $easy->errno) { + return self::finishError($handler, $easy, $factory); + } + + // Return the response if it is present and there is no error. + $factory->release($easy); + + // Rewind the body of the response if possible. + $body = $easy->response->getBody(); + if ($body->isSeekable()) { + $body->rewind(); + } + + return new FulfilledPromise($easy->response); + } + + private static function invokeStats(EasyHandle $easy) + { + $curlStats = curl_getinfo($easy->handle); + $stats = new TransferStats( + $easy->request, + $easy->response, + $curlStats['total_time'], + $easy->errno, + $curlStats + ); + call_user_func($easy->options['on_stats'], $stats); + } + + private static function finishError( + callable $handler, + EasyHandle $easy, + CurlFactoryInterface $factory + ) { + // Get error information and release the handle to the factory. + $ctx = [ + 'errno' => $easy->errno, + 'error' => curl_error($easy->handle), + ] + curl_getinfo($easy->handle); + $factory->release($easy); + + // Retry when nothing is present or when curl failed to rewind. + if (empty($easy->options['_err_message']) + && (!$easy->errno || $easy->errno == 65) + ) { + return self::retryFailedRewind($handler, $easy, $ctx); + } + + return self::createRejection($easy, $ctx); + } + + private static function createRejection(EasyHandle $easy, array $ctx) + { + static $connectionErrors = [ + CURLE_OPERATION_TIMEOUTED => true, + CURLE_COULDNT_RESOLVE_HOST => true, + CURLE_COULDNT_CONNECT => true, + CURLE_SSL_CONNECT_ERROR => true, + CURLE_GOT_NOTHING => true, + ]; + + // If an exception was encountered during the onHeaders event, then + // return a rejected promise that wraps that exception. + if ($easy->onHeadersException) { + return \GuzzleHttp\Promise\rejection_for( + new RequestException( + 'An error was encountered during the on_headers event', + $easy->request, + $easy->response, + $easy->onHeadersException, + $ctx + ) + ); + } + + $message = sprintf( + 'cURL error %s: %s (%s)', + $ctx['errno'], + $ctx['error'], + 'see http://curl.haxx.se/libcurl/c/libcurl-errors.html' + ); + + // Create a connection exception if it was a specific error code. + $error = isset($connectionErrors[$easy->errno]) + ? new ConnectException($message, $easy->request, null, $ctx) + : new RequestException($message, $easy->request, $easy->response, null, $ctx); + + return \GuzzleHttp\Promise\rejection_for($error); + } + + private function getDefaultConf(EasyHandle $easy) + { + $conf = [ + '_headers' => $easy->request->getHeaders(), + CURLOPT_CUSTOMREQUEST => $easy->request->getMethod(), + CURLOPT_URL => (string) $easy->request->getUri()->withFragment(''), + CURLOPT_RETURNTRANSFER => false, + CURLOPT_HEADER => false, + CURLOPT_CONNECTTIMEOUT => 150, + ]; + + if (defined('CURLOPT_PROTOCOLS')) { + $conf[CURLOPT_PROTOCOLS] = CURLPROTO_HTTP | CURLPROTO_HTTPS; + } + + $version = $easy->request->getProtocolVersion(); + if ($version == 1.1) { + $conf[CURLOPT_HTTP_VERSION] = CURL_HTTP_VERSION_1_1; + } elseif ($version == 2.0) { + $conf[CURLOPT_HTTP_VERSION] = CURL_HTTP_VERSION_2_0; + } else { + $conf[CURLOPT_HTTP_VERSION] = CURL_HTTP_VERSION_1_0; + } + + return $conf; + } + + private function applyMethod(EasyHandle $easy, array &$conf) + { + $body = $easy->request->getBody(); + $size = $body->getSize(); + + if ($size === null || $size > 0) { + $this->applyBody($easy->request, $easy->options, $conf); + return; + } + + $method = $easy->request->getMethod(); + if ($method === 'PUT' || $method === 'POST') { + // See http://tools.ietf.org/html/rfc7230#section-3.3.2 + if (!$easy->request->hasHeader('Content-Length')) { + $conf[CURLOPT_HTTPHEADER][] = 'Content-Length: 0'; + } + } elseif ($method === 'HEAD') { + $conf[CURLOPT_NOBODY] = true; + unset( + $conf[CURLOPT_WRITEFUNCTION], + $conf[CURLOPT_READFUNCTION], + $conf[CURLOPT_FILE], + $conf[CURLOPT_INFILE] + ); + } + } + + private function applyBody(RequestInterface $request, array $options, array &$conf) + { + $size = $request->hasHeader('Content-Length') + ? (int) $request->getHeaderLine('Content-Length') + : null; + + // Send the body as a string if the size is less than 1MB OR if the + // [curl][body_as_string] request value is set. + if (($size !== null && $size < 1000000) || + !empty($options['_body_as_string']) + ) { + $conf[CURLOPT_POSTFIELDS] = (string) $request->getBody(); + // Don't duplicate the Content-Length header + $this->removeHeader('Content-Length', $conf); + $this->removeHeader('Transfer-Encoding', $conf); + } else { + $conf[CURLOPT_UPLOAD] = true; + if ($size !== null) { + $conf[CURLOPT_INFILESIZE] = $size; + $this->removeHeader('Content-Length', $conf); + } + $body = $request->getBody(); + if ($body->isSeekable()) { + $body->rewind(); + } + $conf[CURLOPT_READFUNCTION] = function ($ch, $fd, $length) use ($body) { + return $body->read($length); + }; + } + + // If the Expect header is not present, prevent curl from adding it + if (!$request->hasHeader('Expect')) { + $conf[CURLOPT_HTTPHEADER][] = 'Expect:'; + } + + // cURL sometimes adds a content-type by default. Prevent this. + if (!$request->hasHeader('Content-Type')) { + $conf[CURLOPT_HTTPHEADER][] = 'Content-Type:'; + } + } + + private function applyHeaders(EasyHandle $easy, array &$conf) + { + foreach ($conf['_headers'] as $name => $values) { + foreach ($values as $value) { + $value = (string) $value; + if ($value === '') { + // cURL requires a special format for empty headers. + // See https://github.com/guzzle/guzzle/issues/1882 for more details. + $conf[CURLOPT_HTTPHEADER][] = "$name;"; + } else { + $conf[CURLOPT_HTTPHEADER][] = "$name: $value"; + } + } + } + + // Remove the Accept header if one was not set + if (!$easy->request->hasHeader('Accept')) { + $conf[CURLOPT_HTTPHEADER][] = 'Accept:'; + } + } + + /** + * Remove a header from the options array. + * + * @param string $name Case-insensitive header to remove + * @param array $options Array of options to modify + */ + private function removeHeader($name, array &$options) + { + foreach (array_keys($options['_headers']) as $key) { + if (!strcasecmp($key, $name)) { + unset($options['_headers'][$key]); + return; + } + } + } + + private function applyHandlerOptions(EasyHandle $easy, array &$conf) + { + $options = $easy->options; + if (isset($options['verify'])) { + if ($options['verify'] === false) { + unset($conf[CURLOPT_CAINFO]); + $conf[CURLOPT_SSL_VERIFYHOST] = 0; + $conf[CURLOPT_SSL_VERIFYPEER] = false; + } else { + $conf[CURLOPT_SSL_VERIFYHOST] = 2; + $conf[CURLOPT_SSL_VERIFYPEER] = true; + if (is_string($options['verify'])) { + // Throw an error if the file/folder/link path is not valid or doesn't exist. + if (!file_exists($options['verify'])) { + throw new \InvalidArgumentException( + "SSL CA bundle not found: {$options['verify']}" + ); + } + // If it's a directory or a link to a directory use CURLOPT_CAPATH. + // If not, it's probably a file, or a link to a file, so use CURLOPT_CAINFO. + if (is_dir($options['verify']) || + (is_link($options['verify']) && is_dir(readlink($options['verify'])))) { + $conf[CURLOPT_CAPATH] = $options['verify']; + } else { + $conf[CURLOPT_CAINFO] = $options['verify']; + } + } + } + } + + if (!empty($options['decode_content'])) { + $accept = $easy->request->getHeaderLine('Accept-Encoding'); + if ($accept) { + $conf[CURLOPT_ENCODING] = $accept; + } else { + $conf[CURLOPT_ENCODING] = ''; + // Don't let curl send the header over the wire + $conf[CURLOPT_HTTPHEADER][] = 'Accept-Encoding:'; + } + } + + if (isset($options['sink'])) { + $sink = $options['sink']; + if (!is_string($sink)) { + $sink = \GuzzleHttp\Psr7\stream_for($sink); + } elseif (!is_dir(dirname($sink))) { + // Ensure that the directory exists before failing in curl. + throw new \RuntimeException(sprintf( + 'Directory %s does not exist for sink value of %s', + dirname($sink), + $sink + )); + } else { + $sink = new LazyOpenStream($sink, 'w+'); + } + $easy->sink = $sink; + $conf[CURLOPT_WRITEFUNCTION] = function ($ch, $write) use ($sink) { + return $sink->write($write); + }; + } else { + // Use a default temp stream if no sink was set. + $conf[CURLOPT_FILE] = fopen('php://temp', 'w+'); + $easy->sink = Psr7\stream_for($conf[CURLOPT_FILE]); + } + $timeoutRequiresNoSignal = false; + if (isset($options['timeout'])) { + $timeoutRequiresNoSignal |= $options['timeout'] < 1; + $conf[CURLOPT_TIMEOUT_MS] = $options['timeout'] * 1000; + } + + // CURL default value is CURL_IPRESOLVE_WHATEVER + if (isset($options['force_ip_resolve'])) { + if ('v4' === $options['force_ip_resolve']) { + $conf[CURLOPT_IPRESOLVE] = CURL_IPRESOLVE_V4; + } elseif ('v6' === $options['force_ip_resolve']) { + $conf[CURLOPT_IPRESOLVE] = CURL_IPRESOLVE_V6; + } + } + + if (isset($options['connect_timeout'])) { + $timeoutRequiresNoSignal |= $options['connect_timeout'] < 1; + $conf[CURLOPT_CONNECTTIMEOUT_MS] = $options['connect_timeout'] * 1000; + } + + if ($timeoutRequiresNoSignal && strtoupper(substr(PHP_OS, 0, 3)) !== 'WIN') { + $conf[CURLOPT_NOSIGNAL] = true; + } + + if (isset($options['proxy'])) { + if (!is_array($options['proxy'])) { + $conf[CURLOPT_PROXY] = $options['proxy']; + } else { + $scheme = $easy->request->getUri()->getScheme(); + if (isset($options['proxy'][$scheme])) { + $host = $easy->request->getUri()->getHost(); + if (!isset($options['proxy']['no']) || + !\GuzzleHttp\is_host_in_noproxy($host, $options['proxy']['no']) + ) { + $conf[CURLOPT_PROXY] = $options['proxy'][$scheme]; + } + } + } + } + + if (isset($options['cert'])) { + $cert = $options['cert']; + if (is_array($cert)) { + $conf[CURLOPT_SSLCERTPASSWD] = $cert[1]; + $cert = $cert[0]; + } + if (!file_exists($cert)) { + throw new \InvalidArgumentException( + "SSL certificate not found: {$cert}" + ); + } + $conf[CURLOPT_SSLCERT] = $cert; + } + + if (isset($options['ssl_key'])) { + $sslKey = $options['ssl_key']; + if (is_array($sslKey)) { + $conf[CURLOPT_SSLKEYPASSWD] = $sslKey[1]; + $sslKey = $sslKey[0]; + } + if (!file_exists($sslKey)) { + throw new \InvalidArgumentException( + "SSL private key not found: {$sslKey}" + ); + } + $conf[CURLOPT_SSLKEY] = $sslKey; + } + + if (isset($options['progress'])) { + $progress = $options['progress']; + if (!is_callable($progress)) { + throw new \InvalidArgumentException( + 'progress client option must be callable' + ); + } + $conf[CURLOPT_NOPROGRESS] = false; + $conf[CURLOPT_PROGRESSFUNCTION] = function () use ($progress) { + $args = func_get_args(); + // PHP 5.5 pushed the handle onto the start of the args + if (is_resource($args[0])) { + array_shift($args); + } + call_user_func_array($progress, $args); + }; + } + + if (!empty($options['debug'])) { + $conf[CURLOPT_STDERR] = \GuzzleHttp\debug_resource($options['debug']); + $conf[CURLOPT_VERBOSE] = true; + } + } + + /** + * This function ensures that a response was set on a transaction. If one + * was not set, then the request is retried if possible. This error + * typically means you are sending a payload, curl encountered a + * "Connection died, retrying a fresh connect" error, tried to rewind the + * stream, and then encountered a "necessary data rewind wasn't possible" + * error, causing the request to be sent through curl_multi_info_read() + * without an error status. + */ + private static function retryFailedRewind( + callable $handler, + EasyHandle $easy, + array $ctx + ) { + try { + // Only rewind if the body has been read from. + $body = $easy->request->getBody(); + if ($body->tell() > 0) { + $body->rewind(); + } + } catch (\RuntimeException $e) { + $ctx['error'] = 'The connection unexpectedly failed without ' + . 'providing an error. The request would have been retried, ' + . 'but attempting to rewind the request body failed. ' + . 'Exception: ' . $e; + return self::createRejection($easy, $ctx); + } + + // Retry no more than 3 times before giving up. + if (!isset($easy->options['_curl_retries'])) { + $easy->options['_curl_retries'] = 1; + } elseif ($easy->options['_curl_retries'] == 2) { + $ctx['error'] = 'The cURL request was retried 3 times ' + . 'and did not succeed. The most likely reason for the failure ' + . 'is that cURL was unable to rewind the body of the request ' + . 'and subsequent retries resulted in the same error. Turn on ' + . 'the debug option to see what went wrong. See ' + . 'https://bugs.php.net/bug.php?id=47204 for more information.'; + return self::createRejection($easy, $ctx); + } else { + $easy->options['_curl_retries']++; + } + + return $handler($easy->request, $easy->options); + } + + private function createHeaderFn(EasyHandle $easy) + { + if (isset($easy->options['on_headers'])) { + $onHeaders = $easy->options['on_headers']; + + if (!is_callable($onHeaders)) { + throw new \InvalidArgumentException('on_headers must be callable'); + } + } else { + $onHeaders = null; + } + + return function ($ch, $h) use ( + $onHeaders, + $easy, + &$startingResponse + ) { + $value = trim($h); + if ($value === '') { + $startingResponse = true; + $easy->createResponse(); + if ($onHeaders !== null) { + try { + $onHeaders($easy->response); + } catch (\Exception $e) { + // Associate the exception with the handle and trigger + // a curl header write error by returning 0. + $easy->onHeadersException = $e; + return -1; + } + } + } elseif ($startingResponse) { + $startingResponse = false; + $easy->headers = [$value]; + } else { + $easy->headers[] = $value; + } + return strlen($h); + }; + } +} diff --git a/vendor/guzzlehttp/guzzle/src/Handler/CurlFactoryInterface.php b/vendor/guzzlehttp/guzzle/src/Handler/CurlFactoryInterface.php new file mode 100644 index 00000000000..b0fc236850b --- /dev/null +++ b/vendor/guzzlehttp/guzzle/src/Handler/CurlFactoryInterface.php @@ -0,0 +1,27 @@ +<?php +namespace GuzzleHttp\Handler; + +use Psr\Http\Message\RequestInterface; + +interface CurlFactoryInterface +{ + /** + * Creates a cURL handle resource. + * + * @param RequestInterface $request Request + * @param array $options Transfer options + * + * @return EasyHandle + * @throws \RuntimeException when an option cannot be applied + */ + public function create(RequestInterface $request, array $options); + + /** + * Release an easy handle, allowing it to be reused or closed. + * + * This function must call unset on the easy handle's "handle" property. + * + * @param EasyHandle $easy + */ + public function release(EasyHandle $easy); +} diff --git a/vendor/guzzlehttp/guzzle/src/Handler/CurlHandler.php b/vendor/guzzlehttp/guzzle/src/Handler/CurlHandler.php new file mode 100644 index 00000000000..43577da66e2 --- /dev/null +++ b/vendor/guzzlehttp/guzzle/src/Handler/CurlHandler.php @@ -0,0 +1,45 @@ +<?php +namespace GuzzleHttp\Handler; + +use GuzzleHttp\Psr7; +use Psr\Http\Message\RequestInterface; + +/** + * HTTP handler that uses cURL easy handles as a transport layer. + * + * When using the CurlHandler, custom curl options can be specified as an + * associative array of curl option constants mapping to values in the + * **curl** key of the "client" key of the request. + */ +class CurlHandler +{ + /** @var CurlFactoryInterface */ + private $factory; + + /** + * Accepts an associative array of options: + * + * - factory: Optional curl factory used to create cURL handles. + * + * @param array $options Array of options to use with the handler + */ + public function __construct(array $options = []) + { + $this->factory = isset($options['handle_factory']) + ? $options['handle_factory'] + : new CurlFactory(3); + } + + public function __invoke(RequestInterface $request, array $options) + { + if (isset($options['delay'])) { + usleep($options['delay'] * 1000); + } + + $easy = $this->factory->create($request, $options); + curl_exec($easy->handle); + $easy->errno = curl_errno($easy->handle); + + return CurlFactory::finish($this, $easy, $this->factory); + } +} diff --git a/vendor/guzzlehttp/guzzle/src/Handler/CurlMultiHandler.php b/vendor/guzzlehttp/guzzle/src/Handler/CurlMultiHandler.php new file mode 100644 index 00000000000..2754d8e4378 --- /dev/null +++ b/vendor/guzzlehttp/guzzle/src/Handler/CurlMultiHandler.php @@ -0,0 +1,199 @@ +<?php +namespace GuzzleHttp\Handler; + +use GuzzleHttp\Promise as P; +use GuzzleHttp\Promise\Promise; +use GuzzleHttp\Psr7; +use Psr\Http\Message\RequestInterface; + +/** + * Returns an asynchronous response using curl_multi_* functions. + * + * When using the CurlMultiHandler, custom curl options can be specified as an + * associative array of curl option constants mapping to values in the + * **curl** key of the provided request options. + * + * @property resource $_mh Internal use only. Lazy loaded multi-handle. + */ +class CurlMultiHandler +{ + /** @var CurlFactoryInterface */ + private $factory; + private $selectTimeout; + private $active; + private $handles = []; + private $delays = []; + + /** + * This handler accepts the following options: + * + * - handle_factory: An optional factory used to create curl handles + * - select_timeout: Optional timeout (in seconds) to block before timing + * out while selecting curl handles. Defaults to 1 second. + * + * @param array $options + */ + public function __construct(array $options = []) + { + $this->factory = isset($options['handle_factory']) + ? $options['handle_factory'] : new CurlFactory(50); + $this->selectTimeout = isset($options['select_timeout']) + ? $options['select_timeout'] : 1; + } + + public function __get($name) + { + if ($name === '_mh') { + return $this->_mh = curl_multi_init(); + } + + throw new \BadMethodCallException(); + } + + public function __destruct() + { + if (isset($this->_mh)) { + curl_multi_close($this->_mh); + unset($this->_mh); + } + } + + public function __invoke(RequestInterface $request, array $options) + { + $easy = $this->factory->create($request, $options); + $id = (int) $easy->handle; + + $promise = new Promise( + [$this, 'execute'], + function () use ($id) { + return $this->cancel($id); + } + ); + + $this->addRequest(['easy' => $easy, 'deferred' => $promise]); + + return $promise; + } + + /** + * Ticks the curl event loop. + */ + public function tick() + { + // Add any delayed handles if needed. + if ($this->delays) { + $currentTime = microtime(true); + foreach ($this->delays as $id => $delay) { + if ($currentTime >= $delay) { + unset($this->delays[$id]); + curl_multi_add_handle( + $this->_mh, + $this->handles[$id]['easy']->handle + ); + } + } + } + + // Step through the task queue which may add additional requests. + P\queue()->run(); + + if ($this->active && + curl_multi_select($this->_mh, $this->selectTimeout) === -1 + ) { + // Perform a usleep if a select returns -1. + // See: https://bugs.php.net/bug.php?id=61141 + usleep(250); + } + + while (curl_multi_exec($this->_mh, $this->active) === CURLM_CALL_MULTI_PERFORM); + + $this->processMessages(); + } + + /** + * Runs until all outstanding connections have completed. + */ + public function execute() + { + $queue = P\queue(); + + while ($this->handles || !$queue->isEmpty()) { + // If there are no transfers, then sleep for the next delay + if (!$this->active && $this->delays) { + usleep($this->timeToNext()); + } + $this->tick(); + } + } + + private function addRequest(array $entry) + { + $easy = $entry['easy']; + $id = (int) $easy->handle; + $this->handles[$id] = $entry; + if (empty($easy->options['delay'])) { + curl_multi_add_handle($this->_mh, $easy->handle); + } else { + $this->delays[$id] = microtime(true) + ($easy->options['delay'] / 1000); + } + } + + /** + * Cancels a handle from sending and removes references to it. + * + * @param int $id Handle ID to cancel and remove. + * + * @return bool True on success, false on failure. + */ + private function cancel($id) + { + // Cannot cancel if it has been processed. + if (!isset($this->handles[$id])) { + return false; + } + + $handle = $this->handles[$id]['easy']->handle; + unset($this->delays[$id], $this->handles[$id]); + curl_multi_remove_handle($this->_mh, $handle); + curl_close($handle); + + return true; + } + + private function processMessages() + { + while ($done = curl_multi_info_read($this->_mh)) { + $id = (int) $done['handle']; + curl_multi_remove_handle($this->_mh, $done['handle']); + + if (!isset($this->handles[$id])) { + // Probably was cancelled. + continue; + } + + $entry = $this->handles[$id]; + unset($this->handles[$id], $this->delays[$id]); + $entry['easy']->errno = $done['result']; + $entry['deferred']->resolve( + CurlFactory::finish( + $this, + $entry['easy'], + $this->factory + ) + ); + } + } + + private function timeToNext() + { + $currentTime = microtime(true); + $nextTime = PHP_INT_MAX; + foreach ($this->delays as $time) { + if ($time < $nextTime) { + $nextTime = $time; + } + } + + return max(0, $nextTime - $currentTime) * 1000000; + } +} diff --git a/vendor/guzzlehttp/guzzle/src/Handler/EasyHandle.php b/vendor/guzzlehttp/guzzle/src/Handler/EasyHandle.php new file mode 100644 index 00000000000..7754e9111b7 --- /dev/null +++ b/vendor/guzzlehttp/guzzle/src/Handler/EasyHandle.php @@ -0,0 +1,92 @@ +<?php +namespace GuzzleHttp\Handler; + +use GuzzleHttp\Psr7\Response; +use Psr\Http\Message\RequestInterface; +use Psr\Http\Message\ResponseInterface; +use Psr\Http\Message\StreamInterface; + +/** + * Represents a cURL easy handle and the data it populates. + * + * @internal + */ +final class EasyHandle +{ + /** @var resource cURL resource */ + public $handle; + + /** @var StreamInterface Where data is being written */ + public $sink; + + /** @var array Received HTTP headers so far */ + public $headers = []; + + /** @var ResponseInterface Received response (if any) */ + public $response; + + /** @var RequestInterface Request being sent */ + public $request; + + /** @var array Request options */ + public $options = []; + + /** @var int cURL error number (if any) */ + public $errno = 0; + + /** @var \Exception Exception during on_headers (if any) */ + public $onHeadersException; + + /** + * Attach a response to the easy handle based on the received headers. + * + * @throws \RuntimeException if no headers have been received. + */ + public function createResponse() + { + if (empty($this->headers)) { + throw new \RuntimeException('No headers have been received'); + } + + // HTTP-version SP status-code SP reason-phrase + $startLine = explode(' ', array_shift($this->headers), 3); + $headers = \GuzzleHttp\headers_from_lines($this->headers); + $normalizedKeys = \GuzzleHttp\normalize_header_keys($headers); + + if (!empty($this->options['decode_content']) + && isset($normalizedKeys['content-encoding']) + ) { + $headers['x-encoded-content-encoding'] + = $headers[$normalizedKeys['content-encoding']]; + unset($headers[$normalizedKeys['content-encoding']]); + if (isset($normalizedKeys['content-length'])) { + $headers['x-encoded-content-length'] + = $headers[$normalizedKeys['content-length']]; + + $bodyLength = (int) $this->sink->getSize(); + if ($bodyLength) { + $headers[$normalizedKeys['content-length']] = $bodyLength; + } else { + unset($headers[$normalizedKeys['content-length']]); + } + } + } + + // Attach a response to the easy handle with the parsed headers. + $this->response = new Response( + $startLine[1], + $headers, + $this->sink, + substr($startLine[0], 5), + isset($startLine[2]) ? (string) $startLine[2] : null + ); + } + + public function __get($name) + { + $msg = $name === 'handle' + ? 'The EasyHandle has been released' + : 'Invalid property: ' . $name; + throw new \BadMethodCallException($msg); + } +} diff --git a/vendor/guzzlehttp/guzzle/src/Handler/MockHandler.php b/vendor/guzzlehttp/guzzle/src/Handler/MockHandler.php new file mode 100644 index 00000000000..d892061c7a0 --- /dev/null +++ b/vendor/guzzlehttp/guzzle/src/Handler/MockHandler.php @@ -0,0 +1,189 @@ +<?php +namespace GuzzleHttp\Handler; + +use GuzzleHttp\Exception\RequestException; +use GuzzleHttp\HandlerStack; +use GuzzleHttp\Promise\PromiseInterface; +use GuzzleHttp\Promise\RejectedPromise; +use GuzzleHttp\TransferStats; +use Psr\Http\Message\RequestInterface; +use Psr\Http\Message\ResponseInterface; + +/** + * Handler that returns responses or throw exceptions from a queue. + */ +class MockHandler implements \Countable +{ + private $queue = []; + private $lastRequest; + private $lastOptions; + private $onFulfilled; + private $onRejected; + + /** + * Creates a new MockHandler that uses the default handler stack list of + * middlewares. + * + * @param array $queue Array of responses, callables, or exceptions. + * @param callable $onFulfilled Callback to invoke when the return value is fulfilled. + * @param callable $onRejected Callback to invoke when the return value is rejected. + * + * @return HandlerStack + */ + public static function createWithMiddleware( + array $queue = null, + callable $onFulfilled = null, + callable $onRejected = null + ) { + return HandlerStack::create(new self($queue, $onFulfilled, $onRejected)); + } + + /** + * The passed in value must be an array of + * {@see Psr7\Http\Message\ResponseInterface} objects, Exceptions, + * callables, or Promises. + * + * @param array $queue + * @param callable $onFulfilled Callback to invoke when the return value is fulfilled. + * @param callable $onRejected Callback to invoke when the return value is rejected. + */ + public function __construct( + array $queue = null, + callable $onFulfilled = null, + callable $onRejected = null + ) { + $this->onFulfilled = $onFulfilled; + $this->onRejected = $onRejected; + + if ($queue) { + call_user_func_array([$this, 'append'], $queue); + } + } + + public function __invoke(RequestInterface $request, array $options) + { + if (!$this->queue) { + throw new \OutOfBoundsException('Mock queue is empty'); + } + + if (isset($options['delay'])) { + usleep($options['delay'] * 1000); + } + + $this->lastRequest = $request; + $this->lastOptions = $options; + $response = array_shift($this->queue); + + if (isset($options['on_headers'])) { + if (!is_callable($options['on_headers'])) { + throw new \InvalidArgumentException('on_headers must be callable'); + } + try { + $options['on_headers']($response); + } catch (\Exception $e) { + $msg = 'An error was encountered during the on_headers event'; + $response = new RequestException($msg, $request, $response, $e); + } + } + + if (is_callable($response)) { + $response = call_user_func($response, $request, $options); + } + + $response = $response instanceof \Exception + ? \GuzzleHttp\Promise\rejection_for($response) + : \GuzzleHttp\Promise\promise_for($response); + + return $response->then( + function ($value) use ($request, $options) { + $this->invokeStats($request, $options, $value); + if ($this->onFulfilled) { + call_user_func($this->onFulfilled, $value); + } + if (isset($options['sink'])) { + $contents = (string) $value->getBody(); + $sink = $options['sink']; + + if (is_resource($sink)) { + fwrite($sink, $contents); + } elseif (is_string($sink)) { + file_put_contents($sink, $contents); + } elseif ($sink instanceof \Psr\Http\Message\StreamInterface) { + $sink->write($contents); + } + } + + return $value; + }, + function ($reason) use ($request, $options) { + $this->invokeStats($request, $options, null, $reason); + if ($this->onRejected) { + call_user_func($this->onRejected, $reason); + } + return \GuzzleHttp\Promise\rejection_for($reason); + } + ); + } + + /** + * Adds one or more variadic requests, exceptions, callables, or promises + * to the queue. + */ + public function append() + { + foreach (func_get_args() as $value) { + if ($value instanceof ResponseInterface + || $value instanceof \Exception + || $value instanceof PromiseInterface + || is_callable($value) + ) { + $this->queue[] = $value; + } else { + throw new \InvalidArgumentException('Expected a response or ' + . 'exception. Found ' . \GuzzleHttp\describe_type($value)); + } + } + } + + /** + * Get the last received request. + * + * @return RequestInterface + */ + public function getLastRequest() + { + return $this->lastRequest; + } + + /** + * Get the last received request options. + * + * @return array + */ + public function getLastOptions() + { + return $this->lastOptions; + } + + /** + * Returns the number of remaining items in the queue. + * + * @return int + */ + public function count() + { + return count($this->queue); + } + + private function invokeStats( + RequestInterface $request, + array $options, + ResponseInterface $response = null, + $reason = null + ) { + if (isset($options['on_stats'])) { + $stats = new TransferStats($request, $response, 0, $reason); + call_user_func($options['on_stats'], $stats); + } + } +} diff --git a/vendor/guzzlehttp/guzzle/src/Handler/Proxy.php b/vendor/guzzlehttp/guzzle/src/Handler/Proxy.php new file mode 100644 index 00000000000..f8b00be0b99 --- /dev/null +++ b/vendor/guzzlehttp/guzzle/src/Handler/Proxy.php @@ -0,0 +1,55 @@ +<?php +namespace GuzzleHttp\Handler; + +use GuzzleHttp\RequestOptions; +use Psr\Http\Message\RequestInterface; + +/** + * Provides basic proxies for handlers. + */ +class Proxy +{ + /** + * Sends synchronous requests to a specific handler while sending all other + * requests to another handler. + * + * @param callable $default Handler used for normal responses + * @param callable $sync Handler used for synchronous responses. + * + * @return callable Returns the composed handler. + */ + public static function wrapSync( + callable $default, + callable $sync + ) { + return function (RequestInterface $request, array $options) use ($default, $sync) { + return empty($options[RequestOptions::SYNCHRONOUS]) + ? $default($request, $options) + : $sync($request, $options); + }; + } + + /** + * Sends streaming requests to a streaming compatible handler while sending + * all other requests to a default handler. + * + * This, for example, could be useful for taking advantage of the + * performance benefits of curl while still supporting true streaming + * through the StreamHandler. + * + * @param callable $default Handler used for non-streaming responses + * @param callable $streaming Handler used for streaming responses + * + * @return callable Returns the composed handler. + */ + public static function wrapStreaming( + callable $default, + callable $streaming + ) { + return function (RequestInterface $request, array $options) use ($default, $streaming) { + return empty($options['stream']) + ? $default($request, $options) + : $streaming($request, $options); + }; + } +} diff --git a/vendor/guzzlehttp/guzzle/src/Handler/StreamHandler.php b/vendor/guzzlehttp/guzzle/src/Handler/StreamHandler.php new file mode 100644 index 00000000000..b686545ea7f --- /dev/null +++ b/vendor/guzzlehttp/guzzle/src/Handler/StreamHandler.php @@ -0,0 +1,532 @@ +<?php +namespace GuzzleHttp\Handler; + +use GuzzleHttp\Exception\RequestException; +use GuzzleHttp\Exception\ConnectException; +use GuzzleHttp\Promise\FulfilledPromise; +use GuzzleHttp\Promise\PromiseInterface; +use GuzzleHttp\Psr7; +use GuzzleHttp\TransferStats; +use Psr\Http\Message\RequestInterface; +use Psr\Http\Message\ResponseInterface; +use Psr\Http\Message\StreamInterface; + +/** + * HTTP handler that uses PHP's HTTP stream wrapper. + */ +class StreamHandler +{ + private $lastHeaders = []; + + /** + * Sends an HTTP request. + * + * @param RequestInterface $request Request to send. + * @param array $options Request transfer options. + * + * @return PromiseInterface + */ + public function __invoke(RequestInterface $request, array $options) + { + // Sleep if there is a delay specified. + if (isset($options['delay'])) { + usleep($options['delay'] * 1000); + } + + $startTime = isset($options['on_stats']) ? microtime(true) : null; + + try { + // Does not support the expect header. + $request = $request->withoutHeader('Expect'); + + // Append a content-length header if body size is zero to match + // cURL's behavior. + if (0 === $request->getBody()->getSize()) { + $request = $request->withHeader('Content-Length', 0); + } + + return $this->createResponse( + $request, + $options, + $this->createStream($request, $options), + $startTime + ); + } catch (\InvalidArgumentException $e) { + throw $e; + } catch (\Exception $e) { + // Determine if the error was a networking error. + $message = $e->getMessage(); + // This list can probably get more comprehensive. + if (strpos($message, 'getaddrinfo') // DNS lookup failed + || strpos($message, 'Connection refused') + || strpos($message, "couldn't connect to host") // error on HHVM + || strpos($message, "connection attempt failed") + ) { + $e = new ConnectException($e->getMessage(), $request, $e); + } + $e = RequestException::wrapException($request, $e); + $this->invokeStats($options, $request, $startTime, null, $e); + + return \GuzzleHttp\Promise\rejection_for($e); + } + } + + private function invokeStats( + array $options, + RequestInterface $request, + $startTime, + ResponseInterface $response = null, + $error = null + ) { + if (isset($options['on_stats'])) { + $stats = new TransferStats( + $request, + $response, + microtime(true) - $startTime, + $error, + [] + ); + call_user_func($options['on_stats'], $stats); + } + } + + private function createResponse( + RequestInterface $request, + array $options, + $stream, + $startTime + ) { + $hdrs = $this->lastHeaders; + $this->lastHeaders = []; + $parts = explode(' ', array_shift($hdrs), 3); + $ver = explode('/', $parts[0])[1]; + $status = $parts[1]; + $reason = isset($parts[2]) ? $parts[2] : null; + $headers = \GuzzleHttp\headers_from_lines($hdrs); + list($stream, $headers) = $this->checkDecode($options, $headers, $stream); + $stream = Psr7\stream_for($stream); + $sink = $stream; + + if (strcasecmp('HEAD', $request->getMethod())) { + $sink = $this->createSink($stream, $options); + } + + $response = new Psr7\Response($status, $headers, $sink, $ver, $reason); + + if (isset($options['on_headers'])) { + try { + $options['on_headers']($response); + } catch (\Exception $e) { + $msg = 'An error was encountered during the on_headers event'; + $ex = new RequestException($msg, $request, $response, $e); + return \GuzzleHttp\Promise\rejection_for($ex); + } + } + + // Do not drain when the request is a HEAD request because they have + // no body. + if ($sink !== $stream) { + $this->drain( + $stream, + $sink, + $response->getHeaderLine('Content-Length') + ); + } + + $this->invokeStats($options, $request, $startTime, $response, null); + + return new FulfilledPromise($response); + } + + private function createSink(StreamInterface $stream, array $options) + { + if (!empty($options['stream'])) { + return $stream; + } + + $sink = isset($options['sink']) + ? $options['sink'] + : fopen('php://temp', 'r+'); + + return is_string($sink) + ? new Psr7\LazyOpenStream($sink, 'w+') + : Psr7\stream_for($sink); + } + + private function checkDecode(array $options, array $headers, $stream) + { + // Automatically decode responses when instructed. + if (!empty($options['decode_content'])) { + $normalizedKeys = \GuzzleHttp\normalize_header_keys($headers); + if (isset($normalizedKeys['content-encoding'])) { + $encoding = $headers[$normalizedKeys['content-encoding']]; + if ($encoding[0] === 'gzip' || $encoding[0] === 'deflate') { + $stream = new Psr7\InflateStream( + Psr7\stream_for($stream) + ); + $headers['x-encoded-content-encoding'] + = $headers[$normalizedKeys['content-encoding']]; + // Remove content-encoding header + unset($headers[$normalizedKeys['content-encoding']]); + // Fix content-length header + if (isset($normalizedKeys['content-length'])) { + $headers['x-encoded-content-length'] + = $headers[$normalizedKeys['content-length']]; + + $length = (int) $stream->getSize(); + if ($length === 0) { + unset($headers[$normalizedKeys['content-length']]); + } else { + $headers[$normalizedKeys['content-length']] = [$length]; + } + } + } + } + } + + return [$stream, $headers]; + } + + /** + * Drains the source stream into the "sink" client option. + * + * @param StreamInterface $source + * @param StreamInterface $sink + * @param string $contentLength Header specifying the amount of + * data to read. + * + * @return StreamInterface + * @throws \RuntimeException when the sink option is invalid. + */ + private function drain( + StreamInterface $source, + StreamInterface $sink, + $contentLength + ) { + // If a content-length header is provided, then stop reading once + // that number of bytes has been read. This can prevent infinitely + // reading from a stream when dealing with servers that do not honor + // Connection: Close headers. + Psr7\copy_to_stream( + $source, + $sink, + (strlen($contentLength) > 0 && (int) $contentLength > 0) ? (int) $contentLength : -1 + ); + + $sink->seek(0); + $source->close(); + + return $sink; + } + + /** + * Create a resource and check to ensure it was created successfully + * + * @param callable $callback Callable that returns stream resource + * + * @return resource + * @throws \RuntimeException on error + */ + private function createResource(callable $callback) + { + $errors = null; + set_error_handler(function ($_, $msg, $file, $line) use (&$errors) { + $errors[] = [ + 'message' => $msg, + 'file' => $file, + 'line' => $line + ]; + return true; + }); + + $resource = $callback(); + restore_error_handler(); + + if (!$resource) { + $message = 'Error creating resource: '; + foreach ($errors as $err) { + foreach ($err as $key => $value) { + $message .= "[$key] $value" . PHP_EOL; + } + } + throw new \RuntimeException(trim($message)); + } + + return $resource; + } + + private function createStream(RequestInterface $request, array $options) + { + static $methods; + if (!$methods) { + $methods = array_flip(get_class_methods(__CLASS__)); + } + + // HTTP/1.1 streams using the PHP stream wrapper require a + // Connection: close header + if ($request->getProtocolVersion() == '1.1' + && !$request->hasHeader('Connection') + ) { + $request = $request->withHeader('Connection', 'close'); + } + + // Ensure SSL is verified by default + if (!isset($options['verify'])) { + $options['verify'] = true; + } + + $params = []; + $context = $this->getDefaultContext($request); + + if (isset($options['on_headers']) && !is_callable($options['on_headers'])) { + throw new \InvalidArgumentException('on_headers must be callable'); + } + + if (!empty($options)) { + foreach ($options as $key => $value) { + $method = "add_{$key}"; + if (isset($methods[$method])) { + $this->{$method}($request, $context, $value, $params); + } + } + } + + if (isset($options['stream_context'])) { + if (!is_array($options['stream_context'])) { + throw new \InvalidArgumentException('stream_context must be an array'); + } + $context = array_replace_recursive( + $context, + $options['stream_context'] + ); + } + + // Microsoft NTLM authentication only supported with curl handler + if (isset($options['auth']) + && is_array($options['auth']) + && isset($options['auth'][2]) + && 'ntlm' == $options['auth'][2] + ) { + throw new \InvalidArgumentException('Microsoft NTLM authentication only supported with curl handler'); + } + + $uri = $this->resolveHost($request, $options); + + $context = $this->createResource( + function () use ($context, $params) { + return stream_context_create($context, $params); + } + ); + + return $this->createResource( + function () use ($uri, &$http_response_header, $context, $options) { + $resource = fopen((string) $uri, 'r', null, $context); + $this->lastHeaders = $http_response_header; + + if (isset($options['read_timeout'])) { + $readTimeout = $options['read_timeout']; + $sec = (int) $readTimeout; + $usec = ($readTimeout - $sec) * 100000; + stream_set_timeout($resource, $sec, $usec); + } + + return $resource; + } + ); + } + + private function resolveHost(RequestInterface $request, array $options) + { + $uri = $request->getUri(); + + if (isset($options['force_ip_resolve']) && !filter_var($uri->getHost(), FILTER_VALIDATE_IP)) { + if ('v4' === $options['force_ip_resolve']) { + $records = dns_get_record($uri->getHost(), DNS_A); + if (!isset($records[0]['ip'])) { + throw new ConnectException(sprintf("Could not resolve IPv4 address for host '%s'", $uri->getHost()), $request); + } + $uri = $uri->withHost($records[0]['ip']); + } elseif ('v6' === $options['force_ip_resolve']) { + $records = dns_get_record($uri->getHost(), DNS_AAAA); + if (!isset($records[0]['ipv6'])) { + throw new ConnectException(sprintf("Could not resolve IPv6 address for host '%s'", $uri->getHost()), $request); + } + $uri = $uri->withHost('[' . $records[0]['ipv6'] . ']'); + } + } + + return $uri; + } + + private function getDefaultContext(RequestInterface $request) + { + $headers = ''; + foreach ($request->getHeaders() as $name => $value) { + foreach ($value as $val) { + $headers .= "$name: $val\r\n"; + } + } + + $context = [ + 'http' => [ + 'method' => $request->getMethod(), + 'header' => $headers, + 'protocol_version' => $request->getProtocolVersion(), + 'ignore_errors' => true, + 'follow_location' => 0, + ], + ]; + + $body = (string) $request->getBody(); + + if (!empty($body)) { + $context['http']['content'] = $body; + // Prevent the HTTP handler from adding a Content-Type header. + if (!$request->hasHeader('Content-Type')) { + $context['http']['header'] .= "Content-Type:\r\n"; + } + } + + $context['http']['header'] = rtrim($context['http']['header']); + + return $context; + } + + private function add_proxy(RequestInterface $request, &$options, $value, &$params) + { + if (!is_array($value)) { + $options['http']['proxy'] = $value; + } else { + $scheme = $request->getUri()->getScheme(); + if (isset($value[$scheme])) { + if (!isset($value['no']) + || !\GuzzleHttp\is_host_in_noproxy( + $request->getUri()->getHost(), + $value['no'] + ) + ) { + $options['http']['proxy'] = $value[$scheme]; + } + } + } + } + + private function add_timeout(RequestInterface $request, &$options, $value, &$params) + { + if ($value > 0) { + $options['http']['timeout'] = $value; + } + } + + private function add_verify(RequestInterface $request, &$options, $value, &$params) + { + if ($value === true) { + // PHP 5.6 or greater will find the system cert by default. When + // < 5.6, use the Guzzle bundled cacert. + if (PHP_VERSION_ID < 50600) { + $options['ssl']['cafile'] = \GuzzleHttp\default_ca_bundle(); + } + } elseif (is_string($value)) { + $options['ssl']['cafile'] = $value; + if (!file_exists($value)) { + throw new \RuntimeException("SSL CA bundle not found: $value"); + } + } elseif ($value === false) { + $options['ssl']['verify_peer'] = false; + $options['ssl']['verify_peer_name'] = false; + return; + } else { + throw new \InvalidArgumentException('Invalid verify request option'); + } + + $options['ssl']['verify_peer'] = true; + $options['ssl']['verify_peer_name'] = true; + $options['ssl']['allow_self_signed'] = false; + } + + private function add_cert(RequestInterface $request, &$options, $value, &$params) + { + if (is_array($value)) { + $options['ssl']['passphrase'] = $value[1]; + $value = $value[0]; + } + + if (!file_exists($value)) { + throw new \RuntimeException("SSL certificate not found: {$value}"); + } + + $options['ssl']['local_cert'] = $value; + } + + private function add_progress(RequestInterface $request, &$options, $value, &$params) + { + $this->addNotification( + $params, + function ($code, $a, $b, $c, $transferred, $total) use ($value) { + if ($code == STREAM_NOTIFY_PROGRESS) { + $value($total, $transferred, null, null); + } + } + ); + } + + private function add_debug(RequestInterface $request, &$options, $value, &$params) + { + if ($value === false) { + return; + } + + static $map = [ + STREAM_NOTIFY_CONNECT => 'CONNECT', + STREAM_NOTIFY_AUTH_REQUIRED => 'AUTH_REQUIRED', + STREAM_NOTIFY_AUTH_RESULT => 'AUTH_RESULT', + STREAM_NOTIFY_MIME_TYPE_IS => 'MIME_TYPE_IS', + STREAM_NOTIFY_FILE_SIZE_IS => 'FILE_SIZE_IS', + STREAM_NOTIFY_REDIRECTED => 'REDIRECTED', + STREAM_NOTIFY_PROGRESS => 'PROGRESS', + STREAM_NOTIFY_FAILURE => 'FAILURE', + STREAM_NOTIFY_COMPLETED => 'COMPLETED', + STREAM_NOTIFY_RESOLVE => 'RESOLVE', + ]; + static $args = ['severity', 'message', 'message_code', + 'bytes_transferred', 'bytes_max']; + + $value = \GuzzleHttp\debug_resource($value); + $ident = $request->getMethod() . ' ' . $request->getUri()->withFragment(''); + $this->addNotification( + $params, + function () use ($ident, $value, $map, $args) { + $passed = func_get_args(); + $code = array_shift($passed); + fprintf($value, '<%s> [%s] ', $ident, $map[$code]); + foreach (array_filter($passed) as $i => $v) { + fwrite($value, $args[$i] . ': "' . $v . '" '); + } + fwrite($value, "\n"); + } + ); + } + + private function addNotification(array &$params, callable $notify) + { + // Wrap the existing function if needed. + if (!isset($params['notification'])) { + $params['notification'] = $notify; + } else { + $params['notification'] = $this->callArray([ + $params['notification'], + $notify + ]); + } + } + + private function callArray(array $functions) + { + return function () use ($functions) { + $args = func_get_args(); + foreach ($functions as $fn) { + call_user_func_array($fn, $args); + } + }; + } +} diff --git a/vendor/guzzlehttp/guzzle/src/HandlerStack.php b/vendor/guzzlehttp/guzzle/src/HandlerStack.php new file mode 100644 index 00000000000..24c46fd9fea --- /dev/null +++ b/vendor/guzzlehttp/guzzle/src/HandlerStack.php @@ -0,0 +1,273 @@ +<?php +namespace GuzzleHttp; + +use Psr\Http\Message\RequestInterface; + +/** + * Creates a composed Guzzle handler function by stacking middlewares on top of + * an HTTP handler function. + */ +class HandlerStack +{ + /** @var callable */ + private $handler; + + /** @var array */ + private $stack = []; + + /** @var callable|null */ + private $cached; + + /** + * Creates a default handler stack that can be used by clients. + * + * The returned handler will wrap the provided handler or use the most + * appropriate default handler for your system. The returned HandlerStack has + * support for cookies, redirects, HTTP error exceptions, and preparing a body + * before sending. + * + * The returned handler stack can be passed to a client in the "handler" + * option. + * + * @param callable $handler HTTP handler function to use with the stack. If no + * handler is provided, the best handler for your + * system will be utilized. + * + * @return HandlerStack + */ + public static function create(callable $handler = null) + { + $stack = new self($handler ?: choose_handler()); + $stack->push(Middleware::httpErrors(), 'http_errors'); + $stack->push(Middleware::redirect(), 'allow_redirects'); + $stack->push(Middleware::cookies(), 'cookies'); + $stack->push(Middleware::prepareBody(), 'prepare_body'); + + return $stack; + } + + /** + * @param callable $handler Underlying HTTP handler. + */ + public function __construct(callable $handler = null) + { + $this->handler = $handler; + } + + /** + * Invokes the handler stack as a composed handler + * + * @param RequestInterface $request + * @param array $options + */ + public function __invoke(RequestInterface $request, array $options) + { + $handler = $this->resolve(); + + return $handler($request, $options); + } + + /** + * Dumps a string representation of the stack. + * + * @return string + */ + public function __toString() + { + $depth = 0; + $stack = []; + if ($this->handler) { + $stack[] = "0) Handler: " . $this->debugCallable($this->handler); + } + + $result = ''; + foreach (array_reverse($this->stack) as $tuple) { + $depth++; + $str = "{$depth}) Name: '{$tuple[1]}', "; + $str .= "Function: " . $this->debugCallable($tuple[0]); + $result = "> {$str}\n{$result}"; + $stack[] = $str; + } + + foreach (array_keys($stack) as $k) { + $result .= "< {$stack[$k]}\n"; + } + + return $result; + } + + /** + * Set the HTTP handler that actually returns a promise. + * + * @param callable $handler Accepts a request and array of options and + * returns a Promise. + */ + public function setHandler(callable $handler) + { + $this->handler = $handler; + $this->cached = null; + } + + /** + * Returns true if the builder has a handler. + * + * @return bool + */ + public function hasHandler() + { + return (bool) $this->handler; + } + + /** + * Unshift a middleware to the bottom of the stack. + * + * @param callable $middleware Middleware function + * @param string $name Name to register for this middleware. + */ + public function unshift(callable $middleware, $name = null) + { + array_unshift($this->stack, [$middleware, $name]); + $this->cached = null; + } + + /** + * Push a middleware to the top of the stack. + * + * @param callable $middleware Middleware function + * @param string $name Name to register for this middleware. + */ + public function push(callable $middleware, $name = '') + { + $this->stack[] = [$middleware, $name]; + $this->cached = null; + } + + /** + * Add a middleware before another middleware by name. + * + * @param string $findName Middleware to find + * @param callable $middleware Middleware function + * @param string $withName Name to register for this middleware. + */ + public function before($findName, callable $middleware, $withName = '') + { + $this->splice($findName, $withName, $middleware, true); + } + + /** + * Add a middleware after another middleware by name. + * + * @param string $findName Middleware to find + * @param callable $middleware Middleware function + * @param string $withName Name to register for this middleware. + */ + public function after($findName, callable $middleware, $withName = '') + { + $this->splice($findName, $withName, $middleware, false); + } + + /** + * Remove a middleware by instance or name from the stack. + * + * @param callable|string $remove Middleware to remove by instance or name. + */ + public function remove($remove) + { + $this->cached = null; + $idx = is_callable($remove) ? 0 : 1; + $this->stack = array_values(array_filter( + $this->stack, + function ($tuple) use ($idx, $remove) { + return $tuple[$idx] !== $remove; + } + )); + } + + /** + * Compose the middleware and handler into a single callable function. + * + * @return callable + */ + public function resolve() + { + if (!$this->cached) { + if (!($prev = $this->handler)) { + throw new \LogicException('No handler has been specified'); + } + + foreach (array_reverse($this->stack) as $fn) { + $prev = $fn[0]($prev); + } + + $this->cached = $prev; + } + + return $this->cached; + } + + /** + * @param $name + * @return int + */ + private function findByName($name) + { + foreach ($this->stack as $k => $v) { + if ($v[1] === $name) { + return $k; + } + } + + throw new \InvalidArgumentException("Middleware not found: $name"); + } + + /** + * Splices a function into the middleware list at a specific position. + * + * @param $findName + * @param $withName + * @param callable $middleware + * @param $before + */ + private function splice($findName, $withName, callable $middleware, $before) + { + $this->cached = null; + $idx = $this->findByName($findName); + $tuple = [$middleware, $withName]; + + if ($before) { + if ($idx === 0) { + array_unshift($this->stack, $tuple); + } else { + $replacement = [$tuple, $this->stack[$idx]]; + array_splice($this->stack, $idx, 1, $replacement); + } + } elseif ($idx === count($this->stack) - 1) { + $this->stack[] = $tuple; + } else { + $replacement = [$this->stack[$idx], $tuple]; + array_splice($this->stack, $idx, 1, $replacement); + } + } + + /** + * Provides a debug string for a given callable. + * + * @param array|callable $fn Function to write as a string. + * + * @return string + */ + private function debugCallable($fn) + { + if (is_string($fn)) { + return "callable({$fn})"; + } + + if (is_array($fn)) { + return is_string($fn[0]) + ? "callable({$fn[0]}::{$fn[1]})" + : "callable(['" . get_class($fn[0]) . "', '{$fn[1]}'])"; + } + + return 'callable(' . spl_object_hash($fn) . ')'; + } +} diff --git a/vendor/guzzlehttp/guzzle/src/MessageFormatter.php b/vendor/guzzlehttp/guzzle/src/MessageFormatter.php new file mode 100644 index 00000000000..663ac739163 --- /dev/null +++ b/vendor/guzzlehttp/guzzle/src/MessageFormatter.php @@ -0,0 +1,180 @@ +<?php +namespace GuzzleHttp; + +use Psr\Http\Message\MessageInterface; +use Psr\Http\Message\RequestInterface; +use Psr\Http\Message\ResponseInterface; + +/** + * Formats log messages using variable substitutions for requests, responses, + * and other transactional data. + * + * The following variable substitutions are supported: + * + * - {request}: Full HTTP request message + * - {response}: Full HTTP response message + * - {ts}: ISO 8601 date in GMT + * - {date_iso_8601} ISO 8601 date in GMT + * - {date_common_log} Apache common log date using the configured timezone. + * - {host}: Host of the request + * - {method}: Method of the request + * - {uri}: URI of the request + * - {version}: Protocol version + * - {target}: Request target of the request (path + query + fragment) + * - {hostname}: Hostname of the machine that sent the request + * - {code}: Status code of the response (if available) + * - {phrase}: Reason phrase of the response (if available) + * - {error}: Any error messages (if available) + * - {req_header_*}: Replace `*` with the lowercased name of a request header to add to the message + * - {res_header_*}: Replace `*` with the lowercased name of a response header to add to the message + * - {req_headers}: Request headers + * - {res_headers}: Response headers + * - {req_body}: Request body + * - {res_body}: Response body + */ +class MessageFormatter +{ + /** + * Apache Common Log Format. + * @link http://httpd.apache.org/docs/2.4/logs.html#common + * @var string + */ + const CLF = "{hostname} {req_header_User-Agent} - [{date_common_log}] \"{method} {target} HTTP/{version}\" {code} {res_header_Content-Length}"; + const DEBUG = ">>>>>>>>\n{request}\n<<<<<<<<\n{response}\n--------\n{error}"; + const SHORT = '[{ts}] "{method} {target} HTTP/{version}" {code}'; + + /** @var string Template used to format log messages */ + private $template; + + /** + * @param string $template Log message template + */ + public function __construct($template = self::CLF) + { + $this->template = $template ?: self::CLF; + } + + /** + * Returns a formatted message string. + * + * @param RequestInterface $request Request that was sent + * @param ResponseInterface $response Response that was received + * @param \Exception $error Exception that was received + * + * @return string + */ + public function format( + RequestInterface $request, + ResponseInterface $response = null, + \Exception $error = null + ) { + $cache = []; + + return preg_replace_callback( + '/{\s*([A-Za-z_\-\.0-9]+)\s*}/', + function (array $matches) use ($request, $response, $error, &$cache) { + if (isset($cache[$matches[1]])) { + return $cache[$matches[1]]; + } + + $result = ''; + switch ($matches[1]) { + case 'request': + $result = Psr7\str($request); + break; + case 'response': + $result = $response ? Psr7\str($response) : ''; + break; + case 'req_headers': + $result = trim($request->getMethod() + . ' ' . $request->getRequestTarget()) + . ' HTTP/' . $request->getProtocolVersion() . "\r\n" + . $this->headers($request); + break; + case 'res_headers': + $result = $response ? + sprintf( + 'HTTP/%s %d %s', + $response->getProtocolVersion(), + $response->getStatusCode(), + $response->getReasonPhrase() + ) . "\r\n" . $this->headers($response) + : 'NULL'; + break; + case 'req_body': + $result = $request->getBody(); + break; + case 'res_body': + $result = $response ? $response->getBody() : 'NULL'; + break; + case 'ts': + case 'date_iso_8601': + $result = gmdate('c'); + break; + case 'date_common_log': + $result = date('d/M/Y:H:i:s O'); + break; + case 'method': + $result = $request->getMethod(); + break; + case 'version': + $result = $request->getProtocolVersion(); + break; + case 'uri': + case 'url': + $result = $request->getUri(); + break; + case 'target': + $result = $request->getRequestTarget(); + break; + case 'req_version': + $result = $request->getProtocolVersion(); + break; + case 'res_version': + $result = $response + ? $response->getProtocolVersion() + : 'NULL'; + break; + case 'host': + $result = $request->getHeaderLine('Host'); + break; + case 'hostname': + $result = gethostname(); + break; + case 'code': + $result = $response ? $response->getStatusCode() : 'NULL'; + break; + case 'phrase': + $result = $response ? $response->getReasonPhrase() : 'NULL'; + break; + case 'error': + $result = $error ? $error->getMessage() : 'NULL'; + break; + default: + // handle prefixed dynamic headers + if (strpos($matches[1], 'req_header_') === 0) { + $result = $request->getHeaderLine(substr($matches[1], 11)); + } elseif (strpos($matches[1], 'res_header_') === 0) { + $result = $response + ? $response->getHeaderLine(substr($matches[1], 11)) + : 'NULL'; + } + } + + $cache[$matches[1]] = $result; + return $result; + }, + $this->template + ); + } + + private function headers(MessageInterface $message) + { + $result = ''; + foreach ($message->getHeaders() as $name => $values) { + $result .= $name . ': ' . implode(', ', $values) . "\r\n"; + } + + return trim($result); + } +} diff --git a/vendor/guzzlehttp/guzzle/src/Middleware.php b/vendor/guzzlehttp/guzzle/src/Middleware.php new file mode 100644 index 00000000000..d4ad75c94f3 --- /dev/null +++ b/vendor/guzzlehttp/guzzle/src/Middleware.php @@ -0,0 +1,255 @@ +<?php +namespace GuzzleHttp; + +use GuzzleHttp\Cookie\CookieJarInterface; +use GuzzleHttp\Exception\RequestException; +use GuzzleHttp\Promise\RejectedPromise; +use GuzzleHttp\Psr7; +use Psr\Http\Message\ResponseInterface; +use Psr\Log\LoggerInterface; +use Psr\Log\LogLevel; + +/** + * Functions used to create and wrap handlers with handler middleware. + */ +final class Middleware +{ + /** + * Middleware that adds cookies to requests. + * + * The options array must be set to a CookieJarInterface in order to use + * cookies. This is typically handled for you by a client. + * + * @return callable Returns a function that accepts the next handler. + */ + public static function cookies() + { + return function (callable $handler) { + return function ($request, array $options) use ($handler) { + if (empty($options['cookies'])) { + return $handler($request, $options); + } elseif (!($options['cookies'] instanceof CookieJarInterface)) { + throw new \InvalidArgumentException('cookies must be an instance of GuzzleHttp\Cookie\CookieJarInterface'); + } + $cookieJar = $options['cookies']; + $request = $cookieJar->withCookieHeader($request); + return $handler($request, $options) + ->then( + function ($response) use ($cookieJar, $request) { + $cookieJar->extractCookies($request, $response); + return $response; + } + ); + }; + }; + } + + /** + * Middleware that throws exceptions for 4xx or 5xx responses when the + * "http_error" request option is set to true. + * + * @return callable Returns a function that accepts the next handler. + */ + public static function httpErrors() + { + return function (callable $handler) { + return function ($request, array $options) use ($handler) { + if (empty($options['http_errors'])) { + return $handler($request, $options); + } + return $handler($request, $options)->then( + function (ResponseInterface $response) use ($request, $handler) { + $code = $response->getStatusCode(); + if ($code < 400) { + return $response; + } + throw RequestException::create($request, $response); + } + ); + }; + }; + } + + /** + * Middleware that pushes history data to an ArrayAccess container. + * + * @param array|\ArrayAccess $container Container to hold the history (by reference). + * + * @return callable Returns a function that accepts the next handler. + * @throws \InvalidArgumentException if container is not an array or ArrayAccess. + */ + public static function history(&$container) + { + if (!is_array($container) && !$container instanceof \ArrayAccess) { + throw new \InvalidArgumentException('history container must be an array or object implementing ArrayAccess'); + } + + return function (callable $handler) use (&$container) { + return function ($request, array $options) use ($handler, &$container) { + return $handler($request, $options)->then( + function ($value) use ($request, &$container, $options) { + $container[] = [ + 'request' => $request, + 'response' => $value, + 'error' => null, + 'options' => $options + ]; + return $value; + }, + function ($reason) use ($request, &$container, $options) { + $container[] = [ + 'request' => $request, + 'response' => null, + 'error' => $reason, + 'options' => $options + ]; + return \GuzzleHttp\Promise\rejection_for($reason); + } + ); + }; + }; + } + + /** + * Middleware that invokes a callback before and after sending a request. + * + * The provided listener cannot modify or alter the response. It simply + * "taps" into the chain to be notified before returning the promise. The + * before listener accepts a request and options array, and the after + * listener accepts a request, options array, and response promise. + * + * @param callable $before Function to invoke before forwarding the request. + * @param callable $after Function invoked after forwarding. + * + * @return callable Returns a function that accepts the next handler. + */ + public static function tap(callable $before = null, callable $after = null) + { + return function (callable $handler) use ($before, $after) { + return function ($request, array $options) use ($handler, $before, $after) { + if ($before) { + $before($request, $options); + } + $response = $handler($request, $options); + if ($after) { + $after($request, $options, $response); + } + return $response; + }; + }; + } + + /** + * Middleware that handles request redirects. + * + * @return callable Returns a function that accepts the next handler. + */ + public static function redirect() + { + return function (callable $handler) { + return new RedirectMiddleware($handler); + }; + } + + /** + * Middleware that retries requests based on the boolean result of + * invoking the provided "decider" function. + * + * If no delay function is provided, a simple implementation of exponential + * backoff will be utilized. + * + * @param callable $decider Function that accepts the number of retries, + * a request, [response], and [exception] and + * returns true if the request is to be retried. + * @param callable $delay Function that accepts the number of retries and + * returns the number of milliseconds to delay. + * + * @return callable Returns a function that accepts the next handler. + */ + public static function retry(callable $decider, callable $delay = null) + { + return function (callable $handler) use ($decider, $delay) { + return new RetryMiddleware($decider, $handler, $delay); + }; + } + + /** + * Middleware that logs requests, responses, and errors using a message + * formatter. + * + * @param LoggerInterface $logger Logs messages. + * @param MessageFormatter $formatter Formatter used to create message strings. + * @param string $logLevel Level at which to log requests. + * + * @return callable Returns a function that accepts the next handler. + */ + public static function log(LoggerInterface $logger, MessageFormatter $formatter, $logLevel = LogLevel::INFO) + { + return function (callable $handler) use ($logger, $formatter, $logLevel) { + return function ($request, array $options) use ($handler, $logger, $formatter, $logLevel) { + return $handler($request, $options)->then( + function ($response) use ($logger, $request, $formatter, $logLevel) { + $message = $formatter->format($request, $response); + $logger->log($logLevel, $message); + return $response; + }, + function ($reason) use ($logger, $request, $formatter) { + $response = $reason instanceof RequestException + ? $reason->getResponse() + : null; + $message = $formatter->format($request, $response, $reason); + $logger->notice($message); + return \GuzzleHttp\Promise\rejection_for($reason); + } + ); + }; + }; + } + + /** + * This middleware adds a default content-type if possible, a default + * content-length or transfer-encoding header, and the expect header. + * + * @return callable + */ + public static function prepareBody() + { + return function (callable $handler) { + return new PrepareBodyMiddleware($handler); + }; + } + + /** + * Middleware that applies a map function to the request before passing to + * the next handler. + * + * @param callable $fn Function that accepts a RequestInterface and returns + * a RequestInterface. + * @return callable + */ + public static function mapRequest(callable $fn) + { + return function (callable $handler) use ($fn) { + return function ($request, array $options) use ($handler, $fn) { + return $handler($fn($request), $options); + }; + }; + } + + /** + * Middleware that applies a map function to the resolved promise's + * response. + * + * @param callable $fn Function that accepts a ResponseInterface and + * returns a ResponseInterface. + * @return callable + */ + public static function mapResponse(callable $fn) + { + return function (callable $handler) use ($fn) { + return function ($request, array $options) use ($handler, $fn) { + return $handler($request, $options)->then($fn); + }; + }; + } +} diff --git a/vendor/guzzlehttp/guzzle/src/Pool.php b/vendor/guzzlehttp/guzzle/src/Pool.php new file mode 100644 index 00000000000..8f1be33cd39 --- /dev/null +++ b/vendor/guzzlehttp/guzzle/src/Pool.php @@ -0,0 +1,123 @@ +<?php +namespace GuzzleHttp; + +use GuzzleHttp\Promise\PromisorInterface; +use Psr\Http\Message\RequestInterface; +use GuzzleHttp\Promise\EachPromise; + +/** + * Sends and iterator of requests concurrently using a capped pool size. + * + * The pool will read from an iterator until it is cancelled or until the + * iterator is consumed. When a request is yielded, the request is sent after + * applying the "request_options" request options (if provided in the ctor). + * + * When a function is yielded by the iterator, the function is provided the + * "request_options" array that should be merged on top of any existing + * options, and the function MUST then return a wait-able promise. + */ +class Pool implements PromisorInterface +{ + /** @var EachPromise */ + private $each; + + /** + * @param ClientInterface $client Client used to send the requests. + * @param array|\Iterator $requests Requests or functions that return + * requests to send concurrently. + * @param array $config Associative array of options + * - concurrency: (int) Maximum number of requests to send concurrently + * - options: Array of request options to apply to each request. + * - fulfilled: (callable) Function to invoke when a request completes. + * - rejected: (callable) Function to invoke when a request is rejected. + */ + public function __construct( + ClientInterface $client, + $requests, + array $config = [] + ) { + // Backwards compatibility. + if (isset($config['pool_size'])) { + $config['concurrency'] = $config['pool_size']; + } elseif (!isset($config['concurrency'])) { + $config['concurrency'] = 25; + } + + if (isset($config['options'])) { + $opts = $config['options']; + unset($config['options']); + } else { + $opts = []; + } + + $iterable = \GuzzleHttp\Promise\iter_for($requests); + $requests = function () use ($iterable, $client, $opts) { + foreach ($iterable as $key => $rfn) { + if ($rfn instanceof RequestInterface) { + yield $key => $client->sendAsync($rfn, $opts); + } elseif (is_callable($rfn)) { + yield $key => $rfn($opts); + } else { + throw new \InvalidArgumentException('Each value yielded by ' + . 'the iterator must be a Psr7\Http\Message\RequestInterface ' + . 'or a callable that returns a promise that fulfills ' + . 'with a Psr7\Message\Http\ResponseInterface object.'); + } + } + }; + + $this->each = new EachPromise($requests(), $config); + } + + public function promise() + { + return $this->each->promise(); + } + + /** + * Sends multiple requests concurrently and returns an array of responses + * and exceptions that uses the same ordering as the provided requests. + * + * IMPORTANT: This method keeps every request and response in memory, and + * as such, is NOT recommended when sending a large number or an + * indeterminate number of requests concurrently. + * + * @param ClientInterface $client Client used to send the requests + * @param array|\Iterator $requests Requests to send concurrently. + * @param array $options Passes through the options available in + * {@see GuzzleHttp\Pool::__construct} + * + * @return array Returns an array containing the response or an exception + * in the same order that the requests were sent. + * @throws \InvalidArgumentException if the event format is incorrect. + */ + public static function batch( + ClientInterface $client, + $requests, + array $options = [] + ) { + $res = []; + self::cmpCallback($options, 'fulfilled', $res); + self::cmpCallback($options, 'rejected', $res); + $pool = new static($client, $requests, $options); + $pool->promise()->wait(); + ksort($res); + + return $res; + } + + private static function cmpCallback(array &$options, $name, array &$results) + { + if (!isset($options[$name])) { + $options[$name] = function ($v, $k) use (&$results) { + $results[$k] = $v; + }; + } else { + $currentFn = $options[$name]; + $options[$name] = function ($v, $k) use (&$results, $currentFn) { + $currentFn($v, $k); + $results[$k] = $v; + }; + } + } +} diff --git a/vendor/guzzlehttp/guzzle/src/PrepareBodyMiddleware.php b/vendor/guzzlehttp/guzzle/src/PrepareBodyMiddleware.php new file mode 100644 index 00000000000..2eb95f9b2d3 --- /dev/null +++ b/vendor/guzzlehttp/guzzle/src/PrepareBodyMiddleware.php @@ -0,0 +1,106 @@ +<?php +namespace GuzzleHttp; + +use GuzzleHttp\Promise\PromiseInterface; +use GuzzleHttp\Psr7; +use Psr\Http\Message\RequestInterface; + +/** + * Prepares requests that contain a body, adding the Content-Length, + * Content-Type, and Expect headers. + */ +class PrepareBodyMiddleware +{ + /** @var callable */ + private $nextHandler; + + /** + * @param callable $nextHandler Next handler to invoke. + */ + public function __construct(callable $nextHandler) + { + $this->nextHandler = $nextHandler; + } + + /** + * @param RequestInterface $request + * @param array $options + * + * @return PromiseInterface + */ + public function __invoke(RequestInterface $request, array $options) + { + $fn = $this->nextHandler; + + // Don't do anything if the request has no body. + if ($request->getBody()->getSize() === 0) { + return $fn($request, $options); + } + + $modify = []; + + // Add a default content-type if possible. + if (!$request->hasHeader('Content-Type')) { + if ($uri = $request->getBody()->getMetadata('uri')) { + if ($type = Psr7\mimetype_from_filename($uri)) { + $modify['set_headers']['Content-Type'] = $type; + } + } + } + + // Add a default content-length or transfer-encoding header. + if (!$request->hasHeader('Content-Length') + && !$request->hasHeader('Transfer-Encoding') + ) { + $size = $request->getBody()->getSize(); + if ($size !== null) { + $modify['set_headers']['Content-Length'] = $size; + } else { + $modify['set_headers']['Transfer-Encoding'] = 'chunked'; + } + } + + // Add the expect header if needed. + $this->addExpectHeader($request, $options, $modify); + + return $fn(Psr7\modify_request($request, $modify), $options); + } + + private function addExpectHeader( + RequestInterface $request, + array $options, + array &$modify + ) { + // Determine if the Expect header should be used + if ($request->hasHeader('Expect')) { + return; + } + + $expect = isset($options['expect']) ? $options['expect'] : null; + + // Return if disabled or if you're not using HTTP/1.1 or HTTP/2.0 + if ($expect === false || $request->getProtocolVersion() < 1.1) { + return; + } + + // The expect header is unconditionally enabled + if ($expect === true) { + $modify['set_headers']['Expect'] = '100-Continue'; + return; + } + + // By default, send the expect header when the payload is > 1mb + if ($expect === null) { + $expect = 1048576; + } + + // Always add if the body cannot be rewound, the size cannot be + // determined, or the size is greater than the cutoff threshold + $body = $request->getBody(); + $size = $body->getSize(); + + if ($size === null || $size >= (int) $expect || !$body->isSeekable()) { + $modify['set_headers']['Expect'] = '100-Continue'; + } + } +} diff --git a/vendor/guzzlehttp/guzzle/src/RedirectMiddleware.php b/vendor/guzzlehttp/guzzle/src/RedirectMiddleware.php new file mode 100644 index 00000000000..131b77179aa --- /dev/null +++ b/vendor/guzzlehttp/guzzle/src/RedirectMiddleware.php @@ -0,0 +1,237 @@ +<?php +namespace GuzzleHttp; + +use GuzzleHttp\Exception\BadResponseException; +use GuzzleHttp\Exception\TooManyRedirectsException; +use GuzzleHttp\Promise\PromiseInterface; +use GuzzleHttp\Psr7; +use Psr\Http\Message\RequestInterface; +use Psr\Http\Message\ResponseInterface; +use Psr\Http\Message\UriInterface; + +/** + * Request redirect middleware. + * + * Apply this middleware like other middleware using + * {@see GuzzleHttp\Middleware::redirect()}. + */ +class RedirectMiddleware +{ + const HISTORY_HEADER = 'X-Guzzle-Redirect-History'; + + const STATUS_HISTORY_HEADER = 'X-Guzzle-Redirect-Status-History'; + + public static $defaultSettings = [ + 'max' => 5, + 'protocols' => ['http', 'https'], + 'strict' => false, + 'referer' => false, + 'track_redirects' => false, + ]; + + /** @var callable */ + private $nextHandler; + + /** + * @param callable $nextHandler Next handler to invoke. + */ + public function __construct(callable $nextHandler) + { + $this->nextHandler = $nextHandler; + } + + /** + * @param RequestInterface $request + * @param array $options + * + * @return PromiseInterface + */ + public function __invoke(RequestInterface $request, array $options) + { + $fn = $this->nextHandler; + + if (empty($options['allow_redirects'])) { + return $fn($request, $options); + } + + if ($options['allow_redirects'] === true) { + $options['allow_redirects'] = self::$defaultSettings; + } elseif (!is_array($options['allow_redirects'])) { + throw new \InvalidArgumentException('allow_redirects must be true, false, or array'); + } else { + // Merge the default settings with the provided settings + $options['allow_redirects'] += self::$defaultSettings; + } + + if (empty($options['allow_redirects']['max'])) { + return $fn($request, $options); + } + + return $fn($request, $options) + ->then(function (ResponseInterface $response) use ($request, $options) { + return $this->checkRedirect($request, $options, $response); + }); + } + + /** + * @param RequestInterface $request + * @param array $options + * @param ResponseInterface|PromiseInterface $response + * + * @return ResponseInterface|PromiseInterface + */ + public function checkRedirect( + RequestInterface $request, + array $options, + ResponseInterface $response + ) { + if (substr($response->getStatusCode(), 0, 1) != '3' + || !$response->hasHeader('Location') + ) { + return $response; + } + + $this->guardMax($request, $options); + $nextRequest = $this->modifyRequest($request, $options, $response); + + if (isset($options['allow_redirects']['on_redirect'])) { + call_user_func( + $options['allow_redirects']['on_redirect'], + $request, + $response, + $nextRequest->getUri() + ); + } + + /** @var PromiseInterface|ResponseInterface $promise */ + $promise = $this($nextRequest, $options); + + // Add headers to be able to track history of redirects. + if (!empty($options['allow_redirects']['track_redirects'])) { + return $this->withTracking( + $promise, + (string) $nextRequest->getUri(), + $response->getStatusCode() + ); + } + + return $promise; + } + + private function withTracking(PromiseInterface $promise, $uri, $statusCode) + { + return $promise->then( + function (ResponseInterface $response) use ($uri, $statusCode) { + // Note that we are pushing to the front of the list as this + // would be an earlier response than what is currently present + // in the history header. + $historyHeader = $response->getHeader(self::HISTORY_HEADER); + $statusHeader = $response->getHeader(self::STATUS_HISTORY_HEADER); + array_unshift($historyHeader, $uri); + array_unshift($statusHeader, $statusCode); + return $response->withHeader(self::HISTORY_HEADER, $historyHeader) + ->withHeader(self::STATUS_HISTORY_HEADER, $statusHeader); + } + ); + } + + private function guardMax(RequestInterface $request, array &$options) + { + $current = isset($options['__redirect_count']) + ? $options['__redirect_count'] + : 0; + $options['__redirect_count'] = $current + 1; + $max = $options['allow_redirects']['max']; + + if ($options['__redirect_count'] > $max) { + throw new TooManyRedirectsException( + "Will not follow more than {$max} redirects", + $request + ); + } + } + + /** + * @param RequestInterface $request + * @param array $options + * @param ResponseInterface $response + * + * @return RequestInterface + */ + public function modifyRequest( + RequestInterface $request, + array $options, + ResponseInterface $response + ) { + // Request modifications to apply. + $modify = []; + $protocols = $options['allow_redirects']['protocols']; + + // Use a GET request if this is an entity enclosing request and we are + // not forcing RFC compliance, but rather emulating what all browsers + // would do. + $statusCode = $response->getStatusCode(); + if ($statusCode == 303 || + ($statusCode <= 302 && $request->getBody() && !$options['allow_redirects']['strict']) + ) { + $modify['method'] = 'GET'; + $modify['body'] = ''; + } + + $modify['uri'] = $this->redirectUri($request, $response, $protocols); + Psr7\rewind_body($request); + + // Add the Referer header if it is told to do so and only + // add the header if we are not redirecting from https to http. + if ($options['allow_redirects']['referer'] + && $modify['uri']->getScheme() === $request->getUri()->getScheme() + ) { + $uri = $request->getUri()->withUserInfo('', ''); + $modify['set_headers']['Referer'] = (string) $uri; + } else { + $modify['remove_headers'][] = 'Referer'; + } + + // Remove Authorization header if host is different. + if ($request->getUri()->getHost() !== $modify['uri']->getHost()) { + $modify['remove_headers'][] = 'Authorization'; + } + + return Psr7\modify_request($request, $modify); + } + + /** + * Set the appropriate URL on the request based on the location header + * + * @param RequestInterface $request + * @param ResponseInterface $response + * @param array $protocols + * + * @return UriInterface + */ + private function redirectUri( + RequestInterface $request, + ResponseInterface $response, + array $protocols + ) { + $location = Psr7\UriResolver::resolve( + $request->getUri(), + new Psr7\Uri($response->getHeaderLine('Location')) + ); + + // Ensure that the redirect URI is allowed based on the protocols. + if (!in_array($location->getScheme(), $protocols)) { + throw new BadResponseException( + sprintf( + 'Redirect URI, %s, does not use one of the allowed redirect protocols: %s', + $location, + implode(', ', $protocols) + ), + $request, + $response + ); + } + + return $location; + } +} diff --git a/vendor/guzzlehttp/guzzle/src/RequestOptions.php b/vendor/guzzlehttp/guzzle/src/RequestOptions.php new file mode 100644 index 00000000000..c6aacfb1578 --- /dev/null +++ b/vendor/guzzlehttp/guzzle/src/RequestOptions.php @@ -0,0 +1,255 @@ +<?php +namespace GuzzleHttp; + +/** + * This class contains a list of built-in Guzzle request options. + * + * More documentation for each option can be found at http://guzzlephp.org/. + * + * @link http://docs.guzzlephp.org/en/v6/request-options.html + */ +final class RequestOptions +{ + /** + * allow_redirects: (bool|array) Controls redirect behavior. Pass false + * to disable redirects, pass true to enable redirects, pass an + * associative to provide custom redirect settings. Defaults to "false". + * This option only works if your handler has the RedirectMiddleware. When + * passing an associative array, you can provide the following key value + * pairs: + * + * - max: (int, default=5) maximum number of allowed redirects. + * - strict: (bool, default=false) Set to true to use strict redirects + * meaning redirect POST requests with POST requests vs. doing what most + * browsers do which is redirect POST requests with GET requests + * - referer: (bool, default=true) Set to false to disable the Referer + * header. + * - protocols: (array, default=['http', 'https']) Allowed redirect + * protocols. + * - on_redirect: (callable) PHP callable that is invoked when a redirect + * is encountered. The callable is invoked with the request, the redirect + * response that was received, and the effective URI. Any return value + * from the on_redirect function is ignored. + */ + const ALLOW_REDIRECTS = 'allow_redirects'; + + /** + * auth: (array) Pass an array of HTTP authentication parameters to use + * with the request. The array must contain the username in index [0], + * the password in index [1], and you can optionally provide a built-in + * authentication type in index [2]. Pass null to disable authentication + * for a request. + */ + const AUTH = 'auth'; + + /** + * body: (resource|string|null|int|float|StreamInterface|callable|\Iterator) + * Body to send in the request. + */ + const BODY = 'body'; + + /** + * cert: (string|array) Set to a string to specify the path to a file + * containing a PEM formatted SSL client side certificate. If a password + * is required, then set cert to an array containing the path to the PEM + * file in the first array element followed by the certificate password + * in the second array element. + */ + const CERT = 'cert'; + + /** + * cookies: (bool|GuzzleHttp\Cookie\CookieJarInterface, default=false) + * Specifies whether or not cookies are used in a request or what cookie + * jar to use or what cookies to send. This option only works if your + * handler has the `cookie` middleware. Valid values are `false` and + * an instance of {@see GuzzleHttp\Cookie\CookieJarInterface}. + */ + const COOKIES = 'cookies'; + + /** + * connect_timeout: (float, default=0) Float describing the number of + * seconds to wait while trying to connect to a server. Use 0 to wait + * indefinitely (the default behavior). + */ + const CONNECT_TIMEOUT = 'connect_timeout'; + + /** + * debug: (bool|resource) Set to true or set to a PHP stream returned by + * fopen() enable debug output with the HTTP handler used to send a + * request. + */ + const DEBUG = 'debug'; + + /** + * decode_content: (bool, default=true) Specify whether or not + * Content-Encoding responses (gzip, deflate, etc.) are automatically + * decoded. + */ + const DECODE_CONTENT = 'decode_content'; + + /** + * delay: (int) The amount of time to delay before sending in milliseconds. + */ + const DELAY = 'delay'; + + /** + * expect: (bool|integer) Controls the behavior of the + * "Expect: 100-Continue" header. + * + * Set to `true` to enable the "Expect: 100-Continue" header for all + * requests that sends a body. Set to `false` to disable the + * "Expect: 100-Continue" header for all requests. Set to a number so that + * the size of the payload must be greater than the number in order to send + * the Expect header. Setting to a number will send the Expect header for + * all requests in which the size of the payload cannot be determined or + * where the body is not rewindable. + * + * By default, Guzzle will add the "Expect: 100-Continue" header when the + * size of the body of a request is greater than 1 MB and a request is + * using HTTP/1.1. + */ + const EXPECT = 'expect'; + + /** + * form_params: (array) Associative array of form field names to values + * where each value is a string or array of strings. Sets the Content-Type + * header to application/x-www-form-urlencoded when no Content-Type header + * is already present. + */ + const FORM_PARAMS = 'form_params'; + + /** + * headers: (array) Associative array of HTTP headers. Each value MUST be + * a string or array of strings. + */ + const HEADERS = 'headers'; + + /** + * http_errors: (bool, default=true) Set to false to disable exceptions + * when a non- successful HTTP response is received. By default, + * exceptions will be thrown for 4xx and 5xx responses. This option only + * works if your handler has the `httpErrors` middleware. + */ + const HTTP_ERRORS = 'http_errors'; + + /** + * json: (mixed) Adds JSON data to a request. The provided value is JSON + * encoded and a Content-Type header of application/json will be added to + * the request if no Content-Type header is already present. + */ + const JSON = 'json'; + + /** + * multipart: (array) Array of associative arrays, each containing a + * required "name" key mapping to the form field, name, a required + * "contents" key mapping to a StreamInterface|resource|string, an + * optional "headers" associative array of custom headers, and an + * optional "filename" key mapping to a string to send as the filename in + * the part. If no "filename" key is present, then no "filename" attribute + * will be added to the part. + */ + const MULTIPART = 'multipart'; + + /** + * on_headers: (callable) A callable that is invoked when the HTTP headers + * of the response have been received but the body has not yet begun to + * download. + */ + const ON_HEADERS = 'on_headers'; + + /** + * on_stats: (callable) allows you to get access to transfer statistics of + * a request and access the lower level transfer details of the handler + * associated with your client. ``on_stats`` is a callable that is invoked + * when a handler has finished sending a request. The callback is invoked + * with transfer statistics about the request, the response received, or + * the error encountered. Included in the data is the total amount of time + * taken to send the request. + */ + const ON_STATS = 'on_stats'; + + /** + * progress: (callable) Defines a function to invoke when transfer + * progress is made. The function accepts the following positional + * arguments: the total number of bytes expected to be downloaded, the + * number of bytes downloaded so far, the number of bytes expected to be + * uploaded, the number of bytes uploaded so far. + */ + const PROGRESS = 'progress'; + + /** + * proxy: (string|array) Pass a string to specify an HTTP proxy, or an + * array to specify different proxies for different protocols (where the + * key is the protocol and the value is a proxy string). + */ + const PROXY = 'proxy'; + + /** + * query: (array|string) Associative array of query string values to add + * to the request. This option uses PHP's http_build_query() to create + * the string representation. Pass a string value if you need more + * control than what this method provides + */ + const QUERY = 'query'; + + /** + * sink: (resource|string|StreamInterface) Where the data of the + * response is written to. Defaults to a PHP temp stream. Providing a + * string will write data to a file by the given name. + */ + const SINK = 'sink'; + + /** + * synchronous: (bool) Set to true to inform HTTP handlers that you intend + * on waiting on the response. This can be useful for optimizations. Note + * that a promise is still returned if you are using one of the async + * client methods. + */ + const SYNCHRONOUS = 'synchronous'; + + /** + * ssl_key: (array|string) Specify the path to a file containing a private + * SSL key in PEM format. If a password is required, then set to an array + * containing the path to the SSL key in the first array element followed + * by the password required for the certificate in the second element. + */ + const SSL_KEY = 'ssl_key'; + + /** + * stream: Set to true to attempt to stream a response rather than + * download it all up-front. + */ + const STREAM = 'stream'; + + /** + * verify: (bool|string, default=true) Describes the SSL certificate + * verification behavior of a request. Set to true to enable SSL + * certificate verification using the system CA bundle when available + * (the default). Set to false to disable certificate verification (this + * is insecure!). Set to a string to provide the path to a CA bundle on + * disk to enable verification using a custom certificate. + */ + const VERIFY = 'verify'; + + /** + * timeout: (float, default=0) Float describing the timeout of the + * request in seconds. Use 0 to wait indefinitely (the default behavior). + */ + const TIMEOUT = 'timeout'; + + /** + * read_timeout: (float, default=default_socket_timeout ini setting) Float describing + * the body read timeout, for stream requests. + */ + const READ_TIMEOUT = 'read_timeout'; + + /** + * version: (float) Specifies the HTTP protocol version to attempt to use. + */ + const VERSION = 'version'; + + /** + * force_ip_resolve: (bool) Force client to use only ipv4 or ipv6 protocol + */ + const FORCE_IP_RESOLVE = 'force_ip_resolve'; +} diff --git a/vendor/guzzlehttp/guzzle/src/RetryMiddleware.php b/vendor/guzzlehttp/guzzle/src/RetryMiddleware.php new file mode 100644 index 00000000000..f27090fd197 --- /dev/null +++ b/vendor/guzzlehttp/guzzle/src/RetryMiddleware.php @@ -0,0 +1,112 @@ +<?php +namespace GuzzleHttp; + +use GuzzleHttp\Promise\PromiseInterface; +use GuzzleHttp\Promise\RejectedPromise; +use GuzzleHttp\Psr7; +use Psr\Http\Message\RequestInterface; +use Psr\Http\Message\ResponseInterface; + +/** + * Middleware that retries requests based on the boolean result of + * invoking the provided "decider" function. + */ +class RetryMiddleware +{ + /** @var callable */ + private $nextHandler; + + /** @var callable */ + private $decider; + + /** + * @param callable $decider Function that accepts the number of retries, + * a request, [response], and [exception] and + * returns true if the request is to be + * retried. + * @param callable $nextHandler Next handler to invoke. + * @param callable $delay Function that accepts the number of retries + * and [response] and returns the number of + * milliseconds to delay. + */ + public function __construct( + callable $decider, + callable $nextHandler, + callable $delay = null + ) { + $this->decider = $decider; + $this->nextHandler = $nextHandler; + $this->delay = $delay ?: __CLASS__ . '::exponentialDelay'; + } + + /** + * Default exponential backoff delay function. + * + * @param $retries + * + * @return int + */ + public static function exponentialDelay($retries) + { + return (int) pow(2, $retries - 1); + } + + /** + * @param RequestInterface $request + * @param array $options + * + * @return PromiseInterface + */ + public function __invoke(RequestInterface $request, array $options) + { + if (!isset($options['retries'])) { + $options['retries'] = 0; + } + + $fn = $this->nextHandler; + return $fn($request, $options) + ->then( + $this->onFulfilled($request, $options), + $this->onRejected($request, $options) + ); + } + + private function onFulfilled(RequestInterface $req, array $options) + { + return function ($value) use ($req, $options) { + if (!call_user_func( + $this->decider, + $options['retries'], + $req, + $value, + null + )) { + return $value; + } + return $this->doRetry($req, $options, $value); + }; + } + + private function onRejected(RequestInterface $req, array $options) + { + return function ($reason) use ($req, $options) { + if (!call_user_func( + $this->decider, + $options['retries'], + $req, + null, + $reason + )) { + return \GuzzleHttp\Promise\rejection_for($reason); + } + return $this->doRetry($req, $options); + }; + } + + private function doRetry(RequestInterface $request, array $options, ResponseInterface $response = null) + { + $options['delay'] = call_user_func($this->delay, ++$options['retries'], $response); + + return $this($request, $options); + } +} diff --git a/vendor/guzzlehttp/guzzle/src/TransferStats.php b/vendor/guzzlehttp/guzzle/src/TransferStats.php new file mode 100644 index 00000000000..15f717e1ea6 --- /dev/null +++ b/vendor/guzzlehttp/guzzle/src/TransferStats.php @@ -0,0 +1,126 @@ +<?php +namespace GuzzleHttp; + +use Psr\Http\Message\RequestInterface; +use Psr\Http\Message\ResponseInterface; +use Psr\Http\Message\UriInterface; + +/** + * Represents data at the point after it was transferred either successfully + * or after a network error. + */ +final class TransferStats +{ + private $request; + private $response; + private $transferTime; + private $handlerStats; + private $handlerErrorData; + + /** + * @param RequestInterface $request Request that was sent. + * @param ResponseInterface $response Response received (if any) + * @param null $transferTime Total handler transfer time. + * @param mixed $handlerErrorData Handler error data. + * @param array $handlerStats Handler specific stats. + */ + public function __construct( + RequestInterface $request, + ResponseInterface $response = null, + $transferTime = null, + $handlerErrorData = null, + $handlerStats = [] + ) { + $this->request = $request; + $this->response = $response; + $this->transferTime = $transferTime; + $this->handlerErrorData = $handlerErrorData; + $this->handlerStats = $handlerStats; + } + + /** + * @return RequestInterface + */ + public function getRequest() + { + return $this->request; + } + + /** + * Returns the response that was received (if any). + * + * @return ResponseInterface|null + */ + public function getResponse() + { + return $this->response; + } + + /** + * Returns true if a response was received. + * + * @return bool + */ + public function hasResponse() + { + return $this->response !== null; + } + + /** + * Gets handler specific error data. + * + * This might be an exception, a integer representing an error code, or + * anything else. Relying on this value assumes that you know what handler + * you are using. + * + * @return mixed + */ + public function getHandlerErrorData() + { + return $this->handlerErrorData; + } + + /** + * Get the effective URI the request was sent to. + * + * @return UriInterface + */ + public function getEffectiveUri() + { + return $this->request->getUri(); + } + + /** + * Get the estimated time the request was being transferred by the handler. + * + * @return float Time in seconds. + */ + public function getTransferTime() + { + return $this->transferTime; + } + + /** + * Gets an array of all of the handler specific transfer data. + * + * @return array + */ + public function getHandlerStats() + { + return $this->handlerStats; + } + + /** + * Get a specific handler statistic from the handler by name. + * + * @param string $stat Handler specific transfer stat to retrieve. + * + * @return mixed|null + */ + public function getHandlerStat($stat) + { + return isset($this->handlerStats[$stat]) + ? $this->handlerStats[$stat] + : null; + } +} diff --git a/vendor/guzzlehttp/guzzle/src/UriTemplate.php b/vendor/guzzlehttp/guzzle/src/UriTemplate.php new file mode 100644 index 00000000000..96dcfd09cd7 --- /dev/null +++ b/vendor/guzzlehttp/guzzle/src/UriTemplate.php @@ -0,0 +1,237 @@ +<?php +namespace GuzzleHttp; + +/** + * Expands URI templates. Userland implementation of PECL uri_template. + * + * @link http://tools.ietf.org/html/rfc6570 + */ +class UriTemplate +{ + /** @var string URI template */ + private $template; + + /** @var array Variables to use in the template expansion */ + private $variables; + + /** @var array Hash for quick operator lookups */ + private static $operatorHash = [ + '' => ['prefix' => '', 'joiner' => ',', 'query' => false], + '+' => ['prefix' => '', 'joiner' => ',', 'query' => false], + '#' => ['prefix' => '#', 'joiner' => ',', 'query' => false], + '.' => ['prefix' => '.', 'joiner' => '.', 'query' => false], + '/' => ['prefix' => '/', 'joiner' => '/', 'query' => false], + ';' => ['prefix' => ';', 'joiner' => ';', 'query' => true], + '?' => ['prefix' => '?', 'joiner' => '&', 'query' => true], + '&' => ['prefix' => '&', 'joiner' => '&', 'query' => true] + ]; + + /** @var array Delimiters */ + private static $delims = [':', '/', '?', '#', '[', ']', '@', '!', '$', + '&', '\'', '(', ')', '*', '+', ',', ';', '=']; + + /** @var array Percent encoded delimiters */ + private static $delimsPct = ['%3A', '%2F', '%3F', '%23', '%5B', '%5D', + '%40', '%21', '%24', '%26', '%27', '%28', '%29', '%2A', '%2B', '%2C', + '%3B', '%3D']; + + public function expand($template, array $variables) + { + if (false === strpos($template, '{')) { + return $template; + } + + $this->template = $template; + $this->variables = $variables; + + return preg_replace_callback( + '/\{([^\}]+)\}/', + [$this, 'expandMatch'], + $this->template + ); + } + + /** + * Parse an expression into parts + * + * @param string $expression Expression to parse + * + * @return array Returns an associative array of parts + */ + private function parseExpression($expression) + { + $result = []; + + if (isset(self::$operatorHash[$expression[0]])) { + $result['operator'] = $expression[0]; + $expression = substr($expression, 1); + } else { + $result['operator'] = ''; + } + + foreach (explode(',', $expression) as $value) { + $value = trim($value); + $varspec = []; + if ($colonPos = strpos($value, ':')) { + $varspec['value'] = substr($value, 0, $colonPos); + $varspec['modifier'] = ':'; + $varspec['position'] = (int) substr($value, $colonPos + 1); + } elseif (substr($value, -1) === '*') { + $varspec['modifier'] = '*'; + $varspec['value'] = substr($value, 0, -1); + } else { + $varspec['value'] = (string) $value; + $varspec['modifier'] = ''; + } + $result['values'][] = $varspec; + } + + return $result; + } + + /** + * Process an expansion + * + * @param array $matches Matches met in the preg_replace_callback + * + * @return string Returns the replacement string + */ + private function expandMatch(array $matches) + { + static $rfc1738to3986 = ['+' => '%20', '%7e' => '~']; + + $replacements = []; + $parsed = self::parseExpression($matches[1]); + $prefix = self::$operatorHash[$parsed['operator']]['prefix']; + $joiner = self::$operatorHash[$parsed['operator']]['joiner']; + $useQuery = self::$operatorHash[$parsed['operator']]['query']; + + foreach ($parsed['values'] as $value) { + if (!isset($this->variables[$value['value']])) { + continue; + } + + $variable = $this->variables[$value['value']]; + $actuallyUseQuery = $useQuery; + $expanded = ''; + + if (is_array($variable)) { + $isAssoc = $this->isAssoc($variable); + $kvp = []; + foreach ($variable as $key => $var) { + if ($isAssoc) { + $key = rawurlencode($key); + $isNestedArray = is_array($var); + } else { + $isNestedArray = false; + } + + if (!$isNestedArray) { + $var = rawurlencode($var); + if ($parsed['operator'] === '+' || + $parsed['operator'] === '#' + ) { + $var = $this->decodeReserved($var); + } + } + + if ($value['modifier'] === '*') { + if ($isAssoc) { + if ($isNestedArray) { + // Nested arrays must allow for deeply nested + // structures. + $var = strtr( + http_build_query([$key => $var]), + $rfc1738to3986 + ); + } else { + $var = $key . '=' . $var; + } + } elseif ($key > 0 && $actuallyUseQuery) { + $var = $value['value'] . '=' . $var; + } + } + + $kvp[$key] = $var; + } + + if (empty($variable)) { + $actuallyUseQuery = false; + } elseif ($value['modifier'] === '*') { + $expanded = implode($joiner, $kvp); + if ($isAssoc) { + // Don't prepend the value name when using the explode + // modifier with an associative array. + $actuallyUseQuery = false; + } + } else { + if ($isAssoc) { + // When an associative array is encountered and the + // explode modifier is not set, then the result must be + // a comma separated list of keys followed by their + // respective values. + foreach ($kvp as $k => &$v) { + $v = $k . ',' . $v; + } + } + $expanded = implode(',', $kvp); + } + } else { + if ($value['modifier'] === ':') { + $variable = substr($variable, 0, $value['position']); + } + $expanded = rawurlencode($variable); + if ($parsed['operator'] === '+' || $parsed['operator'] === '#') { + $expanded = $this->decodeReserved($expanded); + } + } + + if ($actuallyUseQuery) { + if (!$expanded && $joiner !== '&') { + $expanded = $value['value']; + } else { + $expanded = $value['value'] . '=' . $expanded; + } + } + + $replacements[] = $expanded; + } + + $ret = implode($joiner, $replacements); + if ($ret && $prefix) { + return $prefix . $ret; + } + + return $ret; + } + + /** + * Determines if an array is associative. + * + * This makes the assumption that input arrays are sequences or hashes. + * This assumption is a tradeoff for accuracy in favor of speed, but it + * should work in almost every case where input is supplied for a URI + * template. + * + * @param array $array Array to check + * + * @return bool + */ + private function isAssoc(array $array) + { + return $array && array_keys($array)[0] !== 0; + } + + /** + * Removes percent encoding on reserved characters (used with + and # + * modifiers). + * + * @param string $string String to fix + * + * @return string + */ + private function decodeReserved($string) + { + return str_replace(self::$delimsPct, self::$delims, $string); + } +} diff --git a/vendor/guzzlehttp/guzzle/src/functions.php b/vendor/guzzlehttp/guzzle/src/functions.php new file mode 100644 index 00000000000..a3ac450db94 --- /dev/null +++ b/vendor/guzzlehttp/guzzle/src/functions.php @@ -0,0 +1,333 @@ +<?php +namespace GuzzleHttp; + +use GuzzleHttp\Handler\CurlHandler; +use GuzzleHttp\Handler\CurlMultiHandler; +use GuzzleHttp\Handler\Proxy; +use GuzzleHttp\Handler\StreamHandler; + +/** + * Expands a URI template + * + * @param string $template URI template + * @param array $variables Template variables + * + * @return string + */ +function uri_template($template, array $variables) +{ + if (extension_loaded('uri_template')) { + // @codeCoverageIgnoreStart + return \uri_template($template, $variables); + // @codeCoverageIgnoreEnd + } + + static $uriTemplate; + if (!$uriTemplate) { + $uriTemplate = new UriTemplate(); + } + + return $uriTemplate->expand($template, $variables); +} + +/** + * Debug function used to describe the provided value type and class. + * + * @param mixed $input + * + * @return string Returns a string containing the type of the variable and + * if a class is provided, the class name. + */ +function describe_type($input) +{ + switch (gettype($input)) { + case 'object': + return 'object(' . get_class($input) . ')'; + case 'array': + return 'array(' . count($input) . ')'; + default: + ob_start(); + var_dump($input); + // normalize float vs double + return str_replace('double(', 'float(', rtrim(ob_get_clean())); + } +} + +/** + * Parses an array of header lines into an associative array of headers. + * + * @param array $lines Header lines array of strings in the following + * format: "Name: Value" + * @return array + */ +function headers_from_lines($lines) +{ + $headers = []; + + foreach ($lines as $line) { + $parts = explode(':', $line, 2); + $headers[trim($parts[0])][] = isset($parts[1]) + ? trim($parts[1]) + : null; + } + + return $headers; +} + +/** + * Returns a debug stream based on the provided variable. + * + * @param mixed $value Optional value + * + * @return resource + */ +function debug_resource($value = null) +{ + if (is_resource($value)) { + return $value; + } elseif (defined('STDOUT')) { + return STDOUT; + } + + return fopen('php://output', 'w'); +} + +/** + * Chooses and creates a default handler to use based on the environment. + * + * The returned handler is not wrapped by any default middlewares. + * + * @throws \RuntimeException if no viable Handler is available. + * @return callable Returns the best handler for the given system. + */ +function choose_handler() +{ + $handler = null; + if (function_exists('curl_multi_exec') && function_exists('curl_exec')) { + $handler = Proxy::wrapSync(new CurlMultiHandler(), new CurlHandler()); + } elseif (function_exists('curl_exec')) { + $handler = new CurlHandler(); + } elseif (function_exists('curl_multi_exec')) { + $handler = new CurlMultiHandler(); + } + + if (ini_get('allow_url_fopen')) { + $handler = $handler + ? Proxy::wrapStreaming($handler, new StreamHandler()) + : new StreamHandler(); + } elseif (!$handler) { + throw new \RuntimeException('GuzzleHttp requires cURL, the ' + . 'allow_url_fopen ini setting, or a custom HTTP handler.'); + } + + return $handler; +} + +/** + * Get the default User-Agent string to use with Guzzle + * + * @return string + */ +function default_user_agent() +{ + static $defaultAgent = ''; + + if (!$defaultAgent) { + $defaultAgent = 'GuzzleHttp/' . Client::VERSION; + if (extension_loaded('curl') && function_exists('curl_version')) { + $defaultAgent .= ' curl/' . \curl_version()['version']; + } + $defaultAgent .= ' PHP/' . PHP_VERSION; + } + + return $defaultAgent; +} + +/** + * Returns the default cacert bundle for the current system. + * + * First, the openssl.cafile and curl.cainfo php.ini settings are checked. + * If those settings are not configured, then the common locations for + * bundles found on Red Hat, CentOS, Fedora, Ubuntu, Debian, FreeBSD, OS X + * and Windows are checked. If any of these file locations are found on + * disk, they will be utilized. + * + * Note: the result of this function is cached for subsequent calls. + * + * @return string + * @throws \RuntimeException if no bundle can be found. + */ +function default_ca_bundle() +{ + static $cached = null; + static $cafiles = [ + // Red Hat, CentOS, Fedora (provided by the ca-certificates package) + '/etc/pki/tls/certs/ca-bundle.crt', + // Ubuntu, Debian (provided by the ca-certificates package) + '/etc/ssl/certs/ca-certificates.crt', + // FreeBSD (provided by the ca_root_nss package) + '/usr/local/share/certs/ca-root-nss.crt', + // SLES 12 (provided by the ca-certificates package) + '/var/lib/ca-certificates/ca-bundle.pem', + // OS X provided by homebrew (using the default path) + '/usr/local/etc/openssl/cert.pem', + // Google app engine + '/etc/ca-certificates.crt', + // Windows? + 'C:\\windows\\system32\\curl-ca-bundle.crt', + 'C:\\windows\\curl-ca-bundle.crt', + ]; + + if ($cached) { + return $cached; + } + + if ($ca = ini_get('openssl.cafile')) { + return $cached = $ca; + } + + if ($ca = ini_get('curl.cainfo')) { + return $cached = $ca; + } + + foreach ($cafiles as $filename) { + if (file_exists($filename)) { + return $cached = $filename; + } + } + + throw new \RuntimeException(<<< EOT +No system CA bundle could be found in any of the the common system locations. +PHP versions earlier than 5.6 are not properly configured to use the system's +CA bundle by default. In order to verify peer certificates, you will need to +supply the path on disk to a certificate bundle to the 'verify' request +option: http://docs.guzzlephp.org/en/latest/clients.html#verify. If you do not +need a specific certificate bundle, then Mozilla provides a commonly used CA +bundle which can be downloaded here (provided by the maintainer of cURL): +https://raw.githubusercontent.com/bagder/ca-bundle/master/ca-bundle.crt. Once +you have a CA bundle available on disk, you can set the 'openssl.cafile' PHP +ini setting to point to the path to the file, allowing you to omit the 'verify' +request option. See http://curl.haxx.se/docs/sslcerts.html for more +information. +EOT + ); +} + +/** + * Creates an associative array of lowercase header names to the actual + * header casing. + * + * @param array $headers + * + * @return array + */ +function normalize_header_keys(array $headers) +{ + $result = []; + foreach (array_keys($headers) as $key) { + $result[strtolower($key)] = $key; + } + + return $result; +} + +/** + * Returns true if the provided host matches any of the no proxy areas. + * + * This method will strip a port from the host if it is present. Each pattern + * can be matched with an exact match (e.g., "foo.com" == "foo.com") or a + * partial match: (e.g., "foo.com" == "baz.foo.com" and ".foo.com" == + * "baz.foo.com", but ".foo.com" != "foo.com"). + * + * Areas are matched in the following cases: + * 1. "*" (without quotes) always matches any hosts. + * 2. An exact match. + * 3. The area starts with "." and the area is the last part of the host. e.g. + * '.mit.edu' will match any host that ends with '.mit.edu'. + * + * @param string $host Host to check against the patterns. + * @param array $noProxyArray An array of host patterns. + * + * @return bool + */ +function is_host_in_noproxy($host, array $noProxyArray) +{ + if (strlen($host) === 0) { + throw new \InvalidArgumentException('Empty host provided'); + } + + // Strip port if present. + if (strpos($host, ':')) { + $host = explode($host, ':', 2)[0]; + } + + foreach ($noProxyArray as $area) { + // Always match on wildcards. + if ($area === '*') { + return true; + } elseif (empty($area)) { + // Don't match on empty values. + continue; + } elseif ($area === $host) { + // Exact matches. + return true; + } else { + // Special match if the area when prefixed with ".". Remove any + // existing leading "." and add a new leading ".". + $area = '.' . ltrim($area, '.'); + if (substr($host, -(strlen($area))) === $area) { + return true; + } + } + } + + return false; +} + +/** + * Wrapper for json_decode that throws when an error occurs. + * + * @param string $json JSON data to parse + * @param bool $assoc When true, returned objects will be converted + * into associative arrays. + * @param int $depth User specified recursion depth. + * @param int $options Bitmask of JSON decode options. + * + * @return mixed + * @throws \InvalidArgumentException if the JSON cannot be decoded. + * @link http://www.php.net/manual/en/function.json-decode.php + */ +function json_decode($json, $assoc = false, $depth = 512, $options = 0) +{ + $data = \json_decode($json, $assoc, $depth, $options); + if (JSON_ERROR_NONE !== json_last_error()) { + throw new \InvalidArgumentException( + 'json_decode error: ' . json_last_error_msg() + ); + } + + return $data; +} + +/** + * Wrapper for JSON encoding that throws when an error occurs. + * + * @param mixed $value The value being encoded + * @param int $options JSON encode option bitmask + * @param int $depth Set the maximum depth. Must be greater than zero. + * + * @return string + * @throws \InvalidArgumentException if the JSON cannot be encoded. + * @link http://www.php.net/manual/en/function.json-encode.php + */ +function json_encode($value, $options = 0, $depth = 512) +{ + $json = \json_encode($value, $options, $depth); + if (JSON_ERROR_NONE !== json_last_error()) { + throw new \InvalidArgumentException( + 'json_encode error: ' . json_last_error_msg() + ); + } + + return $json; +} diff --git a/vendor/guzzlehttp/guzzle/src/functions_include.php b/vendor/guzzlehttp/guzzle/src/functions_include.php new file mode 100644 index 00000000000..a93393acc4e --- /dev/null +++ b/vendor/guzzlehttp/guzzle/src/functions_include.php @@ -0,0 +1,6 @@ +<?php + +// Don't redefine the functions if included multiple times. +if (!function_exists('GuzzleHttp\uri_template')) { + require __DIR__ . '/functions.php'; +} diff --git a/vendor/guzzlehttp/promises/CHANGELOG.md b/vendor/guzzlehttp/promises/CHANGELOG.md new file mode 100644 index 00000000000..551929f6bd5 --- /dev/null +++ b/vendor/guzzlehttp/promises/CHANGELOG.md @@ -0,0 +1,65 @@ +# CHANGELOG + + +## 1.3.1 - 2016-12-20 + +### Fixed + +- `wait()` foreign promise compatibility + + +## 1.3.0 - 2016-11-18 + +### Added + +- Adds support for custom task queues. + +### Fixed + +- Fixed coroutine promise memory leak. + + +## 1.2.0 - 2016-05-18 + +### Changed + +- Update to now catch `\Throwable` on PHP 7+ + + +## 1.1.0 - 2016-03-07 + +### Changed + +- Update EachPromise to prevent recurring on a iterator when advancing, as this + could trigger fatal generator errors. +- Update Promise to allow recursive waiting without unwrapping exceptions. + + +## 1.0.3 - 2015-10-15 + +### Changed + +- Update EachPromise to immediately resolve when the underlying promise iterator + is empty. Previously, such a promise would throw an exception when its `wait` + function was called. + + +## 1.0.2 - 2015-05-15 + +### Changed + +- Conditionally require functions.php. + + +## 1.0.1 - 2015-06-24 + +### Changed + +- Updating EachPromise to call next on the underlying promise iterator as late + as possible to ensure that generators that generate new requests based on + callbacks are not iterated until after callbacks are invoked. + + +## 1.0.0 - 2015-05-12 + +- Initial release diff --git a/vendor/guzzlehttp/promises/LICENSE b/vendor/guzzlehttp/promises/LICENSE new file mode 100644 index 00000000000..67f91a14c00 --- /dev/null +++ b/vendor/guzzlehttp/promises/LICENSE @@ -0,0 +1,19 @@ +Copyright (c) 2015-2016 Michael Dowling, https://github.com/mtdowling <mtdowling@gmail.com> + +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/guzzlehttp/promises/Makefile b/vendor/guzzlehttp/promises/Makefile new file mode 100644 index 00000000000..8d5b3ef95eb --- /dev/null +++ b/vendor/guzzlehttp/promises/Makefile @@ -0,0 +1,13 @@ +all: clean test + +test: + vendor/bin/phpunit + +coverage: + vendor/bin/phpunit --coverage-html=artifacts/coverage + +view-coverage: + open artifacts/coverage/index.html + +clean: + rm -rf artifacts/* diff --git a/vendor/guzzlehttp/promises/README.md b/vendor/guzzlehttp/promises/README.md new file mode 100644 index 00000000000..7b607e28b13 --- /dev/null +++ b/vendor/guzzlehttp/promises/README.md @@ -0,0 +1,504 @@ +# Guzzle Promises + +[Promises/A+](https://promisesaplus.com/) implementation that handles promise +chaining and resolution iteratively, allowing for "infinite" promise chaining +while keeping the stack size constant. Read [this blog post](https://blog.domenic.me/youre-missing-the-point-of-promises/) +for a general introduction to promises. + +- [Features](#features) +- [Quick start](#quick-start) +- [Synchronous wait](#synchronous-wait) +- [Cancellation](#cancellation) +- [API](#api) + - [Promise](#promise) + - [FulfilledPromise](#fulfilledpromise) + - [RejectedPromise](#rejectedpromise) +- [Promise interop](#promise-interop) +- [Implementation notes](#implementation-notes) + + +# Features + +- [Promises/A+](https://promisesaplus.com/) implementation. +- Promise resolution and chaining is handled iteratively, allowing for + "infinite" promise chaining. +- Promises have a synchronous `wait` method. +- Promises can be cancelled. +- Works with any object that has a `then` function. +- C# style async/await coroutine promises using + `GuzzleHttp\Promise\coroutine()`. + + +# Quick start + +A *promise* represents the eventual result of an asynchronous operation. The +primary way of interacting with a promise is through its `then` method, which +registers callbacks to receive either a promise's eventual value or the reason +why the promise cannot be fulfilled. + + +## Callbacks + +Callbacks are registered with the `then` method by providing an optional +`$onFulfilled` followed by an optional `$onRejected` function. + + +```php +use GuzzleHttp\Promise\Promise; + +$promise = new Promise(); +$promise->then( + // $onFulfilled + function ($value) { + echo 'The promise was fulfilled.'; + }, + // $onRejected + function ($reason) { + echo 'The promise was rejected.'; + } +); +``` + +*Resolving* a promise means that you either fulfill a promise with a *value* or +reject a promise with a *reason*. Resolving a promises triggers callbacks +registered with the promises's `then` method. These callbacks are triggered +only once and in the order in which they were added. + + +## Resolving a promise + +Promises are fulfilled using the `resolve($value)` method. Resolving a promise +with any value other than a `GuzzleHttp\Promise\RejectedPromise` will trigger +all of the onFulfilled callbacks (resolving a promise with a rejected promise +will reject the promise and trigger the `$onRejected` callbacks). + +```php +use GuzzleHttp\Promise\Promise; + +$promise = new Promise(); +$promise + ->then(function ($value) { + // Return a value and don't break the chain + return "Hello, " . $value; + }) + // This then is executed after the first then and receives the value + // returned from the first then. + ->then(function ($value) { + echo $value; + }); + +// Resolving the promise triggers the $onFulfilled callbacks and outputs +// "Hello, reader". +$promise->resolve('reader.'); +``` + + +## Promise forwarding + +Promises can be chained one after the other. Each then in the chain is a new +promise. The return value of a promise is what's forwarded to the next +promise in the chain. Returning a promise in a `then` callback will cause the +subsequent promises in the chain to only be fulfilled when the returned promise +has been fulfilled. The next promise in the chain will be invoked with the +resolved value of the promise. + +```php +use GuzzleHttp\Promise\Promise; + +$promise = new Promise(); +$nextPromise = new Promise(); + +$promise + ->then(function ($value) use ($nextPromise) { + echo $value; + return $nextPromise; + }) + ->then(function ($value) { + echo $value; + }); + +// Triggers the first callback and outputs "A" +$promise->resolve('A'); +// Triggers the second callback and outputs "B" +$nextPromise->resolve('B'); +``` + +## Promise rejection + +When a promise is rejected, the `$onRejected` callbacks are invoked with the +rejection reason. + +```php +use GuzzleHttp\Promise\Promise; + +$promise = new Promise(); +$promise->then(null, function ($reason) { + echo $reason; +}); + +$promise->reject('Error!'); +// Outputs "Error!" +``` + +## Rejection forwarding + +If an exception is thrown in an `$onRejected` callback, subsequent +`$onRejected` callbacks are invoked with the thrown exception as the reason. + +```php +use GuzzleHttp\Promise\Promise; + +$promise = new Promise(); +$promise->then(null, function ($reason) { + throw new \Exception($reason); +})->then(null, function ($reason) { + assert($reason->getMessage() === 'Error!'); +}); + +$promise->reject('Error!'); +``` + +You can also forward a rejection down the promise chain by returning a +`GuzzleHttp\Promise\RejectedPromise` in either an `$onFulfilled` or +`$onRejected` callback. + +```php +use GuzzleHttp\Promise\Promise; +use GuzzleHttp\Promise\RejectedPromise; + +$promise = new Promise(); +$promise->then(null, function ($reason) { + return new RejectedPromise($reason); +})->then(null, function ($reason) { + assert($reason === 'Error!'); +}); + +$promise->reject('Error!'); +``` + +If an exception is not thrown in a `$onRejected` callback and the callback +does not return a rejected promise, downstream `$onFulfilled` callbacks are +invoked using the value returned from the `$onRejected` callback. + +```php +use GuzzleHttp\Promise\Promise; +use GuzzleHttp\Promise\RejectedPromise; + +$promise = new Promise(); +$promise + ->then(null, function ($reason) { + return "It's ok"; + }) + ->then(function ($value) { + assert($value === "It's ok"); + }); + +$promise->reject('Error!'); +``` + +# Synchronous wait + +You can synchronously force promises to complete using a promise's `wait` +method. When creating a promise, you can provide a wait function that is used +to synchronously force a promise to complete. When a wait function is invoked +it is expected to deliver a value to the promise or reject the promise. If the +wait function does not deliver a value, then an exception is thrown. The wait +function provided to a promise constructor is invoked when the `wait` function +of the promise is called. + +```php +$promise = new Promise(function () use (&$promise) { + $promise->resolve('foo'); +}); + +// Calling wait will return the value of the promise. +echo $promise->wait(); // outputs "foo" +``` + +If an exception is encountered while invoking the wait function of a promise, +the promise is rejected with the exception and the exception is thrown. + +```php +$promise = new Promise(function () use (&$promise) { + throw new \Exception('foo'); +}); + +$promise->wait(); // throws the exception. +``` + +Calling `wait` on a promise that has been fulfilled will not trigger the wait +function. It will simply return the previously resolved value. + +```php +$promise = new Promise(function () { die('this is not called!'); }); +$promise->resolve('foo'); +echo $promise->wait(); // outputs "foo" +``` + +Calling `wait` on a promise that has been rejected will throw an exception. If +the rejection reason is an instance of `\Exception` the reason is thrown. +Otherwise, a `GuzzleHttp\Promise\RejectionException` is thrown and the reason +can be obtained by calling the `getReason` method of the exception. + +```php +$promise = new Promise(); +$promise->reject('foo'); +$promise->wait(); +``` + +> PHP Fatal error: Uncaught exception 'GuzzleHttp\Promise\RejectionException' with message 'The promise was rejected with value: foo' + + +## Unwrapping a promise + +When synchronously waiting on a promise, you are joining the state of the +promise into the current state of execution (i.e., return the value of the +promise if it was fulfilled or throw an exception if it was rejected). This is +called "unwrapping" the promise. Waiting on a promise will by default unwrap +the promise state. + +You can force a promise to resolve and *not* unwrap the state of the promise +by passing `false` to the first argument of the `wait` function: + +```php +$promise = new Promise(); +$promise->reject('foo'); +// This will not throw an exception. It simply ensures the promise has +// been resolved. +$promise->wait(false); +``` + +When unwrapping a promise, the resolved value of the promise will be waited +upon until the unwrapped value is not a promise. This means that if you resolve +promise A with a promise B and unwrap promise A, the value returned by the +wait function will be the value delivered to promise B. + +**Note**: when you do not unwrap the promise, no value is returned. + + +# Cancellation + +You can cancel a promise that has not yet been fulfilled using the `cancel()` +method of a promise. When creating a promise you can provide an optional +cancel function that when invoked cancels the action of computing a resolution +of the promise. + + +# API + + +## Promise + +When creating a promise object, you can provide an optional `$waitFn` and +`$cancelFn`. `$waitFn` is a function that is invoked with no arguments and is +expected to resolve the promise. `$cancelFn` is a function with no arguments +that is expected to cancel the computation of a promise. It is invoked when the +`cancel()` method of a promise is called. + +```php +use GuzzleHttp\Promise\Promise; + +$promise = new Promise( + function () use (&$promise) { + $promise->resolve('waited'); + }, + function () { + // do something that will cancel the promise computation (e.g., close + // a socket, cancel a database query, etc...) + } +); + +assert('waited' === $promise->wait()); +``` + +A promise has the following methods: + +- `then(callable $onFulfilled, callable $onRejected) : PromiseInterface` + + Appends fulfillment and rejection handlers to the promise, and returns a new promise resolving to the return value of the called handler. + +- `otherwise(callable $onRejected) : PromiseInterface` + + Appends a rejection handler callback to the promise, and returns a new promise resolving to the return value of the callback if it is called, or to its original fulfillment value if the promise is instead fulfilled. + +- `wait($unwrap = true) : mixed` + + Synchronously waits on the promise to complete. + + `$unwrap` controls whether or not the value of the promise is returned for a + fulfilled promise or if an exception is thrown if the promise is rejected. + This is set to `true` by default. + +- `cancel()` + + Attempts to cancel the promise if possible. The promise being cancelled and + the parent most ancestor that has not yet been resolved will also be + cancelled. Any promises waiting on the cancelled promise to resolve will also + be cancelled. + +- `getState() : string` + + Returns the state of the promise. One of `pending`, `fulfilled`, or + `rejected`. + +- `resolve($value)` + + Fulfills the promise with the given `$value`. + +- `reject($reason)` + + Rejects the promise with the given `$reason`. + + +## FulfilledPromise + +A fulfilled promise can be created to represent a promise that has been +fulfilled. + +```php +use GuzzleHttp\Promise\FulfilledPromise; + +$promise = new FulfilledPromise('value'); + +// Fulfilled callbacks are immediately invoked. +$promise->then(function ($value) { + echo $value; +}); +``` + + +## RejectedPromise + +A rejected promise can be created to represent a promise that has been +rejected. + +```php +use GuzzleHttp\Promise\RejectedPromise; + +$promise = new RejectedPromise('Error'); + +// Rejected callbacks are immediately invoked. +$promise->then(null, function ($reason) { + echo $reason; +}); +``` + + +# Promise interop + +This library works with foreign promises that have a `then` method. This means +you can use Guzzle promises with [React promises](https://github.com/reactphp/promise) +for example. When a foreign promise is returned inside of a then method +callback, promise resolution will occur recursively. + +```php +// Create a React promise +$deferred = new React\Promise\Deferred(); +$reactPromise = $deferred->promise(); + +// Create a Guzzle promise that is fulfilled with a React promise. +$guzzlePromise = new \GuzzleHttp\Promise\Promise(); +$guzzlePromise->then(function ($value) use ($reactPromise) { + // Do something something with the value... + // Return the React promise + return $reactPromise; +}); +``` + +Please note that wait and cancel chaining is no longer possible when forwarding +a foreign promise. You will need to wrap a third-party promise with a Guzzle +promise in order to utilize wait and cancel functions with foreign promises. + + +## Event Loop Integration + +In order to keep the stack size constant, Guzzle promises are resolved +asynchronously using a task queue. When waiting on promises synchronously, the +task queue will be automatically run to ensure that the blocking promise and +any forwarded promises are resolved. When using promises asynchronously in an +event loop, you will need to run the task queue on each tick of the loop. If +you do not run the task queue, then promises will not be resolved. + +You can run the task queue using the `run()` method of the global task queue +instance. + +```php +// Get the global task queue +$queue = \GuzzleHttp\Promise\queue(); +$queue->run(); +``` + +For example, you could use Guzzle promises with React using a periodic timer: + +```php +$loop = React\EventLoop\Factory::create(); +$loop->addPeriodicTimer(0, [$queue, 'run']); +``` + +*TODO*: Perhaps adding a `futureTick()` on each tick would be faster? + + +# Implementation notes + + +## Promise resolution and chaining is handled iteratively + +By shuffling pending handlers from one owner to another, promises are +resolved iteratively, allowing for "infinite" then chaining. + +```php +<?php +require 'vendor/autoload.php'; + +use GuzzleHttp\Promise\Promise; + +$parent = new Promise(); +$p = $parent; + +for ($i = 0; $i < 1000; $i++) { + $p = $p->then(function ($v) { + // The stack size remains constant (a good thing) + echo xdebug_get_stack_depth() . ', '; + return $v + 1; + }); +} + +$parent->resolve(0); +var_dump($p->wait()); // int(1000) + +``` + +When a promise is fulfilled or rejected with a non-promise value, the promise +then takes ownership of the handlers of each child promise and delivers values +down the chain without using recursion. + +When a promise is resolved with another promise, the original promise transfers +all of its pending handlers to the new promise. When the new promise is +eventually resolved, all of the pending handlers are delivered the forwarded +value. + + +## A promise is the deferred. + +Some promise libraries implement promises using a deferred object to represent +a computation and a promise object to represent the delivery of the result of +the computation. This is a nice separation of computation and delivery because +consumers of the promise cannot modify the value that will be eventually +delivered. + +One side effect of being able to implement promise resolution and chaining +iteratively is that you need to be able for one promise to reach into the state +of another promise to shuffle around ownership of handlers. In order to achieve +this without making the handlers of a promise publicly mutable, a promise is +also the deferred value, allowing promises of the same parent class to reach +into and modify the private properties of promises of the same type. While this +does allow consumers of the value to modify the resolution or rejection of the +deferred, it is a small price to pay for keeping the stack size constant. + +```php +$promise = new Promise(); +$promise->then(function ($value) { echo $value; }); +// The promise is the deferred value, so you can deliver a value to it. +$promise->resolve('foo'); +// prints "foo" +``` diff --git a/vendor/guzzlehttp/promises/composer.json b/vendor/guzzlehttp/promises/composer.json new file mode 100644 index 00000000000..ec41a61e6e8 --- /dev/null +++ b/vendor/guzzlehttp/promises/composer.json @@ -0,0 +1,34 @@ +{ + "name": "guzzlehttp/promises", + "description": "Guzzle promises library", + "keywords": ["promise"], + "license": "MIT", + "authors": [ + { + "name": "Michael Dowling", + "email": "mtdowling@gmail.com", + "homepage": "https://github.com/mtdowling" + } + ], + "require": { + "php": ">=5.5.0" + }, + "require-dev": { + "phpunit/phpunit": "^4.0" + }, + "autoload": { + "psr-4": { + "GuzzleHttp\\Promise\\": "src/" + }, + "files": ["src/functions_include.php"] + }, + "scripts": { + "test": "vendor/bin/phpunit", + "test-ci": "vendor/bin/phpunit --coverage-text" + }, + "extra": { + "branch-alias": { + "dev-master": "1.4-dev" + } + } +} diff --git a/vendor/guzzlehttp/promises/src/AggregateException.php b/vendor/guzzlehttp/promises/src/AggregateException.php new file mode 100644 index 00000000000..6a5690c3762 --- /dev/null +++ b/vendor/guzzlehttp/promises/src/AggregateException.php @@ -0,0 +1,16 @@ +<?php +namespace GuzzleHttp\Promise; + +/** + * Exception thrown when too many errors occur in the some() or any() methods. + */ +class AggregateException extends RejectionException +{ + public function __construct($msg, array $reasons) + { + parent::__construct( + $reasons, + sprintf('%s; %d rejected promises', $msg, count($reasons)) + ); + } +} diff --git a/vendor/guzzlehttp/promises/src/CancellationException.php b/vendor/guzzlehttp/promises/src/CancellationException.php new file mode 100644 index 00000000000..cb360b8056a --- /dev/null +++ b/vendor/guzzlehttp/promises/src/CancellationException.php @@ -0,0 +1,9 @@ +<?php +namespace GuzzleHttp\Promise; + +/** + * Exception that is set as the reason for a promise that has been cancelled. + */ +class CancellationException extends RejectionException +{ +} diff --git a/vendor/guzzlehttp/promises/src/Coroutine.php b/vendor/guzzlehttp/promises/src/Coroutine.php new file mode 100644 index 00000000000..6aa09587022 --- /dev/null +++ b/vendor/guzzlehttp/promises/src/Coroutine.php @@ -0,0 +1,151 @@ +<?php +namespace GuzzleHttp\Promise; + +use Exception; +use Generator; +use Throwable; + +/** + * Creates a promise that is resolved using a generator that yields values or + * promises (somewhat similar to C#'s async keyword). + * + * When called, the coroutine function will start an instance of the generator + * and returns a promise that is fulfilled with its final yielded value. + * + * Control is returned back to the generator when the yielded promise settles. + * This can lead to less verbose code when doing lots of sequential async calls + * with minimal processing in between. + * + * use GuzzleHttp\Promise; + * + * function createPromise($value) { + * return new Promise\FulfilledPromise($value); + * } + * + * $promise = Promise\coroutine(function () { + * $value = (yield createPromise('a')); + * try { + * $value = (yield createPromise($value . 'b')); + * } catch (\Exception $e) { + * // The promise was rejected. + * } + * yield $value . 'c'; + * }); + * + * // Outputs "abc" + * $promise->then(function ($v) { echo $v; }); + * + * @param callable $generatorFn Generator function to wrap into a promise. + * + * @return Promise + * @link https://github.com/petkaantonov/bluebird/blob/master/API.md#generators inspiration + */ +final class Coroutine implements PromiseInterface +{ + /** + * @var PromiseInterface|null + */ + private $currentPromise; + + /** + * @var Generator + */ + private $generator; + + /** + * @var Promise + */ + private $result; + + public function __construct(callable $generatorFn) + { + $this->generator = $generatorFn(); + $this->result = new Promise(function () { + while (isset($this->currentPromise)) { + $this->currentPromise->wait(); + } + }); + $this->nextCoroutine($this->generator->current()); + } + + public function then( + callable $onFulfilled = null, + callable $onRejected = null + ) { + return $this->result->then($onFulfilled, $onRejected); + } + + public function otherwise(callable $onRejected) + { + return $this->result->otherwise($onRejected); + } + + public function wait($unwrap = true) + { + return $this->result->wait($unwrap); + } + + public function getState() + { + return $this->result->getState(); + } + + public function resolve($value) + { + $this->result->resolve($value); + } + + public function reject($reason) + { + $this->result->reject($reason); + } + + public function cancel() + { + $this->currentPromise->cancel(); + $this->result->cancel(); + } + + private function nextCoroutine($yielded) + { + $this->currentPromise = promise_for($yielded) + ->then([$this, '_handleSuccess'], [$this, '_handleFailure']); + } + + /** + * @internal + */ + public function _handleSuccess($value) + { + unset($this->currentPromise); + try { + $next = $this->generator->send($value); + if ($this->generator->valid()) { + $this->nextCoroutine($next); + } else { + $this->result->resolve($value); + } + } catch (Exception $exception) { + $this->result->reject($exception); + } catch (Throwable $throwable) { + $this->result->reject($throwable); + } + } + + /** + * @internal + */ + public function _handleFailure($reason) + { + unset($this->currentPromise); + try { + $nextYield = $this->generator->throw(exception_for($reason)); + // The throw was caught, so keep iterating on the coroutine + $this->nextCoroutine($nextYield); + } catch (Exception $exception) { + $this->result->reject($exception); + } catch (Throwable $throwable) { + $this->result->reject($throwable); + } + } +} diff --git a/vendor/guzzlehttp/promises/src/EachPromise.php b/vendor/guzzlehttp/promises/src/EachPromise.php new file mode 100644 index 00000000000..d0ddf603fb3 --- /dev/null +++ b/vendor/guzzlehttp/promises/src/EachPromise.php @@ -0,0 +1,229 @@ +<?php +namespace GuzzleHttp\Promise; + +/** + * Represents a promise that iterates over many promises and invokes + * side-effect functions in the process. + */ +class EachPromise implements PromisorInterface +{ + private $pending = []; + + /** @var \Iterator */ + private $iterable; + + /** @var callable|int */ + private $concurrency; + + /** @var callable */ + private $onFulfilled; + + /** @var callable */ + private $onRejected; + + /** @var Promise */ + private $aggregate; + + /** @var bool */ + private $mutex; + + /** + * Configuration hash can include the following key value pairs: + * + * - fulfilled: (callable) Invoked when a promise fulfills. The function + * is invoked with three arguments: the fulfillment value, the index + * position from the iterable list of the promise, and the aggregate + * promise that manages all of the promises. The aggregate promise may + * be resolved from within the callback to short-circuit the promise. + * - rejected: (callable) Invoked when a promise is rejected. The + * function is invoked with three arguments: the rejection reason, the + * index position from the iterable list of the promise, and the + * aggregate promise that manages all of the promises. The aggregate + * promise may be resolved from within the callback to short-circuit + * the promise. + * - concurrency: (integer) Pass this configuration option to limit the + * allowed number of outstanding concurrently executing promises, + * creating a capped pool of promises. There is no limit by default. + * + * @param mixed $iterable Promises or values to iterate. + * @param array $config Configuration options + */ + public function __construct($iterable, array $config = []) + { + $this->iterable = iter_for($iterable); + + if (isset($config['concurrency'])) { + $this->concurrency = $config['concurrency']; + } + + if (isset($config['fulfilled'])) { + $this->onFulfilled = $config['fulfilled']; + } + + if (isset($config['rejected'])) { + $this->onRejected = $config['rejected']; + } + } + + public function promise() + { + if ($this->aggregate) { + return $this->aggregate; + } + + try { + $this->createPromise(); + $this->iterable->rewind(); + $this->refillPending(); + } catch (\Throwable $e) { + $this->aggregate->reject($e); + } catch (\Exception $e) { + $this->aggregate->reject($e); + } + + return $this->aggregate; + } + + private function createPromise() + { + $this->mutex = false; + $this->aggregate = new Promise(function () { + reset($this->pending); + if (empty($this->pending) && !$this->iterable->valid()) { + $this->aggregate->resolve(null); + return; + } + + // Consume a potentially fluctuating list of promises while + // ensuring that indexes are maintained (precluding array_shift). + while ($promise = current($this->pending)) { + next($this->pending); + $promise->wait(); + if ($this->aggregate->getState() !== PromiseInterface::PENDING) { + return; + } + } + }); + + // Clear the references when the promise is resolved. + $clearFn = function () { + $this->iterable = $this->concurrency = $this->pending = null; + $this->onFulfilled = $this->onRejected = null; + }; + + $this->aggregate->then($clearFn, $clearFn); + } + + private function refillPending() + { + if (!$this->concurrency) { + // Add all pending promises. + while ($this->addPending() && $this->advanceIterator()); + return; + } + + // Add only up to N pending promises. + $concurrency = is_callable($this->concurrency) + ? call_user_func($this->concurrency, count($this->pending)) + : $this->concurrency; + $concurrency = max($concurrency - count($this->pending), 0); + // Concurrency may be set to 0 to disallow new promises. + if (!$concurrency) { + return; + } + // Add the first pending promise. + $this->addPending(); + // Note this is special handling for concurrency=1 so that we do + // not advance the iterator after adding the first promise. This + // helps work around issues with generators that might not have the + // next value to yield until promise callbacks are called. + while (--$concurrency + && $this->advanceIterator() + && $this->addPending()); + } + + private function addPending() + { + if (!$this->iterable || !$this->iterable->valid()) { + return false; + } + + $promise = promise_for($this->iterable->current()); + $idx = $this->iterable->key(); + + $this->pending[$idx] = $promise->then( + function ($value) use ($idx) { + if ($this->onFulfilled) { + call_user_func( + $this->onFulfilled, $value, $idx, $this->aggregate + ); + } + $this->step($idx); + }, + function ($reason) use ($idx) { + if ($this->onRejected) { + call_user_func( + $this->onRejected, $reason, $idx, $this->aggregate + ); + } + $this->step($idx); + } + ); + + return true; + } + + private function advanceIterator() + { + // Place a lock on the iterator so that we ensure to not recurse, + // preventing fatal generator errors. + if ($this->mutex) { + return false; + } + + $this->mutex = true; + + try { + $this->iterable->next(); + $this->mutex = false; + return true; + } catch (\Throwable $e) { + $this->aggregate->reject($e); + $this->mutex = false; + return false; + } catch (\Exception $e) { + $this->aggregate->reject($e); + $this->mutex = false; + return false; + } + } + + private function step($idx) + { + // If the promise was already resolved, then ignore this step. + if ($this->aggregate->getState() !== PromiseInterface::PENDING) { + return; + } + + unset($this->pending[$idx]); + + // Only refill pending promises if we are not locked, preventing the + // EachPromise to recursively invoke the provided iterator, which + // cause a fatal error: "Cannot resume an already running generator" + if ($this->advanceIterator() && !$this->checkIfFinished()) { + // Add more pending promises if possible. + $this->refillPending(); + } + } + + private function checkIfFinished() + { + if (!$this->pending && !$this->iterable->valid()) { + // Resolve the promise if there's nothing left to do. + $this->aggregate->resolve(null); + return true; + } + + return false; + } +} diff --git a/vendor/guzzlehttp/promises/src/FulfilledPromise.php b/vendor/guzzlehttp/promises/src/FulfilledPromise.php new file mode 100644 index 00000000000..dbbeeb9f71b --- /dev/null +++ b/vendor/guzzlehttp/promises/src/FulfilledPromise.php @@ -0,0 +1,82 @@ +<?php +namespace GuzzleHttp\Promise; + +/** + * A promise that has been fulfilled. + * + * Thenning off of this promise will invoke the onFulfilled callback + * immediately and ignore other callbacks. + */ +class FulfilledPromise implements PromiseInterface +{ + private $value; + + public function __construct($value) + { + if (method_exists($value, 'then')) { + throw new \InvalidArgumentException( + 'You cannot create a FulfilledPromise with a promise.'); + } + + $this->value = $value; + } + + public function then( + callable $onFulfilled = null, + callable $onRejected = null + ) { + // Return itself if there is no onFulfilled function. + if (!$onFulfilled) { + return $this; + } + + $queue = queue(); + $p = new Promise([$queue, 'run']); + $value = $this->value; + $queue->add(static function () use ($p, $value, $onFulfilled) { + if ($p->getState() === self::PENDING) { + try { + $p->resolve($onFulfilled($value)); + } catch (\Throwable $e) { + $p->reject($e); + } catch (\Exception $e) { + $p->reject($e); + } + } + }); + + return $p; + } + + public function otherwise(callable $onRejected) + { + return $this->then(null, $onRejected); + } + + public function wait($unwrap = true, $defaultDelivery = null) + { + return $unwrap ? $this->value : null; + } + + public function getState() + { + return self::FULFILLED; + } + + public function resolve($value) + { + if ($value !== $this->value) { + throw new \LogicException("Cannot resolve a fulfilled promise"); + } + } + + public function reject($reason) + { + throw new \LogicException("Cannot reject a fulfilled promise"); + } + + public function cancel() + { + // pass + } +} diff --git a/vendor/guzzlehttp/promises/src/Promise.php b/vendor/guzzlehttp/promises/src/Promise.php new file mode 100644 index 00000000000..844ada073c8 --- /dev/null +++ b/vendor/guzzlehttp/promises/src/Promise.php @@ -0,0 +1,280 @@ +<?php +namespace GuzzleHttp\Promise; + +/** + * Promises/A+ implementation that avoids recursion when possible. + * + * @link https://promisesaplus.com/ + */ +class Promise implements PromiseInterface +{ + private $state = self::PENDING; + private $result; + private $cancelFn; + private $waitFn; + private $waitList; + private $handlers = []; + + /** + * @param callable $waitFn Fn that when invoked resolves the promise. + * @param callable $cancelFn Fn that when invoked cancels the promise. + */ + public function __construct( + callable $waitFn = null, + callable $cancelFn = null + ) { + $this->waitFn = $waitFn; + $this->cancelFn = $cancelFn; + } + + public function then( + callable $onFulfilled = null, + callable $onRejected = null + ) { + if ($this->state === self::PENDING) { + $p = new Promise(null, [$this, 'cancel']); + $this->handlers[] = [$p, $onFulfilled, $onRejected]; + $p->waitList = $this->waitList; + $p->waitList[] = $this; + return $p; + } + + // Return a fulfilled promise and immediately invoke any callbacks. + if ($this->state === self::FULFILLED) { + return $onFulfilled + ? promise_for($this->result)->then($onFulfilled) + : promise_for($this->result); + } + + // It's either cancelled or rejected, so return a rejected promise + // and immediately invoke any callbacks. + $rejection = rejection_for($this->result); + return $onRejected ? $rejection->then(null, $onRejected) : $rejection; + } + + public function otherwise(callable $onRejected) + { + return $this->then(null, $onRejected); + } + + public function wait($unwrap = true) + { + $this->waitIfPending(); + + $inner = $this->result instanceof PromiseInterface + ? $this->result->wait($unwrap) + : $this->result; + + if ($unwrap) { + if ($this->result instanceof PromiseInterface + || $this->state === self::FULFILLED + ) { + return $inner; + } else { + // It's rejected so "unwrap" and throw an exception. + throw exception_for($inner); + } + } + } + + public function getState() + { + return $this->state; + } + + public function cancel() + { + if ($this->state !== self::PENDING) { + return; + } + + $this->waitFn = $this->waitList = null; + + if ($this->cancelFn) { + $fn = $this->cancelFn; + $this->cancelFn = null; + try { + $fn(); + } catch (\Throwable $e) { + $this->reject($e); + } catch (\Exception $e) { + $this->reject($e); + } + } + + // Reject the promise only if it wasn't rejected in a then callback. + if ($this->state === self::PENDING) { + $this->reject(new CancellationException('Promise has been cancelled')); + } + } + + public function resolve($value) + { + $this->settle(self::FULFILLED, $value); + } + + public function reject($reason) + { + $this->settle(self::REJECTED, $reason); + } + + private function settle($state, $value) + { + if ($this->state !== self::PENDING) { + // Ignore calls with the same resolution. + if ($state === $this->state && $value === $this->result) { + return; + } + throw $this->state === $state + ? new \LogicException("The promise is already {$state}.") + : new \LogicException("Cannot change a {$this->state} promise to {$state}"); + } + + if ($value === $this) { + throw new \LogicException('Cannot fulfill or reject a promise with itself'); + } + + // Clear out the state of the promise but stash the handlers. + $this->state = $state; + $this->result = $value; + $handlers = $this->handlers; + $this->handlers = null; + $this->waitList = $this->waitFn = null; + $this->cancelFn = null; + + if (!$handlers) { + return; + } + + // If the value was not a settled promise or a thenable, then resolve + // it in the task queue using the correct ID. + if (!method_exists($value, 'then')) { + $id = $state === self::FULFILLED ? 1 : 2; + // It's a success, so resolve the handlers in the queue. + queue()->add(static function () use ($id, $value, $handlers) { + foreach ($handlers as $handler) { + self::callHandler($id, $value, $handler); + } + }); + } elseif ($value instanceof Promise + && $value->getState() === self::PENDING + ) { + // We can just merge our handlers onto the next promise. + $value->handlers = array_merge($value->handlers, $handlers); + } else { + // Resolve the handlers when the forwarded promise is resolved. + $value->then( + static function ($value) use ($handlers) { + foreach ($handlers as $handler) { + self::callHandler(1, $value, $handler); + } + }, + static function ($reason) use ($handlers) { + foreach ($handlers as $handler) { + self::callHandler(2, $reason, $handler); + } + } + ); + } + } + + /** + * Call a stack of handlers using a specific callback index and value. + * + * @param int $index 1 (resolve) or 2 (reject). + * @param mixed $value Value to pass to the callback. + * @param array $handler Array of handler data (promise and callbacks). + * + * @return array Returns the next group to resolve. + */ + private static function callHandler($index, $value, array $handler) + { + /** @var PromiseInterface $promise */ + $promise = $handler[0]; + + // The promise may have been cancelled or resolved before placing + // this thunk in the queue. + if ($promise->getState() !== self::PENDING) { + return; + } + + try { + if (isset($handler[$index])) { + $promise->resolve($handler[$index]($value)); + } elseif ($index === 1) { + // Forward resolution values as-is. + $promise->resolve($value); + } else { + // Forward rejections down the chain. + $promise->reject($value); + } + } catch (\Throwable $reason) { + $promise->reject($reason); + } catch (\Exception $reason) { + $promise->reject($reason); + } + } + + private function waitIfPending() + { + if ($this->state !== self::PENDING) { + return; + } elseif ($this->waitFn) { + $this->invokeWaitFn(); + } elseif ($this->waitList) { + $this->invokeWaitList(); + } else { + // If there's not wait function, then reject the promise. + $this->reject('Cannot wait on a promise that has ' + . 'no internal wait function. You must provide a wait ' + . 'function when constructing the promise to be able to ' + . 'wait on a promise.'); + } + + queue()->run(); + + if ($this->state === self::PENDING) { + $this->reject('Invoking the wait callback did not resolve the promise'); + } + } + + private function invokeWaitFn() + { + try { + $wfn = $this->waitFn; + $this->waitFn = null; + $wfn(true); + } catch (\Exception $reason) { + if ($this->state === self::PENDING) { + // The promise has not been resolved yet, so reject the promise + // with the exception. + $this->reject($reason); + } else { + // The promise was already resolved, so there's a problem in + // the application. + throw $reason; + } + } + } + + private function invokeWaitList() + { + $waitList = $this->waitList; + $this->waitList = null; + + foreach ($waitList as $result) { + while (true) { + $result->waitIfPending(); + + if ($result->result instanceof Promise) { + $result = $result->result; + } else { + if ($result->result instanceof PromiseInterface) { + $result->result->wait(false); + } + break; + } + } + } + } +} diff --git a/vendor/guzzlehttp/promises/src/PromiseInterface.php b/vendor/guzzlehttp/promises/src/PromiseInterface.php new file mode 100644 index 00000000000..8f5f4b99b26 --- /dev/null +++ b/vendor/guzzlehttp/promises/src/PromiseInterface.php @@ -0,0 +1,93 @@ +<?php +namespace GuzzleHttp\Promise; + +/** + * A promise represents the eventual result of an asynchronous operation. + * + * The primary way of interacting with a promise is through its then method, + * which registers callbacks to receive either a promise’s eventual value or + * the reason why the promise cannot be fulfilled. + * + * @link https://promisesaplus.com/ + */ +interface PromiseInterface +{ + const PENDING = 'pending'; + const FULFILLED = 'fulfilled'; + const REJECTED = 'rejected'; + + /** + * Appends fulfillment and rejection handlers to the promise, and returns + * a new promise resolving to the return value of the called handler. + * + * @param callable $onFulfilled Invoked when the promise fulfills. + * @param callable $onRejected Invoked when the promise is rejected. + * + * @return PromiseInterface + */ + public function then( + callable $onFulfilled = null, + callable $onRejected = null + ); + + /** + * Appends a rejection handler callback to the promise, and returns a new + * promise resolving to the return value of the callback if it is called, + * or to its original fulfillment value if the promise is instead + * fulfilled. + * + * @param callable $onRejected Invoked when the promise is rejected. + * + * @return PromiseInterface + */ + public function otherwise(callable $onRejected); + + /** + * Get the state of the promise ("pending", "rejected", or "fulfilled"). + * + * The three states can be checked against the constants defined on + * PromiseInterface: PENDING, FULFILLED, and REJECTED. + * + * @return string + */ + public function getState(); + + /** + * Resolve the promise with the given value. + * + * @param mixed $value + * @throws \RuntimeException if the promise is already resolved. + */ + public function resolve($value); + + /** + * Reject the promise with the given reason. + * + * @param mixed $reason + * @throws \RuntimeException if the promise is already resolved. + */ + public function reject($reason); + + /** + * Cancels the promise if possible. + * + * @link https://github.com/promises-aplus/cancellation-spec/issues/7 + */ + public function cancel(); + + /** + * Waits until the promise completes if possible. + * + * Pass $unwrap as true to unwrap the result of the promise, either + * returning the resolved value or throwing the rejected exception. + * + * If the promise cannot be waited on, then the promise will be rejected. + * + * @param bool $unwrap + * + * @return mixed + * @throws \LogicException if the promise has no wait function or if the + * promise does not settle after waiting. + */ + public function wait($unwrap = true); +} diff --git a/vendor/guzzlehttp/promises/src/PromisorInterface.php b/vendor/guzzlehttp/promises/src/PromisorInterface.php new file mode 100644 index 00000000000..b07fe32bc13 --- /dev/null +++ b/vendor/guzzlehttp/promises/src/PromisorInterface.php @@ -0,0 +1,15 @@ +<?php +namespace GuzzleHttp\Promise; + +/** + * Interface used with classes that return a promise. + */ +interface PromisorInterface +{ + /** + * Returns a promise. + * + * @return PromiseInterface + */ + public function promise(); +} diff --git a/vendor/guzzlehttp/promises/src/RejectedPromise.php b/vendor/guzzlehttp/promises/src/RejectedPromise.php new file mode 100644 index 00000000000..2bc6508e7b6 --- /dev/null +++ b/vendor/guzzlehttp/promises/src/RejectedPromise.php @@ -0,0 +1,87 @@ +<?php +namespace GuzzleHttp\Promise; + +/** + * A promise that has been rejected. + * + * Thenning off of this promise will invoke the onRejected callback + * immediately and ignore other callbacks. + */ +class RejectedPromise implements PromiseInterface +{ + private $reason; + + public function __construct($reason) + { + if (method_exists($reason, 'then')) { + throw new \InvalidArgumentException( + 'You cannot create a RejectedPromise with a promise.'); + } + + $this->reason = $reason; + } + + public function then( + callable $onFulfilled = null, + callable $onRejected = null + ) { + // If there's no onRejected callback then just return self. + if (!$onRejected) { + return $this; + } + + $queue = queue(); + $reason = $this->reason; + $p = new Promise([$queue, 'run']); + $queue->add(static function () use ($p, $reason, $onRejected) { + if ($p->getState() === self::PENDING) { + try { + // Return a resolved promise if onRejected does not throw. + $p->resolve($onRejected($reason)); + } catch (\Throwable $e) { + // onRejected threw, so return a rejected promise. + $p->reject($e); + } catch (\Exception $e) { + // onRejected threw, so return a rejected promise. + $p->reject($e); + } + } + }); + + return $p; + } + + public function otherwise(callable $onRejected) + { + return $this->then(null, $onRejected); + } + + public function wait($unwrap = true, $defaultDelivery = null) + { + if ($unwrap) { + throw exception_for($this->reason); + } + } + + public function getState() + { + return self::REJECTED; + } + + public function resolve($value) + { + throw new \LogicException("Cannot resolve a rejected promise"); + } + + public function reject($reason) + { + if ($reason !== $this->reason) { + throw new \LogicException("Cannot reject a rejected promise"); + } + } + + public function cancel() + { + // pass + } +} diff --git a/vendor/guzzlehttp/promises/src/RejectionException.php b/vendor/guzzlehttp/promises/src/RejectionException.php new file mode 100644 index 00000000000..07c1136da16 --- /dev/null +++ b/vendor/guzzlehttp/promises/src/RejectionException.php @@ -0,0 +1,47 @@ +<?php +namespace GuzzleHttp\Promise; + +/** + * A special exception that is thrown when waiting on a rejected promise. + * + * The reason value is available via the getReason() method. + */ +class RejectionException extends \RuntimeException +{ + /** @var mixed Rejection reason. */ + private $reason; + + /** + * @param mixed $reason Rejection reason. + * @param string $description Optional description + */ + public function __construct($reason, $description = null) + { + $this->reason = $reason; + + $message = 'The promise was rejected'; + + if ($description) { + $message .= ' with reason: ' . $description; + } elseif (is_string($reason) + || (is_object($reason) && method_exists($reason, '__toString')) + ) { + $message .= ' with reason: ' . $this->reason; + } elseif ($reason instanceof \JsonSerializable) { + $message .= ' with reason: ' + . json_encode($this->reason, JSON_PRETTY_PRINT); + } + + parent::__construct($message); + } + + /** + * Returns the rejection reason. + * + * @return mixed + */ + public function getReason() + { + return $this->reason; + } +} diff --git a/vendor/guzzlehttp/promises/src/TaskQueue.php b/vendor/guzzlehttp/promises/src/TaskQueue.php new file mode 100644 index 00000000000..6e8a2a083cd --- /dev/null +++ b/vendor/guzzlehttp/promises/src/TaskQueue.php @@ -0,0 +1,66 @@ +<?php +namespace GuzzleHttp\Promise; + +/** + * A task queue that executes tasks in a FIFO order. + * + * This task queue class is used to settle promises asynchronously and + * maintains a constant stack size. You can use the task queue asynchronously + * by calling the `run()` function of the global task queue in an event loop. + * + * GuzzleHttp\Promise\queue()->run(); + */ +class TaskQueue implements TaskQueueInterface +{ + private $enableShutdown = true; + private $queue = []; + + public function __construct($withShutdown = true) + { + if ($withShutdown) { + register_shutdown_function(function () { + if ($this->enableShutdown) { + // Only run the tasks if an E_ERROR didn't occur. + $err = error_get_last(); + if (!$err || ($err['type'] ^ E_ERROR)) { + $this->run(); + } + } + }); + } + } + + public function isEmpty() + { + return !$this->queue; + } + + public function add(callable $task) + { + $this->queue[] = $task; + } + + public function run() + { + /** @var callable $task */ + while ($task = array_shift($this->queue)) { + $task(); + } + } + + /** + * The task queue will be run and exhausted by default when the process + * exits IFF the exit is not the result of a PHP E_ERROR error. + * + * You can disable running the automatic shutdown of the queue by calling + * this function. If you disable the task queue shutdown process, then you + * MUST either run the task queue (as a result of running your event loop + * or manually using the run() method) or wait on each outstanding promise. + * + * Note: This shutdown will occur before any destructors are triggered. + */ + public function disableShutdown() + { + $this->enableShutdown = false; + } +} diff --git a/vendor/guzzlehttp/promises/src/TaskQueueInterface.php b/vendor/guzzlehttp/promises/src/TaskQueueInterface.php new file mode 100644 index 00000000000..ac8306e1971 --- /dev/null +++ b/vendor/guzzlehttp/promises/src/TaskQueueInterface.php @@ -0,0 +1,25 @@ +<?php +namespace GuzzleHttp\Promise; + +interface TaskQueueInterface +{ + /** + * Returns true if the queue is empty. + * + * @return bool + */ + public function isEmpty(); + + /** + * Adds a task to the queue that will be executed the next time run is + * called. + * + * @param callable $task + */ + public function add(callable $task); + + /** + * Execute all of the pending task in the queue. + */ + public function run(); +} diff --git a/vendor/guzzlehttp/promises/src/functions.php b/vendor/guzzlehttp/promises/src/functions.php new file mode 100644 index 00000000000..4e27709af36 --- /dev/null +++ b/vendor/guzzlehttp/promises/src/functions.php @@ -0,0 +1,457 @@ +<?php +namespace GuzzleHttp\Promise; + +/** + * Get the global task queue used for promise resolution. + * + * This task queue MUST be run in an event loop in order for promises to be + * settled asynchronously. It will be automatically run when synchronously + * waiting on a promise. + * + * <code> + * while ($eventLoop->isRunning()) { + * GuzzleHttp\Promise\queue()->run(); + * } + * </code> + * + * @param TaskQueueInterface $assign Optionally specify a new queue instance. + * + * @return TaskQueueInterface + */ +function queue(TaskQueueInterface $assign = null) +{ + static $queue; + + if ($assign) { + $queue = $assign; + } elseif (!$queue) { + $queue = new TaskQueue(); + } + + return $queue; +} + +/** + * Adds a function to run in the task queue when it is next `run()` and returns + * a promise that is fulfilled or rejected with the result. + * + * @param callable $task Task function to run. + * + * @return PromiseInterface + */ +function task(callable $task) +{ + $queue = queue(); + $promise = new Promise([$queue, 'run']); + $queue->add(function () use ($task, $promise) { + try { + $promise->resolve($task()); + } catch (\Throwable $e) { + $promise->reject($e); + } catch (\Exception $e) { + $promise->reject($e); + } + }); + + return $promise; +} + +/** + * Creates a promise for a value if the value is not a promise. + * + * @param mixed $value Promise or value. + * + * @return PromiseInterface + */ +function promise_for($value) +{ + if ($value instanceof PromiseInterface) { + return $value; + } + + // Return a Guzzle promise that shadows the given promise. + if (method_exists($value, 'then')) { + $wfn = method_exists($value, 'wait') ? [$value, 'wait'] : null; + $cfn = method_exists($value, 'cancel') ? [$value, 'cancel'] : null; + $promise = new Promise($wfn, $cfn); + $value->then([$promise, 'resolve'], [$promise, 'reject']); + return $promise; + } + + return new FulfilledPromise($value); +} + +/** + * Creates a rejected promise for a reason if the reason is not a promise. If + * the provided reason is a promise, then it is returned as-is. + * + * @param mixed $reason Promise or reason. + * + * @return PromiseInterface + */ +function rejection_for($reason) +{ + if ($reason instanceof PromiseInterface) { + return $reason; + } + + return new RejectedPromise($reason); +} + +/** + * Create an exception for a rejected promise value. + * + * @param mixed $reason + * + * @return \Exception|\Throwable + */ +function exception_for($reason) +{ + return $reason instanceof \Exception || $reason instanceof \Throwable + ? $reason + : new RejectionException($reason); +} + +/** + * Returns an iterator for the given value. + * + * @param mixed $value + * + * @return \Iterator + */ +function iter_for($value) +{ + if ($value instanceof \Iterator) { + return $value; + } elseif (is_array($value)) { + return new \ArrayIterator($value); + } else { + return new \ArrayIterator([$value]); + } +} + +/** + * Synchronously waits on a promise to resolve and returns an inspection state + * array. + * + * Returns a state associative array containing a "state" key mapping to a + * valid promise state. If the state of the promise is "fulfilled", the array + * will contain a "value" key mapping to the fulfilled value of the promise. If + * the promise is rejected, the array will contain a "reason" key mapping to + * the rejection reason of the promise. + * + * @param PromiseInterface $promise Promise or value. + * + * @return array + */ +function inspect(PromiseInterface $promise) +{ + try { + return [ + 'state' => PromiseInterface::FULFILLED, + 'value' => $promise->wait() + ]; + } catch (RejectionException $e) { + return ['state' => PromiseInterface::REJECTED, 'reason' => $e->getReason()]; + } catch (\Throwable $e) { + return ['state' => PromiseInterface::REJECTED, 'reason' => $e]; + } catch (\Exception $e) { + return ['state' => PromiseInterface::REJECTED, 'reason' => $e]; + } +} + +/** + * Waits on all of the provided promises, but does not unwrap rejected promises + * as thrown exception. + * + * Returns an array of inspection state arrays. + * + * @param PromiseInterface[] $promises Traversable of promises to wait upon. + * + * @return array + * @see GuzzleHttp\Promise\inspect for the inspection state array format. + */ +function inspect_all($promises) +{ + $results = []; + foreach ($promises as $key => $promise) { + $results[$key] = inspect($promise); + } + + return $results; +} + +/** + * Waits on all of the provided promises and returns the fulfilled values. + * + * Returns an array that contains the value of each promise (in the same order + * the promises were provided). An exception is thrown if any of the promises + * are rejected. + * + * @param mixed $promises Iterable of PromiseInterface objects to wait on. + * + * @return array + * @throws \Exception on error + * @throws \Throwable on error in PHP >=7 + */ +function unwrap($promises) +{ + $results = []; + foreach ($promises as $key => $promise) { + $results[$key] = $promise->wait(); + } + + return $results; +} + +/** + * Given an array of promises, return a promise that is fulfilled when all the + * items in the array are fulfilled. + * + * The promise's fulfillment value is an array with fulfillment values at + * respective positions to the original array. If any promise in the array + * rejects, the returned promise is rejected with the rejection reason. + * + * @param mixed $promises Promises or values. + * + * @return PromiseInterface + */ +function all($promises) +{ + $results = []; + return each( + $promises, + function ($value, $idx) use (&$results) { + $results[$idx] = $value; + }, + function ($reason, $idx, Promise $aggregate) { + $aggregate->reject($reason); + } + )->then(function () use (&$results) { + ksort($results); + return $results; + }); +} + +/** + * Initiate a competitive race between multiple promises or values (values will + * become immediately fulfilled promises). + * + * When count amount of promises have been fulfilled, the returned promise is + * fulfilled with an array that contains the fulfillment values of the winners + * in order of resolution. + * + * This prommise is rejected with a {@see GuzzleHttp\Promise\AggregateException} + * if the number of fulfilled promises is less than the desired $count. + * + * @param int $count Total number of promises. + * @param mixed $promises Promises or values. + * + * @return PromiseInterface + */ +function some($count, $promises) +{ + $results = []; + $rejections = []; + + return each( + $promises, + function ($value, $idx, PromiseInterface $p) use (&$results, $count) { + if ($p->getState() !== PromiseInterface::PENDING) { + return; + } + $results[$idx] = $value; + if (count($results) >= $count) { + $p->resolve(null); + } + }, + function ($reason) use (&$rejections) { + $rejections[] = $reason; + } + )->then( + function () use (&$results, &$rejections, $count) { + if (count($results) !== $count) { + throw new AggregateException( + 'Not enough promises to fulfill count', + $rejections + ); + } + ksort($results); + return array_values($results); + } + ); +} + +/** + * Like some(), with 1 as count. However, if the promise fulfills, the + * fulfillment value is not an array of 1 but the value directly. + * + * @param mixed $promises Promises or values. + * + * @return PromiseInterface + */ +function any($promises) +{ + return some(1, $promises)->then(function ($values) { return $values[0]; }); +} + +/** + * Returns a promise that is fulfilled when all of the provided promises have + * been fulfilled or rejected. + * + * The returned promise is fulfilled with an array of inspection state arrays. + * + * @param mixed $promises Promises or values. + * + * @return PromiseInterface + * @see GuzzleHttp\Promise\inspect for the inspection state array format. + */ +function settle($promises) +{ + $results = []; + + return each( + $promises, + function ($value, $idx) use (&$results) { + $results[$idx] = ['state' => PromiseInterface::FULFILLED, 'value' => $value]; + }, + function ($reason, $idx) use (&$results) { + $results[$idx] = ['state' => PromiseInterface::REJECTED, 'reason' => $reason]; + } + )->then(function () use (&$results) { + ksort($results); + return $results; + }); +} + +/** + * Given an iterator that yields promises or values, returns a promise that is + * fulfilled with a null value when the iterator has been consumed or the + * aggregate promise has been fulfilled or rejected. + * + * $onFulfilled is a function that accepts the fulfilled value, iterator + * index, and the aggregate promise. The callback can invoke any necessary side + * effects and choose to resolve or reject the aggregate promise if needed. + * + * $onRejected is a function that accepts the rejection reason, iterator + * index, and the aggregate promise. The callback can invoke any necessary side + * effects and choose to resolve or reject the aggregate promise if needed. + * + * @param mixed $iterable Iterator or array to iterate over. + * @param callable $onFulfilled + * @param callable $onRejected + * + * @return PromiseInterface + */ +function each( + $iterable, + callable $onFulfilled = null, + callable $onRejected = null +) { + return (new EachPromise($iterable, [ + 'fulfilled' => $onFulfilled, + 'rejected' => $onRejected + ]))->promise(); +} + +/** + * Like each, but only allows a certain number of outstanding promises at any + * given time. + * + * $concurrency may be an integer or a function that accepts the number of + * pending promises and returns a numeric concurrency limit value to allow for + * dynamic a concurrency size. + * + * @param mixed $iterable + * @param int|callable $concurrency + * @param callable $onFulfilled + * @param callable $onRejected + * + * @return PromiseInterface + */ +function each_limit( + $iterable, + $concurrency, + callable $onFulfilled = null, + callable $onRejected = null +) { + return (new EachPromise($iterable, [ + 'fulfilled' => $onFulfilled, + 'rejected' => $onRejected, + 'concurrency' => $concurrency + ]))->promise(); +} + +/** + * Like each_limit, but ensures that no promise in the given $iterable argument + * is rejected. If any promise is rejected, then the aggregate promise is + * rejected with the encountered rejection. + * + * @param mixed $iterable + * @param int|callable $concurrency + * @param callable $onFulfilled + * + * @return PromiseInterface + */ +function each_limit_all( + $iterable, + $concurrency, + callable $onFulfilled = null +) { + return each_limit( + $iterable, + $concurrency, + $onFulfilled, + function ($reason, $idx, PromiseInterface $aggregate) { + $aggregate->reject($reason); + } + ); +} + +/** + * Returns true if a promise is fulfilled. + * + * @param PromiseInterface $promise + * + * @return bool + */ +function is_fulfilled(PromiseInterface $promise) +{ + return $promise->getState() === PromiseInterface::FULFILLED; +} + +/** + * Returns true if a promise is rejected. + * + * @param PromiseInterface $promise + * + * @return bool + */ +function is_rejected(PromiseInterface $promise) +{ + return $promise->getState() === PromiseInterface::REJECTED; +} + +/** + * Returns true if a promise is fulfilled or rejected. + * + * @param PromiseInterface $promise + * + * @return bool + */ +function is_settled(PromiseInterface $promise) +{ + return $promise->getState() !== PromiseInterface::PENDING; +} + +/** + * @see Coroutine + * + * @param callable $generatorFn + * + * @return PromiseInterface + */ +function coroutine(callable $generatorFn) +{ + return new Coroutine($generatorFn); +} diff --git a/vendor/guzzlehttp/promises/src/functions_include.php b/vendor/guzzlehttp/promises/src/functions_include.php new file mode 100644 index 00000000000..34cd1710aa2 --- /dev/null +++ b/vendor/guzzlehttp/promises/src/functions_include.php @@ -0,0 +1,6 @@ +<?php + +// Don't redefine the functions if included multiple times. +if (!function_exists('GuzzleHttp\Promise\promise_for')) { + require __DIR__ . '/functions.php'; +} diff --git a/vendor/guzzlehttp/psr7/CHANGELOG.md b/vendor/guzzlehttp/psr7/CHANGELOG.md new file mode 100644 index 00000000000..5c252b3a208 --- /dev/null +++ b/vendor/guzzlehttp/psr7/CHANGELOG.md @@ -0,0 +1,110 @@ +# CHANGELOG + +## 1.4.2 - 2017-03-20 + +* Reverted BC break to `Uri::resolve` and `Uri::removeDotSegments` by removing + calls to `trigger_error` when deprecated methods are invoked. + +## 1.4.1 - 2017-02-27 + +* Reverted BC break by reintroducing behavior to automagically fix a URI with a + relative path and an authority by adding a leading slash to the path. It's only + deprecated now. +* Added triggering of silenced deprecation warnings. + +## 1.4.0 - 2017-02-21 + +* Fix `Stream::read` when length parameter <= 0. +* `copy_to_stream` reads bytes in chunks instead of `maxLen` into memory. +* Fix `ServerRequest::getUriFromGlobals` when `Host` header contains port. +* Ensure `ServerRequest::getUriFromGlobals` returns a URI in absolute form. +* Allow `parse_response` to parse a response without delimiting space and reason. +* Ensure each URI modification results in a valid URI according to PSR-7 discussions. + Invalid modifications will throw an exception instead of returning a wrong URI or + doing some magic. + - `(new Uri)->withPath('foo')->withHost('example.com')` will throw an exception + because the path of a URI with an authority must start with a slash "/" or be empty + - `(new Uri())->withScheme('http')` will return `'http://localhost'` +* Fix compatibility of URIs with `file` scheme and empty host. +* Added common URI utility methods based on RFC 3986 (see documentation in the readme): + - `Uri::isDefaultPort` + - `Uri::isAbsolute` + - `Uri::isNetworkPathReference` + - `Uri::isAbsolutePathReference` + - `Uri::isRelativePathReference` + - `Uri::isSameDocumentReference` + - `Uri::composeComponents` + - `UriNormalizer::normalize` + - `UriNormalizer::isEquivalent` + - `UriResolver::relativize` +* Deprecated `Uri::resolve` in favor of `UriResolver::resolve` +* Deprecated `Uri::removeDotSegments` in favor of `UriResolver::removeDotSegments` + +## 1.3.1 - 2016-06-25 + +* Fix `Uri::__toString` for network path references, e.g. `//example.org`. +* Fix missing lowercase normalization for host. +* Fix handling of URI components in case they are `'0'` in a lot of places, + e.g. as a user info password. +* Fix `Uri::withAddedHeader` to correctly merge headers with different case. +* Fix trimming of header values in `Uri::withAddedHeader`. Header values may + be surrounded by whitespace which should be ignored according to RFC 7230 + Section 3.2.4. This does not apply to header names. +* Fix `Uri::withAddedHeader` with an array of header values. +* Fix `Uri::resolve` when base path has no slash and handling of fragment. +* Fix handling of encoding in `Uri::with(out)QueryValue` so one can pass the + key/value both in encoded as well as decoded form to those methods. This is + consistent with withPath, withQuery etc. +* Fix `ServerRequest::withoutAttribute` when attribute value is null. + +## 1.3.0 - 2016-04-13 + +* Added remaining interfaces needed for full PSR7 compatibility + (ServerRequestInterface, UploadedFileInterface, etc.). +* Added support for stream_for from scalars. +* Can now extend Uri. +* Fixed a bug in validating request methods by making it more permissive. + +## 1.2.3 - 2016-02-18 + +* Fixed support in `GuzzleHttp\Psr7\CachingStream` for seeking forward on remote + streams, which can sometimes return fewer bytes than requested with `fread`. +* Fixed handling of gzipped responses with FNAME headers. + +## 1.2.2 - 2016-01-22 + +* Added support for URIs without any authority. +* Added support for HTTP 451 'Unavailable For Legal Reasons.' +* Added support for using '0' as a filename. +* Added support for including non-standard ports in Host headers. + +## 1.2.1 - 2015-11-02 + +* Now supporting negative offsets when seeking to SEEK_END. + +## 1.2.0 - 2015-08-15 + +* Body as `"0"` is now properly added to a response. +* Now allowing forward seeking in CachingStream. +* Now properly parsing HTTP requests that contain proxy targets in + `parse_request`. +* functions.php is now conditionally required. +* user-info is no longer dropped when resolving URIs. + +## 1.1.0 - 2015-06-24 + +* URIs can now be relative. +* `multipart/form-data` headers are now overridden case-insensitively. +* URI paths no longer encode the following characters because they are allowed + in URIs: "(", ")", "*", "!", "'" +* A port is no longer added to a URI when the scheme is missing and no port is + present. + +## 1.0.0 - 2015-05-19 + +Initial release. + +Currently unsupported: + +- `Psr\Http\Message\ServerRequestInterface` +- `Psr\Http\Message\UploadedFileInterface` diff --git a/vendor/guzzlehttp/psr7/LICENSE b/vendor/guzzlehttp/psr7/LICENSE new file mode 100644 index 00000000000..581d95f9202 --- /dev/null +++ b/vendor/guzzlehttp/psr7/LICENSE @@ -0,0 +1,19 @@ +Copyright (c) 2015 Michael Dowling, https://github.com/mtdowling <mtdowling@gmail.com> + +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/guzzlehttp/psr7/README.md b/vendor/guzzlehttp/psr7/README.md new file mode 100644 index 00000000000..16499358ea1 --- /dev/null +++ b/vendor/guzzlehttp/psr7/README.md @@ -0,0 +1,739 @@ +# PSR-7 Message Implementation + +This repository contains a full [PSR-7](http://www.php-fig.org/psr/psr-7/) +message implementation, several stream decorators, and some helpful +functionality like query string parsing. + + +[](https://travis-ci.org/guzzle/psr7) + + +# Stream implementation + +This package comes with a number of stream implementations and stream +decorators. + + +## AppendStream + +`GuzzleHttp\Psr7\AppendStream` + +Reads from multiple streams, one after the other. + +```php +use GuzzleHttp\Psr7; + +$a = Psr7\stream_for('abc, '); +$b = Psr7\stream_for('123.'); +$composed = new Psr7\AppendStream([$a, $b]); + +$composed->addStream(Psr7\stream_for(' Above all listen to me')); + +echo $composed; // abc, 123. Above all listen to me. +``` + + +## BufferStream + +`GuzzleHttp\Psr7\BufferStream` + +Provides a buffer stream that can be written to fill a buffer, and read +from to remove bytes from the buffer. + +This stream returns a "hwm" metadata value that tells upstream consumers +what the configured high water mark of the stream is, or the maximum +preferred size of the buffer. + +```php +use GuzzleHttp\Psr7; + +// When more than 1024 bytes are in the buffer, it will begin returning +// false to writes. This is an indication that writers should slow down. +$buffer = new Psr7\BufferStream(1024); +``` + + +## CachingStream + +The CachingStream is used to allow seeking over previously read bytes on +non-seekable streams. This can be useful when transferring a non-seekable +entity body fails due to needing to rewind the stream (for example, resulting +from a redirect). Data that is read from the remote stream will be buffered in +a PHP temp stream so that previously read bytes are cached first in memory, +then on disk. + +```php +use GuzzleHttp\Psr7; + +$original = Psr7\stream_for(fopen('http://www.google.com', 'r')); +$stream = new Psr7\CachingStream($original); + +$stream->read(1024); +echo $stream->tell(); +// 1024 + +$stream->seek(0); +echo $stream->tell(); +// 0 +``` + + +## DroppingStream + +`GuzzleHttp\Psr7\DroppingStream` + +Stream decorator that begins dropping data once the size of the underlying +stream becomes too full. + +```php +use GuzzleHttp\Psr7; + +// Create an empty stream +$stream = Psr7\stream_for(); + +// Start dropping data when the stream has more than 10 bytes +$dropping = new Psr7\DroppingStream($stream, 10); + +$dropping->write('01234567890123456789'); +echo $stream; // 0123456789 +``` + + +## FnStream + +`GuzzleHttp\Psr7\FnStream` + +Compose stream implementations based on a hash of functions. + +Allows for easy testing and extension of a provided stream without needing +to create a concrete class for a simple extension point. + +```php + +use GuzzleHttp\Psr7; + +$stream = Psr7\stream_for('hi'); +$fnStream = Psr7\FnStream::decorate($stream, [ + 'rewind' => function () use ($stream) { + echo 'About to rewind - '; + $stream->rewind(); + echo 'rewound!'; + } +]); + +$fnStream->rewind(); +// Outputs: About to rewind - rewound! +``` + + +## InflateStream + +`GuzzleHttp\Psr7\InflateStream` + +Uses PHP's zlib.inflate filter to inflate deflate or gzipped content. + +This stream decorator skips the first 10 bytes of the given stream to remove +the gzip header, converts the provided stream to a PHP stream resource, +then appends the zlib.inflate filter. The stream is then converted back +to a Guzzle stream resource to be used as a Guzzle stream. + + +## LazyOpenStream + +`GuzzleHttp\Psr7\LazyOpenStream` + +Lazily reads or writes to a file that is opened only after an IO operation +take place on the stream. + +```php +use GuzzleHttp\Psr7; + +$stream = new Psr7\LazyOpenStream('/path/to/file', 'r'); +// The file has not yet been opened... + +echo $stream->read(10); +// The file is opened and read from only when needed. +``` + + +## LimitStream + +`GuzzleHttp\Psr7\LimitStream` + +LimitStream can be used to read a subset or slice of an existing stream object. +This can be useful for breaking a large file into smaller pieces to be sent in +chunks (e.g. Amazon S3's multipart upload API). + +```php +use GuzzleHttp\Psr7; + +$original = Psr7\stream_for(fopen('/tmp/test.txt', 'r+')); +echo $original->getSize(); +// >>> 1048576 + +// Limit the size of the body to 1024 bytes and start reading from byte 2048 +$stream = new Psr7\LimitStream($original, 1024, 2048); +echo $stream->getSize(); +// >>> 1024 +echo $stream->tell(); +// >>> 0 +``` + + +## MultipartStream + +`GuzzleHttp\Psr7\MultipartStream` + +Stream that when read returns bytes for a streaming multipart or +multipart/form-data stream. + + +## NoSeekStream + +`GuzzleHttp\Psr7\NoSeekStream` + +NoSeekStream wraps a stream and does not allow seeking. + +```php +use GuzzleHttp\Psr7; + +$original = Psr7\stream_for('foo'); +$noSeek = new Psr7\NoSeekStream($original); + +echo $noSeek->read(3); +// foo +var_export($noSeek->isSeekable()); +// false +$noSeek->seek(0); +var_export($noSeek->read(3)); +// NULL +``` + + +## PumpStream + +`GuzzleHttp\Psr7\PumpStream` + +Provides a read only stream that pumps data from a PHP callable. + +When invoking the provided callable, the PumpStream will pass the amount of +data requested to read to the callable. The callable can choose to ignore +this value and return fewer or more bytes than requested. Any extra data +returned by the provided callable is buffered internally until drained using +the read() function of the PumpStream. The provided callable MUST return +false when there is no more data to read. + + +## Implementing stream decorators + +Creating a stream decorator is very easy thanks to the +`GuzzleHttp\Psr7\StreamDecoratorTrait`. This trait provides methods that +implement `Psr\Http\Message\StreamInterface` by proxying to an underlying +stream. Just `use` the `StreamDecoratorTrait` and implement your custom +methods. + +For example, let's say we wanted to call a specific function each time the last +byte is read from a stream. This could be implemented by overriding the +`read()` method. + +```php +use Psr\Http\Message\StreamInterface; +use GuzzleHttp\Psr7\StreamDecoratorTrait; + +class EofCallbackStream implements StreamInterface +{ + use StreamDecoratorTrait; + + private $callback; + + public function __construct(StreamInterface $stream, callable $cb) + { + $this->stream = $stream; + $this->callback = $cb; + } + + public function read($length) + { + $result = $this->stream->read($length); + + // Invoke the callback when EOF is hit. + if ($this->eof()) { + call_user_func($this->callback); + } + + return $result; + } +} +``` + +This decorator could be added to any existing stream and used like so: + +```php +use GuzzleHttp\Psr7; + +$original = Psr7\stream_for('foo'); + +$eofStream = new EofCallbackStream($original, function () { + echo 'EOF!'; +}); + +$eofStream->read(2); +$eofStream->read(1); +// echoes "EOF!" +$eofStream->seek(0); +$eofStream->read(3); +// echoes "EOF!" +``` + + +## PHP StreamWrapper + +You can use the `GuzzleHttp\Psr7\StreamWrapper` class if you need to use a +PSR-7 stream as a PHP stream resource. + +Use the `GuzzleHttp\Psr7\StreamWrapper::getResource()` method to create a PHP +stream from a PSR-7 stream. + +```php +use GuzzleHttp\Psr7\StreamWrapper; + +$stream = GuzzleHttp\Psr7\stream_for('hello!'); +$resource = StreamWrapper::getResource($stream); +echo fread($resource, 6); // outputs hello! +``` + + +# Function API + +There are various functions available under the `GuzzleHttp\Psr7` namespace. + + +## `function str` + +`function str(MessageInterface $message)` + +Returns the string representation of an HTTP message. + +```php +$request = new GuzzleHttp\Psr7\Request('GET', 'http://example.com'); +echo GuzzleHttp\Psr7\str($request); +``` + + +## `function uri_for` + +`function uri_for($uri)` + +This function accepts a string or `Psr\Http\Message\UriInterface` and returns a +UriInterface for the given value. If the value is already a `UriInterface`, it +is returned as-is. + +```php +$uri = GuzzleHttp\Psr7\uri_for('http://example.com'); +assert($uri === GuzzleHttp\Psr7\uri_for($uri)); +``` + + +## `function stream_for` + +`function stream_for($resource = '', array $options = [])` + +Create a new stream based on the input type. + +Options is an associative array that can contain the following keys: + +* - metadata: Array of custom metadata. +* - size: Size of the stream. + +This method accepts the following `$resource` types: + +- `Psr\Http\Message\StreamInterface`: Returns the value as-is. +- `string`: Creates a stream object that uses the given string as the contents. +- `resource`: Creates a stream object that wraps the given PHP stream resource. +- `Iterator`: If the provided value implements `Iterator`, then a read-only + stream object will be created that wraps the given iterable. Each time the + stream is read from, data from the iterator will fill a buffer and will be + continuously called until the buffer is equal to the requested read size. + Subsequent read calls will first read from the buffer and then call `next` + on the underlying iterator until it is exhausted. +- `object` with `__toString()`: If the object has the `__toString()` method, + the object will be cast to a string and then a stream will be returned that + uses the string value. +- `NULL`: When `null` is passed, an empty stream object is returned. +- `callable` When a callable is passed, a read-only stream object will be + created that invokes the given callable. The callable is invoked with the + number of suggested bytes to read. The callable can return any number of + bytes, but MUST return `false` when there is no more data to return. The + stream object that wraps the callable will invoke the callable until the + number of requested bytes are available. Any additional bytes will be + buffered and used in subsequent reads. + +```php +$stream = GuzzleHttp\Psr7\stream_for('foo'); +$stream = GuzzleHttp\Psr7\stream_for(fopen('/path/to/file', 'r')); + +$generator function ($bytes) { + for ($i = 0; $i < $bytes; $i++) { + yield ' '; + } +} + +$stream = GuzzleHttp\Psr7\stream_for($generator(100)); +``` + + +## `function parse_header` + +`function parse_header($header)` + +Parse an array of header values containing ";" separated data into an array of +associative arrays representing the header key value pair data of the header. +When a parameter does not contain a value, but just contains a key, this +function will inject a key with a '' string value. + + +## `function normalize_header` + +`function normalize_header($header)` + +Converts an array of header values that may contain comma separated headers +into an array of headers with no comma separated values. + + +## `function modify_request` + +`function modify_request(RequestInterface $request, array $changes)` + +Clone and modify a request with the given changes. This method is useful for +reducing the number of clones needed to mutate a message. + +The changes can be one of: + +- method: (string) Changes the HTTP method. +- set_headers: (array) Sets the given headers. +- remove_headers: (array) Remove the given headers. +- body: (mixed) Sets the given body. +- uri: (UriInterface) Set the URI. +- query: (string) Set the query string value of the URI. +- version: (string) Set the protocol version. + + +## `function rewind_body` + +`function rewind_body(MessageInterface $message)` + +Attempts to rewind a message body and throws an exception on failure. The body +of the message will only be rewound if a call to `tell()` returns a value other +than `0`. + + +## `function try_fopen` + +`function try_fopen($filename, $mode)` + +Safely opens a PHP stream resource using a filename. + +When fopen fails, PHP normally raises a warning. This function adds an error +handler that checks for errors and throws an exception instead. + + +## `function copy_to_string` + +`function copy_to_string(StreamInterface $stream, $maxLen = -1)` + +Copy the contents of a stream into a string until the given number of bytes +have been read. + + +## `function copy_to_stream` + +`function copy_to_stream(StreamInterface $source, StreamInterface $dest, $maxLen = -1)` + +Copy the contents of a stream into another stream until the given number of +bytes have been read. + + +## `function hash` + +`function hash(StreamInterface $stream, $algo, $rawOutput = false)` + +Calculate a hash of a Stream. This method reads the entire stream to calculate +a rolling hash (based on PHP's hash_init functions). + + +## `function readline` + +`function readline(StreamInterface $stream, $maxLength = null)` + +Read a line from the stream up to the maximum allowed buffer length. + + +## `function parse_request` + +`function parse_request($message)` + +Parses a request message string into a request object. + + +## `function parse_response` + +`function parse_response($message)` + +Parses a response message string into a response object. + + +## `function parse_query` + +`function parse_query($str, $urlEncoding = true)` + +Parse a query string into an associative array. + +If multiple values are found for the same key, the value of that key value pair +will become an array. This function does not parse nested PHP style arrays into +an associative array (e.g., `foo[a]=1&foo[b]=2` will be parsed into +`['foo[a]' => '1', 'foo[b]' => '2']`). + + +## `function build_query` + +`function build_query(array $params, $encoding = PHP_QUERY_RFC3986)` + +Build a query string from an array of key value pairs. + +This function can use the return value of parse_query() to build a query string. +This function does not modify the provided keys when an array is encountered +(like http_build_query would). + + +## `function mimetype_from_filename` + +`function mimetype_from_filename($filename)` + +Determines the mimetype of a file by looking at its extension. + + +## `function mimetype_from_extension` + +`function mimetype_from_extension($extension)` + +Maps a file extensions to a mimetype. + + +# Additional URI Methods + +Aside from the standard `Psr\Http\Message\UriInterface` implementation in form of the `GuzzleHttp\Psr7\Uri` class, +this library also provides additional functionality when working with URIs as static methods. + +## URI Types + +An instance of `Psr\Http\Message\UriInterface` can either be an absolute URI or a relative reference. +An absolute URI has a scheme. A relative reference is used to express a URI relative to another URI, +the base URI. Relative references can be divided into several forms according to +[RFC 3986 Section 4.2](https://tools.ietf.org/html/rfc3986#section-4.2): + +- network-path references, e.g. `//example.com/path` +- absolute-path references, e.g. `/path` +- relative-path references, e.g. `subpath` + +The following methods can be used to identify the type of the URI. + +### `GuzzleHttp\Psr7\Uri::isAbsolute` + +`public static function isAbsolute(UriInterface $uri): bool` + +Whether the URI is absolute, i.e. it has a scheme. + +### `GuzzleHttp\Psr7\Uri::isNetworkPathReference` + +`public static function isNetworkPathReference(UriInterface $uri): bool` + +Whether the URI is a network-path reference. A relative reference that begins with two slash characters is +termed an network-path reference. + +### `GuzzleHttp\Psr7\Uri::isAbsolutePathReference` + +`public static function isAbsolutePathReference(UriInterface $uri): bool` + +Whether the URI is a absolute-path reference. A relative reference that begins with a single slash character is +termed an absolute-path reference. + +### `GuzzleHttp\Psr7\Uri::isRelativePathReference` + +`public static function isRelativePathReference(UriInterface $uri): bool` + +Whether the URI is a relative-path reference. A relative reference that does not begin with a slash character is +termed a relative-path reference. + +### `GuzzleHttp\Psr7\Uri::isSameDocumentReference` + +`public static function isSameDocumentReference(UriInterface $uri, UriInterface $base = null): bool` + +Whether the URI is a same-document reference. A same-document reference refers to a URI that is, aside from its +fragment component, identical to the base URI. When no base URI is given, only an empty URI reference +(apart from its fragment) is considered a same-document reference. + +## URI Components + +Additional methods to work with URI components. + +### `GuzzleHttp\Psr7\Uri::isDefaultPort` + +`public static function isDefaultPort(UriInterface $uri): bool` + +Whether the URI has the default port of the current scheme. `Psr\Http\Message\UriInterface::getPort` may return null +or the standard port. This method can be used independently of the implementation. + +### `GuzzleHttp\Psr7\Uri::composeComponents` + +`public static function composeComponents($scheme, $authority, $path, $query, $fragment): string` + +Composes a URI reference string from its various components according to +[RFC 3986 Section 5.3](https://tools.ietf.org/html/rfc3986#section-5.3). Usually this method does not need to be called +manually but instead is used indirectly via `Psr\Http\Message\UriInterface::__toString`. + +### `GuzzleHttp\Psr7\Uri::fromParts` + +`public static function fromParts(array $parts): UriInterface` + +Creates a URI from a hash of [`parse_url`](http://php.net/manual/en/function.parse-url.php) components. + + +### `GuzzleHttp\Psr7\Uri::withQueryValue` + +`public static function withQueryValue(UriInterface $uri, $key, $value): UriInterface` + +Creates a new URI with a specific query string value. Any existing query string values that exactly match the +provided key are removed and replaced with the given key value pair. A value of null will set the query string +key without a value, e.g. "key" instead of "key=value". + + +### `GuzzleHttp\Psr7\Uri::withoutQueryValue` + +`public static function withoutQueryValue(UriInterface $uri, $key): UriInterface` + +Creates a new URI with a specific query string value removed. Any existing query string values that exactly match the +provided key are removed. + +## Reference Resolution + +`GuzzleHttp\Psr7\UriResolver` provides methods to resolve a URI reference in the context of a base URI according +to [RFC 3986 Section 5](https://tools.ietf.org/html/rfc3986#section-5). This is for example also what web browsers +do when resolving a link in a website based on the current request URI. + +### `GuzzleHttp\Psr7\UriResolver::resolve` + +`public static function resolve(UriInterface $base, UriInterface $rel): UriInterface` + +Converts the relative URI into a new URI that is resolved against the base URI. + +### `GuzzleHttp\Psr7\UriResolver::removeDotSegments` + +`public static function removeDotSegments(string $path): string` + +Removes dot segments from a path and returns the new path according to +[RFC 3986 Section 5.2.4](https://tools.ietf.org/html/rfc3986#section-5.2.4). + +### `GuzzleHttp\Psr7\UriResolver::relativize` + +`public static function relativize(UriInterface $base, UriInterface $target): UriInterface` + +Returns the target URI as a relative reference from the base URI. This method is the counterpart to resolve(): + +```php +(string) $target === (string) UriResolver::resolve($base, UriResolver::relativize($base, $target)) +``` + +One use-case is to use the current request URI as base URI and then generate relative links in your documents +to reduce the document size or offer self-contained downloadable document archives. + +```php +$base = new Uri('http://example.com/a/b/'); +echo UriResolver::relativize($base, new Uri('http://example.com/a/b/c')); // prints 'c'. +echo UriResolver::relativize($base, new Uri('http://example.com/a/x/y')); // prints '../x/y'. +echo UriResolver::relativize($base, new Uri('http://example.com/a/b/?q')); // prints '?q'. +echo UriResolver::relativize($base, new Uri('http://example.org/a/b/')); // prints '//example.org/a/b/'. +``` + +## Normalization and Comparison + +`GuzzleHttp\Psr7\UriNormalizer` provides methods to normalize and compare URIs according to +[RFC 3986 Section 6](https://tools.ietf.org/html/rfc3986#section-6). + +### `GuzzleHttp\Psr7\UriNormalizer::normalize` + +`public static function normalize(UriInterface $uri, $flags = self::PRESERVING_NORMALIZATIONS): UriInterface` + +Returns a normalized URI. The scheme and host component are already normalized to lowercase per PSR-7 UriInterface. +This methods adds additional normalizations that can be configured with the `$flags` parameter which is a bitmask +of normalizations to apply. The following normalizations are available: + +- `UriNormalizer::PRESERVING_NORMALIZATIONS` + + Default normalizations which only include the ones that preserve semantics. + +- `UriNormalizer::CAPITALIZE_PERCENT_ENCODING` + + All letters within a percent-encoding triplet (e.g., "%3A") are case-insensitive, and should be capitalized. + + Example: `http://example.org/a%c2%b1b` → `http://example.org/a%C2%B1b` + +- `UriNormalizer::DECODE_UNRESERVED_CHARACTERS` + + Decodes percent-encoded octets of unreserved characters. For consistency, percent-encoded octets in the ranges of + ALPHA (%41–%5A and %61–%7A), DIGIT (%30–%39), hyphen (%2D), period (%2E), underscore (%5F), or tilde (%7E) should + not be created by URI producers and, when found in a URI, should be decoded to their corresponding unreserved + characters by URI normalizers. + + Example: `http://example.org/%7Eusern%61me/` → `http://example.org/~username/` + +- `UriNormalizer::CONVERT_EMPTY_PATH` + + Converts the empty path to "/" for http and https URIs. + + Example: `http://example.org` → `http://example.org/` + +- `UriNormalizer::REMOVE_DEFAULT_HOST` + + Removes the default host of the given URI scheme from the URI. Only the "file" scheme defines the default host + "localhost". All of `file:/myfile`, `file:///myfile`, and `file://localhost/myfile` are equivalent according to + RFC 3986. + + Example: `file://localhost/myfile` → `file:///myfile` + +- `UriNormalizer::REMOVE_DEFAULT_PORT` + + Removes the default port of the given URI scheme from the URI. + + Example: `http://example.org:80/` → `http://example.org/` + +- `UriNormalizer::REMOVE_DOT_SEGMENTS` + + Removes unnecessary dot-segments. Dot-segments in relative-path references are not removed as it would + change the semantics of the URI reference. + + Example: `http://example.org/../a/b/../c/./d.html` → `http://example.org/a/c/d.html` + +- `UriNormalizer::REMOVE_DUPLICATE_SLASHES` + + Paths which include two or more adjacent slashes are converted to one. Webservers usually ignore duplicate slashes + and treat those URIs equivalent. But in theory those URIs do not need to be equivalent. So this normalization + may change the semantics. Encoded slashes (%2F) are not removed. + + Example: `http://example.org//foo///bar.html` → `http://example.org/foo/bar.html` + +- `UriNormalizer::SORT_QUERY_PARAMETERS` + + Sort query parameters with their values in alphabetical order. However, the order of parameters in a URI may be + significant (this is not defined by the standard). So this normalization is not safe and may change the semantics + of the URI. + + Example: `?lang=en&article=fred` → `?article=fred&lang=en` + +### `GuzzleHttp\Psr7\UriNormalizer::isEquivalent` + +`public static function isEquivalent(UriInterface $uri1, UriInterface $uri2, $normalizations = self::PRESERVING_NORMALIZATIONS): bool` + +Whether two URIs can be considered equivalent. Both URIs are normalized automatically before comparison with the given +`$normalizations` bitmask. The method also accepts relative URI references and returns true when they are equivalent. +This of course assumes they will be resolved against the same base URI. If this is not the case, determination of +equivalence or difference of relative references does not mean anything. diff --git a/vendor/guzzlehttp/psr7/composer.json b/vendor/guzzlehttp/psr7/composer.json new file mode 100644 index 00000000000..b1c5a90ba71 --- /dev/null +++ b/vendor/guzzlehttp/psr7/composer.json @@ -0,0 +1,39 @@ +{ + "name": "guzzlehttp/psr7", + "type": "library", + "description": "PSR-7 message implementation that also provides common utility methods", + "keywords": ["request", "response", "message", "stream", "http", "uri", "url"], + "license": "MIT", + "authors": [ + { + "name": "Michael Dowling", + "email": "mtdowling@gmail.com", + "homepage": "https://github.com/mtdowling" + }, + { + "name": "Tobias Schultze", + "homepage": "https://github.com/Tobion" + } + ], + "require": { + "php": ">=5.4.0", + "psr/http-message": "~1.0" + }, + "require-dev": { + "phpunit/phpunit": "~4.0" + }, + "provide": { + "psr/http-message-implementation": "1.0" + }, + "autoload": { + "psr-4": { + "GuzzleHttp\\Psr7\\": "src/" + }, + "files": ["src/functions_include.php"] + }, + "extra": { + "branch-alias": { + "dev-master": "1.4-dev" + } + } +} diff --git a/vendor/guzzlehttp/psr7/src/AppendStream.php b/vendor/guzzlehttp/psr7/src/AppendStream.php new file mode 100644 index 00000000000..23039fd794b --- /dev/null +++ b/vendor/guzzlehttp/psr7/src/AppendStream.php @@ -0,0 +1,233 @@ +<?php +namespace GuzzleHttp\Psr7; + +use Psr\Http\Message\StreamInterface; + +/** + * Reads from multiple streams, one after the other. + * + * This is a read-only stream decorator. + */ +class AppendStream implements StreamInterface +{ + /** @var StreamInterface[] Streams being decorated */ + private $streams = []; + + private $seekable = true; + private $current = 0; + private $pos = 0; + private $detached = false; + + /** + * @param StreamInterface[] $streams Streams to decorate. Each stream must + * be readable. + */ + public function __construct(array $streams = []) + { + foreach ($streams as $stream) { + $this->addStream($stream); + } + } + + public function __toString() + { + try { + $this->rewind(); + return $this->getContents(); + } catch (\Exception $e) { + return ''; + } + } + + /** + * Add a stream to the AppendStream + * + * @param StreamInterface $stream Stream to append. Must be readable. + * + * @throws \InvalidArgumentException if the stream is not readable + */ + public function addStream(StreamInterface $stream) + { + if (!$stream->isReadable()) { + throw new \InvalidArgumentException('Each stream must be readable'); + } + + // The stream is only seekable if all streams are seekable + if (!$stream->isSeekable()) { + $this->seekable = false; + } + + $this->streams[] = $stream; + } + + public function getContents() + { + return copy_to_string($this); + } + + /** + * Closes each attached stream. + * + * {@inheritdoc} + */ + public function close() + { + $this->pos = $this->current = 0; + + foreach ($this->streams as $stream) { + $stream->close(); + } + + $this->streams = []; + } + + /** + * Detaches each attached stream + * + * {@inheritdoc} + */ + public function detach() + { + $this->close(); + $this->detached = true; + } + + public function tell() + { + return $this->pos; + } + + /** + * Tries to calculate the size by adding the size of each stream. + * + * If any of the streams do not return a valid number, then the size of the + * append stream cannot be determined and null is returned. + * + * {@inheritdoc} + */ + public function getSize() + { + $size = 0; + + foreach ($this->streams as $stream) { + $s = $stream->getSize(); + if ($s === null) { + return null; + } + $size += $s; + } + + return $size; + } + + public function eof() + { + return !$this->streams || + ($this->current >= count($this->streams) - 1 && + $this->streams[$this->current]->eof()); + } + + public function rewind() + { + $this->seek(0); + } + + /** + * Attempts to seek to the given position. Only supports SEEK_SET. + * + * {@inheritdoc} + */ + public function seek($offset, $whence = SEEK_SET) + { + if (!$this->seekable) { + throw new \RuntimeException('This AppendStream is not seekable'); + } elseif ($whence !== SEEK_SET) { + throw new \RuntimeException('The AppendStream can only seek with SEEK_SET'); + } + + $this->pos = $this->current = 0; + + // Rewind each stream + foreach ($this->streams as $i => $stream) { + try { + $stream->rewind(); + } catch (\Exception $e) { + throw new \RuntimeException('Unable to seek stream ' + . $i . ' of the AppendStream', 0, $e); + } + } + + // Seek to the actual position by reading from each stream + while ($this->pos < $offset && !$this->eof()) { + $result = $this->read(min(8096, $offset - $this->pos)); + if ($result === '') { + break; + } + } + } + + /** + * Reads from all of the appended streams until the length is met or EOF. + * + * {@inheritdoc} + */ + public function read($length) + { + $buffer = ''; + $total = count($this->streams) - 1; + $remaining = $length; + $progressToNext = false; + + while ($remaining > 0) { + + // Progress to the next stream if needed. + if ($progressToNext || $this->streams[$this->current]->eof()) { + $progressToNext = false; + if ($this->current === $total) { + break; + } + $this->current++; + } + + $result = $this->streams[$this->current]->read($remaining); + + // Using a loose comparison here to match on '', false, and null + if ($result == null) { + $progressToNext = true; + continue; + } + + $buffer .= $result; + $remaining = $length - strlen($buffer); + } + + $this->pos += strlen($buffer); + + return $buffer; + } + + public function isReadable() + { + return true; + } + + public function isWritable() + { + return false; + } + + public function isSeekable() + { + return $this->seekable; + } + + public function write($string) + { + throw new \RuntimeException('Cannot write to an AppendStream'); + } + + public function getMetadata($key = null) + { + return $key ? null : []; + } +} diff --git a/vendor/guzzlehttp/psr7/src/BufferStream.php b/vendor/guzzlehttp/psr7/src/BufferStream.php new file mode 100644 index 00000000000..af4d4c22777 --- /dev/null +++ b/vendor/guzzlehttp/psr7/src/BufferStream.php @@ -0,0 +1,137 @@ +<?php +namespace GuzzleHttp\Psr7; + +use Psr\Http\Message\StreamInterface; + +/** + * Provides a buffer stream that can be written to to fill a buffer, and read + * from to remove bytes from the buffer. + * + * This stream returns a "hwm" metadata value that tells upstream consumers + * what the configured high water mark of the stream is, or the maximum + * preferred size of the buffer. + */ +class BufferStream implements StreamInterface +{ + private $hwm; + private $buffer = ''; + + /** + * @param int $hwm High water mark, representing the preferred maximum + * buffer size. If the size of the buffer exceeds the high + * water mark, then calls to write will continue to succeed + * but will return false to inform writers to slow down + * until the buffer has been drained by reading from it. + */ + public function __construct($hwm = 16384) + { + $this->hwm = $hwm; + } + + public function __toString() + { + return $this->getContents(); + } + + public function getContents() + { + $buffer = $this->buffer; + $this->buffer = ''; + + return $buffer; + } + + public function close() + { + $this->buffer = ''; + } + + public function detach() + { + $this->close(); + } + + public function getSize() + { + return strlen($this->buffer); + } + + public function isReadable() + { + return true; + } + + public function isWritable() + { + return true; + } + + public function isSeekable() + { + return false; + } + + public function rewind() + { + $this->seek(0); + } + + public function seek($offset, $whence = SEEK_SET) + { + throw new \RuntimeException('Cannot seek a BufferStream'); + } + + public function eof() + { + return strlen($this->buffer) === 0; + } + + public function tell() + { + throw new \RuntimeException('Cannot determine the position of a BufferStream'); + } + + /** + * Reads data from the buffer. + */ + public function read($length) + { + $currentLength = strlen($this->buffer); + + if ($length >= $currentLength) { + // No need to slice the buffer because we don't have enough data. + $result = $this->buffer; + $this->buffer = ''; + } else { + // Slice up the result to provide a subset of the buffer. + $result = substr($this->buffer, 0, $length); + $this->buffer = substr($this->buffer, $length); + } + + return $result; + } + + /** + * Writes data to the buffer. + */ + public function write($string) + { + $this->buffer .= $string; + + // TODO: What should happen here? + if (strlen($this->buffer) >= $this->hwm) { + return false; + } + + return strlen($string); + } + + public function getMetadata($key = null) + { + if ($key == 'hwm') { + return $this->hwm; + } + + return $key ? null : []; + } +} diff --git a/vendor/guzzlehttp/psr7/src/CachingStream.php b/vendor/guzzlehttp/psr7/src/CachingStream.php new file mode 100644 index 00000000000..ed68f0861aa --- /dev/null +++ b/vendor/guzzlehttp/psr7/src/CachingStream.php @@ -0,0 +1,138 @@ +<?php +namespace GuzzleHttp\Psr7; + +use Psr\Http\Message\StreamInterface; + +/** + * Stream decorator that can cache previously read bytes from a sequentially + * read stream. + */ +class CachingStream implements StreamInterface +{ + use StreamDecoratorTrait; + + /** @var StreamInterface Stream being wrapped */ + private $remoteStream; + + /** @var int Number of bytes to skip reading due to a write on the buffer */ + private $skipReadBytes = 0; + + /** + * We will treat the buffer object as the body of the stream + * + * @param StreamInterface $stream Stream to cache + * @param StreamInterface $target Optionally specify where data is cached + */ + public function __construct( + StreamInterface $stream, + StreamInterface $target = null + ) { + $this->remoteStream = $stream; + $this->stream = $target ?: new Stream(fopen('php://temp', 'r+')); + } + + public function getSize() + { + return max($this->stream->getSize(), $this->remoteStream->getSize()); + } + + public function rewind() + { + $this->seek(0); + } + + public function seek($offset, $whence = SEEK_SET) + { + if ($whence == SEEK_SET) { + $byte = $offset; + } elseif ($whence == SEEK_CUR) { + $byte = $offset + $this->tell(); + } elseif ($whence == SEEK_END) { + $size = $this->remoteStream->getSize(); + if ($size === null) { + $size = $this->cacheEntireStream(); + } + $byte = $size + $offset; + } else { + throw new \InvalidArgumentException('Invalid whence'); + } + + $diff = $byte - $this->stream->getSize(); + + if ($diff > 0) { + // Read the remoteStream until we have read in at least the amount + // of bytes requested, or we reach the end of the file. + while ($diff > 0 && !$this->remoteStream->eof()) { + $this->read($diff); + $diff = $byte - $this->stream->getSize(); + } + } else { + // We can just do a normal seek since we've already seen this byte. + $this->stream->seek($byte); + } + } + + public function read($length) + { + // Perform a regular read on any previously read data from the buffer + $data = $this->stream->read($length); + $remaining = $length - strlen($data); + + // More data was requested so read from the remote stream + if ($remaining) { + // If data was written to the buffer in a position that would have + // been filled from the remote stream, then we must skip bytes on + // the remote stream to emulate overwriting bytes from that + // position. This mimics the behavior of other PHP stream wrappers. + $remoteData = $this->remoteStream->read( + $remaining + $this->skipReadBytes + ); + + if ($this->skipReadBytes) { + $len = strlen($remoteData); + $remoteData = substr($remoteData, $this->skipReadBytes); + $this->skipReadBytes = max(0, $this->skipReadBytes - $len); + } + + $data .= $remoteData; + $this->stream->write($remoteData); + } + + return $data; + } + + public function write($string) + { + // When appending to the end of the currently read stream, you'll want + // to skip bytes from being read from the remote stream to emulate + // other stream wrappers. Basically replacing bytes of data of a fixed + // length. + $overflow = (strlen($string) + $this->tell()) - $this->remoteStream->tell(); + if ($overflow > 0) { + $this->skipReadBytes += $overflow; + } + + return $this->stream->write($string); + } + + public function eof() + { + return $this->stream->eof() && $this->remoteStream->eof(); + } + + /** + * Close both the remote stream and buffer stream + */ + public function close() + { + $this->remoteStream->close() && $this->stream->close(); + } + + private function cacheEntireStream() + { + $target = new FnStream(['write' => 'strlen']); + copy_to_stream($this, $target); + + return $this->tell(); + } +} diff --git a/vendor/guzzlehttp/psr7/src/DroppingStream.php b/vendor/guzzlehttp/psr7/src/DroppingStream.php new file mode 100644 index 00000000000..8935c80d729 --- /dev/null +++ b/vendor/guzzlehttp/psr7/src/DroppingStream.php @@ -0,0 +1,42 @@ +<?php +namespace GuzzleHttp\Psr7; + +use Psr\Http\Message\StreamInterface; + +/** + * Stream decorator that begins dropping data once the size of the underlying + * stream becomes too full. + */ +class DroppingStream implements StreamInterface +{ + use StreamDecoratorTrait; + + private $maxLength; + + /** + * @param StreamInterface $stream Underlying stream to decorate. + * @param int $maxLength Maximum size before dropping data. + */ + public function __construct(StreamInterface $stream, $maxLength) + { + $this->stream = $stream; + $this->maxLength = $maxLength; + } + + public function write($string) + { + $diff = $this->maxLength - $this->stream->getSize(); + + // Begin returning 0 when the underlying stream is too large. + if ($diff <= 0) { + return 0; + } + + // Write the stream or a subset of the stream if needed. + if (strlen($string) < $diff) { + return $this->stream->write($string); + } + + return $this->stream->write(substr($string, 0, $diff)); + } +} diff --git a/vendor/guzzlehttp/psr7/src/FnStream.php b/vendor/guzzlehttp/psr7/src/FnStream.php new file mode 100644 index 00000000000..cc9b4453f71 --- /dev/null +++ b/vendor/guzzlehttp/psr7/src/FnStream.php @@ -0,0 +1,149 @@ +<?php +namespace GuzzleHttp\Psr7; + +use Psr\Http\Message\StreamInterface; + +/** + * Compose stream implementations based on a hash of functions. + * + * Allows for easy testing and extension of a provided stream without needing + * to create a concrete class for a simple extension point. + */ +class FnStream implements StreamInterface +{ + /** @var array */ + private $methods; + + /** @var array Methods that must be implemented in the given array */ + private static $slots = ['__toString', 'close', 'detach', 'rewind', + 'getSize', 'tell', 'eof', 'isSeekable', 'seek', 'isWritable', 'write', + 'isReadable', 'read', 'getContents', 'getMetadata']; + + /** + * @param array $methods Hash of method name to a callable. + */ + public function __construct(array $methods) + { + $this->methods = $methods; + + // Create the functions on the class + foreach ($methods as $name => $fn) { + $this->{'_fn_' . $name} = $fn; + } + } + + /** + * Lazily determine which methods are not implemented. + * @throws \BadMethodCallException + */ + public function __get($name) + { + throw new \BadMethodCallException(str_replace('_fn_', '', $name) + . '() is not implemented in the FnStream'); + } + + /** + * The close method is called on the underlying stream only if possible. + */ + public function __destruct() + { + if (isset($this->_fn_close)) { + call_user_func($this->_fn_close); + } + } + + /** + * Adds custom functionality to an underlying stream by intercepting + * specific method calls. + * + * @param StreamInterface $stream Stream to decorate + * @param array $methods Hash of method name to a closure + * + * @return FnStream + */ + public static function decorate(StreamInterface $stream, array $methods) + { + // If any of the required methods were not provided, then simply + // proxy to the decorated stream. + foreach (array_diff(self::$slots, array_keys($methods)) as $diff) { + $methods[$diff] = [$stream, $diff]; + } + + return new self($methods); + } + + public function __toString() + { + return call_user_func($this->_fn___toString); + } + + public function close() + { + return call_user_func($this->_fn_close); + } + + public function detach() + { + return call_user_func($this->_fn_detach); + } + + public function getSize() + { + return call_user_func($this->_fn_getSize); + } + + public function tell() + { + return call_user_func($this->_fn_tell); + } + + public function eof() + { + return call_user_func($this->_fn_eof); + } + + public function isSeekable() + { + return call_user_func($this->_fn_isSeekable); + } + + public function rewind() + { + call_user_func($this->_fn_rewind); + } + + public function seek($offset, $whence = SEEK_SET) + { + call_user_func($this->_fn_seek, $offset, $whence); + } + + public function isWritable() + { + return call_user_func($this->_fn_isWritable); + } + + public function write($string) + { + return call_user_func($this->_fn_write, $string); + } + + public function isReadable() + { + return call_user_func($this->_fn_isReadable); + } + + public function read($length) + { + return call_user_func($this->_fn_read, $length); + } + + public function getContents() + { + return call_user_func($this->_fn_getContents); + } + + public function getMetadata($key = null) + { + return call_user_func($this->_fn_getMetadata, $key); + } +} diff --git a/vendor/guzzlehttp/psr7/src/InflateStream.php b/vendor/guzzlehttp/psr7/src/InflateStream.php new file mode 100644 index 00000000000..0051d3fec5a --- /dev/null +++ b/vendor/guzzlehttp/psr7/src/InflateStream.php @@ -0,0 +1,52 @@ +<?php +namespace GuzzleHttp\Psr7; + +use Psr\Http\Message\StreamInterface; + +/** + * Uses PHP's zlib.inflate filter to inflate deflate or gzipped content. + * + * This stream decorator skips the first 10 bytes of the given stream to remove + * the gzip header, converts the provided stream to a PHP stream resource, + * then appends the zlib.inflate filter. The stream is then converted back + * to a Guzzle stream resource to be used as a Guzzle stream. + * + * @link http://tools.ietf.org/html/rfc1952 + * @link http://php.net/manual/en/filters.compression.php + */ +class InflateStream implements StreamInterface +{ + use StreamDecoratorTrait; + + public function __construct(StreamInterface $stream) + { + // read the first 10 bytes, ie. gzip header + $header = $stream->read(10); + $filenameHeaderLength = $this->getLengthOfPossibleFilenameHeader($stream, $header); + // Skip the header, that is 10 + length of filename + 1 (nil) bytes + $stream = new LimitStream($stream, -1, 10 + $filenameHeaderLength); + $resource = StreamWrapper::getResource($stream); + stream_filter_append($resource, 'zlib.inflate', STREAM_FILTER_READ); + $this->stream = new Stream($resource); + } + + /** + * @param StreamInterface $stream + * @param $header + * @return int + */ + private function getLengthOfPossibleFilenameHeader(StreamInterface $stream, $header) + { + $filename_header_length = 0; + + if (substr(bin2hex($header), 6, 2) === '08') { + // we have a filename, read until nil + $filename_header_length = 1; + while ($stream->read(1) !== chr(0)) { + $filename_header_length++; + } + } + + return $filename_header_length; + } +} diff --git a/vendor/guzzlehttp/psr7/src/LazyOpenStream.php b/vendor/guzzlehttp/psr7/src/LazyOpenStream.php new file mode 100644 index 00000000000..02cec3af493 --- /dev/null +++ b/vendor/guzzlehttp/psr7/src/LazyOpenStream.php @@ -0,0 +1,39 @@ +<?php +namespace GuzzleHttp\Psr7; + +use Psr\Http\Message\StreamInterface; + +/** + * Lazily reads or writes to a file that is opened only after an IO operation + * take place on the stream. + */ +class LazyOpenStream implements StreamInterface +{ + use StreamDecoratorTrait; + + /** @var string File to open */ + private $filename; + + /** @var string $mode */ + private $mode; + + /** + * @param string $filename File to lazily open + * @param string $mode fopen mode to use when opening the stream + */ + public function __construct($filename, $mode) + { + $this->filename = $filename; + $this->mode = $mode; + } + + /** + * Creates the underlying stream lazily when required. + * + * @return StreamInterface + */ + protected function createStream() + { + return stream_for(try_fopen($this->filename, $this->mode)); + } +} diff --git a/vendor/guzzlehttp/psr7/src/LimitStream.php b/vendor/guzzlehttp/psr7/src/LimitStream.php new file mode 100644 index 00000000000..3c13d4f4115 --- /dev/null +++ b/vendor/guzzlehttp/psr7/src/LimitStream.php @@ -0,0 +1,155 @@ +<?php +namespace GuzzleHttp\Psr7; + +use Psr\Http\Message\StreamInterface; + + +/** + * Decorator used to return only a subset of a stream + */ +class LimitStream implements StreamInterface +{ + use StreamDecoratorTrait; + + /** @var int Offset to start reading from */ + private $offset; + + /** @var int Limit the number of bytes that can be read */ + private $limit; + + /** + * @param StreamInterface $stream Stream to wrap + * @param int $limit Total number of bytes to allow to be read + * from the stream. Pass -1 for no limit. + * @param int $offset Position to seek to before reading (only + * works on seekable streams). + */ + public function __construct( + StreamInterface $stream, + $limit = -1, + $offset = 0 + ) { + $this->stream = $stream; + $this->setLimit($limit); + $this->setOffset($offset); + } + + public function eof() + { + // Always return true if the underlying stream is EOF + if ($this->stream->eof()) { + return true; + } + + // No limit and the underlying stream is not at EOF + if ($this->limit == -1) { + return false; + } + + return $this->stream->tell() >= $this->offset + $this->limit; + } + + /** + * Returns the size of the limited subset of data + * {@inheritdoc} + */ + public function getSize() + { + if (null === ($length = $this->stream->getSize())) { + return null; + } elseif ($this->limit == -1) { + return $length - $this->offset; + } else { + return min($this->limit, $length - $this->offset); + } + } + + /** + * Allow for a bounded seek on the read limited stream + * {@inheritdoc} + */ + public function seek($offset, $whence = SEEK_SET) + { + if ($whence !== SEEK_SET || $offset < 0) { + throw new \RuntimeException(sprintf( + 'Cannot seek to offset % with whence %s', + $offset, + $whence + )); + } + + $offset += $this->offset; + + if ($this->limit !== -1) { + if ($offset > $this->offset + $this->limit) { + $offset = $this->offset + $this->limit; + } + } + + $this->stream->seek($offset); + } + + /** + * Give a relative tell() + * {@inheritdoc} + */ + public function tell() + { + return $this->stream->tell() - $this->offset; + } + + /** + * Set the offset to start limiting from + * + * @param int $offset Offset to seek to and begin byte limiting from + * + * @throws \RuntimeException if the stream cannot be seeked. + */ + public function setOffset($offset) + { + $current = $this->stream->tell(); + + if ($current !== $offset) { + // If the stream cannot seek to the offset position, then read to it + if ($this->stream->isSeekable()) { + $this->stream->seek($offset); + } elseif ($current > $offset) { + throw new \RuntimeException("Could not seek to stream offset $offset"); + } else { + $this->stream->read($offset - $current); + } + } + + $this->offset = $offset; + } + + /** + * Set the limit of bytes that the decorator allows to be read from the + * stream. + * + * @param int $limit Number of bytes to allow to be read from the stream. + * Use -1 for no limit. + */ + public function setLimit($limit) + { + $this->limit = $limit; + } + + public function read($length) + { + if ($this->limit == -1) { + return $this->stream->read($length); + } + + // Check if the current position is less than the total allowed + // bytes + original offset + $remaining = ($this->offset + $this->limit) - $this->stream->tell(); + if ($remaining > 0) { + // Only return the amount of requested data, ensuring that the byte + // limit is not exceeded + return $this->stream->read(min($remaining, $length)); + } + + return ''; + } +} diff --git a/vendor/guzzlehttp/psr7/src/MessageTrait.php b/vendor/guzzlehttp/psr7/src/MessageTrait.php new file mode 100644 index 00000000000..1e4da649ad5 --- /dev/null +++ b/vendor/guzzlehttp/psr7/src/MessageTrait.php @@ -0,0 +1,183 @@ +<?php +namespace GuzzleHttp\Psr7; + +use Psr\Http\Message\StreamInterface; + +/** + * Trait implementing functionality common to requests and responses. + */ +trait MessageTrait +{ + /** @var array Map of all registered headers, as original name => array of values */ + private $headers = []; + + /** @var array Map of lowercase header name => original name at registration */ + private $headerNames = []; + + /** @var string */ + private $protocol = '1.1'; + + /** @var StreamInterface */ + private $stream; + + public function getProtocolVersion() + { + return $this->protocol; + } + + public function withProtocolVersion($version) + { + if ($this->protocol === $version) { + return $this; + } + + $new = clone $this; + $new->protocol = $version; + return $new; + } + + public function getHeaders() + { + return $this->headers; + } + + public function hasHeader($header) + { + return isset($this->headerNames[strtolower($header)]); + } + + public function getHeader($header) + { + $header = strtolower($header); + + if (!isset($this->headerNames[$header])) { + return []; + } + + $header = $this->headerNames[$header]; + + return $this->headers[$header]; + } + + public function getHeaderLine($header) + { + return implode(', ', $this->getHeader($header)); + } + + public function withHeader($header, $value) + { + if (!is_array($value)) { + $value = [$value]; + } + + $value = $this->trimHeaderValues($value); + $normalized = strtolower($header); + + $new = clone $this; + if (isset($new->headerNames[$normalized])) { + unset($new->headers[$new->headerNames[$normalized]]); + } + $new->headerNames[$normalized] = $header; + $new->headers[$header] = $value; + + return $new; + } + + public function withAddedHeader($header, $value) + { + if (!is_array($value)) { + $value = [$value]; + } + + $value = $this->trimHeaderValues($value); + $normalized = strtolower($header); + + $new = clone $this; + if (isset($new->headerNames[$normalized])) { + $header = $this->headerNames[$normalized]; + $new->headers[$header] = array_merge($this->headers[$header], $value); + } else { + $new->headerNames[$normalized] = $header; + $new->headers[$header] = $value; + } + + return $new; + } + + public function withoutHeader($header) + { + $normalized = strtolower($header); + + if (!isset($this->headerNames[$normalized])) { + return $this; + } + + $header = $this->headerNames[$normalized]; + + $new = clone $this; + unset($new->headers[$header], $new->headerNames[$normalized]); + + return $new; + } + + public function getBody() + { + if (!$this->stream) { + $this->stream = stream_for(''); + } + + return $this->stream; + } + + public function withBody(StreamInterface $body) + { + if ($body === $this->stream) { + return $this; + } + + $new = clone $this; + $new->stream = $body; + return $new; + } + + private function setHeaders(array $headers) + { + $this->headerNames = $this->headers = []; + foreach ($headers as $header => $value) { + if (!is_array($value)) { + $value = [$value]; + } + + $value = $this->trimHeaderValues($value); + $normalized = strtolower($header); + if (isset($this->headerNames[$normalized])) { + $header = $this->headerNames[$normalized]; + $this->headers[$header] = array_merge($this->headers[$header], $value); + } else { + $this->headerNames[$normalized] = $header; + $this->headers[$header] = $value; + } + } + } + + /** + * Trims whitespace from the header values. + * + * Spaces and tabs ought to be excluded by parsers when extracting the field value from a header field. + * + * header-field = field-name ":" OWS field-value OWS + * OWS = *( SP / HTAB ) + * + * @param string[] $values Header values + * + * @return string[] Trimmed header values + * + * @see https://tools.ietf.org/html/rfc7230#section-3.2.4 + */ + private function trimHeaderValues(array $values) + { + return array_map(function ($value) { + return trim($value, " \t"); + }, $values); + } +} diff --git a/vendor/guzzlehttp/psr7/src/MultipartStream.php b/vendor/guzzlehttp/psr7/src/MultipartStream.php new file mode 100644 index 00000000000..c0fd584f759 --- /dev/null +++ b/vendor/guzzlehttp/psr7/src/MultipartStream.php @@ -0,0 +1,153 @@ +<?php +namespace GuzzleHttp\Psr7; + +use Psr\Http\Message\StreamInterface; + +/** + * Stream that when read returns bytes for a streaming multipart or + * multipart/form-data stream. + */ +class MultipartStream implements StreamInterface +{ + use StreamDecoratorTrait; + + private $boundary; + + /** + * @param array $elements Array of associative arrays, each containing a + * required "name" key mapping to the form field, + * name, a required "contents" key mapping to a + * StreamInterface/resource/string, an optional + * "headers" associative array of custom headers, + * and an optional "filename" key mapping to a + * string to send as the filename in the part. + * @param string $boundary You can optionally provide a specific boundary + * + * @throws \InvalidArgumentException + */ + public function __construct(array $elements = [], $boundary = null) + { + $this->boundary = $boundary ?: sha1(uniqid('', true)); + $this->stream = $this->createStream($elements); + } + + /** + * Get the boundary + * + * @return string + */ + public function getBoundary() + { + return $this->boundary; + } + + public function isWritable() + { + return false; + } + + /** + * Get the headers needed before transferring the content of a POST file + */ + private function getHeaders(array $headers) + { + $str = ''; + foreach ($headers as $key => $value) { + $str .= "{$key}: {$value}\r\n"; + } + + return "--{$this->boundary}\r\n" . trim($str) . "\r\n\r\n"; + } + + /** + * Create the aggregate stream that will be used to upload the POST data + */ + protected function createStream(array $elements) + { + $stream = new AppendStream(); + + foreach ($elements as $element) { + $this->addElement($stream, $element); + } + + // Add the trailing boundary with CRLF + $stream->addStream(stream_for("--{$this->boundary}--\r\n")); + + return $stream; + } + + private function addElement(AppendStream $stream, array $element) + { + foreach (['contents', 'name'] as $key) { + if (!array_key_exists($key, $element)) { + throw new \InvalidArgumentException("A '{$key}' key is required"); + } + } + + $element['contents'] = stream_for($element['contents']); + + if (empty($element['filename'])) { + $uri = $element['contents']->getMetadata('uri'); + if (substr($uri, 0, 6) !== 'php://') { + $element['filename'] = $uri; + } + } + + list($body, $headers) = $this->createElement( + $element['name'], + $element['contents'], + isset($element['filename']) ? $element['filename'] : null, + isset($element['headers']) ? $element['headers'] : [] + ); + + $stream->addStream(stream_for($this->getHeaders($headers))); + $stream->addStream($body); + $stream->addStream(stream_for("\r\n")); + } + + /** + * @return array + */ + private function createElement($name, StreamInterface $stream, $filename, array $headers) + { + // Set a default content-disposition header if one was no provided + $disposition = $this->getHeader($headers, 'content-disposition'); + if (!$disposition) { + $headers['Content-Disposition'] = ($filename === '0' || $filename) + ? sprintf('form-data; name="%s"; filename="%s"', + $name, + basename($filename)) + : "form-data; name=\"{$name}\""; + } + + // Set a default content-length header if one was no provided + $length = $this->getHeader($headers, 'content-length'); + if (!$length) { + if ($length = $stream->getSize()) { + $headers['Content-Length'] = (string) $length; + } + } + + // Set a default Content-Type if one was not supplied + $type = $this->getHeader($headers, 'content-type'); + if (!$type && ($filename === '0' || $filename)) { + if ($type = mimetype_from_filename($filename)) { + $headers['Content-Type'] = $type; + } + } + + return [$stream, $headers]; + } + + private function getHeader(array $headers, $key) + { + $lowercaseHeader = strtolower($key); + foreach ($headers as $k => $v) { + if (strtolower($k) === $lowercaseHeader) { + return $v; + } + } + + return null; + } +} diff --git a/vendor/guzzlehttp/psr7/src/NoSeekStream.php b/vendor/guzzlehttp/psr7/src/NoSeekStream.php new file mode 100644 index 00000000000..233221805ec --- /dev/null +++ b/vendor/guzzlehttp/psr7/src/NoSeekStream.php @@ -0,0 +1,22 @@ +<?php +namespace GuzzleHttp\Psr7; + +use Psr\Http\Message\StreamInterface; + +/** + * Stream decorator that prevents a stream from being seeked + */ +class NoSeekStream implements StreamInterface +{ + use StreamDecoratorTrait; + + public function seek($offset, $whence = SEEK_SET) + { + throw new \RuntimeException('Cannot seek a NoSeekStream'); + } + + public function isSeekable() + { + return false; + } +} diff --git a/vendor/guzzlehttp/psr7/src/PumpStream.php b/vendor/guzzlehttp/psr7/src/PumpStream.php new file mode 100644 index 00000000000..ffb5440da16 --- /dev/null +++ b/vendor/guzzlehttp/psr7/src/PumpStream.php @@ -0,0 +1,165 @@ +<?php +namespace GuzzleHttp\Psr7; + +use Psr\Http\Message\StreamInterface; + +/** + * Provides a read only stream that pumps data from a PHP callable. + * + * When invoking the provided callable, the PumpStream will pass the amount of + * data requested to read to the callable. The callable can choose to ignore + * this value and return fewer or more bytes than requested. Any extra data + * returned by the provided callable is buffered internally until drained using + * the read() function of the PumpStream. The provided callable MUST return + * false when there is no more data to read. + */ +class PumpStream implements StreamInterface +{ + /** @var callable */ + private $source; + + /** @var int */ + private $size; + + /** @var int */ + private $tellPos = 0; + + /** @var array */ + private $metadata; + + /** @var BufferStream */ + private $buffer; + + /** + * @param callable $source Source of the stream data. The callable MAY + * accept an integer argument used to control the + * amount of data to return. The callable MUST + * return a string when called, or false on error + * or EOF. + * @param array $options Stream options: + * - metadata: Hash of metadata to use with stream. + * - size: Size of the stream, if known. + */ + public function __construct(callable $source, array $options = []) + { + $this->source = $source; + $this->size = isset($options['size']) ? $options['size'] : null; + $this->metadata = isset($options['metadata']) ? $options['metadata'] : []; + $this->buffer = new BufferStream(); + } + + public function __toString() + { + try { + return copy_to_string($this); + } catch (\Exception $e) { + return ''; + } + } + + public function close() + { + $this->detach(); + } + + public function detach() + { + $this->tellPos = false; + $this->source = null; + } + + public function getSize() + { + return $this->size; + } + + public function tell() + { + return $this->tellPos; + } + + public function eof() + { + return !$this->source; + } + + public function isSeekable() + { + return false; + } + + public function rewind() + { + $this->seek(0); + } + + public function seek($offset, $whence = SEEK_SET) + { + throw new \RuntimeException('Cannot seek a PumpStream'); + } + + public function isWritable() + { + return false; + } + + public function write($string) + { + throw new \RuntimeException('Cannot write to a PumpStream'); + } + + public function isReadable() + { + return true; + } + + public function read($length) + { + $data = $this->buffer->read($length); + $readLen = strlen($data); + $this->tellPos += $readLen; + $remaining = $length - $readLen; + + if ($remaining) { + $this->pump($remaining); + $data .= $this->buffer->read($remaining); + $this->tellPos += strlen($data) - $readLen; + } + + return $data; + } + + public function getContents() + { + $result = ''; + while (!$this->eof()) { + $result .= $this->read(1000000); + } + + return $result; + } + + public function getMetadata($key = null) + { + if (!$key) { + return $this->metadata; + } + + return isset($this->metadata[$key]) ? $this->metadata[$key] : null; + } + + private function pump($length) + { + if ($this->source) { + do { + $data = call_user_func($this->source, $length); + if ($data === false || $data === null) { + $this->source = null; + return; + } + $this->buffer->write($data); + $length -= strlen($data); + } while ($length > 0); + } + } +} diff --git a/vendor/guzzlehttp/psr7/src/Request.php b/vendor/guzzlehttp/psr7/src/Request.php new file mode 100644 index 00000000000..08285484da2 --- /dev/null +++ b/vendor/guzzlehttp/psr7/src/Request.php @@ -0,0 +1,142 @@ +<?php +namespace GuzzleHttp\Psr7; + +use InvalidArgumentException; +use Psr\Http\Message\RequestInterface; +use Psr\Http\Message\StreamInterface; +use Psr\Http\Message\UriInterface; + +/** + * PSR-7 request implementation. + */ +class Request implements RequestInterface +{ + use MessageTrait; + + /** @var string */ + private $method; + + /** @var null|string */ + private $requestTarget; + + /** @var UriInterface */ + private $uri; + + /** + * @param string $method HTTP method + * @param string|UriInterface $uri URI + * @param array $headers Request headers + * @param string|null|resource|StreamInterface $body Request body + * @param string $version Protocol version + */ + public function __construct( + $method, + $uri, + array $headers = [], + $body = null, + $version = '1.1' + ) { + if (!($uri instanceof UriInterface)) { + $uri = new Uri($uri); + } + + $this->method = strtoupper($method); + $this->uri = $uri; + $this->setHeaders($headers); + $this->protocol = $version; + + if (!$this->hasHeader('Host')) { + $this->updateHostFromUri(); + } + + if ($body !== '' && $body !== null) { + $this->stream = stream_for($body); + } + } + + public function getRequestTarget() + { + if ($this->requestTarget !== null) { + return $this->requestTarget; + } + + $target = $this->uri->getPath(); + if ($target == '') { + $target = '/'; + } + if ($this->uri->getQuery() != '') { + $target .= '?' . $this->uri->getQuery(); + } + + return $target; + } + + public function withRequestTarget($requestTarget) + { + if (preg_match('#\s#', $requestTarget)) { + throw new InvalidArgumentException( + 'Invalid request target provided; cannot contain whitespace' + ); + } + + $new = clone $this; + $new->requestTarget = $requestTarget; + return $new; + } + + public function getMethod() + { + return $this->method; + } + + public function withMethod($method) + { + $new = clone $this; + $new->method = strtoupper($method); + return $new; + } + + public function getUri() + { + return $this->uri; + } + + public function withUri(UriInterface $uri, $preserveHost = false) + { + if ($uri === $this->uri) { + return $this; + } + + $new = clone $this; + $new->uri = $uri; + + if (!$preserveHost) { + $new->updateHostFromUri(); + } + + return $new; + } + + private function updateHostFromUri() + { + $host = $this->uri->getHost(); + + if ($host == '') { + return; + } + + if (($port = $this->uri->getPort()) !== null) { + $host .= ':' . $port; + } + + if (isset($this->headerNames['host'])) { + $header = $this->headerNames['host']; + } else { + $header = 'Host'; + $this->headerNames['host'] = 'Host'; + } + // Ensure Host is the first header. + // See: http://tools.ietf.org/html/rfc7230#section-5.4 + $this->headers = [$header => [$host]] + $this->headers; + } +} diff --git a/vendor/guzzlehttp/psr7/src/Response.php b/vendor/guzzlehttp/psr7/src/Response.php new file mode 100644 index 00000000000..2830c6c9ee1 --- /dev/null +++ b/vendor/guzzlehttp/psr7/src/Response.php @@ -0,0 +1,132 @@ +<?php +namespace GuzzleHttp\Psr7; + +use Psr\Http\Message\ResponseInterface; +use Psr\Http\Message\StreamInterface; + +/** + * PSR-7 response implementation. + */ +class Response implements ResponseInterface +{ + use MessageTrait; + + /** @var array Map of standard HTTP status code/reason phrases */ + private static $phrases = [ + 100 => 'Continue', + 101 => 'Switching Protocols', + 102 => 'Processing', + 200 => 'OK', + 201 => 'Created', + 202 => 'Accepted', + 203 => 'Non-Authoritative Information', + 204 => 'No Content', + 205 => 'Reset Content', + 206 => 'Partial Content', + 207 => 'Multi-status', + 208 => 'Already Reported', + 300 => 'Multiple Choices', + 301 => 'Moved Permanently', + 302 => 'Found', + 303 => 'See Other', + 304 => 'Not Modified', + 305 => 'Use Proxy', + 306 => 'Switch Proxy', + 307 => 'Temporary Redirect', + 400 => 'Bad Request', + 401 => 'Unauthorized', + 402 => 'Payment Required', + 403 => 'Forbidden', + 404 => 'Not Found', + 405 => 'Method Not Allowed', + 406 => 'Not Acceptable', + 407 => 'Proxy Authentication Required', + 408 => 'Request Time-out', + 409 => 'Conflict', + 410 => 'Gone', + 411 => 'Length Required', + 412 => 'Precondition Failed', + 413 => 'Request Entity Too Large', + 414 => 'Request-URI Too Large', + 415 => 'Unsupported Media Type', + 416 => 'Requested range not satisfiable', + 417 => 'Expectation Failed', + 418 => 'I\'m a teapot', + 422 => 'Unprocessable Entity', + 423 => 'Locked', + 424 => 'Failed Dependency', + 425 => 'Unordered Collection', + 426 => 'Upgrade Required', + 428 => 'Precondition Required', + 429 => 'Too Many Requests', + 431 => 'Request Header Fields Too Large', + 451 => 'Unavailable For Legal Reasons', + 500 => 'Internal Server Error', + 501 => 'Not Implemented', + 502 => 'Bad Gateway', + 503 => 'Service Unavailable', + 504 => 'Gateway Time-out', + 505 => 'HTTP Version not supported', + 506 => 'Variant Also Negotiates', + 507 => 'Insufficient Storage', + 508 => 'Loop Detected', + 511 => 'Network Authentication Required', + ]; + + /** @var string */ + private $reasonPhrase = ''; + + /** @var int */ + private $statusCode = 200; + + /** + * @param int $status Status code + * @param array $headers Response headers + * @param string|null|resource|StreamInterface $body Response body + * @param string $version Protocol version + * @param string|null $reason Reason phrase (when empty a default will be used based on the status code) + */ + public function __construct( + $status = 200, + array $headers = [], + $body = null, + $version = '1.1', + $reason = null + ) { + $this->statusCode = (int) $status; + + if ($body !== '' && $body !== null) { + $this->stream = stream_for($body); + } + + $this->setHeaders($headers); + if ($reason == '' && isset(self::$phrases[$this->statusCode])) { + $this->reasonPhrase = self::$phrases[$this->statusCode]; + } else { + $this->reasonPhrase = (string) $reason; + } + + $this->protocol = $version; + } + + public function getStatusCode() + { + return $this->statusCode; + } + + public function getReasonPhrase() + { + return $this->reasonPhrase; + } + + public function withStatus($code, $reasonPhrase = '') + { + $new = clone $this; + $new->statusCode = (int) $code; + if ($reasonPhrase == '' && isset(self::$phrases[$new->statusCode])) { + $reasonPhrase = self::$phrases[$new->statusCode]; + } + $new->reasonPhrase = $reasonPhrase; + return $new; + } +} diff --git a/vendor/guzzlehttp/psr7/src/ServerRequest.php b/vendor/guzzlehttp/psr7/src/ServerRequest.php new file mode 100644 index 00000000000..575aab8489a --- /dev/null +++ b/vendor/guzzlehttp/psr7/src/ServerRequest.php @@ -0,0 +1,358 @@ +<?php + +namespace GuzzleHttp\Psr7; + +use InvalidArgumentException; +use Psr\Http\Message\ServerRequestInterface; +use Psr\Http\Message\UriInterface; +use Psr\Http\Message\StreamInterface; +use Psr\Http\Message\UploadedFileInterface; + +/** + * Server-side HTTP request + * + * Extends the Request definition to add methods for accessing incoming data, + * specifically server parameters, cookies, matched path parameters, query + * string arguments, body parameters, and upload file information. + * + * "Attributes" are discovered via decomposing the request (and usually + * specifically the URI path), and typically will be injected by the application. + * + * Requests are considered immutable; all methods that might change state are + * implemented such that they retain the internal state of the current + * message and return a new instance that contains the changed state. + */ +class ServerRequest extends Request implements ServerRequestInterface +{ + /** + * @var array + */ + private $attributes = []; + + /** + * @var array + */ + private $cookieParams = []; + + /** + * @var null|array|object + */ + private $parsedBody; + + /** + * @var array + */ + private $queryParams = []; + + /** + * @var array + */ + private $serverParams; + + /** + * @var array + */ + private $uploadedFiles = []; + + /** + * @param string $method HTTP method + * @param string|UriInterface $uri URI + * @param array $headers Request headers + * @param string|null|resource|StreamInterface $body Request body + * @param string $version Protocol version + * @param array $serverParams Typically the $_SERVER superglobal + */ + public function __construct( + $method, + $uri, + array $headers = [], + $body = null, + $version = '1.1', + array $serverParams = [] + ) { + $this->serverParams = $serverParams; + + parent::__construct($method, $uri, $headers, $body, $version); + } + + /** + * Return an UploadedFile instance array. + * + * @param array $files A array which respect $_FILES structure + * @throws InvalidArgumentException for unrecognized values + * @return array + */ + public static function normalizeFiles(array $files) + { + $normalized = []; + + foreach ($files as $key => $value) { + if ($value instanceof UploadedFileInterface) { + $normalized[$key] = $value; + } elseif (is_array($value) && isset($value['tmp_name'])) { + $normalized[$key] = self::createUploadedFileFromSpec($value); + } elseif (is_array($value)) { + $normalized[$key] = self::normalizeFiles($value); + continue; + } else { + throw new InvalidArgumentException('Invalid value in files specification'); + } + } + + return $normalized; + } + + /** + * Create and return an UploadedFile instance from a $_FILES specification. + * + * If the specification represents an array of values, this method will + * delegate to normalizeNestedFileSpec() and return that return value. + * + * @param array $value $_FILES struct + * @return array|UploadedFileInterface + */ + private static function createUploadedFileFromSpec(array $value) + { + if (is_array($value['tmp_name'])) { + return self::normalizeNestedFileSpec($value); + } + + return new UploadedFile( + $value['tmp_name'], + (int) $value['size'], + (int) $value['error'], + $value['name'], + $value['type'] + ); + } + + /** + * Normalize an array of file specifications. + * + * Loops through all nested files and returns a normalized array of + * UploadedFileInterface instances. + * + * @param array $files + * @return UploadedFileInterface[] + */ + private static function normalizeNestedFileSpec(array $files = []) + { + $normalizedFiles = []; + + foreach (array_keys($files['tmp_name']) as $key) { + $spec = [ + 'tmp_name' => $files['tmp_name'][$key], + 'size' => $files['size'][$key], + 'error' => $files['error'][$key], + 'name' => $files['name'][$key], + 'type' => $files['type'][$key], + ]; + $normalizedFiles[$key] = self::createUploadedFileFromSpec($spec); + } + + return $normalizedFiles; + } + + /** + * Return a ServerRequest populated with superglobals: + * $_GET + * $_POST + * $_COOKIE + * $_FILES + * $_SERVER + * + * @return ServerRequestInterface + */ + public static function fromGlobals() + { + $method = isset($_SERVER['REQUEST_METHOD']) ? $_SERVER['REQUEST_METHOD'] : 'GET'; + $headers = function_exists('getallheaders') ? getallheaders() : []; + $uri = self::getUriFromGlobals(); + $body = new LazyOpenStream('php://input', 'r+'); + $protocol = isset($_SERVER['SERVER_PROTOCOL']) ? str_replace('HTTP/', '', $_SERVER['SERVER_PROTOCOL']) : '1.1'; + + $serverRequest = new ServerRequest($method, $uri, $headers, $body, $protocol, $_SERVER); + + return $serverRequest + ->withCookieParams($_COOKIE) + ->withQueryParams($_GET) + ->withParsedBody($_POST) + ->withUploadedFiles(self::normalizeFiles($_FILES)); + } + + /** + * Get a Uri populated with values from $_SERVER. + * + * @return UriInterface + */ + public static function getUriFromGlobals() { + $uri = new Uri(''); + + $uri = $uri->withScheme(!empty($_SERVER['HTTPS']) && $_SERVER['HTTPS'] !== 'off' ? 'https' : 'http'); + + $hasPort = false; + if (isset($_SERVER['HTTP_HOST'])) { + $hostHeaderParts = explode(':', $_SERVER['HTTP_HOST']); + $uri = $uri->withHost($hostHeaderParts[0]); + if (isset($hostHeaderParts[1])) { + $hasPort = true; + $uri = $uri->withPort($hostHeaderParts[1]); + } + } elseif (isset($_SERVER['SERVER_NAME'])) { + $uri = $uri->withHost($_SERVER['SERVER_NAME']); + } elseif (isset($_SERVER['SERVER_ADDR'])) { + $uri = $uri->withHost($_SERVER['SERVER_ADDR']); + } + + if (!$hasPort && isset($_SERVER['SERVER_PORT'])) { + $uri = $uri->withPort($_SERVER['SERVER_PORT']); + } + + $hasQuery = false; + if (isset($_SERVER['REQUEST_URI'])) { + $requestUriParts = explode('?', $_SERVER['REQUEST_URI']); + $uri = $uri->withPath($requestUriParts[0]); + if (isset($requestUriParts[1])) { + $hasQuery = true; + $uri = $uri->withQuery($requestUriParts[1]); + } + } + + if (!$hasQuery && isset($_SERVER['QUERY_STRING'])) { + $uri = $uri->withQuery($_SERVER['QUERY_STRING']); + } + + return $uri; + } + + + /** + * {@inheritdoc} + */ + public function getServerParams() + { + return $this->serverParams; + } + + /** + * {@inheritdoc} + */ + public function getUploadedFiles() + { + return $this->uploadedFiles; + } + + /** + * {@inheritdoc} + */ + public function withUploadedFiles(array $uploadedFiles) + { + $new = clone $this; + $new->uploadedFiles = $uploadedFiles; + + return $new; + } + + /** + * {@inheritdoc} + */ + public function getCookieParams() + { + return $this->cookieParams; + } + + /** + * {@inheritdoc} + */ + public function withCookieParams(array $cookies) + { + $new = clone $this; + $new->cookieParams = $cookies; + + return $new; + } + + /** + * {@inheritdoc} + */ + public function getQueryParams() + { + return $this->queryParams; + } + + /** + * {@inheritdoc} + */ + public function withQueryParams(array $query) + { + $new = clone $this; + $new->queryParams = $query; + + return $new; + } + + /** + * {@inheritdoc} + */ + public function getParsedBody() + { + return $this->parsedBody; + } + + /** + * {@inheritdoc} + */ + public function withParsedBody($data) + { + $new = clone $this; + $new->parsedBody = $data; + + return $new; + } + + /** + * {@inheritdoc} + */ + public function getAttributes() + { + return $this->attributes; + } + + /** + * {@inheritdoc} + */ + public function getAttribute($attribute, $default = null) + { + if (false === array_key_exists($attribute, $this->attributes)) { + return $default; + } + + return $this->attributes[$attribute]; + } + + /** + * {@inheritdoc} + */ + public function withAttribute($attribute, $value) + { + $new = clone $this; + $new->attributes[$attribute] = $value; + + return $new; + } + + /** + * {@inheritdoc} + */ + public function withoutAttribute($attribute) + { + if (false === array_key_exists($attribute, $this->attributes)) { + return $this; + } + + $new = clone $this; + unset($new->attributes[$attribute]); + + return $new; + } +} diff --git a/vendor/guzzlehttp/psr7/src/Stream.php b/vendor/guzzlehttp/psr7/src/Stream.php new file mode 100644 index 00000000000..e33662879ff --- /dev/null +++ b/vendor/guzzlehttp/psr7/src/Stream.php @@ -0,0 +1,257 @@ +<?php +namespace GuzzleHttp\Psr7; + +use Psr\Http\Message\StreamInterface; + +/** + * PHP stream implementation. + * + * @var $stream + */ +class Stream implements StreamInterface +{ + private $stream; + private $size; + private $seekable; + private $readable; + private $writable; + private $uri; + private $customMetadata; + + /** @var array Hash of readable and writable stream types */ + private static $readWriteHash = [ + 'read' => [ + 'r' => true, 'w+' => true, 'r+' => true, 'x+' => true, 'c+' => true, + 'rb' => true, 'w+b' => true, 'r+b' => true, 'x+b' => true, + 'c+b' => true, 'rt' => true, 'w+t' => true, 'r+t' => true, + 'x+t' => true, 'c+t' => true, 'a+' => true + ], + 'write' => [ + 'w' => true, 'w+' => true, 'rw' => true, 'r+' => true, 'x+' => true, + 'c+' => true, 'wb' => true, 'w+b' => true, 'r+b' => true, + 'x+b' => true, 'c+b' => true, 'w+t' => true, 'r+t' => true, + 'x+t' => true, 'c+t' => true, 'a' => true, 'a+' => true + ] + ]; + + /** + * This constructor accepts an associative array of options. + * + * - size: (int) If a read stream would otherwise have an indeterminate + * size, but the size is known due to foreknowledge, then you can + * provide that size, in bytes. + * - metadata: (array) Any additional metadata to return when the metadata + * of the stream is accessed. + * + * @param resource $stream Stream resource to wrap. + * @param array $options Associative array of options. + * + * @throws \InvalidArgumentException if the stream is not a stream resource + */ + public function __construct($stream, $options = []) + { + if (!is_resource($stream)) { + throw new \InvalidArgumentException('Stream must be a resource'); + } + + if (isset($options['size'])) { + $this->size = $options['size']; + } + + $this->customMetadata = isset($options['metadata']) + ? $options['metadata'] + : []; + + $this->stream = $stream; + $meta = stream_get_meta_data($this->stream); + $this->seekable = $meta['seekable']; + $this->readable = isset(self::$readWriteHash['read'][$meta['mode']]); + $this->writable = isset(self::$readWriteHash['write'][$meta['mode']]); + $this->uri = $this->getMetadata('uri'); + } + + public function __get($name) + { + if ($name == 'stream') { + throw new \RuntimeException('The stream is detached'); + } + + throw new \BadMethodCallException('No value for ' . $name); + } + + /** + * Closes the stream when the destructed + */ + public function __destruct() + { + $this->close(); + } + + public function __toString() + { + try { + $this->seek(0); + return (string) stream_get_contents($this->stream); + } catch (\Exception $e) { + return ''; + } + } + + public function getContents() + { + $contents = stream_get_contents($this->stream); + + if ($contents === false) { + throw new \RuntimeException('Unable to read stream contents'); + } + + return $contents; + } + + public function close() + { + if (isset($this->stream)) { + if (is_resource($this->stream)) { + fclose($this->stream); + } + $this->detach(); + } + } + + public function detach() + { + if (!isset($this->stream)) { + return null; + } + + $result = $this->stream; + unset($this->stream); + $this->size = $this->uri = null; + $this->readable = $this->writable = $this->seekable = false; + + return $result; + } + + public function getSize() + { + if ($this->size !== null) { + return $this->size; + } + + if (!isset($this->stream)) { + return null; + } + + // Clear the stat cache if the stream has a URI + if ($this->uri) { + clearstatcache(true, $this->uri); + } + + $stats = fstat($this->stream); + if (isset($stats['size'])) { + $this->size = $stats['size']; + return $this->size; + } + + return null; + } + + public function isReadable() + { + return $this->readable; + } + + public function isWritable() + { + return $this->writable; + } + + public function isSeekable() + { + return $this->seekable; + } + + public function eof() + { + return !$this->stream || feof($this->stream); + } + + public function tell() + { + $result = ftell($this->stream); + + if ($result === false) { + throw new \RuntimeException('Unable to determine stream position'); + } + + return $result; + } + + public function rewind() + { + $this->seek(0); + } + + public function seek($offset, $whence = SEEK_SET) + { + if (!$this->seekable) { + throw new \RuntimeException('Stream is not seekable'); + } elseif (fseek($this->stream, $offset, $whence) === -1) { + throw new \RuntimeException('Unable to seek to stream position ' + . $offset . ' with whence ' . var_export($whence, true)); + } + } + + public function read($length) + { + if (!$this->readable) { + throw new \RuntimeException('Cannot read from non-readable stream'); + } + if ($length < 0) { + throw new \RuntimeException('Length parameter cannot be negative'); + } + + if (0 === $length) { + return ''; + } + + $string = fread($this->stream, $length); + if (false === $string) { + throw new \RuntimeException('Unable to read from stream'); + } + + return $string; + } + + public function write($string) + { + if (!$this->writable) { + throw new \RuntimeException('Cannot write to a non-writable stream'); + } + + // We can't know the size after writing anything + $this->size = null; + $result = fwrite($this->stream, $string); + + if ($result === false) { + throw new \RuntimeException('Unable to write to stream'); + } + + return $result; + } + + public function getMetadata($key = null) + { + if (!isset($this->stream)) { + return $key ? null : []; + } elseif (!$key) { + return $this->customMetadata + stream_get_meta_data($this->stream); + } elseif (isset($this->customMetadata[$key])) { + return $this->customMetadata[$key]; + } + + $meta = stream_get_meta_data($this->stream); + + return isset($meta[$key]) ? $meta[$key] : null; + } +} diff --git a/vendor/guzzlehttp/psr7/src/StreamDecoratorTrait.php b/vendor/guzzlehttp/psr7/src/StreamDecoratorTrait.php new file mode 100644 index 00000000000..daec6f52ea7 --- /dev/null +++ b/vendor/guzzlehttp/psr7/src/StreamDecoratorTrait.php @@ -0,0 +1,149 @@ +<?php +namespace GuzzleHttp\Psr7; + +use Psr\Http\Message\StreamInterface; + +/** + * Stream decorator trait + * @property StreamInterface stream + */ +trait StreamDecoratorTrait +{ + /** + * @param StreamInterface $stream Stream to decorate + */ + public function __construct(StreamInterface $stream) + { + $this->stream = $stream; + } + + /** + * Magic method used to create a new stream if streams are not added in + * the constructor of a decorator (e.g., LazyOpenStream). + * + * @param string $name Name of the property (allows "stream" only). + * + * @return StreamInterface + */ + public function __get($name) + { + if ($name == 'stream') { + $this->stream = $this->createStream(); + return $this->stream; + } + + throw new \UnexpectedValueException("$name not found on class"); + } + + public function __toString() + { + try { + if ($this->isSeekable()) { + $this->seek(0); + } + return $this->getContents(); + } catch (\Exception $e) { + // Really, PHP? https://bugs.php.net/bug.php?id=53648 + trigger_error('StreamDecorator::__toString exception: ' + . (string) $e, E_USER_ERROR); + return ''; + } + } + + public function getContents() + { + return copy_to_string($this); + } + + /** + * Allow decorators to implement custom methods + * + * @param string $method Missing method name + * @param array $args Method arguments + * + * @return mixed + */ + public function __call($method, array $args) + { + $result = call_user_func_array([$this->stream, $method], $args); + + // Always return the wrapped object if the result is a return $this + return $result === $this->stream ? $this : $result; + } + + public function close() + { + $this->stream->close(); + } + + public function getMetadata($key = null) + { + return $this->stream->getMetadata($key); + } + + public function detach() + { + return $this->stream->detach(); + } + + public function getSize() + { + return $this->stream->getSize(); + } + + public function eof() + { + return $this->stream->eof(); + } + + public function tell() + { + return $this->stream->tell(); + } + + public function isReadable() + { + return $this->stream->isReadable(); + } + + public function isWritable() + { + return $this->stream->isWritable(); + } + + public function isSeekable() + { + return $this->stream->isSeekable(); + } + + public function rewind() + { + $this->seek(0); + } + + public function seek($offset, $whence = SEEK_SET) + { + $this->stream->seek($offset, $whence); + } + + public function read($length) + { + return $this->stream->read($length); + } + + public function write($string) + { + return $this->stream->write($string); + } + + /** + * Implement in subclasses to dynamically create streams when requested. + * + * @return StreamInterface + * @throws \BadMethodCallException + */ + protected function createStream() + { + throw new \BadMethodCallException('Not implemented'); + } +} diff --git a/vendor/guzzlehttp/psr7/src/StreamWrapper.php b/vendor/guzzlehttp/psr7/src/StreamWrapper.php new file mode 100644 index 00000000000..cf7b2232e46 --- /dev/null +++ b/vendor/guzzlehttp/psr7/src/StreamWrapper.php @@ -0,0 +1,121 @@ +<?php +namespace GuzzleHttp\Psr7; + +use Psr\Http\Message\StreamInterface; + +/** + * Converts Guzzle streams into PHP stream resources. + */ +class StreamWrapper +{ + /** @var resource */ + public $context; + + /** @var StreamInterface */ + private $stream; + + /** @var string r, r+, or w */ + private $mode; + + /** + * Returns a resource representing the stream. + * + * @param StreamInterface $stream The stream to get a resource for + * + * @return resource + * @throws \InvalidArgumentException if stream is not readable or writable + */ + public static function getResource(StreamInterface $stream) + { + self::register(); + + if ($stream->isReadable()) { + $mode = $stream->isWritable() ? 'r+' : 'r'; + } elseif ($stream->isWritable()) { + $mode = 'w'; + } else { + throw new \InvalidArgumentException('The stream must be readable, ' + . 'writable, or both.'); + } + + return fopen('guzzle://stream', $mode, null, stream_context_create([ + 'guzzle' => ['stream' => $stream] + ])); + } + + /** + * Registers the stream wrapper if needed + */ + public static function register() + { + if (!in_array('guzzle', stream_get_wrappers())) { + stream_wrapper_register('guzzle', __CLASS__); + } + } + + public function stream_open($path, $mode, $options, &$opened_path) + { + $options = stream_context_get_options($this->context); + + if (!isset($options['guzzle']['stream'])) { + return false; + } + + $this->mode = $mode; + $this->stream = $options['guzzle']['stream']; + + return true; + } + + public function stream_read($count) + { + return $this->stream->read($count); + } + + public function stream_write($data) + { + return (int) $this->stream->write($data); + } + + public function stream_tell() + { + return $this->stream->tell(); + } + + public function stream_eof() + { + return $this->stream->eof(); + } + + public function stream_seek($offset, $whence) + { + $this->stream->seek($offset, $whence); + + return true; + } + + public function stream_stat() + { + static $modeMap = [ + 'r' => 33060, + 'r+' => 33206, + 'w' => 33188 + ]; + + return [ + 'dev' => 0, + 'ino' => 0, + 'mode' => $modeMap[$this->mode], + 'nlink' => 0, + 'uid' => 0, + 'gid' => 0, + 'rdev' => 0, + 'size' => $this->stream->getSize() ?: 0, + 'atime' => 0, + 'mtime' => 0, + 'ctime' => 0, + 'blksize' => 0, + 'blocks' => 0 + ]; + } +} diff --git a/vendor/guzzlehttp/psr7/src/UploadedFile.php b/vendor/guzzlehttp/psr7/src/UploadedFile.php new file mode 100644 index 00000000000..e62bd5c8076 --- /dev/null +++ b/vendor/guzzlehttp/psr7/src/UploadedFile.php @@ -0,0 +1,316 @@ +<?php +namespace GuzzleHttp\Psr7; + +use InvalidArgumentException; +use Psr\Http\Message\StreamInterface; +use Psr\Http\Message\UploadedFileInterface; +use RuntimeException; + +class UploadedFile implements UploadedFileInterface +{ + /** + * @var int[] + */ + private static $errors = [ + UPLOAD_ERR_OK, + UPLOAD_ERR_INI_SIZE, + UPLOAD_ERR_FORM_SIZE, + UPLOAD_ERR_PARTIAL, + UPLOAD_ERR_NO_FILE, + UPLOAD_ERR_NO_TMP_DIR, + UPLOAD_ERR_CANT_WRITE, + UPLOAD_ERR_EXTENSION, + ]; + + /** + * @var string + */ + private $clientFilename; + + /** + * @var string + */ + private $clientMediaType; + + /** + * @var int + */ + private $error; + + /** + * @var null|string + */ + private $file; + + /** + * @var bool + */ + private $moved = false; + + /** + * @var int + */ + private $size; + + /** + * @var StreamInterface|null + */ + private $stream; + + /** + * @param StreamInterface|string|resource $streamOrFile + * @param int $size + * @param int $errorStatus + * @param string|null $clientFilename + * @param string|null $clientMediaType + */ + public function __construct( + $streamOrFile, + $size, + $errorStatus, + $clientFilename = null, + $clientMediaType = null + ) { + $this->setError($errorStatus); + $this->setSize($size); + $this->setClientFilename($clientFilename); + $this->setClientMediaType($clientMediaType); + + if ($this->isOk()) { + $this->setStreamOrFile($streamOrFile); + } + } + + /** + * Depending on the value set file or stream variable + * + * @param mixed $streamOrFile + * @throws InvalidArgumentException + */ + private function setStreamOrFile($streamOrFile) + { + if (is_string($streamOrFile)) { + $this->file = $streamOrFile; + } elseif (is_resource($streamOrFile)) { + $this->stream = new Stream($streamOrFile); + } elseif ($streamOrFile instanceof StreamInterface) { + $this->stream = $streamOrFile; + } else { + throw new InvalidArgumentException( + 'Invalid stream or file provided for UploadedFile' + ); + } + } + + /** + * @param int $error + * @throws InvalidArgumentException + */ + private function setError($error) + { + if (false === is_int($error)) { + throw new InvalidArgumentException( + 'Upload file error status must be an integer' + ); + } + + if (false === in_array($error, UploadedFile::$errors)) { + throw new InvalidArgumentException( + 'Invalid error status for UploadedFile' + ); + } + + $this->error = $error; + } + + /** + * @param int $size + * @throws InvalidArgumentException + */ + private function setSize($size) + { + if (false === is_int($size)) { + throw new InvalidArgumentException( + 'Upload file size must be an integer' + ); + } + + $this->size = $size; + } + + /** + * @param mixed $param + * @return boolean + */ + private function isStringOrNull($param) + { + return in_array(gettype($param), ['string', 'NULL']); + } + + /** + * @param mixed $param + * @return boolean + */ + private function isStringNotEmpty($param) + { + return is_string($param) && false === empty($param); + } + + /** + * @param string|null $clientFilename + * @throws InvalidArgumentException + */ + private function setClientFilename($clientFilename) + { + if (false === $this->isStringOrNull($clientFilename)) { + throw new InvalidArgumentException( + 'Upload file client filename must be a string or null' + ); + } + + $this->clientFilename = $clientFilename; + } + + /** + * @param string|null $clientMediaType + * @throws InvalidArgumentException + */ + private function setClientMediaType($clientMediaType) + { + if (false === $this->isStringOrNull($clientMediaType)) { + throw new InvalidArgumentException( + 'Upload file client media type must be a string or null' + ); + } + + $this->clientMediaType = $clientMediaType; + } + + /** + * Return true if there is no upload error + * + * @return boolean + */ + private function isOk() + { + return $this->error === UPLOAD_ERR_OK; + } + + /** + * @return boolean + */ + public function isMoved() + { + return $this->moved; + } + + /** + * @throws RuntimeException if is moved or not ok + */ + private function validateActive() + { + if (false === $this->isOk()) { + throw new RuntimeException('Cannot retrieve stream due to upload error'); + } + + if ($this->isMoved()) { + throw new RuntimeException('Cannot retrieve stream after it has already been moved'); + } + } + + /** + * {@inheritdoc} + * @throws RuntimeException if the upload was not successful. + */ + public function getStream() + { + $this->validateActive(); + + if ($this->stream instanceof StreamInterface) { + return $this->stream; + } + + return new LazyOpenStream($this->file, 'r+'); + } + + /** + * {@inheritdoc} + * + * @see http://php.net/is_uploaded_file + * @see http://php.net/move_uploaded_file + * @param string $targetPath Path to which to move the uploaded file. + * @throws RuntimeException if the upload was not successful. + * @throws InvalidArgumentException if the $path specified is invalid. + * @throws RuntimeException on any error during the move operation, or on + * the second or subsequent call to the method. + */ + public function moveTo($targetPath) + { + $this->validateActive(); + + if (false === $this->isStringNotEmpty($targetPath)) { + throw new InvalidArgumentException( + 'Invalid path provided for move operation; must be a non-empty string' + ); + } + + if ($this->file) { + $this->moved = php_sapi_name() == 'cli' + ? rename($this->file, $targetPath) + : move_uploaded_file($this->file, $targetPath); + } else { + copy_to_stream( + $this->getStream(), + new LazyOpenStream($targetPath, 'w') + ); + + $this->moved = true; + } + + if (false === $this->moved) { + throw new RuntimeException( + sprintf('Uploaded file could not be moved to %s', $targetPath) + ); + } + } + + /** + * {@inheritdoc} + * + * @return int|null The file size in bytes or null if unknown. + */ + public function getSize() + { + return $this->size; + } + + /** + * {@inheritdoc} + * + * @see http://php.net/manual/en/features.file-upload.errors.php + * @return int One of PHP's UPLOAD_ERR_XXX constants. + */ + public function getError() + { + return $this->error; + } + + /** + * {@inheritdoc} + * + * @return string|null The filename sent by the client or null if none + * was provided. + */ + public function getClientFilename() + { + return $this->clientFilename; + } + + /** + * {@inheritdoc} + */ + public function getClientMediaType() + { + return $this->clientMediaType; + } +} diff --git a/vendor/guzzlehttp/psr7/src/Uri.php b/vendor/guzzlehttp/psr7/src/Uri.php new file mode 100644 index 00000000000..f46c1db9e07 --- /dev/null +++ b/vendor/guzzlehttp/psr7/src/Uri.php @@ -0,0 +1,702 @@ +<?php +namespace GuzzleHttp\Psr7; + +use Psr\Http\Message\UriInterface; + +/** + * PSR-7 URI implementation. + * + * @author Michael Dowling + * @author Tobias Schultze + * @author Matthew Weier O'Phinney + */ +class Uri implements UriInterface +{ + /** + * Absolute http and https URIs require a host per RFC 7230 Section 2.7 + * but in generic URIs the host can be empty. So for http(s) URIs + * we apply this default host when no host is given yet to form a + * valid URI. + */ + const HTTP_DEFAULT_HOST = 'localhost'; + + private static $defaultPorts = [ + 'http' => 80, + 'https' => 443, + 'ftp' => 21, + 'gopher' => 70, + 'nntp' => 119, + 'news' => 119, + 'telnet' => 23, + 'tn3270' => 23, + 'imap' => 143, + 'pop' => 110, + 'ldap' => 389, + ]; + + private static $charUnreserved = 'a-zA-Z0-9_\-\.~'; + private static $charSubDelims = '!\$&\'\(\)\*\+,;='; + private static $replaceQuery = ['=' => '%3D', '&' => '%26']; + + /** @var string Uri scheme. */ + private $scheme = ''; + + /** @var string Uri user info. */ + private $userInfo = ''; + + /** @var string Uri host. */ + private $host = ''; + + /** @var int|null Uri port. */ + private $port; + + /** @var string Uri path. */ + private $path = ''; + + /** @var string Uri query string. */ + private $query = ''; + + /** @var string Uri fragment. */ + private $fragment = ''; + + /** + * @param string $uri URI to parse + */ + public function __construct($uri = '') + { + // weak type check to also accept null until we can add scalar type hints + if ($uri != '') { + $parts = parse_url($uri); + if ($parts === false) { + throw new \InvalidArgumentException("Unable to parse URI: $uri"); + } + $this->applyParts($parts); + } + } + + public function __toString() + { + return self::composeComponents( + $this->scheme, + $this->getAuthority(), + $this->path, + $this->query, + $this->fragment + ); + } + + /** + * Composes a URI reference string from its various components. + * + * Usually this method does not need to be called manually but instead is used indirectly via + * `Psr\Http\Message\UriInterface::__toString`. + * + * PSR-7 UriInterface treats an empty component the same as a missing component as + * getQuery(), getFragment() etc. always return a string. This explains the slight + * difference to RFC 3986 Section 5.3. + * + * Another adjustment is that the authority separator is added even when the authority is missing/empty + * for the "file" scheme. This is because PHP stream functions like `file_get_contents` only work with + * `file:///myfile` but not with `file:/myfile` although they are equivalent according to RFC 3986. But + * `file:///` is the more common syntax for the file scheme anyway (Chrome for example redirects to + * that format). + * + * @param string $scheme + * @param string $authority + * @param string $path + * @param string $query + * @param string $fragment + * + * @return string + * + * @link https://tools.ietf.org/html/rfc3986#section-5.3 + */ + public static function composeComponents($scheme, $authority, $path, $query, $fragment) + { + $uri = ''; + + // weak type checks to also accept null until we can add scalar type hints + if ($scheme != '') { + $uri .= $scheme . ':'; + } + + if ($authority != ''|| $scheme === 'file') { + $uri .= '//' . $authority; + } + + $uri .= $path; + + if ($query != '') { + $uri .= '?' . $query; + } + + if ($fragment != '') { + $uri .= '#' . $fragment; + } + + return $uri; + } + + /** + * Whether the URI has the default port of the current scheme. + * + * `Psr\Http\Message\UriInterface::getPort` may return null or the standard port. This method can be used + * independently of the implementation. + * + * @param UriInterface $uri + * + * @return bool + */ + public static function isDefaultPort(UriInterface $uri) + { + return $uri->getPort() === null + || (isset(self::$defaultPorts[$uri->getScheme()]) && $uri->getPort() === self::$defaultPorts[$uri->getScheme()]); + } + + /** + * Whether the URI is absolute, i.e. it has a scheme. + * + * An instance of UriInterface can either be an absolute URI or a relative reference. This method returns true + * if it is the former. An absolute URI has a scheme. A relative reference is used to express a URI relative + * to another URI, the base URI. Relative references can be divided into several forms: + * - network-path references, e.g. '//example.com/path' + * - absolute-path references, e.g. '/path' + * - relative-path references, e.g. 'subpath' + * + * @param UriInterface $uri + * + * @return bool + * @see Uri::isNetworkPathReference + * @see Uri::isAbsolutePathReference + * @see Uri::isRelativePathReference + * @link https://tools.ietf.org/html/rfc3986#section-4 + */ + public static function isAbsolute(UriInterface $uri) + { + return $uri->getScheme() !== ''; + } + + /** + * Whether the URI is a network-path reference. + * + * A relative reference that begins with two slash characters is termed an network-path reference. + * + * @param UriInterface $uri + * + * @return bool + * @link https://tools.ietf.org/html/rfc3986#section-4.2 + */ + public static function isNetworkPathReference(UriInterface $uri) + { + return $uri->getScheme() === '' && $uri->getAuthority() !== ''; + } + + /** + * Whether the URI is a absolute-path reference. + * + * A relative reference that begins with a single slash character is termed an absolute-path reference. + * + * @param UriInterface $uri + * + * @return bool + * @link https://tools.ietf.org/html/rfc3986#section-4.2 + */ + public static function isAbsolutePathReference(UriInterface $uri) + { + return $uri->getScheme() === '' + && $uri->getAuthority() === '' + && isset($uri->getPath()[0]) + && $uri->getPath()[0] === '/'; + } + + /** + * Whether the URI is a relative-path reference. + * + * A relative reference that does not begin with a slash character is termed a relative-path reference. + * + * @param UriInterface $uri + * + * @return bool + * @link https://tools.ietf.org/html/rfc3986#section-4.2 + */ + public static function isRelativePathReference(UriInterface $uri) + { + return $uri->getScheme() === '' + && $uri->getAuthority() === '' + && (!isset($uri->getPath()[0]) || $uri->getPath()[0] !== '/'); + } + + /** + * Whether the URI is a same-document reference. + * + * A same-document reference refers to a URI that is, aside from its fragment + * component, identical to the base URI. When no base URI is given, only an empty + * URI reference (apart from its fragment) is considered a same-document reference. + * + * @param UriInterface $uri The URI to check + * @param UriInterface|null $base An optional base URI to compare against + * + * @return bool + * @link https://tools.ietf.org/html/rfc3986#section-4.4 + */ + public static function isSameDocumentReference(UriInterface $uri, UriInterface $base = null) + { + if ($base !== null) { + $uri = UriResolver::resolve($base, $uri); + + return ($uri->getScheme() === $base->getScheme()) + && ($uri->getAuthority() === $base->getAuthority()) + && ($uri->getPath() === $base->getPath()) + && ($uri->getQuery() === $base->getQuery()); + } + + return $uri->getScheme() === '' && $uri->getAuthority() === '' && $uri->getPath() === '' && $uri->getQuery() === ''; + } + + /** + * Removes dot segments from a path and returns the new path. + * + * @param string $path + * + * @return string + * + * @deprecated since version 1.4. Use UriResolver::removeDotSegments instead. + * @see UriResolver::removeDotSegments + */ + public static function removeDotSegments($path) + { + return UriResolver::removeDotSegments($path); + } + + /** + * Converts the relative URI into a new URI that is resolved against the base URI. + * + * @param UriInterface $base Base URI + * @param string|UriInterface $rel Relative URI + * + * @return UriInterface + * + * @deprecated since version 1.4. Use UriResolver::resolve instead. + * @see UriResolver::resolve + */ + public static function resolve(UriInterface $base, $rel) + { + if (!($rel instanceof UriInterface)) { + $rel = new self($rel); + } + + return UriResolver::resolve($base, $rel); + } + + /** + * Creates a new URI with a specific query string value removed. + * + * Any existing query string values that exactly match the provided key are + * removed. + * + * @param UriInterface $uri URI to use as a base. + * @param string $key Query string key to remove. + * + * @return UriInterface + */ + public static function withoutQueryValue(UriInterface $uri, $key) + { + $current = $uri->getQuery(); + if ($current === '') { + return $uri; + } + + $decodedKey = rawurldecode($key); + $result = array_filter(explode('&', $current), function ($part) use ($decodedKey) { + return rawurldecode(explode('=', $part)[0]) !== $decodedKey; + }); + + return $uri->withQuery(implode('&', $result)); + } + + /** + * Creates a new URI with a specific query string value. + * + * Any existing query string values that exactly match the provided key are + * removed and replaced with the given key value pair. + * + * A value of null will set the query string key without a value, e.g. "key" + * instead of "key=value". + * + * @param UriInterface $uri URI to use as a base. + * @param string $key Key to set. + * @param string|null $value Value to set + * + * @return UriInterface + */ + public static function withQueryValue(UriInterface $uri, $key, $value) + { + $current = $uri->getQuery(); + + if ($current === '') { + $result = []; + } else { + $decodedKey = rawurldecode($key); + $result = array_filter(explode('&', $current), function ($part) use ($decodedKey) { + return rawurldecode(explode('=', $part)[0]) !== $decodedKey; + }); + } + + // Query string separators ("=", "&") within the key or value need to be encoded + // (while preventing double-encoding) before setting the query string. All other + // chars that need percent-encoding will be encoded by withQuery(). + $key = strtr($key, self::$replaceQuery); + + if ($value !== null) { + $result[] = $key . '=' . strtr($value, self::$replaceQuery); + } else { + $result[] = $key; + } + + return $uri->withQuery(implode('&', $result)); + } + + /** + * Creates a URI from a hash of `parse_url` components. + * + * @param array $parts + * + * @return UriInterface + * @link http://php.net/manual/en/function.parse-url.php + * + * @throws \InvalidArgumentException If the components do not form a valid URI. + */ + public static function fromParts(array $parts) + { + $uri = new self(); + $uri->applyParts($parts); + $uri->validateState(); + + return $uri; + } + + public function getScheme() + { + return $this->scheme; + } + + public function getAuthority() + { + $authority = $this->host; + if ($this->userInfo !== '') { + $authority = $this->userInfo . '@' . $authority; + } + + if ($this->port !== null) { + $authority .= ':' . $this->port; + } + + return $authority; + } + + public function getUserInfo() + { + return $this->userInfo; + } + + public function getHost() + { + return $this->host; + } + + public function getPort() + { + return $this->port; + } + + public function getPath() + { + return $this->path; + } + + public function getQuery() + { + return $this->query; + } + + public function getFragment() + { + return $this->fragment; + } + + public function withScheme($scheme) + { + $scheme = $this->filterScheme($scheme); + + if ($this->scheme === $scheme) { + return $this; + } + + $new = clone $this; + $new->scheme = $scheme; + $new->removeDefaultPort(); + $new->validateState(); + + return $new; + } + + public function withUserInfo($user, $password = null) + { + $info = $user; + if ($password != '') { + $info .= ':' . $password; + } + + if ($this->userInfo === $info) { + return $this; + } + + $new = clone $this; + $new->userInfo = $info; + $new->validateState(); + + return $new; + } + + public function withHost($host) + { + $host = $this->filterHost($host); + + if ($this->host === $host) { + return $this; + } + + $new = clone $this; + $new->host = $host; + $new->validateState(); + + return $new; + } + + public function withPort($port) + { + $port = $this->filterPort($port); + + if ($this->port === $port) { + return $this; + } + + $new = clone $this; + $new->port = $port; + $new->removeDefaultPort(); + $new->validateState(); + + return $new; + } + + public function withPath($path) + { + $path = $this->filterPath($path); + + if ($this->path === $path) { + return $this; + } + + $new = clone $this; + $new->path = $path; + $new->validateState(); + + return $new; + } + + public function withQuery($query) + { + $query = $this->filterQueryAndFragment($query); + + if ($this->query === $query) { + return $this; + } + + $new = clone $this; + $new->query = $query; + + return $new; + } + + public function withFragment($fragment) + { + $fragment = $this->filterQueryAndFragment($fragment); + + if ($this->fragment === $fragment) { + return $this; + } + + $new = clone $this; + $new->fragment = $fragment; + + return $new; + } + + /** + * Apply parse_url parts to a URI. + * + * @param array $parts Array of parse_url parts to apply. + */ + private function applyParts(array $parts) + { + $this->scheme = isset($parts['scheme']) + ? $this->filterScheme($parts['scheme']) + : ''; + $this->userInfo = isset($parts['user']) ? $parts['user'] : ''; + $this->host = isset($parts['host']) + ? $this->filterHost($parts['host']) + : ''; + $this->port = isset($parts['port']) + ? $this->filterPort($parts['port']) + : null; + $this->path = isset($parts['path']) + ? $this->filterPath($parts['path']) + : ''; + $this->query = isset($parts['query']) + ? $this->filterQueryAndFragment($parts['query']) + : ''; + $this->fragment = isset($parts['fragment']) + ? $this->filterQueryAndFragment($parts['fragment']) + : ''; + if (isset($parts['pass'])) { + $this->userInfo .= ':' . $parts['pass']; + } + + $this->removeDefaultPort(); + } + + /** + * @param string $scheme + * + * @return string + * + * @throws \InvalidArgumentException If the scheme is invalid. + */ + private function filterScheme($scheme) + { + if (!is_string($scheme)) { + throw new \InvalidArgumentException('Scheme must be a string'); + } + + return strtolower($scheme); + } + + /** + * @param string $host + * + * @return string + * + * @throws \InvalidArgumentException If the host is invalid. + */ + private function filterHost($host) + { + if (!is_string($host)) { + throw new \InvalidArgumentException('Host must be a string'); + } + + return strtolower($host); + } + + /** + * @param int|null $port + * + * @return int|null + * + * @throws \InvalidArgumentException If the port is invalid. + */ + private function filterPort($port) + { + if ($port === null) { + return null; + } + + $port = (int) $port; + if (1 > $port || 0xffff < $port) { + throw new \InvalidArgumentException( + sprintf('Invalid port: %d. Must be between 1 and 65535', $port) + ); + } + + return $port; + } + + private function removeDefaultPort() + { + if ($this->port !== null && self::isDefaultPort($this)) { + $this->port = null; + } + } + + /** + * Filters the path of a URI + * + * @param string $path + * + * @return string + * + * @throws \InvalidArgumentException If the path is invalid. + */ + private function filterPath($path) + { + if (!is_string($path)) { + throw new \InvalidArgumentException('Path must be a string'); + } + + return preg_replace_callback( + '/(?:[^' . self::$charUnreserved . self::$charSubDelims . '%:@\/]++|%(?![A-Fa-f0-9]{2}))/', + [$this, 'rawurlencodeMatchZero'], + $path + ); + } + + /** + * Filters the query string or fragment of a URI. + * + * @param string $str + * + * @return string + * + * @throws \InvalidArgumentException If the query or fragment is invalid. + */ + private function filterQueryAndFragment($str) + { + if (!is_string($str)) { + throw new \InvalidArgumentException('Query and fragment must be a string'); + } + + return preg_replace_callback( + '/(?:[^' . self::$charUnreserved . self::$charSubDelims . '%:@\/\?]++|%(?![A-Fa-f0-9]{2}))/', + [$this, 'rawurlencodeMatchZero'], + $str + ); + } + + private function rawurlencodeMatchZero(array $match) + { + return rawurlencode($match[0]); + } + + private function validateState() + { + if ($this->host === '' && ($this->scheme === 'http' || $this->scheme === 'https')) { + $this->host = self::HTTP_DEFAULT_HOST; + } + + if ($this->getAuthority() === '') { + if (0 === strpos($this->path, '//')) { + throw new \InvalidArgumentException('The path of a URI without an authority must not start with two slashes "//"'); + } + if ($this->scheme === '' && false !== strpos(explode('/', $this->path, 2)[0], ':')) { + throw new \InvalidArgumentException('A relative URI must not have a path beginning with a segment containing a colon'); + } + } elseif (isset($this->path[0]) && $this->path[0] !== '/') { + @trigger_error( + 'The path of a URI with an authority must start with a slash "/" or be empty. Automagically fixing the URI ' . + 'by adding a leading slash to the path is deprecated since version 1.4 and will throw an exception instead.', + E_USER_DEPRECATED + ); + $this->path = '/'. $this->path; + //throw new \InvalidArgumentException('The path of a URI with an authority must start with a slash "/" or be empty'); + } + } +} diff --git a/vendor/guzzlehttp/psr7/src/UriNormalizer.php b/vendor/guzzlehttp/psr7/src/UriNormalizer.php new file mode 100644 index 00000000000..384c29e5086 --- /dev/null +++ b/vendor/guzzlehttp/psr7/src/UriNormalizer.php @@ -0,0 +1,216 @@ +<?php +namespace GuzzleHttp\Psr7; + +use Psr\Http\Message\UriInterface; + +/** + * Provides methods to normalize and compare URIs. + * + * @author Tobias Schultze + * + * @link https://tools.ietf.org/html/rfc3986#section-6 + */ +final class UriNormalizer +{ + /** + * Default normalizations which only include the ones that preserve semantics. + * + * self::CAPITALIZE_PERCENT_ENCODING | self::DECODE_UNRESERVED_CHARACTERS | self::CONVERT_EMPTY_PATH | + * self::REMOVE_DEFAULT_HOST | self::REMOVE_DEFAULT_PORT | self::REMOVE_DOT_SEGMENTS + */ + const PRESERVING_NORMALIZATIONS = 63; + + /** + * All letters within a percent-encoding triplet (e.g., "%3A") are case-insensitive, and should be capitalized. + * + * Example: http://example.org/a%c2%b1b → http://example.org/a%C2%B1b + */ + const CAPITALIZE_PERCENT_ENCODING = 1; + + /** + * Decodes percent-encoded octets of unreserved characters. + * + * For consistency, percent-encoded octets in the ranges of ALPHA (%41–%5A and %61–%7A), DIGIT (%30–%39), + * hyphen (%2D), period (%2E), underscore (%5F), or tilde (%7E) should not be created by URI producers and, + * when found in a URI, should be decoded to their corresponding unreserved characters by URI normalizers. + * + * Example: http://example.org/%7Eusern%61me/ → http://example.org/~username/ + */ + const DECODE_UNRESERVED_CHARACTERS = 2; + + /** + * Converts the empty path to "/" for http and https URIs. + * + * Example: http://example.org → http://example.org/ + */ + const CONVERT_EMPTY_PATH = 4; + + /** + * Removes the default host of the given URI scheme from the URI. + * + * Only the "file" scheme defines the default host "localhost". + * All of `file:/myfile`, `file:///myfile`, and `file://localhost/myfile` + * are equivalent according to RFC 3986. The first format is not accepted + * by PHPs stream functions and thus already normalized implicitly to the + * second format in the Uri class. See `GuzzleHttp\Psr7\Uri::composeComponents`. + * + * Example: file://localhost/myfile → file:///myfile + */ + const REMOVE_DEFAULT_HOST = 8; + + /** + * Removes the default port of the given URI scheme from the URI. + * + * Example: http://example.org:80/ → http://example.org/ + */ + const REMOVE_DEFAULT_PORT = 16; + + /** + * Removes unnecessary dot-segments. + * + * Dot-segments in relative-path references are not removed as it would + * change the semantics of the URI reference. + * + * Example: http://example.org/../a/b/../c/./d.html → http://example.org/a/c/d.html + */ + const REMOVE_DOT_SEGMENTS = 32; + + /** + * Paths which include two or more adjacent slashes are converted to one. + * + * Webservers usually ignore duplicate slashes and treat those URIs equivalent. + * But in theory those URIs do not need to be equivalent. So this normalization + * may change the semantics. Encoded slashes (%2F) are not removed. + * + * Example: http://example.org//foo///bar.html → http://example.org/foo/bar.html + */ + const REMOVE_DUPLICATE_SLASHES = 64; + + /** + * Sort query parameters with their values in alphabetical order. + * + * However, the order of parameters in a URI may be significant (this is not defined by the standard). + * So this normalization is not safe and may change the semantics of the URI. + * + * Example: ?lang=en&article=fred → ?article=fred&lang=en + * + * Note: The sorting is neither locale nor Unicode aware (the URI query does not get decoded at all) as the + * purpose is to be able to compare URIs in a reproducible way, not to have the params sorted perfectly. + */ + const SORT_QUERY_PARAMETERS = 128; + + /** + * Returns a normalized URI. + * + * The scheme and host component are already normalized to lowercase per PSR-7 UriInterface. + * This methods adds additional normalizations that can be configured with the $flags parameter. + * + * PSR-7 UriInterface cannot distinguish between an empty component and a missing component as + * getQuery(), getFragment() etc. always return a string. This means the URIs "/?#" and "/" are + * treated equivalent which is not necessarily true according to RFC 3986. But that difference + * is highly uncommon in reality. So this potential normalization is implied in PSR-7 as well. + * + * @param UriInterface $uri The URI to normalize + * @param int $flags A bitmask of normalizations to apply, see constants + * + * @return UriInterface The normalized URI + * @link https://tools.ietf.org/html/rfc3986#section-6.2 + */ + public static function normalize(UriInterface $uri, $flags = self::PRESERVING_NORMALIZATIONS) + { + if ($flags & self::CAPITALIZE_PERCENT_ENCODING) { + $uri = self::capitalizePercentEncoding($uri); + } + + if ($flags & self::DECODE_UNRESERVED_CHARACTERS) { + $uri = self::decodeUnreservedCharacters($uri); + } + + if ($flags & self::CONVERT_EMPTY_PATH && $uri->getPath() === '' && + ($uri->getScheme() === 'http' || $uri->getScheme() === 'https') + ) { + $uri = $uri->withPath('/'); + } + + if ($flags & self::REMOVE_DEFAULT_HOST && $uri->getScheme() === 'file' && $uri->getHost() === 'localhost') { + $uri = $uri->withHost(''); + } + + if ($flags & self::REMOVE_DEFAULT_PORT && $uri->getPort() !== null && Uri::isDefaultPort($uri)) { + $uri = $uri->withPort(null); + } + + if ($flags & self::REMOVE_DOT_SEGMENTS && !Uri::isRelativePathReference($uri)) { + $uri = $uri->withPath(UriResolver::removeDotSegments($uri->getPath())); + } + + if ($flags & self::REMOVE_DUPLICATE_SLASHES) { + $uri = $uri->withPath(preg_replace('#//++#', '/', $uri->getPath())); + } + + if ($flags & self::SORT_QUERY_PARAMETERS && $uri->getQuery() !== '') { + $queryKeyValues = explode('&', $uri->getQuery()); + sort($queryKeyValues); + $uri = $uri->withQuery(implode('&', $queryKeyValues)); + } + + return $uri; + } + + /** + * Whether two URIs can be considered equivalent. + * + * Both URIs are normalized automatically before comparison with the given $normalizations bitmask. The method also + * accepts relative URI references and returns true when they are equivalent. This of course assumes they will be + * resolved against the same base URI. If this is not the case, determination of equivalence or difference of + * relative references does not mean anything. + * + * @param UriInterface $uri1 An URI to compare + * @param UriInterface $uri2 An URI to compare + * @param int $normalizations A bitmask of normalizations to apply, see constants + * + * @return bool + * @link https://tools.ietf.org/html/rfc3986#section-6.1 + */ + public static function isEquivalent(UriInterface $uri1, UriInterface $uri2, $normalizations = self::PRESERVING_NORMALIZATIONS) + { + return (string) self::normalize($uri1, $normalizations) === (string) self::normalize($uri2, $normalizations); + } + + private static function capitalizePercentEncoding(UriInterface $uri) + { + $regex = '/(?:%[A-Fa-f0-9]{2})++/'; + + $callback = function (array $match) { + return strtoupper($match[0]); + }; + + return + $uri->withPath( + preg_replace_callback($regex, $callback, $uri->getPath()) + )->withQuery( + preg_replace_callback($regex, $callback, $uri->getQuery()) + ); + } + + private static function decodeUnreservedCharacters(UriInterface $uri) + { + $regex = '/%(?:2D|2E|5F|7E|3[0-9]|[46][1-9A-F]|[57][0-9A])/i'; + + $callback = function (array $match) { + return rawurldecode($match[0]); + }; + + return + $uri->withPath( + preg_replace_callback($regex, $callback, $uri->getPath()) + )->withQuery( + preg_replace_callback($regex, $callback, $uri->getQuery()) + ); + } + + private function __construct() + { + // cannot be instantiated + } +} diff --git a/vendor/guzzlehttp/psr7/src/UriResolver.php b/vendor/guzzlehttp/psr7/src/UriResolver.php new file mode 100644 index 00000000000..c1cb8a275af --- /dev/null +++ b/vendor/guzzlehttp/psr7/src/UriResolver.php @@ -0,0 +1,219 @@ +<?php +namespace GuzzleHttp\Psr7; + +use Psr\Http\Message\UriInterface; + +/** + * Resolves a URI reference in the context of a base URI and the opposite way. + * + * @author Tobias Schultze + * + * @link https://tools.ietf.org/html/rfc3986#section-5 + */ +final class UriResolver +{ + /** + * Removes dot segments from a path and returns the new path. + * + * @param string $path + * + * @return string + * @link http://tools.ietf.org/html/rfc3986#section-5.2.4 + */ + public static function removeDotSegments($path) + { + if ($path === '' || $path === '/') { + return $path; + } + + $results = []; + $segments = explode('/', $path); + foreach ($segments as $segment) { + if ($segment === '..') { + array_pop($results); + } elseif ($segment !== '.') { + $results[] = $segment; + } + } + + $newPath = implode('/', $results); + + if ($path[0] === '/' && (!isset($newPath[0]) || $newPath[0] !== '/')) { + // Re-add the leading slash if necessary for cases like "/.." + $newPath = '/' . $newPath; + } elseif ($newPath !== '' && ($segment === '.' || $segment === '..')) { + // Add the trailing slash if necessary + // If newPath is not empty, then $segment must be set and is the last segment from the foreach + $newPath .= '/'; + } + + return $newPath; + } + + /** + * Converts the relative URI into a new URI that is resolved against the base URI. + * + * @param UriInterface $base Base URI + * @param UriInterface $rel Relative URI + * + * @return UriInterface + * @link http://tools.ietf.org/html/rfc3986#section-5.2 + */ + public static function resolve(UriInterface $base, UriInterface $rel) + { + if ((string) $rel === '') { + // we can simply return the same base URI instance for this same-document reference + return $base; + } + + if ($rel->getScheme() != '') { + return $rel->withPath(self::removeDotSegments($rel->getPath())); + } + + if ($rel->getAuthority() != '') { + $targetAuthority = $rel->getAuthority(); + $targetPath = self::removeDotSegments($rel->getPath()); + $targetQuery = $rel->getQuery(); + } else { + $targetAuthority = $base->getAuthority(); + if ($rel->getPath() === '') { + $targetPath = $base->getPath(); + $targetQuery = $rel->getQuery() != '' ? $rel->getQuery() : $base->getQuery(); + } else { + if ($rel->getPath()[0] === '/') { + $targetPath = $rel->getPath(); + } else { + if ($targetAuthority != '' && $base->getPath() === '') { + $targetPath = '/' . $rel->getPath(); + } else { + $lastSlashPos = strrpos($base->getPath(), '/'); + if ($lastSlashPos === false) { + $targetPath = $rel->getPath(); + } else { + $targetPath = substr($base->getPath(), 0, $lastSlashPos + 1) . $rel->getPath(); + } + } + } + $targetPath = self::removeDotSegments($targetPath); + $targetQuery = $rel->getQuery(); + } + } + + return new Uri(Uri::composeComponents( + $base->getScheme(), + $targetAuthority, + $targetPath, + $targetQuery, + $rel->getFragment() + )); + } + + /** + * Returns the target URI as a relative reference from the base URI. + * + * This method is the counterpart to resolve(): + * + * (string) $target === (string) UriResolver::resolve($base, UriResolver::relativize($base, $target)) + * + * One use-case is to use the current request URI as base URI and then generate relative links in your documents + * to reduce the document size or offer self-contained downloadable document archives. + * + * $base = new Uri('http://example.com/a/b/'); + * echo UriResolver::relativize($base, new Uri('http://example.com/a/b/c')); // prints 'c'. + * echo UriResolver::relativize($base, new Uri('http://example.com/a/x/y')); // prints '../x/y'. + * echo UriResolver::relativize($base, new Uri('http://example.com/a/b/?q')); // prints '?q'. + * echo UriResolver::relativize($base, new Uri('http://example.org/a/b/')); // prints '//example.org/a/b/'. + * + * This method also accepts a target that is already relative and will try to relativize it further. Only a + * relative-path reference will be returned as-is. + * + * echo UriResolver::relativize($base, new Uri('/a/b/c')); // prints 'c' as well + * + * @param UriInterface $base Base URI + * @param UriInterface $target Target URI + * + * @return UriInterface The relative URI reference + */ + public static function relativize(UriInterface $base, UriInterface $target) + { + if ($target->getScheme() !== '' && + ($base->getScheme() !== $target->getScheme() || $target->getAuthority() === '' && $base->getAuthority() !== '') + ) { + return $target; + } + + if (Uri::isRelativePathReference($target)) { + // As the target is already highly relative we return it as-is. It would be possible to resolve + // the target with `$target = self::resolve($base, $target);` and then try make it more relative + // by removing a duplicate query. But let's not do that automatically. + return $target; + } + + if ($target->getAuthority() !== '' && $base->getAuthority() !== $target->getAuthority()) { + return $target->withScheme(''); + } + + // We must remove the path before removing the authority because if the path starts with two slashes, the URI + // would turn invalid. And we also cannot set a relative path before removing the authority, as that is also + // invalid. + $emptyPathUri = $target->withScheme('')->withPath('')->withUserInfo('')->withPort(null)->withHost(''); + + if ($base->getPath() !== $target->getPath()) { + return $emptyPathUri->withPath(self::getRelativePath($base, $target)); + } + + if ($base->getQuery() === $target->getQuery()) { + // Only the target fragment is left. And it must be returned even if base and target fragment are the same. + return $emptyPathUri->withQuery(''); + } + + // If the base URI has a query but the target has none, we cannot return an empty path reference as it would + // inherit the base query component when resolving. + if ($target->getQuery() === '') { + $segments = explode('/', $target->getPath()); + $lastSegment = end($segments); + + return $emptyPathUri->withPath($lastSegment === '' ? './' : $lastSegment); + } + + return $emptyPathUri; + } + + private static function getRelativePath(UriInterface $base, UriInterface $target) + { + $sourceSegments = explode('/', $base->getPath()); + $targetSegments = explode('/', $target->getPath()); + array_pop($sourceSegments); + $targetLastSegment = array_pop($targetSegments); + foreach ($sourceSegments as $i => $segment) { + if (isset($targetSegments[$i]) && $segment === $targetSegments[$i]) { + unset($sourceSegments[$i], $targetSegments[$i]); + } else { + break; + } + } + $targetSegments[] = $targetLastSegment; + $relativePath = str_repeat('../', count($sourceSegments)) . implode('/', $targetSegments); + + // A reference to am empty last segment or an empty first sub-segment must be prefixed with "./". + // This also applies to a segment with a colon character (e.g., "file:colon") that cannot be used + // as the first segment of a relative-path reference, as it would be mistaken for a scheme name. + if ('' === $relativePath || false !== strpos(explode('/', $relativePath, 2)[0], ':')) { + $relativePath = "./$relativePath"; + } elseif ('/' === $relativePath[0]) { + if ($base->getAuthority() != '' && $base->getPath() === '') { + // In this case an extra slash is added by resolve() automatically. So we must not add one here. + $relativePath = ".$relativePath"; + } else { + $relativePath = "./$relativePath"; + } + } + + return $relativePath; + } + + private function __construct() + { + // cannot be instantiated + } +} diff --git a/vendor/guzzlehttp/psr7/src/functions.php b/vendor/guzzlehttp/psr7/src/functions.php new file mode 100644 index 00000000000..e40348d6abd --- /dev/null +++ b/vendor/guzzlehttp/psr7/src/functions.php @@ -0,0 +1,828 @@ +<?php +namespace GuzzleHttp\Psr7; + +use Psr\Http\Message\MessageInterface; +use Psr\Http\Message\RequestInterface; +use Psr\Http\Message\ResponseInterface; +use Psr\Http\Message\ServerRequestInterface; +use Psr\Http\Message\StreamInterface; +use Psr\Http\Message\UriInterface; + +/** + * Returns the string representation of an HTTP message. + * + * @param MessageInterface $message Message to convert to a string. + * + * @return string + */ +function str(MessageInterface $message) +{ + if ($message instanceof RequestInterface) { + $msg = trim($message->getMethod() . ' ' + . $message->getRequestTarget()) + . ' HTTP/' . $message->getProtocolVersion(); + if (!$message->hasHeader('host')) { + $msg .= "\r\nHost: " . $message->getUri()->getHost(); + } + } elseif ($message instanceof ResponseInterface) { + $msg = 'HTTP/' . $message->getProtocolVersion() . ' ' + . $message->getStatusCode() . ' ' + . $message->getReasonPhrase(); + } else { + throw new \InvalidArgumentException('Unknown message type'); + } + + foreach ($message->getHeaders() as $name => $values) { + $msg .= "\r\n{$name}: " . implode(', ', $values); + } + + return "{$msg}\r\n\r\n" . $message->getBody(); +} + +/** + * Returns a UriInterface for the given value. + * + * This function accepts a string or {@see Psr\Http\Message\UriInterface} and + * returns a UriInterface for the given value. If the value is already a + * `UriInterface`, it is returned as-is. + * + * @param string|UriInterface $uri + * + * @return UriInterface + * @throws \InvalidArgumentException + */ +function uri_for($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'); +} + +/** + * Create a new stream based on the input type. + * + * Options is an associative array that can contain the following keys: + * - metadata: Array of custom metadata. + * - size: Size of the stream. + * + * @param resource|string|null|int|float|bool|StreamInterface|callable $resource Entity body data + * @param array $options Additional options + * + * @return Stream + * @throws \InvalidArgumentException if the $resource arg is not valid. + */ +function stream_for($resource = '', array $options = []) +{ + if (is_scalar($resource)) { + $stream = fopen('php://temp', 'r+'); + if ($resource !== '') { + fwrite($stream, $resource); + fseek($stream, 0); + } + return new Stream($stream, $options); + } + + switch (gettype($resource)) { + case 'resource': + return new Stream($resource, $options); + case 'object': + if ($resource instanceof StreamInterface) { + return $resource; + } elseif ($resource instanceof \Iterator) { + return new PumpStream(function () use ($resource) { + if (!$resource->valid()) { + return false; + } + $result = $resource->current(); + $resource->next(); + return $result; + }, $options); + } elseif (method_exists($resource, '__toString')) { + return stream_for((string) $resource, $options); + } + break; + case 'NULL': + return new Stream(fopen('php://temp', 'r+'), $options); + } + + if (is_callable($resource)) { + return new PumpStream($resource, $options); + } + + throw new \InvalidArgumentException('Invalid resource type: ' . gettype($resource)); +} + +/** + * Parse an array of header values containing ";" separated data into an + * array of associative arrays representing the header key value pair + * data of the header. When a parameter does not contain a value, but just + * contains a key, this function will inject a key with a '' string value. + * + * @param string|array $header Header to parse into components. + * + * @return array Returns the parsed header values. + */ +function parse_header($header) +{ + static $trimmed = "\"' \n\t\r"; + $params = $matches = []; + + foreach (normalize_header($header) as $val) { + $part = []; + foreach (preg_split('/;(?=([^"]*"[^"]*")*[^"]*$)/', $val) as $kvp) { + if (preg_match_all('/<[^>]+>|[^=]+/', $kvp, $matches)) { + $m = $matches[0]; + if (isset($m[1])) { + $part[trim($m[0], $trimmed)] = trim($m[1], $trimmed); + } else { + $part[] = trim($m[0], $trimmed); + } + } + } + if ($part) { + $params[] = $part; + } + } + + return $params; +} + +/** + * Converts an array of header values that may contain comma separated + * headers into an array of headers with no comma separated values. + * + * @param string|array $header Header to normalize. + * + * @return array Returns the normalized header field values. + */ +function normalize_header($header) +{ + if (!is_array($header)) { + return array_map('trim', explode(',', $header)); + } + + $result = []; + foreach ($header as $value) { + foreach ((array) $value as $v) { + if (strpos($v, ',') === false) { + $result[] = $v; + continue; + } + foreach (preg_split('/,(?=([^"]*"[^"]*")*[^"]*$)/', $v) as $vv) { + $result[] = trim($vv); + } + } + } + + return $result; +} + +/** + * Clone and modify a request with the given changes. + * + * The changes can be one of: + * - method: (string) Changes the HTTP method. + * - set_headers: (array) Sets the given headers. + * - remove_headers: (array) Remove the given headers. + * - body: (mixed) Sets the given body. + * - uri: (UriInterface) Set the URI. + * - query: (string) Set the query string value of the URI. + * - version: (string) Set the protocol version. + * + * @param RequestInterface $request Request to clone and modify. + * @param array $changes Changes to apply. + * + * @return RequestInterface + */ +function modify_request(RequestInterface $request, array $changes) +{ + if (!$changes) { + return $request; + } + + $headers = $request->getHeaders(); + + if (!isset($changes['uri'])) { + $uri = $request->getUri(); + } else { + // Remove the host header if one is on the URI + if ($host = $changes['uri']->getHost()) { + $changes['set_headers']['Host'] = $host; + + if ($port = $changes['uri']->getPort()) { + $standardPorts = ['http' => 80, 'https' => 443]; + $scheme = $changes['uri']->getScheme(); + if (isset($standardPorts[$scheme]) && $port != $standardPorts[$scheme]) { + $changes['set_headers']['Host'] .= ':'.$port; + } + } + } + $uri = $changes['uri']; + } + + if (!empty($changes['remove_headers'])) { + $headers = _caseless_remove($changes['remove_headers'], $headers); + } + + if (!empty($changes['set_headers'])) { + $headers = _caseless_remove(array_keys($changes['set_headers']), $headers); + $headers = $changes['set_headers'] + $headers; + } + + if (isset($changes['query'])) { + $uri = $uri->withQuery($changes['query']); + } + + if ($request instanceof ServerRequestInterface) { + return new ServerRequest( + isset($changes['method']) ? $changes['method'] : $request->getMethod(), + $uri, + $headers, + isset($changes['body']) ? $changes['body'] : $request->getBody(), + isset($changes['version']) + ? $changes['version'] + : $request->getProtocolVersion(), + $request->getServerParams() + ); + } + + return new Request( + isset($changes['method']) ? $changes['method'] : $request->getMethod(), + $uri, + $headers, + isset($changes['body']) ? $changes['body'] : $request->getBody(), + isset($changes['version']) + ? $changes['version'] + : $request->getProtocolVersion() + ); +} + +/** + * Attempts to rewind a message body and throws an exception on failure. + * + * The body of the message will only be rewound if a call to `tell()` returns a + * value other than `0`. + * + * @param MessageInterface $message Message to rewind + * + * @throws \RuntimeException + */ +function rewind_body(MessageInterface $message) +{ + $body = $message->getBody(); + + if ($body->tell()) { + $body->rewind(); + } +} + +/** + * Safely opens a PHP stream resource using a filename. + * + * When fopen fails, PHP normally raises a warning. This function adds an + * error handler that checks for errors and throws an exception instead. + * + * @param string $filename File to open + * @param string $mode Mode used to open the file + * + * @return resource + * @throws \RuntimeException if the file cannot be opened + */ +function try_fopen($filename, $mode) +{ + $ex = null; + set_error_handler(function () use ($filename, $mode, &$ex) { + $ex = new \RuntimeException(sprintf( + 'Unable to open %s using mode %s: %s', + $filename, + $mode, + func_get_args()[1] + )); + }); + + $handle = fopen($filename, $mode); + restore_error_handler(); + + if ($ex) { + /** @var $ex \RuntimeException */ + throw $ex; + } + + return $handle; +} + +/** + * Copy the contents of a stream into a string until the given number of + * bytes have been read. + * + * @param StreamInterface $stream Stream to read + * @param int $maxLen Maximum number of bytes to read. Pass -1 + * to read the entire stream. + * @return string + * @throws \RuntimeException on error. + */ +function copy_to_string(StreamInterface $stream, $maxLen = -1) +{ + $buffer = ''; + + if ($maxLen === -1) { + while (!$stream->eof()) { + $buf = $stream->read(1048576); + // Using a loose equality here to match on '' and false. + if ($buf == null) { + break; + } + $buffer .= $buf; + } + return $buffer; + } + + $len = 0; + while (!$stream->eof() && $len < $maxLen) { + $buf = $stream->read($maxLen - $len); + // Using a loose equality here to match on '' and false. + if ($buf == null) { + break; + } + $buffer .= $buf; + $len = strlen($buffer); + } + + return $buffer; +} + +/** + * Copy the contents of a stream into another stream until the given number + * of bytes have been read. + * + * @param StreamInterface $source Stream to read from + * @param StreamInterface $dest Stream to write to + * @param int $maxLen Maximum number of bytes to read. Pass -1 + * to read the entire stream. + * + * @throws \RuntimeException on error. + */ +function copy_to_stream( + StreamInterface $source, + StreamInterface $dest, + $maxLen = -1 +) { + $bufferSize = 8192; + + if ($maxLen === -1) { + while (!$source->eof()) { + if (!$dest->write($source->read($bufferSize))) { + break; + } + } + } else { + $remaining = $maxLen; + while ($remaining > 0 && !$source->eof()) { + $buf = $source->read(min($bufferSize, $remaining)); + $len = strlen($buf); + if (!$len) { + break; + } + $remaining -= $len; + $dest->write($buf); + } + } +} + +/** + * Calculate a hash of a Stream + * + * @param StreamInterface $stream Stream to calculate the hash for + * @param string $algo Hash algorithm (e.g. md5, crc32, etc) + * @param bool $rawOutput Whether or not to use raw output + * + * @return string Returns the hash of the stream + * @throws \RuntimeException on error. + */ +function hash( + StreamInterface $stream, + $algo, + $rawOutput = false +) { + $pos = $stream->tell(); + + if ($pos > 0) { + $stream->rewind(); + } + + $ctx = hash_init($algo); + while (!$stream->eof()) { + hash_update($ctx, $stream->read(1048576)); + } + + $out = hash_final($ctx, (bool) $rawOutput); + $stream->seek($pos); + + return $out; +} + +/** + * Read a line from the stream up to the maximum allowed buffer length + * + * @param StreamInterface $stream Stream to read from + * @param int $maxLength Maximum buffer length + * + * @return string|bool + */ +function readline(StreamInterface $stream, $maxLength = null) +{ + $buffer = ''; + $size = 0; + + while (!$stream->eof()) { + // Using a loose equality here to match on '' and false. + if (null == ($byte = $stream->read(1))) { + return $buffer; + } + $buffer .= $byte; + // Break when a new line is found or the max length - 1 is reached + if ($byte === "\n" || ++$size === $maxLength - 1) { + break; + } + } + + return $buffer; +} + +/** + * Parses a request message string into a request object. + * + * @param string $message Request message string. + * + * @return Request + */ +function parse_request($message) +{ + $data = _parse_message($message); + $matches = []; + if (!preg_match('/^[\S]+\s+([a-zA-Z]+:\/\/|\/).*/', $data['start-line'], $matches)) { + throw new \InvalidArgumentException('Invalid request string'); + } + $parts = explode(' ', $data['start-line'], 3); + $version = isset($parts[2]) ? explode('/', $parts[2])[1] : '1.1'; + + $request = new Request( + $parts[0], + $matches[1] === '/' ? _parse_request_uri($parts[1], $data['headers']) : $parts[1], + $data['headers'], + $data['body'], + $version + ); + + return $matches[1] === '/' ? $request : $request->withRequestTarget($parts[1]); +} + +/** + * Parses a response message string into a response object. + * + * @param string $message Response message string. + * + * @return Response + */ +function parse_response($message) +{ + $data = _parse_message($message); + // According to https://tools.ietf.org/html/rfc7230#section-3.1.2 the space + // between status-code and reason-phrase is required. But browsers accept + // responses without space and reason as well. + if (!preg_match('/^HTTP\/.* [0-9]{3}( .*|$)/', $data['start-line'])) { + throw new \InvalidArgumentException('Invalid response string'); + } + $parts = explode(' ', $data['start-line'], 3); + + return new Response( + $parts[1], + $data['headers'], + $data['body'], + explode('/', $parts[0])[1], + isset($parts[2]) ? $parts[2] : null + ); +} + +/** + * Parse a query string into an associative array. + * + * If multiple values are found for the same key, the value of that key + * value pair will become an array. This function does not parse nested + * PHP style arrays into an associative array (e.g., foo[a]=1&foo[b]=2 will + * be parsed into ['foo[a]' => '1', 'foo[b]' => '2']). + * + * @param string $str Query string to parse + * @param bool|string $urlEncoding How the query string is encoded + * + * @return array + */ +function parse_query($str, $urlEncoding = true) +{ + $result = []; + + if ($str === '') { + return $result; + } + + if ($urlEncoding === true) { + $decoder = function ($value) { + return rawurldecode(str_replace('+', ' ', $value)); + }; + } elseif ($urlEncoding == PHP_QUERY_RFC3986) { + $decoder = 'rawurldecode'; + } elseif ($urlEncoding == PHP_QUERY_RFC1738) { + $decoder = 'urldecode'; + } else { + $decoder = function ($str) { return $str; }; + } + + foreach (explode('&', $str) as $kvp) { + $parts = explode('=', $kvp, 2); + $key = $decoder($parts[0]); + $value = isset($parts[1]) ? $decoder($parts[1]) : null; + if (!isset($result[$key])) { + $result[$key] = $value; + } else { + if (!is_array($result[$key])) { + $result[$key] = [$result[$key]]; + } + $result[$key][] = $value; + } + } + + return $result; +} + +/** + * Build a query string from an array of key value pairs. + * + * This function can use the return value of parse_query() to build a query + * string. This function does not modify the provided keys when an array is + * encountered (like http_build_query would). + * + * @param array $params Query string parameters. + * @param int|false $encoding Set to false to not encode, PHP_QUERY_RFC3986 + * to encode using RFC3986, or PHP_QUERY_RFC1738 + * to encode using RFC1738. + * @return string + */ +function build_query(array $params, $encoding = PHP_QUERY_RFC3986) +{ + if (!$params) { + return ''; + } + + if ($encoding === false) { + $encoder = function ($str) { return $str; }; + } elseif ($encoding === PHP_QUERY_RFC3986) { + $encoder = 'rawurlencode'; + } elseif ($encoding === PHP_QUERY_RFC1738) { + $encoder = 'urlencode'; + } else { + throw new \InvalidArgumentException('Invalid type'); + } + + $qs = ''; + foreach ($params as $k => $v) { + $k = $encoder($k); + if (!is_array($v)) { + $qs .= $k; + if ($v !== null) { + $qs .= '=' . $encoder($v); + } + $qs .= '&'; + } else { + foreach ($v as $vv) { + $qs .= $k; + if ($vv !== null) { + $qs .= '=' . $encoder($vv); + } + $qs .= '&'; + } + } + } + + return $qs ? (string) substr($qs, 0, -1) : ''; +} + +/** + * Determines the mimetype of a file by looking at its extension. + * + * @param $filename + * + * @return null|string + */ +function mimetype_from_filename($filename) +{ + return mimetype_from_extension(pathinfo($filename, PATHINFO_EXTENSION)); +} + +/** + * Maps a file extensions to a mimetype. + * + * @param $extension string The file extension. + * + * @return string|null + * @link http://svn.apache.org/repos/asf/httpd/httpd/branches/1.3.x/conf/mime.types + */ +function mimetype_from_extension($extension) +{ + static $mimetypes = [ + '7z' => 'application/x-7z-compressed', + 'aac' => 'audio/x-aac', + 'ai' => 'application/postscript', + 'aif' => 'audio/x-aiff', + 'asc' => 'text/plain', + 'asf' => 'video/x-ms-asf', + 'atom' => 'application/atom+xml', + 'avi' => 'video/x-msvideo', + 'bmp' => 'image/bmp', + 'bz2' => 'application/x-bzip2', + 'cer' => 'application/pkix-cert', + 'crl' => 'application/pkix-crl', + 'crt' => 'application/x-x509-ca-cert', + 'css' => 'text/css', + 'csv' => 'text/csv', + 'cu' => 'application/cu-seeme', + 'deb' => 'application/x-debian-package', + 'doc' => 'application/msword', + 'docx' => 'application/vnd.openxmlformats-officedocument.wordprocessingml.document', + 'dvi' => 'application/x-dvi', + 'eot' => 'application/vnd.ms-fontobject', + 'eps' => 'application/postscript', + 'epub' => 'application/epub+zip', + 'etx' => 'text/x-setext', + 'flac' => 'audio/flac', + 'flv' => 'video/x-flv', + 'gif' => 'image/gif', + 'gz' => 'application/gzip', + 'htm' => 'text/html', + 'html' => 'text/html', + 'ico' => 'image/x-icon', + 'ics' => 'text/calendar', + 'ini' => 'text/plain', + 'iso' => 'application/x-iso9660-image', + 'jar' => 'application/java-archive', + 'jpe' => 'image/jpeg', + 'jpeg' => 'image/jpeg', + 'jpg' => 'image/jpeg', + 'js' => 'text/javascript', + 'json' => 'application/json', + 'latex' => 'application/x-latex', + 'log' => 'text/plain', + 'm4a' => 'audio/mp4', + 'm4v' => 'video/mp4', + 'mid' => 'audio/midi', + 'midi' => 'audio/midi', + 'mov' => 'video/quicktime', + 'mp3' => 'audio/mpeg', + 'mp4' => 'video/mp4', + 'mp4a' => 'audio/mp4', + 'mp4v' => 'video/mp4', + 'mpe' => 'video/mpeg', + 'mpeg' => 'video/mpeg', + 'mpg' => 'video/mpeg', + 'mpg4' => 'video/mp4', + 'oga' => 'audio/ogg', + 'ogg' => 'audio/ogg', + 'ogv' => 'video/ogg', + 'ogx' => 'application/ogg', + 'pbm' => 'image/x-portable-bitmap', + 'pdf' => 'application/pdf', + 'pgm' => 'image/x-portable-graymap', + 'png' => 'image/png', + 'pnm' => 'image/x-portable-anymap', + 'ppm' => 'image/x-portable-pixmap', + 'ppt' => 'application/vnd.ms-powerpoint', + 'pptx' => 'application/vnd.openxmlformats-officedocument.presentationml.presentation', + 'ps' => 'application/postscript', + 'qt' => 'video/quicktime', + 'rar' => 'application/x-rar-compressed', + 'ras' => 'image/x-cmu-raster', + 'rss' => 'application/rss+xml', + 'rtf' => 'application/rtf', + 'sgm' => 'text/sgml', + 'sgml' => 'text/sgml', + 'svg' => 'image/svg+xml', + 'swf' => 'application/x-shockwave-flash', + 'tar' => 'application/x-tar', + 'tif' => 'image/tiff', + 'tiff' => 'image/tiff', + 'torrent' => 'application/x-bittorrent', + 'ttf' => 'application/x-font-ttf', + 'txt' => 'text/plain', + 'wav' => 'audio/x-wav', + 'webm' => 'video/webm', + 'wma' => 'audio/x-ms-wma', + 'wmv' => 'video/x-ms-wmv', + 'woff' => 'application/x-font-woff', + 'wsdl' => 'application/wsdl+xml', + 'xbm' => 'image/x-xbitmap', + 'xls' => 'application/vnd.ms-excel', + 'xlsx' => 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet', + 'xml' => 'application/xml', + 'xpm' => 'image/x-xpixmap', + 'xwd' => 'image/x-xwindowdump', + 'yaml' => 'text/yaml', + 'yml' => 'text/yaml', + 'zip' => 'application/zip', + ]; + + $extension = strtolower($extension); + + return isset($mimetypes[$extension]) + ? $mimetypes[$extension] + : null; +} + +/** + * Parses an HTTP message into an associative array. + * + * The array contains the "start-line" key containing the start line of + * the message, "headers" key containing an associative array of header + * array values, and a "body" key containing the body of the message. + * + * @param string $message HTTP request or response to parse. + * + * @return array + * @internal + */ +function _parse_message($message) +{ + if (!$message) { + throw new \InvalidArgumentException('Invalid message'); + } + + // Iterate over each line in the message, accounting for line endings + $lines = preg_split('/(\\r?\\n)/', $message, -1, PREG_SPLIT_DELIM_CAPTURE); + $result = ['start-line' => array_shift($lines), 'headers' => [], 'body' => '']; + array_shift($lines); + + for ($i = 0, $totalLines = count($lines); $i < $totalLines; $i += 2) { + $line = $lines[$i]; + // If two line breaks were encountered, then this is the end of body + if (empty($line)) { + if ($i < $totalLines - 1) { + $result['body'] = implode('', array_slice($lines, $i + 2)); + } + break; + } + if (strpos($line, ':')) { + $parts = explode(':', $line, 2); + $key = trim($parts[0]); + $value = isset($parts[1]) ? trim($parts[1]) : ''; + $result['headers'][$key][] = $value; + } + } + + return $result; +} + +/** + * Constructs a URI for an HTTP request message. + * + * @param string $path Path from the start-line + * @param array $headers Array of headers (each value an array). + * + * @return string + * @internal + */ +function _parse_request_uri($path, array $headers) +{ + $hostKey = array_filter(array_keys($headers), function ($k) { + return strtolower($k) === 'host'; + }); + + // If no host is found, then a full URI cannot be constructed. + if (!$hostKey) { + return $path; + } + + $host = $headers[reset($hostKey)][0]; + $scheme = substr($host, -4) === ':443' ? 'https' : 'http'; + + return $scheme . '://' . $host . '/' . ltrim($path, '/'); +} + +/** @internal */ +function _caseless_remove($keys, array $data) +{ + $result = []; + + foreach ($keys as &$key) { + $key = strtolower($key); + } + + foreach ($data as $k => $v) { + if (!in_array(strtolower($k), $keys)) { + $result[$k] = $v; + } + } + + return $result; +} diff --git a/vendor/guzzlehttp/psr7/src/functions_include.php b/vendor/guzzlehttp/psr7/src/functions_include.php new file mode 100644 index 00000000000..96a4a83a01a --- /dev/null +++ b/vendor/guzzlehttp/psr7/src/functions_include.php @@ -0,0 +1,6 @@ +<?php + +// Don't redefine the functions if included multiple times. +if (!function_exists('GuzzleHttp\Psr7\str')) { + require __DIR__ . '/functions.php'; +} diff --git a/vendor/m4tthumphrey/php-gitlab-api/.gitignore b/vendor/m4tthumphrey/php-gitlab-api/.gitignore deleted file mode 100644 index d1502b087b4..00000000000 --- a/vendor/m4tthumphrey/php-gitlab-api/.gitignore +++ /dev/null @@ -1,2 +0,0 @@ -vendor/ -composer.lock diff --git a/vendor/m4tthumphrey/php-gitlab-api/.travis.yml b/vendor/m4tthumphrey/php-gitlab-api/.travis.yml deleted file mode 100644 index 97190200d26..00000000000 --- a/vendor/m4tthumphrey/php-gitlab-api/.travis.yml +++ /dev/null @@ -1,17 +0,0 @@ -language: php - -sudo: false - -php: - - 5.3 - - 5.4 - - 5.5 - - 5.6 - - 7.0 - -before_script: - - travis_retry composer self-update - - travis_retry composer install --no-interaction --prefer-source - -script: - - vendor/bin/phpunit --verbose --coverage-text diff --git a/vendor/m4tthumphrey/php-gitlab-api/LICENSE b/vendor/m4tthumphrey/php-gitlab-api/LICENSE index 8791c72e0c8..a30bb7fa527 100644 --- a/vendor/m4tthumphrey/php-gitlab-api/LICENSE +++ b/vendor/m4tthumphrey/php-gitlab-api/LICENSE @@ -1,6 +1,6 @@ MIT License -Copyright (c) 2017 Matt Humphrey +Copyright (c) 2018 Matt Humphrey Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/vendor/m4tthumphrey/php-gitlab-api/README.md b/vendor/m4tthumphrey/php-gitlab-api/README.md deleted file mode 100644 index b8cbdb352a5..00000000000 --- a/vendor/m4tthumphrey/php-gitlab-api/README.md +++ /dev/null @@ -1,96 +0,0 @@ -A PHP wrapper to be used with [Gitlab's API](https://github.com/gitlabhq/gitlabhq/tree/master/doc/api). -============== - -[](https://travis-ci.org/m4tthumphrey/php-gitlab-api) - -Based on [php-github-api](https://github.com/m4tthumphrey/php-github-api) and code from [KnpLabs](https://github.com/KnpLabs/php-github-api). - -Installation ------------- -1. Install Composer - - ```bash - $ curl -sS https://getcomposer.org/installer | php - $ sudo mv composer.phar /usr/local/bin/composer - ``` - -2. Add the following to your require block in composer.json config. - - > Note: be careful when using the `dev-master` tag as this may have unexpected results depending on your version of - Gitlab. See the Versioning section below for more information. - - `php composer.phar require m4tthumphrey/php-gitlab-api:dev-master` - -3. Include Composer's autoloader: - - ```php - require_once dirname(__DIR__).'/vendor/autoload.php'; - ``` - -Versioning ----------- - -From the 6.0 stable release of Gitlab, I shall now be matching the client version with the Gitlab version. For example -when Gitlab 6.1 is released I will release version 6.1.0 of the API client. If I need to make future updates to the client -before the next API version is released, I'll simply use a 3rd build version - `6.1.1`, `6.1.2` etc for example. - -It is recommended that you keep your composer file in sync with whatever version of Gitlab you are currently running: -if you are using 6.0, you should require `6.0.*`; 6.1 should be `6.1.*`... - -General API Usage ------------------ - -```php -$client = new \Gitlab\Client('http://git.yourdomain.com/api/v3/'); // change here -$client->authenticate('your_gitlab_token_here', \Gitlab\Client::AUTH_URL_TOKEN); // change here - -$project = $client->api('projects')->create('My Project', array( - 'description' => 'This is a project', - 'issues_enabled' => false -)); - -``` - -Model Usage ------------ - -You can also use the library in an object oriented manner: - -```php -$client = new \Gitlab\Client('http://git.yourdomain.com/api/v3/'); // change here -$client->authenticate('your_gitlab_token_here', \Gitlab\Client::AUTH_URL_TOKEN); // change here - -# Creating a new project -$project = \Gitlab\Model\Project::create($client, 'My Project', array( - 'description' => 'This is my project', - 'issues_enabled' => false -)); - -$project->addHook('http://mydomain.com/hook/push/1'); - -# Creating a new issue -$project = new \Gitlab\Model\Project(1, $client); -$issue = $project->createIssue('This does not work.', array( - 'description' => 'This doesn\'t work properly. Please fix.', - 'assignee_id' => 2 -)); - -# Closing that issue -$issue->close(); -``` - -You get the idea! Take a look around ([API methods](https://github.com/m4tthumphrey/php-gitlab-api/tree/master/lib/Gitlab/Api), -[models](https://github.com/m4tthumphrey/php-gitlab-api/tree/master/lib/Gitlab/Model)) and please feel free to report any bugs. - -Framework Integrations ----------------------- -- **Symfony** - https://github.com/Zeichen32/GitLabApiBundle -- **Laravel** - https://github.com/vinkla/gitlab - -If you have integrated GitLab into a popular PHP framework, let us know! - -Contributing ------------- - -There are many parts of Gitlab that I have not added to this as it was originally created for personal use, hence the -lack of tests. Feel free to fork and add new functionality and tests, I'll gladly accept decent pull requests. diff --git a/vendor/m4tthumphrey/php-gitlab-api/composer.json b/vendor/m4tthumphrey/php-gitlab-api/composer.json index 3c29600a42e..1075af28e95 100644 --- a/vendor/m4tthumphrey/php-gitlab-api/composer.json +++ b/vendor/m4tthumphrey/php-gitlab-api/composer.json @@ -1,35 +1,50 @@ -{ - "name": "m4tthumphrey/php-gitlab-api", - "type": "library", - "description": "GitLab API client", - "homepage": "https://github.com/m4tthumphrey/php-gitlab-api", - "keywords": ["gitlab", "api"], - "license": "MIT", - "authors": [ - { - "name": "Matt Humphrey", - "homepage": "http://m4tt.io" - }, - { - "name": "KnpLabs Team", - "homepage": "http://knplabs.com" - }, - { - "name": "Thibault Duplessis", - "email": "thibault.duplessis@gmail.com", - "homepage": "http://ornicar.github.com" - } - ], - "require": { - "php": ">=5.3.2", - "ext-curl": "*", - "ext-xml": "*", - "kriswallsmith/buzz": ">=0.7" - }, - "require-dev": { - "phpunit/phpunit": "~4.5" - }, - "autoload": { - "psr-0": { "Gitlab\\": "lib/" } - } -} +{ + "name": "m4tthumphrey/php-gitlab-api", + "type": "library", + "description": "GitLab API client", + "homepage": "https://github.com/m4tthumphrey/php-gitlab-api", + "keywords": ["gitlab", "api"], + "license": "MIT", + "authors": [ + { + "name": "Matt Humphrey", + "homepage": "http://m4tt.io" + }, + { + "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", + "ext-xml": "*", + "php-http/client-common": "^1.6", + "php-http/client-implementation": "^1.0", + "php-http/discovery": "^1.2", + "php-http/httplug": "^1.1", + "php-http/multipart-stream-builder": "^1.0", + "symfony/options-resolver": "^2.6 || ^3.0 || ^4.0" + }, + "require-dev": { + "guzzlehttp/psr7": "^1.2", + "php-http/guzzle6-adapter": "^1.0", + "php-http/mock-client": "^1.0", + "phpunit/phpunit": "^5.0" + }, + "autoload": { + "psr-4": { "Gitlab\\": "lib/Gitlab/" } + }, + "autoload-dev": { + "psr-4": { "Gitlab\\Tests\\": "test/Gitlab/Tests/" } + }, + "extra": { + "branch-alias": { + "dev-master": "9.8.x-dev" + } + } +} diff --git a/vendor/m4tthumphrey/php-gitlab-api/lib/Gitlab/Api/AbstractApi.php b/vendor/m4tthumphrey/php-gitlab-api/lib/Gitlab/Api/AbstractApi.php index d6e3ab723c6..ef9120a20d0 100644 --- a/vendor/m4tthumphrey/php-gitlab-api/lib/Gitlab/Api/AbstractApi.php +++ b/vendor/m4tthumphrey/php-gitlab-api/lib/Gitlab/Api/AbstractApi.php @@ -1,6 +1,14 @@ <?php namespace Gitlab\Api; use Gitlab\Client; +use Gitlab\HttpClient\Message\QueryStringBuilder; +use Gitlab\HttpClient\Message\ResponseMediator; +use Gitlab\Tests\HttpClient\Message\QueryStringBuilderTest; +use Http\Discovery\StreamFactoryDiscovery; +use Http\Message\MultipartStream\MultipartStreamBuilder; +use Http\Message\StreamFactory; +use Psr\Http\Message\ResponseInterface; +use Symfony\Component\OptionsResolver\OptionsResolver; /** * Abstract class for Api classes @@ -11,11 +19,6 @@ use Gitlab\Client; */ abstract class AbstractApi implements ApiInterface { - /** - * Default entries per page - */ - const PER_PAGE = 20; - /** * The client * @@ -23,12 +26,19 @@ abstract class AbstractApi implements ApiInterface */ protected $client; + /** + * @var StreamFactory + */ + private $streamFactory; + /** * @param Client $client + * @param StreamFactory|null $streamFactory */ - public function __construct(Client $client) + public function __construct(Client $client, StreamFactory $streamFactory = null) { $this->client = $client; + $this->streamFactory = $streamFactory ?: StreamFactoryDiscovery::find(); } /** @@ -41,43 +51,69 @@ abstract class AbstractApi implements ApiInterface } /** + * Performs a GET query and returns the response as a PSR-7 response object. + * * @param string $path * @param array $parameters * @param array $requestHeaders - * @return mixed + * @return ResponseInterface */ - protected function get($path, array $parameters = array(), $requestHeaders = array()) + protected function getAsResponse($path, array $parameters = array(), $requestHeaders = array()) { - $response = $this->client->getHttpClient()->get($path, $parameters, $requestHeaders); + $path = $this->preparePath($path, $parameters); - return $response->getContent(); + return $this->client->getHttpClient()->get($path, $requestHeaders); } /** * @param string $path * @param array $parameters * @param array $requestHeaders - * @param array $files * @return mixed */ - protected function post($path, array $parameters = array(), $requestHeaders = array(), array $files = array()) + protected function get($path, array $parameters = array(), $requestHeaders = array()) { - $response = $this->client->getHttpClient()->post($path, $parameters, $requestHeaders, $files); - - return $response->getContent(); + return ResponseMediator::getContent($this->getAsResponse($path, $parameters, $requestHeaders)); } /** * @param string $path * @param array $parameters * @param array $requestHeaders + * @param array $files * @return mixed */ - protected function patch($path, array $parameters = array(), $requestHeaders = array()) + protected function post($path, array $parameters = array(), $requestHeaders = array(), array $files = array()) { - $response = $this->client->getHttpClient()->patch($path, $parameters, $requestHeaders); - - return $response->getContent(); + $path = $this->preparePath($path); + + $body = null; + if (empty($files) && !empty($parameters)) { + $body = $this->streamFactory->createStream(QueryStringBuilder::build($parameters)); + $requestHeaders['Content-Type'] = 'application/x-www-form-urlencoded'; + } elseif (!empty($files)) { + $builder = new MultipartStreamBuilder($this->streamFactory); + + foreach ($parameters as $name => $value) { + $builder->addResource($name, $value); + } + + foreach ($files as $name => $file) { + $builder->addResource($name, fopen($file, 'r'), [ + 'headers' => [ + 'Content-Type' => $this->guessContentType($file), + ], + 'filename' => basename($file), + ]); + } + + $body = $builder->build(); + $requestHeaders['Content-Type'] = 'multipart/form-data; boundary='.$builder->getBoundary(); + } + + $response = $this->client->getHttpClient()->post($path, $requestHeaders, $body); + + return ResponseMediator::getContent($response); } /** @@ -88,9 +124,17 @@ abstract class AbstractApi implements ApiInterface */ protected function put($path, array $parameters = array(), $requestHeaders = array()) { - $response = $this->client->getHttpClient()->put($path, $parameters, $requestHeaders); + $path = $this->preparePath($path); + + $body = null; + if (!empty($parameters)) { + $body = $this->streamFactory->createStream(http_build_query($parameters)); + $requestHeaders['Content-Type'] = 'application/x-www-form-urlencoded'; + } - return $response->getContent(); + $response = $this->client->getHttpClient()->put($path, $requestHeaders, $body); + + return ResponseMediator::getContent($response); } /** @@ -101,9 +145,11 @@ abstract class AbstractApi implements ApiInterface */ protected function delete($path, array $parameters = array(), $requestHeaders = array()) { - $response = $this->client->getHttpClient()->delete($path, $parameters, $requestHeaders); + $path = $this->preparePath($path, $parameters); + + $response = $this->client->getHttpClient()->delete($path, $requestHeaders); - return $response->getContent(); + return ResponseMediator::getContent($response); } /** @@ -126,4 +172,52 @@ abstract class AbstractApi implements ApiInterface return str_replace('.', '%2E', $path); } + + /** + * Create a new OptionsResolver with page and per_page options. + * + * @return OptionsResolver + */ + protected function createOptionsResolver() + { + $resolver = new OptionsResolver(); + $resolver->setDefined('page') + ->setAllowedTypes('page', 'int') + ->setAllowedValues('page', function ($value) { + return $value > 0; + }) + ; + $resolver->setDefined('per_page') + ->setAllowedTypes('per_page', 'int') + ->setAllowedValues('per_page', function ($value) { + return $value > 0 && $value <= 100; + }) + ; + + return $resolver; + } + + private function preparePath($path, array $parameters = []) + { + if (count($parameters) > 0) { + $path .= '?'.QueryStringBuilder::build($parameters); + } + + return $path; + } + + /** + * @param $file + * + * @return string + */ + private function guessContentType($file) + { + if (!class_exists(\finfo::class, false)) { + return 'application/octet-stream'; + } + $finfo = new \finfo(FILEINFO_MIME_TYPE); + + return $finfo->file($file); + } } diff --git a/vendor/m4tthumphrey/php-gitlab-api/lib/Gitlab/Api/DeployKeys.php b/vendor/m4tthumphrey/php-gitlab-api/lib/Gitlab/Api/DeployKeys.php index f7e2a39d5f5..32e0a82cbd0 100644 --- a/vendor/m4tthumphrey/php-gitlab-api/lib/Gitlab/Api/DeployKeys.php +++ b/vendor/m4tthumphrey/php-gitlab-api/lib/Gitlab/Api/DeployKeys.php @@ -2,23 +2,15 @@ class DeployKeys extends AbstractApi { - const ORDER_BY = 'id'; - const SORT = 'asc'; - /** - * @param int $page - * @param int $per_page - * @param string $order_by - * @param string $sort + * @param array $parameters + * * @return mixed */ - public function all($page = 1, $per_page = self::PER_PAGE, $order_by = self::ORDER_BY, $sort = self::SORT) + public function all(array $parameters = []) { - return $this->get('deploy_keys', array( - 'page' => $page, - 'per_page' => $per_page, - 'order_by' => $order_by, - 'sort' => $sort - )); + $resolver = $this->createOptionsResolver(); + + return $this->get('deploy_keys', $resolver->resolve($parameters)); } } diff --git a/vendor/m4tthumphrey/php-gitlab-api/lib/Gitlab/Api/Deployments.php b/vendor/m4tthumphrey/php-gitlab-api/lib/Gitlab/Api/Deployments.php new file mode 100644 index 00000000000..f085e20f2f9 --- /dev/null +++ b/vendor/m4tthumphrey/php-gitlab-api/lib/Gitlab/Api/Deployments.php @@ -0,0 +1,25 @@ +<?php namespace Gitlab\Api; + +class Deployments extends AbstractApi +{ + /** + * @param int $project_id + * @param array $parameters + * @return mixed + */ + public function all($project_id, array $parameters = []) + { + $resolver = $this->createOptionsResolver(); + return $this->get($this->getProjectPath($project_id, 'deployments'), $resolver->resolve($parameters)); + } + + /** + * @param int $project_id + * @param string $deployment_id + * @return mixed + */ + public function show($project_id, $deployment_id) + { + return $this->get($this->getProjectPath($project_id, 'deployments/' . $deployment_id)); + } +} diff --git a/vendor/m4tthumphrey/php-gitlab-api/lib/Gitlab/Api/Environments.php b/vendor/m4tthumphrey/php-gitlab-api/lib/Gitlab/Api/Environments.php new file mode 100644 index 00000000000..48270a6334b --- /dev/null +++ b/vendor/m4tthumphrey/php-gitlab-api/lib/Gitlab/Api/Environments.php @@ -0,0 +1,48 @@ +<?php namespace Gitlab\Api; + +use Symfony\Component\OptionsResolver\OptionsResolver; + +class Environments extends AbstractApi +{ + /** + * @param int $project_id + * @param array $parameters + * @return mixed + */ + public function all($project_id, array $parameters = []) + { + $resolver = $this->createOptionsResolver(); + return $this->get($this->getProjectPath($project_id, 'environments'), $resolver->resolve($parameters)); + } + + /** + * @param int $project_id + * @param array $parameters ( + * + * @var string $name The name of the environment + * @var string $external_url Place to link to for this environment + * ) + * @return mixed + */ + public function create($project_id, array $parameters = array()) + { + $resolver = new OptionsResolver(); + $resolver->setDefined('name') + ->setRequired('name') + ->setAllowedTypes('name', 'string'); + $resolver->setDefined('external_url') + ->setAllowedTypes('external_url', 'string'); + + return $this->post($this->getProjectPath($project_id, 'environment'), $resolver->resolve($parameters)); + } + + /** + * @param int $project_id + * @param string $environment_id + * @return mixed + */ + public function remove($project_id, $environment_id) + { + return $this->delete($this->getProjectPath($project_id, 'environments/' . $environment_id)); + } +} diff --git a/vendor/m4tthumphrey/php-gitlab-api/lib/Gitlab/Api/Groups.php b/vendor/m4tthumphrey/php-gitlab-api/lib/Gitlab/Api/Groups.php index 4ec574d64cd..03807e6c0c7 100644 --- a/vendor/m4tthumphrey/php-gitlab-api/lib/Gitlab/Api/Groups.php +++ b/vendor/m4tthumphrey/php-gitlab-api/lib/Gitlab/Api/Groups.php @@ -1,32 +1,56 @@ <?php namespace Gitlab\Api; +use Symfony\Component\OptionsResolver\Options; + class Groups extends AbstractApi { /** - * @param int $page - * @param int $per_page + * @param array $parameters ( + * + * @var int[] $skip_groups Skip the group IDs passes. + * @var bool $all_available Show all the groups you have access to. + * @var string $search Return list of authorized groups matching the search criteria. + * @var string $order_by Order groups by name or path. Default is name. + * @var string $sort Order groups in asc or desc order. Default is asc. + * @var bool $statistics Include group statistics (admins only). + * @var bool $owned Limit by groups owned by the current user. + * ) * @return mixed */ - public function all($page = 1, $per_page = self::PER_PAGE) + public function all(array $parameters = []) { - return $this->get('groups', array( - 'page' => $page, - 'per_page' => $per_page - )); - } + $resolver = $this->createOptionsResolver(); + $booleanNormalizer = function (Options $resolver, $value) { + return $value ? 'true' : 'false'; + }; - /** - * @param string $query - * @param int $page - * @param int $per_page - * @return mixed - */ - public function search($query, $page = 1, $per_page = self::PER_PAGE) - { - return $this->get('groups?search='.$this->encodePath($query), array( - 'page' => $page, - 'per_page' => $per_page - )); + $resolver->setDefined('skip_groups') + ->setAllowedTypes('skip_groups', 'array') + ->setAllowedValues('skip_groups', function (array $value) { + return count($value) == count(array_filter($value, 'is_int')); + }) + ; + $resolver->setDefined('all_available') + ->setAllowedTypes('all_available', 'bool') + ->setNormalizer('all_available', $booleanNormalizer) + ; + $resolver->setDefined('search'); + $resolver->setDefined('order_by') + ->setAllowedValues('order_by', ['name', 'path']) + ; + $resolver->setDefined('sort') + ->setAllowedValues('sort', ['asc', 'desc']) + ; + $resolver->setDefined('statistics') + ->setAllowedTypes('statistics', 'bool') + ->setNormalizer('statistics', $booleanNormalizer) + ; + $resolver->setDefined('owned') + ->setAllowedTypes('owned', 'bool') + ->setNormalizer('owned', $booleanNormalizer) + ; + + return $this->get('groups', $resolver->resolve($parameters)); } /** @@ -42,15 +66,16 @@ class Groups extends AbstractApi * @param string $name * @param string $path * @param string $description + * @param string $visibility * @return mixed */ - public function create($name, $path, $description = null, $visibility_level = 0) + public function create($name, $path, $description = null, $visibility = 'private') { return $this->post('groups', array( 'name' => $name, 'path' => $path, 'description' => $description, - 'visibility_level' => $visibility_level + 'visibility' => $visibility, )); } @@ -84,17 +109,20 @@ class Groups extends AbstractApi } /** - * @param int $id - * @param int $page - * @param int $per_page + * @param int $id + * @param array $parameters ( + * + * @var string $query A query string to search for members. + * ) + * * @return mixed */ - public function members($id, $page = 1, $per_page = self::PER_PAGE) + public function members($id, array $parameters = []) { - return $this->get('groups/'.$this->encodePath($id).'/members', array( - 'page' => $page, - 'per_page' => $per_page - )); + $resolver = $this->createOptionsResolver(); + $resolver->setDefined('query'); + + return $this->get('groups/'.$this->encodePath($id).'/members', $resolver->resolve($parameters)); } /** @@ -136,15 +164,55 @@ class Groups extends AbstractApi /** * @param $id - * @param int $page - * @param int $per_page + * @param array $parameters ( + * + * @var bool $archived Limit by archived status. + * @var string $visibility Limit by visibility public, internal, or private. + * @var string $order_by Return projects ordered by id, name, path, created_at, updated_at, or last_activity_at fields. + * Default is created_at. + * @var string $sort Return projects sorted in asc or desc order. Default is desc. + * @var string $search Return list of authorized projects matching the search criteria. + * @var bool $simple Return only the ID, URL, name, and path of each project. + * @var bool $owned Limit by projects owned by the current user. + * @var bool $starred Limit by projects starred by the current user. + * ) + * * @return mixed */ - public function projects($id, $page = 1, $per_page = self::PER_PAGE) + public function projects($id, array $parameters = []) { - return $this->get('groups/'.$this->encodePath($id).'/projects', array( - 'page' => $page, - 'per_page' => $per_page - )); + $resolver = $this->createOptionsResolver(); + $booleanNormalizer = function (Options $resolver, $value) { + return $value ? 'true' : 'false'; + }; + + $resolver->setDefined('archived') + ->setAllowedTypes('archived', 'bool') + ->setNormalizer('archived', $booleanNormalizer) + ; + $resolver->setDefined('visibility') + ->setAllowedValues('visibility', ['public', 'internal', 'private']) + ; + $resolver->setDefined('order_by') + ->setAllowedValues('order_by', ['id', 'name', 'path', 'created_at', 'updated_at', 'last_activity_at']) + ; + $resolver->setDefined('sort') + ->setAllowedValues('sort', ['asc', 'desc']) + ; + $resolver->setDefined('search'); + $resolver->setDefined('simple') + ->setAllowedTypes('simple', 'bool') + ->setNormalizer('simple', $booleanNormalizer) + ; + $resolver->setDefined('owned') + ->setAllowedTypes('owned', 'bool') + ->setNormalizer('owned', $booleanNormalizer) + ; + $resolver->setDefined('starred') + ->setAllowedTypes('starred', 'bool') + ->setNormalizer('starred', $booleanNormalizer) + ; + + return $this->get('groups/'.$this->encodePath($id).'/projects', $resolver->resolve($parameters)); } } diff --git a/vendor/m4tthumphrey/php-gitlab-api/lib/Gitlab/Api/IssueBoards.php b/vendor/m4tthumphrey/php-gitlab-api/lib/Gitlab/Api/IssueBoards.php new file mode 100644 index 00000000000..e5d460bc553 --- /dev/null +++ b/vendor/m4tthumphrey/php-gitlab-api/lib/Gitlab/Api/IssueBoards.php @@ -0,0 +1,88 @@ +<?php namespace Gitlab\Api; + +class IssueBoards extends AbstractApi +{ + /** + * @param int $project_id + * @param array $parameters + * + * @return mixed + */ + public function all($project_id = null, array $parameters = []) + { + $resolver = $this->createOptionsResolver(); + + $path = $project_id === null ? 'boards' : $this->getProjectPath($project_id, 'boards'); + + return $this->get($path, $resolver->resolve($parameters)); + } + + /** + * @param int $project_id + * @param int $board_id + * @return mixed + */ + public function allLists($project_id, $board_id) + { + return $this->get($this->getProjectPath($project_id, 'boards/'.$this->encodePath($board_id).'/lists')); + } + + + /** + * @param int $project_id + * @param int $board_id + * @param int $list_id + * @return mixed + */ + public function showList($project_id, $board_id, $list_id) + { + return $this->get($this->getProjectPath($project_id, 'boards/'.$this->encodePath($board_id).'/lists'.$this->encodePath($list_id))); + } + + /** + * @param int $project_id + * @param int $board_id + * @param int $label_id + * @return mixed + */ + public function createList($project_id, $board_id, $label_id) + { + $params = array( + 'id' => $project_id, + 'board_id' => $board_id, + 'label_id' => $label_id + ); + + return $this->get($this->getProjectPath($project_id, 'boards/'.$this->encodePath($board_id).'/lists'), $params); + } + + /** + * @param int $project_id + * @param int $board_id + * @param int $list_id + * @param int $position + * @return mixed + */ + public function updateList($project_id, $board_id, $list_id, $position) + { + $params = array( + 'id' => $project_id, + 'board_id' => $board_id, + 'list_id' => $list_id, + 'position' => $position + ); + + return $this->put($this->getProjectPath($project_id, 'boards/'.$this->encodePath($board_id).'/lists/'.$this->encodePath($list_id)), $params); + } + + /** + * @param int $project_id + * @param int $board_id + * @param int $list_id + * @return mixed + */ + public function deleteList($project_id, $board_id, $list_id) + { + return $this->delete($this->getProjectPath($project_id, 'boards/'.$this->encodePath($board_id).'/lists/'.$this->encodePath($list_id))); + } +} diff --git a/vendor/m4tthumphrey/php-gitlab-api/lib/Gitlab/Api/Issues.php b/vendor/m4tthumphrey/php-gitlab-api/lib/Gitlab/Api/Issues.php index 661ea7ac18a..9fa0f74da2b 100644 --- a/vendor/m4tthumphrey/php-gitlab-api/lib/Gitlab/Api/Issues.php +++ b/vendor/m4tthumphrey/php-gitlab-api/lib/Gitlab/Api/Issues.php @@ -4,32 +4,60 @@ class Issues extends AbstractApi { /** * @param int $project_id - * @param int $page - * @param int $per_page - * @param array $params + * @param array $parameters ( + * + * @var string $state Return all issues or just those that are opened or closed. + * @var string $labels Comma-separated list of label names, issues must have all labels to be returned. + * No+Label lists all issues with no labels. + * @var string $milestone The milestone title. + * @var string scope Return issues for the given scope: created-by-me, assigned-to-me or all. Defaults to created-by-me + * @var int[] $iids Return only the issues having the given iid. + * @var string $order_by Return requests ordered by created_at or updated_at fields. Default is created_at. + * @var string $sort Return requests sorted in asc or desc order. Default is desc. + * @var string $search Search issues against their title and description. + * ) + * * @return mixed */ - public function all($project_id = null, $page = 1, $per_page = self::PER_PAGE, array $params = array()) + public function all($project_id = null, array $parameters = []) { - $path = $project_id === null ? 'issues' : $this->getProjectPath($project_id, 'issues'); + $resolver = $this->createOptionsResolver(); + + $resolver->setDefined('state') + ->setAllowedValues('state', ['opened', 'closed']) + ; + $resolver->setDefined('labels'); + $resolver->setDefined('milestone'); + $resolver->setDefined('iids') + ->setAllowedTypes('iids', 'array') + ->setAllowedValues('iids', function (array $value) { + return count($value) == count(array_filter($value, 'is_int')); + }) + ; + $resolver->setDefined('scope') + ->setAllowedValues('scope', ['created-by-me', 'assigned-to-me', 'all']) + ; + $resolver->setDefined('order_by') + ->setAllowedValues('order_by', ['created_at', 'updated_at']) + ; + $resolver->setDefined('sort') + ->setAllowedValues('sort', ['asc', 'desc']) + ; + $resolver->setDefined('search'); - $params = array_intersect_key($params, array('labels' => '', 'state' => '', 'sort' => '', 'order_by' => '', 'milestone' => '')); - $params = array_merge(array( - 'page' => $page, - 'per_page' => $per_page - ), $params); + $path = $project_id === null ? 'issues' : $this->getProjectPath($project_id, 'issues'); - return $this->get($path, $params); + return $this->get($path, $resolver->resolve($parameters)); } /** * @param int $project_id - * @param int $issue_id + * @param int $issue_iid * @return mixed */ - public function show($project_id, $issue_id) + public function show($project_id, $issue_iid) { - return $this->get($this->getProjectPath($project_id, 'issues?iid='.$this->encodePath($issue_id))); + return $this->get($this->getProjectPath($project_id, 'issues/'.$this->encodePath($issue_iid))); } /** @@ -44,53 +72,53 @@ class Issues extends AbstractApi /** * @param int $project_id - * @param int $issue_id + * @param int $issue_iid * @param array $params * @return mixed */ - public function update($project_id, $issue_id, array $params) + public function update($project_id, $issue_iid, array $params) { - return $this->put($this->getProjectPath($project_id, 'issues/'.$this->encodePath($issue_id)), $params); + return $this->put($this->getProjectPath($project_id, 'issues/'.$this->encodePath($issue_iid)), $params); } /** * @param int $project_id - * @param int $issue_id + * @param int $issue_iid * @return mixed */ - public function remove($project_id, $issue_id) + public function remove($project_id, $issue_iid) { - return $this->delete($this->getProjectPath($project_id, 'issues/'.$this->encodePath($issue_id))); + return $this->delete($this->getProjectPath($project_id, 'issues/'.$this->encodePath($issue_iid))); } /** * @param int $project_id - * @param int $issue_id + * @param int $issue_iid * @return mixed */ - public function showComments($project_id, $issue_id) + public function showComments($project_id, $issue_iid) { - return $this->get($this->getProjectPath($project_id, 'issues/'.$this->encodePath($issue_id)).'/notes'); + return $this->get($this->getProjectPath($project_id, 'issues/'.$this->encodePath($issue_iid)).'/notes'); } /** * @param int $project_id - * @param int $issue_id + * @param int $issue_iid * @param int $note_id * @return mixed */ - public function showComment($project_id, $issue_id, $note_id) + public function showComment($project_id, $issue_iid, $note_id) { - return $this->get($this->getProjectPath($project_id, 'issues/'.$this->encodePath($issue_id)).'/notes/'.$this->encodePath($note_id)); + return $this->get($this->getProjectPath($project_id, 'issues/'.$this->encodePath($issue_iid)).'/notes/'.$this->encodePath($note_id)); } /** * @param int $project_id - * @param int $issue_id + * @param int $issue_iid * @param string|array $body * @return mixed */ - public function addComment($project_id, $issue_id, $body) + public function addComment($project_id, $issue_iid, $body) { // backwards compatibility if (is_array($body)) { @@ -99,70 +127,70 @@ class Issues extends AbstractApi $params = array('body' => $body); } - return $this->post($this->getProjectPath($project_id, 'issues/'.$this->encodePath($issue_id).'/notes'), $params); + return $this->post($this->getProjectPath($project_id, 'issues/'.$this->encodePath($issue_iid).'/notes'), $params); } /** * @param int $project_id - * @param int $issue_id + * @param int $issue_iid * @param int $note_id * @param string $body * @return mixed */ - public function updateComment($project_id, $issue_id, $note_id, $body) + public function updateComment($project_id, $issue_iid, $note_id, $body) { - return $this->put($this->getProjectPath($project_id, 'issues/'.$this->encodePath($issue_id).'/notes/'.$this->encodePath($note_id)), array( + return $this->put($this->getProjectPath($project_id, 'issues/'.$this->encodePath($issue_iid).'/notes/'.$this->encodePath($note_id)), array( 'body' => $body )); } /** * @param int $project_id - * @param int $issue_id + * @param int $issue_iid * @param int $note_id * @return mixed */ - public function removeComment($project_id, $issue_id, $note_id) + public function removeComment($project_id, $issue_iid, $note_id) { - return $this->delete($this->getProjectPath($project_id, 'issues/'.$this->encodePath($issue_id).'/notes/'.$this->encodePath($note_id))); + return $this->delete($this->getProjectPath($project_id, 'issues/'.$this->encodePath($issue_iid).'/notes/'.$this->encodePath($note_id))); } /** * @param int $project_id - * @param int $issue_id + * @param int $issue_iid * @param string $duration */ - public function setTimeEstimate($project_id, $issue_id, $duration) + public function setTimeEstimate($project_id, $issue_iid, $duration) { - return $this->post($this->getProjectPath($project_id, 'issues/'.$this->encodePath($issue_id).'/time_estimate'), array('duration' => $duration)); + return $this->post($this->getProjectPath($project_id, 'issues/'.$this->encodePath($issue_iid).'/time_estimate'), array('duration' => $duration)); } /** * @param int $project_id - * @param int $issue_id + * @param int $issue_iid */ - public function resetTimeEstimate($project_id, $issue_id) + public function resetTimeEstimate($project_id, $issue_iid) { - return $this->post($this->getProjectPath($project_id, 'issues/'.$this->encodePath($issue_id).'/reset_time_estimate')); + return $this->post($this->getProjectPath($project_id, 'issues/'.$this->encodePath($issue_iid).'/reset_time_estimate')); } /** * @param int $project_id - * @param int $issue_id + * @param int $issue_iid * @param string $duration */ - public function addSpentTime($project_id, $issue_id, $duration) + public function addSpentTime($project_id, $issue_iid, $duration) { - return $this->post($this->getProjectPath($project_id, 'issues/'.$this->encodePath($issue_id).'/add_spent_time'), array('duration' => $duration)); + return $this->post($this->getProjectPath($project_id, 'issues/'.$this->encodePath($issue_iid).'/add_spent_time'), array('duration' => $duration)); } /** * @param int $project_id - * @param int $issue_id + * @param int $issue_iid */ - public function resetSpentTime($project_id, $issue_id) + public function resetSpentTime($project_id, $issue_iid) { - return $this->post($this->getProjectPath($project_id, 'issues/'.$this->encodePath($issue_id).'/reset_spent_time')); + return $this->post($this->getProjectPath($project_id, 'issues/'.$this->encodePath($issue_iid).'/reset_spent_time')); } /** @@ -174,4 +202,25 @@ class Issues extends AbstractApi { return $this->get($this->getProjectPath($project_id, 'issues/'.$this->encodePath($issue_iid) .'/time_stats')); } + + /** + * @param int $project_id + * @param int $issue_iid + * + * @return mixed + */ + public function awardEmoji($project_id, $issue_iid) + { + return $this->get($this->getProjectPath($project_id, 'issues/'.$this->encodePath($issue_iid).'/award_emoji')); + } + + /** + * @param int $project_id + * @param int $issue_iid + * @return mixed + */ + public function closedByMergeRequests($project_id, $issue_iid) + { + return $this->get($this->getProjectPath($project_id, 'issues/'.$this->encodePath($issue_iid)).'/closed_by'); + } } diff --git a/vendor/m4tthumphrey/php-gitlab-api/lib/Gitlab/Api/Jobs.php b/vendor/m4tthumphrey/php-gitlab-api/lib/Gitlab/Api/Jobs.php new file mode 100644 index 00000000000..68c6509a540 --- /dev/null +++ b/vendor/m4tthumphrey/php-gitlab-api/lib/Gitlab/Api/Jobs.php @@ -0,0 +1,178 @@ +<?php namespace Gitlab\Api; + +use Psr\Http\Message\StreamInterface; +use Symfony\Component\OptionsResolver\OptionsResolver; + +class Jobs extends AbstractApi +{ + const SCOPE_CREATED = 'created'; + const SCOPE_PENDING = 'pending'; + const SCOPE_RUNNING = 'running'; + const SCOPE_FAILED = 'failed'; + const SCOPE_SUCCESS = 'success'; + const SCOPE_CANCELED = 'canceled'; + const SCOPE_SKIPPED = 'skipped'; + const SCOPE_MANUAL = 'manual'; + + /** + * @param int|string $project_id + * @param array $parameters ( + * + * @var string|string[] $scope The scope of jobs to show, one or array of: created, pending, running, failed, + * success, canceled, skipped, manual; showing all jobs if none provided. + * ) + * + * @return mixed + */ + public function all($project_id, array $parameters = []) + { + $resolver = $this->createOptionsResolver(); + + return $this->get("projects/".$this->encodePath($project_id)."/jobs", $resolver->resolve($parameters)); + } + + /** + * @param int|string $project_id + * @param int $pipeline_id + * @param array $parameters ( + * + * @var string|string[] $scope The scope of jobs to show, one or array of: created, pending, running, failed, + * success, canceled, skipped, manual; showing all jobs if none provided. + * ) + * + * @return mixed + */ + public function pipelineJobs($project_id, $pipeline_id, array $parameters = []) + { + $resolver = $this->createOptionsResolver(); + + return $this->get( + $this->getProjectPath($project_id, 'pipelines/').$this->encodePath($pipeline_id)."/jobs", + $resolver->resolve($parameters) + ); + } + + /** + * @param int|string $project_id + * @param int $job_id + * @return mixed + */ + public function show($project_id, $job_id) + { + return $this->get("projects/".$this->encodePath($project_id)."/jobs/".$this->encodePath($job_id)); + } + + /** + * @param int|string $project_id + * @param int $job_id + * @return StreamInterface + */ + public function artifacts($project_id, $job_id) + { + return $this->getAsResponse("projects/".$this->encodePath($project_id)."/jobs/".$this->encodePath($job_id)."/artifacts")->getBody(); + } + + /** + * @param int|string $project_id + * @param string $ref_name + * @param string $job_name + * @return StreamInterface + */ + public function artifactsByRefName($project_id, $ref_name, $job_name) + { + return $this->getAsResponse("projects/".$this->encodePath($project_id)."/jobs/artifacts/".$this->encodePath($ref_name)."/download", array( + 'job' => $this->encodePath($job_name) + ))->getBody(); + } + + /** + * @param int|string $project_id + * @param int $job_id + * @return string + */ + public function trace($project_id, $job_id) + { + return $this->get("projects/".$this->encodePath($project_id)."/jobs/".$this->encodePath($job_id)."/trace"); + } + + /** + * @param int|string $project_id + * @param int $job_id + * @return mixed + */ + public function cancel($project_id, $job_id) + { + return $this->post("projects/".$this->encodePath($project_id)."/jobs/".$this->encodePath($job_id)."/cancel"); + } + + /** + * @param int|string $project_id + * @param int $job_id + * @return mixed + */ + public function retry($project_id, $job_id) + { + return $this->post("projects/".$this->encodePath($project_id)."/jobs/".$this->encodePath($job_id)."/retry"); + } + + /** + * @param int|string $project_id + * @param int $job_id + * @return mixed + */ + public function erase($project_id, $job_id) + { + return $this->post("projects/".$this->encodePath($project_id)."/jobs/".$this->encodePath($job_id)."/erase"); + } + + /** + * @param int|string $project_id + * @param int $job_id + * @return mixed + */ + public function keepArtifacts($project_id, $job_id) + { + return $this->post("projects/".$this->encodePath($project_id)."/jobs/".$this->encodePath($job_id)."/artifacts/keep"); + } + + /** + * @param int|string $project_id + * @param int $job_id + * @return mixed + */ + public function play($project_id, $job_id) + { + return $this->post("projects/".$this->encodePath($project_id)."/jobs/".$this->encodePath($job_id)."/play"); + } + + /** + * {@inheritdoc} + */ + protected function createOptionsResolver() + { + $allowedScopeValues = [ + self::SCOPE_CANCELED, + self::SCOPE_CREATED, + self::SCOPE_FAILED, + self::SCOPE_MANUAL, + self::SCOPE_PENDING, + self::SCOPE_RUNNING, + self::SCOPE_SKIPPED, + self::SCOPE_SUCCESS, + ]; + + $resolver = parent::createOptionsResolver(); + $resolver->setDefined('scope') + ->setAllowedTypes('scope', ['string', 'array']) + ->setAllowedValues('scope', $allowedScopeValues) + ->addAllowedValues('scope', function ($value) use ($allowedScopeValues) { + return is_array($value) && empty(array_diff($value, $allowedScopeValues)); + }) + ->setNormalizer('scope', function (OptionsResolver $resolver, $value) { + return (array) $value; + }) + ; + + return $resolver; + } +} diff --git a/vendor/m4tthumphrey/php-gitlab-api/lib/Gitlab/Api/MergeRequests.php b/vendor/m4tthumphrey/php-gitlab-api/lib/Gitlab/Api/MergeRequests.php index 2d6dc618495..d7b4ebb2e5f 100644 --- a/vendor/m4tthumphrey/php-gitlab-api/lib/Gitlab/Api/MergeRequests.php +++ b/vendor/m4tthumphrey/php-gitlab-api/lib/Gitlab/Api/MergeRequests.php @@ -1,5 +1,9 @@ <?php namespace Gitlab\Api; +use Symfony\Component\OptionsResolver\Options; +use Symfony\Component\OptionsResolver\Exception\InvalidOptionsException; +use Symfony\Component\OptionsResolver\Exception\UndefinedOptionsException; + class MergeRequests extends AbstractApi { const STATE_ALL = 'all'; @@ -7,80 +11,66 @@ class MergeRequests extends AbstractApi const STATE_OPENED = 'opened'; const STATE_CLOSED = 'closed'; - const ORDER_BY = 'created_at'; - const SORT = 'asc'; - - /** - * @param int $project_id - * @param string $state - * @param int $page - * @param int $per_page - * @param string $order_by - * @param string $sort - * @param string $object - * @return mixed - */ - public function getList($project_id, $state = self::STATE_ALL, $page = 1, $per_page = self::PER_PAGE, $order_by = self::ORDER_BY, $sort = self::SORT, $object = 'merge_requests') - { - return $this->get($this->getProjectPath($project_id, $object), array( - 'page' => $page, - 'per_page' => $per_page, - 'state' => $state, - 'order_by' => $order_by, - 'sort' => $sort - )); - } - - /** - * @param int $project_id - * @param int $page - * @param int $per_page - * @param string $order_by - * @param string $sort - * @return mixed - */ - public function all($project_id, $page = 1, $per_page = self::PER_PAGE, $order_by = self::ORDER_BY, $sort = self::SORT) - { - return $this->getList($project_id, self::STATE_ALL, $page, $per_page, $order_by, $sort); - } - /** - * @param int $project_id - * @param int $page - * @param int $per_page - * @param string $order_by - * @param string $sort + * @param int $project_id + * @param array $parameters { + * + * @var int[] $iids Return the request having the given iid. + * @var string $state Return all merge requests or just those that are opened, closed, or + * merged. + * @var string $order_by Return requests ordered by created_at or updated_at fields. Default + * is created_at. + * @var string $sort Return requests sorted in asc or desc order. Default is desc. + * @var string $milestone Return merge requests for a specific milestone. + * @var string $view If simple, returns the iid, URL, title, description, and basic state + * of merge request. + * @var string $labels Return merge requests matching a comma separated list of labels. + * @var \DateTimeInterface $created_after Return merge requests created after the given time (inclusive). + * @var \DateTimeInterface $created_before Return merge requests created before the given time (inclusive). + * } + * + * @throws UndefinedOptionsException If an option name is undefined. + * @throws InvalidOptionsException If an option doesn't fulfill the specified validation rules. + * * @return mixed */ - public function merged($project_id, $page = 1, $per_page = self::PER_PAGE, $order_by = self::ORDER_BY, $sort = self::SORT) + public function all($project_id, array $parameters = []) { - return $this->getList($project_id, self::STATE_MERGED, $page, $per_page, $order_by, $sort); - } - - /** - * @param int $project_id - * @param int $page - * @param int $per_page - * @param string $order_by - * @param string $sort - * @return mixed - */ - public function opened($project_id, $page = 1, $per_page = self::PER_PAGE, $order_by = self::ORDER_BY, $sort = self::SORT) - { - return $this->getList($project_id, self::STATE_OPENED, $page, $per_page, $order_by, $sort); - } - - /** - * @param int $project_id - * @param int $page - * @param int $per_page - * @param string $order_by - * @param string $sort - * @return mixed - */ - public function closed($project_id, $page = 1, $per_page = self::PER_PAGE, $order_by = self::ORDER_BY, $sort = self::SORT) - { - return $this->getList($project_id, self::STATE_CLOSED, $page, $per_page, $order_by, $sort); + $resolver = $this->createOptionsResolver(); + $datetimeNormalizer = function (Options $resolver, \DateTimeInterface $value) { + return $value->format('c'); + }; + $resolver->setDefined('iids') + ->setAllowedTypes('iids', 'array') + ->setAllowedValues('iids', function (array $value) { + return count($value) == count(array_filter($value, 'is_int')); + }) + ; + $resolver->setDefined('state') + ->setAllowedValues('state', ['all', 'opened', 'merged', 'closed']) + ; + $resolver->setDefined('order_by') + ->setAllowedValues('order_by', ['created_at', 'updated_at']) + ; + $resolver->setDefined('sort') + ->setAllowedValues('sort', ['asc', 'desc']) + ; + $resolver->setDefined('milestone'); + $resolver->setDefined('view') + ->setAllowedValues('view', ['simple']) + ; + $resolver->setDefined('labels'); + $resolver->setDefined('created_after') + ->setAllowedTypes('created_after', \DateTimeInterface::class) + ->setNormalizer('created_after', $datetimeNormalizer) + ; + $resolver->setDefined('created_before') + ->setAllowedTypes('created_before', \DateTimeInterface::class) + ->setNormalizer('created_before', $datetimeNormalizer) + ; + $resolver->setDefined('search'); + + return $this->get($this->getProjectPath($project_id, 'merge_requests'), $resolver->resolve($parameters)); } /** @@ -90,7 +80,7 @@ class MergeRequests extends AbstractApi */ public function show($project_id, $mr_id) { - return $this->get($this->getProjectPath($project_id, 'merge_request/'.$this->encodePath($mr_id))); + return $this->get($this->getProjectPath($project_id, 'merge_requests/'.$this->encodePath($mr_id))); } /** @@ -123,7 +113,7 @@ class MergeRequests extends AbstractApi */ public function update($project_id, $mr_id, array $params) { - return $this->put($this->getProjectPath($project_id, 'merge_request/'.$this->encodePath($mr_id)), $params); + return $this->put($this->getProjectPath($project_id, 'merge_requests/'.$this->encodePath($mr_id)), $params); } /** @@ -140,17 +130,18 @@ class MergeRequests extends AbstractApi $params = array('merge_commit_message' => $message); } - return $this->put($this->getProjectPath($project_id, 'merge_request/'.$this->encodePath($mr_id).'/merge'), $params); + return $this->put($this->getProjectPath($project_id, 'merge_requests/'.$this->encodePath($mr_id).'/merge'), $params); } /** - * @param int $project_id - * @param int $mr_id + * @param int $project_id + * @param int $mr_id + * * @return mixed */ - public function showNotes($project_id, $mr_id, $page = 1, $per_page = self::PER_PAGE, $order_by = self::ORDER_BY, $sort = 'desc') + public function showNotes($project_id, $mr_id) { - return $this->getList($project_id, null, $page, $per_page, $order_by, $sort, 'merge_requests/'.$this->encodePath($mr_id).'/notes'); + return $this->get($this->getProjectPath($project_id, 'merge_requests/'.$this->encodePath($mr_id).'/notes')); } /** @@ -173,7 +164,9 @@ class MergeRequests extends AbstractApi */ public function showComments($project_id, $mr_id) { - return $this->get($this->getProjectPath($project_id, 'merge_request/'.$this->encodePath($mr_id).'/comments')); + @trigger_error(sprintf('The %s() method is deprecated since version 9.1 and will be removed in 10.0. Use the showNotes() method instead.', __METHOD__), E_USER_DEPRECATED); + + return $this->showNotes($project_id, $mr_id); } /** @@ -184,9 +177,9 @@ class MergeRequests extends AbstractApi */ public function addComment($project_id, $mr_id, $note) { - return $this->post($this->getProjectPath($project_id, 'merge_request/'.$this->encodePath($mr_id).'/comments'), array( - 'note' => $note - )); + @trigger_error(sprintf('The %s() method is deprecated since version 9.1 and will be removed in 10.0. Use the addNote() method instead.', __METHOD__), E_USER_DEPRECATED); + + return $this->addNote($project_id, $mr_id, $note); } /** @@ -196,17 +189,17 @@ class MergeRequests extends AbstractApi */ public function changes($project_id, $mr_id) { - return $this->get($this->getProjectPath($project_id, 'merge_request/'.$this->encodePath($mr_id).'/changes')); + return $this->get($this->getProjectPath($project_id, 'merge_requests/'.$this->encodePath($mr_id).'/changes')); } /** - * @param $project_id - * @param $mr_iid + * @param int $project_id + * @param int $mr_id * @return mixed */ - public function getByIid($project_id, $mr_iid) + public function commits($project_id, $mr_id) { - return $this->get($this->getProjectPath($project_id, 'merge_requests'), array('iid' => $mr_iid)); + return $this->get($this->getProjectPath($project_id, 'merge_requests/'.$this->encodePath($mr_id).'/commits')); } /** @@ -214,8 +207,52 @@ class MergeRequests extends AbstractApi * @param int $mr_id * @return mixed */ - public function commits($project_id, $mr_id) + public function closesIssues($project_id, $mr_id) + { + return $this->get($this->getProjectPath($project_id, 'merge_requests/'.$this->encodePath($mr_id).'/closes_issues')); + } + + /** + * @param int $project_id + * @param int $mr_id + * + * @return mixed + */ + public function approvals($project_id, $merge_request_iid) + { + return $this->get($this->getProjectPath($project_id, 'merge_requests/'.$this->encodePath($merge_request_iid).'/approvals')); + } + + /** + * @param int $project_id + * @param int $mr_id + * + * @return mixed + */ + public function approve($project_id, $merge_request_iid) + { + return $this->post($this->getProjectPath($project_id, 'merge_requests/'.$this->encodePath($merge_request_iid).'/approve')); + } + + /** + * @param int $project_id + * @param int $mr_id + * + * @return mixed + */ + public function unapprove($project_id, $merge_request_iid) + { + return $this->post($this->getProjectPath($project_id, 'merge_requests/'.$this->encodePath($merge_request_iid).'/unapprove')); + } + + /** + * @param int $project_id + * @param int $merge_request_iid + * + * @return mixed + */ + public function awardEmoji($project_id, $merge_request_iid) { - return $this->get($this->getProjectPath($project_id, 'merge_request/'.$this->encodePath($mr_id).'/commits')); + return $this->get($this->getProjectPath($project_id, 'merge_requests/'.$this->encodePath($merge_request_iid).'/award_emoji')); } } diff --git a/vendor/m4tthumphrey/php-gitlab-api/lib/Gitlab/Api/Milestones.php b/vendor/m4tthumphrey/php-gitlab-api/lib/Gitlab/Api/Milestones.php index 3b15281834e..0f9b2d25ed5 100644 --- a/vendor/m4tthumphrey/php-gitlab-api/lib/Gitlab/Api/Milestones.php +++ b/vendor/m4tthumphrey/php-gitlab-api/lib/Gitlab/Api/Milestones.php @@ -4,16 +4,30 @@ class Milestones extends AbstractApi { /** * @param int $project_id - * @param int $page - * @param int $per_page + * @param array $parameters ( + * + * @var int[] $iids Return only the milestones having the given iids. + * @var string $state Return only active or closed milestones. + * @var string $search Return only milestones with a title or description matching the provided string. + * ) + * * @return mixed */ - public function all($project_id, $page = 1, $per_page = self::PER_PAGE) + public function all($project_id, array $parameters = []) { - return $this->get($this->getProjectPath($project_id, 'milestones'), array( - 'page' => $page, - 'per_page' => $per_page - )); + $resolver = $this->createOptionsResolver(); + $resolver->setDefined('iids') + ->setAllowedTypes('iids', 'array') + ->setAllowedValues('iids', function (array $value) { + return count($value) == count(array_filter($value, 'is_int')); + }) + ; + $resolver->setDefined('state') + ->setAllowedValues('state', ['active', 'closed']) + ; + $resolver->setDefined('search'); + + return $this->get($this->getProjectPath($project_id, 'milestones'), $resolver->resolve($parameters)); } /** diff --git a/vendor/m4tthumphrey/php-gitlab-api/lib/Gitlab/Api/ProjectNamespaces.php b/vendor/m4tthumphrey/php-gitlab-api/lib/Gitlab/Api/ProjectNamespaces.php index 480fa79c880..815d4e083ad 100644 --- a/vendor/m4tthumphrey/php-gitlab-api/lib/Gitlab/Api/ProjectNamespaces.php +++ b/vendor/m4tthumphrey/php-gitlab-api/lib/Gitlab/Api/ProjectNamespaces.php @@ -3,30 +3,18 @@ class ProjectNamespaces extends AbstractApi { /** - * @param int $page - * @param int $per_page + * @param array $parameters ( + * + * @var string $search Returns a list of namespaces the user is authorized to see based on the search criteria. + * ) + * * @return mixed */ - public function all($page = 1, $per_page = self::PER_PAGE) + public function all(array $parameters = []) { - return $this->get('namespaces', array( - 'page' => $page, - 'per_page' => $per_page - )); - } + $resolver = $this->createOptionsResolver(); + $resolver->setDefined('search'); - /** - * @param string $terms - * @param int $page - * @param int $per_page - * @return mixed - */ - public function search($terms, $page = 1, $per_page = self::PER_PAGE) - { - return $this->get('namespaces', array( - 'search' => $terms, - 'page' => $page, - 'per_page' => $per_page - )); + return $this->get('namespaces', $resolver->resolve($parameters)); } } diff --git a/vendor/m4tthumphrey/php-gitlab-api/lib/Gitlab/Api/Projects.php b/vendor/m4tthumphrey/php-gitlab-api/lib/Gitlab/Api/Projects.php index 65781babcb4..f5f67df160a 100644 --- a/vendor/m4tthumphrey/php-gitlab-api/lib/Gitlab/Api/Projects.php +++ b/vendor/m4tthumphrey/php-gitlab-api/lib/Gitlab/Api/Projects.php @@ -1,87 +1,112 @@ <?php namespace Gitlab\Api; +use Symfony\Component\OptionsResolver\Options; +use Symfony\Component\OptionsResolver\Exception\InvalidOptionsException; +use Symfony\Component\OptionsResolver\Exception\UndefinedOptionsException; +use Symfony\Component\OptionsResolver\OptionsResolver; + class Projects extends AbstractApi { - const ORDER_BY = 'created_at'; - const SORT = 'asc'; - - /** - * @param int $page - * @param int $per_page - * @param string $order_by - * @param string $sort - * @return mixed - */ - public function all($page = 1, $per_page = self::PER_PAGE, $order_by = self::ORDER_BY, $sort = self::SORT) - { - return $this->get('projects/all', array( - 'page' => $page, - 'per_page' => $per_page, - 'order_by' => $order_by, - 'sort' => $sort - )); - } - - /** - * @param int $page - * @param int $per_page - * @param string $order_by - * @param string $sort - * @return mixed - */ - public function accessible($page = 1, $per_page = self::PER_PAGE, $order_by = self::ORDER_BY, $sort = self::SORT) - { - return $this->get('projects', array( - 'page' => $page, - 'per_page' => $per_page, - 'order_by' => $order_by, - 'sort' => $sort - )); - } - - /** - * Get projects owned by the current user - * @param int $page - * @param int $per_page - * @param string $order_by - * @param string $sort - * @return mixed - */ - public function owned($page = 1, $per_page = self::PER_PAGE, $order_by = self::ORDER_BY, $sort = self::SORT) - { - return $this->get('projects?owned=1', array( - 'page' => $page, - 'per_page' => $per_page, - 'order_by' => $order_by, - 'sort' => $sort - )); - } - - /** - * @param string $query - * @param int $page - * @param int $per_page - * @param string $order_by - * @param string $sort - * @return mixed - */ - public function search($query, $page = 1, $per_page = self::PER_PAGE, $order_by = self::ORDER_BY, $sort = self::SORT) - { - return $this->get('projects/search/'.$this->encodePath($query), array( - 'page' => $page, - 'per_page' => $per_page, - 'order_by' => $order_by, - 'sort' => $sort - )); - } - /** - * @param int $project_id - * @return mixed - */ - public function show($project_id) - { - return $this->get('projects/'.$this->encodePath($project_id)); + * @param array $parameters { + * + * @var bool $archived Limit by archived status. + * @var string $visibility Limit by visibility public, internal, or private. + * @var string $order_by Return projects ordered by id, name, path, created_at, updated_at, + * or last_activity_at fields. Default is created_at. + * @var string $sort Return projects sorted in asc or desc order. Default is desc. + * @var string $search Return list of projects matching the search criteria. + * @var bool $simple Return only the ID, URL, name, and path of each project. + * @var bool $owned Limit by projects owned by the current user. + * @var bool $membership Limit by projects that the current user is a member of. + * @var bool $starred Limit by projects starred by the current user. + * @var bool $statistics Include project statistics. + * @var bool $with_issues_enabled Limit by enabled issues feature. + * @var bool $with_merge_requests_enabled Limit by enabled merge requests feature. + * } + * + * @throws UndefinedOptionsException If an option name is undefined + * @throws InvalidOptionsException If an option doesn't fulfill the + * specified validation rules + * + * @return mixed + */ + public function all(array $parameters = []) + { + $resolver = $this->createOptionsResolver(); + $booleanNormalizer = function (Options $resolver, $value) { + return $value ? 'true' : 'false'; + }; + $resolver->setDefined('archived') + ->setAllowedTypes('archived', 'bool') + ->setNormalizer('archived', $booleanNormalizer) + ; + $resolver->setDefined('visibility') + ->setAllowedValues('visibility', ['public', 'internal', 'private']) + ; + $resolver->setDefined('order_by') + ->setAllowedValues('order_by', ['id', 'name', 'path', 'created_at', 'updated_at', 'last_activity_at']) + ; + $resolver->setDefined('sort') + ->setAllowedValues('sort', ['asc', 'desc']) + ; + $resolver->setDefined('search'); + $resolver->setDefined('simple') + ->setAllowedTypes('simple', 'bool') + ->setNormalizer('simple', $booleanNormalizer) + ; + $resolver->setDefined('owned') + ->setAllowedTypes('owned', 'bool') + ->setNormalizer('owned', $booleanNormalizer) + ; + $resolver->setDefined('membership') + ->setAllowedTypes('membership', 'bool') + ->setNormalizer('membership', $booleanNormalizer) + ; + $resolver->setDefined('starred') + ->setAllowedTypes('starred', 'bool') + ->setNormalizer('starred', $booleanNormalizer) + ; + $resolver->setDefined('statistics') + ->setAllowedTypes('statistics', 'bool') + ->setNormalizer('statistics', $booleanNormalizer) + ; + $resolver->setDefined('with_issues_enabled') + ->setAllowedTypes('with_issues_enabled', 'bool') + ->setNormalizer('with_issues_enabled', $booleanNormalizer) + ; + $resolver->setDefined('with_merge_requests_enabled') + ->setAllowedTypes('with_merge_requests_enabled', 'bool') + ->setNormalizer('with_merge_requests_enabled', $booleanNormalizer) + ; + + return $this->get('projects', $resolver->resolve($parameters)); + } + + /** + * @param int $project_id + * @param array $parameters { + * + * @var bool $statistics Include project statistics. + * @var bool $with_custom_attributes Include project custom attributes. + * } + * @return mixed + */ + public function show($project_id, array $parameters = []) + { + $resolver = $this->createOptionsResolver(); + $booleanNormalizer = function (Options $resolver, $value) { + return $value ? true : false; + }; + $resolver->setDefined('statistics') + ->setAllowedTypes('statistics', 'bool') + ->setNormalizer('statistics', $booleanNormalizer) + ; + $resolver->setDefined('with_custom_attributes') + ->setAllowedTypes('with_custom_attributes', 'bool') + ->setNormalizer('with_custom_attributes', $booleanNormalizer) + ; + return $this->get('projects/'.$this->encodePath($project_id), $resolver->resolve($parameters)); } /** @@ -132,7 +157,8 @@ class Projects extends AbstractApi * @param int $project_id * @return mixed */ - public function archive($project_id){ + public function archive($project_id) + { return $this->post("projects/".$this->encodePath($project_id)."/archive"); } @@ -140,49 +166,54 @@ class Projects extends AbstractApi * @param int $project_id * @return mixed */ - public function unarchive($project_id){ + public function unarchive($project_id) + { return $this->post("projects/".$this->encodePath($project_id)."/unarchive"); } /** - * @param int $project_id - * @param array $scope + * @param int $project_id + * @param array $parameters ( + * + * @var string $scope The scope of pipelines, one of: running, pending, finished, branches, tags. + * @var string $status The status of pipelines, one of: running, pending, success, failed, canceled, skipped. + * @var string $ref The ref of pipelines. + * @var bool $yaml_errors Returns pipelines with invalid configurations. + * @var string $name The name of the user who triggered pipelines. + * @var string $username The username of the user who triggered pipelines. + * @var string $order_by Order pipelines by id, status, ref, or user_id (default: id). + * @var string $order Sort pipelines in asc or desc order (default: desc). + * ) * @return mixed */ - public function builds($project_id, $scope = null) + public function pipelines($project_id, array $parameters = []) { - return $this->get($this->getProjectPath($project_id, 'builds'), array( - 'scope' => $scope - )); - } + $resolver = $this->createOptionsResolver(); + $booleanNormalizer = function (Options $resolver, $value) { + return $value ? 'true' : 'false'; + }; - /** - * @param int $project_id - * @param int $build_id - * @return mixed - */ - public function build($project_id, $build_id) - { - return $this->get($this->getProjectPath($project_id, 'builds/'.$this->encodePath($build_id))); - } + $resolver->setDefined('scope') + ->setAllowedValues('scope', ['running', 'pending', 'finished', 'branches', 'tags']) + ; + $resolver->setDefined('status') + ->setAllowedValues('status', ['running', 'pending', 'success', 'failed', 'canceled', 'skipped']) + ; + $resolver->setDefined('ref'); + $resolver->setDefined('yaml_errors') + ->setAllowedTypes('yaml_errors', 'bool') + ->setNormalizer('yaml_errors', $booleanNormalizer) + ; + $resolver->setDefined('name'); + $resolver->setDefined('username'); + $resolver->setDefined('order_by') + ->setAllowedValues('order_by', ['id', 'status', 'ref', 'user_id']) + ; + $resolver->setDefined('sort') + ->setAllowedValues('sort', ['asc', 'desc']) + ; - /** - * @param int $project_id - * @param int $build_id - * @return mixed - */ - public function trace($project_id, $build_id) - { - return $this->get($this->getProjectPath($project_id, 'builds/'.$this->encodePath($build_id).'/trace')); - } - - /** - * @param int $project_id - * @return mixed - */ - public function pipelines($project_id) - { - return $this->get($this->getProjectPath($project_id, 'pipelines')); + return $this->get($this->getProjectPath($project_id, 'pipelines'), $resolver->resolve($parameters)); } /** @@ -202,7 +233,7 @@ class Projects extends AbstractApi */ public function createPipeline($project_id, $commit_ref) { - return $this->post($this->getProjectPath($project_id, 'pipelines'), array( + return $this->post($this->getProjectPath($project_id, 'pipeline'), array( 'ref' => $commit_ref)); } @@ -228,14 +259,33 @@ class Projects extends AbstractApi /** * @param int $project_id - * @param string $username_query + * @param array $parameters ( + * + * @var string $query The query you want to search members for. + * ) + * + * @throws MissingOptionsException If a required option is not provided + * * @return mixed */ - public function members($project_id, $username_query = null) + public function members($project_id, $parameters = []) { - return $this->get($this->getProjectPath($project_id, 'members'), array( - 'query' => $username_query - )); + if (!is_array($parameters)) { + @trigger_error("Deprecated: String parameter of the members() function is deprecated.", E_USER_NOTICE); + $username_query = $parameters; + $parameters = array(); + if (!empty($username_query)) { + $parameters['query'] = $username_query; + } + } + + $resolver = $this->createOptionsResolver(); + + $resolver->setDefined('query') + ->setAllowedTypes('query', 'string') + ; + + return $this->get($this->getProjectPath($project_id, 'members'), $resolver->resolve($parameters)); } /** @@ -287,25 +337,15 @@ class Projects extends AbstractApi /** * @param int $project_id + * @param array $parameters + * * @return mixed */ - public function deployKeys($project_id) + public function hooks($project_id, array $parameters = []) { - return $this->get($this->getProjectPath($project_id, 'deploy_keys')); - } + $resolver = $this->createOptionsResolver(); - /** - * @param int $project_id - * @param int $page - * @param int $per_page - * @return mixed - */ - public function hooks($project_id, $page = 1, $per_page = self::PER_PAGE) - { - return $this->get($this->getProjectPath($project_id, 'hooks'), array( - 'page' => $page, - 'per_page' => $per_page - )); + return $this->get($this->getProjectPath($project_id, 'hooks'), $resolver->resolve($parameters)); } /** @@ -360,9 +400,9 @@ class Projects extends AbstractApi * @param int $project_id * @return mixed */ - public function keys($project_id) + public function deployKeys($project_id) { - return $this->get($this->getProjectPath($project_id, 'keys')); + return $this->get($this->getProjectPath($project_id, 'deploy_keys')); } /** @@ -370,22 +410,24 @@ class Projects extends AbstractApi * @param int $key_id * @return mixed */ - public function key($project_id, $key_id) + public function deployKey($project_id, $key_id) { - return $this->get($this->getProjectPath($project_id, 'keys/'.$this->encodePath($key_id))); + return $this->get($this->getProjectPath($project_id, 'deploy_keys/'.$this->encodePath($key_id))); } /** * @param int $project_id * @param string $title * @param string $key + * @param bool $canPush * @return mixed */ - public function addKey($project_id, $title, $key) + public function addDeployKey($project_id, $title, $key, $canPush = false) { - return $this->post($this->getProjectPath($project_id, 'keys'), array( + return $this->post($this->getProjectPath($project_id, 'deploy_keys'), array( 'title' => $title, - 'key' => $key + 'key' => $key, + 'can_push' => $canPush )); } @@ -394,9 +436,9 @@ class Projects extends AbstractApi * @param int $key_id * @return mixed */ - public function removeKey($project_id, $key_id) + public function deleteDeployKey($project_id, $key_id) { - return $this->delete($this->getProjectPath($project_id, 'keys/'.$this->encodePath($key_id))); + return $this->delete($this->getProjectPath($project_id, 'deploy_keys/'.$this->encodePath($key_id))); } /** @@ -404,33 +446,49 @@ class Projects extends AbstractApi * @param int $key_id * @return mixed */ - public function enableKey($project_id, $key_id) + public function enableDeployKey($project_id, $key_id) { - return $this->post($this->getProjectPath($project_id, 'keys/'.$this->encodePath($key_id).'/enable')); + return $this->post($this->getProjectPath($project_id, 'deploy_keys/'.$this->encodePath($key_id).'/enable')); } /** * @param int $project_id - * @param int $key_id + * @param array $parameters ( + * + * @var string $action Include only events of a particular action type. + * @var string $target_type Include only events of a particular target type. + * @var \DateTimeInterface $before Include only events created before a particular date. + * @var \DateTimeInterface $after Include only events created after a particular date. + * @var string $sort Sort events in asc or desc order by created_at. Default is desc. + * ) + * * @return mixed */ - public function disableKey($project_id, $key_id) + public function events($project_id, array $parameters = []) { - return $this->delete($this->getProjectPath($project_id, 'keys/'.$this->encodePath($key_id).'/disable')); - } + $resolver = $this->createOptionsResolver(); + $datetimeNormalizer = function (Options $resolver, \DateTimeInterface $value) { + return $value->format('Y-m-d'); + }; - /** - * @param int $project_id - * @param int $page - * @param int $per_page - * @return mixed - */ - public function events($project_id, $page = 1, $per_page = self::PER_PAGE) - { - return $this->get($this->getProjectPath($project_id, 'events'), array( - 'page' => $page, - 'per_page' => $per_page - )); + $resolver->setDefined('action') + ->setAllowedValues('action', ['created', 'updated', 'closed', 'reopened', 'pushed', 'commented', 'merged', 'joined', 'left', 'destroyed', 'expired']) + ; + $resolver->setDefined('target_type') + ->setAllowedValues('target_type', ['issue', 'milestone', 'merge_request', 'note', 'project', 'snippet', 'user']) + ; + $resolver->setDefined('before') + ->setAllowedTypes('before', \DateTimeInterface::class) + ->setNormalizer('before', $datetimeNormalizer); + $resolver->setDefined('after') + ->setAllowedTypes('after', \DateTimeInterface::class) + ->setNormalizer('after', $datetimeNormalizer) + ; + $resolver->setDefined('sort') + ->setAllowedValues('sort', ['asc', 'desc']) + ; + + return $this->get($this->getProjectPath($project_id, 'events'), $resolver->resolve($parameters)); } /** @@ -475,12 +533,32 @@ class Projects extends AbstractApi } /** + * Get languages used in a project with percentage value. + * * @param int $project_id * @return mixed */ - public function fork($project_id) + public function languages($project_id) { - return $this->post('projects/fork/'.$this->encodePath($project_id)); + return $this->get($this->getProjectPath($project_id, 'languages')); + } + + /** + * @param int $project_id + * @param array $params ( + * + * @var string $namespace The ID or path of the namespace that the project will be forked to + * ) + * @return mixed + */ + public function fork($project_id, array $parameters = []) + { + $resolver = new OptionsResolver(); + $resolver->setDefined('namespace'); + + $resolved = $resolver->resolve($parameters); + + return $this->post($this->getProjectPath($project_id, 'fork'), $resolved); } /** @@ -525,11 +603,15 @@ class Projects extends AbstractApi /** * @param int $project_id + * @param array $parameters + * * @return mixed */ - public function variables($project_id) + public function variables($project_id, array $parameters = []) { - return $this->get($this->getProjectPath($project_id, 'variables')); + $resolver = $this->createOptionsResolver(); + + return $this->get($this->getProjectPath($project_id, 'variables'), $resolver->resolve($parameters)); } /** @@ -546,27 +628,51 @@ class Projects extends AbstractApi * @param int $project_id * @param string $key * @param string $value + * @param bool $protected + * @param string $environment_scope * @return mixed */ - public function addVariable($project_id, $key, $value) + public function addVariable($project_id, $key, $value, $protected = null, $environment_scope = null) { - return $this->post($this->getProjectPath($project_id, 'variables'), array( + $payload = array( 'key' => $key, - 'value' => $value - )); + 'value' => $value, + ); + + if ($protected) { + $payload['protected'] = $protected; + } + + if ($environment_scope) { + $payload['environment_scope'] = $environment_scope; + } + + return $this->post($this->getProjectPath($project_id, 'variables'), $payload); } /** * @param int $project_id * @param string $key * @param string $value + * @param bool $protected + * @param string $environment_scope * @return mixed */ - public function updateVariable($project_id, $key, $value) + public function updateVariable($project_id, $key, $value, $protected = null, $environment_scope = null) { - return $this->put($this->getProjectPath($project_id, 'variables/'.$this->encodePath($key)), array( + $payload = array( 'value' => $value, - )); + ); + + if ($protected) { + $payload['protected'] = $protected; + } + + if ($environment_scope) { + $payload['environment_scope'] = $environment_scope; + } + + return $this->put($this->getProjectPath($project_id, 'variables/'.$this->encodePath($key)), $payload); } /** @@ -591,16 +697,15 @@ class Projects extends AbstractApi /** * @param int $project_id - * @param int $page - * @param int $per_page + * @param array $parameters + * * @return mixed */ - public function deployments($project_id, $page = 1, $per_page = self::PER_PAGE) + public function deployments($project_id, array $parameters = []) { - return $this->get($this->getProjectPath($project_id, 'deployments'), array( - 'page' => $page, - 'per_page' => $per_page - )); + $resolver = $this->createOptionsResolver(); + + return $this->get($this->getProjectPath($project_id, 'deployments'), $resolver->resolve($parameters)); } /** diff --git a/vendor/m4tthumphrey/php-gitlab-api/lib/Gitlab/Api/Repositories.php b/vendor/m4tthumphrey/php-gitlab-api/lib/Gitlab/Api/Repositories.php index bb6e842e858..14313b66e7e 100644 --- a/vendor/m4tthumphrey/php-gitlab-api/lib/Gitlab/Api/Repositories.php +++ b/vendor/m4tthumphrey/php-gitlab-api/lib/Gitlab/Api/Repositories.php @@ -1,14 +1,19 @@ <?php namespace Gitlab\Api; +use Symfony\Component\OptionsResolver\Options; +use Symfony\Component\OptionsResolver\OptionsResolver; + class Repositories extends AbstractApi { /** * @param int $project_id + * @param array $parameters * @return mixed */ - public function branches($project_id) + public function branches($project_id, array $parameters = []) { - return $this->get($this->getProjectPath($project_id, 'repository/branches')); + $resolver = $this->createOptionsResolver(); + return $this->get($this->getProjectPath($project_id, 'repository/branches'), $resolver->resolve($parameters)); } /** @@ -18,31 +23,31 @@ class Repositories extends AbstractApi */ public function branch($project_id, $branch_id) { - return $this->get($this->getProjectPath($project_id, 'repository/branches/'.$this->encodeBranch($branch_id))); + return $this->get($this->getProjectPath($project_id, 'repository/branches/'.$this->encodePath($branch_id))); } /** * @param int $project_id - * @param string $branch_name + * @param string $branch * @param string $ref * @return mixed */ - public function createBranch($project_id, $branch_name, $ref) + public function createBranch($project_id, $branch, $ref) { return $this->post($this->getProjectPath($project_id, 'repository/branches'), array( - 'branch_name' => $branch_name, + 'branch' => $branch, 'ref' => $ref )); } /** * @param int $project_id - * @param string $branch_name + * @param string $branch * @return mixed */ - public function deleteBranch($project_id, $branch_name) + public function deleteBranch($project_id, $branch) { - return $this->delete($this->getProjectPath($project_id, 'repository/branches/'.$this->encodeBranch($branch_name))); + return $this->delete($this->getProjectPath($project_id, 'repository/branches/'.$this->encodePath($branch))); } /** @@ -54,7 +59,7 @@ class Repositories extends AbstractApi */ public function protectBranch($project_id, $branch_name, $devPush = false, $devMerge = false) { - return $this->put($this->getProjectPath($project_id, 'repository/branches/'.$this->encodeBranch($branch_name).'/protect'), array( + return $this->put($this->getProjectPath($project_id, 'repository/branches/'.$this->encodePath($branch_name).'/protect'), array( 'developers_can_push' => $devPush, 'developers_can_merge' => $devMerge )); @@ -67,16 +72,19 @@ class Repositories extends AbstractApi */ public function unprotectBranch($project_id, $branch_name) { - return $this->put($this->getProjectPath($project_id, 'repository/branches/'.$this->encodeBranch($branch_name).'/unprotect')); + return $this->put($this->getProjectPath($project_id, 'repository/branches/'.$this->encodePath($branch_name).'/unprotect')); } /** * @param int $project_id + * @param array $parameters * @return mixed */ - public function tags($project_id) + public function tags($project_id, array $parameters = []) { - return $this->get($this->getProjectPath($project_id, 'repository/tags')); + $resolver = $this->createOptionsResolver(); + + return $this->get($this->getProjectPath($project_id, 'repository/tags'), $resolver->resolve($parameters)); } /** @@ -102,12 +110,13 @@ class Repositories extends AbstractApi * * @return mixed */ - public function createRelease( $project_id, $tag_name, $description ) { - return $this->post( $this->getProjectPath( $project_id, 'repository/tags/' . $this->encodeBranch( $tag_name ) . '/release' ), array( + public function createRelease($project_id, $tag_name, $description) + { + return $this->post($this->getProjectPath($project_id, 'repository/tags/' . $this->encodePath($tag_name) . '/release'), array( 'id' => $project_id, 'tag_name' => $tag_name, 'description' => $description - ) ); + )); } /** @@ -117,71 +126,134 @@ class Repositories extends AbstractApi * * @return mixed */ - public function updateRelease( $project_id, $tag_name, $description ) { - return $this->put( $this->getProjectPath( $project_id, 'repository/tags/' . $this->encodeBranch( $tag_name ) . '/release' ), array( + public function updateRelease($project_id, $tag_name, $description) + { + return $this->put($this->getProjectPath($project_id, 'repository/tags/' . $this->encodePath($tag_name) . '/release'), array( 'id' => $project_id, 'tag_name' => $tag_name, 'description' => $description - ) ); + )); } /** * @param int $project_id - * @param string $sha - * @param string $scope - * @param int $page - * @param int $per_page + * @param array $parameters ( + * + * @var string $ref_name The name of a repository branch or tag or if not given the default branch. + * @var \DateTimeInterface $since Only commits after or on this date will be returned. + * @var \DateTimeInterface $until Only commits before or on this date will be returned. + * ) * * @return mixed */ - public function commitBuilds($project_id, $sha, $scope = null, $page = 0, $per_page = self::PER_PAGE) + public function commits($project_id, array $parameters = []) { - return $this->get($this->getProjectPath($project_id, 'repository/commits/'.$this->encodePath($sha).'/builds'), array( - 'page' => $page, - 'per_page' => $per_page, - 'scope' => $scope - )); + $resolver = $this->createOptionsResolver(); + $datetimeNormalizer = function (Options $options, \DateTimeInterface $value) { + return $value->format('c'); + }; + + $resolver->setDefined('ref_name'); + $resolver->setDefined('since') + ->setAllowedTypes('since', \DateTimeInterface::class) + ->setNormalizer('since', $datetimeNormalizer) + ; + $resolver->setDefined('until') + ->setAllowedTypes('until', \DateTimeInterface::class) + ->setNormalizer('until', $datetimeNormalizer) + ; + + return $this->get($this->getProjectPath($project_id, 'repository/commits'), $resolver->resolve($parameters)); } /** * @param int $project_id - * @param int $page - * @param int $per_page - * @param null $ref_name + * @param $sha * @return mixed */ - public function commits($project_id, $page = 0, $per_page = self::PER_PAGE, $ref_name = null) + public function commit($project_id, $sha) { - return $this->get($this->getProjectPath($project_id, 'repository/commits'), array( - 'page' => $page, - 'per_page' => $per_page, - 'ref_name' => $ref_name - )); + return $this->get($this->getProjectPath($project_id, 'repository/commits/'.$this->encodePath($sha))); } /** * @param int $project_id - * @param $sha + * @param array $parameters ( + * + * @var string $branch Name of the branch to commit into. To create a new branch, also provide start_branch. + * @var string $commit_message Commit message. + * @var string $start_branch Name of the branch to start the new commit from. + * @var array $actions ( + * + * @var string $action he action to perform, create, delete, move, update. + * @var string $file_path Full path to the file. + * @var string $previous_path Original full path to the file being moved. + * @var string $content File content, required for all except delete. Optional for move. + * @var string $encoding text or base64. text is default. + * ) + * @var string $author_email Specify the commit author's email address. + * @var string $author_name Specify the commit author's name. + * ) + * * @return mixed */ - public function commit($project_id, $sha) + public function createCommit($project_id, array $parameters = []) { - return $this->get($this->getProjectPath($project_id, 'repository/commits/'.$this->encodePath($sha))); + $resolver = new OptionsResolver(); + $resolver->setDefined('branch') + ->setRequired('branch') + ; + $resolver->setDefined('commit_message') + ->setRequired('commit_message') + ; + $resolver->setDefined('start_branch'); + $resolver->setDefined('actions') + ->setRequired('actions') + ->setAllowedTypes('actions', 'array') + ->setAllowedValues('actions', function (array $actions) { + return !empty($actions); + }) + ->setNormalizer('actions', function (Options $resolver, array $actions) { + $actionsOptionsResolver = new OptionsResolver(); + $actionsOptionsResolver->setDefined('action') + ->setRequired('action') + ->setAllowedValues('action', ['create', 'delete', 'move', 'update']) + ; + $actionsOptionsResolver->setDefined('file_path') + ->setRequired('file_path') + ; + $actionsOptionsResolver->setDefined('previous_path'); + $actionsOptionsResolver->setDefined('content'); + $actionsOptionsResolver->setDefined('encoding') + ->setAllowedValues('encoding', ['test', 'base64']) + ; + + return array_map(function ($action) use ($actionsOptionsResolver) { + return $actionsOptionsResolver->resolve($action); + }, $actions); + }) + ; + $resolver->setDefined('author_email'); + $resolver->setDefined('author_name'); + + return $this->post($this->getProjectPath($project_id, 'repository/commits'), $resolver->resolve($parameters)); } /** - * @param int $project_id + * @param int $project_id * @param string $sha - * @param int $page - * @param int $per_page + * @param array $parameters + * * @return mixed */ - public function commitComments($project_id, $sha, $page = 0, $per_page = self::PER_PAGE) + public function commitComments($project_id, $sha, array $parameters = []) { - return $this->get($this->getProjectPath($project_id, 'repository/commits/'.$this->encodePath($sha).'/comments'), array( - 'page' => $page, - 'per_page' => $per_page - )); + $resolver = $this->createOptionsResolver(); + + return $this->get( + $this->getProjectPath($project_id, 'repository/commits/'.$this->encodePath($sha).'/comments'), + $resolver->resolve($parameters) + ); } /** @@ -198,6 +270,31 @@ class Repositories extends AbstractApi return $this->post($this->getProjectPath($project_id, 'repository/commits/'.$this->encodePath($sha).'/comments'), $params); } + /** + * @param int $project_id + * @param string $sha + * @param array $params + * @return mixed + */ + public function getCommitBuildStatus($project_id, $sha, array $params = array()) + { + return $this->get($this->getProjectPath($project_id, 'repository/commits/'.$this->encodePath($sha).'/statuses'), $params); + } + + /** + * @param int $project_id + * @param string $sha + * @param string $state + * @param array $params + * @return mixed + */ + public function postCommitBuildStatus($project_id, $sha, $state, array $params = array()) + { + $params['state'] = $state; + + return $this->post($this->getProjectPath($project_id, 'statuses/'.$this->encodePath($sha)), $params); + } + /** * @param int $project_id * @param string $fromShaOrMaster @@ -208,7 +305,7 @@ class Repositories extends AbstractApi { return $this->get($this->getProjectPath( $project_id, - 'repository/compare?from='.$this->encodeBranch($fromShaOrMaster).'&to='.$this->encodeBranch($toShaOrMaster) + 'repository/compare?from='.$this->encodePath($fromShaOrMaster).'&to='.$this->encodePath($toShaOrMaster) )); } @@ -240,9 +337,9 @@ class Repositories extends AbstractApi */ public function blob($project_id, $sha, $filepath) { - return $this->get($this->getProjectPath($project_id, 'repository/commits/'.$this->encodePath($sha).'/blob'), array( - 'filepath' => $filepath - )); + @trigger_error(sprintf('The %s() method is deprecated since version 9.2 and will be removed in 10.0. Use the %s::getRawFile() method instead.', __METHOD__, RepositoryFiles::class), E_USER_DEPRECATED); + + return $this->client->repositoryFiles()->getRawFile($project_id, $filepath, $sha); } /** @@ -253,78 +350,83 @@ class Repositories extends AbstractApi */ public function getFile($project_id, $file_path, $ref) { - return $this->get($this->getProjectPath($project_id, 'repository/files'), array( - 'file_path' => $file_path, - 'ref' => $ref - )); + @trigger_error(sprintf('The %s() method is deprecated since version 9.2 and will be removed in 10.0. Use the %s::getFile() method instead.', __METHOD__, RepositoryFiles::class), E_USER_DEPRECATED); + + return $this->client->repositoryFiles()->getFile($project_id, $file_path, $ref); } /** * @param int $project_id * @param string $file_path * @param string $content - * @param string $branch_name + * @param string $branch * @param string $commit_message * @param string $encoding * @param string $author_email * @param string $author_name * @return mixed */ - public function createFile($project_id, $file_path, $content, $branch_name, $commit_message, $encoding = null, $author_email = null, $author_name = null) + public function createFile($project_id, $file_path, $content, $branch, $commit_message, $encoding = null, $author_email = null, $author_name = null) { - return $this->post($this->getProjectPath($project_id, 'repository/files'), array( + @trigger_error(sprintf('The %s() method is deprecated since version 9.2 and will be removed in 10.0. Use the %s::createFile() method instead.', __METHOD__, RepositoryFiles::class), E_USER_DEPRECATED); + + return $this->client->repositoryFiles()->createFile($project_id, [ 'file_path' => $file_path, - 'branch_name' => $branch_name, + 'branch' => $branch, 'content' => $content, 'commit_message' => $commit_message, 'encoding' => $encoding, 'author_email' => $author_email, 'author_name' => $author_name, - )); + ]); } /** * @param int $project_id * @param string $file_path * @param string $content - * @param string $branch_name + * @param string $branch * @param string $commit_message * @param string $encoding * @param string $author_email * @param string $author_name * @return mixed */ - public function updateFile($project_id, $file_path, $content, $branch_name, $commit_message, $encoding = null, $author_email = null, $author_name = null) + public function updateFile($project_id, $file_path, $content, $branch, $commit_message, $encoding = null, $author_email = null, $author_name = null) { - return $this->put($this->getProjectPath($project_id, 'repository/files'), array( + @trigger_error(sprintf('The %s() method is deprecated since version 9.2 and will be removed in 10.0. Use the %s::updateFile() method instead.', __METHOD__, RepositoryFiles::class), E_USER_DEPRECATED); + + return $this->client->repositoryFiles()->updateFile($project_id, [ 'file_path' => $file_path, - 'branch_name' => $branch_name, + 'branch' => $branch, 'content' => $content, 'commit_message' => $commit_message, 'encoding' => $encoding, 'author_email' => $author_email, 'author_name' => $author_name, - )); + ]); } /** * @param int $project_id * @param string $file_path - * @param string $branch_name + * @param string $branch * @param string $commit_message * @param string $author_email * @param string $author_name * @return mixed */ - public function deleteFile($project_id, $file_path, $branch_name, $commit_message, $author_email = null, $author_name = null) + public function deleteFile($project_id, $file_path, $branch, $commit_message, $author_email = null, $author_name = null) { - return $this->delete($this->getProjectPath($project_id, 'repository/files'), array( + @trigger_error(sprintf('The %s() method is deprecated since version 9.2 and will be removed in 10.0. Use the %s::deleteFile() method instead.', __METHOD__, RepositoryFiles::class), E_USER_DEPRECATED); + + return $this->client->repositoryFiles()->deleteFile($project_id, [ 'file_path' => $file_path, - 'branch_name' => $branch_name, + 'branch' => $branch, 'commit_message' => $commit_message, 'author_email' => $author_email, 'author_name' => $author_name, - )); + ]); } /** @@ -346,15 +448,4 @@ class Repositories extends AbstractApi { return $this->get($this->getProjectPath($project_id, 'repository/archive.'.$format), $params); } - - /** - * @param string $path - * @return string - */ - protected function encodeBranch($path) - { - $path = $this->encodePath($path); - - return str_replace('%2F', '/', $path); - } } diff --git a/vendor/m4tthumphrey/php-gitlab-api/lib/Gitlab/Api/RepositoryFiles.php b/vendor/m4tthumphrey/php-gitlab-api/lib/Gitlab/Api/RepositoryFiles.php new file mode 100644 index 00000000000..bc20c00f0d8 --- /dev/null +++ b/vendor/m4tthumphrey/php-gitlab-api/lib/Gitlab/Api/RepositoryFiles.php @@ -0,0 +1,135 @@ +<?php + +namespace Gitlab\Api; + +use Symfony\Component\OptionsResolver\OptionsResolver; + +class RepositoryFiles extends AbstractApi +{ + /** + * @param int $project_id + * @param string $file_path + * @param string $ref + * @return mixed + */ + public function getFile($project_id, $file_path, $ref) + { + return $this->get($this->getProjectPath($project_id, 'repository/files/'.$this->encodePath($file_path)), array( + 'ref' => $ref + )); + } + + /** + * @param int $project_id + * @param string $file_path + * @param string $ref + * @return mixed + */ + public function getRawFile($project_id, $file_path, $ref) + { + return $this->get($this->getProjectPath($project_id, 'repository/files/'.$this->encodePath($file_path).'/raw'), array( + 'ref' => $ref, + )); + } + + /** + * @param int $project_id + * @param array $parameters ( + * + * @var string $file_path Url encoded full path to new file. Ex. lib%2Fclass%2Erb. + * @var string $branch Name of the branch. + * @var string $start_branch Name of the branch to start the new commit from. + * @var string $encoding Change encoding to 'base64'. Default is text. + * @var string $author_email Specify the commit author's email address. + * @var string $author_name Specify the commit author's name. + * @var string $content File content. + * @var string $commit_message Commit message. + * ) + * + * @return mixed + */ + public function createFile($project_id, array $parameters = []) + { + $resolver = new OptionsResolver(); + $resolver->setRequired('file_path'); + $resolver->setRequired('branch'); + $resolver->setDefined('start_branch'); + $resolver->setDefined('encoding') + ->setAllowedValues('encoding', ['text', 'base64']) + ; + $resolver->setDefined('author_email'); + $resolver->setDefined('author_name'); + $resolver->setRequired('content'); + $resolver->setRequired('commit_message'); + + $resolved = $resolver->resolve($parameters); + + return $this->post($this->getProjectPath($project_id, 'repository/files/'.$this->encodePath($resolved['file_path'])), $resolved); + } + + /** + * @param int $project_id + * @param array $parameters ( + * + * @var string $file_path Url encoded full path to new file. Ex. lib%2Fclass%2Erb. + * @var string $branch Name of the branch. + * @var string $start_branch Name of the branch to start the new commit from. + * @var string $encoding Change encoding to 'base64'. Default is text. + * @var string $author_email Specify the commit author's email address. + * @var string $author_name Specify the commit author's name. + * @var string $content File content. + * @var string $commit_message Commit message. + * @var string $last_commit_id Last known file commit id. + * ) + * + * @return mixed + */ + public function updateFile($project_id, array $parameters = []) + { + $resolver = new OptionsResolver(); + $resolver->setRequired('file_path'); + $resolver->setRequired('branch'); + $resolver->setDefined('start_branch'); + $resolver->setDefined('encoding') + ->setAllowedValues('encoding', ['text', 'base64']) + ; + $resolver->setDefined('author_email'); + $resolver->setDefined('author_name'); + $resolver->setRequired('content'); + $resolver->setRequired('commit_message'); + $resolver->setDefined('last_commit_id'); + + $resolved = $resolver->resolve($parameters); + + return $this->put($this->getProjectPath($project_id, 'repository/files/'.$this->encodePath($resolved['file_path'])), $resolved); + } + + /** + * @param int $project_id + * @param array $parameters ( + * + * @var string $file_path Url encoded full path to new file. Ex. lib%2Fclass%2Erb. + * @var string $branch Name of the branch. + * @var string $start_branch Name of the branch to start the new commit from. + * @var string $author_email Specify the commit author's email address. + * @var string $author_name Specify the commit author's name. + * @var string $commit_message Commit message. + * ) + * + * @return mixed + */ + public function deleteFile($project_id, array $parameters = []) + { + $resolver = new OptionsResolver(); + $resolver->setRequired('file_path'); + $resolver->setRequired('branch'); + $resolver->setDefined('start_branch'); + $resolver->setDefined('author_email'); + $resolver->setDefined('author_name'); + $resolver->setRequired('commit_message'); + + $resolved = $resolver->resolve($parameters); + + return $this->delete($this->getProjectPath($project_id, 'repository/files/'.$this->encodePath($resolved['file_path'])), $resolved); + } +} diff --git a/vendor/m4tthumphrey/php-gitlab-api/lib/Gitlab/Api/Snippets.php b/vendor/m4tthumphrey/php-gitlab-api/lib/Gitlab/Api/Snippets.php index 00921128c89..e17c0cba754 100644 --- a/vendor/m4tthumphrey/php-gitlab-api/lib/Gitlab/Api/Snippets.php +++ b/vendor/m4tthumphrey/php-gitlab-api/lib/Gitlab/Api/Snippets.php @@ -67,4 +67,15 @@ class Snippets extends AbstractApi { return $this->delete($this->getProjectPath($project_id, 'snippets/'.$this->encodePath($snippet_id))); } + + /** + * @param int $project_id + * @param int $snippet_id + * + * @return mixed + */ + public function awardEmoji($project_id, $snippet_id) + { + return $this->get($this->getProjectPath($project_id, 'snippets/'.$this->encodePath($snippet_id).'/award_emoji')); + } } diff --git a/vendor/m4tthumphrey/php-gitlab-api/lib/Gitlab/Api/Users.php b/vendor/m4tthumphrey/php-gitlab-api/lib/Gitlab/Api/Users.php index a3e31ff5cfc..d4f49b1e086 100644 --- a/vendor/m4tthumphrey/php-gitlab-api/lib/Gitlab/Api/Users.php +++ b/vendor/m4tthumphrey/php-gitlab-api/lib/Gitlab/Api/Users.php @@ -1,57 +1,75 @@ <?php namespace Gitlab\Api; +use Symfony\Component\OptionsResolver\Options; + class Users extends AbstractApi { /** - * @param null|true $active - * @param int $page - * @param int $per_page + * @param array $parameters ( + * + * @var string $search Search for user by email or username. + * @var string $username Lookup for user by username. + * @var bool $external Search for external users only. + * @var string $extern_uid Lookup for users by external uid. + * @var string $provider Lookup for users by provider. + * @var \DateTimeInterface $created_before Return users created before the given time (inclusive). + * @var \DateTimeInterface $created_after Return users created after the given time (inclusive). + * @var bool $active Return only active users. It does not support filtering inactive users. + * @var bool $blocked Return only blocked users. It does not support filtering non-blocked users. + * ) + * * @return mixed */ - public function all($active = null, $page = 1, $per_page = self::PER_PAGE) + public function all(array $parameters = []) { - return $this->get('users', array( - 'active' => $active, - 'page' => $page, - 'per_page' => $per_page - )); - } + $resolver = $this->createOptionsResolver(); + $datetimeNormalizer = function (Options $resolver, \DateTimeInterface $value) { + return $value->format('c'); + }; - /** - * @param string $username - * @return mixed - */ - public function lookup($username) - { - return $this->get('users', array( - 'username' => $username - )); + $resolver->setDefined('search'); + $resolver->setDefined('username'); + $resolver->setDefined('external') + ->setAllowedTypes('external', 'bool') + ; + $resolver->setDefined('extern_uid'); + $resolver->setDefined('provider'); + $resolver->setDefined('created_before') + ->setAllowedTypes('created_before', \DateTimeInterface::class) + ->setNormalizer('created_before', $datetimeNormalizer) + ; + $resolver->setDefined('created_after') + ->setAllowedTypes('created_after', \DateTimeInterface::class) + ->setNormalizer('created_after', $datetimeNormalizer) + ; + $resolver->setDefined('active') + ->setAllowedTypes('active', 'bool') + ->setAllowedValues('active', true) + ; + $resolver->setDefined('blocked') + ->setAllowedTypes('blocked', 'bool') + ->setAllowedValues('blocked', true) + ; + + + return $this->get('users', $resolver->resolve($parameters)); } /** - * @param string $query - * @param null|true $active - * @param int $page - * @param int $per_page + * @param int $id * @return mixed */ - public function search($query, $active = null, $page = 1, $per_page = self::PER_PAGE) + public function show($id) { - return $this->get('users', array( - 'search' => $query, - 'active' => $active, - 'page' => $page, - 'per_page' => $per_page - )); + return $this->get('users/'.$this->encodePath($id)); } /** - * @param int $id * @return mixed */ - public function show($id) + public function user() { - return $this->get('users/'.$this->encodePath($id)); + return $this->get('user'); } /** @@ -93,7 +111,7 @@ class Users extends AbstractApi */ public function block($id) { - return $this->put('users/'.$this->encodePath($id).'/block'); + return $this->post('users/'.$this->encodePath($id).'/block'); } /** @@ -102,7 +120,7 @@ class Users extends AbstractApi */ public function unblock($id) { - return $this->put('users/'.$this->encodePath($id).'/unblock'); + return $this->post('users/'.$this->encodePath($id).'/unblock'); } /** @@ -235,4 +253,49 @@ class Users extends AbstractApi { return $this->get('user/emails/'.$this->encodePath($id)); } + + /** + * @param int $user_id + * @return mixed + */ + public function userImpersonationTokens($user_id) + { + return $this->get('users/'.$this->encodePath($user_id).'/impersonation_tokens'); + } + + /** + * @param int $user_id + * @param int $impersonation_token_id + * @return mixed + */ + public function userImpersonationToken($user_id, $impersonation_token_id) + { + return $this->get('users/'.$this->encodePath($user_id).'/impersonation_tokens/'.$this->encodePath($impersonation_token_id)); + } + + /** + * @param int $user_id + * @param string $name + * @param array $scopes + * @param null $expires_at + * @return mixed + */ + public function createImpersonationToken($user_id, $name, array $scopes, $expires_at = null) + { + return $this->post('users/'.$this->encodePath($user_id).'/impersonation_tokens', array( + 'name' => $name, + 'scopes' => $scopes, + 'expires_at' => $expires_at + )); + } + + /** + * @param int $user_id + * @param int $impersonation_token_id + * @return mixed + */ + public function removeImpersonationToken($user_id, $impersonation_token_id) + { + return $this->delete('users/'.$this->encodePath($user_id).'/impersonation_tokens/'.$this->encodePath($impersonation_token_id)); + } } diff --git a/vendor/m4tthumphrey/php-gitlab-api/lib/Gitlab/Api/Version.php b/vendor/m4tthumphrey/php-gitlab-api/lib/Gitlab/Api/Version.php new file mode 100644 index 00000000000..59a58104b28 --- /dev/null +++ b/vendor/m4tthumphrey/php-gitlab-api/lib/Gitlab/Api/Version.php @@ -0,0 +1,9 @@ +<?php namespace Gitlab\Api; + +class Version extends AbstractApi +{ + public function show() + { + return $this->get('version'); + } +} diff --git a/vendor/m4tthumphrey/php-gitlab-api/lib/Gitlab/Client.php b/vendor/m4tthumphrey/php-gitlab-api/lib/Gitlab/Client.php index 5f0fd395091..376e461f4cb 100644 --- a/vendor/m4tthumphrey/php-gitlab-api/lib/Gitlab/Client.php +++ b/vendor/m4tthumphrey/php-gitlab-api/lib/Gitlab/Client.php @@ -1,14 +1,18 @@ <?php namespace Gitlab; -use Buzz\Client\Curl; -use Buzz\Client\ClientInterface; - use Gitlab\Api\AbstractApi; use Gitlab\Exception\InvalidArgumentException; -use Gitlab\HttpClient\HttpClient; -use Gitlab\HttpClient\HttpClientInterface; -use Gitlab\HttpClient\Listener\AuthListener; -use Gitlab\HttpClient\Listener\PaginationListener; +use Gitlab\HttpClient\Builder; +use Gitlab\HttpClient\Plugin\ApiVersion; +use Gitlab\HttpClient\Plugin\History; +use Gitlab\HttpClient\Plugin\Authentication; +use Gitlab\HttpClient\Plugin\GitlabExceptionThrower; +use Http\Client\Common\HttpMethodsClient; +use Http\Client\Common\Plugin\AddHostPlugin; +use Http\Client\Common\Plugin\HeaderDefaultsPlugin; +use Http\Client\Common\Plugin\HistoryPlugin; +use Http\Client\HttpClient; +use Http\Discovery\UriFactoryDiscovery; /** * Simple API wrapper for Gitlab @@ -17,6 +21,7 @@ use Gitlab\HttpClient\Listener\PaginationListener; * * @property-read \Gitlab\Api\Groups $groups * @property-read \Gitlab\Api\Issues $issues + * @property-read \Gitlab\Api\Jobs $jobs * @property-read \Gitlab\Api\MergeRequests $merge_requests * @property-read \Gitlab\Api\MergeRequests $mr * @property-read \Gitlab\Api\Milestones $milestones @@ -32,6 +37,7 @@ use Gitlab\HttpClient\Listener\PaginationListener; * @property-read \Gitlab\Api\Users $users * @property-read \Gitlab\Api\Keys $keys * @property-read \Gitlab\Api\Tags $tags + * @property-read \Gitlab\Api\Version $version */ class Client { @@ -54,48 +60,206 @@ class Client const AUTH_OAUTH_TOKEN = 'oauth_token'; /** - * @var array + * @var History */ - private $options = array( - 'user_agent' => 'php-gitlab-api (http://github.com/m4tthumphrey/php-gitlab-api)', - 'timeout' => 60 - ); + private $responseHistory; - private $baseUrl; + /** + * @var Builder + */ + private $httpClientBuilder; /** - * The Buzz instance used to communicate with Gitlab + * Instantiate a new Gitlab client * - * @var HttpClient + * @param Builder $httpClientBuilder */ - private $httpClient; + public function __construct(Builder $httpClientBuilder = null) + { + $this->responseHistory = new History(); + $this->httpClientBuilder = $httpClientBuilder ?: new Builder(); + + $this->httpClientBuilder->addPlugin(new GitlabExceptionThrower()); + $this->httpClientBuilder->addPlugin(new HistoryPlugin($this->responseHistory)); + $this->httpClientBuilder->addPlugin(new ApiVersion()); + $this->httpClientBuilder->addPlugin(new HeaderDefaultsPlugin([ + 'User-Agent' => 'php-gitlab-api (http://github.com/m4tthumphrey/php-gitlab-api)', + ])); + + $this->setUrl('https://gitlab.com'); + } /** - * Instantiate a new Gitlab client + * Create a Gitlab\Client using an url. + * + * @param string $url * - * @param string $baseUrl - * @param null|ClientInterface $httpClient Buzz client - * @param array $options + * @return Client */ - public function __construct($baseUrl, ClientInterface $httpClient = null, array $options = array()) + public static function create($url) { - foreach ($options as $name => $value) { - $this->setOption($name, $value); - } + $client = new self(); + $client->setUrl($url); + + return $client; + } + + /** + * Create a Gitlab\Client using an HttpClient. + * + * @param HttpClient $httpClient + * + * @return Client + */ + public static function createWithHttpClient(HttpClient $httpClient) + { + $builder = new Builder($httpClient); + + return new self($builder); + } + + /** + * @return Api\DeployKeys + */ + public function deployKeys() + { + return new Api\DeployKeys($this); + } + + /** + * @return Api\Environments + */ + public function environments() + { + return new Api\Environments($this); + } + + /** + * @return Api\Groups + */ + public function groups() + { + return new Api\Groups($this); + } + + /** + * @return Api\Issues + */ + public function issues() + { + return new Api\Issues($this); + } + + /** + * @return Api\IssueBoards + */ + public function issueBoards() + { + return new Api\IssueBoards($this); + } + + /** + * @return Api\Jobs + */ + public function jobs() + { + return new Api\Jobs($this); + } + + /** + * @return Api\MergeRequests + */ + public function mergeRequests() + { + return new Api\MergeRequests($this); + } + + /** + * @return Api\Milestones + */ + public function milestones() + { + return new Api\Milestones($this); + } + + /** + * @return Api\ProjectNamespaces + */ + public function namespaces() + { + return new Api\ProjectNamespaces($this); + } + + /** + * @return Api\Projects + */ + public function projects() + { + return new Api\Projects($this); + } + + /** + * @return Api\Repositories + */ + public function repositories() + { + return new Api\Repositories($this); + } + + /** + * @return Api\RepositoryFiles + */ + public function repositoryFiles() + { + return new Api\RepositoryFiles($this); + } + + /** + * @return Api\Snippets + */ + public function snippets() + { + return new Api\Snippets($this); + } - $httpClient = $httpClient ?: new Curl(); - $httpClient->setTimeout($this->options['timeout']); - $httpClient->setVerifyPeer(false); + /** + * @return Api\SystemHooks + */ + public function systemHooks() + { + return new Api\SystemHooks($this); + } - $this->baseUrl = $baseUrl; - $this->httpClient = new HttpClient($this->baseUrl, $this->options, $httpClient); + /** + * @return Api\Users + */ + public function users() + { + return new Api\Users($this); + } - /** - * a Pagination listener on Response - */ - $this->httpClient->addListener( - new PaginationListener() - ); + /** + * @return Api\Keys + */ + public function keys() + { + return new Api\Keys($this); + } + + /** + * @return Api\Tags + */ + public function tags() + { + return new Api\Tags($this); + } + + /** + * @return Api\Version + */ + public function version() + { + return new Api\Version($this); } /** @@ -109,68 +273,67 @@ class Client switch ($name) { case 'deploy_keys': - $api = new Api\DeployKeys($this); - break; + return $this->deployKeys(); + case 'environments': + return $this->environments(); + case 'groups': - $api = new Api\Groups($this); - break; + return $this->groups(); case 'issues': - $api = new Api\Issues($this); - break; + return $this->issues(); + + case 'board': + case 'issue_boards': + return $this->issueBoards(); + case 'jobs': + return $this->jobs(); case 'mr': case 'merge_requests': - $api = new Api\MergeRequests($this); - break; + return $this->mergeRequests(); case 'milestones': case 'ms': - $api = new Api\Milestones($this); - break; + return $this->milestones(); case 'namespaces': case 'ns': - $api = new Api\ProjectNamespaces($this); - break; + return $this->namespaces(); case 'projects': - $api = new Api\Projects($this); - break; + return $this->projects(); case 'repo': case 'repositories': - $api = new Api\Repositories($this); - break; + return $this->repositories(); + case 'repositoryFiles': + return $this->repositoryFiles(); + case 'snippets': - $api = new Api\Snippets($this); - break; + return $this->snippets(); case 'hooks': case 'system_hooks': - $api = new Api\SystemHooks($this); - break; + return $this->systemHooks(); case 'users': - $api = new Api\Users($this); - break; + return $this->users(); case 'keys': - $api = new Api\Keys($this); - break; + return $this->keys(); case 'tags': - $api = new Api\Tags($this); - break; + return $this->tags(); + + case 'version': + return $this->version(); default: throw new InvalidArgumentException('Invalid endpoint: "'.$name.'"'); - } - - return $api; } /** @@ -183,117 +346,47 @@ class Client */ public function authenticate($token, $authMethod = self::AUTH_URL_TOKEN, $sudo = null) { - $this->httpClient->addListener( - new AuthListener( - $authMethod, - $token, - $sudo - ) - ); - - return $this; - } - - /** - * @return HttpClient - */ - public function getHttpClient() - { - return $this->httpClient; - } - - /** - * @param HttpClientInterface $httpClient - * @return $this - */ - public function setHttpClient(HttpClientInterface $httpClient) - { - $this->httpClient = $httpClient; + $this->httpClientBuilder->removePlugin(Authentication::class); + $this->httpClientBuilder->addPlugin(new Authentication($authMethod, $token, $sudo)); return $this; } /** * @param string $url - * @return $this - */ - public function setBaseUrl($url) - { - $this->baseUrl = $url; - - return $this; - } - - /** - * @return string - */ - public function getBaseUrl() - { - return $this->baseUrl; - } - - /** - * Clears used headers * * @return $this */ - public function clearHeaders() - { - $this->httpClient->clearHeaders(); - - return $this; - } - - /** - * @param array $headers - * @return $this - */ - public function setHeaders(array $headers) + public function setUrl($url) { - $this->httpClient->setHeaders($headers); + $this->httpClientBuilder->removePlugin(AddHostPlugin::class); + $this->httpClientBuilder->addPlugin(new AddHostPlugin(UriFactoryDiscovery::find()->createUri($url))); return $this; } /** - * @param string $name - * - * @return mixed - * - * @throws InvalidArgumentException + * @param string $api + * @return AbstractApi */ - public function getOption($name) + public function __get($api) { - if (!array_key_exists($name, $this->options)) { - throw new InvalidArgumentException(sprintf('Undefined option called: "%s"', $name)); - } - - return $this->options[$name]; + return $this->api($api); } /** - * @param string $name - * @param mixed $value - * @throws InvalidArgumentException - * @return $this + * @return HttpMethodsClient */ - public function setOption($name, $value) + public function getHttpClient() { - if (!array_key_exists($name, $this->options)) { - throw new InvalidArgumentException(sprintf('Undefined option called: "%s"', $name)); - } - - $this->options[$name] = $value; - - return $this; + return $this->httpClientBuilder->getHttpClient(); } /** - * @param string $api - * @return AbstractApi + * @return History */ - public function __get($api) + public function getResponseHistory() { - return $this->api($api); + return $this->responseHistory; } } diff --git a/vendor/m4tthumphrey/php-gitlab-api/lib/Gitlab/Exception/ErrorException.php b/vendor/m4tthumphrey/php-gitlab-api/lib/Gitlab/Exception/ErrorException.php index 322a7082da8..4bca15dc291 100644 --- a/vendor/m4tthumphrey/php-gitlab-api/lib/Gitlab/Exception/ErrorException.php +++ b/vendor/m4tthumphrey/php-gitlab-api/lib/Gitlab/Exception/ErrorException.php @@ -5,7 +5,6 @@ * * @author Joseph Bielawski <stloyd@gmail.com> */ -class ErrorException extends \ErrorException +class ErrorException extends \ErrorException implements ExceptionInterface { - } diff --git a/vendor/m4tthumphrey/php-gitlab-api/lib/Gitlab/Exception/ExceptionInterface.php b/vendor/m4tthumphrey/php-gitlab-api/lib/Gitlab/Exception/ExceptionInterface.php new file mode 100644 index 00000000000..81403dcf89c --- /dev/null +++ b/vendor/m4tthumphrey/php-gitlab-api/lib/Gitlab/Exception/ExceptionInterface.php @@ -0,0 +1,12 @@ +<?php namespace Gitlab\Exception; + +use Http\Client\Exception; + +/** + * ExceptionInterface + * + * @author Graham Campbell <graham@alt-three.com> + */ +interface ExceptionInterface extends Exception +{ +} diff --git a/vendor/m4tthumphrey/php-gitlab-api/lib/Gitlab/Exception/InvalidArgumentException.php b/vendor/m4tthumphrey/php-gitlab-api/lib/Gitlab/Exception/InvalidArgumentException.php index 892a6dc4bd9..f430ab9e5e2 100644 --- a/vendor/m4tthumphrey/php-gitlab-api/lib/Gitlab/Exception/InvalidArgumentException.php +++ b/vendor/m4tthumphrey/php-gitlab-api/lib/Gitlab/Exception/InvalidArgumentException.php @@ -5,7 +5,6 @@ * * @author Joseph Bielawski <stloyd@gmail.com> */ -class InvalidArgumentException extends \InvalidArgumentException +class InvalidArgumentException extends \InvalidArgumentException implements ExceptionInterface { - } diff --git a/vendor/m4tthumphrey/php-gitlab-api/lib/Gitlab/Exception/MissingArgumentException.php b/vendor/m4tthumphrey/php-gitlab-api/lib/Gitlab/Exception/MissingArgumentException.php index f157b49d1e5..93a5451e5ec 100644 --- a/vendor/m4tthumphrey/php-gitlab-api/lib/Gitlab/Exception/MissingArgumentException.php +++ b/vendor/m4tthumphrey/php-gitlab-api/lib/Gitlab/Exception/MissingArgumentException.php @@ -5,7 +5,7 @@ * * @author Joseph Bielawski <stloyd@gmail.com> */ -class MissingArgumentException extends ErrorException +class MissingArgumentException extends ErrorException implements ExceptionInterface { public function __construct($required, $code = 0, $previous = null) { diff --git a/vendor/m4tthumphrey/php-gitlab-api/lib/Gitlab/Exception/RuntimeException.php b/vendor/m4tthumphrey/php-gitlab-api/lib/Gitlab/Exception/RuntimeException.php index 9d938e2cc39..0a12b500cc2 100644 --- a/vendor/m4tthumphrey/php-gitlab-api/lib/Gitlab/Exception/RuntimeException.php +++ b/vendor/m4tthumphrey/php-gitlab-api/lib/Gitlab/Exception/RuntimeException.php @@ -5,7 +5,6 @@ * * @author Joseph Bielawski <stloyd@gmail.com> */ -class RuntimeException extends \RuntimeException +class RuntimeException extends \RuntimeException implements ExceptionInterface { - } diff --git a/vendor/m4tthumphrey/php-gitlab-api/lib/Gitlab/Exception/ValidationFailedException.php b/vendor/m4tthumphrey/php-gitlab-api/lib/Gitlab/Exception/ValidationFailedException.php index 9ce6635fe58..066b9f855cd 100644 --- a/vendor/m4tthumphrey/php-gitlab-api/lib/Gitlab/Exception/ValidationFailedException.php +++ b/vendor/m4tthumphrey/php-gitlab-api/lib/Gitlab/Exception/ValidationFailedException.php @@ -5,7 +5,6 @@ * * @author Joseph Bielawski <stloyd@gmail.com> */ -class ValidationFailedException extends ErrorException +class ValidationFailedException extends ErrorException implements ExceptionInterface { - } diff --git a/vendor/m4tthumphrey/php-gitlab-api/lib/Gitlab/HttpClient/Builder.php b/vendor/m4tthumphrey/php-gitlab-api/lib/Gitlab/HttpClient/Builder.php new file mode 100644 index 00000000000..8c6f586eba1 --- /dev/null +++ b/vendor/m4tthumphrey/php-gitlab-api/lib/Gitlab/HttpClient/Builder.php @@ -0,0 +1,118 @@ +<?php + +namespace Gitlab\HttpClient; + +use Http\Client\Common\HttpMethodsClient; +use Http\Client\Common\Plugin; +use Http\Client\Common\PluginClient; +use Http\Client\Common\PluginClientFactory; +use Http\Client\HttpClient; +use Http\Discovery\HttpClientDiscovery; +use Http\Discovery\MessageFactoryDiscovery; +use Http\Discovery\StreamFactoryDiscovery; +use Http\Message\MessageFactory; +use Http\Message\RequestFactory; +use Http\Message\StreamFactory; + +/** + * A builder that builds the API client. + * This will allow you to fluently add and remove plugins. + * + * @author Tobias Nyholm <tobias.nyholm@gmail.com> + */ +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 = []; + + /** + * @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; + + $this->pluginClient = new HttpMethodsClient( + (new PluginClientFactory())->createClient($this->httpClient, $this->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; + } + } + } +} diff --git a/vendor/m4tthumphrey/php-gitlab-api/lib/Gitlab/HttpClient/HttpClient.php b/vendor/m4tthumphrey/php-gitlab-api/lib/Gitlab/HttpClient/HttpClient.php deleted file mode 100644 index 7a84bfb436a..00000000000 --- a/vendor/m4tthumphrey/php-gitlab-api/lib/Gitlab/HttpClient/HttpClient.php +++ /dev/null @@ -1,231 +0,0 @@ -<?php namespace Gitlab\HttpClient; - -use Buzz\Client\ClientInterface; -use Buzz\Listener\ListenerInterface; -use Buzz\Message\Form\FormUpload; - -use Gitlab\Exception\ErrorException; -use Gitlab\Exception\RuntimeException; -use Gitlab\HttpClient\Listener\ErrorListener; -use Gitlab\HttpClient\Message\Request; -use Gitlab\HttpClient\Message\Response; -use Gitlab\HttpClient\Message\FormRequest; - -/** - * Performs requests on Gitlab API. API documentation should be self-explanatory. - * - * @author Joseph Bielawski <stloyd@gmail.com> - * @author Matt Humphrey <matt@m4tt.co> - */ -class HttpClient implements HttpClientInterface -{ - /** - * @var array - */ - protected $options = array( - 'user_agent' => 'php-gitlab-api (http://github.com/m4tthumphrey/php-gitlab-api)', - 'timeout' => 10, - ); - - /** - * @var string - */ - protected $baseUrl; - - /** - * @var ListenerInterface[] - */ - protected $listeners = array(); - /** - * @var array - */ - protected $headers = array(); - - /** - * @var Response - */ - private $lastResponse; - - /** - * @var Request - */ - private $lastRequest; - - /** - * @param string $baseUrl - * @param array $options - * @param ClientInterface $client - */ - public function __construct($baseUrl, array $options, ClientInterface $client) - { - $this->baseUrl = $baseUrl; - $this->options = array_merge($this->options, $options); - $this->client = $client; - - $this->addListener(new ErrorListener($this->options)); - - $this->clearHeaders(); - } - - /** - * {@inheritDoc} - */ - public function setOption($name, $value) - { - $this->options[$name] = $value; - } - - /** - * {@inheritDoc} - */ - public function setHeaders(array $headers) - { - $this->headers = array_merge($this->headers, $headers); - } - - /** - * Clears used headers - */ - public function clearHeaders() - { - $this->headers = array(); - } - - /** - * @param ListenerInterface $listener - */ - public function addListener(ListenerInterface $listener) - { - $this->listeners[get_class($listener)] = $listener; - } - - /** - * {@inheritDoc} - */ - public function get($path, array $parameters = array(), array $headers = array()) - { - if (0 < count($parameters)) { - $path .= (false === strpos($path, '?') ? '?' : '&').http_build_query($parameters, '', '&'); - } - - return $this->request($path, array(), 'GET', $headers); - } - - /** - * {@inheritDoc} - */ - public function post($path, array $parameters = array(), array $headers = array(), array $files = array()) - { - return $this->request($path, $parameters, 'POST', $headers, $files); - } - - /** - * {@inheritDoc} - */ - public function patch($path, array $parameters = array(), array $headers = array()) - { - return $this->request($path, $parameters, 'PATCH', $headers); - } - - /** - * {@inheritDoc} - */ - public function delete($path, array $parameters = array(), array $headers = array()) - { - return $this->request($path, $parameters, 'DELETE', $headers); - } - - /** - * {@inheritDoc} - */ - public function put($path, array $parameters = array(), array $headers = array()) - { - return $this->request($path, $parameters, 'PUT', $headers); - } - - /** - * {@inheritDoc} - */ - public function request($path, array $parameters = array(), $httpMethod = 'GET', array $headers = array(), array $files = array()) - { - $path = trim($this->baseUrl.$path, '/'); - - $request = $this->createRequest($httpMethod, $path, $parameters, $headers, $files); - - $hasListeners = 0 < count($this->listeners); - if ($hasListeners) { - foreach ($this->listeners as $listener) { - $listener->preSend($request); - } - } - - $response = new Response(); - - try { - $this->client->send($request, $response); - } catch (\LogicException $e) { - throw new ErrorException($e->getMessage()); - } catch (\RuntimeException $e) { - throw new RuntimeException($e->getMessage()); - } - - $this->lastRequest = $request; - $this->lastResponse = $response; - - if ($hasListeners) { - foreach ($this->listeners as $listener) { - $listener->postSend($request, $response); - } - } - - return $response; - } - - /** - * @return Request - */ - public function getLastRequest() - { - return $this->lastRequest; - } - - /** - * @return Response - */ - public function getLastResponse() - { - return $this->lastResponse; - } - - /** - * @param string $httpMethod - * @param string $url - * @param array $parameters - * @param array $headers - * @param array $files - * - * @return FormRequest|Request - */ - private function createRequest($httpMethod, $url, array $parameters, array $headers, array $files) - { - if (empty($files)) { - $request = new Request($httpMethod); - $request->setContent(http_build_query($parameters)); - } else { - $request = new FormRequest($httpMethod); - foreach ($parameters as $name => $value) { - $request->setField($name, $value); - } - - foreach ($files as $name => $file) { - $upload = new FormUpload($file); - $request->setField($name, $upload); - } - } - $request->setHeaders($this->headers); - $request->fromUrl($url); - $request->addHeaders($headers); - - return $request; - } -} diff --git a/vendor/m4tthumphrey/php-gitlab-api/lib/Gitlab/HttpClient/HttpClientInterface.php b/vendor/m4tthumphrey/php-gitlab-api/lib/Gitlab/HttpClient/HttpClientInterface.php deleted file mode 100644 index 674987becc9..00000000000 --- a/vendor/m4tthumphrey/php-gitlab-api/lib/Gitlab/HttpClient/HttpClientInterface.php +++ /dev/null @@ -1,98 +0,0 @@ -<?php namespace Gitlab\HttpClient; - -use Gitlab\Exception\InvalidArgumentException; - -/** - * Performs requests on Gitlab API. API documentation should be self-explanatory. - * - * @author Joseph Bielawski <stloyd@gmail.com> - * @author Matt Humphrey <matt@m4tt.co> - */ -interface HttpClientInterface -{ - /** - * Send a GET request - * - * @param string $path Request path - * @param array $parameters GET Parameters - * @param array $headers Reconfigure the request headers for this call only - * - * @return array Data - */ - public function get($path, array $parameters = array(), array $headers = array()); - - /** - * Send a POST request - * - * @param string $path Request path - * @param array $parameters POST Parameters - * @param array $headers Reconfigure the request headers for this call only - * @param array $files Files paths of files to upload - * - * @return array Data - */ - public function post($path, array $parameters = array(), array $headers = array(), array $files = array()); - - /** - * Send a PATCH request - * - * @param string $path Request path - * @param array $parameters PATCH Parameters - * @param array $headers Reconfigure the request headers for this call only - * - * @return array Data - */ - public function patch($path, array $parameters = array(), array $headers = array()); - - /** - * Send a PUT request - * - * @param string $path Request path - * @param array $parameters PUT Parameters - * @param array $headers Reconfigure the request headers for this call only - * - * @return array Data - */ - public function put($path, array $parameters = array(), array $headers = array()); - - /** - * Send a DELETE request - * - * @param string $path Request path - * @param array $parameters DELETE Parameters - * @param array $headers Reconfigure the request headers for this call only - * - * @return array Data - */ - public function delete($path, array $parameters = array(), array $headers = array()); - - /** - * Send a request to the server, receive a response, - * decode the response and returns an associative array - * - * @param string $path Request API path - * @param array $parameters Parameters - * @param string $httpMethod HTTP method to use - * @param array $headers Request headers - * - * @return array Data - */ - public function request($path, array $parameters = array(), $httpMethod = 'GET', array $headers = array()); - - /** - * Change an option value. - * - * @param string $name The option name - * @param mixed $value The value - * - * @throws InvalidArgumentException - */ - public function setOption($name, $value); - - /** - * Set HTTP headers - * - * @param array $headers - */ - public function setHeaders(array $headers); -} diff --git a/vendor/m4tthumphrey/php-gitlab-api/lib/Gitlab/HttpClient/Listener/AuthListener.php b/vendor/m4tthumphrey/php-gitlab-api/lib/Gitlab/HttpClient/Listener/AuthListener.php deleted file mode 100644 index 27164112280..00000000000 --- a/vendor/m4tthumphrey/php-gitlab-api/lib/Gitlab/HttpClient/Listener/AuthListener.php +++ /dev/null @@ -1,95 +0,0 @@ -<?php namespace Gitlab\HttpClient\Listener; - -use Gitlab\Client; -use Gitlab\Exception\InvalidArgumentException; - -use Buzz\Listener\ListenerInterface; -use Buzz\Message\MessageInterface; -use Buzz\Message\RequestInterface; -use Buzz\Util\Url; - -/** - * @author Joseph Bielawski <stloyd@gmail.com> - * @author Matt Humphrey <matt@m4tt.co> - */ -class AuthListener implements ListenerInterface -{ - /** - * @var string - */ - private $method; - - /** - * @var string - */ - private $token; - - /** - * @var string|null - */ - private $sudo; - - /** - * @param string $method - * @param string $token - * @param string|null $sudo - */ - public function __construct($method, $token, $sudo = null) - { - $this->method = $method; - $this->token = $token; - $this->sudo = $sudo; - } - - /** - * {@inheritDoc} - * - * @throws InvalidArgumentException - */ - public function preSend(RequestInterface $request) - { - // Skip by default - if (null === $this->method) { - return; - } - - switch ($this->method) { - case Client::AUTH_HTTP_TOKEN: - $request->addHeader('PRIVATE-TOKEN: '.$this->token); - if (!is_null($this->sudo)) { - $request->addHeader('SUDO: '.$this->sudo); - } - break; - - case Client::AUTH_URL_TOKEN: - $url = $request->getUrl(); - - $query = array( - 'private_token' => $this->token - ); - - if (!is_null($this->sudo)) { - $query['sudo'] = $this->sudo; - } - - $url .= (false === strpos($url, '?') ? '?' : '&').utf8_encode(http_build_query($query, '', '&')); - - $request->fromUrl(new Url($url)); - break; - - case Client::AUTH_OAUTH_TOKEN: - $request->addHeader('Authorization: Bearer '.$this->token); - if (!is_null($this->sudo)) { - $request->addHeader('SUDO: '.$this->sudo); - } - break; - } - } - - /** - * {@inheritDoc} - */ - public function postSend(RequestInterface $request, MessageInterface $response) - { - } -} diff --git a/vendor/m4tthumphrey/php-gitlab-api/lib/Gitlab/HttpClient/Listener/ErrorListener.php b/vendor/m4tthumphrey/php-gitlab-api/lib/Gitlab/HttpClient/Listener/ErrorListener.php deleted file mode 100644 index cb2acc68b21..00000000000 --- a/vendor/m4tthumphrey/php-gitlab-api/lib/Gitlab/HttpClient/Listener/ErrorListener.php +++ /dev/null @@ -1,97 +0,0 @@ -<?php namespace Gitlab\HttpClient\Listener; - -use Buzz\Listener\ListenerInterface; -use Buzz\Message\MessageInterface; -use Buzz\Message\RequestInterface; -use Gitlab\Exception\ErrorException; -use Gitlab\Exception\RuntimeException; - -/** - * @author Joseph Bielawski <stloyd@gmail.com> - * @author Matt Humphrey <git@m4tt.co> - */ -class ErrorListener implements ListenerInterface -{ - /** - * @var array - */ - private $options; - - /** - * @param array $options - */ - public function __construct(array $options) - { - $this->options = $options; - } - - /** - * {@inheritDoc} - */ - public function preSend(RequestInterface $request) - { - } - - /** - * {@inheritDoc} - */ - public function postSend(RequestInterface $request, MessageInterface $response) - { - /** @var $response \Gitlab\HttpClient\Message\Response */ - if ($response->isClientError() || $response->isServerError()) { - $content = $response->getContent(); - if (is_array($content) && isset($content['message'])) { - if (400 == $response->getStatusCode()) { - $message = $this->parseMessage($content['message']); - - throw new ErrorException($message, 400); - } - } - - $errorMessage = null; - if (isset($content['error'])) { - $errorMessage = $content['error']; - if (is_array($content['error'])) { - $errorMessage = implode("\n", $content['error']); - } - } elseif (isset($content['message'])) { - $errorMessage = $this->parseMessage($content['message']); - } else { - $errorMessage = $content; - } - - throw new RuntimeException($errorMessage, $response->getStatusCode()); - } - } - - /** - * @param mixed $message - * @return string - */ - protected function parseMessage($message) - { - $string = $message; - - if (is_array($message)) { - $format = '"%s" %s'; - $errors = array(); - - foreach ($message as $field => $messages) { - if (is_array($messages)) { - $messages = array_unique($messages); - foreach ($messages as $error) { - $errors[] = sprintf($format, $field, $error); - } - } elseif (is_integer($field)) { - $errors[] = $messages; - } else { - $errors[] = sprintf($format, $field, $messages); - } - } - - $string = implode(', ', $errors); - } - - return $string; - } -} diff --git a/vendor/m4tthumphrey/php-gitlab-api/lib/Gitlab/HttpClient/Listener/PaginationListener.php b/vendor/m4tthumphrey/php-gitlab-api/lib/Gitlab/HttpClient/Listener/PaginationListener.php deleted file mode 100644 index 80ba39ea965..00000000000 --- a/vendor/m4tthumphrey/php-gitlab-api/lib/Gitlab/HttpClient/Listener/PaginationListener.php +++ /dev/null @@ -1,38 +0,0 @@ -<?php namespace Gitlab\HttpClient\Listener; - -use Buzz\Listener\ListenerInterface; -use Buzz\Message\MessageInterface; -use Buzz\Message\RequestInterface; - -class PaginationListener implements ListenerInterface -{ - /** - * {@inheritDoc} - */ - public function preSend(RequestInterface $request) - { - } - - /** - * {@inheritDoc} - */ - public function postSend(RequestInterface $request, MessageInterface $response) - { - $header = $response->getHeader('Link'); - - if (empty($header)) { - return null; - } - - $pagination = array(); - foreach (explode(',', $header) as $link) { - preg_match('/<(.*)>; rel="(.*)"/i', trim($link, ','), $match); - - if (3 === count($match)) { - $pagination[$match[2]] = $match[1]; - } - } - - $response->setPagination($pagination); - } -} diff --git a/vendor/m4tthumphrey/php-gitlab-api/lib/Gitlab/HttpClient/Message/FormRequest.php b/vendor/m4tthumphrey/php-gitlab-api/lib/Gitlab/HttpClient/Message/FormRequest.php deleted file mode 100644 index ecd05e17d8e..00000000000 --- a/vendor/m4tthumphrey/php-gitlab-api/lib/Gitlab/HttpClient/Message/FormRequest.php +++ /dev/null @@ -1,8 +0,0 @@ -<?php namespace Gitlab\HttpClient\Message; - -use Buzz\Message\Form\FormRequest as BaseFormRequest; - -class FormRequest extends BaseFormRequest -{ - -} diff --git a/vendor/m4tthumphrey/php-gitlab-api/lib/Gitlab/HttpClient/Message/QueryStringBuilder.php b/vendor/m4tthumphrey/php-gitlab-api/lib/Gitlab/HttpClient/Message/QueryStringBuilder.php new file mode 100644 index 00000000000..03e16a32610 --- /dev/null +++ b/vendor/m4tthumphrey/php-gitlab-api/lib/Gitlab/HttpClient/Message/QueryStringBuilder.php @@ -0,0 +1,76 @@ +<?php + +namespace Gitlab\HttpClient\Message; + +final class QueryStringBuilder +{ + /** + * Encode a query as a query string according to RFC 3986. Indexed arrays are encoded using + * empty squared brackets ([]) unlike http_build_query. + * + * @param mixed $query + * + * @return string + */ + public static function build($query) + { + if (!is_array($query)) { + return static::rawurlencode($query); + } + return implode('&', array_map(function ($value, $key) { + return static::encode($value, $key); + }, $query, array_keys($query))); + } + + /** + * Encode a value + * @param mixed $query + * @param string $prefix + * + * @return string + */ + private static function encode($query, $prefix) + { + if (!is_array($query)) { + return static::rawurlencode($prefix).'='.static::rawurlencode($query); + } + + $isIndexedArray = static::isIndexedArray($query); + return implode('&', array_map(function ($value, $key) use ($prefix, $isIndexedArray) { + $prefix = $isIndexedArray ? $prefix.'[]' : $prefix.'['.$key.']'; + return static::encode($value, $prefix); + }, $query, array_keys($query))); + } + + /** + * Tell if the given array is an indexed one (i.e. contains only sequential integer keys starting from 0). + * + * @param array $query + * + * @return bool + */ + public static function isIndexedArray(array $query) + { + if (empty($query) || !isset($query[0])) { + return false; + } + + return array_keys($query) === range(0, count($query) - 1); + } + + /** + * Encode a value like rawurlencode, but return "0" when false is given. + * + * @param mixed $value + * + * @return string + */ + private static function rawurlencode($value) + { + if ($value === false) { + return '0'; + } + + return rawurlencode($value); + } +} diff --git a/vendor/m4tthumphrey/php-gitlab-api/lib/Gitlab/HttpClient/Message/Request.php b/vendor/m4tthumphrey/php-gitlab-api/lib/Gitlab/HttpClient/Message/Request.php deleted file mode 100644 index 41a950f35f3..00000000000 --- a/vendor/m4tthumphrey/php-gitlab-api/lib/Gitlab/HttpClient/Message/Request.php +++ /dev/null @@ -1,8 +0,0 @@ -<?php namespace Gitlab\HttpClient\Message; - -use Buzz\Message\Request as BaseRequest; - -class Request extends BaseRequest -{ - -} diff --git a/vendor/m4tthumphrey/php-gitlab-api/lib/Gitlab/HttpClient/Message/Response.php b/vendor/m4tthumphrey/php-gitlab-api/lib/Gitlab/HttpClient/Message/Response.php deleted file mode 100644 index e5746e0b8c2..00000000000 --- a/vendor/m4tthumphrey/php-gitlab-api/lib/Gitlab/HttpClient/Message/Response.php +++ /dev/null @@ -1,48 +0,0 @@ -<?php namespace Gitlab\HttpClient\Message; - -use Buzz\Message\Response as BaseResponse; - -class Response extends BaseResponse -{ - /** - * @var array pagination - * See PaginationListener - */ - protected $pagination = array(); - - /** - * @return mixed - */ - public function getPagination() - { - return $this->pagination; - } - - /** - * @param array $pagination - */ - public function setPagination(array $pagination) - { - $this->pagination = $pagination; - } - - /** - * {@inheritDoc} - */ - public function getContent() - { - $response = parent::getContent(); - - if ($this->getHeader('Content-Type') === 'application/json') { - $content = json_decode($response, true); - - if (JSON_ERROR_NONE !== json_last_error()) { - return $response; - } - - return $content; - } - - return $response; - } -} diff --git a/vendor/m4tthumphrey/php-gitlab-api/lib/Gitlab/HttpClient/Message/ResponseMediator.php b/vendor/m4tthumphrey/php-gitlab-api/lib/Gitlab/HttpClient/Message/ResponseMediator.php new file mode 100644 index 00000000000..16bd781f07f --- /dev/null +++ b/vendor/m4tthumphrey/php-gitlab-api/lib/Gitlab/HttpClient/Message/ResponseMediator.php @@ -0,0 +1,73 @@ +<?php + +namespace Gitlab\HttpClient\Message; + +use Psr\Http\Message\ResponseInterface; + +/** + * Utilities to parse response headers and content. + */ +class ResponseMediator +{ + /** + * Return the response body as a string or json array if content type is application/json. + *. + * @param ResponseInterface $response + * + * @return array|string + */ + public static function getContent(ResponseInterface $response) + { + $body = $response->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; + } + + /** + * Extract pagination URIs from Link header. + * + * @param ResponseInterface $response + * + * @return array|null + */ + public static function getPagination(ResponseInterface $response) + { + if (!$response->hasHeader('Link')) { + return null; + } + + $header = self::getHeader($response, 'Link'); + $pagination = array(); + foreach (explode(',', $header) as $link) { + preg_match('/<(.*)>; rel="(.*)"/i', trim($link, ','), $match); + + if (3 === count($match)) { + $pagination[$match[2]] = $match[1]; + } + } + + return $pagination; + } + + + /** + * Get the value for a single header. + * + * @param ResponseInterface $response + * @param string $name + * + * @return string|null + */ + private static function getHeader(ResponseInterface $response, $name) + { + $headers = $response->getHeader($name); + + return array_shift($headers); + } +} diff --git a/vendor/m4tthumphrey/php-gitlab-api/lib/Gitlab/HttpClient/Plugin/ApiVersion.php b/vendor/m4tthumphrey/php-gitlab-api/lib/Gitlab/HttpClient/Plugin/ApiVersion.php new file mode 100644 index 00000000000..46b20e40228 --- /dev/null +++ b/vendor/m4tthumphrey/php-gitlab-api/lib/Gitlab/HttpClient/Plugin/ApiVersion.php @@ -0,0 +1,29 @@ +<?php + + +namespace Gitlab\HttpClient\Plugin; + +use Http\Client\Common\Plugin; +use Psr\Http\Message\RequestInterface; + +/** + * Prefix requests path with /api/v4/ if required. + * + * @author Fabien Bourigault <bourigaultfabien@gmail.com> + */ +class ApiVersion implements Plugin +{ + /** + * {@inheritdoc} + */ + public function handleRequest(RequestInterface $request, callable $next, callable $first) + { + $uri = $request->getUri(); + + if (substr($uri->getPath(), 0, 8) !== '/api/v4/') { + $request = $request->withUri($uri->withPath('/api/v4/'.$uri->getPath())); + } + + return $next($request); + } +} diff --git a/vendor/m4tthumphrey/php-gitlab-api/lib/Gitlab/HttpClient/Plugin/Authentication.php b/vendor/m4tthumphrey/php-gitlab-api/lib/Gitlab/HttpClient/Plugin/Authentication.php new file mode 100644 index 00000000000..aac0acc4a2e --- /dev/null +++ b/vendor/m4tthumphrey/php-gitlab-api/lib/Gitlab/HttpClient/Plugin/Authentication.php @@ -0,0 +1,86 @@ +<?php + +namespace Gitlab\HttpClient\Plugin; + +use Gitlab\Client; +use Http\Client\Common\Plugin; +use Psr\Http\Message\RequestInterface; + +/** + * Add authentication to the request. + * + * @author Tobias Nyholm <tobias.nyholm@gmail.com> + * @author Fabien Bourigault <bourigaultfabien@gmail.com> + */ +class Authentication implements Plugin +{ + /** + * @var string + */ + private $method; + + /** + * @var string + */ + private $token; + + /** + * @var string|null + */ + private $sudo; + + /** + * @param string $method + * @param string $token + * @param string|null $sudo + */ + public function __construct($method, $token, $sudo = null) + { + $this->method = $method; + $this->token = $token; + $this->sudo = $sudo; + } + + /** + * {@inheritdoc} + */ + public function handleRequest(RequestInterface $request, callable $next, callable $first) + { + switch ($this->method) { + case Client::AUTH_HTTP_TOKEN: + $request = $request->withHeader('PRIVATE-TOKEN', $this->token); + if (!is_null($this->sudo)) { + $request = $request->withHeader('SUDO', $this->sudo); + } + break; + + case Client::AUTH_URL_TOKEN: + $uri = $request->getUri(); + $query = $uri->getQuery(); + + $parameters = [ + 'private_token' => $this->token, + ]; + + if (!is_null($this->sudo)) { + $parameters['sudo'] = $this->sudo; + } + + $query .= empty($query) ? '' : '&'; + $query .= utf8_encode(http_build_query($parameters, '', '&')); + + $uri = $uri->withQuery($query); + $request = $request->withUri($uri); + break; + + case Client::AUTH_OAUTH_TOKEN: + $request = $request->withHeader('Authorization', 'Bearer '.$this->token); + if (!is_null($this->sudo)) { + $request = $request->withHeader('SUDO', $this->sudo); + } + break; + } + + return $next($request); + } +} diff --git a/vendor/m4tthumphrey/php-gitlab-api/lib/Gitlab/HttpClient/Plugin/GitlabExceptionThrower.php b/vendor/m4tthumphrey/php-gitlab-api/lib/Gitlab/HttpClient/Plugin/GitlabExceptionThrower.php new file mode 100644 index 00000000000..bb61e31219f --- /dev/null +++ b/vendor/m4tthumphrey/php-gitlab-api/lib/Gitlab/HttpClient/Plugin/GitlabExceptionThrower.php @@ -0,0 +1,86 @@ +<?php + +namespace Gitlab\HttpClient\Plugin; + +use Gitlab\Exception\ErrorException; +use Gitlab\Exception\RuntimeException; +use Gitlab\HttpClient\Message\ResponseMediator; +use Http\Client\Common\Plugin; +use Psr\Http\Message\RequestInterface; +use Psr\Http\Message\ResponseInterface; + +/** + * A plugin to remember the last response. + * + * @author Tobias Nyholm <tobias.nyholm@gmail.com> + * @author Fabien Bourigault <bourigaultfabien@gmail.com> + */ +class GitlabExceptionThrower implements Plugin +{ + /** + * {@inheritdoc} + */ + public function handleRequest(RequestInterface $request, callable $next, callable $first) + { + return $next($request)->then(function (ResponseInterface $response) { + if ($response->getStatusCode() >= 400 && $response->getStatusCode() < 600) { + $content = ResponseMediator::getContent($response); + if (is_array($content) && isset($content['message'])) { + if (400 == $response->getStatusCode()) { + $message = $this->parseMessage($content['message']); + + throw new ErrorException($message, 400); + } + } + + $errorMessage = null; + if (isset($content['error'])) { + $errorMessage = $content['error']; + if (is_array($content['error'])) { + $errorMessage = implode("\n", $content['error']); + } + } elseif (isset($content['message'])) { + $errorMessage = $this->parseMessage($content['message']); + } else { + $errorMessage = $content; + } + + throw new RuntimeException($errorMessage, $response->getStatusCode()); + } + + return $response; + }); + } + + /** + * @param mixed $message + * + * @return string + */ + private function parseMessage($message) + { + $string = $message; + + if (is_array($message)) { + $format = '"%s" %s'; + $errors = array(); + + foreach ($message as $field => $messages) { + if (is_array($messages)) { + $messages = array_unique($messages); + foreach ($messages as $error) { + $errors[] = sprintf($format, $field, $error); + } + } elseif (is_integer($field)) { + $errors[] = $messages; + } else { + $errors[] = sprintf($format, $field, $messages); + } + } + + $string = implode(', ', $errors); + } + + return $string; + } +} diff --git a/vendor/m4tthumphrey/php-gitlab-api/lib/Gitlab/HttpClient/Plugin/History.php b/vendor/m4tthumphrey/php-gitlab-api/lib/Gitlab/HttpClient/Plugin/History.php new file mode 100644 index 00000000000..8412356fdae --- /dev/null +++ b/vendor/m4tthumphrey/php-gitlab-api/lib/Gitlab/HttpClient/Plugin/History.php @@ -0,0 +1,44 @@ +<?php + +namespace Gitlab\HttpClient\Plugin; + +use Http\Client\Common\Plugin\Journal; +use Http\Client\Exception; +use Psr\Http\Message\RequestInterface; +use Psr\Http\Message\ResponseInterface; + +/** + * A plugin to remember the last response. + * + * @author Tobias Nyholm <tobias.nyholm@gmail.com> + */ +class History implements Journal +{ + /** + * @var ResponseInterface + */ + private $lastResponse; + + /** + * @return ResponseInterface|null + */ + public function getLastResponse() + { + return $this->lastResponse; + } + + /** + * {@inheritdoc} + */ + public function addSuccess(RequestInterface $request, ResponseInterface $response) + { + $this->lastResponse = $response; + } + + /** + * {@inheritdoc} + */ + public function addFailure(RequestInterface $request, Exception $exception) + { + } +} diff --git a/vendor/m4tthumphrey/php-gitlab-api/lib/Gitlab/Model/Branch.php b/vendor/m4tthumphrey/php-gitlab-api/lib/Gitlab/Model/Branch.php index 23afebcfea9..8c7ef36b415 100644 --- a/vendor/m4tthumphrey/php-gitlab-api/lib/Gitlab/Model/Branch.php +++ b/vendor/m4tthumphrey/php-gitlab-api/lib/Gitlab/Model/Branch.php @@ -1,7 +1,7 @@ <?php namespace Gitlab\Model; +use Gitlab\Api\Projects; use Gitlab\Client; -use Gitlab\Api\AbstractApi as Api; /** * Class Branch @@ -57,7 +57,7 @@ class Branch extends AbstractModel */ public function show() { - $data = $this->api('repositories')->branch($this->project->id, $this->name); + $data = $this->client->repositories()->branch($this->project->id, $this->name); return static::fromArray($this->getClient(), $this->project, $data); } @@ -69,7 +69,7 @@ class Branch extends AbstractModel */ public function protect($devPush = false, $devMerge = false) { - $data = $this->api('repositories')->protectBranch($this->project->id, $this->name, $devPush, $devMerge); + $data = $this->client->repositories()->protectBranch($this->project->id, $this->name, $devPush, $devMerge); return static::fromArray($this->getClient(), $this->project, $data); } @@ -79,7 +79,7 @@ class Branch extends AbstractModel */ public function unprotect() { - $data = $this->api('repositories')->unprotectBranch($this->project->id, $this->name); + $data = $this->client->repositories()->unprotectBranch($this->project->id, $this->name); return static::fromArray($this->getClient(), $this->project, $data); } @@ -89,19 +89,21 @@ class Branch extends AbstractModel */ public function delete() { - $this->api('repositories')->deleteBranch($this->project->id, $this->name); + $this->client->repositories()->deleteBranch($this->project->id, $this->name); return true; } /** - * @param int $page - * @param int $per_page + * @param array $parameters + * + * @see Projects::commits for available parameters. + * * @return Commit[] */ - public function commits($page = 1, $per_page = Api::PER_PAGE) + public function commits(array $parameters = []) { - return $this->project->commits($page, $per_page, $this->name); + return $this->project->commits($parameters); } /** @@ -112,7 +114,7 @@ class Branch extends AbstractModel */ public function createFile($file_path, $content, $commit_message) { - $data = $this->api('repositories')->createFile($this->project->id, $file_path, $content, $this->name, $commit_message); + $data = $this->client->repositories()->createFile($this->project->id, $file_path, $content, $this->name, $commit_message); return File::fromArray($this->getClient(), $this->project, $data); } @@ -125,7 +127,7 @@ class Branch extends AbstractModel */ public function updateFile($file_path, $content, $commit_message) { - $data = $this->api('repositories')->updateFile($this->project->id, $file_path, $content, $this->name, $commit_message); + $data = $this->client->repositories()->updateFile($this->project->id, $file_path, $content, $this->name, $commit_message); return File::fromArray($this->getClient(), $this->project, $data); } @@ -137,7 +139,7 @@ class Branch extends AbstractModel */ public function deleteFile($file_path, $commit_message) { - $this->api('repositories')->deleteFile($this->project->id, $file_path, $this->name, $commit_message); + $this->client->repositories()->deleteFile($this->project->id, $file_path, $this->name, $commit_message); return true; } diff --git a/vendor/m4tthumphrey/php-gitlab-api/lib/Gitlab/Model/Group.php b/vendor/m4tthumphrey/php-gitlab-api/lib/Gitlab/Model/Group.php index 38891d6c195..48923f18240 100644 --- a/vendor/m4tthumphrey/php-gitlab-api/lib/Gitlab/Model/Group.php +++ b/vendor/m4tthumphrey/php-gitlab-api/lib/Gitlab/Model/Group.php @@ -52,7 +52,7 @@ class Group extends AbstractModel */ public static function create(Client $client, $name, $path) { - $data = $client->api('groups')->create($name, $path); + $data = $client->groups()->create($name, $path); return static::fromArray($client, $data); } @@ -72,7 +72,7 @@ class Group extends AbstractModel */ public function show() { - $data = $this->api('groups')->show($this->id); + $data = $this->client->groups()->show($this->id); return Group::fromArray($this->getClient(), $data); } @@ -83,7 +83,7 @@ class Group extends AbstractModel */ public function transfer($project_id) { - $data = $this->api('groups')->transfer($this->id, $project_id); + $data = $this->client->groups()->transfer($this->id, $project_id); return Group::fromArray($this->getClient(), $data); } @@ -93,7 +93,7 @@ class Group extends AbstractModel */ public function members() { - $data = $this->api('groups')->members($this->id); + $data = $this->client->groups()->members($this->id); $members = array(); foreach ($data as $member) { @@ -110,7 +110,7 @@ class Group extends AbstractModel */ public function addMember($user_id, $access_level) { - $data = $this->api('groups')->addMember($this->id, $user_id, $access_level); + $data = $this->client->groups()->addMember($this->id, $user_id, $access_level); return User::fromArray($this->getClient(), $data); } @@ -121,7 +121,7 @@ class Group extends AbstractModel */ public function removeMember($user_id) { - $this->api('groups')->removeMember($this->id, $user_id); + $this->client->groups()->removeMember($this->id, $user_id); return true; } @@ -131,9 +131,8 @@ class Group extends AbstractModel */ public function projects() { + $data = $this->client->groups()->projects($this->id); - $data = $this->api('groups')->projects($this->id); - - return Group::fromArray($this->getClient(),$data); + return Group::fromArray($this->getClient(), $data); } } diff --git a/vendor/m4tthumphrey/php-gitlab-api/lib/Gitlab/Model/Hook.php b/vendor/m4tthumphrey/php-gitlab-api/lib/Gitlab/Model/Hook.php index 15bda399228..d276982e206 100644 --- a/vendor/m4tthumphrey/php-gitlab-api/lib/Gitlab/Model/Hook.php +++ b/vendor/m4tthumphrey/php-gitlab-api/lib/Gitlab/Model/Hook.php @@ -39,7 +39,7 @@ class Hook extends AbstractModel */ public static function create(Client $client, $url) { - $data = $client->api('system_hooks')->create($url); + $data = $client->systemHooks()->create($url); return static::fromArray($client, $data); } @@ -59,7 +59,7 @@ class Hook extends AbstractModel */ public function test() { - $this->api('system_hooks')->test($this->id); + $this->client->systemHooks()->test($this->id); return true; } @@ -69,7 +69,7 @@ class Hook extends AbstractModel */ public function delete() { - $this->api('system_hooks')->remove($this->id); + $this->client->systemHooks()->remove($this->id); return true; } diff --git a/vendor/m4tthumphrey/php-gitlab-api/lib/Gitlab/Model/Issue.php b/vendor/m4tthumphrey/php-gitlab-api/lib/Gitlab/Model/Issue.php index 86c7e2adda5..9ee7c45742c 100644 --- a/vendor/m4tthumphrey/php-gitlab-api/lib/Gitlab/Model/Issue.php +++ b/vendor/m4tthumphrey/php-gitlab-api/lib/Gitlab/Model/Issue.php @@ -50,7 +50,7 @@ class Issue extends AbstractModel implements Noteable */ public static function fromArray(Client $client, Project $project, array $data) { - $issue = new static($project, $data['id'], $client); + $issue = new static($project, $data['iid'], $client); if (isset($data['author'])) { $data['author'] = User::fromArray($client, $data['author']); @@ -65,14 +65,14 @@ class Issue extends AbstractModel implements Noteable /** * @param Project $project - * @param int $id + * @param int $iid * @param Client $client */ - public function __construct(Project $project, $id = null, Client $client = null) + public function __construct(Project $project, $iid = null, Client $client = null) { $this->setClient($client); $this->setData('project', $project); - $this->setData('id', $id); + $this->setData('iid', $iid); } /** @@ -80,7 +80,7 @@ class Issue extends AbstractModel implements Noteable */ public function show() { - $data = $this->api('issues')->show($this->project->id, $this->id); + $data = $this->client->issues()->show($this->project->id, $this->iid); return static::fromArray($this->getClient(), $this->project, $data); } @@ -91,7 +91,7 @@ class Issue extends AbstractModel implements Noteable */ public function update(array $params) { - $data = $this->api('issues')->update($this->project->id, $this->id, $params); + $data = $this->client->issues()->update($this->project->id, $this->iid, $params); return static::fromArray($this->getClient(), $this->project, $data); } @@ -135,7 +135,7 @@ class Issue extends AbstractModel implements Noteable */ public function addComment($comment) { - $data = $this->api('issues')->addComment($this->project->id, $this->id, array( + $data = $this->client->issues()->addComment($this->project->id, $this->iid, array( 'body' => $comment )); @@ -148,7 +148,7 @@ class Issue extends AbstractModel implements Noteable public function showComments() { $notes = array(); - $data = $this->api('issues')->showComments($this->project->id, $this->id); + $data = $this->client->issues()->showComments($this->project->id, $this->iid); foreach ($data as $note) { $notes[] = Note::fromArray($this->getClient(), $this, $note); diff --git a/vendor/m4tthumphrey/php-gitlab-api/lib/Gitlab/Model/Job.php b/vendor/m4tthumphrey/php-gitlab-api/lib/Gitlab/Model/Job.php new file mode 100644 index 00000000000..586e3a31185 --- /dev/null +++ b/vendor/m4tthumphrey/php-gitlab-api/lib/Gitlab/Model/Job.php @@ -0,0 +1,83 @@ +<?php namespace Gitlab\Model; + +use Gitlab\Client; + +/** + * Class Commit + * + * @property-read Commit $commit + * @property-read int $id + * @property-read string $coverage + * @property-read string $created_at + * @property-read string $artifacts_file + * @property-read string $finished_at + * @property-read string $name + * @property-read Pipeline $pipeline + * @property-read string $ref + * @property-read string $runner + * @property-read string $stage + * @property-read string $started_at + * @property-read string $status + * @property-read string|bool $tag + * @property-read User $user + */ +class Job extends AbstractModel +{ + /** + * @var array + */ + protected static $properties = array( + 'id', + 'commit', + 'coverage', + 'created_at', + 'artifacts_file', + 'finished_at', + 'name', + 'pipeline', + 'ref', + 'runner', + 'stage', + 'started_at', + 'status', + 'tag', + 'user' + ); + + /** + * @param Client $client + * @param Project $project + * @param array $data + * @return Job + */ + public static function fromArray(Client $client, Project $project, array $data) + { + $job = new static($project, $data['id'], $client); + + if (isset($data['user'])) { + $data['user'] = User::fromArray($client, $data['user']); + } + + if (isset($data['commit'])) { + $data['commit'] = Commit::fromArray($client, $project, $data['commit']); + } + + if (isset($data['pipeline'])) { + $data['pipeline'] = Pipeline::fromArray($client, $project, $data['pipeline']); + } + + return $job->hydrate($data); + } + + /** + * @param Project $project + * @param int $id + * @param Client $client + */ + public function __construct(Project $project, $id = null, Client $client = null) + { + $this->setClient($client); + $this->setData('project', $project); + $this->setData('id', $id); + } +} diff --git a/vendor/m4tthumphrey/php-gitlab-api/lib/Gitlab/Model/MergeRequest.php b/vendor/m4tthumphrey/php-gitlab-api/lib/Gitlab/Model/MergeRequest.php index 686b47a7d29..03251f3b01d 100644 --- a/vendor/m4tthumphrey/php-gitlab-api/lib/Gitlab/Model/MergeRequest.php +++ b/vendor/m4tthumphrey/php-gitlab-api/lib/Gitlab/Model/MergeRequest.php @@ -90,14 +90,14 @@ class MergeRequest extends AbstractModel implements Noteable /** * @param Project $project - * @param int $id + * @param int $iid * @param Client $client */ - public function __construct(Project $project, $id = null, Client $client = null) + public function __construct(Project $project, $iid = null, Client $client = null) { $this->setClient($client); $this->setData('project', $project); - $this->setData('id', $id); + $this->setData('iid', $iid); } /** @@ -105,7 +105,7 @@ class MergeRequest extends AbstractModel implements Noteable */ public function show() { - $data = $this->api('mr')->show($this->project->id, $this->id); + $data = $this->client->mergeRequests()->show($this->project->id, $this->iid); return static::fromArray($this->getClient(), $this->project, $data); } @@ -116,7 +116,7 @@ class MergeRequest extends AbstractModel implements Noteable */ public function update(array $params) { - $data = $this->api('mr')->update($this->project->id, $this->id, $params); + $data = $this->client->mergeRequests()->update($this->project->id, $this->iid, $params); return static::fromArray($this->getClient(), $this->project, $data); } @@ -160,7 +160,7 @@ class MergeRequest extends AbstractModel implements Noteable */ public function merge($message = null) { - $data = $this->api('mr')->merge($this->project->id, $this->id, array( + $data = $this->client->mergeRequests()->merge($this->project->id, $this->iid, array( 'merge_commit_message' => $message )); @@ -183,7 +183,7 @@ class MergeRequest extends AbstractModel implements Noteable */ public function addComment($comment) { - $data = $this->api('mr')->addComment($this->project->id, $this->id, $comment); + $data = $this->client->mergeRequests()->addComment($this->project->id, $this->iid, $comment); return Note::fromArray($this->getClient(), $this, $data); } @@ -194,7 +194,7 @@ class MergeRequest extends AbstractModel implements Noteable public function showComments() { $notes = array(); - $data = $this->api('mr')->showComments($this->project->id, $this->id); + $data = $this->client->mergeRequests()->showComments($this->project->id, $this->iid); foreach ($data as $note) { $notes[] = Note::fromArray($this->getClient(), $this, $note); @@ -220,7 +220,7 @@ class MergeRequest extends AbstractModel implements Noteable */ public function changes() { - $data = $this->api('mr')->changes($this->project->id, $this->id); + $data = $this->client->mergeRequests()->changes($this->project->id, $this->iid); return static::fromArray($this->getClient(), $this->project, $data); } diff --git a/vendor/m4tthumphrey/php-gitlab-api/lib/Gitlab/Model/Milestone.php b/vendor/m4tthumphrey/php-gitlab-api/lib/Gitlab/Model/Milestone.php index c59afca38bf..8b07d38af8a 100644 --- a/vendor/m4tthumphrey/php-gitlab-api/lib/Gitlab/Model/Milestone.php +++ b/vendor/m4tthumphrey/php-gitlab-api/lib/Gitlab/Model/Milestone.php @@ -11,6 +11,7 @@ use Gitlab\Client; * @property-read string $title * @property-read string $description * @property-read string $due_date + * @property-read string $start_date * @property-read string $state * @property-read bool $closed * @property-read string $updated_at @@ -30,6 +31,7 @@ class Milestone extends AbstractModel 'title', 'description', 'due_date', + 'start_date', 'state', 'closed', 'updated_at', @@ -66,7 +68,7 @@ class Milestone extends AbstractModel */ public function show() { - $data = $this->api('milestones')->show($this->project->id, $this->id); + $data = $this->client->milestones()->show($this->project->id, $this->id); return static::fromArray($this->getClient(), $this->project, $data); } @@ -77,7 +79,7 @@ class Milestone extends AbstractModel */ public function update(array $params) { - $data = $this->api('milestones')->update($this->project->id, $this->id, $params); + $data = $this->client->milestones()->update($this->project->id, $this->id, $params); return static::fromArray($this->getClient(), $this->project, $data); } @@ -103,7 +105,7 @@ class Milestone extends AbstractModel */ public function issues() { - $data = $this->api('milestones')->issues($this->project->id, $this->id); + $data = $this->client->milestones()->issues($this->project->id, $this->id); $issues = array(); foreach ($data as $issue) { diff --git a/vendor/m4tthumphrey/php-gitlab-api/lib/Gitlab/Model/Pipeline.php b/vendor/m4tthumphrey/php-gitlab-api/lib/Gitlab/Model/Pipeline.php new file mode 100644 index 00000000000..efeccbc9879 --- /dev/null +++ b/vendor/m4tthumphrey/php-gitlab-api/lib/Gitlab/Model/Pipeline.php @@ -0,0 +1,49 @@ +<?php namespace Gitlab\Model; + +use Gitlab\Client; + +/** + * Class Commit + * + * @property-read int $id + * @property-read string $ref + * @property-read string $sha + * @property-read string $status + */ +class Pipeline extends AbstractModel +{ + /** + * @var array + */ + protected static $properties = array( + 'id', + 'ref', + 'sha', + 'status' + ); + + /** + * @param Client $client + * @param Project $project + * @param array $data + * @return Pipeline + */ + public static function fromArray(Client $client, Project $project, array $data) + { + $pipeline = new static($project, $data['id'], $client); + + return $pipeline->hydrate($data); + } + + /** + * @param Project $project + * @param int $id + * @param Client $client + */ + public function __construct(Project $project, $id = null, Client $client = null) + { + $this->setClient($client); + $this->setData('project', $project); + $this->setData('id', $id); + } +} diff --git a/vendor/m4tthumphrey/php-gitlab-api/lib/Gitlab/Model/Project.php b/vendor/m4tthumphrey/php-gitlab-api/lib/Gitlab/Model/Project.php index 44fa9a7eed1..399014b7c63 100644 --- a/vendor/m4tthumphrey/php-gitlab-api/lib/Gitlab/Model/Project.php +++ b/vendor/m4tthumphrey/php-gitlab-api/lib/Gitlab/Model/Project.php @@ -1,8 +1,8 @@ <?php namespace Gitlab\Model; -use Gitlab\Api\MergeRequests; +use Gitlab\Api\Projects; +use Gitlab\Api\Repositories; use Gitlab\Client; -use Gitlab\Api\AbstractApi as Api; /** * Class Project @@ -29,6 +29,7 @@ use Gitlab\Api\AbstractApi as Api; * @property-read int $greatest_access_level * @property-read string $last_activity_at * @property-read string $tag_list + * @property-read string $avatar_url * @property-read User $owner * @property-read ProjectNamespace $namespace */ @@ -61,7 +62,8 @@ class Project extends AbstractModel 'greatest_access_level', 'last_activity_at', 'snippets_enabled', - 'tag_list' + 'tag_list', + 'avatar_url' ); /** @@ -93,7 +95,7 @@ class Project extends AbstractModel */ public static function create(Client $client, $name, array $params = array()) { - $data = $client->api('projects')->create($name, $params); + $data = $client->projects()->create($name, $params); return static::fromArray($client, $data); } @@ -107,7 +109,7 @@ class Project extends AbstractModel */ public static function createForUser($user_id, Client $client, $name, array $params = array()) { - $data = $client->api('projects')->createForUser($user_id, $name, $params); + $data = $client->projects()->createForUser($user_id, $name, $params); return static::fromArray($client, $data); } @@ -126,7 +128,7 @@ class Project extends AbstractModel */ public function show() { - $data = $this->api('projects')->show($this->id); + $data = $this->client->projects()->show($this->id); return static::fromArray($this->getClient(), $data); } @@ -137,7 +139,7 @@ class Project extends AbstractModel */ public function update(array $params) { - $data = $this->api('projects')->update($this->id, $params); + $data = $this->client->projects()->update($this->id, $params); return static::fromArray($this->getClient(), $data); } @@ -147,7 +149,7 @@ class Project extends AbstractModel */ public function archive() { - $data = $this->api("projects")->archive($this->id); + $data = $this->client->projects()->archive($this->id); return static::fromArray($this->getClient(), $data); } @@ -157,7 +159,7 @@ class Project extends AbstractModel */ public function unarchive() { - $data = $this->api("projects")->unarchive($this->id); + $data = $this->client->projects()->unarchive($this->id); return static::fromArray($this->getClient(), $data); } @@ -167,7 +169,7 @@ class Project extends AbstractModel */ public function remove() { - $this->api('projects')->remove($this->id); + $this->client->projects()->remove($this->id); return true; } @@ -178,7 +180,7 @@ class Project extends AbstractModel */ public function members($username_query = null) { - $data = $this->api('projects')->members($this->id, $username_query); + $data = $this->client->projects()->members($this->id, $username_query); $members = array(); foreach ($data as $member) { @@ -194,7 +196,7 @@ class Project extends AbstractModel */ public function member($user_id) { - $data = $this->api('projects')->member($this->id, $user_id); + $data = $this->client->projects()->member($this->id, $user_id); return User::fromArray($this->getClient(), $data); } @@ -206,7 +208,7 @@ class Project extends AbstractModel */ public function addMember($user_id, $access_level) { - $data = $this->api('projects')->addMember($this->id, $user_id, $access_level); + $data = $this->client->projects()->addMember($this->id, $user_id, $access_level); return User::fromArray($this->getClient(), $data); } @@ -218,7 +220,7 @@ class Project extends AbstractModel */ public function saveMember($user_id, $access_level) { - $data = $this->api('projects')->saveMember($this->id, $user_id, $access_level); + $data = $this->client->projects()->saveMember($this->id, $user_id, $access_level); return User::fromArray($this->getClient(), $data); } @@ -229,19 +231,21 @@ class Project extends AbstractModel */ public function removeMember($user_id) { - $this->api('projects')->removeMember($this->id, $user_id); + $this->client->projects()->removeMember($this->id, $user_id); return true; } /** - * @param int $page - * @param int $per_page + * @param array $parameters + * + * @see Projects::hooks() for available parameters. + * * @return ProjectHook[] */ - public function hooks($page = 1, $per_page = Api::PER_PAGE) + public function hooks(array $parameters = []) { - $data = $this->api('projects')->hooks($this->id, $page, $per_page); + $data = $this->client->projects()->hooks($this->id, $parameters); $hooks = array(); foreach ($data as $hook) { @@ -269,7 +273,7 @@ class Project extends AbstractModel */ public function addHook($url, array $events = array()) { - $data = $this->api('projects')->addHook($this->id, $url, $events); + $data = $this->client->projects()->addHook($this->id, $url, $events); return ProjectHook::fromArray($this->getClient(), $this, $data); } @@ -300,9 +304,9 @@ class Project extends AbstractModel /** * @return Key[] */ - public function keys() + public function deployKeys() { - $data = $this->api('projects')->keys($this->id); + $data = $this->client->projects()->deployKeys($this->id); $keys = array(); foreach ($data as $key) { @@ -316,9 +320,9 @@ class Project extends AbstractModel * @param int $key_id * @return Key */ - public function key($key_id) + public function deployKey($key_id) { - $data = $this->api('projects')->key($this->id, $key_id); + $data = $this->client->projects()->deployKey($this->id, $key_id); return Key::fromArray($this->getClient(), $data); } @@ -326,11 +330,12 @@ class Project extends AbstractModel /** * @param string $title * @param string $key + * @param bool $canPush * @return Key */ - public function addKey($title, $key) + public function addDeployKey($title, $key, $canPush = false) { - $data = $this->api('projects')->addKey($this->id, $title, $key); + $data = $this->client->projects()->addDeployKey($this->id, $title, $key, $canPush); return Key::fromArray($this->getClient(), $data); } @@ -339,9 +344,9 @@ class Project extends AbstractModel * @param string $key_id * @return bool */ - public function removeKey($key_id) + public function deleteDeployKey($key_id) { - $this->api('projects')->removeKey($this->id, $key_id); + $this->client->projects()->deleteDeployKey($this->id, $key_id); return true; } @@ -350,20 +355,9 @@ class Project extends AbstractModel * @param string $key_id * @return bool */ - public function enableKey($key_id) + public function enableDeployKey($key_id) { - $this->api('projects')->enableKey($this->id, $key_id); - - return true; - } - - /** - * @param string $key_id - * @return bool - */ - public function disableKey($key_id) - { - $this->api('projects')->disableKey($this->id, $key_id); + $this->client->projects()->enableDeployKey($this->id, $key_id); return true; } @@ -375,7 +369,7 @@ class Project extends AbstractModel */ public function createBranch($name, $ref) { - $data = $this->api('repositories')->createBranch($this->id, $name, $ref); + $data = $this->client->repositories()->createBranch($this->id, $name, $ref); return Branch::fromArray($this->getClient(), $this, $data); } @@ -386,7 +380,7 @@ class Project extends AbstractModel */ public function deleteBranch($name) { - $this->api('repositories')->deleteBranch($this->id, $name); + $this->client->repositories()->deleteBranch($this->id, $name); return true; } @@ -396,7 +390,7 @@ class Project extends AbstractModel */ public function branches() { - $data = $this->api('repo')->branches($this->id); + $data = $this->client->repositories()->branches($this->id); $branches = array(); foreach ($data as $branch) { @@ -449,7 +443,7 @@ class Project extends AbstractModel */ public function tags() { - $data = $this->api('repo')->tags($this->id); + $data = $this->client->repositories()->tags($this->id); $tags = array(); foreach ($data as $tag) { @@ -460,14 +454,15 @@ class Project extends AbstractModel } /** - * @param int $page - * @param int $per_page - * @param string $ref_name + * @param array $parameters + * + * @see Repositories::commits() for available parameters. + * * @return Commit[] */ - public function commits($page = 0, $per_page = Api::PER_PAGE, $ref_name = null) + public function commits(array $parameters = []) { - $data = $this->api('repo')->commits($this->id, $page, $per_page, $ref_name); + $data = $this->client->repositories()->commits($this->id, $parameters); $commits = array(); foreach ($data as $commit) { @@ -483,20 +478,22 @@ class Project extends AbstractModel */ public function commit($sha) { - $data = $this->api('repo')->commit($this->id, $sha); + $data = $this->client->repositories()->commit($this->id, $sha); return Commit::fromArray($this->getClient(), $this, $data); } /** * @param string $ref - * @param int $page - * @param int $per_page + * @param array $parameters + * + * @see Repositories::commitComments() for available parameters. + * * @return Commit[] */ - public function commitComments($ref, $page = 0, $per_page = Api::PER_PAGE) + public function commitComments($ref, array $parameters = []) { - $data = $this->api('repo')->commitComments($this->id, $ref, $page, $per_page); + $data = $this->client->repositories()->commitComments($this->id, $ref, $parameters); $comments = array(); foreach ($data as $comment) { @@ -514,7 +511,7 @@ class Project extends AbstractModel */ public function createCommitComment($ref, $note, array $params = array()) { - $data = $this->api('repo')->createCommitComment($this->id, $ref, $note, $params); + $data = $this->client->repositories()->createCommitComment($this->id, $ref, $note, $params); return CommitNote::fromArray($this->getClient(), $data); } @@ -525,7 +522,7 @@ class Project extends AbstractModel */ public function diff($sha) { - return $this->api('repo')->diff($this->id, $sha); + return $this->client->repositories()->diff($this->id, $sha); } /** @@ -535,7 +532,7 @@ class Project extends AbstractModel */ public function compare($from, $to) { - $data = $this->api('repo')->compare($this->id, $from, $to); + $data = $this->client->repositories()->compare($this->id, $from, $to); return Comparison::fromArray($this->getClient(), $this, $data); } @@ -546,7 +543,7 @@ class Project extends AbstractModel */ public function tree(array $params = array()) { - $data = $this->api('repo')->tree($this->id, $params); + $data = $this->client->repositories()->tree($this->id, $params); $tree = array(); foreach ($data as $node) { @@ -563,7 +560,7 @@ class Project extends AbstractModel */ public function blob($sha, $filepath) { - return $this->api('repo')->blob($this->id, $sha, $filepath); + return $this->client->repositories()->blob($this->id, $sha, $filepath); } /** @@ -574,7 +571,7 @@ class Project extends AbstractModel */ public function getFile($sha, $filepath) { - return $this->api('repo')->getFile($this->id, $filepath, $sha); + return $this->client->repositories()->getFile($this->id, $filepath, $sha); } /** @@ -588,7 +585,22 @@ class Project extends AbstractModel */ public function createFile($file_path, $content, $branch_name, $commit_message, $author_email = null, $author_name = null) { - $data = $this->api('repo')->createFile($this->id, $file_path, $content, $branch_name, $commit_message, null, $author_email, $author_name); + $parameters = [ + 'file_path' => $file_path, + 'branch' => $branch_name, + 'content' => $content, + 'commit_message' => $commit_message, + ]; + + if ($author_email !== null) { + $parameters['author_email'] = $author_email; + } + + if ($author_name !== null) { + $parameters['author_name'] = $author_name; + } + + $data = $this->client->repositoryFiles()->createFile($this->id, $parameters); return File::fromArray($this->getClient(), $this, $data); } @@ -604,7 +616,22 @@ class Project extends AbstractModel */ public function updateFile($file_path, $content, $branch_name, $commit_message, $author_email = null, $author_name = null) { - $data = $this->api('repo')->updateFile($this->id, $file_path, $content, $branch_name, $commit_message, null, $author_email, $author_name); + $parameters = [ + 'file_path' => $file_path, + 'branch' => $branch_name, + 'content' => $content, + 'commit_message' => $commit_message, + ]; + + if ($author_email !== null) { + $parameters['author_email'] = $author_email; + } + + if ($author_name !== null) { + $parameters['author_name'] = $author_name; + } + + $data = $this->client->repositoryFiles()->updateFile($this->id, $parameters); return File::fromArray($this->getClient(), $this, $data); } @@ -619,19 +646,35 @@ class Project extends AbstractModel */ public function deleteFile($file_path, $branch_name, $commit_message, $author_email = null, $author_name = null) { - $this->api('repo')->deleteFile($this->id, $file_path, $branch_name, $commit_message, $author_email, $author_name); + $parameters = [ + 'file_path' => $file_path, + 'branch' => $branch_name, + 'commit_message' => $commit_message, + ]; + + if ($author_email !== null) { + $parameters['author_email'] = $author_email; + } + + if ($author_name !== null) { + $parameters['author_name'] = $author_name; + } + + $this->client->repositoryFiles()->deleteFile($this->id, $parameters); return true; } /** - * @param int $page - * @param int $per_page + * @param array $parameters + * + * @see Projects::events() for available parameters. + * * @return Event[] */ - public function events($page = 1, $per_page = Api::PER_PAGE) + public function events(array $parameters = []) { - $data = $this->api('projects')->events($this->id, $page, $per_page); + $data = $this->client->projects()->events($this->id, $parameters); $events = array(); foreach ($data as $event) { @@ -642,14 +685,15 @@ class Project extends AbstractModel } /** - * @param int $page - * @param int $per_page - * @param string $state + * @param array $parameters + * + * @see MergeRequests::all() for available parameters. + * * @return MergeRequest[] */ - public function mergeRequests($page = 1, $per_page = Api::PER_PAGE, $state = MergeRequests::STATE_ALL) + public function mergeRequests(array $parameters = []) { - $data = $this->api('mr')->$state($this->id, $page, $per_page); + $data = $this->client->mergeRequests()->all($this->id, $parameters); $mrs = array(); foreach ($data as $mr) { @@ -680,7 +724,7 @@ class Project extends AbstractModel */ public function createMergeRequest($source, $target, $title, $assignee = null, $description = null) { - $data = $this->api('mr')->create($this->id, $source, $target, $title, $assignee, null, $description); + $data = $this->client->mergeRequests()->create($this->id, $source, $target, $title, $assignee, $this->id, $description); return MergeRequest::fromArray($this->getClient(), $this, $data); } @@ -731,13 +775,15 @@ class Project extends AbstractModel } /** - * @param int $page - * @param int $per_page + * @param array $parameters + * + * @see Issues::all() for available parameters. + * * @return Issue[] */ - public function issues($page = 1, $per_page = Api::PER_PAGE) + public function issues(array $parameters = []) { - $data = $this->api('issues')->all($this->id, $page, $per_page); + $data = $this->client->issues()->all($this->id, $parameters); $issues = array(); foreach ($data as $issue) { @@ -755,65 +801,67 @@ class Project extends AbstractModel public function createIssue($title, array $params = array()) { $params['title'] = $title; - $data = $this->api('issues')->create($this->id, $params); + $data = $this->client->issues()->create($this->id, $params); return Issue::fromArray($this->getClient(), $this, $data); } /** - * @param int $id + * @param int $iid * @return Issue */ - public function issue($id) + public function issue($iid) { - $issue = new Issue($this, $id, $this->getClient()); + $issue = new Issue($this, $iid, $this->getClient()); return $issue->show(); } /** - * @param int $id + * @param int $iid * @param array $params * @return Issue */ - public function updateIssue($id, array $params) + public function updateIssue($iid, array $params) { - $issue = new Issue($this, $id, $this->getClient()); + $issue = new Issue($this, $iid, $this->getClient()); return $issue->update($params); } /** - * @param int $id + * @param int $iid * @param string $comment * @return Issue */ - public function closeIssue($id, $comment = null) + public function closeIssue($iid, $comment = null) { - $issue = new Issue($this, $id, $this->getClient()); + $issue = new Issue($this, $iid, $this->getClient()); return $issue->close($comment); } /** - * @param int $id + * @param int $iid * @return Issue */ - public function openIssue($id) + public function openIssue($iid) { - $issue = new Issue($this, $id, $this->getClient()); + $issue = new Issue($this, $iid, $this->getClient()); return $issue->open(); } /** - * @param int $page - * @param int $per_page + * @param array $parameters + * + * @see Milestones::all() for available parameters. + * * @return Milestone[] */ - public function milestones($page = 1, $per_page = Api::PER_PAGE) + public function milestones(array $parameters = []) { - $data = $this->api('milestones')->all($this->id, $page, $per_page); + $data = $this->client->milestones()->all($this->id, $parameters); $milestones = array(); foreach ($data as $milestone) { @@ -831,7 +879,7 @@ class Project extends AbstractModel public function createMilestone($title, array $params = array()) { $params['title'] = $title; - $data = $this->api('milestones')->create($this->id, $params); + $data = $this->client->milestones()->create($this->id, $params); return Milestone::fromArray($this->getClient(), $this, $data); } @@ -875,7 +923,7 @@ class Project extends AbstractModel */ public function snippets() { - $data = $this->api('snippets')->all($this->id); + $data = $this->client->snippets()->all($this->id); $snippets = array(); foreach ($data as $snippet) { @@ -889,12 +937,11 @@ class Project extends AbstractModel * @param string $title * @param string $filename * @param string $code - * @param string $lifetime * @return Snippet */ - public function createSnippet($title, $filename, $code, $lifetime = null) + public function createSnippet($title, $filename, $code) { - $data = $this->api('snippets')->create($this->id, $title, $filename, $code, $lifetime); + $data = $this->client->snippets()->create($this->id, $title, $filename, $code); return Snippet::fromArray($this->getClient(), $this, $data); } @@ -912,7 +959,7 @@ class Project extends AbstractModel /** * @param int $id - * @return Snippet + * @return string */ public function snippetContent($id) { @@ -961,7 +1008,7 @@ class Project extends AbstractModel */ public function forkTo($id) { - $data = $this->api('projects')->createForkRelation($id, $this->id); + $data = $this->client->projects()->createForkRelation($id, $this->id); return Project::fromArray($this->getClient(), $data); } @@ -981,7 +1028,7 @@ class Project extends AbstractModel */ public function createForkRelation($id) { - $data = $this->api('projects')->createForkRelation($this->id, $id); + $data = $this->client->projects()->createForkRelation($this->id, $id); return Project::fromArray($this->getClient(), $data); } @@ -991,7 +1038,7 @@ class Project extends AbstractModel */ public function removeForkRelation() { - $this->api('projects')->removeForkRelation($this->id); + $this->client->projects()->removeForkRelation($this->id); return true; } @@ -1003,7 +1050,7 @@ class Project extends AbstractModel */ public function setService($service_name, array $params = array()) { - $this->api('projects')->setService($this->id, $service_name, $params); + $this->client->projects()->setService($this->id, $service_name, $params); return true; } @@ -1014,7 +1061,7 @@ class Project extends AbstractModel */ public function removeService($service_name) { - $this->api('projects')->removeService($this->id, $service_name); + $this->client->projects()->removeService($this->id, $service_name); return true; } @@ -1024,7 +1071,7 @@ class Project extends AbstractModel */ public function labels() { - $data = $this->api('projects')->labels($this->id); + $data = $this->client->projects()->labels($this->id); $labels = array(); foreach ($data as $label) { @@ -1041,7 +1088,7 @@ class Project extends AbstractModel */ public function addLabel($name, $color) { - $data = $this->api('projects')->addLabel($this->id, array( + $data = $this->client->projects()->addLabel($this->id, array( 'name' => $name, 'color' => $color )); @@ -1062,7 +1109,7 @@ class Project extends AbstractModel $params['name'] = $name; - $data = $this->api('projects')->updateLabel($this->id, $params); + $data = $this->client->projects()->updateLabel($this->id, $params); return Label::fromArray($this->getClient(), $this, $data); } @@ -1073,7 +1120,7 @@ class Project extends AbstractModel */ public function removeLabel($name) { - $this->api('projects')->removeLabel($this->id, $name); + $this->client->projects()->removeLabel($this->id, $name); return true; } @@ -1083,7 +1130,7 @@ class Project extends AbstractModel */ public function contributors() { - $data = $this->api('repo')->contributors($this->id); + $data = $this->client->repositories()->contributors($this->id); $contributors = array(); foreach ($data as $contributor) { @@ -1092,4 +1139,48 @@ class Project extends AbstractModel return $contributors; } + + /** + * @param array $scopes + * @return Job[] + */ + public function jobs(array $scopes = []) + { + $data = $this->client->jobs()->all($this->id, $scopes); + + $jobs = array(); + foreach ($data as $job) { + $jobs[] = Job::fromArray($this->getClient(), $this, $job); + } + + return $jobs; + } + + /** + * @param int $pipeline_id + * @param array $scopes + * @return Job[] + */ + public function pipelineJobs($pipeline_id, array $scopes = []) + { + $data = $this->client->jobs()->pipelineJobs($this->id, $pipeline_id, $scopes); + + $jobs = array(); + foreach ($data as $job) { + $jobs[] = Job::fromArray($this->getClient(), $this, $job); + } + + return $jobs; + } + + /** + * @param int $job_id + * @return Job + */ + public function job($job_id) + { + $data = $this->client->jobs()->show($this->id, $job_id); + + return Job::fromArray($this->getClient(), $this, $data); + } } diff --git a/vendor/m4tthumphrey/php-gitlab-api/lib/Gitlab/Model/ProjectHook.php b/vendor/m4tthumphrey/php-gitlab-api/lib/Gitlab/Model/ProjectHook.php index 3da0a7c02ba..1789ca68bbc 100644 --- a/vendor/m4tthumphrey/php-gitlab-api/lib/Gitlab/Model/ProjectHook.php +++ b/vendor/m4tthumphrey/php-gitlab-api/lib/Gitlab/Model/ProjectHook.php @@ -11,8 +11,9 @@ use Gitlab\Client; * @property-read bool $push_events * @property-read bool $issues_events * @property-read bool $merge_requests_events - * @property-read bool $build_events + * @property-read bool $job_events * @property-read bool $tag_push_events + * @property-read bool $pipeline_events * @property-read string $created_at * @property-read Project $project */ @@ -29,8 +30,9 @@ class ProjectHook extends AbstractModel 'push_events', 'issues_events', 'merge_requests_events', - 'build_events', + 'job_events', 'tag_push_events', + 'pipeline_events', 'created_at' ); @@ -64,7 +66,7 @@ class ProjectHook extends AbstractModel */ public function show() { - $data = $this->api('projects')->hook($this->project->id, $this->id); + $data = $this->client->projects()->hook($this->project->id, $this->id); return static::fromArray($this->getClient(), $this->project, $data); } @@ -74,7 +76,7 @@ class ProjectHook extends AbstractModel */ public function delete() { - $this->api('projects')->removeHook($this->project->id, $this->id); + $this->client->projects()->removeHook($this->project->id, $this->id); return true; } @@ -93,7 +95,7 @@ class ProjectHook extends AbstractModel */ public function update(array $params) { - $data = $this->api('projects')->updateHook($this->project->id, $this->id, $params); + $data = $this->client->projects()->updateHook($this->project->id, $this->id, $params); return static::fromArray($this->getClient(), $this->project, $data); } diff --git a/vendor/m4tthumphrey/php-gitlab-api/lib/Gitlab/Model/Session.php b/vendor/m4tthumphrey/php-gitlab-api/lib/Gitlab/Model/Session.php index f9b8177beff..7cf4cfe79c4 100644 --- a/vendor/m4tthumphrey/php-gitlab-api/lib/Gitlab/Model/Session.php +++ b/vendor/m4tthumphrey/php-gitlab-api/lib/Gitlab/Model/Session.php @@ -51,7 +51,7 @@ class Session extends AbstractModel */ public function me() { - $data = $this->api('users')->show(); + $data = $this->client->users()->user(); return User::fromArray($this->getClient(), $data); } @@ -59,11 +59,11 @@ class Session extends AbstractModel /** * @param string $email * @param string $password - * @return $this + * @return Session */ public function login($email, $password) { - $data = $this->api('users')->session($email, $password); + $data = $this->client->users()->session($email, $password); return $this->hydrate($data); } diff --git a/vendor/m4tthumphrey/php-gitlab-api/lib/Gitlab/Model/Snippet.php b/vendor/m4tthumphrey/php-gitlab-api/lib/Gitlab/Model/Snippet.php index 5d20e0dbf72..1f021bcb4f8 100644 --- a/vendor/m4tthumphrey/php-gitlab-api/lib/Gitlab/Model/Snippet.php +++ b/vendor/m4tthumphrey/php-gitlab-api/lib/Gitlab/Model/Snippet.php @@ -8,7 +8,6 @@ use Gitlab\Client; * @property-read int $id * @property-read string $title * @property-read string $file_name - * @property-read string $expires_at * @property-read string $updated_at * @property-read string $created_at * @property-read Project $project @@ -24,7 +23,6 @@ class Snippet extends AbstractModel 'title', 'file_name', 'author', - 'expires_at', 'updated_at', 'created_at', 'project' @@ -64,7 +62,7 @@ class Snippet extends AbstractModel */ public function show() { - $data = $this->api('snippets')->show($this->project->id, $this->id); + $data = $this->client->snippets()->show($this->project->id, $this->id); return static::fromArray($this->getClient(), $this->project, $data); } @@ -75,7 +73,7 @@ class Snippet extends AbstractModel */ public function update(array $params) { - $data = $this->api('snippets')->update($this->project->id, $this->id, $params); + $data = $this->client->snippets()->update($this->project->id, $this->id, $params); return static::fromArray($this->getClient(), $this->project, $data); } @@ -85,7 +83,7 @@ class Snippet extends AbstractModel */ public function content() { - return $this->api('snippets')->content($this->project->id, $this->id); + return $this->client->snippets()->content($this->project->id, $this->id); } /** @@ -93,7 +91,7 @@ class Snippet extends AbstractModel */ public function remove() { - $this->api('snippets')->remove($this->project->id, $this->id); + $this->client->snippets()->remove($this->project->id, $this->id); return true; } diff --git a/vendor/m4tthumphrey/php-gitlab-api/lib/Gitlab/Model/User.php b/vendor/m4tthumphrey/php-gitlab-api/lib/Gitlab/Model/User.php index 5ffffc16e26..ef654756df4 100644 --- a/vendor/m4tthumphrey/php-gitlab-api/lib/Gitlab/Model/User.php +++ b/vendor/m4tthumphrey/php-gitlab-api/lib/Gitlab/Model/User.php @@ -86,7 +86,7 @@ class User extends AbstractModel */ public static function create(Client $client, $email, $password, array $params = array()) { - $data = $client->api('users')->create($email, $password, $params); + $data = $client->users()->create($email, $password, $params); return static::fromArray($client, $data); } @@ -106,7 +106,7 @@ class User extends AbstractModel */ public function show() { - $data = $this->api('users')->show($this->id); + $data = $this->client->users()->show($this->id); return static::fromArray($this->getClient(), $data); } @@ -117,7 +117,7 @@ class User extends AbstractModel */ public function update(array $params) { - $data = $this->api('users')->update($this->id, $params); + $data = $this->client->users()->update($this->id, $params); return static::fromArray($this->getClient(), $data); } @@ -127,7 +127,7 @@ class User extends AbstractModel */ public function remove() { - $this->api('users')->remove($this->id); + $this->client->users()->remove($this->id); return true; } @@ -137,7 +137,7 @@ class User extends AbstractModel */ public function block() { - $this->api('users')->block($this->id); + $this->client->users()->block($this->id); return true; } @@ -147,7 +147,7 @@ class User extends AbstractModel */ public function unblock() { - $this->api('users')->unblock($this->id); + $this->client->users()->unblock($this->id); return true; } @@ -157,7 +157,7 @@ class User extends AbstractModel */ public function keys() { - $data = $this->api('users')->keys(); + $data = $this->client->users()->keys(); $keys = array(); foreach ($data as $key) { @@ -174,7 +174,7 @@ class User extends AbstractModel */ public function createKey($title, $key) { - $data = $this->api('users')->createKey($title, $key); + $data = $this->client->users()->createKey($title, $key); return Key::fromArray($this->getClient(), $data); } @@ -186,7 +186,7 @@ class User extends AbstractModel */ public function createKeyForUser($user_id, $title, $key) { - $data = $this->api('users')->createKeyForUser($user_id, $title, $key); + $data = $this->client->users()->createKeyForUser($user_id, $title, $key); return Key::fromArray($this->getClient(), $data); } @@ -197,7 +197,7 @@ class User extends AbstractModel */ public function removeKey($id) { - $this->api('users')->removeKey($id); + $this->client->users()->removeKey($id); return true; } diff --git a/vendor/m4tthumphrey/php-gitlab-api/lib/Gitlab/ResultPager.php b/vendor/m4tthumphrey/php-gitlab-api/lib/Gitlab/ResultPager.php index 3cfb44d1373..b436305b135 100644 --- a/vendor/m4tthumphrey/php-gitlab-api/lib/Gitlab/ResultPager.php +++ b/vendor/m4tthumphrey/php-gitlab-api/lib/Gitlab/ResultPager.php @@ -1,6 +1,7 @@ <?php namespace Gitlab; use Gitlab\Api\ApiInterface; +use Gitlab\HttpClient\Message\ResponseMediator; /** * Pager class for supporting pagination in Gitlab classes @@ -33,9 +34,7 @@ class ResultPager implements ResultPagerInterface */ public function fetch(ApiInterface $api, $method, array $parameters = array()) { - $result = call_user_func_array(array($api, $method), $parameters); - - return $result; + return call_user_func_array(array($api, $method), $parameters); } /** @@ -44,7 +43,6 @@ class ResultPager implements ResultPagerInterface public function fetchAll(ApiInterface $api, $method, array $parameters = array()) { $result = call_user_func_array(array($api, $method), $parameters); - while ($this->hasNext()) { $result = array_merge($result, $this->fetchNext()); } @@ -105,7 +103,17 @@ class ResultPager implements ResultPagerInterface */ protected function has($key) { - return !empty($this->client->getHttpClient()->getLastResponse()->getPagination()) && isset($this->client->getHttpClient()->getLastResponse()->getPagination()[$key]); + $lastResponse = $this->client->getResponseHistory()->getLastResponse(); + if ($lastResponse == null) { + return false; + } + + $pagination = ResponseMediator::getPagination($lastResponse); + if ($pagination == null) { + return false; + } + + return isset($pagination[$key]); } /** @@ -113,9 +121,12 @@ class ResultPager implements ResultPagerInterface */ protected function get($key) { - if ($this->has($key)) { - $result = $this->client->getHttpClient()->get(strtr($this->client->getHttpClient()->getLastResponse()->getPagination()[$key], array($this->client->getBaseUrl() => '')))->getContent(); - return $result; + if (!$this->has($key)) { + return []; } + + $pagination = ResponseMediator::getPagination($this->client->getResponseHistory()->getLastResponse()); + + return ResponseMediator::getContent($this->client->getHttpClient()->get($pagination[$key])); } } diff --git a/vendor/m4tthumphrey/php-gitlab-api/phpunit.xml.dist b/vendor/m4tthumphrey/php-gitlab-api/phpunit.xml.dist deleted file mode 100644 index e01386f15e1..00000000000 --- a/vendor/m4tthumphrey/php-gitlab-api/phpunit.xml.dist +++ /dev/null @@ -1,25 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> - -<phpunit backupGlobals="false" - backupStaticAttributes="false" - colors="true" - convertErrorsToExceptions="true" - convertNoticesToExceptions="true" - convertWarningsToExceptions="true" - processIsolation="false" - stopOnFailure="false" - syntaxCheck="false" - bootstrap="test/bootstrap.php" - > - <testsuites> - <testsuite name="php-gitlab-api Test Suite"> - <directory>./test/Gitlab/</directory> - </testsuite> - </testsuites> - - <filter> - <whitelist> - <directory suffix=".php">./lib/Gitlab/</directory> - </whitelist> - </filter> -</phpunit> \ No newline at end of file diff --git a/vendor/m4tthumphrey/php-gitlab-api/test/Gitlab/Tests/Api/AbstractApiTest.php b/vendor/m4tthumphrey/php-gitlab-api/test/Gitlab/Tests/Api/AbstractApiTest.php deleted file mode 100644 index aa1eeaea288..00000000000 --- a/vendor/m4tthumphrey/php-gitlab-api/test/Gitlab/Tests/Api/AbstractApiTest.php +++ /dev/null @@ -1,177 +0,0 @@ -<?php namespace Gitlab\Tests\Api; - -use Gitlab\Api\AbstractApi; -use Gitlab\Client; -use Gitlab\HttpClient\Message\Response; - -class AbstractApiTest extends TestCase -{ - /** - * @test - */ - public function shouldPassGETRequestToClient() - { - $response = $this->getResponse('value'); - - $httpClient = $this->getHttpMock(); - $httpClient - ->expects($this->any()) - ->method('get') - ->with('/path', array('param1' => 'param1value'), array('header1' => 'header1value')) - ->will($this->returnValue($response)); - - $client = $this->getClientMock(); - $client->setHttpClient($httpClient); - - $api = $this->getAbstractApiObject($client); - $this->assertEquals('value', $api->get('/path', array('param1' => 'param1value'), array('header1' => 'header1value'))); - } - - /** - * @test - */ - public function shouldPassPOSTRequestToClient() - { - $response = $this->getResponse('value'); - - $httpClient = $this->getHttpMock(); - $httpClient - ->expects($this->any()) - ->method('post') - ->with('/path', array('param1' => 'param1value'), array('header1' => 'header1value')) - ->will($this->returnValue($response)); - - $client = $this->getClientMock(); - $client->setHttpClient($httpClient); - - $api = $this->getAbstractApiObject($client); - $this->assertEquals('value', $api->post('/path', array('param1' => 'param1value'), array('header1' => 'header1value'))); - } - - /** - * @test - */ - public function shouldPassPUTRequestToClient() - { - $response = $this->getResponse('value'); - - $httpClient = $this->getHttpMock(); - $httpClient - ->expects($this->any()) - ->method('put') - ->with('/path', array('param1' => 'param1value'), array('header1' => 'header1value')) - ->will($this->returnValue($response)); - - $client = $this->getClientMock(); - $client->setHttpClient($httpClient); - - $api = $this->getAbstractApiObject($client); - $this->assertEquals('value', $api->put('/path', array('param1' => 'param1value'), array('header1' => 'header1value'))); - } - - /** - * @test - */ - public function shouldPassDELETERequestToClient() - { - $response = $this->getResponse('value'); - - $httpClient = $this->getHttpMock(); - $httpClient - ->expects($this->any()) - ->method('delete') - ->with('/path', array('param1' => 'param1value'), array('header1' => 'header1value')) - ->will($this->returnValue($response)); - - $client = $this->getClientMock(); - $client->setHttpClient($httpClient); - - $api = $this->getAbstractApiObject($client); - $this->assertEquals('value', $api->delete('/path', array('param1' => 'param1value'), array('header1' => 'header1value'))); - } - - /** - * @test - */ - public function shouldPassPATCHRequestToClient() - { - $response = $this->getResponse('value'); - - $httpClient = $this->getHttpMock(); - $httpClient - ->expects($this->any()) - ->method('patch') - ->with('/path', array('param1' => 'param1value'), array('header1' => 'header1value')) - ->will($this->returnValue($response)); - - $client = $this->getClientMock(); - $client->setHttpClient($httpClient); - - $api = $this->getAbstractApiObject($client); - $this->assertEquals('value', $api->patch('/path', array('param1' => 'param1value'), array('header1' => 'header1value'))); - } - - /** - * @param mixed $value - * @return Response - */ - protected function getResponse($value) - { - $response = new Response(); - $response->setContent($value); - - return $response; - } - - /** - * @param Client $client - * @return AbstractApiTestInstance - */ - protected function getAbstractApiObject(Client $client) - { - return new AbstractApiTestInstance($client); - } -} - -class AbstractApiTestInstance extends AbstractApi -{ - /** - * {@inheritDoc} - */ - public function get($path, array $parameters = array(), $requestHeaders = array()) - { - return parent::get($path, $parameters, $requestHeaders); - } - - /** - * {@inheritDoc} - */ - public function post($path, array $parameters = array(), $requestHeaders = array(), array $files = array()) - { - return parent::post($path, $parameters, $requestHeaders, $files); - } - - /** - * {@inheritDoc} - */ - public function patch($path, array $parameters = array(), $requestHeaders = array()) - { - return parent::patch($path, $parameters, $requestHeaders); - } - - /** - * {@inheritDoc} - */ - public function put($path, array $parameters = array(), $requestHeaders = array()) - { - return parent::put($path, $parameters, $requestHeaders); - } - - /** - * {@inheritDoc} - */ - public function delete($path, array $parameters = array(), $requestHeaders = array()) - { - return parent::delete($path, $parameters, $requestHeaders); - } -} diff --git a/vendor/m4tthumphrey/php-gitlab-api/test/Gitlab/Tests/Api/ApiTestCase.php b/vendor/m4tthumphrey/php-gitlab-api/test/Gitlab/Tests/Api/ApiTestCase.php deleted file mode 100644 index a695d97cb80..00000000000 --- a/vendor/m4tthumphrey/php-gitlab-api/test/Gitlab/Tests/Api/ApiTestCase.php +++ /dev/null @@ -1,23 +0,0 @@ -<?php namespace Gitlab\Tests\Api; - -abstract class ApiTestCase extends TestCase -{ - abstract protected function getApiClass(); - - /** - * @param array $methods - * @return \PHPUnit_Framework_MockObject_MockObject|mixed - */ - protected function getApiMock($methods = array()) - { - $client = $this->getClientMock(); - - $methods = array_merge(array('get', 'post', 'postRaw', 'patch', 'delete', 'put', 'head'), $methods); - - return $this->getMockBuilder($this->getApiClass()) - ->setMethods($methods) - ->setConstructorArgs(array($client)) - ->getMock() - ; - } -} diff --git a/vendor/m4tthumphrey/php-gitlab-api/test/Gitlab/Tests/Api/DeployKeysTest.php b/vendor/m4tthumphrey/php-gitlab-api/test/Gitlab/Tests/Api/DeployKeysTest.php deleted file mode 100644 index a17dd312c3d..00000000000 --- a/vendor/m4tthumphrey/php-gitlab-api/test/Gitlab/Tests/Api/DeployKeysTest.php +++ /dev/null @@ -1,51 +0,0 @@ -<?php namespace Gitlab\Tests\Api; - -class DeployKeysTest extends ApiTestCase -{ - /** - * @test - */ - public function shouldGetAllDeployKeys() - { - $expectedArray = $this->getMultipleDeployKeysData(); - - $api = $this->getMultipleDeployKeysRequestMock('deploy_keys', $expectedArray); - - $this->assertEquals($expectedArray, $api->all()); - } - - protected function getMultipleDeployKeysRequestMock($path, $expectedArray = array(), $page = 1, $per_page = 20, $order_by = 'id', $sort = 'asc') - { - $api = $this->getApiMock(); - $api->expects($this->once()) - ->method('get') - ->with($path, array('page' => $page, 'per_page' => $per_page, 'order_by' => $order_by, 'sort' => $sort)) - ->will($this->returnValue($expectedArray)) - ; - - return $api; - } - - protected function getMultipleDeployKeysData() - { - return array( - array( - 'id' => 1, - 'title' => 'Public key', - 'key' => 'ssh-rsa AAAAB3NzaC1yc2EAAAABJQAAAIEAiPWx6WM4lhHNedGfBpPJNPpZ7yKu+dnn1SJejgt4596k6YjzGGphH2TUxwKzxcKDKKezwkpfnxPkSMkuEspGRt/aZZ9wa++Oi7Qkr8prgHc4soW6NUlfDzpvZK2H5E7eQaSeP3SAwGmQKUFHCddNaP0L+hM7zhFNzjFvpaMgJw0=', - 'created_at' => '2013-10-02T10:12:29Z' - ), - array( - 'id' => 3, - 'title' => 'Another Public key', - 'key' => 'ssh-rsa AAAAB3NzaC1yc2EAAAABJQAAAIEAiPWx6WM4lhHNedGfBpPJNPpZ7yKu+dnn1SJejgt4596k6YjzGGphH2TUxwKzxcKDKKezwkpfnxPkSMkuEspGRt/aZZ9wa++Oi7Qkr8prgHc4soW6NUlfDzpvZK2H5E7eQaSeP3SAwGmQKUFHCddNaP0L+hM7zhFNzjFvpaMgJw0=', - 'created_at' => '2013-10-02T11:12:29Z' - ) - ); - } - - protected function getApiClass() - { - return 'Gitlab\Api\DeployKeys'; - } -} diff --git a/vendor/m4tthumphrey/php-gitlab-api/test/Gitlab/Tests/Api/GroupsTest.php b/vendor/m4tthumphrey/php-gitlab-api/test/Gitlab/Tests/Api/GroupsTest.php deleted file mode 100644 index 20fd1fd58f3..00000000000 --- a/vendor/m4tthumphrey/php-gitlab-api/test/Gitlab/Tests/Api/GroupsTest.php +++ /dev/null @@ -1,264 +0,0 @@ -<?php namespace Gitlab\Tests\Api; - -use Gitlab\Api\AbstractApi; - -class GroupsTest extends ApiTestCase -{ - /** - * @test - */ - public function shouldGetAllGroups() - { - $expectedArray = array( - array('id' => 1, 'name' => 'A group'), - array('id' => 2, 'name' => 'Another group'), - ); - - $api = $this->getApiMock(); - $api->expects($this->once()) - ->method('get') - ->with('groups', array('page' => 1, 'per_page' => 10)) - ->will($this->returnValue($expectedArray)) - ; - - $this->assertEquals($expectedArray, $api->all(1, 10)); - } - - /** - * @test - */ - public function shouldNotNeedPaginationWhenGettingGroups() - { - $expectedArray = array( - array('id' => 1, 'name' => 'A group'), - array('id' => 2, 'name' => 'Another group'), - ); - - $api = $this->getApiMock(); - $api->expects($this->once()) - ->method('get') - ->with('groups', array('page' => 1, 'per_page' => AbstractApi::PER_PAGE)) - ->will($this->returnValue($expectedArray)) - ; - - $this->assertEquals($expectedArray, $api->all()); - } - - /** - * @test - */ - public function shouldSearchGroups() - { - $expectedArray = array( - array('id' => 1, 'name' => 'A group'), - array('id' => 2, 'name' => 'Another group'), - ); - - $api = $this->getApiMock(); - $api->expects($this->once()) - ->method('get') - ->with('groups?search=some%20group', array('page' => 1, 'per_page' => AbstractApi::PER_PAGE)) - ->will($this->returnValue($expectedArray)) - ; - - $this->assertEquals($expectedArray, $api->search('some group')); - } - - /** - * @test - */ - public function shouldSearchGroupsWithPagination() - { - $expectedArray = array( - array('id' => 1, 'name' => 'A group'), - array('id' => 2, 'name' => 'Another group'), - ); - - $api = $this->getApiMock(); - $api->expects($this->once()) - ->method('get') - ->with('groups?search=group', array('page' => 2, 'per_page' => 5)) - ->will($this->returnValue($expectedArray)) - ; - - $this->assertEquals($expectedArray, $api->search('group', 2, 5)); - } - - /** - * @test - */ - public function shouldShowGroup() - { - $expectedArray = array('id' => 1, 'name' => 'A group'); - - $api = $this->getApiMock(); - $api->expects($this->once()) - ->method('get') - ->with('groups/1') - ->will($this->returnValue($expectedArray)) - ; - - $this->assertEquals($expectedArray, $api->show(1)); - } - - /** - * @test - */ - public function shouldCreateGroup() - { - $expectedArray = array('id' => 1, 'name' => 'A new group'); - - $api = $this->getApiMock(); - $api->expects($this->once()) - ->method('post') - ->with('groups', array('name' => 'A new group', 'path' => 'a-new-group', 'description' => null, 'visibility_level' => 0)) - ->will($this->returnValue($expectedArray)) - ; - - $this->assertEquals($expectedArray, $api->create('A new group', 'a-new-group')); - } - - /** - * @test - */ - public function shouldCreateGroupWithDescriptionAndVisLevel() - { - $expectedArray = array('id' => 1, 'name' => 'A new group', 'visibility_level' => 2); - - $api = $this->getApiMock(); - $api->expects($this->once()) - ->method('post') - ->with('groups', array('name' => 'A new group', 'path' => 'a-new-group', 'description' => 'Description', 'visibility_level' => 2)) - ->will($this->returnValue($expectedArray)) - ; - - $this->assertEquals($expectedArray, $api->create('A new group', 'a-new-group', 'Description', 2)); - } - - /** - * @test - */ - public function shouldUpdateGroup() - { - $expectedArray = array('id' => 3, 'name' => 'Group name', 'path' => 'group-path'); - - $api = $this->getApiMock(); - $api->expects($this->once()) - ->method('put') - ->with('groups/3', array('name' => 'Group name', 'path' => 'group-path')) - ->will($this->returnValue($expectedArray)) - ; - - $this->assertEquals($expectedArray, $api->update(3, array('name' => 'Group name', 'path' => 'group-path'))); - } - - /** - * @test - */ - public function shouldTransferProjectToGroup() - { - $expectedBool = true; - - $api = $this->getApiMock(); - $api->expects($this->once()) - ->method('post') - ->with('groups/1/projects/2') - ->will($this->returnValue($expectedBool)) - ; - - $this->assertEquals($expectedBool, $api->transfer(1, 2)); - } - - /** - * @test - */ - public function shouldGetMembers() - { - $expectedArray = array( - array('id' => 1, 'name' => 'Matt'), - array('id' => 2, 'name' => 'Bob') - ); - - $api = $this->getApiMock(); - $api->expects($this->once()) - ->method('get') - ->with('groups/1/members') - ->will($this->returnValue($expectedArray)) - ; - - $this->assertEquals($expectedArray, $api->members(1)); - } - - /** - * @test - */ - public function shouldAddMember() - { - $expectedArray = array('id' => 1, 'name' => 'Matt'); - - $api = $this->getApiMock(); - $api->expects($this->once()) - ->method('post') - ->with('groups/1/members', array('user_id' => 2, 'access_level' => 3)) - ->will($this->returnValue($expectedArray)) - ; - - $this->assertEquals($expectedArray, $api->addMember(1, 2, 3)); - } - - /** - * @test - */ - public function shouldSaveMember() - { - $expectedArray = array('id' => 1, 'name' => 'Matt'); - - $api = $this->getApiMock(); - $api->expects($this->once()) - ->method('put') - ->with('groups/1/members/2', array('access_level' => 4)) - ->will($this->returnValue($expectedArray)) - ; - - $this->assertEquals($expectedArray, $api->saveMember(1, 2, 4)); - } - - /** - * @test - */ - public function shouldRemoveMember() - { - $expectedBool = true; - - $api = $this->getApiMock(); - $api->expects($this->once()) - ->method('delete') - ->with('groups/1/members/2') - ->will($this->returnValue($expectedBool)) - ; - - $this->assertEquals($expectedBool, $api->removeMember(1, 2)); - } - - /** - * @test - */ - public function shouldRemoveGroup() - { - $expectedBool = true; - - $api = $this->getApiMock(); - $api->expects($this->once()) - ->method('delete') - ->with('groups/1') - ->will($this->returnValue($expectedBool)) - ; - - $this->assertEquals($expectedBool, $api->remove(1)); - } - - protected function getApiClass() - { - return 'Gitlab\Api\Groups'; - } -} diff --git a/vendor/m4tthumphrey/php-gitlab-api/test/Gitlab/Tests/Api/IssuesTest.php b/vendor/m4tthumphrey/php-gitlab-api/test/Gitlab/Tests/Api/IssuesTest.php deleted file mode 100644 index 8c656f003a2..00000000000 --- a/vendor/m4tthumphrey/php-gitlab-api/test/Gitlab/Tests/Api/IssuesTest.php +++ /dev/null @@ -1,279 +0,0 @@ -<?php namespace Gitlab\Tests\Api; - -use Gitlab\Api\AbstractApi; - -class IssuesTest extends ApiTestCase -{ - /** - * @test - */ - public function shouldGetAllIssues() - { - $expectedArray = array( - array('id' => 1, 'title' => 'An issue'), - array('id' => 2, 'title' => 'Another issue'), - ); - - $api = $this->getApiMock(); - $api->expects($this->once()) - ->method('get') - ->with('issues', array('page' => 1, 'per_page' => AbstractApi::PER_PAGE)) - ->will($this->returnValue($expectedArray)) - ; - - $this->assertEquals($expectedArray, $api->all()); - } - - /** - * @test - */ - public function shouldGetProjectIssuesWithPagination() - { - $expectedArray = array( - array('id' => 1, 'title' => 'An issue'), - array('id' => 2, 'title' => 'Another issue'), - ); - - $api = $this->getApiMock(); - $api->expects($this->once()) - ->method('get') - ->with('projects/1/issues', array('page' => 2, 'per_page' => 5)) - ->will($this->returnValue($expectedArray)) - ; - - $this->assertEquals($expectedArray, $api->all(1, 2, 5)); - } - - /** - * @test - */ - public function shouldGetProjectIssuesWithParams() - { - $expectedArray = array( - array('id' => 1, 'title' => 'An issue'), - array('id' => 2, 'title' => 'Another issue'), - ); - - $api = $this->getApiMock(); - $api->expects($this->once()) - ->method('get') - ->with('projects/1/issues', array('page' => 2, 'per_page' => 5, 'order_by' => 'created_at', 'sort' => 'desc', 'labels' => 'foo,bar', 'state' => 'open')) - ->will($this->returnValue($expectedArray)) - ; - - $this->assertEquals($expectedArray, $api->all(1, 2, 5, array('order_by' => 'created_at', 'sort' => 'desc', 'labels' => 'foo,bar', 'state' => 'open'))); - } - - /** - * @test - */ - public function shouldShowIssue() - { - $expectedArray = array('id' => 2, 'title' => 'Another issue'); - - $api = $this->getApiMock(); - $api->expects($this->once()) - ->method('get') - ->with('projects/1/issues?iid=2') - ->will($this->returnValue($expectedArray)) - ; - - $this->assertEquals($expectedArray, $api->show(1, 2)); - } - - /** - * @test - */ - public function shouldCreateIssue() - { - $expectedArray = array('id' => 3, 'title' => 'A new issue'); - - $api = $this->getApiMock(); - $api->expects($this->once()) - ->method('post') - ->with('projects/1/issues', array('title' => 'A new issue', 'labels' => 'foo,bar')) - ->will($this->returnValue($expectedArray)) - ; - - $this->assertEquals($expectedArray, $api->create(1, array('title' => 'A new issue', 'labels' => 'foo,bar'))); - } - - /** - * @test - */ - public function shouldUpdateIssue() - { - $expectedArray = array('id' => 2, 'title' => 'A renamed issue'); - - $api = $this->getApiMock(); - $api->expects($this->once()) - ->method('put') - ->with('projects/1/issues/2', array('title' => 'A renamed issue', 'labels' => 'foo')) - ->will($this->returnValue($expectedArray)) - ; - - $this->assertEquals($expectedArray, $api->update(1, 2, array('title' => 'A renamed issue', 'labels' => 'foo'))); - } - - /** - * @test - */ - public function shouldGetIssueComments() - { - $expectedArray = array( - array('id' => 1, 'body' => 'A comment'), - array('id' => 2, 'body' => 'Another comment') - ); - - $api = $this->getApiMock(); - $api->expects($this->once()) - ->method('get') - ->with('projects/1/issues/2/notes') - ->will($this->returnValue($expectedArray)) - ; - - $this->assertEquals($expectedArray, $api->showComments(1, 2)); - } - - /** - * @test - */ - public function shouldGetIssueComment() - { - $expectedArray = array('id' => 3, 'body' => 'A new comment'); - - $api = $this->getApiMock(); - $api->expects($this->once()) - ->method('get') - ->with('projects/1/issues/2/notes/3') - ->will($this->returnValue($expectedArray)) - ; - - $this->assertEquals($expectedArray, $api->showComment(1, 2, 3)); - } - - /** - * @test - */ - public function shouldCreateComment() - { - $expectedArray = array('id' => 3, 'body' => 'A new comment'); - - $api = $this->getApiMock(); - $api->expects($this->exactly(2)) - ->method('post') - ->with('projects/1/issues/2/notes', array('body' => 'A new comment')) - ->will($this->returnValue($expectedArray)) - ; - - $this->assertEquals($expectedArray, $api->addComment(1, 2, array('body' => 'A new comment'))); - $this->assertEquals($expectedArray, $api->addComment(1, 2, 'A new comment')); - } - - /** - * @test - */ - public function shouldUpdateComment() - { - $expectedArray = array('id' => 3, 'body' => 'An edited comment'); - - $api = $this->getApiMock(); - $api->expects($this->once()) - ->method('put') - ->with('projects/1/issues/2/notes/3', array('body' => 'An edited comment')) - ->will($this->returnValue($expectedArray)) - ; - - $this->assertEquals($expectedArray, $api->updateComment(1, 2, 3, 'An edited comment')); - } - - /** - * @test - */ - public function shouldSetTimeEstimate() - { - $expectedArray = array('time_estimate' => 14400, 'total_time_spent' => 0, 'human_time_estimate' => '4h', 'human_total_time_spent' => null); - - $api = $this->getApiMock(); - $api->expects($this->once()) - ->method('post') - ->with('projects/1/issues/2/time_estimate', array('duration' => '4h')) - ->will($this->returnValue($expectedArray)) - ; - - $this->assertEquals($expectedArray, $api->setTimeEstimate(1, 2, '4h')); - } - - /** - * @test - */ - public function shouldResetTimeEstimate() - { - $expectedArray = array('time_estimate' => 0, 'total_time_spent' => 0, 'human_time_estimate' => null, 'human_total_time_spent' => null); - - $api = $this->getApiMock(); - $api->expects($this->once()) - ->method('post') - ->with('projects/1/issues/2/reset_time_estimate') - ->will($this->returnValue($expectedArray)) - ; - - $this->assertEquals($expectedArray, $api->resetTimeEstimate(1, 2)); - } - - /** - * @test - */ - public function shouldAddSpentTime() - { - $expectedArray = array('time_estimate' => 0, 'total_time_spent' => 14400, 'human_time_estimate' => null, 'human_total_time_spent' => '4h'); - - $api = $this->getApiMock(); - $api->expects($this->once()) - ->method('post') - ->with('projects/1/issues/2/add_spent_time', array('duration' => '4h')) - ->will($this->returnValue($expectedArray)) - ; - - $this->assertEquals($expectedArray, $api->addSpentTime(1, 2, '4h')); - } - - /** - * @test - */ - public function shouldResetSpentTime() - { - $expectedArray = array('time_estimate' => 0, 'total_time_spent' => 0, 'human_time_estimate' => null, 'human_total_time_spent' => null); - - $api = $this->getApiMock(); - $api->expects($this->once()) - ->method('post') - ->with('projects/1/issues/2/reset_spent_time') - ->will($this->returnValue($expectedArray)) - ; - - $this->assertEquals($expectedArray, $api->resetSpentTime(1, 2)); - } - - /** - * @test - */ - public function shouldGetIssueTimeStats() - { - $expectedArray = array('time_estimate' => 14400, 'total_time_spent' => 5400, 'human_time_estimate' => '4h', 'human_total_time_spent' => '1h 30m'); - - $api = $this->getApiMock(); - $api->expects($this->once()) - ->method('get') - ->with('projects/1/issues/2/time_stats') - ->will($this->returnValue($expectedArray)) - ; - - $this->assertEquals($expectedArray, $api->getTimeStats(1, 2)); - } - - protected function getApiClass() - { - return 'Gitlab\Api\Issues'; - } -} diff --git a/vendor/m4tthumphrey/php-gitlab-api/test/Gitlab/Tests/Api/KeysTest.php b/vendor/m4tthumphrey/php-gitlab-api/test/Gitlab/Tests/Api/KeysTest.php deleted file mode 100644 index 2f9e94b7a58..00000000000 --- a/vendor/m4tthumphrey/php-gitlab-api/test/Gitlab/Tests/Api/KeysTest.php +++ /dev/null @@ -1,26 +0,0 @@ -<?php namespace Gitlab\Tests\Api; - -use Gitlab\Api\AbstractApi; - -class KeysTest extends ApiTestCase -{ - /** - * @test - */ - public function shouldShowKey() - { - $expectedArray = array('id' => 1, 'title' => 'A key', 'key' => 'ssh-rsa key', 'created_at' => '2016-01-01T01:00:00.000Z'); - $api = $this->getApiMock(); - $api->expects($this->once()) - ->method('get') - ->with('keys/1') - ->will($this->returnValue($expectedArray)); - - $this->assertEquals($expectedArray, $api->show(1)); - } - - protected function getApiClass() - { - return 'Gitlab\Api\Keys'; - } -} diff --git a/vendor/m4tthumphrey/php-gitlab-api/test/Gitlab/Tests/Api/MergeRequestsTest.php b/vendor/m4tthumphrey/php-gitlab-api/test/Gitlab/Tests/Api/MergeRequestsTest.php deleted file mode 100644 index bd6e6eb2b70..00000000000 --- a/vendor/m4tthumphrey/php-gitlab-api/test/Gitlab/Tests/Api/MergeRequestsTest.php +++ /dev/null @@ -1,368 +0,0 @@ -<?php namespace Gitlab\Tests\Api; - -use Gitlab\Api\AbstractApi; -use Gitlab\Api\MergeRequests; - -class MergeRequestsTest extends ApiTestCase -{ - /** - * @test - */ - public function shouldGetMergeRequestListWithDefaultParams() - { - $expectedArray = $this->getMultipleMergeRequestsData(); - - $api = $this->getApiMock(); - $api->expects($this->once()) - ->method('get') - ->with('projects/1/merge_requests', array('page' => 1, 'per_page' => AbstractApi::PER_PAGE, 'state' => MergeRequests::STATE_ALL, 'order_by' => MergeRequests::ORDER_BY, 'sort' => MergeRequests::SORT)) - ->will($this->returnValue($expectedArray)) - ; - - $this->assertEquals($expectedArray, $api->getList(1)); - } - - /** - * @test - */ - public function shouldGetAll() - { - $expectedArray = $this->getMultipleMergeRequestsData(); - - $api = $this->getApiMock(array('getList')); - $api->expects($this->once()) - ->method('getList') - ->with(1, MergeRequests::STATE_ALL, 1, AbstractApi::PER_PAGE, MergeRequests::ORDER_BY, MergeRequests::SORT) - ->will($this->returnValue($expectedArray)) - ; - - $this->assertEquals($expectedArray, $api->all(1)); - } - - /** - * @test - */ - public function shouldGetAllWithParams() - { - $expectedArray = $this->getMultipleMergeRequestsData(); - - $api = $this->getApiMock(array('getList')); - $api->expects($this->once()) - ->method('getList') - ->with(1, MergeRequests::STATE_ALL, 2, 5, 'title', 'desc') - ->will($this->returnValue($expectedArray)) - ; - - $this->assertEquals($expectedArray, $api->all(1, 2, 5, 'title', 'desc')); - } - - /** - * @test - */ - public function shouldGetMerged() - { - $expectedArray = $this->getMultipleMergeRequestsData(); - - $api = $this->getApiMock(array('getList')); - $api->expects($this->once()) - ->method('getList') - ->with(1, MergeRequests::STATE_MERGED, 1, AbstractApi::PER_PAGE, MergeRequests::ORDER_BY, MergeRequests::SORT) - ->will($this->returnValue($expectedArray)) - ; - - $this->assertEquals($expectedArray, $api->merged(1)); - } - - /** - * @test - */ - public function shouldGetMergedWithParams() - { - $expectedArray = $this->getMultipleMergeRequestsData(); - - $api = $this->getApiMock(array('getList')); - $api->expects($this->once()) - ->method('getList') - ->with(1, MergeRequests::STATE_MERGED, 3, 15, 'updated_at', 'asc') - ->will($this->returnValue($expectedArray)) - ; - - $this->assertEquals($expectedArray, $api->merged(1, 3, 15, 'updated_at', 'asc')); - } - - /** - * @test - */ - public function shouldGetOpened() - { - $expectedArray = $this->getMultipleMergeRequestsData(); - - $api = $this->getApiMock(array('getList')); - $api->expects($this->once()) - ->method('getList') - ->with(1, MergeRequests::STATE_OPENED, 1, AbstractApi::PER_PAGE, MergeRequests::ORDER_BY, MergeRequests::SORT) - ->will($this->returnValue($expectedArray)) - ; - - $this->assertEquals($expectedArray, $api->opened(1)); - } - - /** - * @test - */ - public function shouldGetOpenedWithParams() - { - $expectedArray = $this->getMultipleMergeRequestsData(); - - $api = $this->getApiMock(array('getList')); - $api->expects($this->once()) - ->method('getList') - ->with(1, MergeRequests::STATE_OPENED, 2, 4, 'title', 'desc') - ->will($this->returnValue($expectedArray)) - ; - - $this->assertEquals($expectedArray, $api->opened(1, 2, 4, 'title', 'desc')); - } - - /** - * @test - */ - public function shouldGetClosed() - { - $expectedArray = $this->getMultipleMergeRequestsData(); - - $api = $this->getApiMock(array('getList')); - $api->expects($this->once()) - ->method('getList') - ->with(1, MergeRequests::STATE_CLOSED, 1, AbstractApi::PER_PAGE, MergeRequests::ORDER_BY, MergeRequests::SORT) - ->will($this->returnValue($expectedArray)) - ; - - $this->assertEquals($expectedArray, $api->closed(1)); - } - - /** - * @test - */ - public function shouldGetClosedWithParams() - { - $expectedArray = $this->getMultipleMergeRequestsData(); - - $api = $this->getApiMock(array('getList')); - $api->expects($this->once()) - ->method('getList') - ->with(1, MergeRequests::STATE_CLOSED, 2, 4, 'title', 'desc') - ->will($this->returnValue($expectedArray)) - ; - - $this->assertEquals($expectedArray, $api->closed(1, 2, 4, 'title', 'desc')); - } - - /** - * @test - */ - public function shouldShowMergeRequest() - { - $expectedArray = array('id' => 2, 'name' => 'A merge request'); - - $api = $this->getApiMock(); - $api->expects($this->once()) - ->method('get') - ->with('projects/1/merge_request/2') - ->will($this->returnValue($expectedArray)) - ; - - $this->assertEquals($expectedArray, $api->show(1, 2)); - } - - /** - * @test - */ - public function shouldCreateMergeRequestWithoutOptionalParams() - { - $expectedArray = array('id' => 3, 'title' => 'Merge Request'); - - $api = $this->getApiMock(); - $api->expects($this->once()) - ->method('post') - ->with('projects/1/merge_requests', array( - 'title' => 'Merge Request', - 'target_branch' => 'master', - 'source_branch' => 'develop', - 'description' => null, - 'assignee_id' => null, - 'target_project_id' => null - )) - ->will($this->returnValue($expectedArray)) - ; - - $this->assertEquals($expectedArray, $api->create(1, 'develop', 'master', 'Merge Request')); - } - - /** - * @test - */ - public function shouldCreateMergeRequestWithOptionalParams() - { - $expectedArray = array('id' => 3, 'title' => 'Merge Request'); - - $api = $this->getApiMock(); - $api->expects($this->once()) - ->method('post') - ->with('projects/1/merge_requests', array( - 'title' => 'Merge Request', - 'target_branch' => 'master', - 'source_branch' => 'develop', - 'description' => 'Some changes', - 'assignee_id' => 6, - 'target_project_id' => 20 - )) - ->will($this->returnValue($expectedArray)) - ; - - $this->assertEquals($expectedArray, $api->create(1, 'develop', 'master', 'Merge Request', 6, 20, 'Some changes')); - } - - /** - * @test - */ - public function shouldUpdateMergeRequest() - { - $expectedArray = array('id' => 2, 'title' => 'Updated title'); - - $api = $this->getApiMock(); - $api->expects($this->once()) - ->method('put') - ->with('projects/1/merge_request/2', array('title' => 'Updated title', 'description' => 'No so many changes now', 'state_event' => 'close')) - ->will($this->returnValue($expectedArray)) - ; - - $this->assertEquals($expectedArray, $api->update(1, 2, array( - 'title' => 'Updated title', - 'description' => 'No so many changes now', - 'state_event' => 'close' - ))); - } - - /** - * @test - */ - public function shouldMergeMergeRequest() - { - $expectedArray = array('id' => 2, 'title' => 'Updated title'); - - $api = $this->getApiMock(); - $api->expects($this->exactly(2)) - ->method('put') - ->with('projects/1/merge_request/2/merge', array('merge_commit_message' => 'Accepted')) - ->will($this->returnValue($expectedArray)) - ; - - $this->assertEquals($expectedArray, $api->merge(1, 2, 'Accepted')); - $this->assertEquals($expectedArray, $api->merge(1, 2, array('merge_commit_message' => 'Accepted'))); - } - - /** - * @test - */ - public function shouldGetMergeRequestNotes() - { - $expectedArray = array( - array('id' => 1, 'body' => 'A comment'), - array('id' => 2, 'body' => 'Another comment') - ); - - $api = $this->getApiMock(); - $api->expects($this->once()) - ->method('get') - ->with('projects/1/merge_requests/2/notes') - ->will($this->returnValue($expectedArray)) - ; - - $this->assertEquals($expectedArray, $api->showNotes(1, 2)); - } - - /** - * @test - */ - public function shouldGetMergeRequestComments() - { - $expectedArray = array( - array('id' => 1, 'note' => 'A comment'), - array('id' => 2, 'note' => 'Another comment') - ); - - $api = $this->getApiMock(); - $api->expects($this->once()) - ->method('get') - ->with('projects/1/merge_request/2/comments') - ->will($this->returnValue($expectedArray)) - ; - - $this->assertEquals($expectedArray, $api->showComments(1, 2)); - } - - /** - * @test - */ - public function shouldAddMergeRequestComment() - { - $expectedArray = array('id' => 2, 'title' => 'A comment'); - - $api = $this->getApiMock(); - $api->expects($this->once()) - ->method('post') - ->with('projects/1/merge_request/2/comments', array('note' => 'A comment')) - ->will($this->returnValue($expectedArray)) - ; - - $this->assertEquals($expectedArray, $api->addComment(1, 2, 'A comment')); - } - - /** - * @test - */ - public function shouldGetMergeRequestChanges() - { - $expectedArray = array('id' => 1, 'title' => 'A merge request'); - - $api = $this->getApiMock(); - $api->expects($this->once()) - ->method('get') - ->with('projects/1/merge_request/2/changes') - ->will($this->returnValue($expectedArray)) - ; - - $this->assertEquals($expectedArray, $api->changes(1, 2)); - } - - /** - * @test - */ - public function shouldGetMergeRequestByIid() - { - $expectedArray = array('id' => 1, 'title' => 'A merge request'); - - $api = $this->getApiMock(); - $api->expects($this->once()) - ->method('get') - ->with('projects/1/merge_requests', array('iid' => 2)) - ->will($this->returnValue($expectedArray)) - ; - - $this->assertEquals($expectedArray, $api->getByIid(1, 2)); - } - - protected function getMultipleMergeRequestsData() - { - return array( - array('id' => 1, 'title' => 'A merge request'), - array('id' => 2, 'title' => 'Another merge request') - ); - } - - protected function getApiClass() - { - return 'Gitlab\Api\MergeRequests'; - } -} diff --git a/vendor/m4tthumphrey/php-gitlab-api/test/Gitlab/Tests/Api/MilestonesTest.php b/vendor/m4tthumphrey/php-gitlab-api/test/Gitlab/Tests/Api/MilestonesTest.php deleted file mode 100644 index e22c2370ff9..00000000000 --- a/vendor/m4tthumphrey/php-gitlab-api/test/Gitlab/Tests/Api/MilestonesTest.php +++ /dev/null @@ -1,100 +0,0 @@ -<?php namespace Gitlab\Tests\Api; - -class MilestonesTest extends ApiTestCase -{ - /** - * @test - */ - public function shouldGetAllMilestones() - { - $expectedArray = array( - array('id' => 1, 'title' => 'A milestone'), - array('id' => 2, 'title' => 'Another milestone'), - ); - - $api = $this->getApiMock(); - $api->expects($this->once()) - ->method('get') - ->with('projects/1/milestones') - ->will($this->returnValue($expectedArray)) - ; - - $this->assertEquals($expectedArray, $api->all(1)); - } - - /** - * @test - */ - public function shouldShowMilestone() - { - $expectedArray = array('id' => 1, 'name' => 'A milestone'); - - $api = $this->getApiMock(); - $api->expects($this->once()) - ->method('get') - ->with('projects/1/milestones/2') - ->will($this->returnValue($expectedArray)) - ; - - $this->assertEquals($expectedArray, $api->show(1, 2)); - } - - /** - * @test - */ - public function shouldCreateMilestone() - { - $expectedArray = array('id' => 3, 'title' => 'A new milestone'); - - $api = $this->getApiMock(); - $api->expects($this->once()) - ->method('post') - ->with('projects/1/milestones', array('description' => 'Some text', 'title' => 'A new milestone')) - ->will($this->returnValue($expectedArray)) - ; - - $this->assertEquals($expectedArray, $api->create(1, array('description' => 'Some text', 'title' => 'A new milestone'))); - } - - /** - * @test - */ - public function shouldUpdateMilestone() - { - $expectedArray = array('id' => 3, 'title' => 'Updated milestone'); - - $api = $this->getApiMock(); - $api->expects($this->once()) - ->method('put') - ->with('projects/1/milestones/3', array('title' => 'Updated milestone', 'due_date' => '2015-04-01', 'state_event' => 'close')) - ->will($this->returnValue($expectedArray)) - ; - - $this->assertEquals($expectedArray, $api->update(1, 3, array('title' => 'Updated milestone', 'due_date' => '2015-04-01', 'state_event' => 'close'))); - } - - /** - * @test - */ - public function shouldGetMilestonesIssues() - { - $expectedArray = array( - array('id' => 1, 'title' => 'An issue'), - array('id' => 2, 'title' => 'Another issue'), - ); - - $api = $this->getApiMock(); - $api->expects($this->once()) - ->method('get') - ->with('projects/1/milestones/3/issues') - ->will($this->returnValue($expectedArray)) - ; - - $this->assertEquals($expectedArray, $api->issues(1, 3)); - } - - protected function getApiClass() - { - return 'Gitlab\Api\Milestones'; - } -} diff --git a/vendor/m4tthumphrey/php-gitlab-api/test/Gitlab/Tests/Api/ProjectNamespacesTest.php b/vendor/m4tthumphrey/php-gitlab-api/test/Gitlab/Tests/Api/ProjectNamespacesTest.php deleted file mode 100644 index 1feb5ff1f41..00000000000 --- a/vendor/m4tthumphrey/php-gitlab-api/test/Gitlab/Tests/Api/ProjectNamespacesTest.php +++ /dev/null @@ -1,70 +0,0 @@ -<?php namespace Gitlab\Tests\Api; - -use Gitlab\Api\AbstractApi; - -class ProjectNamespacesTest extends ApiTestCase -{ - /** - * @test - */ - public function shouldGetAllNamespaces() - { - $expectedArray = array( - array('id' => 1, 'name' => 'bespokes'), - array('id' => 2, 'name' => 'internal') - ); - - $api = $this->getApiMock(); - $api->expects($this->once()) - ->method('get') - ->with('namespaces', array('page' => 1, 'per_page' => 10)) - ->will($this->returnValue($expectedArray)) - ; - - $this->assertEquals($expectedArray, $api->all(1, 10)); - } - - /** - * @test - */ - public function shouldNotNeedPaginationWhenGettingNamespaces() - { - $expectedArray = array( - array('id' => 1, 'name' => 'bespokes'), - array('id' => 2, 'name' => 'internal') - ); - - $api = $this->getApiMock(); - $api->expects($this->once()) - ->method('get') - ->with('namespaces', array('page' => 1, 'per_page' => AbstractApi::PER_PAGE)) - ->will($this->returnValue($expectedArray)) - ; - - $this->assertEquals($expectedArray, $api->all()); - } - /** - * @test - */ - public function shouldSearchNamespaces() - { - $expectedArray = array( - array('id' => 1, 'name' => 'bespokes'), - array('id' => 2, 'name' => 'internal') - ); - - $api = $this->getApiMock(); - $api->expects($this->once()) - ->method('get') - ->with('namespaces', array('search' => 'term', 'page' => 1, 'per_page' => 10)) - ->will($this->returnValue($expectedArray)) - ; - - $this->assertEquals($expectedArray, $api->search('term', 1, 10)); - } - - protected function getApiClass() - { - return 'Gitlab\Api\ProjectNamespaces'; - } -} diff --git a/vendor/m4tthumphrey/php-gitlab-api/test/Gitlab/Tests/Api/ProjectsTest.php b/vendor/m4tthumphrey/php-gitlab-api/test/Gitlab/Tests/Api/ProjectsTest.php deleted file mode 100644 index 47bb26432f1..00000000000 --- a/vendor/m4tthumphrey/php-gitlab-api/test/Gitlab/Tests/Api/ProjectsTest.php +++ /dev/null @@ -1,1098 +0,0 @@ -<?php namespace Gitlab\Tests\Api; - -use Gitlab\Api\AbstractApi; -use Gitlab\Api\Projects; - -class ProjectsTest extends ApiTestCase -{ - /** - * @test - */ - public function shouldGetAllProjects() - { - $expectedArray = $this->getMultipleProjectsData(); - - $api = $this->getMultipleProjectsRequestMock('projects/all', $expectedArray); - - $this->assertEquals($expectedArray, $api->all()); - } - - /** - * @test - */ - public function shouldGetAllProjectsSortedByName() - { - $expectedArray = $this->getMultipleProjectsData(); - - $api = $this->getMultipleProjectsRequestMock('projects/all', $expectedArray, 1, 5, 'name', 'asc'); - - $this->assertEquals($expectedArray, $api->all(1, 5, 'name')); - } - - /** - * @test - */ - public function shouldNotNeedPaginationWhenGettingProjects() - { - $expectedArray = $this->getMultipleProjectsData(); - - $api = $this->getApiMock(); - $api->expects($this->once()) - ->method('get') - ->with('projects/all', array('page' => 1, 'per_page' => AbstractApi::PER_PAGE, 'order_by' => Projects::ORDER_BY, 'sort' => Projects::SORT)) - ->will($this->returnValue($expectedArray)) - ; - - $this->assertEquals($expectedArray, $api->all()); - } - - /** - * @test - */ - public function shouldGetAccessibleProjects() - { - $expectedArray = $this->getMultipleProjectsData(); - - $api = $this->getMultipleProjectsRequestMock('projects', $expectedArray, 2, 7); - - $this->assertEquals($expectedArray, $api->accessible(2, 7)); - } - - /** - * @test - */ - public function shouldGetOwnedProjects() - { - $expectedArray = $this->getMultipleProjectsData(); - - $api = $this->getMultipleProjectsRequestMock('projects?owned=1', $expectedArray, 3, 50); - - $this->assertEquals($expectedArray, $api->owned(3, 50)); - } - - - /** - * @test - */ - public function shouldSearchProjects() - { - $expectedArray = $this->getMultipleProjectsData(); - - $api = $this->getMultipleProjectsRequestMock('projects/search/a%20project', $expectedArray); - $this->assertEquals($expectedArray, $api->search('a project')); - - $api = $this->getMultipleProjectsRequestMock('projects/search/a%2Eproject', $expectedArray); - $this->assertEquals($expectedArray, $api->search('a.project')); - - $api = $this->getMultipleProjectsRequestMock('projects/search/a%2Fproject', $expectedArray); - $this->assertEquals($expectedArray, $api->search('a/project')); - } - - /** - * @test - */ - public function shouldShowProject() - { - $expectedArray = array('id' => 1, 'name' => 'Project Name'); - - $api = $this->getApiMock(); - $api->expects($this->once()) - ->method('get') - ->with('projects/1') - ->will($this->returnValue($expectedArray)) - ; - - $this->assertEquals($expectedArray, $api->show(1)); - } - - /** - * @test - */ - public function shouldCreateProject() - { - $expectedArray = array('id' => 1, 'name' => 'Project Name'); - - $api = $this->getApiMock(); - $api->expects($this->once()) - ->method('post') - ->with('projects', array('name' => 'Project Name', 'issues_enabled' => true)) - ->will($this->returnValue($expectedArray)) - ; - - $this->assertEquals($expectedArray, $api->create('Project Name', array( - 'issues_enabled' => true - ))); - } - - /** - * @test - */ - public function shouldUpdateProject() - { - $expectedArray = array('id' => 1, 'name' => 'Updated Name'); - - $api = $this->getApiMock(); - $api->expects($this->once()) - ->method('put') - ->with('projects/1', array('name' => 'Updated Name', 'issues_enabled' => true)) - ->will($this->returnValue($expectedArray)) - ; - - $this->assertEquals($expectedArray, $api->update(1, array( - 'name' => 'Updated Name', - 'issues_enabled' => true - ))); - } - - /** - * @test - */ - public function shouldArchiveProject() - { - $expectedArray = array('id' => 1, 'archived' => true); - - $api = $this->getApiMock(); - $api->expects($this->once()) - ->method('post') - ->with('projects/1/archive') - ->will($this->returnValue($expectedArray)) - ; - - $this->assertEquals($expectedArray, $api->archive(1)); - } - - /** - * @test - */ - public function shouldUnarchiveProject() - { - $expectedArray = array('id' => 1, 'archived' => false); - - $api = $this->getApiMock(); - $api->expects($this->once()) - ->method('post') - ->with('projects/1/unarchive') - ->will($this->returnValue($expectedArray)) - ; - - $this->assertEquals($expectedArray, $api->unarchive(1)); - } - - /** - * @test - */ - public function shouldCreateProjectForUser() - { - $expectedArray = array('id' => 1, 'name' => 'Project Name'); - - $api = $this->getApiMock(); - $api->expects($this->once()) - ->method('post') - ->with('projects/user/1', array('name' => 'Project Name', 'issues_enabled' => true)) - ->will($this->returnValue($expectedArray)) - ; - - $this->assertEquals($expectedArray, $api->createForUser(1, 'Project Name', array( - 'issues_enabled' => true - ))); - } - - /** - * @test - */ - public function shouldRemoveProject() - { - $expectedBool = true; - - $api = $this->getApiMock(); - $api->expects($this->once()) - ->method('delete') - ->with('projects/1') - ->will($this->returnValue($expectedBool)) - ; - - $this->assertEquals($expectedBool, $api->remove(1)); - } - - /** - * @test - */ - public function shouldGetBuilds() - { - $expectedArray = array( - array('id' => 1, 'status' => 'success'), - array('id' => 2, 'status' => 'failed') - ); - - $api = $this->getApiMock(); - $api->expects($this->once()) - ->method('get') - ->with('projects/1/builds') - ->will($this->returnValue($expectedArray)) - ; - - $this->assertEquals($expectedArray, $api->builds(1)); - } - - /** - * @test - */ - public function shouldGetBuildsWithScope() - { - $expectedArray = array( - array('id' => 1, 'status' => 'success'), - ); - - $api = $this->getApiMock(); - $api->expects($this->once()) - ->method('get') - ->with('projects/1/builds', array('scope' => 'success')) - ->will($this->returnValue($expectedArray)) - ; - - $this->assertEquals($expectedArray, $api->builds(1, 'success')); - } - - /** - * @test - */ - public function shouldGetBuildsWithMultipleScopes() - { - $expectedArray = array( - array('id' => 1, 'status' => 'success'), - array('id' => 1, 'status' => 'failed'), - ); - - $api = $this->getApiMock(); - $api->expects($this->once()) - ->method('get') - ->with('projects/1/builds', array('scope' => array('success', 'failed'))) - ->will($this->returnValue($expectedArray)) - ; - - $this->assertEquals($expectedArray, $api->builds(1, array('success', 'failed'))); - } - - /** - * @test - */ - public function shouldGetBuild() - { - $expectedArray = array('id' => 2); - - $api = $this->getApiMock(); - $api->expects($this->once()) - ->method('get') - ->with('projects/1/builds/2') - ->will($this->returnValue($expectedArray)) - ; - - $this->assertEquals($expectedArray, $api->build(1, 2)); - } - - /** - * @test - */ - public function shouldGetTrace() - { - $expectedString = 'runner trace of a specific build'; - - $api = $this->getApiMock(); - $api->expects($this->once()) - ->method('get') - ->with('projects/1/builds/2/trace') - ->will($this->returnValue($expectedString)) - ; - - $this->assertEquals($expectedString, $api->trace(1, 2)); - } - - /** - * @test - */ - public function shouldGetPipelines() - { - $expectedArray = array( - array('id' => 1, 'status' => 'success','ref' => 'new-pipeline'), - array('id' => 2, 'status' => 'failed', 'ref' => 'new-pipeline'), - array('id' => 3, 'status' => 'pending', 'ref'=> 'test-pipeline') - ); - - $api = $this->getApiMock(); - $api->expects($this->once()) - ->method('get') - ->with('projects/1/pipelines') - ->will($this->returnValue($expectedArray)) - ; - - $this->assertEquals($expectedArray, $api->pipelines(1)); - } - - /** - * @test - */ - public function shouldGetPipeline() - { - $expectedArray = array( - array('id' => 1, 'status' => 'success','ref' => 'new-pipeline'), - array('id' => 2, 'status' => 'failed', 'ref' => 'new-pipeline'), - array('id' => 3, 'status' => 'pending', 'ref'=> 'test-pipeline') - ); - - $api = $this->getApiMock(); - $api->expects($this->once()) - ->method('get') - ->with('projects/1/pipelines/3') - ->will($this->returnValue($expectedArray)) - ; - - $this->assertEquals($expectedArray, $api->pipeline(1, 3)); - } - - /** - * @test - */ - public function shouldCreatePipeline() - { - $expectedArray = array( - array('id' => 4, 'status' => 'created', 'ref'=> 'test-pipeline') - ); - - $api = $this->getApiMock(); - $api->expects($this->once()) - ->method('post') - ->with('projects/1/pipelines', array('ref' => 'test-pipeline')) - ->will($this->returnValue($expectedArray)) - ; - - $this->assertEquals($expectedArray, $api->createPipeline(1, 'test-pipeline')); - } - - /** - * @test - */ - public function shouldRetryPipeline() - { - $expectedArray = array( - array('id' => 5, 'status' => 'pending', 'ref'=> 'test-pipeline') - ); - - $api = $this->getApiMock(); - $api->expects($this->once()) - ->method('post') - ->with('projects/1/pipelines/4/retry') - ->will($this->returnValue($expectedArray)) - ; - - $this->assertEquals($expectedArray, $api->retryPipeline(1, 4)); - } - - /** - * @test - */ - public function shouldCancelPipeline() - { - $expectedArray = array( - array('id' => 6, 'status' => 'cancelled', 'ref'=> 'test-pipeline') - ); - - $api = $this->getApiMock(); - $api->expects($this->once()) - ->method('post') - ->with('projects/1/pipelines/6/cancel') - ->will($this->returnValue($expectedArray)) - ; - - $this->assertEquals($expectedArray, $api->cancelPipeline(1, 6)); - } - - /** - * @test - */ - public function shouldGetMembers() - { - $expectedArray = array( - array('id' => 1, 'name' => 'Matt'), - array('id' => 2, 'name' => 'Bob') - ); - - $api = $this->getApiMock(); - $api->expects($this->once()) - ->method('get') - ->with('projects/1/members') - ->will($this->returnValue($expectedArray)) - ; - - $this->assertEquals($expectedArray, $api->members(1)); - } - - /** - * @test - */ - public function shouldGetMembersWithQuery() - { - $expectedArray = array( - array('id' => 1, 'name' => 'Matt') - ); - - $api = $this->getApiMock(); - $api->expects($this->once()) - ->method('get') - ->with('projects/1/members', array('query' => 'at')) - ->will($this->returnValue($expectedArray)) - ; - - $this->assertEquals($expectedArray, $api->members(1, 'at')); - } - - /** - * @test - */ - public function shouldGetMember() - { - $expectedArray = array('id' => 2, 'name' => 'Matt'); - - $api = $this->getApiMock(); - $api->expects($this->once()) - ->method('get') - ->with('projects/1/members/2') - ->will($this->returnValue($expectedArray)) - ; - - $this->assertEquals($expectedArray, $api->member(1, 2)); - } - - /** - * @test - */ - public function shouldAddMember() - { - $expectedArray = array('id' => 1, 'name' => 'Matt'); - - $api = $this->getApiMock(); - $api->expects($this->once()) - ->method('post') - ->with('projects/1/members', array('user_id' => 2, 'access_level' => 3)) - ->will($this->returnValue($expectedArray)) - ; - - $this->assertEquals($expectedArray, $api->addMember(1, 2, 3)); - } - - /** - * @test - */ - public function shouldSaveMember() - { - $expectedArray = array('id' => 1, 'name' => 'Matt'); - - $api = $this->getApiMock(); - $api->expects($this->once()) - ->method('put') - ->with('projects/1/members/2', array('access_level' => 4)) - ->will($this->returnValue($expectedArray)) - ; - - $this->assertEquals($expectedArray, $api->saveMember(1, 2, 4)); - } - - /** - * @test - */ - public function shouldRemoveMember() - { - $expectedBool = true; - - $api = $this->getApiMock(); - $api->expects($this->once()) - ->method('delete') - ->with('projects/1/members/2') - ->will($this->returnValue($expectedBool)) - ; - - $this->assertEquals($expectedBool, $api->removeMember(1, 2)); - } - - /** - * @test - */ - public function shouldGetHooks() - { - $expectedArray = array( - array('id' => 1, 'name' => 'Test hook'), - array('id' => 2, 'name' => 'Another hook'), - ); - - $api = $this->getApiMock(); - $api->expects($this->once()) - ->method('get') - ->with('projects/1/hooks') - ->will($this->returnValue($expectedArray)) - ; - - $this->assertEquals($expectedArray, $api->hooks(1)); - } - - /** - * @test - */ - public function shouldGetHook() - { - $expectedArray = array('id' => 2, 'name' => 'Another hook'); - - $api = $this->getApiMock(); - $api->expects($this->once()) - ->method('get') - ->with('projects/1/hooks/2') - ->will($this->returnValue($expectedArray)) - ; - - $this->assertEquals($expectedArray, $api->hook(1, 2)); - } - - /** - * @test - */ - public function shouldAddHook() - { - $expectedArray = array('id' => 3, 'name' => 'A new hook', 'url' => 'http://www.example.com'); - - $api = $this->getApiMock(); - $api->expects($this->once()) - ->method('post') - ->with('projects/1/hooks', array('url' => 'http://www.example.com', 'push_events' => true, 'issues_events' => true, 'merge_requests_events' => true)) - ->will($this->returnValue($expectedArray)) - ; - - $this->assertEquals($expectedArray, $api->addHook(1, 'http://www.example.com', array('push_events' => true, 'issues_events' => true, 'merge_requests_events' => true))); - } - - /** - * @test - */ - public function shouldAddHookWithOnlyUrl() - { - $expectedArray = array('id' => 3, 'name' => 'A new hook', 'url' => 'http://www.example.com'); - - $api = $this->getApiMock(); - $api->expects($this->once()) - ->method('post') - ->with('projects/1/hooks', array('url' => 'http://www.example.com', 'push_events' => true)) - ->will($this->returnValue($expectedArray)) - ; - - $this->assertEquals($expectedArray, $api->addHook(1, 'http://www.example.com')); - } - - /** - * @test - */ - public function shouldAddHookWithoutPushEvents() - { - $expectedArray = array('id' => 3, 'name' => 'A new hook', 'url' => 'http://www.example.com'); - - $api = $this->getApiMock(); - $api->expects($this->once()) - ->method('post') - ->with('projects/1/hooks', array('url' => 'http://www.example.com', 'push_events' => false)) - ->will($this->returnValue($expectedArray)) - ; - - $this->assertEquals($expectedArray, $api->addHook(1, 'http://www.example.com', array('push_events' => false))); - } - - /** - * @test - */ - public function shouldUpdateHook() - { - $expectedArray = array('id' => 3, 'name' => 'A new hook', 'url' => 'http://www.example.com'); - - $api = $this->getApiMock(); - $api->expects($this->once()) - ->method('put') - ->with('projects/1/hooks/3', array('url' => 'http://www.example-test.com', 'push_events' => false)) - ->will($this->returnValue($expectedArray)) - ; - - $this->assertEquals($expectedArray, $api->updateHook(1, 3, array('url' => 'http://www.example-test.com', 'push_events' => false))); - } - - /** - * @test - */ - public function shouldRemoveHook() - { - $expectedBool = true; - - $api = $this->getApiMock(); - $api->expects($this->once()) - ->method('delete') - ->with('projects/1/hooks/2') - ->will($this->returnValue($expectedBool)) - ; - - $this->assertEquals($expectedBool, $api->removeHook(1, 2)); - } - - /** - * @test - */ - public function shouldGetKeys() - { - $expectedArray = array( - array('id' => 1, 'title' => 'test-key'), - array('id' => 2, 'title' => 'another-key') - ); - - $api = $this->getApiMock(); - $api->expects($this->once()) - ->method('get') - ->with('projects/1/keys') - ->will($this->returnValue($expectedArray)) - ; - - $this->assertEquals($expectedArray, $api->keys(1)); - } - - /** - * @test - */ - public function shouldGetKey() - { - $expectedArray = array('id' => 2, 'title' => 'another-key'); - - $api = $this->getApiMock(); - $api->expects($this->once()) - ->method('get') - ->with('projects/1/keys/2') - ->will($this->returnValue($expectedArray)) - ; - - $this->assertEquals($expectedArray, $api->key(1, 2)); - } - - /** - * @test - */ - public function shouldAddKey() - { - $expectedArray = array('id' => 3, 'title' => 'new-key'); - - $api = $this->getApiMock(); - $api->expects($this->once()) - ->method('post') - ->with('projects/1/keys', array('title' => 'new-key', 'key' => '...')) - ->will($this->returnValue($expectedArray)) - ; - - $this->assertEquals($expectedArray, $api->addKey(1, 'new-key', '...')); - } - - /** - * @test - */ - public function shouldRemoveKey() - { - $expectedBool = true; - - $api = $this->getApiMock(); - $api->expects($this->once()) - ->method('delete') - ->with('projects/1/keys/3') - ->will($this->returnValue($expectedBool)) - ; - - $this->assertEquals($expectedBool, $api->removeKey(1, 3)); - } - - /** - * @test - */ - public function shoudEnableKey() - { - $expectedBool = true; - - $api = $this->getApiMock(); - $api->expects($this->once()) - ->method('post') - ->with('projects/1/keys/3/enable') - ->will($this->returnValue($expectedBool)) - ; - - $this->assertEquals($expectedBool, $api->enableKey(1, 3)); - } - - /** - * @test - */ - public function shoudDisableKey() - { - $expectedBool = true; - - $api = $this->getApiMock(); - $api->expects($this->once()) - ->method('delete') - ->with('projects/1/keys/3/disable') - ->will($this->returnValue($expectedBool)) - ; - - $this->assertEquals($expectedBool, $api->disableKey(1, 3)); - } - - /** - * @test - */ - public function shouldGetEvents() - { - $expectedArray = array( - array('id' => 1, 'title' => 'An event'), - array('id' => 2, 'title' => 'Another event') - ); - - $api = $this->getApiMock(); - $api->expects($this->once()) - ->method('get') - ->with('projects/1/events', array( - 'page' => 1, - 'per_page' => AbstractApi::PER_PAGE - )) - ->will($this->returnValue($expectedArray)) - ; - - $this->assertEquals($expectedArray, $api->events(1)); - } - - /** - * @test - */ - public function shouldGetEventsWithPagination() - { - $expectedArray = array( - array('id' => 1, 'title' => 'An event'), - array('id' => 2, 'title' => 'Another event') - ); - - $api = $this->getApiMock(); - $api->expects($this->once()) - ->method('get') - ->with('projects/1/events', array( - 'page' => 2, - 'per_page' => 15 - )) - ->will($this->returnValue($expectedArray)) - ; - - $this->assertEquals($expectedArray, $api->events(1, 2, 15)); - } - - /** - * @test - */ - public function shouldGetLabels() - { - $expectedArray = array( - array('name' => 'bug', 'color' => '#000000'), - array('name' => 'feature', 'color' => '#ff0000') - ); - - $api = $this->getApiMock(); - $api->expects($this->once()) - ->method('get') - ->with('projects/1/labels') - ->will($this->returnValue($expectedArray)) - ; - - $this->assertEquals($expectedArray, $api->labels(1)); - } - - /** - * @test - */ - public function shouldAddLabel() - { - $expectedArray = array('name' => 'bug', 'color' => '#000000'); - - $api = $this->getApiMock(); - $api->expects($this->once()) - ->method('post') - ->with('projects/1/labels', array('name' => 'wont-fix', 'color' => '#ffffff')) - ->will($this->returnValue($expectedArray)) - ; - - $this->assertEquals($expectedArray, $api->addLabel(1, array('name' => 'wont-fix', 'color' => '#ffffff'))); - } - - /** - * @test - */ - public function shouldUpdateLabel() - { - $expectedArray = array('name' => 'bug', 'color' => '#00ffff'); - - $api = $this->getApiMock(); - $api->expects($this->once()) - ->method('put') - ->with('projects/1/labels', array('name' => 'bug', 'new_name' => 'big-bug', 'color' => '#00ffff')) - ->will($this->returnValue($expectedArray)) - ; - - $this->assertEquals($expectedArray, $api->updateLabel(1, array('name' => 'bug', 'new_name' => 'big-bug', 'color' => '#00ffff'))); - } - - /** - * @test - */ - public function shouldRemoveLabel() - { - $expectedBool = true; - - $api = $this->getApiMock(); - $api->expects($this->once()) - ->method('delete') - ->with('projects/1/labels', array('name' => 'bug')) - ->will($this->returnValue($expectedBool)) - ; - - $this->assertEquals($expectedBool, $api->removeLabel(1, 'bug')); - } - - /** - * @test - */ - public function shouldCreateForkRelation() - { - $expectedArray = array('project_id' => 1, 'forked_id' => 2); - - $api = $this->getApiMock(); - $api->expects($this->once()) - ->method('post') - ->with('projects/1/fork/2') - ->will($this->returnValue($expectedArray)) - ; - - $this->assertEquals($expectedArray, $api->createForkRelation(1, 2)); - } - - /** - * @test - */ - public function shouldRemoveForkRelation() - { - $expectedBool = true; - - $api = $this->getApiMock(); - $api->expects($this->once()) - ->method('delete') - ->with('projects/2/fork') - ->will($this->returnValue($expectedBool)) - ; - - $this->assertEquals($expectedBool, $api->removeForkRelation(2)); - } - - /** - * @test - */ - public function shouldSetService() - { - $expectedBool = true; - - $api = $this->getApiMock(); - $api->expects($this->once()) - ->method('put') - ->with('projects/1/services/hipchat', array('param' => 'value')) - ->will($this->returnValue($expectedBool)) - ; - - $this->assertEquals($expectedBool, $api->setService(1, 'hipchat', array('param' => 'value'))); - } - - /** - * @test - */ - public function shouldRemoveService() - { - $expectedBool = true; - - $api = $this->getApiMock(); - $api->expects($this->once()) - ->method('delete') - ->with('projects/1/services/hipchat') - ->will($this->returnValue($expectedBool)) - ; - - $this->assertEquals($expectedBool, $api->removeService(1, 'hipchat')); - } - - /** - * @test - */ - public function shouldGetVariables() - { - $expectedArray = array( - array('key' => 'ftp_username', 'value' => 'ftp'), - array('key' => 'ftp_password', 'value' => 'somepassword') - ); - - $api = $this->getApiMock(); - $api->expects($this->once()) - ->method('get') - ->with('projects/1/variables') - ->will($this->returnValue($expectedArray)) - ; - - $this->assertEquals($expectedArray, $api->variables(1)); - } - - /** - * @test - */ - public function shouldGetVariable() - { - $expectedArray = array('key' => 'ftp_username', 'value' => 'ftp'); - - $api = $this->getApiMock(); - $api->expects($this->once()) - ->method('get') - ->with('projects/1/variables/ftp_username') - ->will($this->returnValue($expectedArray)) - ; - - $this->assertEquals($expectedArray, $api->variable(1, 'ftp_username')); - } - - /** - * @test - */ - public function shouldAddVariable() - { - $expectedKey = 'ftp_port'; - $expectedValue = '21'; - - $expectedArray = array( - 'key' => $expectedKey, - 'value' => $expectedValue, - ); - - $api = $this->getApiMock(); - $api->expects($this->once()) - ->method('post') - ->with('projects/1/variables', $expectedArray) - ->will($this->returnValue($expectedArray)) - ; - - $this->assertEquals($expectedArray, $api->addVariable(1, $expectedKey, $expectedValue)); - } - - /** - * @test - */ - public function shouldUpdateVariable() - { - $expectedKey = 'ftp_port'; - $expectedValue = '22'; - - $expectedArray = array( - 'key' => 'ftp_port', - 'value' => '22', - ); - - $api = $this->getApiMock(); - $api->expects($this->once()) - ->method('put') - ->with('projects/1/variables/'.$expectedKey, array('value' => $expectedValue)) - ->will($this->returnValue($expectedArray)) - ; - - $this->assertEquals($expectedArray, $api->updateVariable(1, $expectedKey, $expectedValue)); - } - - /** - * @test - */ - public function shouldRemoveVariable() - { - $expectedBool = true; - - $api = $this->getApiMock(); - $api->expects($this->once()) - ->method('delete') - ->with('projects/1/variables/ftp_password') - ->will($this->returnValue($expectedBool)) - ; - - $this->assertEquals($expectedBool, $api->removeVariable(1, 'ftp_password')); - } - - protected function getMultipleProjectsRequestMock($path, $expectedArray = array(), $page = 1, $per_page = 20, $order_by = 'created_at', $sort = 'asc') - { - $api = $this->getApiMock(); - $api->expects($this->once()) - ->method('get') - ->with($path, array('page' => $page, 'per_page' => $per_page, 'order_by' => $order_by, 'sort' => $sort)) - ->will($this->returnValue($expectedArray)) - ; - - return $api; - } - - /** - * @test - */ - public function shouldGetDeployments() - { - $expectedArray = array( - array('id' => 1, 'sha' => '0000001'), - array('id' => 2, 'sha' => '0000002'), - ); - - $api = $this->getApiMock(); - $api->expects($this->once()) - ->method('get') - ->with('projects/1/deployments', array( - 'page' => 1, - 'per_page' => AbstractApi::PER_PAGE - )) - ->will($this->returnValue($expectedArray)) - ; - - $this->assertEquals($expectedArray, $api->deployments(1)); - } - - /** - * @test - */ - public function shouldGetDeploymentsWithPagination() - { - $expectedArray = array( - array('id' => 1, 'sha' => '0000001'), - array('id' => 2, 'sha' => '0000002'), - ); - - $api = $this->getApiMock(); - $api->expects($this->once()) - ->method('get') - ->with('projects/1/deployments', array( - 'page' => 2, - 'per_page' => 15 - )) - ->will($this->returnValue($expectedArray)) - ; - - $this->assertEquals($expectedArray, $api->deployments(1, 2, 15)); - } - - protected function getMultipleProjectsData() - { - return array( - array('id' => 1, 'name' => 'A project'), - array('id' => 2, 'name' => 'Another project') - ); - } - - protected function getApiClass() - { - return 'Gitlab\Api\Projects'; - } -} diff --git a/vendor/m4tthumphrey/php-gitlab-api/test/Gitlab/Tests/Api/RepositoriesTest.php b/vendor/m4tthumphrey/php-gitlab-api/test/Gitlab/Tests/Api/RepositoriesTest.php deleted file mode 100644 index a6bffd100c2..00000000000 --- a/vendor/m4tthumphrey/php-gitlab-api/test/Gitlab/Tests/Api/RepositoriesTest.php +++ /dev/null @@ -1,708 +0,0 @@ -<?php namespace Gitlab\Tests\Api; - -use Gitlab\Api\AbstractApi; - -class RepositoriesTest extends ApiTestCase -{ - /** - * @test - */ - public function shouldGetBranches() - { - $expectedArray = array( - array('name' => 'master'), - array('name' => 'develop') - ); - - $api = $this->getApiMock(); - $api->expects($this->once()) - ->method('get') - ->with('projects/1/repository/branches') - ->will($this->returnValue($expectedArray)) - ; - - $this->assertEquals($expectedArray, $api->branches(1)); - } - - /** - * @test - */ - public function shouldGetBranch() - { - $expectedArray = array('name' => 'master'); - - $api = $this->getApiMock(); - $api->expects($this->once()) - ->method('get') - ->with('projects/1/repository/branches/master') - ->will($this->returnValue($expectedArray)) - ; - - $this->assertEquals($expectedArray, $api->branch(1, 'master')); - } - - /** - * @test - */ - public function shouldCreateBranch() - { - $expectedArray = array('name' => 'feature'); - - $api = $this->getApiMock(); - $api->expects($this->once()) - ->method('post') - ->with('projects/1/repository/branches', array('branch_name' => 'feature', 'ref' => 'master')) - ->will($this->returnValue($expectedArray)) - ; - - $this->assertEquals($expectedArray, $api->createBranch(1, 'feature', 'master')); - } - - /** - * @test - */ - public function shouldDeleteBranch() - { - $expectedBool = true; - - $api = $this->getApiMock(); - $api->expects($this->once()) - ->method('delete') - ->with('projects/1/repository/branches/master') - ->will($this->returnValue($expectedBool)) - ; - - $this->assertEquals($expectedBool, $api->deleteBranch(1, 'master')); - } - - /** - * @test - */ - public function shouldProtectBranch() - { - $expectedArray = array('name' => 'master'); - - $api = $this->getApiMock(); - $api->expects($this->once()) - ->method('put') - ->with('projects/1/repository/branches/master/protect', array('developers_can_push' => false, 'developers_can_merge' => false)) - ->will($this->returnValue($expectedArray)) - ; - - $this->assertEquals($expectedArray, $api->protectBranch(1, 'master')); - } - - /** - * @test - */ - public function shouldProtectBranchWithPermissions() - { - $expectedArray = array('name' => 'master'); - - $api = $this->getApiMock(); - $api->expects($this->once()) - ->method('put') - ->with('projects/1/repository/branches/master/protect', array('developers_can_push' => true, 'developers_can_merge' => true)) - ->will($this->returnValue($expectedArray)) - ; - - $this->assertEquals($expectedArray, $api->protectBranch(1, 'master', true, true)); - } - - /** - * @test - */ - public function shouldUnprotectBranch() - { - $expectedArray = array('name' => 'master'); - - $api = $this->getApiMock(); - $api->expects($this->once()) - ->method('put') - ->with('projects/1/repository/branches/master/unprotect') - ->will($this->returnValue($expectedArray)) - ; - - $this->assertEquals($expectedArray, $api->unprotectBranch(1, 'master')); - } - - /** - * @test - */ - public function shouldGetTags() - { - $expectedArray = array( - array('name' => '1.0'), - array('name' => '1.1') - ); - - $api = $this->getApiMock(); - $api->expects($this->once()) - ->method('get') - ->with('projects/1/repository/tags') - ->will($this->returnValue($expectedArray)) - ; - - $this->assertEquals($expectedArray, $api->tags(1)); - } - - /** - * @test - */ - public function shouldCreateTag() - { - $expectedArray = array('name' => '1.0'); - - $api = $this->getApiMock(); - $api->expects($this->once()) - ->method('post') - ->with('projects/1/repository/tags', array( - 'tag_name' => '1.0', - 'ref' => 'abcd1234', - 'message' => '1.0 release' - )) - ->will($this->returnValue($expectedArray)) - ; - - $this->assertEquals($expectedArray, $api->createTag(1, '1.0', 'abcd1234', '1.0 release')); - } - - /** - * @test - */ - public function shouldCreateRelease() { - $project_id = 1; - $tagName = 'sometag'; - $description = '1.0 release'; - - $expectedArray = array( 'name' => $tagName ); - - $api = $this->getApiMock(); - $api->expects( $this->once()) - ->method('post') - ->with( 'projects/' . $project_id . '/repository/tags/' . $tagName . '/release', array( - 'id' => $project_id, - 'tag_name' => $tagName, - 'description' => $description - )) - ->will($this->returnValue($expectedArray)) - ; - - $this->assertEquals( $expectedArray, $api->createRelease( $project_id, $tagName, $description ) ); - } - - /** - * @test - */ - public function shouldUpdateRelease() { - $project_id = 1; - $tagName = 'sometag'; - $description = '1.0 release'; - - $expectedArray = array( 'description' => $tagName ); - - $api = $this->getApiMock(); - $api->expects( $this->once()) - ->method('put') - ->with( 'projects/' . $project_id . '/repository/tags/' . $tagName . '/release', array( - 'id' => $project_id, - 'tag_name' => $tagName, - 'description' => $description - )) - ->will($this->returnValue($expectedArray)) - ; - - $this->assertEquals( $expectedArray, $api->updateRelease( $project_id, $tagName, $description ) ); - } - - /** - * @test - */ - public function shouldGetCommits() - { - $expectedArray = array( - array('id' => 'abcd1234', 'title' => 'A commit'), - array('id' => 'efgh5678', 'title' => 'Another commit') - ); - - $api = $this->getApiMock(); - $api->expects($this->once()) - ->method('get') - ->with('projects/1/repository/commits', array('page' => 0, 'per_page' => AbstractApi::PER_PAGE, 'ref_name' => null)) - ->will($this->returnValue($expectedArray)) - ; - - $this->assertEquals($expectedArray, $api->commits(1)); - } - - /** - * @test - */ - public function shouldGetCommitBuilds() - { - $expectedArray = array( - array('id' => 'abcd1234', 'status' => 'failed'), - array('id' => 'efgh5678', 'status' => 'success') - ); - - $api = $this->getApiMock(); - $api->expects($this->once()) - ->method('get') - ->with('projects/1/repository/commits/abcd12345/builds', array('page' => 0, 'per_page' => AbstractApi::PER_PAGE, 'scope' => null)) - ->will($this->returnValue($expectedArray)) - ; - - $this->assertEquals($expectedArray, $api->commitBuilds(1, 'abcd12345')); - } - - /** - * @test - */ - public function shouldGetCommitBuildsWithScope() - { - $expectedArray = array( - array('id' => 'abcd1234', 'status' => 'success'), - ); - - $api = $this->getApiMock(); - $api->expects($this->once()) - ->method('get') - ->with('projects/1/repository/commits/abcd12345/builds', array('page' => 0, 'per_page' => AbstractApi::PER_PAGE, 'scope' => 'success')) - ->will($this->returnValue($expectedArray)) - ; - - $this->assertEquals($expectedArray, $api->commitBuilds(1, 'abcd12345', 'success')); - } - - - /** - * @test - */ - public function shouldGetCommitsWithParams() - { - $expectedArray = array( - array('id' => 'abcd1234', 'title' => 'A commit'), - array('id' => 'efgh5678', 'title' => 'Another commit') - ); - - $api = $this->getApiMock(); - $api->expects($this->once()) - ->method('get') - ->with('projects/1/repository/commits', array('page' => 2, 'per_page' => 25, 'ref_name' => 'master')) - ->will($this->returnValue($expectedArray)) - ; - - $this->assertEquals($expectedArray, $api->commits(1, 2, 25, 'master')); - } - - /** - * @test - */ - public function shouldGetCommit() - { - $expectedArray = array('id' => 'abcd1234', 'title' => 'A commit'); - - $api = $this->getApiMock(); - $api->expects($this->once()) - ->method('get') - ->with('projects/1/repository/commits/abcd1234') - ->will($this->returnValue($expectedArray)) - ; - - $this->assertEquals($expectedArray, $api->commit(1, 'abcd1234')); - } - - /** - * @test - */ - public function shouldGetCommitComments() - { - $expectedArray = array( - array('note' => 'A commit message'), - array('note' => 'Another commit message') - ); - - $api = $this->getApiMock(); - $api->expects($this->once()) - ->method('get') - ->with('projects/1/repository/commits/abcd1234/comments') - ->will($this->returnValue($expectedArray)) - ; - - $this->assertEquals($expectedArray, $api->commitComments(1, 'abcd1234')); - } - - /** - * @test - */ - public function shouldCreateCommitComment() - { - $expectedArray = array('id' => 2, 'title' => 'A new comment'); - - $api = $this->getApiMock(); - $api->expects($this->once()) - ->method('post') - ->with('projects/1/repository/commits/abcd1234/comments', array('note' => 'A new comment')) - ->will($this->returnValue($expectedArray)) - ; - - $this->assertEquals($expectedArray, $api->createCommitComment(1, 'abcd1234', 'A new comment')); - } - - /** - * @test - */ - public function shouldCreateCommitCommentWithParams() - { - $expectedArray = array('id' => 2, 'title' => 'A new comment'); - - $api = $this->getApiMock(); - $api->expects($this->once()) - ->method('post') - ->with('projects/1/repository/commits/abcd1234/comments', array( - 'note' => 'A new comment', - 'path' => '/some/file.txt', - 'line' => 123, 'line_type' => 'old' - )) - ->will($this->returnValue($expectedArray)) - ; - - $this->assertEquals($expectedArray, $api->createCommitComment(1, 'abcd1234', 'A new comment', array( - 'path' => '/some/file.txt', - 'line' => 123, - 'line_type' => 'old' - ))); - } - - /** - * @test - */ - public function shouldCompare() - { - $expectedArray = array('commit' => 'object'); - - $api = $this->getApiMock(); - $api->expects($this->once()) - ->method('get') - ->with('projects/1/repository/compare?from=master&to=feature') - ->will($this->returnValue($expectedArray)) - ; - - $this->assertEquals($expectedArray, $api->compare(1, 'master', 'feature')); - } - - /** - * @test - */ - public function shouldGetDiff() - { - $expectedArray = array( - array('diff' => '--- ...'), - array('diff' => '+++ ...') - ); - - $api = $this->getApiMock(); - $api->expects($this->once()) - ->method('get') - ->with('projects/1/repository/commits/abcd1234/diff') - ->will($this->returnValue($expectedArray)) - ; - - $this->assertEquals($expectedArray, $api->diff(1, 'abcd1234')); - } - - /** - * @test - */ - public function shouldGetTree() - { - $expectedArray = array( - array('name' => 'file1.txt'), - array('name' => 'file2.csv') - ); - - $api = $this->getApiMock(); - $api->expects($this->once()) - ->method('get') - ->with('projects/1/repository/tree') - ->will($this->returnValue($expectedArray)) - ; - - $this->assertEquals($expectedArray, $api->tree(1)); - } - - /** - * @test - */ - public function shouldGetTreeWithParams() - { - $expectedArray = array( - array('name' => 'dir/file1.txt'), - array('name' => 'dir/file2.csv') - ); - - $api = $this->getApiMock(); - $api->expects($this->once()) - ->method('get') - ->with('projects/1/repository/tree', array('path' => 'dir/', 'ref_name' => 'master')) - ->will($this->returnValue($expectedArray)) - ; - - $this->assertEquals($expectedArray, $api->tree(1, array('path' => 'dir/', 'ref_name' => 'master'))); - } - - /** - * @test - */ - public function shouldGetBlob() - { - $expectedString = 'something in a file'; - - $api = $this->getApiMock(); - $api->expects($this->once()) - ->method('get') - ->with('projects/1/repository/commits/abcd1234/blob', array('filepath' => 'dir/file1.txt')) - ->will($this->returnValue($expectedString)) - ; - - $this->assertEquals($expectedString, $api->blob(1, 'abcd1234', 'dir/file1.txt')); - } - - /** - * @test - */ - public function shouldGetFile() - { - $expectedArray = array('file_name' => 'file1.txt', 'file_path' => 'dir/file1.txt'); - - $api = $this->getApiMock(); - $api->expects($this->once()) - ->method('get') - ->with('projects/1/repository/files', array('file_path' => 'dir/file1.txt', 'ref' => 'abcd1234')) - ->will($this->returnValue($expectedArray)) - ; - - $this->assertEquals($expectedArray, $api->getFile(1, 'dir/file1.txt', 'abcd1234')); - } - - /** - * @test - */ - public function shouldCreateFile() - { - $expectedArray = array('file_name' => 'file1.txt', 'file_path' => 'dir/file1.txt'); - - $api = $this->getApiMock(); - $api->expects($this->once()) - ->method('post') - ->with('projects/1/repository/files', array( - 'file_path' => 'dir/file1.txt', - 'branch_name' => 'master', - 'encoding' => null, - 'content' => 'some contents', - 'commit_message' => 'Added new file', - 'author_email' => null, - 'author_name' => null, - )) - ->will($this->returnValue($expectedArray)) - ; - - $this->assertEquals($expectedArray, $api->createFile(1, 'dir/file1.txt', 'some contents', 'master', 'Added new file')); - } - - /** - * @test - */ - public function shouldCreateFileWithEncoding() - { - $expectedArray = array('file_name' => 'file1.txt', 'file_path' => 'dir/file1.txt'); - - $api = $this->getApiMock(); - $api->expects($this->once()) - ->method('post') - ->with('projects/1/repository/files', array( - 'file_path' => 'dir/file1.txt', - 'branch_name' => 'master', - 'encoding' => 'text', - 'content' => 'some contents', - 'commit_message' => 'Added new file', - 'author_email' => null, - 'author_name' => null, - )) - ->will($this->returnValue($expectedArray)) - ; - - $this->assertEquals($expectedArray, $api->createFile(1, 'dir/file1.txt', 'some contents', 'master', 'Added new file', 'text')); - } - - /** - * @test - */ - public function shouldCreateFileWithAuthor() - { - $expectedArray = array('file_name' => 'file1.txt', 'file_path' => 'dir/file1.txt'); - - $api = $this->getApiMock(); - $api->expects($this->once()) - ->method('post') - ->with('projects/1/repository/files', array( - 'file_path' => 'dir/file1.txt', - 'branch_name' => 'master', - 'encoding' => null, - 'content' => 'some contents', - 'commit_message' => 'Added new file', - 'author_email' => 'gitlab@example.com', - 'author_name' => 'GitLab User', - )) - ->will($this->returnValue($expectedArray)) - ; - - $this->assertEquals($expectedArray, $api->createFile(1, 'dir/file1.txt', 'some contents', 'master', 'Added new file', null, 'gitlab@example.com', 'GitLab User')); - } - - /** - * @test - */ - public function shouldUpdateFile() - { - $expectedArray = array('file_name' => 'file1.txt', 'file_path' => 'dir/file1.txt'); - - $api = $this->getApiMock(); - $api->expects($this->once()) - ->method('put') - ->with('projects/1/repository/files', array( - 'file_path' => 'dir/file1.txt', - 'branch_name' => 'master', - 'encoding' => null, - 'content' => 'some new contents', - 'commit_message' => 'Updated new file', - 'author_email' => null, - 'author_name' => null, - )) - ->will($this->returnValue($expectedArray)) - ; - - $this->assertEquals($expectedArray, $api->updateFile(1, 'dir/file1.txt', 'some new contents', 'master', 'Updated new file')); - } - - /** - * @test - */ - public function shouldUpdateFileWithEncoding() - { - $expectedArray = array('file_name' => 'file1.txt', 'file_path' => 'dir/file1.txt'); - - $api = $this->getApiMock(); - $api->expects($this->once()) - ->method('put') - ->with('projects/1/repository/files', array( - 'file_path' => 'dir/file1.txt', - 'branch_name' => 'master', - 'encoding' => 'base64', - 'content' => 'some new contents', - 'commit_message' => 'Updated file', - 'author_email' => null, - 'author_name' => null, - )) - ->will($this->returnValue($expectedArray)) - ; - - $this->assertEquals($expectedArray, $api->updateFile(1, 'dir/file1.txt', 'some new contents', 'master', 'Updated file', 'base64')); - } - - /** - * @test - */ - public function shouldUpdateFileWithAuthor() - { - $expectedArray = array('file_name' => 'file1.txt', 'file_path' => 'dir/file1.txt'); - - $api = $this->getApiMock(); - $api->expects($this->once()) - ->method('put') - ->with('projects/1/repository/files', array( - 'file_path' => 'dir/file1.txt', - 'branch_name' => 'master', - 'encoding' => null, - 'content' => 'some new contents', - 'commit_message' => 'Updated file', - 'author_email' => 'gitlab@example.com', - 'author_name' => 'GitLab User', - )) - ->will($this->returnValue($expectedArray)) - ; - - $this->assertEquals($expectedArray, $api->updateFile(1, 'dir/file1.txt', 'some new contents', 'master', 'Updated file', null, 'gitlab@example.com', 'GitLab User')); - } - - /** - * @test - */ - public function shouldDeleteFile() - { - $expectedBool = true; - - $api = $this->getApiMock(); - $api->expects($this->once()) - ->method('delete') - ->with('projects/1/repository/files', array( - 'file_path' => 'dir/file1.txt', - 'branch_name' => 'master', - 'commit_message' => 'Deleted file', - 'author_email' => null, - 'author_name' => null, - )) - ->will($this->returnValue($expectedBool)) - ; - - $this->assertEquals($expectedBool, $api->deleteFile(1, 'dir/file1.txt', 'master', 'Deleted file')); - } - - /** - * @test - */ - public function shouldDeleteFileWithAuthor() - { - $expectedBool = true; - - $api = $this->getApiMock(); - $api->expects($this->once()) - ->method('delete') - ->with('projects/1/repository/files', array( - 'file_path' => 'dir/file1.txt', - 'branch_name' => 'master', - 'commit_message' => 'Deleted file', - 'author_email' => 'gitlab@example.com', - 'author_name' => 'GitLab User', - )) - ->will($this->returnValue($expectedBool)) - ; - - $this->assertEquals($expectedBool, $api->deleteFile(1, 'dir/file1.txt', 'master', 'Deleted file', 'gitlab@example.com', 'GitLab User')); - } - - /** - * @test - */ - public function shouldGetContributors() - { - $expectedArray = array( - array('name' => 'Matt'), - array('name' => 'Bob') - ); - - $api = $this->getApiMock(); - $api->expects($this->once()) - ->method('get') - ->with('projects/1/repository/contributors') - ->will($this->returnValue($expectedArray)) - ; - - $this->assertEquals($expectedArray, $api->contributors(1)); - } - - protected function getApiClass() - { - return 'Gitlab\Api\Repositories'; - } -} diff --git a/vendor/m4tthumphrey/php-gitlab-api/test/Gitlab/Tests/Api/SnippetsTest.php b/vendor/m4tthumphrey/php-gitlab-api/test/Gitlab/Tests/Api/SnippetsTest.php deleted file mode 100644 index 694aac5959d..00000000000 --- a/vendor/m4tthumphrey/php-gitlab-api/test/Gitlab/Tests/Api/SnippetsTest.php +++ /dev/null @@ -1,114 +0,0 @@ -<?php namespace Gitlab\Tests\Api; - -class SnippetsTest extends ApiTestCase -{ - /** - * @test - */ - public function shouldGetAllSnippets() - { - $expectedArray = array( - array('id' => 1, 'title' => 'A snippet'), - array('id' => 2, 'title' => 'Another snippet'), - ); - - $api = $this->getApiMock(); - $api->expects($this->once()) - ->method('get') - ->with('projects/1/snippets') - ->will($this->returnValue($expectedArray)) - ; - - $this->assertEquals($expectedArray, $api->all(1)); - } - - /** - * @test - */ - public function shouldShowSnippet() - { - $expectedArray = array('id' => 2, 'title' => 'Another snippet'); - - $api = $this->getApiMock(); - $api->expects($this->once()) - ->method('get') - ->with('projects/1/snippets/2') - ->will($this->returnValue($expectedArray)) - ; - - $this->assertEquals($expectedArray, $api->show(1, 2)); - } - - /** - * @test - */ - public function shouldCreateSnippet() - { - $expectedArray = array('id' => 3, 'title' => 'A new snippet'); - - $api = $this->getApiMock(); - $api->expects($this->once()) - ->method('post') - ->with('projects/1/snippets', array('title' => 'A new snippet', 'code' => 'A file', 'file_name' => 'file.txt')) - ->will($this->returnValue($expectedArray)) - ; - - $this->assertEquals($expectedArray, $api->create(1, 'A new snippet', 'file.txt', 'A file')); - } - - /** - * @test - */ - public function shouldUpdateSnippet() - { - $expectedArray = array('id' => 3, 'title' => 'Updated snippet'); - - $api = $this->getApiMock(); - $api->expects($this->once()) - ->method('put') - ->with('projects/1/snippets/3', array('title' => 'Updated snippet', 'code' => 'New content', 'file_name' => 'new_file.txt')) - ->will($this->returnValue($expectedArray)) - ; - - $this->assertEquals($expectedArray, $api->update(1, 3, array('file_name' => 'new_file.txt', 'code' => 'New content', 'title' => 'Updated snippet'))); - } - - /** - * @test - */ - public function shouldShowContent() - { - $expectedString = 'New content'; - - $api = $this->getApiMock(); - $api->expects($this->once()) - ->method('get') - ->with('projects/1/snippets/3/raw') - ->will($this->returnValue($expectedString)) - ; - - $this->assertEquals($expectedString, $api->content(1, 3)); - } - - /** - * @test - */ - public function shouldRemoveSnippet() - { - $expectedBool = true; - - $api = $this->getApiMock(); - $api->expects($this->once()) - ->method('delete') - ->with('projects/1/snippets/3') - ->will($this->returnValue($expectedBool)) - ; - - $this->assertEquals($expectedBool, $api->remove(1, 3)); - } - - protected function getApiClass() - { - return 'Gitlab\Api\Snippets'; - } -} diff --git a/vendor/m4tthumphrey/php-gitlab-api/test/Gitlab/Tests/Api/SystemHooksTest.php b/vendor/m4tthumphrey/php-gitlab-api/test/Gitlab/Tests/Api/SystemHooksTest.php deleted file mode 100644 index 3bf16bce6bc..00000000000 --- a/vendor/m4tthumphrey/php-gitlab-api/test/Gitlab/Tests/Api/SystemHooksTest.php +++ /dev/null @@ -1,80 +0,0 @@ -<?php namespace Gitlab\Tests\Api; - -class SystemHooksTest extends ApiTestCase -{ - /** - * @test - */ - public function shouldGetAllHooks() - { - $expectedArray = array( - array('id' => 1, 'url' => 'http://www.example.com'), - array('id' => 2, 'url' => 'http://www.example.org'), - ); - - $api = $this->getApiMock(); - $api->expects($this->once()) - ->method('get') - ->with('hooks') - ->will($this->returnValue($expectedArray)) - ; - - $this->assertEquals($expectedArray, $api->all()); - } - - /** - * @test - */ - public function shouldCreateHook() - { - $expectedArray = array('id' => 3, 'url' => 'http://www.example.net'); - - $api = $this->getApiMock(); - $api->expects($this->once()) - ->method('post') - ->with('hooks', array('url' => 'http://www.example.net')) - ->will($this->returnValue($expectedArray)) - ; - - $this->assertEquals($expectedArray, $api->create('http://www.example.net')); - } - - /** - * @test - */ - public function shouldTestHook() - { - $expectedBool = true; - - $api = $this->getApiMock(); - $api->expects($this->once()) - ->method('get') - ->with('hooks/3') - ->will($this->returnValue($expectedBool)) - ; - - $this->assertEquals($expectedBool, $api->test(3)); - } - - /** - * @test - */ - public function shouldRemoveHook() - { - $expectedBool = true; - - $api = $this->getApiMock(); - $api->expects($this->once()) - ->method('delete') - ->with('hooks/3') - ->will($this->returnValue($expectedBool)) - ; - - $this->assertEquals($expectedBool, $api->remove(3)); - } - - protected function getApiClass() - { - return 'Gitlab\Api\SystemHooks'; - } -} diff --git a/vendor/m4tthumphrey/php-gitlab-api/test/Gitlab/Tests/Api/TagsTest.php b/vendor/m4tthumphrey/php-gitlab-api/test/Gitlab/Tests/Api/TagsTest.php deleted file mode 100644 index b6aaafa6fca..00000000000 --- a/vendor/m4tthumphrey/php-gitlab-api/test/Gitlab/Tests/Api/TagsTest.php +++ /dev/null @@ -1,87 +0,0 @@ -<?php namespace Gitlab\Tests\Api; - -use Gitlab\Api\AbstractApi; - -class TagsTest extends ApiTestCase -{ - /** - * @test - */ - public function shouldGetAllTags() - { - $expectedArray = array( - array('name' => 'v1.0.0'), - array('name' => 'v1.1.0'), - ); - - $api = $this->getApiMock(); - $api->expects($this->once()) - ->method('get') - ->with('projects/1/repository/tags') - ->will($this->returnValue($expectedArray)); - $this->assertEquals($expectedArray, $api->all(1)); - } - - /** - * @test - */ - public function shouldShowTag() - { - $expectedArray = array( - array('name' => 'v1.0.0'), - ); - - $api = $this->getApiMock(); - $api->expects($this->once()) - ->method('get') - ->with('projects/1/repository/tags/v1.0.0') - ->will($this->returnValue($expectedArray)); - $this->assertEquals($expectedArray, $api->show(1, 'v1.0.0')); - } - - /** - * @test - */ - public function shouldCreateTag() - { - $expectedArray = array( - array('name' => 'v1.1.0'), - ); - - $params = array( - 'id' => 1, - 'tag_name' => 'v1.1.0', - 'ref' => 'ref/heads/master' - ); - - $api = $this->getApiMock(); - $api->expects($this->once()) - ->method('post') - ->with('projects/1/repository/tags', $params) - ->will($this->returnValue($expectedArray)); - - $this->assertEquals($expectedArray, $api->create(1, $params)); - } - - /** - * @test - */ - public function shouldRemoveTag() - { - $expectedArray = array( - array('name' => 'v1.1.0'), - ); - - $api = $this->getApiMock(); - $api->expects($this->once()) - ->method('delete') - ->with('projects/1/repository/tags/v1.1.0') - ->will($this->returnValue($expectedArray)); - $this->assertEquals($expectedArray, $api->remove(1, 'v1.1.0')); - } - - protected function getApiClass() - { - return 'Gitlab\Api\Tags'; - } -} diff --git a/vendor/m4tthumphrey/php-gitlab-api/test/Gitlab/Tests/Api/TestCase.php b/vendor/m4tthumphrey/php-gitlab-api/test/Gitlab/Tests/Api/TestCase.php deleted file mode 100644 index cba51e5e26a..00000000000 --- a/vendor/m4tthumphrey/php-gitlab-api/test/Gitlab/Tests/Api/TestCase.php +++ /dev/null @@ -1,38 +0,0 @@ -<?php namespace Gitlab\Tests\Api; - -use Buzz\Client\Curl; - -use Gitlab\Client; -use Gitlab\HttpClient\HttpClientInterface; - -abstract class TestCase extends \PHPUnit_Framework_TestCase -{ - /** - * @return Client - */ - protected function getClientMock() - { - return new Client($this->getHttpMock()); - } - - /** - * @return \PHPUnit_Framework_MockObject_MockObject|HttpClientInterface - */ - protected function getHttpMock() - { - return $this->getMock('Gitlab\HttpClient\HttpClient', array(), array(null, array(), $this->getHttpClientMock())); - } - - /** - * @return \PHPUnit_Framework_MockObject_MockObject|Curl - */ - protected function getHttpClientMock() - { - $httpClient = $this->getMock('Buzz\Client\Curl', array('send')); - $httpClient - ->expects($this->any()) - ->method('send'); - - return $httpClient; - } -} diff --git a/vendor/m4tthumphrey/php-gitlab-api/test/Gitlab/Tests/Api/UsersTest.php b/vendor/m4tthumphrey/php-gitlab-api/test/Gitlab/Tests/Api/UsersTest.php deleted file mode 100644 index a1441da0089..00000000000 --- a/vendor/m4tthumphrey/php-gitlab-api/test/Gitlab/Tests/Api/UsersTest.php +++ /dev/null @@ -1,459 +0,0 @@ -<?php namespace Gitlab\Tests\Api; - -use Gitlab\Api\AbstractApi; - -class UsersTest extends ApiTestCase -{ - /** - * @test - */ - public function shouldGetAllUsers() - { - $expectedArray = array( - array('id' => 1, 'name' => 'Matt'), - array('id' => 2, 'name' => 'John'), - ); - - $api = $this->getApiMock(); - $api->expects($this->once()) - ->method('get') - ->with('users', array('active' => null, 'page' => 1, 'per_page' => 10)) - ->will($this->returnValue($expectedArray)) - ; - - $this->assertEquals($expectedArray, $api->all(null, 1, 10)); - } - - /** - * @test - */ - public function shouldGetActiveUsers() - { - $expectedArray = array( - array('id' => 1, 'name' => 'Matt'), - array('id' => 2, 'name' => 'John'), - ); - - $api = $this->getApiMock(); - $api->expects($this->once()) - ->method('get') - ->with('users', array('active' => true, 'page' => 1, 'per_page' => AbstractApi::PER_PAGE)) - ->will($this->returnValue($expectedArray)) - ; - - $this->assertEquals($expectedArray, $api->all(true)); - } - - /** - * @test - */ - public function shouldNotNeedPaginationWhenGettingUsers() - { - $expectedArray = array( - array('id' => 1, 'name' => 'Matt'), - array('id' => 2, 'name' => 'John'), - ); - - $api = $this->getApiMock(); - $api->expects($this->once()) - ->method('get') - ->with('users', array('active' => null, 'page' => 1, 'per_page' => AbstractApi::PER_PAGE)) - ->will($this->returnValue($expectedArray)) - ; - - $this->assertEquals($expectedArray, $api->all()); - } - - /** - * @test - */ - public function shouldSearchUsers() - { - $expectedArray = array( - array('id' => 1, 'name' => 'Matt') - ); - - $api = $this->getApiMock(); - $api->expects($this->once()) - ->method('get') - ->with('users', array('search' => 'ma', 'active' => null, 'page' => 1, 'per_page' => AbstractApi::PER_PAGE)) - ->will($this->returnValue($expectedArray)) - ; - - $this->assertEquals($expectedArray, $api->search('ma')); - } - - /** - * @test - */ - public function shouldSearchActiveUsers() - { - $expectedArray = array( - array('id' => 1, 'name' => 'Matt') - ); - - $api = $this->getApiMock(); - $api->expects($this->once()) - ->method('get') - ->with('users', array('search' => 'ma', 'active' => true, 'page' => 1, 'per_page' => AbstractApi::PER_PAGE)) - ->will($this->returnValue($expectedArray)) - ; - - $this->assertEquals($expectedArray, $api->search('ma', true)); - } - - /** - * @test - */ - public function shouldSearchActiveUsersWithPagination() - { - $expectedArray = array( - array('id' => 1, 'name' => 'Matt') - ); - - $api = $this->getApiMock(); - $api->expects($this->once()) - ->method('get') - ->with('users', array('search' => 'ma', 'active' => true, 'page' => 2, 'per_page' => 5)) - ->will($this->returnValue($expectedArray)) - ; - - $this->assertEquals($expectedArray, $api->search('ma', true, 2, 5)); - } - - /** - * @test - */ - public function shouldShowUser() - { - $expectedArray = array('id' => 1, 'name' => 'Matt'); - - $api = $this->getApiMock(); - $api->expects($this->once()) - ->method('get') - ->with('users/1') - ->will($this->returnValue($expectedArray)) - ; - - $this->assertEquals($expectedArray, $api->show(1)); - } - - /** - * @test - */ - public function shouldCreateUser() - { - $expectedArray = array('id' => 3, 'name' => 'Billy'); - - $api = $this->getApiMock(); - $api->expects($this->once()) - ->method('post') - ->with('users', array('email' => 'billy@example.com', 'password' => 'password')) - ->will($this->returnValue($expectedArray)) - ; - - $this->assertEquals($expectedArray, $api->create('billy@example.com', 'password')); - } - - /** - * @test - */ - public function shouldCreateUserWithAdditionalInfo() - { - $expectedArray = array('id' => 3, 'name' => 'Billy'); - - $api = $this->getApiMock(); - $api->expects($this->once()) - ->method('post') - ->with('users', array('email' => 'billy@example.com', 'password' => 'password', 'name' => 'Billy', 'bio' => 'A person')) - ->will($this->returnValue($expectedArray)) - ; - - $this->assertEquals($expectedArray, $api->create('billy@example.com', 'password', array('name' => 'Billy', 'bio' => 'A person'))); - } - - /** - * @test - */ - public function shouldUpdateUser() - { - $expectedArray = array('id' => 3, 'name' => 'Billy Bob'); - - $api = $this->getApiMock(); - $api->expects($this->once()) - ->method('put') - ->with('users/3', array('name' => 'Billy Bob')) - ->will($this->returnValue($expectedArray)) - ; - - $this->assertEquals($expectedArray, $api->update(3, array('name' => 'Billy Bob'))); - } - - /** - * @test - */ - public function shouldRemoveUser() - { - $expectedBool = true; - - $api = $this->getApiMock(); - $api->expects($this->once()) - ->method('delete') - ->with('users/1') - ->will($this->returnValue($expectedBool)) - ; - - $this->assertEquals($expectedBool, $api->remove(1)); - } - - /** - * @test - */ - public function shouldBlockUser() - { - $expectedBool = true; - - $api = $this->getApiMock(); - $api->expects($this->once()) - ->method('put') - ->with('users/1/block') - ->will($this->returnValue($expectedBool)) - ; - - $this->assertEquals($expectedBool, $api->block(1)); - } - - /** - * @test - */ - public function shouldUnblockUser() - { - $expectedBool = true; - - $api = $this->getApiMock(); - $api->expects($this->once()) - ->method('put') - ->with('users/1/unblock') - ->will($this->returnValue($expectedBool)) - ; - - $this->assertEquals($expectedBool, $api->unblock(1)); - } - - /** - * @test - */ - public function shouldShowCurrentUser() - { - $expectedArray = array('id' => 1, 'name' => 'Matt'); - - $api = $this->getApiMock(); - $api->expects($this->once()) - ->method('get') - ->with('user') - ->will($this->returnValue($expectedArray)) - ; - - $this->assertEquals($expectedArray, $api->me()); - } - - /** - * @test - */ - public function shouldGetCurrentUserKeys() - { - $expectedArray = array( - array('id' => 1, 'title' => 'A key'), - array('id' => 2, 'name' => 'Another key'), - ); - - $api = $this->getApiMock(); - $api->expects($this->once()) - ->method('get') - ->with('user/keys') - ->will($this->returnValue($expectedArray)) - ; - - $this->assertEquals($expectedArray, $api->keys(1)); - } - - /** - * @test - */ - public function shouldGetCurrentUserKey() - { - $expectedArray = array('id' => 1, 'title' => 'A key'); - - $api = $this->getApiMock(); - $api->expects($this->once()) - ->method('get') - ->with('user/keys/1') - ->will($this->returnValue($expectedArray)) - ; - - $this->assertEquals($expectedArray, $api->key(1)); - } - - /** - * @test - */ - public function shouldCreateKeyForCurrentUser() - { - $expectedArray = array('id' => 3, 'title' => 'A new key'); - - $api = $this->getApiMock(); - $api->expects($this->once()) - ->method('post') - ->with('user/keys', array('title' => 'A new key', 'key' => '...')) - ->will($this->returnValue($expectedArray)) - ; - - $this->assertEquals($expectedArray, $api->createKey('A new key', '...')); - } - - /** - * @test - */ - public function shouldDeleteKeyForCurrentUser() - { - $expectedBool = true; - - $api = $this->getApiMock(); - $api->expects($this->once()) - ->method('delete') - ->with('user/keys/3') - ->will($this->returnValue($expectedBool)) - ; - - $this->assertEquals($expectedBool, $api->removeKey(3)); - } - - /** - * @test - */ - public function shouldGetUserKeys() - { - $expectedArray = array( - array('id' => 1, 'title' => 'A key'), - array('id' => 2, 'name' => 'Another key'), - ); - - $api = $this->getApiMock(); - $api->expects($this->once()) - ->method('get') - ->with('users/1/keys') - ->will($this->returnValue($expectedArray)) - ; - - $this->assertEquals($expectedArray, $api->userKeys(1)); - } - - /** - * @test - */ - public function shouldGetUserKey() - { - $expectedArray = array('id' => 2, 'title' => 'Another key'); - - $api = $this->getApiMock(); - $api->expects($this->once()) - ->method('get') - ->with('users/1/keys/2') - ->will($this->returnValue($expectedArray)) - ; - - $this->assertEquals($expectedArray, $api->userKey(1, 2)); - } - - /** - * @test - */ - public function shouldCreateKeyForUser() - { - $expectedArray = array('id' => 3, 'title' => 'A new key'); - - $api = $this->getApiMock(); - $api->expects($this->once()) - ->method('post') - ->with('users/1/keys', array('title' => 'A new key', 'key' => '...')) - ->will($this->returnValue($expectedArray)) - ; - - $this->assertEquals($expectedArray, $api->createKeyForUser(1, 'A new key', '...')); - } - - /** - * @test - */ - public function shouldDeleteKeyForUser() - { - $expectedBool = true; - - $api = $this->getApiMock(); - $api->expects($this->once()) - ->method('delete') - ->with('users/1/keys/3') - ->will($this->returnValue($expectedBool)) - ; - - $this->assertEquals($expectedBool, $api->removeUserKey(1, 3)); - } - - /** - * @test - */ - public function shouldAttemptLogin() - { - $expectedArray = array('id' => 1, 'name' => 'Matt'); - - $api = $this->getApiMock(); - $api->expects($this->exactly(2)) - ->method('post') - ->with('session', array('login' => 'matt', 'password' => 'password', 'email' => 'matt')) - ->will($this->returnValue($expectedArray)) - ; - - $this->assertEquals($expectedArray, $api->session('matt', 'password')); - $this->assertEquals($expectedArray, $api->login('matt', 'password')); - } - - /** - * @test - */ - public function shouldGetUserEmails() - { - $expectedArray = array( - array('id' => 1, 'email' => 'foo@bar.baz'), - array('id' => 2, 'email' => 'foo@bar.qux'), - ); - - $api = $this->getApiMock(); - $api->expects($this->once()) - ->method('get') - ->with('user/emails') - ->will($this->returnValue($expectedArray)); - - $this->assertEquals($expectedArray, $api->emails()); - } - - /** - * @test - */ - public function shouldGetSpecificUserEmail() - { - $expectedArray = array('id' => 1, 'email' => 'foo@bar.baz'); - - $api = $this->getApiMock(); - $api->expects($this->once()) - ->method('get') - ->with('user/emails/1') - ->will($this->returnValue($expectedArray)); - - $this->assertEquals($expectedArray, $api->email(1)); - } - - protected function getApiClass() - { - return 'Gitlab\Api\Users'; - } -} diff --git a/vendor/m4tthumphrey/php-gitlab-api/test/bootstrap.php b/vendor/m4tthumphrey/php-gitlab-api/test/bootstrap.php deleted file mode 100644 index 83bc8b088a4..00000000000 --- a/vendor/m4tthumphrey/php-gitlab-api/test/bootstrap.php +++ /dev/null @@ -1,18 +0,0 @@ -<?php - -function includeIfExists($file) -{ - if (file_exists($file)) { - return include $file; - } -} - -if ((!$loader = includeIfExists(__DIR__.'/../vendor/autoload.php')) && (!$loader = includeIfExists(__DIR__.'/../../../.composer/autoload.php'))) { - die('You must set up the project dependencies, run the following commands:'.PHP_EOL. - 'curl -s http://getcomposer.org/installer | php'.PHP_EOL. - 'php composer.phar install'.PHP_EOL); -} - -$loader->add('Gitlab\Tests', __DIR__); - -return $loader; \ No newline at end of file diff --git a/vendor/php-http/client-common/CHANGELOG.md b/vendor/php-http/client-common/CHANGELOG.md new file mode 100644 index 00000000000..3031720c6d5 --- /dev/null +++ b/vendor/php-http/client-common/CHANGELOG.md @@ -0,0 +1,137 @@ +# Change Log + +## 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 00000000000..4558d6f0630 --- /dev/null +++ b/vendor/php-http/client-common/LICENSE @@ -0,0 +1,19 @@ +Copyright (c) 2015-2016 PHP HTTP Team <team@php-http.org> + +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 00000000000..017bfcec93b --- /dev/null +++ b/vendor/php-http/client-common/README.md @@ -0,0 +1,55 @@ +# HTTP Client Common + +[](https://github.com/php-http/client-common/releases) +[](LICENSE) +[](https://travis-ci.org/php-http/client-common) +[](https://scrutinizer-ci.com/g/php-http/client-common) +[](https://scrutinizer-ci.com/g/php-http/client-common) +[](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 00000000000..fc5fc5fb4ea --- /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.7-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 00000000000..2036355b604 --- /dev/null +++ b/vendor/php-http/client-common/src/BatchClient.php @@ -0,0 +1,73 @@ +<?php + +namespace Http\Client\Common; + +use Http\Client\Exception; +use Http\Client\HttpClient; +use Http\Client\Common\Exception\BatchException; +use Psr\Http\Message\RequestInterface; + +/** + * BatchClient allow to sends multiple request and retrieve a Batch Result. + * + * This implementation simply loops over the requests and uses sendRequest with each of them. + * + * @author Joel Wurtz <jwurtz@jolicode.com> + */ +class BatchClient implements HttpClient +{ + /** + * @var HttpClient + */ + private $client; + + /** + * @param HttpClient $client + */ + public function __construct(HttpClient $client) + { + $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 00000000000..710611d6d00 --- /dev/null +++ b/vendor/php-http/client-common/src/BatchResult.php @@ -0,0 +1,181 @@ +<?php + +namespace Http\Client\Common; + +use Http\Client\Exception; +use Psr\Http\Message\RequestInterface; +use Psr\Http\Message\ResponseInterface; + +/** + * Responses and exceptions returned from parallel request execution. + * + * @author Márk Sági-Kazár <mark.sagikazar@gmail.com> + */ +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/EmulatedHttpAsyncClient.php b/vendor/php-http/client-common/src/EmulatedHttpAsyncClient.php new file mode 100644 index 00000000000..1b163167e6d --- /dev/null +++ b/vendor/php-http/client-common/src/EmulatedHttpAsyncClient.php @@ -0,0 +1,27 @@ +<?php + +namespace Http\Client\Common; + +use Http\Client\HttpAsyncClient; +use Http\Client\HttpClient; + +/** + * Emulates an async HTTP client. + * + * This should be replaced by an anonymous class in PHP 7. + * + * @author Márk Sági-Kazár <mark.sagikazar@gmail.com> + */ +class EmulatedHttpAsyncClient implements HttpClient, HttpAsyncClient +{ + use HttpAsyncClientEmulator; + use HttpClientDecorator; + + /** + * @param HttpClient $httpClient + */ + public function __construct(HttpClient $httpClient) + { + $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 00000000000..01046c83e4c --- /dev/null +++ b/vendor/php-http/client-common/src/EmulatedHttpClient.php @@ -0,0 +1,27 @@ +<?php + +namespace Http\Client\Common; + +use Http\Client\HttpAsyncClient; +use Http\Client\HttpClient; + +/** + * Emulates an HTTP client. + * + * This should be replaced by an anonymous class in PHP 7. + * + * @author Márk Sági-Kazár <mark.sagikazar@gmail.com> + */ +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 00000000000..66a92719e4a --- /dev/null +++ b/vendor/php-http/client-common/src/Exception/BatchException.php @@ -0,0 +1,39 @@ +<?php + +namespace Http\Client\Common\Exception; + +use Http\Client\Exception\TransferException; +use Http\Client\Common\BatchResult; + +/** + * This exception is thrown when HttpClient::sendRequests led to at least one failure. + * + * It gives access to a BatchResult with the request-exception and request-response pairs. + * + * @author Márk Sági-Kazár <mark.sagikazar@gmail.com> + */ +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 00000000000..73ec521e173 --- /dev/null +++ b/vendor/php-http/client-common/src/Exception/CircularRedirectionException.php @@ -0,0 +1,14 @@ +<?php + +namespace Http\Client\Common\Exception; + +use Http\Client\Exception\HttpException; + +/** + * Thrown when circular redirection is detected. + * + * @author Joel Wurtz <joel.wurtz@gmail.com> + */ +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 00000000000..b1f6cc855eb --- /dev/null +++ b/vendor/php-http/client-common/src/Exception/ClientErrorException.php @@ -0,0 +1,14 @@ +<?php + +namespace Http\Client\Common\Exception; + +use Http\Client\Exception\HttpException; + +/** + * Thrown when there is a client error (4xx). + * + * @author Joel Wurtz <joel.wurtz@gmail.com> + */ +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 00000000000..5d33f983836 --- /dev/null +++ b/vendor/php-http/client-common/src/Exception/HttpClientNotFoundException.php @@ -0,0 +1,14 @@ +<?php + +namespace Http\Client\Common\Exception; + +use Http\Client\Exception\TransferException; + +/** + * Thrown when a http client cannot be chosen in a pool. + * + * @author Joel Wurtz <joel.wurtz@gmail.com> + */ +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 00000000000..e834124df60 --- /dev/null +++ b/vendor/php-http/client-common/src/Exception/LoopException.php @@ -0,0 +1,14 @@ +<?php + +namespace Http\Client\Common\Exception; + +use Http\Client\Exception\RequestException; + +/** + * Thrown when the Plugin Client detects an endless loop. + * + * @author Joel Wurtz <joel.wurtz@gmail.com> + */ +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 00000000000..ae514cd738c --- /dev/null +++ b/vendor/php-http/client-common/src/Exception/MultipleRedirectionException.php @@ -0,0 +1,14 @@ +<?php + +namespace Http\Client\Common\Exception; + +use Http\Client\Exception\HttpException; + +/** + * Redirect location cannot be chosen. + * + * @author Joel Wurtz <joel.wurtz@gmail.com> + */ +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 00000000000..665d72418b0 --- /dev/null +++ b/vendor/php-http/client-common/src/Exception/ServerErrorException.php @@ -0,0 +1,14 @@ +<?php + +namespace Http\Client\Common\Exception; + +use Http\Client\Exception\HttpException; + +/** + * Thrown when there is a server error (5xx). + * + * @author Joel Wurtz <joel.wurtz@gmail.com> + */ +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 00000000000..58f88132d26 --- /dev/null +++ b/vendor/php-http/client-common/src/FlexibleHttpClient.php @@ -0,0 +1,39 @@ +<?php + +namespace Http\Client\Common; + +use Http\Client\HttpAsyncClient; +use Http\Client\HttpClient; + +/** + * A flexible http client, which implements both interface and will emulate + * one contract, the other, or none at all depending on the injected client contract. + * + * @author Joel Wurtz <joel.wurtz@gmail.com> + */ +final class FlexibleHttpClient implements HttpClient, HttpAsyncClient +{ + use HttpClientDecorator; + use HttpAsyncClientDecorator; + + /** + * @param HttpClient|HttpAsyncClient $client + */ + public function __construct($client) + { + if (!($client instanceof HttpClient) && !($client instanceof HttpAsyncClient)) { + 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)) { + $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 00000000000..6eb576cdb15 --- /dev/null +++ b/vendor/php-http/client-common/src/HttpAsyncClientDecorator.php @@ -0,0 +1,29 @@ +<?php + +namespace Http\Client\Common; + +use Http\Client\HttpAsyncClient; +use Psr\Http\Message\RequestInterface; + +/** + * Decorates an HTTP Async Client. + * + * @author Márk Sági-Kazár <mark.sagikazar@gmail.com> + */ +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 00000000000..c0ba354f87c --- /dev/null +++ b/vendor/php-http/client-common/src/HttpAsyncClientEmulator.php @@ -0,0 +1,36 @@ +<?php + +namespace Http\Client\Common; + +use Http\Client\Exception; +use Http\Client\Promise; +use Psr\Http\Message\RequestInterface; + +/** + * Emulates an HTTP Async Client in an HTTP Client. + * + * @author Márk Sági-Kazár <mark.sagikazar@gmail.com> + */ +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 00000000000..a33d5efd79e --- /dev/null +++ b/vendor/php-http/client-common/src/HttpClientDecorator.php @@ -0,0 +1,29 @@ +<?php + +namespace Http\Client\Common; + +use Http\Client\HttpClient; +use Psr\Http\Message\RequestInterface; + +/** + * Decorates an HTTP Client. + * + * @author Márk Sági-Kazár <mark.sagikazar@gmail.com> + */ +trait HttpClientDecorator +{ + /** + * @var HttpClient + */ + 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 00000000000..dbec1aba330 --- /dev/null +++ b/vendor/php-http/client-common/src/HttpClientEmulator.php @@ -0,0 +1,32 @@ +<?php + +namespace Http\Client\Common; + +use Psr\Http\Message\RequestInterface; + +/** + * Emulates an HTTP Client in an HTTP Async Client. + * + * @author Márk Sági-Kazár <mark.sagikazar@gmail.com> + */ +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 00000000000..7ac292ca138 --- /dev/null +++ b/vendor/php-http/client-common/src/HttpClientPool.php @@ -0,0 +1,59 @@ +<?php + +namespace Http\Client\Common; + +use Http\Client\Common\Exception\HttpClientNotFoundException; +use Http\Client\HttpAsyncClient; +use Http\Client\HttpClient; +use Psr\Http\Message\RequestInterface; + +/** + * A http client pool allows to send requests on a pool of different http client using a specific strategy (least used, + * round robin, ...). + */ +abstract class HttpClientPool implements HttpAsyncClient, HttpClient +{ + /** + * @var HttpClientPoolItem[] + */ + protected $clientPool = []; + + /** + * Add a client to the pool. + * + * @param HttpClient|HttpAsyncClient|HttpClientPoolItem $client + */ + public function addHttpClient($client) + { + if (!$client instanceof HttpClientPoolItem) { + $client = new HttpClientPoolItem($client); + } + + $this->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 00000000000..6299cceb60f --- /dev/null +++ b/vendor/php-http/client-common/src/HttpClientPool/LeastUsedClientPool.php @@ -0,0 +1,45 @@ +<?php + +namespace Http\Client\Common\HttpClientPool; + +use Http\Client\Common\Exception\HttpClientNotFoundException; +use Http\Client\Common\HttpClientPool; +use Http\Client\Common\HttpClientPoolItem; + +/** + * LeastUsedClientPool will choose the client with the less current request in the pool. + * + * This strategy is only useful when doing async request + * + * @author Joel Wurtz <joel.wurtz@gmail.com> + */ +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 00000000000..3255f865df3 --- /dev/null +++ b/vendor/php-http/client-common/src/HttpClientPool/RandomClientPool.php @@ -0,0 +1,31 @@ +<?php + +namespace Http\Client\Common\HttpClientPool; + +use Http\Client\Common\Exception\HttpClientNotFoundException; +use Http\Client\Common\HttpClientPool; +use Http\Client\Common\HttpClientPoolItem; + +/** + * RoundRobinClientPool will choose the next client in the pool. + * + * @author Joel Wurtz <joel.wurtz@gmail.com> + */ +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 00000000000..8d8e40a013b --- /dev/null +++ b/vendor/php-http/client-common/src/HttpClientPool/RoundRobinClientPool.php @@ -0,0 +1,41 @@ +<?php + +namespace Http\Client\Common\HttpClientPool; + +use Http\Client\Common\Exception\HttpClientNotFoundException; +use Http\Client\Common\HttpClientPool; + +/** + * RoundRobinClientPool will choose the next client in the pool. + * + * @author Joel Wurtz <joel.wurtz@gmail.com> + */ +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 00000000000..09cd6ddf1c9 --- /dev/null +++ b/vendor/php-http/client-common/src/HttpClientPoolItem.php @@ -0,0 +1,178 @@ +<?php + +namespace Http\Client\Common; + +use Http\Client\HttpAsyncClient; +use Http\Client\HttpClient; +use Psr\Http\Message\RequestInterface; +use Http\Client\Exception; + +/** + * A HttpClientPoolItem represent a HttpClient inside a Pool. + * + * It is disabled when a request failed and can be reenable after a certain number of seconds + * It also keep tracks of the current number of request the client is currently sending (only usable for async method) + * + * @author Joel Wurtz <joel.wurtz@gmail.com> + */ +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 00000000000..9f721336a17 --- /dev/null +++ b/vendor/php-http/client-common/src/HttpClientRouter.php @@ -0,0 +1,74 @@ +<?php + +namespace Http\Client\Common; + +use Http\Client\Exception\RequestException; +use Http\Client\HttpAsyncClient; +use Http\Client\HttpClient; +use Http\Message\RequestMatcher; +use Psr\Http\Message\RequestInterface; + +/** + * Route a request to a specific client in the stack based using a RequestMatcher. + * + * @author Joel Wurtz <joel.wurtz@gmail.com> + */ +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 00000000000..58804fc243f --- /dev/null +++ b/vendor/php-http/client-common/src/HttpMethodsClient.php @@ -0,0 +1,205 @@ +<?php + +namespace Http\Client\Common; + +use Http\Client\Exception; +use Http\Client\HttpClient; +use Http\Message\RequestFactory; +use Psr\Http\Message\RequestInterface; +use Psr\Http\Message\ResponseInterface; +use Psr\Http\Message\StreamInterface; +use Psr\Http\Message\UriInterface; + +/** + * Convenience HTTP client that integrates the MessageFactory in order to send + * requests in the following form:. + * + * $client + * ->get('/foo') + * ->post('/bar') + * ; + * + * The client also exposes the sendRequest methods of the wrapped HttpClient. + * + * @author Márk Sági-Kazár <mark.sagikazar@gmail.com> + * @author David Buchmann <mail@davidbu.ch> + */ +class HttpMethodsClient implements HttpClient +{ + /** + * @var HttpClient + */ + private $httpClient; + + /** + * @var RequestFactory + */ + private $requestFactory; + + /** + * @param HttpClient $httpClient The client to send requests with + * @param RequestFactory $requestFactory The message factory to create requests + */ + public function __construct(HttpClient $httpClient, RequestFactory $requestFactory) + { + $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 00000000000..89a2a622d46 --- /dev/null +++ b/vendor/php-http/client-common/src/Plugin.php @@ -0,0 +1,32 @@ +<?php + +namespace Http\Client\Common; + +use Http\Promise\Promise; +use Psr\Http\Message\RequestInterface; + +/** + * A plugin is a middleware to transform the request and/or the response. + * + * The plugin can: + * - break the chain and return a response + * - dispatch the request to the next middleware + * - restart the request + * + * @author Joel Wurtz <joel.wurtz@gmail.com> + */ +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 00000000000..29ab8ae8409 --- /dev/null +++ b/vendor/php-http/client-common/src/Plugin/AddHostPlugin.php @@ -0,0 +1,77 @@ +<?php + +namespace Http\Client\Common\Plugin; + +use Http\Client\Common\Plugin; +use Psr\Http\Message\RequestInterface; +use Psr\Http\Message\UriInterface; +use Symfony\Component\OptionsResolver\OptionsResolver; + +/** + * Add schema, host and port to a request. Can be set to overwrite the schema and host if desired. + * + * @author Tobias Nyholm <tobias.nyholm@gmail.com> + */ +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 00000000000..e24d61a4e42 --- /dev/null +++ b/vendor/php-http/client-common/src/Plugin/AddPathPlugin.php @@ -0,0 +1,48 @@ +<?php + +namespace Http\Client\Common\Plugin; + +use Http\Client\Common\Plugin; +use Psr\Http\Message\RequestInterface; +use Psr\Http\Message\UriInterface; + +/** + * Prepend a base path to the request URI. Useful for base API URLs like http://domain.com/api. + * + * @author Sullivan Senechal <soullivaneuh@gmail.com> + */ +final class AddPathPlugin implements Plugin +{ + /** + * @var UriInterface + */ + private $uri; + + /** + * @param UriInterface $uri + */ + public function __construct(UriInterface $uri) + { + if ('' === $uri->getPath()) { + throw new \LogicException('URI path cannot be empty'); + } + + if ('/' === substr($uri->getPath(), -1)) { + throw new \LogicException('URI path cannot end with a slash.'); + } + + $this->uri = $uri; + } + + /** + * {@inheritdoc} + */ + public function handleRequest(RequestInterface $request, callable $next, callable $first) + { + $request = $request->withUri($request->getUri() + ->withPath($this->uri->getPath().$request->getUri()->getPath()) + ); + + 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 00000000000..194712fcde0 --- /dev/null +++ b/vendor/php-http/client-common/src/Plugin/AuthenticationPlugin.php @@ -0,0 +1,38 @@ +<?php + +namespace Http\Client\Common\Plugin; + +use Http\Client\Common\Plugin; +use Http\Message\Authentication; +use Psr\Http\Message\RequestInterface; + +/** + * Send an authenticated request. + * + * @author Joel Wurtz <joel.wurtz@gmail.com> + */ +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 00000000000..2c2a7752cf4 --- /dev/null +++ b/vendor/php-http/client-common/src/Plugin/BaseUriPlugin.php @@ -0,0 +1,54 @@ +<?php + +namespace Http\Client\Common\Plugin; + +use Http\Client\Common\Plugin; +use Psr\Http\Message\RequestInterface; +use Psr\Http\Message\UriInterface; + +/** + * Combines the AddHostPlugin and AddPathPlugin. + * + * @author Sullivan Senechal <soullivaneuh@gmail.com> + */ +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 00000000000..0f7aafaeda3 --- /dev/null +++ b/vendor/php-http/client-common/src/Plugin/ContentLengthPlugin.php @@ -0,0 +1,36 @@ +<?php + +namespace Http\Client\Common\Plugin; + +use Http\Client\Common\Plugin; +use Http\Message\Encoding\ChunkStream; +use Psr\Http\Message\RequestInterface; + +/** + * Allow to set the correct content length header on the request or to transfer it as a chunk if not possible. + * + * @author Joel Wurtz <joel.wurtz@gmail.com> + */ +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 00000000000..8ef1d62b34b --- /dev/null +++ b/vendor/php-http/client-common/src/Plugin/ContentTypePlugin.php @@ -0,0 +1,123 @@ +<?php + +namespace Http\Client\Common\Plugin; + +use Http\Client\Common\Plugin; +use Psr\Http\Message\RequestInterface; +use Psr\Http\Message\StreamInterface; +use Symfony\Component\OptionsResolver\OptionsResolver; + +/** + * Allow to set the correct content type header on the request automatically only if it is not set. + * + * @author Karim Pinchon <karim.pinchon@gmail.com> + */ +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 00000000000..59ee90da19d --- /dev/null +++ b/vendor/php-http/client-common/src/Plugin/CookiePlugin.php @@ -0,0 +1,180 @@ +<?php + +namespace Http\Client\Common\Plugin; + +use Http\Client\Common\Plugin; +use Http\Client\Exception\TransferException; +use Http\Message\Cookie; +use Http\Message\CookieJar; +use Http\Message\CookieUtil; +use Http\Message\Exception\UnexpectedValueException; +use Psr\Http\Message\RequestInterface; +use Psr\Http\Message\ResponseInterface; + +/** + * Handle request cookies. + * + * @author Joel Wurtz <joel.wurtz@gmail.com> + */ +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) + { + 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; + } + + $request = $request->withAddedHeader('Cookie', sprintf('%s=%s', $cookie->getName(), $cookie->getValue())); + } + + 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 + ), + null, + $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 00000000000..b661b613ac7 --- /dev/null +++ b/vendor/php-http/client-common/src/Plugin/DecoderPlugin.php @@ -0,0 +1,140 @@ +<?php + +namespace Http\Client\Common\Plugin; + +use Http\Client\Common\Plugin; +use Http\Message\Encoding; +use Psr\Http\Message\RequestInterface; +use Psr\Http\Message\ResponseInterface; +use Psr\Http\Message\StreamInterface; +use Symfony\Component\OptionsResolver\OptionsResolver; + +/** + * Allow to decode response body with a chunk, deflate, compress or gzip encoding. + * + * If zlib is not installed, only chunked encoding can be handled. + * + * If Content-Encoding is not disabled, the plugin will add an Accept-Encoding header for the encoding methods it supports. + * + * @author Joel Wurtz <joel.wurtz@gmail.com> + */ +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); + } + + $response = $response->withHeader($headerName, $newEncodings); + } + + 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 00000000000..f09d3b161a8 --- /dev/null +++ b/vendor/php-http/client-common/src/Plugin/ErrorPlugin.php @@ -0,0 +1,55 @@ +<?php + +namespace Http\Client\Common\Plugin; + +use Http\Client\Common\Exception\ClientErrorException; +use Http\Client\Common\Exception\ServerErrorException; +use Http\Client\Common\Plugin; +use Psr\Http\Message\RequestInterface; +use Psr\Http\Message\ResponseInterface; + +/** + * Throw exception when the response of a request is not acceptable. + * + * Status codes 400-499 lead to a ClientErrorException, status 500-599 to a ServerErrorException. + * + * @author Joel Wurtz <joel.wurtz@gmail.com> + */ +final class ErrorPlugin implements Plugin +{ + /** + * {@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 ($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 00000000000..26fd8134585 --- /dev/null +++ b/vendor/php-http/client-common/src/Plugin/HeaderAppendPlugin.php @@ -0,0 +1,45 @@ +<?php + +namespace Http\Client\Common\Plugin; + +use Http\Client\Common\Plugin; +use Psr\Http\Message\RequestInterface; + +/** + * Append headers to the request. + * + * If the header already exists the value will be appended to the current value. + * + * This only makes sense for headers that can have multiple values like 'Forwarded' + * + * @see https://en.wikipedia.org/wiki/List_of_HTTP_header_fields + * + * @author Soufiane Ghzal <sghzal@gmail.com> + */ +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 00000000000..6dfc1115a06 --- /dev/null +++ b/vendor/php-http/client-common/src/Plugin/HeaderDefaultsPlugin.php @@ -0,0 +1,43 @@ +<?php + +namespace Http\Client\Common\Plugin; + +use Http\Client\Common\Plugin; +use Psr\Http\Message\RequestInterface; + +/** + * Set header to default value if it does not exist. + * + * If a given header already exists the value wont be replaced and the request wont be changed. + * + * @author Soufiane Ghzal <sghzal@gmail.com> + */ +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 00000000000..fc9c19d1aad --- /dev/null +++ b/vendor/php-http/client-common/src/Plugin/HeaderRemovePlugin.php @@ -0,0 +1,41 @@ +<?php + +namespace Http\Client\Common\Plugin; + +use Http\Client\Common\Plugin; +use Psr\Http\Message\RequestInterface; + +/** + * Removes headers from the request. + * + * @author Soufiane Ghzal <sghzal@gmail.com> + */ +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 00000000000..75f11d40aaa --- /dev/null +++ b/vendor/php-http/client-common/src/Plugin/HeaderSetPlugin.php @@ -0,0 +1,41 @@ +<?php + +namespace Http\Client\Common\Plugin; + +use Http\Client\Common\Plugin; +use Psr\Http\Message\RequestInterface; + +/** + * Set headers on the request. + * + * If the header does not exist it wil be set, if the header already exists it will be replaced. + * + * @author Soufiane Ghzal <sghzal@gmail.com> + */ +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 00000000000..5abddbd8412 --- /dev/null +++ b/vendor/php-http/client-common/src/Plugin/HistoryPlugin.php @@ -0,0 +1,49 @@ +<?php + +namespace Http\Client\Common\Plugin; + +use Http\Client\Common\Plugin; +use Http\Client\Exception; +use Psr\Http\Message\RequestInterface; +use Psr\Http\Message\ResponseInterface; + +/** + * Record HTTP calls. + * + * @author Joel Wurtz <joel.wurtz@gmail.com> + */ +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 00000000000..15f309569fb --- /dev/null +++ b/vendor/php-http/client-common/src/Plugin/Journal.php @@ -0,0 +1,31 @@ +<?php + +namespace Http\Client\Common\Plugin; + +use Http\Client\Exception; +use Psr\Http\Message\RequestInterface; +use Psr\Http\Message\ResponseInterface; + +/** + * Records history of HTTP calls. + * + * @author Joel Wurtz <joel.wurtz@gmail.com> + */ +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 00000000000..6c1e32cd307 --- /dev/null +++ b/vendor/php-http/client-common/src/Plugin/QueryDefaultsPlugin.php @@ -0,0 +1,54 @@ +<?php + +namespace Http\Client\Common\Plugin; + +use Http\Client\Common\Plugin; +use Psr\Http\Message\RequestInterface; + +/** + * Set query to default value if it does not exist. + * + * If a given query parameter already exists the value wont be replaced and the request wont be changed. + * + * @author Tobias Nyholm <tobias.nyholm@gmail.com> + */ +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) + { + foreach ($this->queryParams as $name => $value) { + $uri = $request->getUri(); + $array = []; + parse_str($uri->getQuery(), $array); + + // If query value is not found + if (!isset($array[$name])) { + $array[$name] = $value; + + // Create a new request with the new URI with the added query param + $request = $request->withUri( + $uri->withQuery(http_build_query($array)) + ); + } + } + + 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 00000000000..d2f442edb8f --- /dev/null +++ b/vendor/php-http/client-common/src/Plugin/RedirectPlugin.php @@ -0,0 +1,270 @@ +<?php + +namespace Http\Client\Common\Plugin; + +use Http\Client\Common\Exception\CircularRedirectionException; +use Http\Client\Common\Exception\MultipleRedirectionException; +use Http\Client\Common\Plugin; +use Http\Client\Exception\HttpException; +use Psr\Http\Message\MessageInterface; +use Psr\Http\Message\RequestInterface; +use Psr\Http\Message\ResponseInterface; +use Psr\Http\Message\UriInterface; +use Symfony\Component\OptionsResolver\OptionsResolver; + +/** + * Follow redirections. + * + * @author Joel Wurtz <joel.wurtz@gmail.com> + */ +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 00000000000..5f72b02d8ac --- /dev/null +++ b/vendor/php-http/client-common/src/Plugin/RequestMatcherPlugin.php @@ -0,0 +1,47 @@ +<?php + +namespace Http\Client\Common\Plugin; + +use Http\Client\Common\Plugin; +use Http\Message\RequestMatcher; +use Psr\Http\Message\RequestInterface; + +/** + * Apply a delegated plugin based on a request match. + * + * @author Márk Sági-Kazár <mark.sagikazar@gmail.com> + */ +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 00000000000..8446246e673 --- /dev/null +++ b/vendor/php-http/client-common/src/Plugin/RetryPlugin.php @@ -0,0 +1,122 @@ +<?php + +namespace Http\Client\Common\Plugin; + +use Http\Client\Common\Plugin; +use Http\Client\Exception; +use Psr\Http\Message\RequestInterface; +use Psr\Http\Message\ResponseInterface; +use Symfony\Component\OptionsResolver\OptionsResolver; + +/** + * Retry the request if an exception is thrown. + * + * By default will retry only one time. + * + * @author Joel Wurtz <joel.wurtz@gmail.com> + */ +final class RetryPlugin implements Plugin +{ + /** + * Number of retry before sending an exception. + * + * @var int + */ + private $retry; + + /** + * @var callable + */ + private $delay; + + /** + * @var callable + */ + private $decider; + + /** + * 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 $decider A callback that gets a request and an exception to decide after a failure whether the request should be retried. + * @var callable $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 = []) + { + $resolver = new OptionsResolver(); + $resolver->setDefaults([ + 'retries' => 1, + 'decider' => function (RequestInterface $request, Exception $e) { + return true; + }, + 'delay' => __CLASS__.'::defaultDelay', + ]); + $resolver->setAllowedTypes('retries', 'int'); + $resolver->setAllowedTypes('decider', 'callable'); + $resolver->setAllowedTypes('delay', 'callable'); + $options = $resolver->resolve($config); + + $this->retry = $options['retries']; + $this->decider = $options['decider']; + $this->delay = $options['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->decider, $request, $exception)) { + throw $exception; + } + + $time = call_user_func($this->delay, $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/PluginClient.php b/vendor/php-http/client-common/src/PluginClient.php new file mode 100644 index 00000000000..93aea8f2fda --- /dev/null +++ b/vendor/php-http/client-common/src/PluginClient.php @@ -0,0 +1,179 @@ +<?php + +namespace Http\Client\Common; + +use Http\Client\Common\Exception\LoopException; +use Http\Client\Exception as HttplugException; +use Http\Client\HttpAsyncClient; +use Http\Client\HttpClient; +use Http\Client\Promise\HttpFulfilledPromise; +use Http\Client\Promise\HttpRejectedPromise; +use Psr\Http\Message\RequestInterface; +use Symfony\Component\OptionsResolver\OptionsResolver; + +/** + * The client managing plugins and providing a decorator around HTTP Clients. + * + * @author Joel Wurtz <joel.wurtz@gmail.com> + */ +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) { + $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 00000000000..bd4c08f9a62 --- /dev/null +++ b/vendor/php-http/client-common/src/PluginClientFactory.php @@ -0,0 +1,62 @@ +<?php + +namespace Http\Client\Common; + +use Http\Client\HttpAsyncClient; +use Http\Client\HttpClient; + +/** + * Factory to create PluginClient instances. Using this factory instead of calling PluginClient constructor will enable + * the Symfony profiling without any configuration. + * + * @author Fabien Bourigault <bourigaultfabien@gmail.com> + */ +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 $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/guzzle6-adapter/CHANGELOG.md b/vendor/php-http/guzzle6-adapter/CHANGELOG.md new file mode 100644 index 00000000000..0fdb5069f33 --- /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 00000000000..48741e41658 --- /dev/null +++ b/vendor/php-http/guzzle6-adapter/LICENSE @@ -0,0 +1,20 @@ +Copyright (c) 2014-2015 Eric GELOEN <geloen.eric@gmail.com> +Copyright (c) 2015-2016 PHP HTTP Team <team@php-http.org> + +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 00000000000..623eb2f8d31 --- /dev/null +++ b/vendor/php-http/guzzle6-adapter/README.md @@ -0,0 +1,59 @@ +# Guzzle 6 HTTP Adapter + +[](https://github.com/php-http/guzzle6-adapter/releases) +[](LICENSE) +[](https://travis-ci.org/php-http/guzzle6-adapter) +[](https://scrutinizer-ci.com/g/php-http/guzzle6-adapter) +[](https://scrutinizer-ci.com/g/php-http/guzzle6-adapter) +[](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 00000000000..2f01d1a465d --- /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 00000000000..bd296146404 --- /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 00000000000..ded7494bddf --- /dev/null +++ b/vendor/php-http/guzzle6-adapter/src/Client.php @@ -0,0 +1,84 @@ +<?php + +namespace Http\Adapter\Guzzle6; + +use GuzzleHttp\Client as GuzzleClient; +use GuzzleHttp\ClientInterface; +use GuzzleHttp\HandlerStack; +use GuzzleHttp\Middleware; +use Http\Client\HttpAsyncClient; +use Http\Client\HttpClient; +use Psr\Http\Message\RequestInterface; + +/** + * HTTP Adapter for Guzzle 6. + * + * @author David de Boer <david@ddeboer.nl> + */ +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 00000000000..4d5eb8df207 --- /dev/null +++ b/vendor/php-http/guzzle6-adapter/src/Promise.php @@ -0,0 +1,140 @@ +<?php + +namespace Http\Adapter\Guzzle6; + +use GuzzleHttp\Exception as GuzzleExceptions; +use GuzzleHttp\Promise\PromiseInterface; +use Http\Client\Exception as HttplugException; +use Http\Promise\Promise as HttpPromise; +use Psr\Http\Message\RequestInterface; +use Psr\Http\Message\ResponseInterface; + +/** + * Wrapper around Guzzle promises. + * + * @author Joel Wurtz <joel.wurtz@gmail.com> + */ +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/multipart-stream-builder/CHANGELOG.md b/vendor/php-http/multipart-stream-builder/CHANGELOG.md new file mode 100644 index 00000000000..1f0ee3d7f32 --- /dev/null +++ b/vendor/php-http/multipart-stream-builder/CHANGELOG.md @@ -0,0 +1,60 @@ +# Change Log + +## 1.0.0 - 2017-05-21 + +No changes from 0.2.0. + +## 0.2.0 - 2017-02-20 + +You may do a BC update to version 0.2.0 if you are sure that you are not adding +multiple resources with the same name to the Builder. + +### Fixed + +- Make sure one can add resources with same name without overwrite. + +## 0.1.6 - 2017-02-16 + +### Fixed + +- Performance improvements by avoid using `uniqid()`. + +## 0.1.5 - 2017-02-14 + +### Fixed + +- Support for non-readable streams. This fix was needed because flaws in Guzzle, Zend and Slims implementations of PSR-7. + +## 0.1.4 - 2016-12-31 + +### Added + +- Added support for resetting the builder + +## 0.1.3 - 2016-12-22 + +### Added + +- Added `CustomMimetypeHelper` to allow you to configure custom mimetypes. + +### Changed + +- Using regular expression instead of `basename($filename)` because basename is depending on locale. + +## 0.1.2 - 2016-08-31 + +### Added + +- Support for Outlook msg files. + +## 0.1.1 - 2016-08-10 + +### Added + +- Support for Apple passbook. + +## 0.1.0 - 2016-07-19 + +### Added + +- Initial release diff --git a/vendor/php-http/multipart-stream-builder/LICENSE b/vendor/php-http/multipart-stream-builder/LICENSE new file mode 100644 index 00000000000..8e2c4a0b85c --- /dev/null +++ b/vendor/php-http/multipart-stream-builder/LICENSE @@ -0,0 +1,19 @@ +Copyright (c) 2015 PHP HTTP Team <team@php-http.org> + +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/multipart-stream-builder/README.md b/vendor/php-http/multipart-stream-builder/README.md new file mode 100644 index 00000000000..10ad0588b4b --- /dev/null +++ b/vendor/php-http/multipart-stream-builder/README.md @@ -0,0 +1,37 @@ +# PSR-7 Multipart Stream Builder + +[](https://github.com/php-http/multipart-stream-builder/releases) +[](https://travis-ci.org/php-http/multipart-stream-builder) +[](https://scrutinizer-ci.com/g/php-http/multipart-stream-builder) +[](https://scrutinizer-ci.com/g/php-http/multipart-stream-builder) +[](https://packagist.org/packages/php-http/multipart-stream-builder) + +**A builder for Multipart PSR-7 Streams. The builder create streams independenly form any PSR-7 implementation.** + + +## Install + +Via Composer + +``` bash +$ composer require php-http/multipart-stream-builder +``` + +## Documentation + +Please see the [official documentation](http://php-http.readthedocs.org/en/latest/components/multipart-stream-builder.html). + + +## 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/multipart-stream-builder/appveyor.yml b/vendor/php-http/multipart-stream-builder/appveyor.yml new file mode 100644 index 00000000000..8d7af73f722 --- /dev/null +++ b/vendor/php-http/multipart-stream-builder/appveyor.yml @@ -0,0 +1,39 @@ +build: false +platform: + - x86 + - x64 + +clone_folder: c:\projects\php-http\multipart-stream-builder + +cache: + - c:\tools\php -> appveyor.yml + +init: + - SET PATH=c:\php;%PATH% + - SET COMPOSER_NO_INTERACTION=1 + - SET PHP=1 + + +install: + - IF EXIST c:\php (SET PHP=0) ELSE (mkdir c:\php) + - cd c:\php + - IF %PHP%==1 appveyor DownloadFile http://windows.php.net/downloads/releases/archives/php-7.0.0-nts-Win32-VC14-x86.zip + - IF %PHP%==1 7z x php-7.0.0-nts-Win32-VC14-x86.zip -y >nul + - IF %PHP%==1 del /Q *.zip + - IF %PHP%==1 echo @php %%~dp0composer.phar %%* > composer.bat + - IF %PHP%==1 copy /Y php.ini-development php.ini + - IF %PHP%==1 echo max_execution_time=1200 >> php.ini + - IF %PHP%==1 echo date.timezone="UTC" >> php.ini + - IF %PHP%==1 echo extension_dir=ext >> php.ini + - IF %PHP%==1 echo extension=php_openssl.dll >> php.ini + - IF %PHP%==1 echo extension=php_mbstring.dll >> php.ini + - IF %PHP%==1 echo extension=php_fileinfo.dll >> php.ini + - appveyor DownloadFile https://getcomposer.org/composer.phar + - cd c:\projects\php-http\multipart-stream-builder + - mkdir %APPDATA%\Composer + - composer update --prefer-dist --no-progress --ansi + +test_script: + - cd c:\projects\php-http\multipart-stream-builder + - vendor\bin\phpunit.bat --verbose + diff --git a/vendor/php-http/multipart-stream-builder/composer.json b/vendor/php-http/multipart-stream-builder/composer.json new file mode 100644 index 00000000000..1ada3605c54 --- /dev/null +++ b/vendor/php-http/multipart-stream-builder/composer.json @@ -0,0 +1,43 @@ +{ + "name": "php-http/multipart-stream-builder", + "description": "A builder class that help you create a multipart stream", + "license": "MIT", + "keywords": ["http", "factory", "message", "stream", "multipart stream"], + "homepage": "http://php-http.org", + "authors": [ + { + "name": "Tobias Nyholm", + "email": "tobias.nyholm@gmail.com" + } + ], + "require": { + "php": "^5.5 || ^7.0", + "psr/http-message": "^1.0", + "php-http/message-factory": "^1.0.2", + "php-http/discovery": "^1.0" + }, + "require-dev": { + "phpunit/phpunit": "^4.8 || ^5.4", + "php-http/message": "^1.5", + "zendframework/zend-diactoros": "^1.3.5" + }, + "autoload": { + "psr-4": { + "Http\\Message\\MultipartStream\\": "src/" + } + }, + "autoload-dev": { + "psr-4": { + "tests\\Http\\Message\\MultipartStream\\": "tests/" + } + }, + "scripts": { + "test": "vendor/bin/phpunit", + "test-ci": "vendor/bin/phpunit --coverage-text --coverage-clover=build/coverage.xml" + }, + "extra": { + "branch-alias": { + "dev-master": "0.3-dev" + } + } +} diff --git a/vendor/php-http/multipart-stream-builder/src/ApacheMimetypeHelper.php b/vendor/php-http/multipart-stream-builder/src/ApacheMimetypeHelper.php new file mode 100644 index 00000000000..6e4ca660246 --- /dev/null +++ b/vendor/php-http/multipart-stream-builder/src/ApacheMimetypeHelper.php @@ -0,0 +1,142 @@ +<?php + +namespace Http\Message\MultipartStream; + +/** + * This class helps to find the proper mime types. The source of this file is taken + * from Guzzle. + * + * @author Michael Dowling and contributors to guzzlehttp/psr7 + * @author Tobias Nyholm <tobias.nyholm@gmail.com> + */ +class ApacheMimetypeHelper implements MimetypeHelper +{ + /** + * {@inheritdoc} + * + * @see http://svn.apache.org/repos/asf/httpd/httpd/branches/1.3.x/conf/mime.types + */ + public function getMimetypeFromFilename($filename) + { + return $this->getMimetypeFromExtension(pathinfo($filename, PATHINFO_EXTENSION)); + } + + /** + * {@inheritdoc} + * + * @see http://svn.apache.org/repos/asf/httpd/httpd/branches/1.3.x/conf/mime.types + */ + public function getMimetypeFromExtension($extension) + { + static $mimetypes = [ + '7z' => 'application/x-7z-compressed', + 'aac' => 'audio/x-aac', + 'ai' => 'application/postscript', + 'aif' => 'audio/x-aiff', + 'asc' => 'text/plain', + 'asf' => 'video/x-ms-asf', + 'atom' => 'application/atom+xml', + 'avi' => 'video/x-msvideo', + 'bmp' => 'image/bmp', + 'bz2' => 'application/x-bzip2', + 'cer' => 'application/pkix-cert', + 'crl' => 'application/pkix-crl', + 'crt' => 'application/x-x509-ca-cert', + 'css' => 'text/css', + 'csv' => 'text/csv', + 'cu' => 'application/cu-seeme', + 'deb' => 'application/x-debian-package', + 'doc' => 'application/msword', + 'docx' => 'application/vnd.openxmlformats-officedocument.wordprocessingml.document', + 'dvi' => 'application/x-dvi', + 'eot' => 'application/vnd.ms-fontobject', + 'eps' => 'application/postscript', + 'epub' => 'application/epub+zip', + 'etx' => 'text/x-setext', + 'flac' => 'audio/flac', + 'flv' => 'video/x-flv', + 'gif' => 'image/gif', + 'gz' => 'application/gzip', + 'htm' => 'text/html', + 'html' => 'text/html', + 'ico' => 'image/x-icon', + 'ics' => 'text/calendar', + 'ini' => 'text/plain', + 'iso' => 'application/x-iso9660-image', + 'jar' => 'application/java-archive', + 'jpe' => 'image/jpeg', + 'jpeg' => 'image/jpeg', + 'jpg' => 'image/jpeg', + 'js' => 'text/javascript', + 'json' => 'application/json', + 'latex' => 'application/x-latex', + 'log' => 'text/plain', + 'm4a' => 'audio/mp4', + 'm4v' => 'video/mp4', + 'mid' => 'audio/midi', + 'midi' => 'audio/midi', + 'mov' => 'video/quicktime', + 'mp3' => 'audio/mpeg', + 'mp4' => 'video/mp4', + 'mp4a' => 'audio/mp4', + 'mp4v' => 'video/mp4', + 'mpe' => 'video/mpeg', + 'mpeg' => 'video/mpeg', + 'mpg' => 'video/mpeg', + 'mpg4' => 'video/mp4', + 'oga' => 'audio/ogg', + 'ogg' => 'audio/ogg', + 'ogv' => 'video/ogg', + 'ogx' => 'application/ogg', + 'pbm' => 'image/x-portable-bitmap', + 'pdf' => 'application/pdf', + 'pgm' => 'image/x-portable-graymap', + 'png' => 'image/png', + 'pnm' => 'image/x-portable-anymap', + 'ppm' => 'image/x-portable-pixmap', + 'ppt' => 'application/vnd.ms-powerpoint', + 'pptx' => 'application/vnd.openxmlformats-officedocument.presentationml.presentation', + 'ps' => 'application/postscript', + 'qt' => 'video/quicktime', + 'rar' => 'application/x-rar-compressed', + 'ras' => 'image/x-cmu-raster', + 'rss' => 'application/rss+xml', + 'rtf' => 'application/rtf', + 'sgm' => 'text/sgml', + 'sgml' => 'text/sgml', + 'svg' => 'image/svg+xml', + 'swf' => 'application/x-shockwave-flash', + 'tar' => 'application/x-tar', + 'tif' => 'image/tiff', + 'tiff' => 'image/tiff', + 'torrent' => 'application/x-bittorrent', + 'ttf' => 'application/x-font-ttf', + 'txt' => 'text/plain', + 'wav' => 'audio/x-wav', + 'webm' => 'video/webm', + 'wma' => 'audio/x-ms-wma', + 'wmv' => 'video/x-ms-wmv', + 'woff' => 'application/x-font-woff', + 'wsdl' => 'application/wsdl+xml', + 'xbm' => 'image/x-xbitmap', + 'xls' => 'application/vnd.ms-excel', + 'xlsx' => 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet', + 'xml' => 'application/xml', + 'xpm' => 'image/x-xpixmap', + 'xwd' => 'image/x-xwindowdump', + 'yaml' => 'text/yaml', + 'yml' => 'text/yaml', + 'zip' => 'application/zip', + + // Non-Apache standard + 'pkpass' => 'application/vnd.apple.pkpass', + 'msg' => 'application/vnd.ms-outlook', + ]; + + $extension = strtolower($extension); + + return isset($mimetypes[$extension]) + ? $mimetypes[$extension] + : null; + } +} diff --git a/vendor/php-http/multipart-stream-builder/src/CustomMimetypeHelper.php b/vendor/php-http/multipart-stream-builder/src/CustomMimetypeHelper.php new file mode 100644 index 00000000000..9f3f0d9211e --- /dev/null +++ b/vendor/php-http/multipart-stream-builder/src/CustomMimetypeHelper.php @@ -0,0 +1,51 @@ +<?php + +namespace Http\Message\MultipartStream; + +/** + * Let you add your own mimetypes. The mimetype lookup will fallback on the ApacheMimeTypeHelper. + * + * @author Tobias Nyholm <tobias.nyholm@gmail.com> + */ +class CustomMimetypeHelper extends ApacheMimetypeHelper +{ + /** + * @var array + */ + private $mimetypes = []; + + /** + * @param array $mimetypes should be of type extension => mimetype + */ + public function __construct(array $mimetypes = []) + { + $this->mimetypes = $mimetypes; + } + + /** + * @param string $extension + * @param string $mimetype + * + * @return $this + */ + public function addMimetype($extension, $mimetype) + { + $this->mimetypes[$extension] = $mimetype; + + return $this; + } + + /** + * {@inheritdoc} + * + * Check if we have any defined mimetypes and of not fallback to ApacheMimetypeHelper + */ + public function getMimetypeFromExtension($extension) + { + $extension = strtolower($extension); + + return isset($this->mimetypes[$extension]) + ? $this->mimetypes[$extension] + : parent::getMimetypeFromExtension($extension); + } +} diff --git a/vendor/php-http/multipart-stream-builder/src/MimetypeHelper.php b/vendor/php-http/multipart-stream-builder/src/MimetypeHelper.php new file mode 100644 index 00000000000..fa7cf180037 --- /dev/null +++ b/vendor/php-http/multipart-stream-builder/src/MimetypeHelper.php @@ -0,0 +1,27 @@ +<?php + +namespace Http\Message\MultipartStream; + +/** + * @author Tobias Nyholm <tobias.nyholm@gmail.com> + */ +interface MimetypeHelper +{ + /** + * Determines the mimetype of a file by looking at its extension. + * + * @param string $filename + * + * @return null|string + */ + public function getMimetypeFromFilename($filename); + + /** + * Maps a file extensions to a mimetype. + * + * @param string $extension The file extension + * + * @return string|null + */ + public function getMimetypeFromExtension($extension); +} diff --git a/vendor/php-http/multipart-stream-builder/src/MultipartStreamBuilder.php b/vendor/php-http/multipart-stream-builder/src/MultipartStreamBuilder.php new file mode 100644 index 00000000000..3421d734874 --- /dev/null +++ b/vendor/php-http/multipart-stream-builder/src/MultipartStreamBuilder.php @@ -0,0 +1,278 @@ +<?php + +namespace Http\Message\MultipartStream; + +use Http\Discovery\StreamFactoryDiscovery; +use Http\Message\StreamFactory; +use Psr\Http\Message\StreamInterface; + +/** + * Build your own Multipart stream. A Multipart stream is a collection of streams separated with a $bounary. This + * class helps you to create a Multipart stream with stream implementations from any PSR7 library. + * + * @author Michael Dowling and contributors to guzzlehttp/psr7 + * @author Tobias Nyholm <tobias.nyholm@gmail.com> + */ +class MultipartStreamBuilder +{ + /** + * @var StreamFactory + */ + private $streamFactory; + + /** + * @var MimetypeHelper + */ + private $mimetypeHelper; + + /** + * @var string + */ + private $boundary; + + /** + * @var array Element where each Element is an array with keys ['contents', 'headers', 'filename'] + */ + private $data = []; + + /** + * @param StreamFactory|null $streamFactory + */ + public function __construct(StreamFactory $streamFactory = null) + { + $this->streamFactory = $streamFactory ?: StreamFactoryDiscovery::find(); + } + + /** + * Add a resource to the Multipart Stream. + * + * @param string $name the formpost name + * @param string|resource|StreamInterface $resource + * @param array $options { + * + * @var array $headers additional headers ['header-name' => 'header-value'] + * @var string $filename + * } + * + * @return MultipartStreamBuilder + */ + public function addResource($name, $resource, array $options = []) + { + $stream = $this->streamFactory->createStream($resource); + + // validate options['headers'] exists + if (!isset($options['headers'])) { + $options['headers'] = []; + } + + // Try to add filename if it is missing + if (empty($options['filename'])) { + $options['filename'] = null; + $uri = $stream->getMetadata('uri'); + if (substr($uri, 0, 6) !== 'php://') { + $options['filename'] = $uri; + } + } + + $this->prepareHeaders($name, $stream, $options['filename'], $options['headers']); + $this->data[] = ['contents' => $stream, 'headers' => $options['headers'], 'filename' => $options['filename']]; + + return $this; + } + + /** + * Build the stream. + * + * @return StreamInterface + */ + public function build() + { + $streams = ''; + foreach ($this->data as $data) { + // Add start and headers + $streams .= "--{$this->getBoundary()}\r\n". + $this->getHeaders($data['headers'])."\r\n"; + + // Convert the stream to string + /* @var $contentStream StreamInterface */ + $contentStream = $data['contents']; + if ($contentStream->isSeekable()) { + $streams .= $contentStream->__toString(); + } else { + $streams .= $contentStream->getContents(); + } + + $streams .= "\r\n"; + } + + // Append end + $streams .= "--{$this->getBoundary()}--\r\n"; + + return $this->streamFactory->createStream($streams); + } + + /** + * Add extra headers if they are missing. + * + * @param string $name + * @param StreamInterface $stream + * @param string $filename + * @param array &$headers + */ + private function prepareHeaders($name, StreamInterface $stream, $filename, array &$headers) + { + $hasFilename = $filename === '0' || $filename; + + // Set a default content-disposition header if one was not provided + if (!$this->hasHeader($headers, 'content-disposition')) { + $headers['Content-Disposition'] = sprintf('form-data; name="%s"', $name); + if ($hasFilename) { + $headers['Content-Disposition'] .= sprintf('; filename="%s"', $this->basename($filename)); + } + } + + // Set a default content-length header if one was not provided + if (!$this->hasHeader($headers, 'content-length')) { + if ($length = $stream->getSize()) { + $headers['Content-Length'] = (string) $length; + } + } + + // Set a default Content-Type if one was not provided + if (!$this->hasHeader($headers, 'content-type') && $hasFilename) { + if ($type = $this->getMimetypeHelper()->getMimetypeFromFilename($filename)) { + $headers['Content-Type'] = $type; + } + } + } + + /** + * Get the headers formatted for the HTTP message. + * + * @param array $headers + * + * @return string + */ + private function getHeaders(array $headers) + { + $str = ''; + foreach ($headers as $key => $value) { + $str .= sprintf("%s: %s\r\n", $key, $value); + } + + return $str; + } + + /** + * Check if header exist. + * + * @param array $headers + * @param string $key case insensitive + * + * @return bool + */ + private function hasHeader(array $headers, $key) + { + $lowercaseHeader = strtolower($key); + foreach ($headers as $k => $v) { + if (strtolower($k) === $lowercaseHeader) { + return true; + } + } + + return false; + } + + /** + * Get the boundary that separates the streams. + * + * @return string + */ + public function getBoundary() + { + if ($this->boundary === null) { + $this->boundary = uniqid('', true); + } + + return $this->boundary; + } + + /** + * @param string $boundary + * + * @return MultipartStreamBuilder + */ + public function setBoundary($boundary) + { + $this->boundary = $boundary; + + return $this; + } + + /** + * @return MimetypeHelper + */ + private function getMimetypeHelper() + { + if ($this->mimetypeHelper === null) { + $this->mimetypeHelper = new ApacheMimetypeHelper(); + } + + return $this->mimetypeHelper; + } + + /** + * If you have custom file extension you may overwrite the default MimetypeHelper with your own. + * + * @param MimetypeHelper $mimetypeHelper + * + * @return MultipartStreamBuilder + */ + public function setMimetypeHelper(MimetypeHelper $mimetypeHelper) + { + $this->mimetypeHelper = $mimetypeHelper; + + return $this; + } + + /** + * Reset and clear all stored data. This allows you to use builder for a subsequent request. + * + * @return MultipartStreamBuilder + */ + public function reset() + { + $this->data = []; + $this->boundary = null; + + return $this; + } + + /** + * Gets the filename from a given path. + * + * PHP's basename() does not properly support streams or filenames beginning with a non-US-ASCII character. + * + * @author Drupal 8.2 + * + * @param string $path + * + * @return string + */ + private function basename($path) + { + $separators = '/'; + if (DIRECTORY_SEPARATOR != '/') { + // For Windows OS add special separator. + $separators .= DIRECTORY_SEPARATOR; + } + + // Remove right-most slashes when $path points to directory. + $path = rtrim($path, $separators); + + // Returns the trailing part of the $path starting after one of the directory separators. + $filename = preg_match('@[^'.preg_quote($separators, '@').']+$@', $path, $matches) ? $matches[0] : ''; + + return $filename; + } +} -- GitLab