diff --git a/.gitattributes b/.gitattributes index 3d7f7e351..ba1c92ba3 100644 --- a/.gitattributes +++ b/.gitattributes @@ -1,10 +1,11 @@ -.editorconfig export-ignore -.gitattributes export-ignore -/.github/ export-ignore -.gitignore export-ignore -/.travis.yml export-ignore -/build/ export-ignore -/docs/ export-ignore -/Makefile export-ignore -/phpunit.xml.dist export-ignore -/tests/ export-ignore +.editorconfig export-ignore +.gitattributes export-ignore +/.github/ export-ignore +.gitignore export-ignore +/build/ export-ignore +/docs/ export-ignore +/Makefile export-ignore +/phpstan-baseline.neon export-ignore +/phpstan.neon.dist export-ignore +/phpunit.xml.dist export-ignore +/tests/ export-ignore diff --git a/.github/ISSUE_TEMPLATE.md b/.github/ISSUE_TEMPLATE.md index d4ecf20f3..f822c0dd1 100644 --- a/.github/ISSUE_TEMPLATE.md +++ b/.github/ISSUE_TEMPLATE.md @@ -1,27 +1 @@ -| Q | A -| ------------ | --- -| Bug? | no|yes -| New Feature? | no|yes -| Version | Specific version or SHA of a commit - - -#### Actual Behavior - -What is the actual behavior? - - -#### Expected Behavior - -What is the behavior you expect? - - -#### Steps to Reproduce - -What are the steps to reproduce this bug? Please add code examples, -screenshots or links to GitHub repositories that reproduce the problem. - - -#### Possible Solutions - -If you have already ideas how to solve the issue, add them here. -(remove this section if not needed) +Please consider using one of the issue templates (bug report, feature request). diff --git a/.github/ISSUE_TEMPLATE/bug_report.md b/.github/ISSUE_TEMPLATE/bug_report.md new file mode 100644 index 000000000..37bc02963 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/bug_report.md @@ -0,0 +1,18 @@ +--- +name: 🐛 Bug Report +about: Report errors and problems +--- + +**Guzzle version(s) affected**: x.y.z + +**Description** + + +**How to reproduce** + + +**Possible Solution** + + +**Additional context** + diff --git a/.github/ISSUE_TEMPLATE/feature_request.md b/.github/ISSUE_TEMPLATE/feature_request.md new file mode 100644 index 000000000..981f4f674 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/feature_request.md @@ -0,0 +1,14 @@ +--- +name: 🚀 Feature Request +about: RFC and ideas for new features and improvements +--- + +**Description** + + +**Example** + + +**Additional context** + diff --git a/.github/ISSUE_TEMPLATE/security_issue.md b/.github/ISSUE_TEMPLATE/security_issue.md new file mode 100644 index 000000000..84ae4e554 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/security_issue.md @@ -0,0 +1,10 @@ +--- +name: ⛔ Security Issue +about: See the description to report security-related issues +--- + +⚠ PLEASE DON'T DISCLOSE SECURITY-RELATED ISSUES PUBLICLY, SEE BELOW. + +If you have found a security issue in Guzzle, please send the details to +security [at] guzzlephp.org and don't disclose it publicly until we can provide a +fix for it. diff --git a/.github/ISSUE_TEMPLATE/support_question.md b/.github/ISSUE_TEMPLATE/support_question.md new file mode 100644 index 000000000..383c6f40e --- /dev/null +++ b/.github/ISSUE_TEMPLATE/support_question.md @@ -0,0 +1,10 @@ +--- +name: ⛔ Support Question +about: See https://github.com/guzzle/guzzle/blob/master/.github/SUPPORT.md for questions about using Guzzle and its components +--- + +We use GitHub issues only to discuss about Guzzle bugs and new features. +For this kind of questions about using Guzzle, +please use any of the support alternatives shown in https://github.com/guzzle/guzzle/blob/master/.github/SUPPORT.md + +Thanks! diff --git a/.github/SUPPORT.md b/.github/SUPPORT.md new file mode 100644 index 000000000..0abc59338 --- /dev/null +++ b/.github/SUPPORT.md @@ -0,0 +1,18 @@ +# Support + +If you're looking for support for Guzzle, here are a few options: + +- [Documentation](http://guzzlephp.org/) +- [Gitter](https://gitter.im/guzzle/guzzle) +- [#guzzle](https://php-http.slack.com/messages/CE6UAAKL4/) channel in [PHP-HTTP](http://php-http.org) Slack team + +Guzzle is a relatively old project, so chances are you will find +much about them on Google or Stack Overflow: + +- [guzzle](https://stackoverflow.com/questions/tagged/guzzle) tag on Stack Overflow (recommended) +- [guzzlehttp](https://stackoverflow.com/questions/tagged/guzzlehttp) tag on Stack Overflow +- [guzzle6](https://stackoverflow.com/questions/tagged/guzzle6) tag on Stack Overflow + + +You can also browse the issue tracker for support requests, +but we encourage everyone to use the channels above instead. diff --git a/.github/workflows/bc.entrypoint b/.github/workflows/bc.entrypoint new file mode 100755 index 000000000..04436e670 --- /dev/null +++ b/.github/workflows/bc.entrypoint @@ -0,0 +1,37 @@ +#!/bin/sh -l + +# +# This file is a hack to suppress warnings from Roave BC check +# + +composer install + +# Capture output to variable AND print it +exec 4711>&1 +OUTPUT=$(/composer/vendor/bin/roave-backward-compatibility-check 2>&1 | tee /dev/fd/4711) + +# Remove rows we want to suppress +OUTPUT=`echo "$OUTPUT" | sed '/GuzzleHttp\\\ClientInterface::VERSION/'d` +OUTPUT=`echo "$OUTPUT" | sed '/Roave\\\BetterReflection\\\Reflection\\\ReflectionClass "Psr\\\Log\\\LogLevel" could not be found in the located source/'d` + +# Number of rows we found with "[BC]" in them +BC_BREAKS=`echo "$OUTPUT" | grep -o '\[BC\]' | wc -l | awk '{ print $1 }'` + +# The last row of the output is "X backwards-incompatible changes detected". Find X. +STATED_BREAKS=`echo "$OUTPUT" | tail -n 1 | awk -F' ' '{ print $1 }'` + +# If +# We found "[BC]" in the command output after we removed suppressed lines +# OR +# We have suppressed X number of BC breaks. If $STATED_BREAKS is larger than X +# THEN +# exit 1 + +if [ $BC_BREAKS -gt 0 ] || [ $STATED_BREAKS -gt 2 ]; then + echo "EXIT 1" + exit 1 +fi + +# No BC breaks found +echo "EXIT 0" +exit 0 diff --git a/.github/workflows/checks.yml b/.github/workflows/checks.yml new file mode 100644 index 000000000..4a248d146 --- /dev/null +++ b/.github/workflows/checks.yml @@ -0,0 +1,21 @@ +name: Checks + +on: + push: + branches: + - master + pull_request: + +jobs: + roave-bc-check: + name: Roave BC Check + runs-on: ubuntu-latest + + steps: + - name: Checkout code + uses: actions/checkout@v2 + + - name: Roave BC Check + uses: docker://nyholm/roave-bc-check-ga + with: + entrypoint: ./.github/workflows/bc.entrypoint diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml new file mode 100644 index 000000000..88649df2e --- /dev/null +++ b/.github/workflows/ci.yml @@ -0,0 +1,70 @@ +name: CI + +on: + push: + branches: + - master + pull_request: + +jobs: + build-lowest: + name: Build lowest + runs-on: ubuntu-latest + + steps: + - name: Set up PHP + uses: shivammathur/setup-php@v2 + with: + php-version: '5.5' + coverage: none + extensions: mbstring, intl + + - name: Set up Node + uses: actions/setup-node@v1 + with: + node-version: '14.x' + + - name: Setup Problem Matchers for PHPUnit + run: echo "::add-matcher::${{ runner.tool_cache }}/phpunit.json" + + - name: Checkout code + uses: actions/checkout@v2 + + - name: Download dependencies + run: composer update --no-interaction --no-progress --prefer-stable --prefer-lowest + + - name: Run tests + run: make test + + build: + name: Build + runs-on: ubuntu-latest + strategy: + max-parallel: 10 + matrix: + php: ['5.5', '5.6', '7.0', '7.1', '7.2', '7.3', '7.4'] + + steps: + - name: Set up PHP + uses: shivammathur/setup-php@v2 + with: + php-version: ${{ matrix.php }} + coverage: none + extensions: mbstring, intl + + - name: Set up Node + uses: actions/setup-node@v1 + with: + node-version: '14.x' + + - name: Setup Problem Matchers for PHPUnit + run: echo "::add-matcher::${{ runner.tool_cache }}/phpunit.json" + + - name: Checkout code + uses: actions/checkout@v2 + + - name: Download dependencies + run: composer update --no-interaction --no-progress + + - name: Run tests + run: make test diff --git a/.php_cs b/.php_cs new file mode 100644 index 000000000..2dd5036c1 --- /dev/null +++ b/.php_cs @@ -0,0 +1,23 @@ +setRiskyAllowed(true) + ->setRules([ + '@PSR2' => true, + 'array_syntax' => ['syntax' => 'short'], + 'declare_strict_types' => false, + 'concat_space' => ['spacing'=>'one'], + 'php_unit_test_case_static_method_calls' => ['call_type' => 'self'], + 'ordered_imports' => true, + // 'phpdoc_align' => ['align'=>'vertical'], + // 'native_function_invocation' => true, + ]) + ->setFinder( + PhpCsFixer\Finder::create() + ->in(__DIR__.'/src') + ->in(__DIR__.'/tests') + ->name('*.php') + ) +; + +return $config; diff --git a/.travis.yml b/.travis.yml deleted file mode 100644 index 94159e79f..000000000 --- a/.travis.yml +++ /dev/null @@ -1,47 +0,0 @@ -language: php - -sudo: false - -php: - - 5.5 - - 5.6 - - 7.0 - - 7.1 - - 7.2 - - nightly - -before_script: - - curl --version - - composer install --no-interaction --prefer-source --dev - - ~/.nvm/nvm.sh install v0.6.14 - - ~/.nvm/nvm.sh run v0.6.14 - - if [[ "$TRAVIS_PHP_VERSION" != "7.0" || "$TRAVIS_PHP_VERSION" != "7.1" ]]; then echo "xdebug.overload_var_dump = 1" >> ~/.phpenv/versions/$(phpenv version-name)/etc/php.ini || true; fi - -script: make test - -matrix: - allow_failures: - - php: hhvm - dist: trusty - - php: nightly - include: - - php: hhvm - dist: trusty - fast_finish: true - -before_deploy: - - make package - -deploy: - provider: releases - skip_cleanup: true - api_key: - secure: UpypqlYgsU68QT/x40YzhHXvzWjFwCNo9d+G8KAdm7U9+blFfcWhV1aMdzugvPMl6woXgvJj7qHq5tAL4v6oswCORhpSBfLgOQVFaica5LiHsvWlAedOhxGmnJqMTwuepjBCxXhs3+I8Kof1n4oUL9gKytXjOVCX/f7XU1HiinU= - file: - - build/artifacts/guzzle.phar - - build/artifacts/guzzle.zip - on: - repo: guzzle/guzzle - tags: true - all_branches: true - php: 5.5 diff --git a/CHANGELOG.md b/CHANGELOG.md index 17badd756..b053017a9 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,70 @@ # Change Log +## 6.5.8 - 2022-06-20 + +* Fix change in port should be considered a change in origin +* Fix `CURLOPT_HTTPAUTH` option not cleared on change of origin + +## 6.5.7 - 2022-06-09 + +* Fix failure to strip Authorization header on HTTP downgrade +* Fix failure to strip the Cookie header on change in host or HTTP downgrade + +## 6.5.6 - 2022-05-25 + +* Fix cross-domain cookie leakage + +## 6.5.5 - 2020-06-16 + +* Unpin version constraint for `symfony/polyfill-intl-idn` [#2678](https://github.com/guzzle/guzzle/pull/2678) + +## 6.5.4 - 2020-05-25 + +* Fix various intl icu issues [#2626](https://github.com/guzzle/guzzle/pull/2626) + +## 6.5.3 - 2020-04-18 + +* Use Symfony intl-idn polyfill [#2550](https://github.com/guzzle/guzzle/pull/2550) +* Remove use of internal functions [#2548](https://github.com/guzzle/guzzle/pull/2548) + +## 6.5.2 - 2019-12-23 + +* idn_to_ascii() fix for old PHP versions [#2489](https://github.com/guzzle/guzzle/pull/2489) + +## 6.5.1 - 2019-12-21 + +* Better defaults for PHP installations with old ICU lib [#2454](https://github.com/guzzle/guzzle/pull/2454) +* IDN support for redirects [#2424](https://github.com/guzzle/guzzle/pull/2424) + +## 6.5.0 - 2019-12-07 + +* Improvement: Added support for reset internal queue in MockHandler. [#2143](https://github.com/guzzle/guzzle/pull/2143) +* Improvement: Added support to pass arbitrary options to `curl_multi_init`. [#2287](https://github.com/guzzle/guzzle/pull/2287) +* Fix: Gracefully handle passing `null` to the `header` option. [#2132](https://github.com/guzzle/guzzle/pull/2132) +* Fix: `RetryMiddleware` did not do exponential delay between retries due unit mismatch. [#2132](https://github.com/guzzle/guzzle/pull/2132) + Previously, `RetryMiddleware` would sleep for 1 millisecond, then 2 milliseconds, then 4 milliseconds. + **After this change, `RetryMiddleware` will sleep for 1 second, then 2 seconds, then 4 seconds.** + `Middleware::retry()` accepts a second callback parameter to override the default timeouts if needed. +* Fix: Prevent undefined offset when using array for ssl_key options. [#2348](https://github.com/guzzle/guzzle/pull/2348) +* Deprecated `ClientInterface::VERSION` + +## 6.4.1 - 2019-10-23 + +* No `guzzle.phar` was created in 6.4.0 due expired API token. This release will fix that +* Added `parent::__construct()` to `FileCookieJar` and `SessionCookieJar` + +## 6.4.0 - 2019-10-23 + +* Improvement: Improved error messages when using curl < 7.21.2 [#2108](https://github.com/guzzle/guzzle/pull/2108) +* Fix: Test if response is readable before returning a summary in `RequestException::getResponseBodySummary()` [#2081](https://github.com/guzzle/guzzle/pull/2081) +* Fix: Add support for GUZZLE_CURL_SELECT_TIMEOUT environment variable [#2161](https://github.com/guzzle/guzzle/pull/2161) +* Improvement: Added `GuzzleHttp\Exception\InvalidArgumentException` [#2163](https://github.com/guzzle/guzzle/pull/2163) +* Improvement: Added `GuzzleHttp\_current_time()` to use `hrtime()` if that function exists. [#2242](https://github.com/guzzle/guzzle/pull/2242) +* Improvement: Added curl's `appconnect_time` in `TransferStats` [#2284](https://github.com/guzzle/guzzle/pull/2284) +* Improvement: Make GuzzleException extend Throwable wherever it's available [#2273](https://github.com/guzzle/guzzle/pull/2273) +* Fix: Prevent concurrent writes to file when saving `CookieJar` [#2335](https://github.com/guzzle/guzzle/pull/2335) +* Improvement: Update `MockHandler` so we can test transfer time [#2362](https://github.com/guzzle/guzzle/pull/2362) + ## 6.3.3 - 2018-04-22 * Fix: Default headers when decode_content is specified diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 000000000..f6a095230 --- /dev/null +++ b/Dockerfile @@ -0,0 +1,18 @@ +FROM composer:latest as setup + +RUN mkdir /guzzle + +WORKDIR /guzzle + +RUN set -xe \ + && composer init --name=guzzlehttp/test --description="Simple project for testing Guzzle scripts" --author="Márk Sági-Kazár " --no-interaction \ + && composer require guzzlehttp/guzzle + + +FROM php:7.3 + +RUN mkdir /guzzle + +WORKDIR /guzzle + +COPY --from=setup /guzzle /guzzle diff --git a/LICENSE b/LICENSE index 50a177b03..fd2375d88 100644 --- a/LICENSE +++ b/LICENSE @@ -1,4 +1,12 @@ -Copyright (c) 2011-2018 Michael Dowling, https://github.com/mtdowling +The MIT License (MIT) + +Copyright (c) 2011 Michael Dowling +Copyright (c) 2012 Jeremy Lindblom +Copyright (c) 2014 Graham Campbell +Copyright (c) 2015 Márk Sági-Kazár +Copyright (c) 2015 Tobias Schultze +Copyright (c) 2016 Tobias Nyholm +Copyright (c) 2016 George Mponos 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/Makefile b/Makefile index a58682615..387a46d2e 100644 --- a/Makefile +++ b/Makefile @@ -10,6 +10,11 @@ help: @echo " docs-show to view the Sphinx docs" @echo " tag to modify the version, update changelog, and chag tag" @echo " package to build the phar and zip files" + @echo " static to run phpstan and php-cs-fixer on the codebase" + @echo " static-phpstan to run phpstan on the codebase" + @echo " static-phpstan-update-baseline to regenerate the phpstan baseline file" + @echo " static-codestyle-fix to run php-cs-fixer on the codebase, writing the changes" + @echo " static-codestyle-check to run php-cs-fixer on the codebase" start-server: stop-server node tests/server.js &> /dev/null & @@ -56,4 +61,18 @@ tag: package: php build/packager.php +static: static-phpstan static-codestyle-check + +static-phpstan: + docker run --rm -it -e REQUIRE_DEV=true -v ${PWD}:/app -w /app oskarstark/phpstan-ga:0.12.28 analyze $(PHPSTAN_PARAMS) + +static-phpstan-update-baseline: + $(MAKE) static-phpstan PHPSTAN_PARAMS="--generate-baseline" + +static-codestyle-fix: + docker run --rm -it -v ${PWD}:/app -w /app oskarstark/php-cs-fixer-ga:2.16.3.1 --diff-format udiff $(CS_PARAMS) + +static-codestyle-check: + $(MAKE) static-codestyle-fix CS_PARAMS="--dry-run" + .PHONY: docs burgomaster coverage-show view-coverage diff --git a/README.md b/README.md index bcd18b8e7..bc98e1a10 100644 --- a/README.md +++ b/README.md @@ -1,8 +1,9 @@ -Guzzle, PHP HTTP client -======================= +![Guzzle](.github/logo.png?raw=true) + +# Guzzle, PHP HTTP client [![Latest Version](https://img.shields.io/github/release/guzzle/guzzle.svg?style=flat-square)](https://github.com/guzzle/guzzle/releases) -[![Build Status](https://img.shields.io/travis/guzzle/guzzle.svg?style=flat-square)](https://travis-ci.org/guzzle/guzzle) +[![Build Status](https://img.shields.io/github/workflow/status/guzzle/guzzle/CI?label=ci%20build&style=flat-square)](https://github.com/guzzle/guzzle/actions?query=workflow%3ACI) [![Total Downloads](https://img.shields.io/packagist/dt/guzzlehttp/guzzle.svg?style=flat-square)](https://packagist.org/packages/guzzlehttp/guzzle) Guzzle is a PHP HTTP client that makes it easy to send HTTP requests and @@ -21,33 +22,35 @@ trivial to integrate with web services. ```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. +$response = $client->request('GET', 'https://api.github.com/repos/guzzle/guzzle'); + +echo $response->getStatusCode(); # 200 +echo $response->getHeaderLine('content-type'); # 'application/json; charset=utf8' +echo $response->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) +We use GitHub issues only to discuss bugs and new features. For support please refer to: + +- [Documentation](https://docs.guzzlephp.org) +- [Stack Overflow](https://stackoverflow.com/questions/tagged/guzzle) +- [#guzzle](https://app.slack.com/client/T0D2S9JCT/CE6UAAKL4) channel on [PHP-HTTP Slack](https://slack.httplug.io/) - [Gitter](https://gitter.im/guzzle/guzzle) ## Installing Guzzle The recommended way to install Guzzle is through -[Composer](http://getcomposer.org). +[Composer](https://getcomposer.org/). ```bash # Install Composer @@ -57,7 +60,7 @@ 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 +composer require guzzlehttp/guzzle ``` After installing, you need to require Composer's autoloader: @@ -69,23 +72,26 @@ require 'vendor/autoload.php'; You can then later update Guzzle using composer: ```bash -composer.phar update +composer 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 | +| 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,<7.0 | +| 4.x | EOL | `guzzlehttp/guzzle` | `GuzzleHttp` | [v4][guzzle-4-repo] | N/A | No | >=5.4,<7.0 | +| 5.x | EOL | `guzzlehttp/guzzle` | `GuzzleHttp` | [v5][guzzle-5-repo] | [v5][guzzle-5-docs] | No | >=5.4,<7.4 | +| 6.x | Security fixes | `guzzlehttp/guzzle` | `GuzzleHttp` | [v6][guzzle-6-repo] | [v6][guzzle-6-docs] | Yes | >=5.5,<8.0 | +| 7.x | Latest | `guzzlehttp/guzzle` | `GuzzleHttp` | [v7][guzzle-7-repo] | [v7][guzzle-7-docs] | Yes | >=7.2.5,<8.2 | [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/ +[guzzle-6-repo]: https://github.com/guzzle/guzzle/tree/6.5 +[guzzle-7-repo]: https://github.com/guzzle/guzzle +[guzzle-3-docs]: https://guzzle3.readthedocs.io/ +[guzzle-5-docs]: https://docs.guzzlephp.org/en/5.3/ +[guzzle-6-docs]: https://docs.guzzlephp.org/en/6.5/ +[guzzle-7-docs]: https://docs.guzzlephp.org/en/latest/ diff --git a/build/packager.php b/build/packager.php index 9d6d21317..f0316e234 100644 --- a/build/packager.php +++ b/build/packager.php @@ -18,9 +18,9 @@ $packager->recursiveCopy('vendor/psr/http-message/src', 'Psr/Http/Message'); $packager->createAutoloader([ - 'GuzzleHttp/functions.php', - 'GuzzleHttp/Psr7/functions.php', - 'GuzzleHttp/Promise/functions.php', + 'GuzzleHttp/functions_include.php', + 'GuzzleHttp/Psr7/functions_include.php', + 'GuzzleHttp/Promise/functions_include.php', ]); $packager->createPhar(__DIR__ . '/artifacts/guzzle.phar'); diff --git a/composer.json b/composer.json index 1f328e308..a57d78f60 100644 --- a/composer.json +++ b/composer.json @@ -2,43 +2,91 @@ "name": "guzzlehttp/guzzle", "type": "library", "description": "Guzzle is a PHP HTTP client library", - "keywords": ["framework", "http", "rest", "web service", "curl", "client", "HTTP client"], + "keywords": [ + "framework", + "http", + "rest", + "web service", + "curl", + "client", + "HTTP client" + ], "homepage": "http://guzzlephp.org/", "license": "MIT", "authors": [ + { + "name": "Graham Campbell", + "email": "hello@gjcampbell.co.uk", + "homepage": "https://github.com/GrahamCampbell" + }, { "name": "Michael Dowling", "email": "mtdowling@gmail.com", "homepage": "https://github.com/mtdowling" + }, + { + "name": "Jeremy Lindblom", + "email": "jeremeamia@gmail.com", + "homepage": "https://github.com/jeremeamia" + }, + { + "name": "George Mponos", + "email": "gmponos@gmail.com", + "homepage": "https://github.com/gmponos" + }, + { + "name": "Tobias Nyholm", + "email": "tobias.nyholm@gmail.com", + "homepage": "https://github.com/Nyholm" + }, + { + "name": "Márk Sági-Kazár", + "email": "mark.sagikazar@gmail.com", + "homepage": "https://github.com/sagikazarmark" + }, + { + "name": "Tobias Schultze", + "email": "webmaster@tubo-world.de", + "homepage": "https://github.com/Tobion" } ], "require": { "php": ">=5.5", - "guzzlehttp/psr7": "^1.4", - "guzzlehttp/promises": "^1.0" + "ext-json": "*", + "symfony/polyfill-intl-idn": "^1.17", + "guzzlehttp/promises": "^1.0", + "guzzlehttp/psr7": "^1.9" }, "require-dev": { "ext-curl": "*", "phpunit/phpunit": "^4.8.35 || ^5.7 || ^6.4 || ^7.0", - "psr/log": "^1.0" + "psr/log": "^1.1" + }, + "suggest": { + "psr/log": "Required for using the Log middleware" + }, + "config": { + "sort-packages": true, + "allow-plugins": { + "bamarni/composer-bin-plugin": true + } + }, + "extra": { + "branch-alias": { + "dev-master": "6.5-dev" + } }, "autoload": { - "files": ["src/functions_include.php"], "psr-4": { "GuzzleHttp\\": "src/" - } + }, + "files": [ + "src/functions_include.php" + ] }, "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/docs/faq.rst b/docs/faq.rst index ee7949bf8..e94b1035b 100644 --- a/docs/faq.rst +++ b/docs/faq.rst @@ -65,6 +65,23 @@ used with a client. ] ]); +If you use asynchronous requests with cURL multi handler and want to tweak it, +additional options can be specified as an associative array in the +**options** key of the ``CurlMultiHandler`` constructor. + +.. code-block:: php + + use \GuzzleHttp\Client; + use \GuzzleHttp\HandlerStack; + use \GuzzleHttp\Handler\CurlMultiHandler; + + $client = new Client(['handler' => HandlerStack::create(new CurlMultiHandler([ + 'options' => [ + CURLMOPT_MAX_TOTAL_CONNECTIONS => 50, + CURLMOPT_MAX_HOST_CONNECTIONS => 5, + ] + ]))]); + How can I add custom stream context options? ============================================ diff --git a/docs/handlers-and-middleware.rst b/docs/handlers-and-middleware.rst index 5c3b0bfc8..be81063c9 100644 --- a/docs/handlers-and-middleware.rst +++ b/docs/handlers-and-middleware.rst @@ -50,7 +50,7 @@ The ``create`` method adds default handlers to the ``HandlerStack``. When the 2. ``cookies`` - extracts response cookies into the cookie jar. 3. ``allow_redirects`` - Follows redirects. 4. ``http_errors`` - throws exceptions when the response status code ``>=`` - 300. + 400. When provided no ``$handler`` argument, ``GuzzleHttp\HandlerStack::create()`` will choose the most appropriate handler based on the extensions available on diff --git a/docs/overview.rst b/docs/overview.rst index 1ddca55e2..6d3fa07ee 100644 --- a/docs/overview.rst +++ b/docs/overview.rst @@ -16,6 +16,8 @@ Requirements Guzzle no longer requires cURL in order to send HTTP requests. Guzzle will use the PHP stream wrapper to send HTTP requests if cURL is not installed. Alternatively, you can provide your own HTTP handler used to send requests. + Keep in mind that cURL is still required for sending concurrent requests. + .. _installation: diff --git a/docs/psr7.rst b/docs/psr7.rst index 10ba01c6e..3766aaa85 100644 --- a/docs/psr7.rst +++ b/docs/psr7.rst @@ -451,5 +451,6 @@ functionality. - `InflateStream `_ - `LazyOpenStream `_ - `LimitStream `_ +- `MultipartStream `_ - `NoSeekStream `_ - `PumpStream `_ diff --git a/docs/quickstart.rst b/docs/quickstart.rst index d4e9a8231..7a3f35fbd 100644 --- a/docs/quickstart.rst +++ b/docs/quickstart.rst @@ -67,8 +67,7 @@ The client constructor accepts an associative array of options: 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. + ``Psr7\Http\Message\ResponseInterface`` on success. ``...`` (mixed) All other options passed to the constructor are used as default @@ -184,26 +183,27 @@ requests. 'webp' => $client->getAsync('/image/webp') ]; - // Wait on all of the requests to complete. Throws a ConnectException + // Wait for the requests to complete; throws a ConnectException // if any of the requests fail - $results = Promise\unwrap($promises); - + $responses = Promise\unwrap($promises); + // Wait for the requests to complete, even if some of them fail - $results = Promise\settle($promises)->wait(); + $responses = Promise\settle($promises)->wait(); - // You can access each result using the key provided to the unwrap - // function. - echo $results['image']['value']->getHeader('Content-Length')[0] - echo $results['png']['value']->getHeader('Content-Length')[0] + // You can access each response using the key of the promise + echo $responses['image']->getHeader('Content-Length')[0]; + echo $responses['png']->getHeader('Content-Length')[0]; You can use the ``GuzzleHttp\Pool`` object when you have an indeterminate amount of requests you wish to send. .. code-block:: php - use GuzzleHttp\Pool; use GuzzleHttp\Client; + use GuzzleHttp\Exception\RequestException; + use GuzzleHttp\Pool; use GuzzleHttp\Psr7\Request; + use GuzzleHttp\Psr7\Response; $client = new Client(); @@ -216,10 +216,10 @@ amount of requests you wish to send. $pool = new Pool($client, $requests(100), [ 'concurrency' => 5, - 'fulfilled' => function ($response, $index) { + 'fulfilled' => function (Response $response, $index) { // this is delivered each successful response }, - 'rejected' => function ($reason, $index) { + 'rejected' => function (RequestException $reason, $index) { // this is delivered each failed request }, ]); @@ -229,7 +229,7 @@ amount of requests you wish to send. // Force the pool of requests to complete. $promise->wait(); - + Or using a closure that will return a promise once the pool calls the closure. .. code-block:: php @@ -246,7 +246,7 @@ Or using a closure that will return a promise once the pool calls the closure. }; $pool = new Pool($client, $requests(100)); - + Using Responses =============== @@ -447,6 +447,43 @@ to use a shared cookie jar for all requests. $client = new \GuzzleHttp\Client(['cookies' => true]); $r = $client->request('GET', 'http://httpbin.org/cookies'); +Different implementations exist for the ``GuzzleHttp\Cookie\CookieJarInterface`` +: + +- The ``GuzzleHttp\Cookie\CookieJar`` class stores cookies as an array. +- The ``GuzzleHttp\Cookie\FileCookieJar`` class persists non-session cookies + using a JSON formatted file. +- The ``GuzzleHttp\Cookie\SessionCookieJar`` class persists cookies in the + client session. + +You can manually set cookies into a cookie jar with the named constructor +``fromArray(array $cookies, $domain)``. + +.. code-block:: php + + $jar = \GuzzleHttp\Cookie\CookieJar::fromArray( + [ + 'some_cookie' => 'foo', + 'other_cookie' => 'barbaz1234' + ], + 'example.org' + ); + +You can get a cookie by its name with the ``getCookieByName($name)`` method +which returns a ``GuzzleHttp\Cookie\SetCookie`` instance. + +.. code-block:: php + + $cookie = $jar->getCookieByName('some_cookie'); + + $cookie->getValue(); // 'foo' + $cookie->getDomain(); // 'example.org' + $cookie->getExpires(); // expiration date as a Unix timestamp + +The cookies can be also fetched into an array thanks to the `toArray()` method. +The ``GuzzleHttp\Cookie\CookieJarInterface`` interface extends +``Traversable`` so it can be iterated in a foreach loop. + Redirects ========= @@ -483,6 +520,23 @@ The following example shows that redirects can be disabled. Exceptions ========== +**Tree View** + +The following tree view describes how the Guzzle Exceptions depend +on each other. + +.. code-block:: none + + . \RuntimeException + ├── SeekException (implements GuzzleException) + └── TransferException (implements GuzzleException) + └── RequestException + ├── BadResponseException + │   ├── ServerException + │ └── ClientException + ├── ConnectException + └── TooManyRedirectsException + Guzzle throws exceptions for errors that occur during a transfer. - In the event of a networking error (connection timeout, DNS errors, etc.), @@ -517,6 +571,7 @@ Guzzle throws exceptions for errors that occur during a transfer. .. code-block:: php + use GuzzleHttp\Psr7; use GuzzleHttp\Exception\ClientException; try { @@ -551,10 +606,12 @@ behavior of the library. the timeout. ``HTTP_PROXY`` Defines the proxy to use when sending requests using the "http" protocol. - + Note: because the HTTP_PROXY variable may contain arbitrary user input on some (CGI) environments, the variable is only used on the CLI SAPI. See https://httpoxy.org for more information. ``HTTPS_PROXY`` Defines the proxy to use when sending requests using the "https" protocol. +``NO_PROXY`` + Defines URLs for which a proxy should not be used. See :ref:`proxy-option` for usage. Relevant ini Settings diff --git a/docs/request-options.rst b/docs/request-options.rst index 2dfc86b1a..a822f58ae 100644 --- a/docs/request-options.rst +++ b/docs/request-options.rst @@ -118,7 +118,7 @@ pairs: This option only has an effect if your handler has the ``GuzzleHttp\Middleware::redirect`` middleware. This middleware is added by default when a client is created with no handler, and is added by - default when creating a handler with ``GuzzleHttp\default_handler``. + default when creating a handler with ``GuzzleHttp\HandlerStack::create``. auth @@ -553,6 +553,31 @@ http_errors default when creating a handler with ``GuzzleHttp\default_handler``. +idn_conversion +-------------- + +:Summary: Internationalized Domain Name (IDN) support (enabled by default if + ``intl`` extension is available). +:Types: + - bool + - int +:Default: ``true`` if ``intl`` extension is available (and ICU library is 4.6+ for PHP 7.2+), ``false`` otherwise +:Constant: ``GuzzleHttp\RequestOptions::IDN_CONVERSION`` + +.. code-block:: php + + $client->request('GET', 'https://яндекс.рф'); + // яндекс.рф is translated to xn--d1acpjx3f.xn--p1ai before passing it to the handler + + $res = $client->request('GET', 'https://яндекс.рф', ['idn_conversion' => false]); + // The domain part (яндекс.рф) stays unmodified + +Enables/disables IDN support, can also be used for precise control by combining +IDNA_* constants (except IDNA_ERROR_*), see ``$options`` parameter in +`idn_to_ascii() `_ +documentation for more details. + + json ---- @@ -737,7 +762,7 @@ progress The function accepts the following positional arguments: -- the total number of bytes expected to be downloaded +- the total number of bytes expected to be downloaded, zero if unknown - the number of bytes downloaded so far - the total number of bytes expected to be uploaded - the number of bytes uploaded so far @@ -788,7 +813,7 @@ host names that should not be proxied to. Guzzle will automatically populate this value with your environment's ``NO_PROXY`` environment variable. However, when providing a ``proxy`` - request option, it is up to your to provide the ``no`` value parsed from + request option, it is up to you to provide the ``no`` value parsed from the ``NO_PROXY`` environment variable (e.g., ``explode(',', getenv('NO_PROXY'))``). diff --git a/docs/testing.rst b/docs/testing.rst index 0fe6e9725..0598a215d 100644 --- a/docs/testing.rst +++ b/docs/testing.rst @@ -32,25 +32,37 @@ a response or exception by shifting return values off of a queue. // Create a mock and queue two responses. $mock = new MockHandler([ - new Response(200, ['X-Foo' => 'Bar']), + new Response(200, ['X-Foo' => 'Bar'], 'Hello, World'), new Response(202, ['Content-Length' => 0]), - new RequestException("Error Communicating with Server", new Request('GET', 'test')) + new RequestException('Error Communicating with Server', new Request('GET', 'test')) ]); - $handler = HandlerStack::create($mock); - $client = new Client(['handler' => $handler]); + $handlerStack = HandlerStack::create($mock); + $client = new Client(['handler' => $handlerStack]); // The first request is intercepted with the first response. - echo $client->request('GET', '/')->getStatusCode(); + $response = $client->request('GET', '/'); + echo $response->getStatusCode(); //> 200 + echo $response->getBody(); + //> Hello, World // The second request is intercepted with the second response. echo $client->request('GET', '/')->getStatusCode(); //> 202 + // Reset the queue and queue up a new response + $mock->reset(); + $mock->append(new Response(201)); + + // As the mock was reset, the new response is the 201 CREATED, + // instead of the previously queued RequestException + echo $client->request('GET', '/')->getStatusCode(); + //> 201 + + When no more responses are in the queue and a request is sent, an ``OutOfBoundsException`` is thrown. - History Middleware ================== @@ -68,11 +80,13 @@ history of the requests that were sent by a client. $container = []; $history = Middleware::history($container); - $stack = HandlerStack::create(); + $handlerStack = HandlerStack::create(); + // or $handlerStack = HandlerStack::create($mock); if using the Mock handler. + // Add the history middleware to the handler stack. - $stack->push($history); + $handlerStack->push($history); - $client = new Client(['handler' => $stack]); + $client = new Client(['handler' => $handlerStack]); $client->request('GET', 'http://httpbin.org/get'); $client->request('HEAD', 'http://httpbin.org/get'); diff --git a/phpstan-baseline.neon b/phpstan-baseline.neon new file mode 100644 index 000000000..6f81f2857 --- /dev/null +++ b/phpstan-baseline.neon @@ -0,0 +1,1352 @@ +parameters: + ignoreErrors: + - + message: "#^Property GuzzleHttp\\\\Client\\:\\:\\$config type has no value type specified in iterable type array\\.$#" + count: 1 + path: src/Client.php + + - + message: "#^Method GuzzleHttp\\\\Client\\:\\:__construct\\(\\) has parameter \\$config with no value type specified in iterable type array\\.$#" + count: 1 + path: src/Client.php + + - + message: "#^Method GuzzleHttp\\\\Client\\:\\:__call\\(\\) has parameter \\$args with no value type specified in iterable type array\\.$#" + count: 1 + path: src/Client.php + + - + message: "#^Method GuzzleHttp\\\\Client\\:\\:__call\\(\\) should return GuzzleHttp\\\\Promise\\\\PromiseInterface but returns GuzzleHttp\\\\Promise\\\\PromiseInterface\\|Psr\\\\Http\\\\Message\\\\ResponseInterface\\.$#" + count: 1 + path: src/Client.php + + - + message: "#^Method GuzzleHttp\\\\Client\\:\\:sendAsync\\(\\) has parameter \\$options with no value type specified in iterable type array\\.$#" + count: 1 + path: src/Client.php + + - + message: "#^Method GuzzleHttp\\\\Client\\:\\:send\\(\\) has parameter \\$options with no value type specified in iterable type array\\.$#" + count: 1 + path: src/Client.php + + - + message: "#^Method GuzzleHttp\\\\Client\\:\\:requestAsync\\(\\) has parameter \\$options with no value type specified in iterable type array\\.$#" + count: 1 + path: src/Client.php + + - + message: "#^Method GuzzleHttp\\\\Client\\:\\:request\\(\\) has parameter \\$options with no value type specified in iterable type array\\.$#" + count: 1 + path: src/Client.php + + - + message: "#^Method GuzzleHttp\\\\Client\\:\\:buildUri\\(\\) has parameter \\$config with no value type specified in iterable type array\\.$#" + count: 1 + path: src/Client.php + + - + message: "#^Method GuzzleHttp\\\\Client\\:\\:configureDefaults\\(\\) has parameter \\$config with no value type specified in iterable type array\\.$#" + count: 1 + path: src/Client.php + + - + message: "#^Parameter \\#1 \\$str of function strtolower expects string, int\\|string given\\.$#" + count: 1 + path: src/Client.php + + - + message: "#^Method GuzzleHttp\\\\Client\\:\\:prepareDefaults\\(\\) has parameter \\$options with no value type specified in iterable type array\\.$#" + count: 1 + path: src/Client.php + + - + message: "#^Method GuzzleHttp\\\\Client\\:\\:prepareDefaults\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: src/Client.php + + - + message: "#^Method GuzzleHttp\\\\Client\\:\\:transfer\\(\\) has parameter \\$options with no value type specified in iterable type array\\.$#" + count: 1 + path: src/Client.php + + - + message: "#^Method GuzzleHttp\\\\Client\\:\\:applyOptions\\(\\) has parameter \\$options with no value type specified in iterable type array\\.$#" + count: 1 + path: src/Client.php + + - + message: "#^Parameter \\#2 \\$prefix of function http_build_query expects string, null given\\.$#" + count: 1 + path: src/Client.php + + - + message: "#^Method GuzzleHttp\\\\ClientInterface\\:\\:send\\(\\) has parameter \\$options with no value type specified in iterable type array\\.$#" + count: 1 + path: src/ClientInterface.php + + - + message: "#^Method GuzzleHttp\\\\ClientInterface\\:\\:sendAsync\\(\\) has parameter \\$options with no value type specified in iterable type array\\.$#" + count: 1 + path: src/ClientInterface.php + + - + message: "#^Method GuzzleHttp\\\\ClientInterface\\:\\:request\\(\\) has parameter \\$options with no value type specified in iterable type array\\.$#" + count: 1 + path: src/ClientInterface.php + + - + message: "#^Method GuzzleHttp\\\\ClientInterface\\:\\:requestAsync\\(\\) has parameter \\$options with no value type specified in iterable type array\\.$#" + count: 1 + path: src/ClientInterface.php + + - + message: "#^Method GuzzleHttp\\\\Cookie\\\\CookieJar\\:\\:__construct\\(\\) has parameter \\$cookieArray with no value type specified in iterable type array\\.$#" + count: 1 + path: src/Cookie/CookieJar.php + + - + message: "#^Method GuzzleHttp\\\\Cookie\\\\CookieJar\\:\\:fromArray\\(\\) has parameter \\$cookies with no value type specified in iterable type array\\.$#" + count: 1 + path: src/Cookie/CookieJar.php + + - + message: "#^Method GuzzleHttp\\\\Cookie\\\\CookieJar\\:\\:fromArray\\(\\) return type has no value type specified in iterable type GuzzleHttp\\\\Cookie\\\\CookieJar\\.$#" + count: 1 + path: src/Cookie/CookieJar.php + + - + message: "#^Method GuzzleHttp\\\\Cookie\\\\CookieJar\\:\\:getCookieValue\\(\\) has no return typehint specified\\.$#" + count: 1 + path: src/Cookie/CookieJar.php + + - + message: "#^Method GuzzleHttp\\\\Cookie\\\\CookieJar\\:\\:getCookieValue\\(\\) has parameter \\$value with no typehint specified\\.$#" + count: 1 + path: src/Cookie/CookieJar.php + + - + message: "#^Result of \\|\\| is always false\\.$#" + count: 1 + path: src/Cookie/CookieJar.php + + - + message: "#^Strict comparison using \\=\\=\\= between string and null will always evaluate to false\\.$#" + count: 2 + path: src/Cookie/CookieJar.php + + - + message: "#^Method GuzzleHttp\\\\Cookie\\\\CookieJar\\:\\:toArray\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: src/Cookie/CookieJar.php + + - + message: "#^Call to an undefined method Traversable\\\\:\\:getArrayCopy\\(\\)\\.$#" + count: 1 + path: src/Cookie/CookieJar.php + + - + message: "#^Method GuzzleHttp\\\\Cookie\\\\CookieJar\\:\\:clear\\(\\) return type has no value type specified in iterable type GuzzleHttp\\\\Cookie\\\\CookieJarInterface\\.$#" + count: 1 + path: src/Cookie/CookieJar.php + + - + message: "#^Method GuzzleHttp\\\\Cookie\\\\CookieJar\\:\\:clear\\(\\) should return GuzzleHttp\\\\Cookie\\\\CookieJarInterface but return statement is missing\\.$#" + count: 1 + path: src/Cookie/CookieJar.php + + - + message: "#^Method GuzzleHttp\\\\Cookie\\\\CookieJar\\:\\:clear\\(\\) should return GuzzleHttp\\\\Cookie\\\\CookieJarInterface but empty return statement found\\.$#" + count: 1 + path: src/Cookie/CookieJar.php + + - + message: "#^Method GuzzleHttp\\\\Cookie\\\\CookieJar\\:\\:clearSessionCookies\\(\\) has no return typehint specified\\.$#" + count: 1 + path: src/Cookie/CookieJar.php + + - + message: "#^Method GuzzleHttp\\\\Cookie\\\\CookieJar\\:\\:getIterator\\(\\) return type has no value type specified in iterable type Traversable\\\\.$#" + count: 1 + path: src/Cookie/CookieJar.php + + - + message: "#^Method GuzzleHttp\\\\Cookie\\\\CookieJar\\:\\:extractCookies\\(\\) has no return typehint specified\\.$#" + count: 1 + path: src/Cookie/CookieJar.php + + - + message: "#^Parameter \\#3 \\$length of function substr expects int, int\\|false given\\.$#" + count: 1 + path: src/Cookie/CookieJar.php + + - + message: "#^Method GuzzleHttp\\\\Cookie\\\\CookieJar\\:\\:removeCookieIfEmpty\\(\\) has no return typehint specified\\.$#" + count: 1 + path: src/Cookie/CookieJar.php + + - + message: "#^Interface GuzzleHttp\\\\Cookie\\\\CookieJarInterface extends generic interface IteratorAggregate but does not specify its types\\: TKey, TValue$#" + count: 1 + path: src/Cookie/CookieJarInterface.php + + - + message: "#^Method GuzzleHttp\\\\Cookie\\\\CookieJarInterface\\:\\:extractCookies\\(\\) has no return typehint specified\\.$#" + count: 1 + path: src/Cookie/CookieJarInterface.php + + - + message: "#^Method GuzzleHttp\\\\Cookie\\\\CookieJarInterface\\:\\:clear\\(\\) return type has no value type specified in iterable type GuzzleHttp\\\\Cookie\\\\CookieJarInterface\\.$#" + count: 1 + path: src/Cookie/CookieJarInterface.php + + - + message: "#^Method GuzzleHttp\\\\Cookie\\\\CookieJarInterface\\:\\:clearSessionCookies\\(\\) has no return typehint specified\\.$#" + count: 1 + path: src/Cookie/CookieJarInterface.php + + - + message: "#^Method GuzzleHttp\\\\Cookie\\\\CookieJarInterface\\:\\:toArray\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: src/Cookie/CookieJarInterface.php + + - + message: "#^Method GuzzleHttp\\\\Cookie\\\\FileCookieJar\\:\\:save\\(\\) has no return typehint specified\\.$#" + count: 1 + path: src/Cookie/FileCookieJar.php + + - + message: "#^Method GuzzleHttp\\\\Cookie\\\\FileCookieJar\\:\\:load\\(\\) has no return typehint specified\\.$#" + count: 1 + path: src/Cookie/FileCookieJar.php + + - + message: "#^Method GuzzleHttp\\\\Cookie\\\\SessionCookieJar\\:\\:save\\(\\) has no return typehint specified\\.$#" + count: 1 + path: src/Cookie/SessionCookieJar.php + + - + message: "#^Method GuzzleHttp\\\\Cookie\\\\SessionCookieJar\\:\\:load\\(\\) has no return typehint specified\\.$#" + count: 1 + path: src/Cookie/SessionCookieJar.php + + - + message: "#^Property GuzzleHttp\\\\Cookie\\\\SetCookie\\:\\:\\$defaults type has no value type specified in iterable type array\\.$#" + count: 1 + path: src/Cookie/SetCookie.php + + - + message: "#^Property GuzzleHttp\\\\Cookie\\\\SetCookie\\:\\:\\$data type has no value type specified in iterable type array\\.$#" + count: 1 + path: src/Cookie/SetCookie.php + + - + message: "#^Method GuzzleHttp\\\\Cookie\\\\SetCookie\\:\\:__construct\\(\\) has parameter \\$data with no value type specified in iterable type array\\.$#" + count: 1 + path: src/Cookie/SetCookie.php + + - + message: "#^Parameter \\#1 \\$timestamp of method GuzzleHttp\\\\Cookie\\\\SetCookie\\:\\:setExpires\\(\\) expects int, mixed given\\.$#" + count: 1 + path: src/Cookie/SetCookie.php + + - + message: "#^Method GuzzleHttp\\\\Cookie\\\\SetCookie\\:\\:toArray\\(\\) has no return typehint specified\\.$#" + count: 1 + path: src/Cookie/SetCookie.php + + - + message: "#^Method GuzzleHttp\\\\Cookie\\\\SetCookie\\:\\:setName\\(\\) has no return typehint specified\\.$#" + count: 1 + path: src/Cookie/SetCookie.php + + - + message: "#^Method GuzzleHttp\\\\Cookie\\\\SetCookie\\:\\:setValue\\(\\) has no return typehint specified\\.$#" + count: 1 + path: src/Cookie/SetCookie.php + + - + message: "#^Method GuzzleHttp\\\\Cookie\\\\SetCookie\\:\\:setDomain\\(\\) has no return typehint specified\\.$#" + count: 1 + path: src/Cookie/SetCookie.php + + - + message: "#^Method GuzzleHttp\\\\Cookie\\\\SetCookie\\:\\:setPath\\(\\) has no return typehint specified\\.$#" + count: 1 + path: src/Cookie/SetCookie.php + + - + message: "#^Method GuzzleHttp\\\\Cookie\\\\SetCookie\\:\\:setMaxAge\\(\\) has no return typehint specified\\.$#" + count: 1 + path: src/Cookie/SetCookie.php + + - + message: "#^Method GuzzleHttp\\\\Cookie\\\\SetCookie\\:\\:setExpires\\(\\) has no return typehint specified\\.$#" + count: 1 + path: src/Cookie/SetCookie.php + + - + message: "#^Else branch is unreachable because ternary operator condition is always true\\.$#" + count: 1 + path: src/Cookie/SetCookie.php + + - + message: "#^Method GuzzleHttp\\\\Cookie\\\\SetCookie\\:\\:setSecure\\(\\) has no return typehint specified\\.$#" + count: 1 + path: src/Cookie/SetCookie.php + + - + message: "#^Method GuzzleHttp\\\\Cookie\\\\SetCookie\\:\\:setDiscard\\(\\) has no return typehint specified\\.$#" + count: 1 + path: src/Cookie/SetCookie.php + + - + message: "#^Method GuzzleHttp\\\\Cookie\\\\SetCookie\\:\\:setHttpOnly\\(\\) has no return typehint specified\\.$#" + count: 1 + path: src/Cookie/SetCookie.php + + - + message: "#^Parameter \\#1 \\$str of function ltrim expects string, string\\|null given\\.$#" + count: 1 + path: src/Cookie/SetCookie.php + + - + message: "#^Method GuzzleHttp\\\\Exception\\\\BadResponseException\\:\\:__construct\\(\\) has parameter \\$handlerContext with no value type specified in iterable type array\\.$#" + count: 1 + path: src/Exception/BadResponseException.php + + - + message: "#^Method GuzzleHttp\\\\Exception\\\\ConnectException\\:\\:__construct\\(\\) has parameter \\$handlerContext with no value type specified in iterable type array\\.$#" + count: 1 + path: src/Exception/ConnectException.php + + - + message: "#^Property GuzzleHttp\\\\Exception\\\\RequestException\\:\\:\\$handlerContext type has no value type specified in iterable type array\\.$#" + count: 1 + path: src/Exception/RequestException.php + + - + message: "#^Method GuzzleHttp\\\\Exception\\\\RequestException\\:\\:__construct\\(\\) has parameter \\$handlerContext with no value type specified in iterable type array\\.$#" + count: 1 + path: src/Exception/RequestException.php + + - + message: "#^Method GuzzleHttp\\\\Exception\\\\RequestException\\:\\:create\\(\\) has parameter \\$ctx with no value type specified in iterable type array\\.$#" + count: 1 + path: src/Exception/RequestException.php + + - + message: "#^Method GuzzleHttp\\\\Exception\\\\RequestException\\:\\:getHandlerContext\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: src/Exception/RequestException.php + + - + message: "#^Property GuzzleHttp\\\\Exception\\\\SeekException\\:\\:\\$stream has no typehint specified\\.$#" + count: 1 + path: src/Exception/SeekException.php + + - + message: "#^Method GuzzleHttp\\\\Exception\\\\SeekException\\:\\:__construct\\(\\) has parameter \\$msg with no typehint specified\\.$#" + count: 1 + path: src/Exception/SeekException.php + + - + message: "#^Method GuzzleHttp\\\\Exception\\\\SeekException\\:\\:__construct\\(\\) has parameter \\$pos with no typehint specified\\.$#" + count: 1 + path: src/Exception/SeekException.php + + - + message: "#^Property GuzzleHttp\\\\Handler\\\\CurlFactory\\:\\:\\$handles type has no value type specified in iterable type array\\.$#" + count: 1 + path: src/Handler/CurlFactory.php + + - + message: "#^Method GuzzleHttp\\\\Handler\\\\CurlFactory\\:\\:create\\(\\) has parameter \\$options with no value type specified in iterable type array\\.$#" + count: 1 + path: src/Handler/CurlFactory.php + + - + message: "#^Method GuzzleHttp\\\\Handler\\\\CurlFactory\\:\\:release\\(\\) has no return typehint specified\\.$#" + count: 1 + path: src/Handler/CurlFactory.php + + - + message: "#^Negated boolean expression is always false\\.$#" + count: 1 + path: src/Handler/CurlFactory.php + + - + message: "#^Method GuzzleHttp\\\\Handler\\\\CurlFactory\\:\\:invokeStats\\(\\) has no return typehint specified\\.$#" + count: 1 + path: src/Handler/CurlFactory.php + + - + message: "#^Method GuzzleHttp\\\\Handler\\\\CurlFactory\\:\\:finishError\\(\\) has no return typehint specified\\.$#" + count: 1 + path: src/Handler/CurlFactory.php + + - + message: "#^Method GuzzleHttp\\\\Handler\\\\CurlFactory\\:\\:createRejection\\(\\) has no return typehint specified\\.$#" + count: 1 + path: src/Handler/CurlFactory.php + + - + message: "#^Method GuzzleHttp\\\\Handler\\\\CurlFactory\\:\\:createRejection\\(\\) has parameter \\$ctx with no value type specified in iterable type array\\.$#" + count: 1 + path: src/Handler/CurlFactory.php + + - + message: "#^If condition is always true\\.$#" + count: 1 + path: src/Handler/CurlFactory.php + + - + message: "#^Unreachable statement \\- code above always terminates\\.$#" + count: 1 + path: src/Handler/CurlFactory.php + + - + message: "#^Method GuzzleHttp\\\\Handler\\\\CurlFactory\\:\\:getDefaultConf\\(\\) has no return typehint specified\\.$#" + count: 1 + path: src/Handler/CurlFactory.php + + - + message: "#^Method GuzzleHttp\\\\Handler\\\\CurlFactory\\:\\:applyMethod\\(\\) has no return typehint specified\\.$#" + count: 1 + path: src/Handler/CurlFactory.php + + - + message: "#^Method GuzzleHttp\\\\Handler\\\\CurlFactory\\:\\:applyMethod\\(\\) has parameter \\$conf with no value type specified in iterable type array\\.$#" + count: 1 + path: src/Handler/CurlFactory.php + + - + message: "#^Method GuzzleHttp\\\\Handler\\\\CurlFactory\\:\\:applyBody\\(\\) has no return typehint specified\\.$#" + count: 1 + path: src/Handler/CurlFactory.php + + - + message: "#^Method GuzzleHttp\\\\Handler\\\\CurlFactory\\:\\:applyBody\\(\\) has parameter \\$conf with no value type specified in iterable type array\\.$#" + count: 1 + path: src/Handler/CurlFactory.php + + - + message: "#^Method GuzzleHttp\\\\Handler\\\\CurlFactory\\:\\:applyBody\\(\\) has parameter \\$options with no value type specified in iterable type array\\.$#" + count: 1 + path: src/Handler/CurlFactory.php + + - + message: "#^Method GuzzleHttp\\\\Handler\\\\CurlFactory\\:\\:applyHeaders\\(\\) has no return typehint specified\\.$#" + count: 1 + path: src/Handler/CurlFactory.php + + - + message: "#^Method GuzzleHttp\\\\Handler\\\\CurlFactory\\:\\:applyHeaders\\(\\) has parameter \\$conf with no value type specified in iterable type array\\.$#" + count: 1 + path: src/Handler/CurlFactory.php + + - + message: "#^Method GuzzleHttp\\\\Handler\\\\CurlFactory\\:\\:removeHeader\\(\\) has no return typehint specified\\.$#" + count: 1 + path: src/Handler/CurlFactory.php + + - + message: "#^Method GuzzleHttp\\\\Handler\\\\CurlFactory\\:\\:removeHeader\\(\\) has parameter \\$options with no value type specified in iterable type array\\.$#" + count: 1 + path: src/Handler/CurlFactory.php + + - + message: "#^Parameter \\#1 \\$str1 of function strcasecmp expects string, int\\|string given\\.$#" + count: 1 + path: src/Handler/CurlFactory.php + + - + message: "#^Method GuzzleHttp\\\\Handler\\\\CurlFactory\\:\\:applyHandlerOptions\\(\\) has no return typehint specified\\.$#" + count: 1 + path: src/Handler/CurlFactory.php + + - + message: "#^Method GuzzleHttp\\\\Handler\\\\CurlFactory\\:\\:applyHandlerOptions\\(\\) has parameter \\$conf with no value type specified in iterable type array\\.$#" + count: 1 + path: src/Handler/CurlFactory.php + + - + message: "#^Parameter \\#1 \\$filename of function is_dir expects string, string\\|false given\\.$#" + count: 1 + path: src/Handler/CurlFactory.php + + - + message: "#^Method GuzzleHttp\\\\Handler\\\\CurlFactory\\:\\:retryFailedRewind\\(\\) has no return typehint specified\\.$#" + count: 1 + path: src/Handler/CurlFactory.php + + - + message: "#^Method GuzzleHttp\\\\Handler\\\\CurlFactory\\:\\:retryFailedRewind\\(\\) has parameter \\$ctx with no value type specified in iterable type array\\.$#" + count: 1 + path: src/Handler/CurlFactory.php + + - + message: "#^Method GuzzleHttp\\\\Handler\\\\CurlFactory\\:\\:createHeaderFn\\(\\) has no return typehint specified\\.$#" + count: 1 + path: src/Handler/CurlFactory.php + + - + message: "#^Method GuzzleHttp\\\\Handler\\\\CurlFactoryInterface\\:\\:create\\(\\) has parameter \\$options with no value type specified in iterable type array\\.$#" + count: 1 + path: src/Handler/CurlFactoryInterface.php + + - + message: "#^Method GuzzleHttp\\\\Handler\\\\CurlFactoryInterface\\:\\:release\\(\\) has no return typehint specified\\.$#" + count: 1 + path: src/Handler/CurlFactoryInterface.php + + - + message: "#^Method GuzzleHttp\\\\Handler\\\\CurlHandler\\:\\:__construct\\(\\) has parameter \\$options with no value type specified in iterable type array\\.$#" + count: 1 + path: src/Handler/CurlHandler.php + + - + message: "#^Method GuzzleHttp\\\\Handler\\\\CurlHandler\\:\\:__invoke\\(\\) has no return typehint specified\\.$#" + count: 1 + path: src/Handler/CurlHandler.php + + - + message: "#^Method GuzzleHttp\\\\Handler\\\\CurlHandler\\:\\:__invoke\\(\\) has parameter \\$options with no value type specified in iterable type array\\.$#" + count: 1 + path: src/Handler/CurlHandler.php + + - + message: "#^Property GuzzleHttp\\\\Handler\\\\CurlMultiHandler\\:\\:\\$selectTimeout has no typehint specified\\.$#" + count: 1 + path: src/Handler/CurlMultiHandler.php + + - + message: "#^Property GuzzleHttp\\\\Handler\\\\CurlMultiHandler\\:\\:\\$active has no typehint specified\\.$#" + count: 1 + path: src/Handler/CurlMultiHandler.php + + - + message: "#^Property GuzzleHttp\\\\Handler\\\\CurlMultiHandler\\:\\:\\$handles has no typehint specified\\.$#" + count: 1 + path: src/Handler/CurlMultiHandler.php + + - + message: "#^Property GuzzleHttp\\\\Handler\\\\CurlMultiHandler\\:\\:\\$delays has no typehint specified\\.$#" + count: 1 + path: src/Handler/CurlMultiHandler.php + + - + message: "#^Property GuzzleHttp\\\\Handler\\\\CurlMultiHandler\\:\\:\\$options has no typehint specified\\.$#" + count: 1 + path: src/Handler/CurlMultiHandler.php + + - + message: "#^Method GuzzleHttp\\\\Handler\\\\CurlMultiHandler\\:\\:__construct\\(\\) has parameter \\$options with no value type specified in iterable type array\\.$#" + count: 1 + path: src/Handler/CurlMultiHandler.php + + - + message: "#^Method GuzzleHttp\\\\Handler\\\\CurlMultiHandler\\:\\:__get\\(\\) has no return typehint specified\\.$#" + count: 1 + path: src/Handler/CurlMultiHandler.php + + - + message: "#^Method GuzzleHttp\\\\Handler\\\\CurlMultiHandler\\:\\:__get\\(\\) has parameter \\$name with no typehint specified\\.$#" + count: 1 + path: src/Handler/CurlMultiHandler.php + + - + message: "#^Property GuzzleHttp\\\\Handler\\\\CurlMultiHandler\\:\\:\\$_mh \\(resource\\) does not accept resource\\|false\\.$#" + count: 1 + path: src/Handler/CurlMultiHandler.php + + - + message: "#^Parameter \\#1 \\$mh of function curl_multi_setopt expects resource, resource\\|false given\\.$#" + count: 1 + path: src/Handler/CurlMultiHandler.php + + - + message: "#^Method GuzzleHttp\\\\Handler\\\\CurlMultiHandler\\:\\:__invoke\\(\\) has no return typehint specified\\.$#" + count: 1 + path: src/Handler/CurlMultiHandler.php + + - + message: "#^Method GuzzleHttp\\\\Handler\\\\CurlMultiHandler\\:\\:__invoke\\(\\) has parameter \\$options with no value type specified in iterable type array\\.$#" + count: 1 + path: src/Handler/CurlMultiHandler.php + + - + message: "#^Method GuzzleHttp\\\\Handler\\\\CurlMultiHandler\\:\\:tick\\(\\) has no return typehint specified\\.$#" + count: 1 + path: src/Handler/CurlMultiHandler.php + + - + message: "#^Method GuzzleHttp\\\\Handler\\\\CurlMultiHandler\\:\\:execute\\(\\) has no return typehint specified\\.$#" + count: 1 + path: src/Handler/CurlMultiHandler.php + + - + message: "#^Method GuzzleHttp\\\\Handler\\\\CurlMultiHandler\\:\\:addRequest\\(\\) has no return typehint specified\\.$#" + count: 1 + path: src/Handler/CurlMultiHandler.php + + - + message: "#^Method GuzzleHttp\\\\Handler\\\\CurlMultiHandler\\:\\:addRequest\\(\\) has parameter \\$entry with no value type specified in iterable type array\\.$#" + count: 1 + path: src/Handler/CurlMultiHandler.php + + - + message: "#^Method GuzzleHttp\\\\Handler\\\\CurlMultiHandler\\:\\:processMessages\\(\\) has no return typehint specified\\.$#" + count: 1 + path: src/Handler/CurlMultiHandler.php + + - + message: "#^Method GuzzleHttp\\\\Handler\\\\CurlMultiHandler\\:\\:timeToNext\\(\\) has no return typehint specified\\.$#" + count: 1 + path: src/Handler/CurlMultiHandler.php + + - + message: "#^Property GuzzleHttp\\\\Handler\\\\EasyHandle\\:\\:\\$headers type has no value type specified in iterable type array\\.$#" + count: 1 + path: src/Handler/EasyHandle.php + + - + message: "#^Property GuzzleHttp\\\\Handler\\\\EasyHandle\\:\\:\\$options type has no value type specified in iterable type array\\.$#" + count: 1 + path: src/Handler/EasyHandle.php + + - + message: "#^Method GuzzleHttp\\\\Handler\\\\EasyHandle\\:\\:createResponse\\(\\) has no return typehint specified\\.$#" + count: 1 + path: src/Handler/EasyHandle.php + + - + message: "#^Parameter \\#1 \\$status of class GuzzleHttp\\\\Psr7\\\\Response constructor expects int, string given\\.$#" + count: 1 + path: src/Handler/EasyHandle.php + + - + message: "#^Method GuzzleHttp\\\\Handler\\\\EasyHandle\\:\\:__get\\(\\) has no return typehint specified\\.$#" + count: 1 + path: src/Handler/EasyHandle.php + + - + message: "#^Method GuzzleHttp\\\\Handler\\\\EasyHandle\\:\\:__get\\(\\) has parameter \\$name with no typehint specified\\.$#" + count: 1 + path: src/Handler/EasyHandle.php + + - + message: "#^Property GuzzleHttp\\\\Handler\\\\MockHandler\\:\\:\\$queue has no typehint specified\\.$#" + count: 1 + path: src/Handler/MockHandler.php + + - + message: "#^Property GuzzleHttp\\\\Handler\\\\MockHandler\\:\\:\\$lastRequest has no typehint specified\\.$#" + count: 1 + path: src/Handler/MockHandler.php + + - + message: "#^Property GuzzleHttp\\\\Handler\\\\MockHandler\\:\\:\\$lastOptions has no typehint specified\\.$#" + count: 1 + path: src/Handler/MockHandler.php + + - + message: "#^Property GuzzleHttp\\\\Handler\\\\MockHandler\\:\\:\\$onFulfilled has no typehint specified\\.$#" + count: 1 + path: src/Handler/MockHandler.php + + - + message: "#^Property GuzzleHttp\\\\Handler\\\\MockHandler\\:\\:\\$onRejected has no typehint specified\\.$#" + count: 1 + path: src/Handler/MockHandler.php + + - + message: "#^Method GuzzleHttp\\\\Handler\\\\MockHandler\\:\\:createWithMiddleware\\(\\) has parameter \\$queue with no value type specified in iterable type array\\.$#" + count: 1 + path: src/Handler/MockHandler.php + + - + message: "#^Method GuzzleHttp\\\\Handler\\\\MockHandler\\:\\:__construct\\(\\) has parameter \\$queue with no value type specified in iterable type array\\.$#" + count: 1 + path: src/Handler/MockHandler.php + + - + message: "#^Parameter \\#2 \\$parameters of function call_user_func_array expects array\\, array given\\.$#" + count: 1 + path: src/Handler/MockHandler.php + + - + message: "#^Method GuzzleHttp\\\\Handler\\\\MockHandler\\:\\:__invoke\\(\\) has no return typehint specified\\.$#" + count: 1 + path: src/Handler/MockHandler.php + + - + message: "#^Method GuzzleHttp\\\\Handler\\\\MockHandler\\:\\:__invoke\\(\\) has parameter \\$options with no value type specified in iterable type array\\.$#" + count: 1 + path: src/Handler/MockHandler.php + + - + message: "#^Binary operation \"\\*\" between float\\|int\\|string and 1000 results in an error\\.$#" + count: 1 + path: src/Handler/MockHandler.php + + - + message: "#^Method GuzzleHttp\\\\Handler\\\\MockHandler\\:\\:append\\(\\) has no return typehint specified\\.$#" + count: 1 + path: src/Handler/MockHandler.php + + - + message: "#^Method GuzzleHttp\\\\Handler\\\\MockHandler\\:\\:getLastOptions\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: src/Handler/MockHandler.php + + - + message: "#^Method GuzzleHttp\\\\Handler\\\\MockHandler\\:\\:reset\\(\\) has no return typehint specified\\.$#" + count: 1 + path: src/Handler/MockHandler.php + + - + message: "#^Method GuzzleHttp\\\\Handler\\\\MockHandler\\:\\:invokeStats\\(\\) has no return typehint specified\\.$#" + count: 1 + path: src/Handler/MockHandler.php + + - + message: "#^Method GuzzleHttp\\\\Handler\\\\MockHandler\\:\\:invokeStats\\(\\) has parameter \\$options with no value type specified in iterable type array\\.$#" + count: 1 + path: src/Handler/MockHandler.php + + - + message: "#^Method GuzzleHttp\\\\Handler\\\\MockHandler\\:\\:invokeStats\\(\\) has parameter \\$reason with no typehint specified\\.$#" + count: 1 + path: src/Handler/MockHandler.php + + - + message: "#^Property GuzzleHttp\\\\Handler\\\\StreamHandler\\:\\:\\$lastHeaders has no typehint specified\\.$#" + count: 1 + path: src/Handler/StreamHandler.php + + - + message: "#^Method GuzzleHttp\\\\Handler\\\\StreamHandler\\:\\:__invoke\\(\\) has parameter \\$options with no value type specified in iterable type array\\.$#" + count: 1 + path: src/Handler/StreamHandler.php + + - + message: "#^Method GuzzleHttp\\\\Handler\\\\StreamHandler\\:\\:invokeStats\\(\\) has no return typehint specified\\.$#" + count: 1 + path: src/Handler/StreamHandler.php + + - + message: "#^Method GuzzleHttp\\\\Handler\\\\StreamHandler\\:\\:invokeStats\\(\\) has parameter \\$error with no typehint specified\\.$#" + count: 1 + path: src/Handler/StreamHandler.php + + - + message: "#^Method GuzzleHttp\\\\Handler\\\\StreamHandler\\:\\:invokeStats\\(\\) has parameter \\$options with no value type specified in iterable type array\\.$#" + count: 1 + path: src/Handler/StreamHandler.php + + - + message: "#^Method GuzzleHttp\\\\Handler\\\\StreamHandler\\:\\:invokeStats\\(\\) has parameter \\$startTime with no typehint specified\\.$#" + count: 1 + path: src/Handler/StreamHandler.php + + - + message: "#^Method GuzzleHttp\\\\Handler\\\\StreamHandler\\:\\:createResponse\\(\\) has no return typehint specified\\.$#" + count: 1 + path: src/Handler/StreamHandler.php + + - + message: "#^Method GuzzleHttp\\\\Handler\\\\StreamHandler\\:\\:createResponse\\(\\) has parameter \\$options with no value type specified in iterable type array\\.$#" + count: 1 + path: src/Handler/StreamHandler.php + + - + message: "#^Method GuzzleHttp\\\\Handler\\\\StreamHandler\\:\\:createResponse\\(\\) has parameter \\$startTime with no typehint specified\\.$#" + count: 1 + path: src/Handler/StreamHandler.php + + - + message: "#^Method GuzzleHttp\\\\Handler\\\\StreamHandler\\:\\:createResponse\\(\\) has parameter \\$stream with no typehint specified\\.$#" + count: 1 + path: src/Handler/StreamHandler.php + + - + message: "#^Parameter \\#1 \\$status of class GuzzleHttp\\\\Psr7\\\\Response constructor expects int, string given\\.$#" + count: 1 + path: src/Handler/StreamHandler.php + + - + message: "#^Method GuzzleHttp\\\\Handler\\\\StreamHandler\\:\\:createSink\\(\\) has no return typehint specified\\.$#" + count: 1 + path: src/Handler/StreamHandler.php + + - + message: "#^Method GuzzleHttp\\\\Handler\\\\StreamHandler\\:\\:createSink\\(\\) has parameter \\$options with no value type specified in iterable type array\\.$#" + count: 1 + path: src/Handler/StreamHandler.php + + - + message: "#^Method GuzzleHttp\\\\Handler\\\\StreamHandler\\:\\:checkDecode\\(\\) has no return typehint specified\\.$#" + count: 1 + path: src/Handler/StreamHandler.php + + - + message: "#^Method GuzzleHttp\\\\Handler\\\\StreamHandler\\:\\:checkDecode\\(\\) has parameter \\$headers with no value type specified in iterable type array\\.$#" + count: 1 + path: src/Handler/StreamHandler.php + + - + message: "#^Method GuzzleHttp\\\\Handler\\\\StreamHandler\\:\\:checkDecode\\(\\) has parameter \\$options with no value type specified in iterable type array\\.$#" + count: 1 + path: src/Handler/StreamHandler.php + + - + message: "#^Method GuzzleHttp\\\\Handler\\\\StreamHandler\\:\\:checkDecode\\(\\) has parameter \\$stream with no typehint specified\\.$#" + count: 1 + path: src/Handler/StreamHandler.php + + - + message: "#^Argument of an invalid type array\\\\>\\|null supplied for foreach, only iterables are supported\\.$#" + count: 1 + path: src/Handler/StreamHandler.php + + - + message: "#^Method GuzzleHttp\\\\Handler\\\\StreamHandler\\:\\:createStream\\(\\) has no return typehint specified\\.$#" + count: 1 + path: src/Handler/StreamHandler.php + + - + message: "#^Method GuzzleHttp\\\\Handler\\\\StreamHandler\\:\\:createStream\\(\\) has parameter \\$options with no value type specified in iterable type array\\.$#" + count: 1 + path: src/Handler/StreamHandler.php + + - + message: "#^Parameter \\#3 \\$use_include_path of function fopen expects bool, null given\\.$#" + count: 1 + path: src/Handler/StreamHandler.php + + - + message: "#^Parameter \\#1 \\$stream of function stream_set_timeout expects resource, resource\\|false given\\.$#" + count: 1 + path: src/Handler/StreamHandler.php + + - + message: "#^Method GuzzleHttp\\\\Handler\\\\StreamHandler\\:\\:resolveHost\\(\\) has no return typehint specified\\.$#" + count: 1 + path: src/Handler/StreamHandler.php + + - + message: "#^Method GuzzleHttp\\\\Handler\\\\StreamHandler\\:\\:resolveHost\\(\\) has parameter \\$options with no value type specified in iterable type array\\.$#" + count: 1 + path: src/Handler/StreamHandler.php + + - + message: "#^Cannot access offset 0 on array\\|false\\.$#" + count: 2 + path: src/Handler/StreamHandler.php + + - + message: "#^Method GuzzleHttp\\\\Handler\\\\StreamHandler\\:\\:getDefaultContext\\(\\) has no return typehint specified\\.$#" + count: 1 + path: src/Handler/StreamHandler.php + + - + message: "#^Method GuzzleHttp\\\\Handler\\\\StreamHandler\\:\\:add_proxy\\(\\) has no return typehint specified\\.$#" + count: 1 + path: src/Handler/StreamHandler.php + + - + message: "#^Method GuzzleHttp\\\\Handler\\\\StreamHandler\\:\\:add_proxy\\(\\) has parameter \\$options with no typehint specified\\.$#" + count: 1 + path: src/Handler/StreamHandler.php + + - + message: "#^Method GuzzleHttp\\\\Handler\\\\StreamHandler\\:\\:add_proxy\\(\\) has parameter \\$params with no typehint specified\\.$#" + count: 1 + path: src/Handler/StreamHandler.php + + - + message: "#^Method GuzzleHttp\\\\Handler\\\\StreamHandler\\:\\:add_proxy\\(\\) has parameter \\$value with no typehint specified\\.$#" + count: 1 + path: src/Handler/StreamHandler.php + + - + message: "#^Method GuzzleHttp\\\\Handler\\\\StreamHandler\\:\\:add_timeout\\(\\) has no return typehint specified\\.$#" + count: 1 + path: src/Handler/StreamHandler.php + + - + message: "#^Method GuzzleHttp\\\\Handler\\\\StreamHandler\\:\\:add_timeout\\(\\) has parameter \\$options with no typehint specified\\.$#" + count: 1 + path: src/Handler/StreamHandler.php + + - + message: "#^Method GuzzleHttp\\\\Handler\\\\StreamHandler\\:\\:add_timeout\\(\\) has parameter \\$params with no typehint specified\\.$#" + count: 1 + path: src/Handler/StreamHandler.php + + - + message: "#^Method GuzzleHttp\\\\Handler\\\\StreamHandler\\:\\:add_timeout\\(\\) has parameter \\$value with no typehint specified\\.$#" + count: 1 + path: src/Handler/StreamHandler.php + + - + message: "#^Method GuzzleHttp\\\\Handler\\\\StreamHandler\\:\\:add_verify\\(\\) has no return typehint specified\\.$#" + count: 1 + path: src/Handler/StreamHandler.php + + - + message: "#^Method GuzzleHttp\\\\Handler\\\\StreamHandler\\:\\:add_verify\\(\\) has parameter \\$options with no typehint specified\\.$#" + count: 1 + path: src/Handler/StreamHandler.php + + - + message: "#^Method GuzzleHttp\\\\Handler\\\\StreamHandler\\:\\:add_verify\\(\\) has parameter \\$params with no typehint specified\\.$#" + count: 1 + path: src/Handler/StreamHandler.php + + - + message: "#^Method GuzzleHttp\\\\Handler\\\\StreamHandler\\:\\:add_verify\\(\\) has parameter \\$value with no typehint specified\\.$#" + count: 1 + path: src/Handler/StreamHandler.php + + - + message: "#^Method GuzzleHttp\\\\Handler\\\\StreamHandler\\:\\:add_cert\\(\\) has no return typehint specified\\.$#" + count: 1 + path: src/Handler/StreamHandler.php + + - + message: "#^Method GuzzleHttp\\\\Handler\\\\StreamHandler\\:\\:add_cert\\(\\) has parameter \\$options with no typehint specified\\.$#" + count: 1 + path: src/Handler/StreamHandler.php + + - + message: "#^Method GuzzleHttp\\\\Handler\\\\StreamHandler\\:\\:add_cert\\(\\) has parameter \\$params with no typehint specified\\.$#" + count: 1 + path: src/Handler/StreamHandler.php + + - + message: "#^Method GuzzleHttp\\\\Handler\\\\StreamHandler\\:\\:add_cert\\(\\) has parameter \\$value with no typehint specified\\.$#" + count: 1 + path: src/Handler/StreamHandler.php + + - + message: "#^Method GuzzleHttp\\\\Handler\\\\StreamHandler\\:\\:add_progress\\(\\) has no return typehint specified\\.$#" + count: 1 + path: src/Handler/StreamHandler.php + + - + message: "#^Method GuzzleHttp\\\\Handler\\\\StreamHandler\\:\\:add_progress\\(\\) has parameter \\$options with no typehint specified\\.$#" + count: 1 + path: src/Handler/StreamHandler.php + + - + message: "#^Method GuzzleHttp\\\\Handler\\\\StreamHandler\\:\\:add_progress\\(\\) has parameter \\$params with no typehint specified\\.$#" + count: 1 + path: src/Handler/StreamHandler.php + + - + message: "#^Method GuzzleHttp\\\\Handler\\\\StreamHandler\\:\\:add_progress\\(\\) has parameter \\$value with no typehint specified\\.$#" + count: 1 + path: src/Handler/StreamHandler.php + + - + message: "#^Method GuzzleHttp\\\\Handler\\\\StreamHandler\\:\\:add_debug\\(\\) has no return typehint specified\\.$#" + count: 1 + path: src/Handler/StreamHandler.php + + - + message: "#^Method GuzzleHttp\\\\Handler\\\\StreamHandler\\:\\:add_debug\\(\\) has parameter \\$options with no typehint specified\\.$#" + count: 1 + path: src/Handler/StreamHandler.php + + - + message: "#^Method GuzzleHttp\\\\Handler\\\\StreamHandler\\:\\:add_debug\\(\\) has parameter \\$params with no typehint specified\\.$#" + count: 1 + path: src/Handler/StreamHandler.php + + - + message: "#^Method GuzzleHttp\\\\Handler\\\\StreamHandler\\:\\:add_debug\\(\\) has parameter \\$value with no typehint specified\\.$#" + count: 1 + path: src/Handler/StreamHandler.php + + - + message: "#^Method GuzzleHttp\\\\Handler\\\\StreamHandler\\:\\:addNotification\\(\\) has no return typehint specified\\.$#" + count: 1 + path: src/Handler/StreamHandler.php + + - + message: "#^Method GuzzleHttp\\\\Handler\\\\StreamHandler\\:\\:addNotification\\(\\) has parameter \\$params with no value type specified in iterable type array\\.$#" + count: 1 + path: src/Handler/StreamHandler.php + + - + message: "#^Method GuzzleHttp\\\\Handler\\\\StreamHandler\\:\\:callArray\\(\\) has no return typehint specified\\.$#" + count: 1 + path: src/Handler/StreamHandler.php + + - + message: "#^Method GuzzleHttp\\\\Handler\\\\StreamHandler\\:\\:callArray\\(\\) has parameter \\$functions with no value type specified in iterable type array\\.$#" + count: 1 + path: src/Handler/StreamHandler.php + + - + message: "#^Property GuzzleHttp\\\\HandlerStack\\:\\:\\$stack type has no value type specified in iterable type array\\.$#" + count: 1 + path: src/HandlerStack.php + + - + message: "#^Method GuzzleHttp\\\\HandlerStack\\:\\:__invoke\\(\\) has parameter \\$options with no value type specified in iterable type array\\.$#" + count: 1 + path: src/HandlerStack.php + + - + message: "#^Method GuzzleHttp\\\\HandlerStack\\:\\:setHandler\\(\\) has no return typehint specified\\.$#" + count: 1 + path: src/HandlerStack.php + + - + message: "#^Method GuzzleHttp\\\\HandlerStack\\:\\:unshift\\(\\) has no return typehint specified\\.$#" + count: 1 + path: src/HandlerStack.php + + - + message: "#^Method GuzzleHttp\\\\HandlerStack\\:\\:push\\(\\) has no return typehint specified\\.$#" + count: 1 + path: src/HandlerStack.php + + - + message: "#^Method GuzzleHttp\\\\HandlerStack\\:\\:before\\(\\) has no return typehint specified\\.$#" + count: 1 + path: src/HandlerStack.php + + - + message: "#^Method GuzzleHttp\\\\HandlerStack\\:\\:after\\(\\) has no return typehint specified\\.$#" + count: 1 + path: src/HandlerStack.php + + - + message: "#^Method GuzzleHttp\\\\HandlerStack\\:\\:remove\\(\\) has no return typehint specified\\.$#" + count: 1 + path: src/HandlerStack.php + + - + message: "#^Method GuzzleHttp\\\\HandlerStack\\:\\:splice\\(\\) has no return typehint specified\\.$#" + count: 1 + path: src/HandlerStack.php + + - + message: "#^Method GuzzleHttp\\\\HandlerStack\\:\\:debugCallable\\(\\) has parameter \\$fn with no value type specified in iterable type array\\.$#" + count: 1 + path: src/HandlerStack.php + + - + message: "#^Parameter \\#1 \\$obj of function spl_object_hash expects object, callable given\\.$#" + count: 1 + path: src/HandlerStack.php + + - + message: "#^Method GuzzleHttp\\\\MessageFormatter\\:\\:format\\(\\) should return string but returns string\\|null\\.$#" + count: 1 + path: src/MessageFormatter.php + + - + message: "#^Method GuzzleHttp\\\\Middleware\\:\\:history\\(\\) has parameter \\$container with generic interface ArrayAccess but does not specify its types\\: TKey, TValue$#" + count: 1 + path: src/Middleware.php + + - + message: "#^Method GuzzleHttp\\\\Middleware\\:\\:history\\(\\) has parameter \\$container with no value type specified in iterable type array\\.$#" + count: 1 + path: src/Middleware.php + + - + message: "#^Result of && is always false\\.$#" + count: 1 + path: src/Middleware.php + + - + message: "#^Method GuzzleHttp\\\\Pool\\:\\:__construct\\(\\) has parameter \\$config with no value type specified in iterable type array\\.$#" + count: 1 + path: src/Pool.php + + - + message: "#^Method GuzzleHttp\\\\Pool\\:\\:__construct\\(\\) has parameter \\$requests with no value type specified in iterable type array\\|Iterator\\.$#" + count: 1 + path: src/Pool.php + + - + message: "#^Method GuzzleHttp\\\\Pool\\:\\:batch\\(\\) has parameter \\$options with no value type specified in iterable type array\\.$#" + count: 1 + path: src/Pool.php + + - + message: "#^Method GuzzleHttp\\\\Pool\\:\\:batch\\(\\) has parameter \\$requests with no value type specified in iterable type array\\|Iterator\\.$#" + count: 1 + path: src/Pool.php + + - + message: "#^Method GuzzleHttp\\\\Pool\\:\\:batch\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: src/Pool.php + + - + message: "#^Unsafe usage of new static\\(\\)\\.$#" + count: 1 + path: src/Pool.php + + - + message: "#^Method GuzzleHttp\\\\Pool\\:\\:cmpCallback\\(\\) has parameter \\$name with no typehint specified\\.$#" + count: 1 + path: src/Pool.php + + - + message: "#^Method GuzzleHttp\\\\Pool\\:\\:cmpCallback\\(\\) has parameter \\$options with no value type specified in iterable type array\\.$#" + count: 1 + path: src/Pool.php + + - + message: "#^Method GuzzleHttp\\\\Pool\\:\\:cmpCallback\\(\\) has parameter \\$results with no value type specified in iterable type array\\.$#" + count: 1 + path: src/Pool.php + + - + message: "#^Method GuzzleHttp\\\\PrepareBodyMiddleware\\:\\:__invoke\\(\\) has parameter \\$options with no value type specified in iterable type array\\.$#" + count: 1 + path: src/PrepareBodyMiddleware.php + + - + message: "#^Method GuzzleHttp\\\\PrepareBodyMiddleware\\:\\:addExpectHeader\\(\\) has parameter \\$modify with no value type specified in iterable type array\\.$#" + count: 1 + path: src/PrepareBodyMiddleware.php + + - + message: "#^Method GuzzleHttp\\\\PrepareBodyMiddleware\\:\\:addExpectHeader\\(\\) has parameter \\$options with no value type specified in iterable type array\\.$#" + count: 1 + path: src/PrepareBodyMiddleware.php + + - + message: "#^Property GuzzleHttp\\\\RedirectMiddleware\\:\\:\\$defaultSettings has no typehint specified\\.$#" + count: 1 + path: src/RedirectMiddleware.php + + - + message: "#^Method GuzzleHttp\\\\RedirectMiddleware\\:\\:__invoke\\(\\) has parameter \\$options with no value type specified in iterable type array\\.$#" + count: 1 + path: src/RedirectMiddleware.php + + - + message: "#^Method GuzzleHttp\\\\RedirectMiddleware\\:\\:checkRedirect\\(\\) has parameter \\$options with no value type specified in iterable type array\\.$#" + count: 1 + path: src/RedirectMiddleware.php + + - + message: "#^Parameter \\#1 \\$str of function substr expects string, int given\\.$#" + count: 1 + path: src/RedirectMiddleware.php + + - + message: "#^Parameter \\#1 \\$promise of method GuzzleHttp\\\\RedirectMiddleware\\:\\:withTracking\\(\\) expects GuzzleHttp\\\\Promise\\\\PromiseInterface, GuzzleHttp\\\\Promise\\\\PromiseInterface\\|Psr\\\\Http\\\\Message\\\\ResponseInterface given\\.$#" + count: 1 + path: src/RedirectMiddleware.php + + - + message: "#^Method GuzzleHttp\\\\RedirectMiddleware\\:\\:withTracking\\(\\) has parameter \\$statusCode with no typehint specified\\.$#" + count: 1 + path: src/RedirectMiddleware.php + + - + message: "#^Method GuzzleHttp\\\\RedirectMiddleware\\:\\:withTracking\\(\\) has parameter \\$uri with no typehint specified\\.$#" + count: 1 + path: src/RedirectMiddleware.php + + - + message: "#^Parameter \\#2 \\$value of method Psr\\\\Http\\\\Message\\\\MessageInterface\\:\\:withHeader\\(\\) expects array\\\\|string, array given\\.$#" + count: 2 + path: src/RedirectMiddleware.php + + - + message: "#^Method GuzzleHttp\\\\RedirectMiddleware\\:\\:guardMax\\(\\) has parameter \\$options with no value type specified in iterable type array\\.$#" + count: 1 + path: src/RedirectMiddleware.php + + - + message: "#^Method GuzzleHttp\\\\RedirectMiddleware\\:\\:modifyRequest\\(\\) has parameter \\$options with no value type specified in iterable type array\\.$#" + count: 1 + path: src/RedirectMiddleware.php + + - + message: "#^Method GuzzleHttp\\\\RedirectMiddleware\\:\\:redirectUri\\(\\) has parameter \\$protocols with no value type specified in iterable type array\\.$#" + count: 1 + path: src/RedirectMiddleware.php + + - + message: "#^Method GuzzleHttp\\\\RetryMiddleware\\:\\:__invoke\\(\\) has parameter \\$options with no value type specified in iterable type array\\.$#" + count: 1 + path: src/RetryMiddleware.php + + - + message: "#^Method GuzzleHttp\\\\RetryMiddleware\\:\\:onFulfilled\\(\\) has parameter \\$options with no value type specified in iterable type array\\.$#" + count: 1 + path: src/RetryMiddleware.php + + - + message: "#^Method GuzzleHttp\\\\RetryMiddleware\\:\\:onRejected\\(\\) has parameter \\$options with no value type specified in iterable type array\\.$#" + count: 1 + path: src/RetryMiddleware.php + + - + message: "#^Method GuzzleHttp\\\\RetryMiddleware\\:\\:doRetry\\(\\) has parameter \\$options with no value type specified in iterable type array\\.$#" + count: 1 + path: src/RetryMiddleware.php + + - + message: "#^Method GuzzleHttp\\\\RetryMiddleware\\:\\:doRetry\\(\\) should return GuzzleHttp\\\\RetryMiddleware but returns GuzzleHttp\\\\Promise\\\\PromiseInterface\\.$#" + count: 1 + path: src/RetryMiddleware.php + + - + message: "#^Property GuzzleHttp\\\\TransferStats\\:\\:\\$request has no typehint specified\\.$#" + count: 1 + path: src/TransferStats.php + + - + message: "#^Property GuzzleHttp\\\\TransferStats\\:\\:\\$response has no typehint specified\\.$#" + count: 1 + path: src/TransferStats.php + + - + message: "#^Property GuzzleHttp\\\\TransferStats\\:\\:\\$transferTime has no typehint specified\\.$#" + count: 1 + path: src/TransferStats.php + + - + message: "#^Property GuzzleHttp\\\\TransferStats\\:\\:\\$handlerStats has no typehint specified\\.$#" + count: 1 + path: src/TransferStats.php + + - + message: "#^Property GuzzleHttp\\\\TransferStats\\:\\:\\$handlerErrorData has no typehint specified\\.$#" + count: 1 + path: src/TransferStats.php + + - + message: "#^Method GuzzleHttp\\\\TransferStats\\:\\:__construct\\(\\) has parameter \\$handlerStats with no value type specified in iterable type array\\.$#" + count: 1 + path: src/TransferStats.php + + - + message: "#^Method GuzzleHttp\\\\TransferStats\\:\\:getHandlerStats\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: src/TransferStats.php + + - + message: "#^Property GuzzleHttp\\\\UriTemplate\\:\\:\\$variables type has no value type specified in iterable type array\\.$#" + count: 1 + path: src/UriTemplate.php + + - + message: "#^Property GuzzleHttp\\\\UriTemplate\\:\\:\\$operatorHash type has no value type specified in iterable type array\\.$#" + count: 1 + path: src/UriTemplate.php + + - + message: "#^Property GuzzleHttp\\\\UriTemplate\\:\\:\\$delims type has no value type specified in iterable type array\\.$#" + count: 1 + path: src/UriTemplate.php + + - + message: "#^Property GuzzleHttp\\\\UriTemplate\\:\\:\\$delimsPct type has no value type specified in iterable type array\\.$#" + count: 1 + path: src/UriTemplate.php + + - + message: "#^Method GuzzleHttp\\\\UriTemplate\\:\\:expand\\(\\) has no return typehint specified\\.$#" + count: 1 + path: src/UriTemplate.php + + - + message: "#^Method GuzzleHttp\\\\UriTemplate\\:\\:expand\\(\\) has parameter \\$template with no typehint specified\\.$#" + count: 1 + path: src/UriTemplate.php + + - + message: "#^Method GuzzleHttp\\\\UriTemplate\\:\\:expand\\(\\) has parameter \\$variables with no value type specified in iterable type array\\.$#" + count: 1 + path: src/UriTemplate.php + + - + message: "#^Method GuzzleHttp\\\\UriTemplate\\:\\:parseExpression\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: src/UriTemplate.php + + - + message: "#^Method GuzzleHttp\\\\UriTemplate\\:\\:expandMatch\\(\\) has parameter \\$matches with no value type specified in iterable type array\\.$#" + count: 1 + path: src/UriTemplate.php + + - + message: "#^Method GuzzleHttp\\\\UriTemplate\\:\\:isAssoc\\(\\) has parameter \\$array with no value type specified in iterable type array\\.$#" + count: 1 + path: src/UriTemplate.php + + - + message: "#^Method GuzzleHttp\\\\Utils\\:\\:idnToAsci\\(\\) has parameter \\$info with no value type specified in iterable type array\\.$#" + count: 1 + path: src/Utils.php + + - + message: "#^Function GuzzleHttp\\\\uri_template\\(\\) has parameter \\$variables with no value type specified in iterable type array\\.$#" + count: 1 + path: src/functions.php + + - + message: "#^Function uri_template not found\\.$#" + count: 1 + path: src/functions.php + + - + message: "#^Parameter \\#1 \\$str of function rtrim expects string, string\\|false given\\.$#" + count: 1 + path: src/functions.php + + - + message: "#^Function GuzzleHttp\\\\headers_from_lines\\(\\) has parameter \\$lines with no value type specified in iterable type iterable\\.$#" + count: 1 + path: src/functions.php + + - + message: "#^Function GuzzleHttp\\\\headers_from_lines\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: src/functions.php + + - + message: "#^Function GuzzleHttp\\\\debug_resource\\(\\) should return resource but returns resource\\|false\\.$#" + count: 1 + path: src/functions.php + + - + message: "#^Function GuzzleHttp\\\\normalize_header_keys\\(\\) has parameter \\$headers with no value type specified in iterable type array\\.$#" + count: 1 + path: src/functions.php + + - + message: "#^Function GuzzleHttp\\\\normalize_header_keys\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: src/functions.php + + - + message: "#^Function GuzzleHttp\\\\is_host_in_noproxy\\(\\) has parameter \\$noProxyArray with no value type specified in iterable type array\\.$#" + count: 1 + path: src/functions.php + + - + message: "#^Cannot access offset 0 on array\\\\|false\\.$#" + count: 1 + path: src/functions.php + + - + message: "#^Function GuzzleHttp\\\\json_encode\\(\\) should return string but returns string\\|false\\.$#" + count: 1 + path: src/functions.php + diff --git a/phpstan.neon.dist b/phpstan.neon.dist new file mode 100644 index 000000000..2d83c9846 --- /dev/null +++ b/phpstan.neon.dist @@ -0,0 +1,7 @@ +includes: + - phpstan-baseline.neon + +parameters: + level: max + paths: + - src diff --git a/phpunit.xml.dist b/phpunit.xml.dist index 70da48c2f..6316581c3 100644 --- a/phpunit.xml.dist +++ b/phpunit.xml.dist @@ -1,9 +1,13 @@ - + - + tests diff --git a/src/Client.php b/src/Client.php index 80417918d..315a022cf 100644 --- a/src/Client.php +++ b/src/Client.php @@ -2,11 +2,12 @@ namespace GuzzleHttp; use GuzzleHttp\Cookie\CookieJar; +use GuzzleHttp\Exception\GuzzleException; use GuzzleHttp\Promise; use GuzzleHttp\Psr7; -use Psr\Http\Message\UriInterface; use Psr\Http\Message\RequestInterface; use Psr\Http\Message\ResponseInterface; +use Psr\Http\Message\UriInterface; /** * @method ResponseInterface get(string|UriInterface $uri, array $options = []) @@ -46,9 +47,8 @@ class Client implements ClientInterface * 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 + * Psr7\Http\Message\ResponseInterface on success. + * 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 @@ -75,6 +75,12 @@ public function __construct(array $config = []) $this->configureDefaults($config); } + /** + * @param string $method + * @param array $args + * + * @return Promise\PromiseInterface + */ public function __call($method, $args) { if (count($args) < 1) { @@ -89,6 +95,14 @@ public function __call($method, $args) : $this->request($method, $uri, $opts); } + /** + * Asynchronously send an HTTP request. + * + * @param array $options Request options to apply to the given + * request and to the transfer. See \GuzzleHttp\RequestOptions. + * + * @return Promise\PromiseInterface + */ public function sendAsync(RequestInterface $request, array $options = []) { // Merge the base URI into the request URI if needed. @@ -100,12 +114,35 @@ public function sendAsync(RequestInterface $request, array $options = []) ); } + /** + * Send an HTTP request. + * + * @param array $options Request options to apply to the given + * request and to the transfer. See \GuzzleHttp\RequestOptions. + * + * @return ResponseInterface + * @throws GuzzleException + */ public function send(RequestInterface $request, array $options = []) { $options[RequestOptions::SYNCHRONOUS] = true; return $this->sendAsync($request, $options)->wait(); } + /** + * 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. See \GuzzleHttp\RequestOptions. + * + * @return Promise\PromiseInterface + */ public function requestAsync($method, $uri = '', array $options = []) { $options = $this->prepareDefaults($options); @@ -125,12 +162,37 @@ public function requestAsync($method, $uri = '', array $options = []) return $this->transfer($request, $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. See \GuzzleHttp\RequestOptions. + * + * @return ResponseInterface + * @throws GuzzleException + */ public function request($method, $uri = '', array $options = []) { $options[RequestOptions::SYNCHRONOUS] = true; return $this->requestAsync($method, $uri, $options)->wait(); } + /** + * 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) { return $option === null @@ -138,6 +200,11 @@ public function getConfig($option = null) : (isset($this->config[$option]) ? $this->config[$option] : null); } + /** + * @param string|null $uri + * + * @return UriInterface + */ private function buildUri($uri, array $config) { // for BC we accept null which would otherwise fail in uri_for @@ -147,6 +214,11 @@ private function buildUri($uri, array $config) $uri = Psr7\UriResolver::resolve(Psr7\uri_for($config['base_uri']), $uri); } + if (isset($config['idn_conversion']) && ($config['idn_conversion'] !== false)) { + $idnOptions = ($config['idn_conversion'] === true) ? IDNA_DEFAULT : $config['idn_conversion']; + $uri = Utils::idnUriConvert($uri, $idnOptions); + } + return $uri->getScheme() === '' && $uri->getHost() !== '' ? $uri->withScheme('http') : $uri; } @@ -154,6 +226,7 @@ private function buildUri($uri, array $config) * Configures the default options for a client. * * @param array $config + * @return void */ private function configureDefaults(array $config) { @@ -162,7 +235,8 @@ private function configureDefaults(array $config) 'http_errors' => true, 'decode_content' => true, 'verify' => true, - 'cookies' => false + 'cookies' => false, + 'idn_conversion' => true, ]; // Use the standard Linux HTTP_PROXY and HTTPS_PROXY if set. @@ -170,7 +244,7 @@ private function configureDefaults(array $config) // 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')) { + if (php_sapi_name() === 'cli' && getenv('HTTP_PROXY')) { $defaults['proxy']['http'] = getenv('HTTP_PROXY'); } @@ -210,7 +284,7 @@ private function configureDefaults(array $config) * * @return array */ - private function prepareDefaults($options) + private function prepareDefaults(array $options) { $defaults = $this->config; @@ -225,7 +299,7 @@ private function prepareDefaults($options) if (array_key_exists('headers', $options)) { // Allows default headers to be unset. if ($options['headers'] === null) { - $defaults['_conditional'] = null; + $defaults['_conditional'] = []; unset($options['headers']); } elseif (!is_array($options['headers'])) { throw new \InvalidArgumentException('headers must be an array'); @@ -251,8 +325,7 @@ private function prepareDefaults($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 + * @param array $options See \GuzzleHttp\RequestOptions. * * @return Promise\PromiseInterface */ @@ -271,6 +344,7 @@ private function transfer(RequestInterface $request, array $options) } $request = $this->applyOptions($request, $options); + /** @var HandlerStack $handler */ $handler = $options['handler']; try { @@ -411,6 +485,11 @@ private function applyOptions(RequestInterface $request, array &$options) return $request; } + /** + * Throw Exception with pre-set message. + * @return void + * @throws \InvalidArgumentException Invalid body. + */ private function invalidBody() { throw new \InvalidArgumentException('Passing in the "body" request ' diff --git a/src/ClientInterface.php b/src/ClientInterface.php index 2dbcffa49..638b75dca 100644 --- a/src/ClientInterface.php +++ b/src/ClientInterface.php @@ -1,8 +1,8 @@ cookies as $cookie) { @@ -103,6 +103,8 @@ public function getCookieByName($name) return $cookie; } } + + return null; } public function toArray() @@ -120,7 +122,7 @@ public function clear($domain = null, $path = null, $name = null) } elseif (!$path) { $this->cookies = array_filter( $this->cookies, - function (SetCookie $cookie) use ($path, $domain) { + function (SetCookie $cookie) use ($domain) { return !$cookie->matchesDomain($domain); } ); @@ -238,6 +240,11 @@ public function extractCookies( if (0 !== strpos($sc->getPath(), '/')) { $sc->setPath($this->getCookiePathFromRequest($request)); } + if (!$sc->matchesDomain($request->getUri()->getHost())) { + continue; + } + // Note: At this point `$sc->getDomain()` being a public suffix should + // be rejected, but we don't want to pull in the full PSL dependency. $this->setCookie($sc); } } diff --git a/src/Cookie/CookieJarInterface.php b/src/Cookie/CookieJarInterface.php index 2cf298a86..6ee11885e 100644 --- a/src/Cookie/CookieJarInterface.php +++ b/src/Cookie/CookieJarInterface.php @@ -58,9 +58,9 @@ public function setCookie(SetCookie $cookie); * 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 + * @param string|null $domain Clears cookies matching a domain + * @param string|null $path Clears cookies matching a domain and path + * @param string|null $name Clears cookies matching a domain, path, and name * * @return CookieJarInterface */ diff --git a/src/Cookie/FileCookieJar.php b/src/Cookie/FileCookieJar.php index 9887c1d54..3fb8600ef 100644 --- a/src/Cookie/FileCookieJar.php +++ b/src/Cookie/FileCookieJar.php @@ -23,6 +23,7 @@ class FileCookieJar extends CookieJar */ public function __construct($cookieFile, $storeSessionCookies = false) { + parent::__construct(); $this->filename = $cookieFile; $this->storeSessionCookies = $storeSessionCookies; @@ -56,7 +57,7 @@ public function save($filename) } $jsonStr = \GuzzleHttp\json_encode($json); - if (false === file_put_contents($filename, $jsonStr)) { + if (false === file_put_contents($filename, $jsonStr, LOCK_EX)) { throw new \RuntimeException("Unable to save file {$filename}"); } } diff --git a/src/Cookie/SessionCookieJar.php b/src/Cookie/SessionCookieJar.php index 4497bcf03..0224a2447 100644 --- a/src/Cookie/SessionCookieJar.php +++ b/src/Cookie/SessionCookieJar.php @@ -22,6 +22,7 @@ class SessionCookieJar extends CookieJar */ public function __construct($sessionKey, $storeSessionCookies = false) { + parent::__construct(); $this->sessionKey = $sessionKey; $this->storeSessionCookies = $storeSessionCookies; $this->load(); diff --git a/src/Cookie/SetCookie.php b/src/Cookie/SetCookie.php index f6993943e..55f6901a7 100644 --- a/src/Cookie/SetCookie.php +++ b/src/Cookie/SetCookie.php @@ -227,7 +227,7 @@ public function setExpires($timestamp) /** * Get whether or not this is a secure cookie * - * @return null|bool + * @return bool|null */ public function getSecure() { @@ -247,7 +247,7 @@ public function setSecure($secure) /** * Get whether or not this is a session cookie * - * @return null|bool + * @return bool|null */ public function getDiscard() { @@ -333,12 +333,19 @@ public function matchesPath($requestPath) */ public function matchesDomain($domain) { + $cookieDomain = $this->getDomain(); + if (null === $cookieDomain) { + return true; + } + // Remove the leading '.' as per spec in RFC 6265. // http://tools.ietf.org/html/rfc6265#section-5.2.3 - $cookieDomain = ltrim($this->getDomain(), '.'); + $cookieDomain = ltrim(strtolower($cookieDomain), '.'); + + $domain = strtolower($domain); // Domain not set or exact match. - if (!$cookieDomain || !strcasecmp($domain, $cookieDomain)) { + if ('' === $cookieDomain || $domain === $cookieDomain) { return true; } diff --git a/src/Exception/ClientException.php b/src/Exception/ClientException.php index f95c09f2b..4cfd393cc 100644 --- a/src/Exception/ClientException.php +++ b/src/Exception/ClientException.php @@ -4,4 +4,6 @@ /** * Exception when a client error is encountered (4xx codes) */ -class ClientException extends BadResponseException {} +class ClientException extends BadResponseException +{ +} diff --git a/src/Exception/GuzzleException.php b/src/Exception/GuzzleException.php index 510778f6e..27b2722b0 100644 --- a/src/Exception/GuzzleException.php +++ b/src/Exception/GuzzleException.php @@ -1,13 +1,23 @@ 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; + return \GuzzleHttp\Psr7\get_message_body_summary($response); } /** - * Obfuscates URI if there is an username and a password present + * Obfuscates URI if there is a username and a password present * * @param UriInterface $uri * * @return UriInterface */ - private static function obfuscateUri($uri) + private static function obfuscateUri(UriInterface $uri) { $userInfo = $uri->getUserInfo(); diff --git a/src/Exception/ServerException.php b/src/Exception/ServerException.php index 7cdd34086..127094c14 100644 --- a/src/Exception/ServerException.php +++ b/src/Exception/ServerException.php @@ -4,4 +4,6 @@ /** * Exception when a server error is encountered (5xx codes) */ -class ServerException extends BadResponseException {} +class ServerException extends BadResponseException +{ +} diff --git a/src/Exception/TooManyRedirectsException.php b/src/Exception/TooManyRedirectsException.php index b60a9678d..fff05251d 100644 --- a/src/Exception/TooManyRedirectsException.php +++ b/src/Exception/TooManyRedirectsException.php @@ -1,4 +1,6 @@ handle); + $curlStats['appconnect_time'] = curl_getinfo($easy->handle, CURLINFO_APPCONNECT_TIME); $stats = new TransferStats( $easy->request, $easy->response, @@ -136,7 +140,9 @@ private static function finishError( $ctx = [ 'errno' => $easy->errno, 'error' => curl_error($easy->handle), + 'appconnect_time' => curl_getinfo($easy->handle, CURLINFO_APPCONNECT_TIME), ] + curl_getinfo($easy->handle); + $ctx[self::CURL_VERSION_STR] = curl_version()['version']; $factory->release($easy); // Retry when nothing is present or when curl failed to rewind. @@ -172,13 +178,22 @@ private static function createRejection(EasyHandle $easy, array $ctx) ) ); } - - $message = sprintf( - 'cURL error %s: %s (%s)', - $ctx['errno'], - $ctx['error'], - 'see http://curl.haxx.se/libcurl/c/libcurl-errors.html' - ); + if (version_compare($ctx[self::CURL_VERSION_STR], self::LOW_CURL_VERSION_NUMBER)) { + $message = sprintf( + 'cURL error %s: %s (%s)', + $ctx['errno'], + $ctx['error'], + 'see https://curl.haxx.se/libcurl/c/libcurl-errors.html' + ); + } else { + $message = sprintf( + 'cURL error %s: %s (%s) for %s', + $ctx['errno'], + $ctx['error'], + 'see https://curl.haxx.se/libcurl/c/libcurl-errors.html', + $easy->request->getUri() + ); + } // Create a connection exception if it was a specific error code. $error = isset($connectionErrors[$easy->errno]) @@ -439,11 +454,16 @@ private function applyHandlerOptions(EasyHandle $easy, array &$conf) } if (isset($options['ssl_key'])) { - $sslKey = $options['ssl_key']; - if (is_array($sslKey)) { - $conf[CURLOPT_SSLKEYPASSWD] = $sslKey[1]; - $sslKey = $sslKey[0]; + if (is_array($options['ssl_key'])) { + if (count($options['ssl_key']) === 2) { + list($sslKey, $conf[CURLOPT_SSLKEYPASSWD]) = $options['ssl_key']; + } else { + list($sslKey) = $options['ssl_key']; + } } + + $sslKey = isset($sslKey) ? $sslKey: $options['ssl_key']; + if (!file_exists($sslKey)) { throw new \InvalidArgumentException( "SSL private key not found: {$sslKey}" diff --git a/src/Handler/CurlMultiHandler.php b/src/Handler/CurlMultiHandler.php index 2754d8e43..564c95f48 100644 --- a/src/Handler/CurlMultiHandler.php +++ b/src/Handler/CurlMultiHandler.php @@ -3,7 +3,7 @@ use GuzzleHttp\Promise as P; use GuzzleHttp\Promise\Promise; -use GuzzleHttp\Psr7; +use GuzzleHttp\Utils; use Psr\Http\Message\RequestInterface; /** @@ -23,6 +23,7 @@ class CurlMultiHandler private $active; private $handles = []; private $delays = []; + private $options = []; /** * This handler accepts the following options: @@ -30,6 +31,8 @@ class CurlMultiHandler * - 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. + * - options: An associative array of CURLMOPT_* options and + * corresponding values for curl_multi_setopt() * * @param array $options */ @@ -37,14 +40,31 @@ 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; + + if (isset($options['select_timeout'])) { + $this->selectTimeout = $options['select_timeout']; + } elseif ($selectTimeout = getenv('GUZZLE_CURL_SELECT_TIMEOUT')) { + $this->selectTimeout = $selectTimeout; + } else { + $this->selectTimeout = 1; + } + + $this->options = isset($options['options']) ? $options['options'] : []; } public function __get($name) { if ($name === '_mh') { - return $this->_mh = curl_multi_init(); + $this->_mh = curl_multi_init(); + + foreach ($this->options as $option => $value) { + // A warning is raised in case of a wrong option. + curl_multi_setopt($this->_mh, $option, $value); + } + + // Further calls to _mh will return the value directly, without entering the + // __get() method at all. + return $this->_mh; } throw new \BadMethodCallException(); @@ -82,7 +102,7 @@ public function tick() { // Add any delayed handles if needed. if ($this->delays) { - $currentTime = microtime(true); + $currentTime = Utils::currentTime(); foreach ($this->delays as $id => $delay) { if ($currentTime >= $delay) { unset($this->delays[$id]); @@ -134,7 +154,7 @@ private function addRequest(array $entry) if (empty($easy->options['delay'])) { curl_multi_add_handle($this->_mh, $easy->handle); } else { - $this->delays[$id] = microtime(true) + ($easy->options['delay'] / 1000); + $this->delays[$id] = Utils::currentTime() + ($easy->options['delay'] / 1000); } } @@ -186,7 +206,7 @@ private function processMessages() private function timeToNext() { - $currentTime = microtime(true); + $currentTime = Utils::currentTime(); $nextTime = PHP_INT_MAX; foreach ($this->delays as $time) { if ($time < $nextTime) { diff --git a/src/Handler/MockHandler.php b/src/Handler/MockHandler.php index d892061c7..5b312bc04 100644 --- a/src/Handler/MockHandler.php +++ b/src/Handler/MockHandler.php @@ -66,7 +66,7 @@ public function __invoke(RequestInterface $request, array $options) throw new \OutOfBoundsException('Mock queue is empty'); } - if (isset($options['delay'])) { + if (isset($options['delay']) && is_numeric($options['delay'])) { usleep($options['delay'] * 1000); } @@ -175,6 +175,11 @@ public function count() return count($this->queue); } + public function reset() + { + $this->queue = []; + } + private function invokeStats( RequestInterface $request, array $options, @@ -182,7 +187,8 @@ private function invokeStats( $reason = null ) { if (isset($options['on_stats'])) { - $stats = new TransferStats($request, $response, 0, $reason); + $transferTime = isset($options['transfer_time']) ? $options['transfer_time'] : 0; + $stats = new TransferStats($request, $response, $transferTime, $reason); call_user_func($options['on_stats'], $stats); } } diff --git a/src/Handler/StreamHandler.php b/src/Handler/StreamHandler.php index b686545ea..a15734a44 100644 --- a/src/Handler/StreamHandler.php +++ b/src/Handler/StreamHandler.php @@ -1,12 +1,13 @@ getBody()->getSize()) { - $request = $request->withHeader('Content-Length', 0); + $request = $request->withHeader('Content-Length', '0'); } return $this->createResponse( @@ -82,7 +83,7 @@ private function invokeStats( $stats = new TransferStats( $request, $response, - microtime(true) - $startTime, + Utils::currentTime() - $startTime, $error, [] ); @@ -343,13 +344,25 @@ private function resolveHost(RequestInterface $request, array $options) 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); + 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); + throw new ConnectException( + sprintf( + "Could not resolve IPv6 address for host '%s'", + $uri->getHost() + ), + $request + ); } $uri = $uri->withHost('[' . $records[0]['ipv6'] . ']'); } diff --git a/src/HandlerStack.php b/src/HandlerStack.php index 24c46fd9f..6a49cc069 100644 --- a/src/HandlerStack.php +++ b/src/HandlerStack.php @@ -1,7 +1,9 @@ extractCookies($request, $response); return $response; } - ); + ); }; }; } @@ -58,7 +57,7 @@ public static function httpErrors() return $handler($request, $options); } return $handler($request, $options)->then( - function (ResponseInterface $response) use ($request, $handler) { + function (ResponseInterface $response) use ($request) { $code = $response->getStatusCode(); if ($code < 400) { return $response; @@ -183,7 +182,7 @@ public static function retry(callable $decider, callable $delay = null) * * @return callable Returns a function that accepts the next handler. */ - public static function log(LoggerInterface $logger, MessageFormatter $formatter, $logLevel = LogLevel::INFO) + public static function log(LoggerInterface $logger, MessageFormatter $formatter, $logLevel = 'info' /* \Psr\Log\LogLevel::INFO */) { return function (callable $handler) use ($logger, $formatter, $logLevel) { return function ($request, array $options) use ($handler, $logger, $formatter, $logLevel) { diff --git a/src/Pool.php b/src/Pool.php index 8f1be33cd..5838db4f4 100644 --- a/src/Pool.php +++ b/src/Pool.php @@ -1,12 +1,13 @@ each = new EachPromise($requests(), $config); } + /** + * Get promise + * + * @return PromiseInterface + */ public function promise() { return $this->each->promise(); @@ -106,6 +112,11 @@ public static function batch( return $res; } + /** + * Execute callback(s) + * + * @return void + */ private static function cmpCallback(array &$options, $name, array &$results) { if (!isset($options[$name])) { diff --git a/src/PrepareBodyMiddleware.php b/src/PrepareBodyMiddleware.php index 2eb95f9b2..568a1e906 100644 --- a/src/PrepareBodyMiddleware.php +++ b/src/PrepareBodyMiddleware.php @@ -66,6 +66,11 @@ public function __invoke(RequestInterface $request, array $options) return $fn(Psr7\modify_request($request, $modify), $options); } + /** + * Add expect header + * + * @return void + */ private function addExpectHeader( RequestInterface $request, array $options, diff --git a/src/RedirectMiddleware.php b/src/RedirectMiddleware.php index 131b77179..008a29b8c 100644 --- a/src/RedirectMiddleware.php +++ b/src/RedirectMiddleware.php @@ -13,7 +13,7 @@ * Request redirect middleware. * * Apply this middleware like other middleware using - * {@see GuzzleHttp\Middleware::redirect()}. + * {@see \GuzzleHttp\Middleware::redirect()}. */ class RedirectMiddleware { @@ -76,7 +76,7 @@ public function __invoke(RequestInterface $request, array $options) /** * @param RequestInterface $request * @param array $options - * @param ResponseInterface|PromiseInterface $response + * @param ResponseInterface $response * * @return ResponseInterface|PromiseInterface */ @@ -94,6 +94,14 @@ public function checkRedirect( $this->guardMax($request, $options); $nextRequest = $this->modifyRequest($request, $options, $response); + // If authorization is handled by curl, unset it if URI is cross-origin. + if (Psr7\UriComparator::isCrossOrigin($request->getUri(), $nextRequest->getUri()) && defined('\CURLOPT_HTTPAUTH')) { + unset( + $options['curl'][\CURLOPT_HTTPAUTH], + $options['curl'][\CURLOPT_USERPWD] + ); + } + if (isset($options['allow_redirects']['on_redirect'])) { call_user_func( $options['allow_redirects']['on_redirect'], @@ -118,6 +126,11 @@ public function checkRedirect( return $promise; } + /** + * Enable tracking on promise. + * + * @return PromiseInterface + */ private function withTracking(PromiseInterface $promise, $uri, $statusCode) { return $promise->then( @@ -135,6 +148,13 @@ function (ResponseInterface $response) use ($uri, $statusCode) { ); } + /** + * Check for too many redirects. + * + * @return void + * + * @throws TooManyRedirectsException Too many redirects. + */ private function guardMax(RequestInterface $request, array &$options) { $current = isset($options['__redirect_count']) @@ -172,13 +192,19 @@ public function modifyRequest( // would do. $statusCode = $response->getStatusCode(); if ($statusCode == 303 || - ($statusCode <= 302 && $request->getBody() && !$options['allow_redirects']['strict']) + ($statusCode <= 302 && !$options['allow_redirects']['strict']) ) { $modify['method'] = 'GET'; $modify['body'] = ''; } - $modify['uri'] = $this->redirectUri($request, $response, $protocols); + $uri = self::redirectUri($request, $response, $protocols); + if (isset($options['idn_conversion']) && ($options['idn_conversion'] !== false)) { + $idnOptions = ($options['idn_conversion'] === true) ? IDNA_DEFAULT : $options['idn_conversion']; + $uri = Utils::idnUriConvert($uri, $idnOptions); + } + + $modify['uri'] = $uri; Psr7\rewind_body($request); // Add the Referer header if it is told to do so and only @@ -186,22 +212,23 @@ public function modifyRequest( if ($options['allow_redirects']['referer'] && $modify['uri']->getScheme() === $request->getUri()->getScheme() ) { - $uri = $request->getUri()->withUserInfo('', ''); + $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()) { + // Remove Authorization and Cookie headers if URI is cross-origin. + if (Psr7\UriComparator::isCrossOrigin($request->getUri(), $modify['uri'])) { $modify['remove_headers'][] = 'Authorization'; + $modify['remove_headers'][] = 'Cookie'; } return Psr7\modify_request($request, $modify); } /** - * Set the appropriate URL on the request based on the location header + * Set the appropriate URL on the request based on the location header. * * @param RequestInterface $request * @param ResponseInterface $response @@ -209,7 +236,7 @@ public function modifyRequest( * * @return UriInterface */ - private function redirectUri( + private static function redirectUri( RequestInterface $request, ResponseInterface $response, array $protocols diff --git a/src/RequestOptions.php b/src/RequestOptions.php index c6aacfb15..355f658f0 100644 --- a/src/RequestOptions.php +++ b/src/RequestOptions.php @@ -22,7 +22,7 @@ final class RequestOptions * - 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 + * - referer: (bool, default=false) Set to true to enable the Referer * header. * - protocols: (array, default=['http', 'https']) Allowed redirect * protocols. @@ -132,6 +132,14 @@ final class RequestOptions */ const HTTP_ERRORS = 'http_errors'; + /** + * idn: (bool|int, default=true) A combination of IDNA_* constants for + * idn_to_ascii() PHP's function (see "options" parameter). Set to false to + * disable IDN support completely, or to true to use the default + * configuration (IDNA_DEFAULT constant). + */ + const IDN_CONVERSION = 'idn_conversion'; + /** * 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 diff --git a/src/RetryMiddleware.php b/src/RetryMiddleware.php index f27090fd1..5acc8c5c3 100644 --- a/src/RetryMiddleware.php +++ b/src/RetryMiddleware.php @@ -19,6 +19,9 @@ class RetryMiddleware /** @var callable */ private $decider; + /** @var callable */ + private $delay; + /** * @param callable $decider Function that accepts the number of retries, * a request, [response], and [exception] and @@ -42,13 +45,13 @@ public function __construct( /** * Default exponential backoff delay function. * - * @param $retries + * @param int $retries * - * @return int + * @return int milliseconds. */ public static function exponentialDelay($retries) { - return (int) pow(2, $retries - 1); + return (int) pow(2, $retries - 1) * 1000; } /** @@ -71,6 +74,11 @@ public function __invoke(RequestInterface $request, array $options) ); } + /** + * Execute fulfilled closure + * + * @return mixed + */ private function onFulfilled(RequestInterface $req, array $options) { return function ($value) use ($req, $options) { @@ -87,6 +95,11 @@ private function onFulfilled(RequestInterface $req, array $options) }; } + /** + * Execute rejected closure + * + * @return callable + */ private function onRejected(RequestInterface $req, array $options) { return function ($reason) use ($req, $options) { @@ -103,6 +116,9 @@ private function onRejected(RequestInterface $req, array $options) }; } + /** + * @return self + */ private function doRetry(RequestInterface $request, array $options, ResponseInterface $response = null) { $options['delay'] = call_user_func($this->delay, ++$options['retries'], $response); diff --git a/src/TransferStats.php b/src/TransferStats.php index 15f717e1e..87fb3c001 100644 --- a/src/TransferStats.php +++ b/src/TransferStats.php @@ -18,11 +18,11 @@ final class TransferStats 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. + * @param RequestInterface $request Request that was sent. + * @param ResponseInterface|null $response Response received (if any) + * @param float|null $transferTime Total handler transfer time. + * @param mixed $handlerErrorData Handler error data. + * @param array $handlerStats Handler specific stats. */ public function __construct( RequestInterface $request, @@ -93,7 +93,7 @@ public function getEffectiveUri() /** * Get the estimated time the request was being transferred by the handler. * - * @return float Time in seconds. + * @return float|null Time in seconds. */ public function getTransferTime() { diff --git a/src/Utils.php b/src/Utils.php new file mode 100644 index 000000000..c698acbf0 --- /dev/null +++ b/src/Utils.php @@ -0,0 +1,92 @@ +getHost()) { + $asciiHost = self::idnToAsci($uri->getHost(), $options, $info); + if ($asciiHost === false) { + $errorBitSet = isset($info['errors']) ? $info['errors'] : 0; + + $errorConstants = array_filter(array_keys(get_defined_constants()), function ($name) { + return substr($name, 0, 11) === 'IDNA_ERROR_'; + }); + + $errors = []; + foreach ($errorConstants as $errorConstant) { + if ($errorBitSet & constant($errorConstant)) { + $errors[] = $errorConstant; + } + } + + $errorMessage = 'IDN conversion failed'; + if ($errors) { + $errorMessage .= ' (errors: ' . implode(', ', $errors) . ')'; + } + + throw new InvalidArgumentException($errorMessage); + } else { + if ($uri->getHost() !== $asciiHost) { + // Replace URI only if the ASCII version is different + $uri = $uri->withHost($asciiHost); + } + } + } + + return $uri; + } + + /** + * @param string $domain + * @param int $options + * @param array $info + * + * @return string|false + */ + private static function idnToAsci($domain, $options, &$info = []) + { + if (\preg_match('%^[ -~]+$%', $domain) === 1) { + return $domain; + } + + if (\extension_loaded('intl') && defined('INTL_IDNA_VARIANT_UTS46')) { + return \idn_to_ascii($domain, $options, INTL_IDNA_VARIANT_UTS46, $info); + } + + /* + * The Idn class is marked as @internal. Verify that class and method exists. + */ + if (method_exists(Idn::class, 'idn_to_ascii')) { + return Idn::idn_to_ascii($domain, $options, Idn::INTL_IDNA_VARIANT_UTS46, $info); + } + + throw new \RuntimeException('ext-intl or symfony/polyfill-intl-idn not loaded or too old'); + } +} diff --git a/src/functions.php b/src/functions.php index a3ac450db..c2afd8c7b 100644 --- a/src/functions.php +++ b/src/functions.php @@ -56,7 +56,7 @@ function describe_type($input) /** * Parses an array of header lines into an associative array of headers. * - * @param array $lines Header lines array of strings in the following + * @param iterable $lines Header lines array of strings in the following * format: "Name: Value" * @return array */ @@ -97,8 +97,8 @@ function debug_resource($value = null) * * 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. + * @throws \RuntimeException if no viable Handler is available. */ function choose_handler() { @@ -196,7 +196,8 @@ function default_ca_bundle() } } - throw new \RuntimeException(<<< EOT + 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 @@ -294,14 +295,14 @@ function is_host_in_noproxy($host, array $noProxyArray) * @param int $options Bitmask of JSON decode options. * * @return mixed - * @throws \InvalidArgumentException if the JSON cannot be decoded. + * @throws Exception\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( + throw new Exception\InvalidArgumentException( 'json_decode error: ' . json_last_error_msg() ); } @@ -317,14 +318,14 @@ function json_decode($json, $assoc = false, $depth = 512, $options = 0) * @param int $depth Set the maximum depth. Must be greater than zero. * * @return string - * @throws \InvalidArgumentException if the JSON cannot be encoded. + * @throws Exception\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( + throw new Exception\InvalidArgumentException( 'json_encode error: ' . json_last_error_msg() ); } diff --git a/tests/ClientTest.php b/tests/ClientTest.php index f14d8eb4e..65f09bd1e 100644 --- a/tests/ClientTest.php +++ b/tests/ClientTest.php @@ -5,13 +5,15 @@ use GuzzleHttp\Cookie\CookieJar; use GuzzleHttp\Handler\MockHandler; use GuzzleHttp\HandlerStack; +use GuzzleHttp\Middleware; use GuzzleHttp\Promise\PromiseInterface; use GuzzleHttp\Psr7; use GuzzleHttp\Psr7\Request; use GuzzleHttp\Psr7\Response; use GuzzleHttp\Psr7\Uri; -use Psr\Http\Message\ResponseInterface; +use GuzzleHttp\RequestOptions; use PHPUnit\Framework\TestCase; +use Psr\Http\Message\ResponseInterface; class ClientTest extends TestCase { @@ -20,7 +22,7 @@ public function testUsesDefaultHandler() $client = new Client(); Server::enqueue([new Response(200, ['Content-Length' => 0])]); $response = $client->get(Server::$url); - $this->assertEquals(200, $response->getStatusCode()); + self::assertSame(200, $response->getStatusCode()); } /** @@ -39,11 +41,11 @@ public function testCanSendMagicAsyncRequests() Server::flush(); Server::enqueue([new Response(200, ['Content-Length' => 2], 'hi')]); $p = $client->getAsync(Server::$url, ['query' => ['test' => 'foo']]); - $this->assertInstanceOf(PromiseInterface::class, $p); - $this->assertEquals(200, $p->wait()->getStatusCode()); + self::assertInstanceOf(PromiseInterface::class, $p); + self::assertSame(200, $p->wait()->getStatusCode()); $received = Server::received(true); - $this->assertCount(1, $received); - $this->assertEquals('test=foo', $received[0]->getUri()->getQuery()); + self::assertCount(1, $received); + self::assertSame('test=foo', $received[0]->getUri()->getQuery()); } public function testCanSendSynchronously() @@ -51,8 +53,8 @@ public function testCanSendSynchronously() $client = new Client(['handler' => new MockHandler([new Response()])]); $request = new Request('GET', 'http://example.com'); $r = $client->send($request); - $this->assertInstanceOf(ResponseInterface::class, $r); - $this->assertEquals(200, $r->getStatusCode()); + self::assertInstanceOf(ResponseInterface::class, $r); + self::assertSame(200, $r->getStatusCode()); } public function testClientHasOptions() @@ -64,12 +66,12 @@ public function testClientHasOptions() 'handler' => new MockHandler() ]); $base = $client->getConfig('base_uri'); - $this->assertEquals('http://foo.com', (string) $base); - $this->assertInstanceOf(Uri::class, $base); - $this->assertNotNull($client->getConfig('handler')); - $this->assertEquals(2, $client->getConfig('timeout')); - $this->assertArrayHasKey('timeout', $client->getConfig()); - $this->assertArrayHasKey('headers', $client->getConfig()); + self::assertSame('http://foo.com', (string) $base); + self::assertInstanceOf(Uri::class, $base); + self::assertNotNull($client->getConfig('handler')); + self::assertSame(2, $client->getConfig('timeout')); + self::assertArrayHasKey('timeout', $client->getConfig()); + self::assertArrayHasKey('headers', $client->getConfig()); } public function testCanMergeOnBaseUri() @@ -80,9 +82,9 @@ public function testCanMergeOnBaseUri() 'handler' => $mock ]); $client->get('baz'); - $this->assertEquals( + self::assertSame( 'http://foo.com/bar/baz', - $mock->getLastRequest()->getUri() + (string)$mock->getLastRequest()->getUri() ); } @@ -94,13 +96,13 @@ public function testCanMergeOnBaseUriWithRequest() 'base_uri' => 'http://foo.com/bar/' ]); $client->request('GET', new Uri('baz')); - $this->assertEquals( + self::assertSame( 'http://foo.com/bar/baz', (string) $mock->getLastRequest()->getUri() ); $client->request('GET', new Uri('baz'), ['base_uri' => 'http://example.com/foo/']); - $this->assertEquals( + self::assertSame( 'http://example.com/foo/baz', (string) $mock->getLastRequest()->getUri(), 'Can overwrite the base_uri through the request options' @@ -114,10 +116,10 @@ public function testCanUseRelativeUriWithSend() 'handler' => $mock, 'base_uri' => 'http://bar.com' ]); - $this->assertEquals('http://bar.com', (string) $client->getConfig('base_uri')); + self::assertSame('http://bar.com', (string) $client->getConfig('base_uri')); $request = new Request('GET', '/baz'); $client->send($request); - $this->assertEquals( + self::assertSame( 'http://bar.com/baz', (string) $mock->getLastRequest()->getUri() ); @@ -126,11 +128,11 @@ public function testCanUseRelativeUriWithSend() public function testMergesDefaultOptionsAndDoesNotOverwriteUa() { $c = new Client(['headers' => ['User-agent' => 'foo']]); - $this->assertEquals(['User-agent' => 'foo'], $c->getConfig('headers')); - $this->assertInternalType('array', $c->getConfig('allow_redirects')); - $this->assertTrue($c->getConfig('http_errors')); - $this->assertTrue($c->getConfig('decode_content')); - $this->assertTrue($c->getConfig('verify')); + self::assertSame(['User-agent' => 'foo'], $c->getConfig('headers')); + self::assertInternalType('array', $c->getConfig('allow_redirects')); + self::assertTrue($c->getConfig('http_errors')); + self::assertTrue($c->getConfig('decode_content')); + self::assertTrue($c->getConfig('verify')); } public function testDoesNotOverwriteHeaderWithDefault() @@ -141,7 +143,7 @@ public function testDoesNotOverwriteHeaderWithDefault() 'handler' => $mock ]); $c->get('http://example.com', ['headers' => ['User-Agent' => 'bar']]); - $this->assertEquals('bar', $mock->getLastRequest()->getHeaderLine('User-Agent')); + self::assertSame('bar', $mock->getLastRequest()->getHeaderLine('User-Agent')); } public function testDoesNotOverwriteHeaderWithDefaultInRequest() @@ -153,7 +155,7 @@ public function testDoesNotOverwriteHeaderWithDefaultInRequest() ]); $request = new Request('GET', Server::$url, ['User-Agent' => 'bar']); $c->send($request); - $this->assertEquals('bar', $mock->getLastRequest()->getHeaderLine('User-Agent')); + self::assertSame('bar', $mock->getLastRequest()->getHeaderLine('User-Agent')); } public function testDoesOverwriteHeaderWithSetRequestOption() @@ -165,7 +167,7 @@ public function testDoesOverwriteHeaderWithSetRequestOption() ]); $request = new Request('GET', Server::$url, ['User-Agent' => 'bar']); $c->send($request, ['headers' => ['User-Agent' => 'YO']]); - $this->assertEquals('YO', $mock->getLastRequest()->getHeaderLine('User-Agent')); + self::assertSame('YO', $mock->getLastRequest()->getHeaderLine('User-Agent')); } public function testCanUnsetRequestOptionWithNull() @@ -176,14 +178,14 @@ public function testCanUnsetRequestOptionWithNull() 'handler' => $mock ]); $c->get('http://example.com', ['headers' => null]); - $this->assertFalse($mock->getLastRequest()->hasHeader('foo')); + self::assertFalse($mock->getLastRequest()->hasHeader('foo')); } public function testRewriteExceptionsToHttpErrors() { $client = new Client(['handler' => new MockHandler([new Response(404)])]); $res = $client->get('http://foo.com', ['exceptions' => false]); - $this->assertEquals(404, $res->getStatusCode()); + self::assertSame(404, $res->getStatusCode()); } public function testRewriteSaveToToSink() @@ -192,7 +194,7 @@ public function testRewriteSaveToToSink() $mock = new MockHandler([new Response(200, [], 'foo')]); $client = new Client(['handler' => $mock]); $client->get('http://foo.com', ['save_to' => $r]); - $this->assertSame($r, $mock->getLastOptions()['sink']); + self::assertSame($r, $mock->getLastOptions()['sink']); } public function testAllowRedirectsCanBeTrue() @@ -201,7 +203,7 @@ public function testAllowRedirectsCanBeTrue() $handler = HandlerStack::create($mock); $client = new Client(['handler' => $handler]); $client->get('http://foo.com', ['allow_redirects' => true]); - $this->assertInternalType('array', $mock->getLastOptions()['allow_redirects']); + self::assertInternalType('array', $mock->getLastOptions()['allow_redirects']); } /** @@ -249,7 +251,7 @@ public function testSetCookieToTrueUsesSharedJar() $client = new Client(['handler' => $handler, 'cookies' => true]); $client->get('http://foo.com'); $client->get('http://foo.com'); - $this->assertEquals('foo=bar', $mock->getLastRequest()->getHeaderLine('Cookie')); + self::assertSame('foo=bar', $mock->getLastRequest()->getHeaderLine('Cookie')); } public function testSetCookieToJar() @@ -263,7 +265,7 @@ public function testSetCookieToJar() $jar = new CookieJar(); $client->get('http://foo.com', ['cookies' => $jar]); $client->get('http://foo.com', ['cookies' => $jar]); - $this->assertEquals('foo=bar', $mock->getLastRequest()->getHeaderLine('Cookie')); + self::assertSame('foo=bar', $mock->getLastRequest()->getHeaderLine('Cookie')); } public function testCanDisableContentDecoding() @@ -272,8 +274,8 @@ public function testCanDisableContentDecoding() $client = new Client(['handler' => $mock]); $client->get('http://foo.com', ['decode_content' => false]); $last = $mock->getLastRequest(); - $this->assertFalse($last->hasHeader('Accept-Encoding')); - $this->assertFalse($mock->getLastOptions()['decode_content']); + self::assertFalse($last->hasHeader('Accept-Encoding')); + self::assertFalse($mock->getLastOptions()['decode_content']); } public function testCanSetContentDecodingToValue() @@ -282,8 +284,8 @@ public function testCanSetContentDecodingToValue() $client = new Client(['handler' => $mock]); $client->get('http://foo.com', ['decode_content' => 'gzip']); $last = $mock->getLastRequest(); - $this->assertEquals('gzip', $last->getHeaderLine('Accept-Encoding')); - $this->assertEquals('gzip', $mock->getLastOptions()['decode_content']); + self::assertSame('gzip', $last->getHeaderLine('Accept-Encoding')); + self::assertSame('gzip', $mock->getLastOptions()['decode_content']); } /** @@ -303,7 +305,7 @@ public function testAddsBody() $request = new Request('PUT', 'http://foo.com'); $client->send($request, ['body' => 'foo']); $last = $mock->getLastRequest(); - $this->assertEquals('foo', (string) $last->getBody()); + self::assertSame('foo', (string) $last->getBody()); } /** @@ -323,7 +325,7 @@ public function testQueryCanBeString() $client = new Client(['handler' => $mock]); $request = new Request('PUT', 'http://foo.com'); $client->send($request, ['query' => 'foo']); - $this->assertEquals('foo', $mock->getLastRequest()->getUri()->getQuery()); + self::assertSame('foo', $mock->getLastRequest()->getUri()->getQuery()); } public function testQueryCanBeArray() @@ -332,7 +334,7 @@ public function testQueryCanBeArray() $client = new Client(['handler' => $mock]); $request = new Request('PUT', 'http://foo.com'); $client->send($request, ['query' => ['foo' => 'bar baz']]); - $this->assertEquals('foo=bar%20baz', $mock->getLastRequest()->getUri()->getQuery()); + self::assertSame('foo=bar%20baz', $mock->getLastRequest()->getUri()->getQuery()); } public function testCanAddJsonData() @@ -342,8 +344,8 @@ public function testCanAddJsonData() $request = new Request('PUT', 'http://foo.com'); $client->send($request, ['json' => ['foo' => 'bar']]); $last = $mock->getLastRequest(); - $this->assertEquals('{"foo":"bar"}', (string) $mock->getLastRequest()->getBody()); - $this->assertEquals('application/json', $last->getHeaderLine('Content-Type')); + self::assertSame('{"foo":"bar"}', (string) $mock->getLastRequest()->getBody()); + self::assertSame('application/json', $last->getHeaderLine('Content-Type')); } public function testCanAddJsonDataWithoutOverwritingContentType() @@ -356,8 +358,22 @@ public function testCanAddJsonDataWithoutOverwritingContentType() 'json' => 'a' ]); $last = $mock->getLastRequest(); - $this->assertEquals('"a"', (string) $mock->getLastRequest()->getBody()); - $this->assertEquals('foo', $last->getHeaderLine('Content-Type')); + self::assertSame('"a"', (string) $mock->getLastRequest()->getBody()); + self::assertSame('foo', $last->getHeaderLine('Content-Type')); + } + + public function testCanAddJsonDataWithNullHeader() + { + $mock = new MockHandler([new Response()]); + $client = new Client(['handler' => $mock]); + $request = new Request('PUT', 'http://foo.com'); + $client->send($request, [ + 'headers' => null, + 'json' => 'a' + ]); + $last = $mock->getLastRequest(); + self::assertSame('"a"', (string) $mock->getLastRequest()->getBody()); + self::assertSame('application/json', $last->getHeaderLine('Content-Type')); } public function testAuthCanBeTrue() @@ -366,7 +382,7 @@ public function testAuthCanBeTrue() $client = new Client(['handler' => $mock]); $client->get('http://foo.com', ['auth' => false]); $last = $mock->getLastRequest(); - $this->assertFalse($last->hasHeader('Authorization')); + self::assertFalse($last->hasHeader('Authorization')); } public function testAuthCanBeArrayForBasicAuth() @@ -375,7 +391,7 @@ public function testAuthCanBeArrayForBasicAuth() $client = new Client(['handler' => $mock]); $client->get('http://foo.com', ['auth' => ['a', 'b']]); $last = $mock->getLastRequest(); - $this->assertEquals('Basic YTpi', $last->getHeaderLine('Authorization')); + self::assertSame('Basic YTpi', $last->getHeaderLine('Authorization')); } public function testAuthCanBeArrayForDigestAuth() @@ -384,7 +400,7 @@ public function testAuthCanBeArrayForDigestAuth() $client = new Client(['handler' => $mock]); $client->get('http://foo.com', ['auth' => ['a', 'b', 'digest']]); $last = $mock->getLastOptions(); - $this->assertEquals([ + self::assertSame([ CURLOPT_HTTPAUTH => 2, CURLOPT_USERPWD => 'a:b' ], $last['curl']); @@ -396,7 +412,7 @@ public function testAuthCanBeArrayForNtlmAuth() $client = new Client(['handler' => $mock]); $client->get('http://foo.com', ['auth' => ['a', 'b', 'ntlm']]); $last = $mock->getLastOptions(); - $this->assertEquals([ + self::assertSame([ CURLOPT_HTTPAUTH => 8, CURLOPT_USERPWD => 'a:b' ], $last['curl']); @@ -408,7 +424,7 @@ public function testAuthCanBeCustomType() $client = new Client(['handler' => $mock]); $client->get('http://foo.com', ['auth' => 'foo']); $last = $mock->getLastOptions(); - $this->assertEquals('foo', $last['auth']); + self::assertSame('foo', $last['auth']); } public function testCanAddFormParams() @@ -422,11 +438,11 @@ public function testCanAddFormParams() ] ]); $last = $mock->getLastRequest(); - $this->assertEquals( + self::assertSame( 'application/x-www-form-urlencoded', $last->getHeaderLine('Content-Type') ); - $this->assertEquals( + self::assertSame( 'foo=bar+bam&baz%5Bboo%5D=qux', (string) $last->getBody() ); @@ -445,7 +461,7 @@ public function testFormParamsEncodedProperly() ] ]); $last = $mock->getLastRequest(); - $this->assertEquals( + self::assertSame( 'foo=bar+bam&baz%5Bboo%5D=qux', (string) $last->getBody() ); @@ -458,7 +474,8 @@ public function testFormParamsEncodedProperly() */ public function testEnsuresThatFormParamsAndMultipartAreExclusive() { - $client = new Client(['handler' => function () {}]); + $client = new Client(['handler' => function () { + }]); $client->post('http://foo.com', [ 'form_params' => ['foo' => 'bar bam'], 'multipart' => [] @@ -483,22 +500,22 @@ public function testCanSendMultipart() ]); $last = $mock->getLastRequest(); - $this->assertContains( + self::assertContains( 'multipart/form-data; boundary=', $last->getHeaderLine('Content-Type') ); - $this->assertContains( + self::assertContains( 'Content-Disposition: form-data; name="foo"', (string) $last->getBody() ); - $this->assertContains('bar', (string) $last->getBody()); - $this->assertContains( + self::assertContains('bar', (string) $last->getBody()); + self::assertContains( 'Content-Disposition: form-data; name="foo"' . "\r\n", (string) $last->getBody() ); - $this->assertContains( + self::assertContains( 'Content-Disposition: form-data; name="test"; filename="ClientTest.php"', (string) $last->getBody() ); @@ -529,22 +546,22 @@ public function testCanSendMultipartWithExplicitBody() ); $last = $mock->getLastRequest(); - $this->assertContains( + self::assertContains( 'multipart/form-data; boundary=', $last->getHeaderLine('Content-Type') ); - $this->assertContains( + self::assertContains( 'Content-Disposition: form-data; name="foo"', (string) $last->getBody() ); - $this->assertContains('bar', (string) $last->getBody()); - $this->assertContains( + self::assertContains('bar', (string) $last->getBody()); + self::assertContains( 'Content-Disposition: form-data; name="foo"' . "\r\n", (string) $last->getBody() ); - $this->assertContains( + self::assertContains( 'Content-Disposition: form-data; name="test"; filename="ClientTest.php"', (string) $last->getBody() ); @@ -556,17 +573,17 @@ public function testUsesProxyEnvironmentVariables() $https = getenv('HTTPS_PROXY'); $no = getenv('NO_PROXY'); $client = new Client(); - $this->assertNull($client->getConfig('proxy')); + self::assertNull($client->getConfig('proxy')); putenv('HTTP_PROXY=127.0.0.1'); $client = new Client(); - $this->assertEquals( + self::assertSame( ['http' => '127.0.0.1'], $client->getConfig('proxy') ); putenv('HTTPS_PROXY=127.0.0.2'); putenv('NO_PROXY=127.0.0.3, 127.0.0.4'); $client = new Client(); - $this->assertEquals( + self::assertSame( ['http' => '127.0.0.1', 'https' => '127.0.0.2', 'no' => ['127.0.0.3','127.0.0.4']], $client->getConfig('proxy') ); @@ -580,7 +597,7 @@ public function testRequestSendsWithSync() $mock = new MockHandler([new Response()]); $client = new Client(['handler' => $mock]); $client->request('GET', 'http://foo.com'); - $this->assertTrue($mock->getLastOptions()['synchronous']); + self::assertTrue($mock->getLastOptions()['synchronous']); } public function testSendSendsWithSync() @@ -588,7 +605,7 @@ public function testSendSendsWithSync() $mock = new MockHandler([new Response()]); $client = new Client(['handler' => $mock]); $client->send(new Request('GET', 'http://foo.com')); - $this->assertTrue($mock->getLastOptions()['synchronous']); + self::assertTrue($mock->getLastOptions()['synchronous']); } public function testCanSetCustomHandler() @@ -596,7 +613,7 @@ public function testCanSetCustomHandler() $mock = new MockHandler([new Response(500)]); $client = new Client(['handler' => $mock]); $mock2 = new MockHandler([new Response(200)]); - $this->assertEquals( + self::assertSame( 200, $client->send(new Request('GET', 'http://foo.com'), [ 'handler' => $mock2 @@ -610,7 +627,7 @@ public function testProperlyBuildsQuery() $client = new Client(['handler' => $mock]); $request = new Request('PUT', 'http://foo.com'); $client->send($request, ['query' => ['foo' => 'bar', 'john' => 'doe']]); - $this->assertEquals('foo=bar&john=doe', $mock->getLastRequest()->getUri()->getQuery()); + self::assertSame('foo=bar&john=doe', $mock->getLastRequest()->getUri()->getQuery()); } public function testSendSendsWithIpAddressAndPortAndHostHeaderInRequestTheHostShouldBePreserved() @@ -621,7 +638,7 @@ public function testSendSendsWithIpAddressAndPortAndHostHeaderInRequestTheHostSh $client->send($request); - $this->assertEquals('foo.com', $mockHandler->getLastRequest()->getHeader('Host')[0]); + self::assertSame('foo.com', $mockHandler->getLastRequest()->getHeader('Host')[0]); } public function testSendSendsWithDomainAndHostHeaderInRequestTheHostShouldBePreserved() @@ -632,7 +649,7 @@ public function testSendSendsWithDomainAndHostHeaderInRequestTheHostShouldBePres $client->send($request); - $this->assertEquals('foo.com', $mockHandler->getLastRequest()->getHeader('Host')[0]); + self::assertSame('foo.com', $mockHandler->getLastRequest()->getHeader('Host')[0]); } /** @@ -652,7 +669,7 @@ public function testHttpDefaultSchemeIfUriHasNone() $client->request('GET', '//example.org/test'); - $this->assertSame('http://example.org/test', (string) $mockHandler->getLastRequest()->getUri()); + self::assertSame('http://example.org/test', (string) $mockHandler->getLastRequest()->getUri()); } public function testOnlyAddSchemeWhenHostIsPresent() @@ -661,7 +678,7 @@ public function testOnlyAddSchemeWhenHostIsPresent() $client = new Client(['handler' => $mockHandler]); $client->request('GET', 'baz'); - $this->assertSame( + self::assertSame( 'baz', (string) $mockHandler->getLastRequest()->getUri() ); @@ -674,4 +691,121 @@ public function testHandlerIsCallable() { new Client(['handler' => 'not_cllable']); } + + public function testResponseBodyAsString() + { + $responseBody = '{ "package": "guzzle" }'; + $mock = new MockHandler([new Response(200, ['Content-Type' => 'application/json'], $responseBody)]); + $client = new Client(['handler' => $mock]); + $request = new Request('GET', 'http://foo.com'); + $response = $client->send($request, ['json' => ['a' => 'b']]); + + self::assertSame($responseBody, (string) $response->getBody()); + } + + public function testResponseContent() + { + $responseBody = '{ "package": "guzzle" }'; + $mock = new MockHandler([new Response(200, ['Content-Type' => 'application/json'], $responseBody)]); + $client = new Client(['handler' => $mock]); + $request = new Request('POST', 'http://foo.com'); + $response = $client->send($request, ['json' => ['a' => 'b']]); + + self::assertSame($responseBody, $response->getBody()->getContents()); + } + + public function testIdnSupportDefaultValue() + { + $mockHandler = new MockHandler([new Response()]); + $client = new Client(['handler' => $mockHandler]); + + $config = $client->getConfig(); + + self::assertTrue($config['idn_conversion']); + } + + public function testIdnIsTranslatedToAsciiWhenConversionIsEnabled() + { + $mockHandler = new MockHandler([new Response()]); + $client = new Client(['handler' => $mockHandler]); + + $client->request('GET', 'https://яндекс.рф/images', ['idn_conversion' => true]); + + $request = $mockHandler->getLastRequest(); + + self::assertSame('https://xn--d1acpjx3f.xn--p1ai/images', (string) $request->getUri()); + self::assertSame('xn--d1acpjx3f.xn--p1ai', (string) $request->getHeaderLine('Host')); + } + + public function testIdnStaysTheSameWhenConversionIsDisabled() + { + $mockHandler = new MockHandler([new Response()]); + $client = new Client(['handler' => $mockHandler]); + + $client->request('GET', 'https://яндекс.рф/images', ['idn_conversion' => false]); + + $request = $mockHandler->getLastRequest(); + + self::assertSame('https://яндекс.рф/images', (string) $request->getUri()); + self::assertSame('яндекс.рф', (string) $request->getHeaderLine('Host')); + } + + /** + * @expectedException \GuzzleHttp\Exception\InvalidArgumentException + * @expectedExceptionMessage IDN conversion failed + */ + public function testExceptionOnInvalidIdn() + { + $mockHandler = new MockHandler([new Response()]); + $client = new Client(['handler' => $mockHandler]); + + $client->request('GET', 'https://-яндекс.рф/images', ['idn_conversion' => true]); + } + + /** + * @depends testCanUseRelativeUriWithSend + * @depends testIdnSupportDefaultValue + */ + public function testIdnBaseUri() + { + $mock = new MockHandler([new Response()]); + $client = new Client([ + 'handler' => $mock, + 'base_uri' => 'http://яндекс.рф', + ]); + self::assertSame('http://яндекс.рф', (string) $client->getConfig('base_uri')); + $request = new Request('GET', '/baz'); + $client->send($request); + self::assertSame('http://xn--d1acpjx3f.xn--p1ai/baz', (string) $mock->getLastRequest()->getUri()); + self::assertSame('xn--d1acpjx3f.xn--p1ai', (string) $mock->getLastRequest()->getHeaderLine('Host')); + } + + public function testIdnWithRedirect() + { + $mockHandler = new MockHandler([ + new Response(302, ['Location' => 'http://www.tést.com/whatever']), + new Response() + ]); + $handler = HandlerStack::create($mockHandler); + $requests = []; + $handler->push(Middleware::history($requests)); + $client = new Client(['handler' => $handler]); + + $client->request('GET', 'https://яндекс.рф/images', [ + RequestOptions::ALLOW_REDIRECTS => [ + 'referer' => true, + 'track_redirects' => true + ], + 'idn_conversion' => true + ]); + + $request = $mockHandler->getLastRequest(); + + self::assertSame('http://www.xn--tst-bma.com/whatever', (string) $request->getUri()); + self::assertSame('www.xn--tst-bma.com', (string) $request->getHeaderLine('Host')); + + $request = $requests[0]['request']; + self::assertSame('https://xn--d1acpjx3f.xn--p1ai/images', (string) $request->getUri()); + self::assertSame('xn--d1acpjx3f.xn--p1ai', (string) $request->getHeaderLine('Host')); + } } diff --git a/tests/Cookie/CookieJarTest.php b/tests/Cookie/CookieJarTest.php index dbdc56646..1e9c84c92 100644 --- a/tests/Cookie/CookieJarTest.php +++ b/tests/Cookie/CookieJarTest.php @@ -1,6 +1,9 @@ 'bar', 'baz' => 'bam' ], 'example.com'); - $this->assertCount(2, $jar); + self::assertCount(2, $jar); } public function testEmptyJarIsCountable() { - $this->assertCount(0, new CookieJar()); + self::assertCount(0, new CookieJar()); } public function testGetsCookiesByName() @@ -51,9 +54,9 @@ public function testGetsCookiesByName() } $testCookie = $cookies[0]; - $this->assertEquals($testCookie, $this->jar->getCookieByName($testCookie->getName())); - $this->assertNull($this->jar->getCookieByName("doesnotexist")); - $this->assertNull($this->jar->getCookieByName("")); + self::assertEquals($testCookie, $this->jar->getCookieByName($testCookie->getName())); + self::assertNull($this->jar->getCookieByName("doesnotexist")); + self::assertNull($this->jar->getCookieByName("")); } /** @@ -79,12 +82,12 @@ public function testStoresAndRetrievesCookies() { $cookies = $this->getTestCookies(); foreach ($cookies as $cookie) { - $this->assertTrue($this->jar->setCookie($cookie)); + self::assertTrue($this->jar->setCookie($cookie)); } - $this->assertCount(3, $this->jar); - $this->assertCount(3, $this->jar->getIterator()); - $this->assertEquals($cookies, $this->jar->getIterator()->getArrayCopy()); + self::assertCount(3, $this->jar); + self::assertCount(3, $this->jar->getIterator()); + self::assertEquals($cookies, $this->jar->getIterator()->getArrayCopy()); } public function testRemovesTemporaryCookies() @@ -94,7 +97,7 @@ public function testRemovesTemporaryCookies() $this->jar->setCookie($cookie); } $this->jar->clearSessionCookies(); - $this->assertEquals( + self::assertEquals( [$cookies[1], $cookies[2]], $this->jar->getIterator()->getArrayCopy() ); @@ -108,75 +111,75 @@ public function testRemovesSelectively() // Remove foo.com cookies $this->jar->clear('foo.com'); - $this->assertCount(2, $this->jar); + self::assertCount(2, $this->jar); // Try again, removing no further cookies $this->jar->clear('foo.com'); - $this->assertCount(2, $this->jar); + self::assertCount(2, $this->jar); // Remove bar.com cookies with path of /boo $this->jar->clear('bar.com', '/boo'); - $this->assertCount(1, $this->jar); + self::assertCount(1, $this->jar); // Remove cookie by name $this->jar->clear(null, null, 'test'); - $this->assertCount(0, $this->jar); + self::assertCount(0, $this->jar); } public function testDoesNotAddIncompleteCookies() { - $this->assertFalse($this->jar->setCookie(new SetCookie())); - $this->assertFalse($this->jar->setCookie(new SetCookie(array( + self::assertFalse($this->jar->setCookie(new SetCookie())); + self::assertFalse($this->jar->setCookie(new SetCookie([ 'Name' => 'foo' - )))); - $this->assertFalse($this->jar->setCookie(new SetCookie(array( + ]))); + self::assertFalse($this->jar->setCookie(new SetCookie([ 'Name' => false - )))); - $this->assertFalse($this->jar->setCookie(new SetCookie(array( + ]))); + self::assertFalse($this->jar->setCookie(new SetCookie([ 'Name' => true - )))); - $this->assertFalse($this->jar->setCookie(new SetCookie(array( + ]))); + self::assertFalse($this->jar->setCookie(new SetCookie([ 'Name' => 'foo', 'Domain' => 'foo.com' - )))); + ]))); } public function testDoesNotAddEmptyCookies() { - $this->assertFalse($this->jar->setCookie(new SetCookie(array( + self::assertFalse($this->jar->setCookie(new SetCookie([ 'Name' => '', 'Domain' => 'foo.com', 'Value' => 0 - )))); + ]))); } public function testDoesAddValidCookies() { - $this->assertTrue($this->jar->setCookie(new SetCookie(array( + self::assertTrue($this->jar->setCookie(new SetCookie([ 'Name' => '0', 'Domain' => 'foo.com', 'Value' => 0 - )))); - $this->assertTrue($this->jar->setCookie(new SetCookie(array( + ]))); + self::assertTrue($this->jar->setCookie(new SetCookie([ 'Name' => 'foo', 'Domain' => 'foo.com', 'Value' => 0 - )))); - $this->assertTrue($this->jar->setCookie(new SetCookie(array( + ]))); + self::assertTrue($this->jar->setCookie(new SetCookie([ 'Name' => 'foo', 'Domain' => 'foo.com', 'Value' => 0.0 - )))); - $this->assertTrue($this->jar->setCookie(new SetCookie(array( + ]))); + self::assertTrue($this->jar->setCookie(new SetCookie([ 'Name' => 'foo', 'Domain' => 'foo.com', 'Value' => '0' - )))); + ]))); } public function testOverwritesCookiesThatAreOlderOrDiscardable() { $t = time() + 1000; - $data = array( + $data = [ 'Name' => 'foo', 'Value' => 'bar', 'Domain' => '.example.com', @@ -185,35 +188,35 @@ public function testOverwritesCookiesThatAreOlderOrDiscardable() 'Secure' => true, 'Discard' => true, 'Expires' => $t - ); + ]; // Make sure that the discard cookie is overridden with the non-discard - $this->assertTrue($this->jar->setCookie(new SetCookie($data))); - $this->assertCount(1, $this->jar); + self::assertTrue($this->jar->setCookie(new SetCookie($data))); + self::assertCount(1, $this->jar); $data['Discard'] = false; - $this->assertTrue($this->jar->setCookie(new SetCookie($data))); - $this->assertCount(1, $this->jar); + self::assertTrue($this->jar->setCookie(new SetCookie($data))); + self::assertCount(1, $this->jar); $c = $this->jar->getIterator()->getArrayCopy(); - $this->assertFalse($c[0]->getDiscard()); + self::assertFalse($c[0]->getDiscard()); // Make sure it doesn't duplicate the cookie $this->jar->setCookie(new SetCookie($data)); - $this->assertCount(1, $this->jar); + self::assertCount(1, $this->jar); // Make sure the more future-ful expiration date supersede the other $data['Expires'] = time() + 2000; - $this->assertTrue($this->jar->setCookie(new SetCookie($data))); - $this->assertCount(1, $this->jar); + self::assertTrue($this->jar->setCookie(new SetCookie($data))); + self::assertCount(1, $this->jar); $c = $this->jar->getIterator()->getArrayCopy(); - $this->assertNotEquals($t, $c[0]->getExpires()); + self::assertNotEquals($t, $c[0]->getExpires()); } public function testOverwritesCookiesThatHaveChanged() { $t = time() + 1000; - $data = array( + $data = [ 'Name' => 'foo', 'Value' => 'bar', 'Domain' => '.example.com', @@ -222,44 +225,44 @@ public function testOverwritesCookiesThatHaveChanged() 'Secure' => true, 'Discard' => true, 'Expires' => $t - ); + ]; // Make sure that the discard cookie is overridden with the non-discard - $this->assertTrue($this->jar->setCookie(new SetCookie($data))); + self::assertTrue($this->jar->setCookie(new SetCookie($data))); $data['Value'] = 'boo'; - $this->assertTrue($this->jar->setCookie(new SetCookie($data))); - $this->assertCount(1, $this->jar); + self::assertTrue($this->jar->setCookie(new SetCookie($data))); + self::assertCount(1, $this->jar); // Changing the value plus a parameter also must overwrite the existing one $data['Value'] = 'zoo'; $data['Secure'] = false; - $this->assertTrue($this->jar->setCookie(new SetCookie($data))); - $this->assertCount(1, $this->jar); + self::assertTrue($this->jar->setCookie(new SetCookie($data))); + self::assertCount(1, $this->jar); $c = $this->jar->getIterator()->getArrayCopy(); - $this->assertEquals('zoo', $c[0]->getValue()); + self::assertSame('zoo', $c[0]->getValue()); } public function testAddsCookiesFromResponseWithRequest() { - $response = new Response(200, array( + $response = new Response(200, [ 'Set-Cookie' => "fpc=d=.Hm.yh4.1XmJWjJfs4orLQzKzPImxklQoxXSHOZATHUSEFciRueW_7704iYUtsXNEXq0M92Px2glMdWypmJ7HIQl6XIUvrZimWjQ3vIdeuRbI.FNQMAfcxu_XN1zSx7l.AcPdKL6guHc2V7hIQFhnjRW0rxm2oHY1P4bGQxFNz7f.tHm12ZD3DbdMDiDy7TBXsuP4DM-&v=2; expires=Fri, 02-Mar-2019 02:17:40 GMT;" - )); + ]); $request = new Request('GET', 'http://www.example.com'); $this->jar->extractCookies($request, $response); - $this->assertCount(1, $this->jar); + self::assertCount(1, $this->jar); } public function getMatchingCookiesDataProvider() { - return array( - array('https://example.com', 'foo=bar; baz=foobar'), - array('http://example.com', ''), - array('https://example.com:8912', 'foo=bar; baz=foobar'), - array('https://foo.example.com', 'foo=bar; baz=foobar'), - array('http://foo.example.com/test/acme/', 'googoo=gaga') - ); + return [ + ['https://example.com', 'foo=bar; baz=foobar'], + ['http://example.com', ''], + ['https://example.com:8912', 'foo=bar; baz=foobar'], + ['https://foo.example.com', 'foo=bar; baz=foobar'], + ['http://foo.example.com/test/acme/', 'googoo=gaga'] + ]; } /** @@ -313,7 +316,7 @@ public function testReturnsCookiesMatchingRequests($url, $cookies) $request = new Request('GET', $url); $request = $this->jar->withCookieHeader($request); - $this->assertEquals($cookies, $request->getHeaderLine('Cookie')); + self::assertSame($cookies, $request->getHeaderLine('Cookie')); } /** @@ -340,13 +343,13 @@ public function testDeletesCookiesByName() foreach ($cookies as $cookie) { $jar->setCookie($cookie); } - $this->assertCount(4, $jar); + self::assertCount(4, $jar); $jar->clear('bar.com', '/boo', 'other'); - $this->assertCount(3, $jar); + self::assertCount(3, $jar); $names = array_map(function (SetCookie $c) { return $c->getName(); }, $jar->getIterator()->getArrayCopy()); - $this->assertEquals(['foo', 'test', 'you'], $names); + self::assertSame(['foo', 'test', 'you'], $names); } public function testCanConvertToAndLoadFromArray() @@ -355,23 +358,23 @@ public function testCanConvertToAndLoadFromArray() foreach ($this->getTestCookies() as $cookie) { $jar->setCookie($cookie); } - $this->assertCount(3, $jar); + self::assertCount(3, $jar); $arr = $jar->toArray(); - $this->assertCount(3, $arr); + self::assertCount(3, $arr); $newCookieJar = new CookieJar(false, $arr); - $this->assertCount(3, $newCookieJar); - $this->assertSame($jar->toArray(), $newCookieJar->toArray()); + self::assertCount(3, $newCookieJar); + self::assertSame($jar->toArray(), $newCookieJar->toArray()); } public function testAddsCookiesWithEmptyPathFromResponse() { - $response = new Response(200, array( - 'Set-Cookie' => "fpc=foobar; expires=Fri, 02-Mar-2019 02:17:40 GMT; path=;" - )); + $response = new Response(200, [ + 'Set-Cookie' => "fpc=foobar; expires={$this->futureExpirationDate()}; path=;" + ]); $request = new Request('GET', 'http://www.example.com'); $this->jar->extractCookies($request, $response); $newRequest = $this->jar->withCookieHeader(new Request('GET', 'http://www.example.com/foo')); - $this->assertTrue($newRequest->hasHeader('Cookie')); + self::assertTrue($newRequest->hasHeader('Cookie')); } public function getCookiePathsDataProvider() @@ -382,9 +385,6 @@ public function getCookiePathsDataProvider() ['/foo', '/'], ['/foo/bar', '/foo'], ['/foo/bar/', '/foo/bar'], - ['foo', '/'], - ['foo/bar', '/'], - ['foo/bar/', '/'], ]; } @@ -394,13 +394,56 @@ public function getCookiePathsDataProvider() public function testCookiePathWithEmptySetCookiePath($uriPath, $cookiePath) { $response = (new Response(200)) - ->withAddedHeader('Set-Cookie', "foo=bar; expires=Fri, 02-Mar-2019 02:17:40 GMT; domain=www.example.com; path=;") - ->withAddedHeader('Set-Cookie', "bar=foo; expires=Fri, 02-Mar-2019 02:17:40 GMT; domain=www.example.com; path=foobar;") + ->withAddedHeader( + 'Set-Cookie', + "foo=bar; expires={$this->futureExpirationDate()}; domain=www.example.com; path=;" + ) + ->withAddedHeader( + 'Set-Cookie', + "bar=foo; expires={$this->futureExpirationDate()}; domain=www.example.com; path=foobar;" + ) + ; + $request = (new Request('GET', "https://www.example.com{$uriPath}")); + $this->jar->extractCookies($request, $response); + + self::assertSame($cookiePath, $this->jar->toArray()[0]['Path']); + self::assertSame($cookiePath, $this->jar->toArray()[1]['Path']); + } + + public function getDomainMatchesProvider() + { + return [ + ['www.example.com', 'www.example.com', true], + ['www.example.com', 'www.EXAMPLE.com', true], + ['www.example.com', 'www.example.net', false], + ['www.example.com', 'ftp.example.com', false], + ['www.example.com', 'example.com', true], + ['www.example.com', 'EXAMPLE.com', true], + ['fra.de.example.com', 'EXAMPLE.com', true], + ['www.EXAMPLE.com', 'www.example.com', true], + ['www.EXAMPLE.com', 'www.example.COM', true], + ]; + } + + /** + * @dataProvider getDomainMatchesProvider + */ + public function testIgnoresCookiesForMismatchingDomains($requestHost, $domainAttribute, $matches) + { + $response = (new Response(200)) + ->withAddedHeader( + 'Set-Cookie', + "foo=bar; expires={$this->futureExpirationDate()}; domain={$domainAttribute}; path=/;" + ) ; - $request = (new Request('GET', $uriPath))->withHeader('Host', 'www.example.com'); + $request = (new Request('GET', "https://{$requestHost}/")); $this->jar->extractCookies($request, $response); - $this->assertEquals($cookiePath, $this->jar->toArray()[0]['Path']); - $this->assertEquals($cookiePath, $this->jar->toArray()[1]['Path']); + self::assertCount($matches ? 1 : 0, $this->jar->toArray()); + } + + private function futureExpirationDate() + { + return (new DateTimeImmutable)->add(new DateInterval('P1D'))->format(DateTime::COOKIE); } } diff --git a/tests/Cookie/FileCookieJarTest.php b/tests/Cookie/FileCookieJarTest.php index af378f403..6391c2013 100644 --- a/tests/Cookie/FileCookieJarTest.php +++ b/tests/Cookie/FileCookieJarTest.php @@ -29,12 +29,12 @@ public function testValidatesCookieFile() public function testLoadsFromFile() { $jar = new FileCookieJar($this->file); - $this->assertEquals([], $jar->getIterator()->getArrayCopy()); + self::assertSame([], $jar->getIterator()->getArrayCopy()); unlink($this->file); } /** - * @dataProvider testPersistsToFileFileParameters + * @dataProvider providerPersistsToFileFileParameters */ public function testPersistsToFile($testSaveSessionCookie = false) { @@ -57,32 +57,32 @@ public function testPersistsToFile($testSaveSessionCookie = false) 'Domain' => 'foo.com', ])); - $this->assertCount(3, $jar); + self::assertCount(3, $jar); unset($jar); // Make sure it wrote to the file $contents = file_get_contents($this->file); - $this->assertNotEmpty($contents); + self::assertNotEmpty($contents); // Load the cookieJar from the file $jar = new FileCookieJar($this->file); if ($testSaveSessionCookie) { - $this->assertCount(3, $jar); + self::assertCount(3, $jar); } else { // Weeds out temporary and session cookies - $this->assertCount(2, $jar); + self::assertCount(2, $jar); } unset($jar); unlink($this->file); } - public function testPersistsToFileFileParameters() + public function providerPersistsToFileFileParameters() { - return array( - array(false), - array(true) - ); + return [ + [false], + [true] + ]; } } diff --git a/tests/Cookie/SessionCookieJarTest.php b/tests/Cookie/SessionCookieJarTest.php index 8d6024a51..2f29bdc15 100644 --- a/tests/Cookie/SessionCookieJarTest.php +++ b/tests/Cookie/SessionCookieJarTest.php @@ -17,7 +17,7 @@ public function setUp() $this->sessionVar = 'sessionKey'; if (!isset($_SESSION)) { - $_SESSION = array(); + $_SESSION = []; } } @@ -33,12 +33,12 @@ public function testValidatesCookieSession() public function testLoadsFromSession() { $jar = new SessionCookieJar($this->sessionVar); - $this->assertEquals([], $jar->getIterator()->getArrayCopy()); + self::assertSame([], $jar->getIterator()->getArrayCopy()); unset($_SESSION[$this->sessionVar]); } /** - * @dataProvider testPersistsToSessionParameters + * @dataProvider providerPersistsToSessionParameters */ public function testPersistsToSession($testSaveSessionCookie = false) { @@ -61,32 +61,32 @@ public function testPersistsToSession($testSaveSessionCookie = false) 'Domain' => 'foo.com', ])); - $this->assertCount(3, $jar); + self::assertCount(3, $jar); unset($jar); // Make sure it wrote to the sessionVar in $_SESSION $contents = $_SESSION[$this->sessionVar]; - $this->assertNotEmpty($contents); + self::assertNotEmpty($contents); // Load the cookieJar from the file $jar = new SessionCookieJar($this->sessionVar); if ($testSaveSessionCookie) { - $this->assertCount(3, $jar); + self::assertCount(3, $jar); } else { // Weeds out temporary and session cookies - $this->assertCount(2, $jar); + self::assertCount(2, $jar); } unset($jar); unset($_SESSION[$this->sessionVar]); } - public function testPersistsToSessionParameters() + public function providerPersistsToSessionParameters() { - return array( - array(false), - array(true) - ); + return [ + [false], + [true] + ]; } } diff --git a/tests/Cookie/SetCookieTest.php b/tests/Cookie/SetCookieTest.php index 3625d323a..335e44baa 100644 --- a/tests/Cookie/SetCookieTest.php +++ b/tests/Cookie/SetCookieTest.php @@ -12,26 +12,26 @@ class SetCookieTest extends TestCase public function testInitializesDefaultValues() { $cookie = new SetCookie(); - $this->assertEquals('/', $cookie->getPath()); + self::assertSame('/', $cookie->getPath()); } public function testConvertsDateTimeMaxAgeToUnixTimestamp() { $cookie = new SetCookie(['Expires' => 'November 20, 1984']); - $this->assertInternalType('integer', $cookie->getExpires()); + self::assertInternalType('integer', $cookie->getExpires()); } public function testAddsExpiresBasedOnMaxAge() { $t = time(); $cookie = new SetCookie(['Max-Age' => 100]); - $this->assertEquals($t + 100, $cookie->getExpires()); + self::assertEquals($t + 100, $cookie->getExpires()); } public function testHoldsValues() { $t = time(); - $data = array( + $data = [ 'Name' => 'foo', 'Value' => 'baz', 'Path' => '/bar', @@ -43,22 +43,22 @@ public function testHoldsValues() 'HttpOnly' => true, 'foo' => 'baz', 'bar' => 'bam' - ); + ]; $cookie = new SetCookie($data); - $this->assertEquals($data, $cookie->toArray()); + self::assertEquals($data, $cookie->toArray()); - $this->assertEquals('foo', $cookie->getName()); - $this->assertEquals('baz', $cookie->getValue()); - $this->assertEquals('baz.com', $cookie->getDomain()); - $this->assertEquals('/bar', $cookie->getPath()); - $this->assertEquals($t, $cookie->getExpires()); - $this->assertEquals(100, $cookie->getMaxAge()); - $this->assertTrue($cookie->getSecure()); - $this->assertTrue($cookie->getDiscard()); - $this->assertTrue($cookie->getHttpOnly()); - $this->assertEquals('baz', $cookie->toArray()['foo']); - $this->assertEquals('bam', $cookie->toArray()['bar']); + self::assertSame('foo', $cookie->getName()); + self::assertSame('baz', $cookie->getValue()); + self::assertSame('baz.com', $cookie->getDomain()); + self::assertSame('/bar', $cookie->getPath()); + self::assertSame($t, $cookie->getExpires()); + self::assertSame(100, $cookie->getMaxAge()); + self::assertTrue($cookie->getSecure()); + self::assertTrue($cookie->getDiscard()); + self::assertTrue($cookie->getHttpOnly()); + self::assertSame('baz', $cookie->toArray()['foo']); + self::assertSame('bam', $cookie->toArray()['bar']); $cookie->setName('a'); $cookie->setValue('b'); @@ -70,55 +70,55 @@ public function testHoldsValues() $cookie->setHttpOnly(false); $cookie->setDiscard(false); - $this->assertEquals('a', $cookie->getName()); - $this->assertEquals('b', $cookie->getValue()); - $this->assertEquals('c', $cookie->getPath()); - $this->assertEquals('bar.com', $cookie->getDomain()); - $this->assertEquals(10, $cookie->getExpires()); - $this->assertEquals(200, $cookie->getMaxAge()); - $this->assertFalse($cookie->getSecure()); - $this->assertFalse($cookie->getDiscard()); - $this->assertFalse($cookie->getHttpOnly()); + self::assertSame('a', $cookie->getName()); + self::assertSame('b', $cookie->getValue()); + self::assertSame('c', $cookie->getPath()); + self::assertSame('bar.com', $cookie->getDomain()); + self::assertSame(10, $cookie->getExpires()); + self::assertSame(200, $cookie->getMaxAge()); + self::assertFalse($cookie->getSecure()); + self::assertFalse($cookie->getDiscard()); + self::assertFalse($cookie->getHttpOnly()); } public function testDeterminesIfExpired() { $c = new SetCookie(); $c->setExpires(10); - $this->assertTrue($c->isExpired()); + self::assertTrue($c->isExpired()); $c->setExpires(time() + 10000); - $this->assertFalse($c->isExpired()); + self::assertFalse($c->isExpired()); } public function testMatchesDomain() { $cookie = new SetCookie(); - $this->assertTrue($cookie->matchesDomain('baz.com')); + self::assertTrue($cookie->matchesDomain('baz.com')); $cookie->setDomain('baz.com'); - $this->assertTrue($cookie->matchesDomain('baz.com')); - $this->assertFalse($cookie->matchesDomain('bar.com')); + self::assertTrue($cookie->matchesDomain('baz.com')); + self::assertFalse($cookie->matchesDomain('bar.com')); $cookie->setDomain('.baz.com'); - $this->assertTrue($cookie->matchesDomain('.baz.com')); - $this->assertTrue($cookie->matchesDomain('foo.baz.com')); - $this->assertFalse($cookie->matchesDomain('baz.bar.com')); - $this->assertTrue($cookie->matchesDomain('baz.com')); + self::assertTrue($cookie->matchesDomain('.baz.com')); + self::assertTrue($cookie->matchesDomain('foo.baz.com')); + self::assertFalse($cookie->matchesDomain('baz.bar.com')); + self::assertTrue($cookie->matchesDomain('baz.com')); $cookie->setDomain('.127.0.0.1'); - $this->assertTrue($cookie->matchesDomain('127.0.0.1')); + self::assertTrue($cookie->matchesDomain('127.0.0.1')); $cookie->setDomain('127.0.0.1'); - $this->assertTrue($cookie->matchesDomain('127.0.0.1')); + self::assertTrue($cookie->matchesDomain('127.0.0.1')); $cookie->setDomain('.com.'); - $this->assertFalse($cookie->matchesDomain('baz.com')); + self::assertFalse($cookie->matchesDomain('baz.com')); $cookie->setDomain('.local'); - $this->assertTrue($cookie->matchesDomain('example.local')); + self::assertTrue($cookie->matchesDomain('example.local')); $cookie->setDomain('example.com/'); // malformed domain - $this->assertFalse($cookie->matchesDomain('example.com')); + self::assertFalse($cookie->matchesDomain('example.com')); } public function pathMatchProvider() @@ -149,20 +149,20 @@ public function testMatchesPath($cookiePath, $requestPath, $isMatch) { $cookie = new SetCookie(); $cookie->setPath($cookiePath); - $this->assertEquals($isMatch, $cookie->matchesPath($requestPath)); + self::assertSame($isMatch, $cookie->matchesPath($requestPath)); } public function cookieValidateProvider() { - return array( - array('foo', 'baz', 'bar', true), - array('0', '0', '0', true), - array('foo[bar]', 'baz', 'bar', true), - array('', 'baz', 'bar', 'The cookie name must not be empty'), - array('foo', '', 'bar', 'The cookie value must not be empty'), - array('foo', 'baz', '', 'The cookie domain must not be empty'), - array("foo\r", 'baz', '0', 'Cookie name must not contain invalid characters: ASCII Control characters (0-31;127), space, tab and the following characters: ()<>@,;:\"/?={}'), - ); + return [ + ['foo', 'baz', 'bar', true], + ['0', '0', '0', true], + ['foo[bar]', 'baz', 'bar', true], + ['', 'baz', 'bar', 'The cookie name must not be empty'], + ['foo', '', 'bar', 'The cookie value must not be empty'], + ['foo', 'baz', '', 'The cookie domain must not be empty'], + ["foo\r", 'baz', '0', 'Cookie name must not contain invalid characters: ASCII Control characters (0-31;127), space, tab and the following characters: ()<>@,;:\"/?={}'], + ]; } /** @@ -170,18 +170,18 @@ public function cookieValidateProvider() */ public function testValidatesCookies($name, $value, $domain, $result) { - $cookie = new SetCookie(array( + $cookie = new SetCookie([ 'Name' => $name, 'Value' => $value, - 'Domain' => $domain - )); - $this->assertSame($result, $cookie->validate()); + 'Domain' => $domain, + ]); + self::assertSame($result, $cookie->validate()); } public function testDoesNotMatchIp() { $cookie = new SetCookie(['Domain' => '192.168.16.']); - $this->assertFalse($cookie->matchesDomain('192.168.16.121')); + self::assertFalse($cookie->matchesDomain('192.168.16.121')); } public function testConvertsToString() @@ -196,7 +196,7 @@ public function testConvertsToString() 'HttpOnly' => true, 'Secure' => true ]); - $this->assertEquals( + self::assertSame( 'test=123; Domain=foo.com; Path=/abc; Expires=Sun, 27 Oct 2013 23:20:08 GMT; Secure; HttpOnly', (string) $cookie ); @@ -209,10 +209,10 @@ public function testConvertsToString() */ public function cookieParserDataProvider() { - return array( - array( + return [ + [ 'ASIHTTPRequestTestCookie=This+is+the+value; expires=Sat, 26-Jul-2008 17:00:42 GMT; path=/tests; domain=allseeing-i.com; PHPSESSID=6c951590e7a9359bcedde25cda73e43c; path=/;', - array( + [ 'Domain' => 'allseeing-i.com', 'Path' => '/', 'PHPSESSID' => '6c951590e7a9359bcedde25cda73e43c', @@ -223,12 +223,12 @@ public function cookieParserDataProvider() 'Name' => 'ASIHTTPRequestTestCookie', 'Value' => 'This+is+the+value', 'HttpOnly' => false - ) - ), - array('', []), - array('foo', []), - array('; foo', []), - array( + ] + ], + ['', []], + ['foo', []], + ['; foo', []], + [ 'foo="bar"', [ 'Name' => 'foo', @@ -241,11 +241,11 @@ public function cookieParserDataProvider() 'Secure' => null, 'HttpOnly' => false ] - ), + ], // Test setting a blank value for a cookie - array(array( - 'foo=', 'foo =', 'foo =;', 'foo= ;', 'foo =', 'foo= '), - array( + [[ + 'foo=', 'foo =', 'foo =;', 'foo= ;', 'foo =', 'foo= '], + [ 'Name' => 'foo', 'Value' => '', 'Discard' => null, @@ -255,12 +255,12 @@ public function cookieParserDataProvider() 'Path' => '/', 'Secure' => null, 'HttpOnly' => false - ) - ), + ] + ], // Test setting a value and removing quotes - array(array( - 'foo=1', 'foo =1', 'foo =1;', 'foo=1 ;', 'foo =1', 'foo= 1', 'foo = 1 ;'), - array( + [[ + 'foo=1', 'foo =1', 'foo =1;', 'foo=1 ;', 'foo =1', 'foo= 1', 'foo = 1 ;'], + [ 'Name' => 'foo', 'Value' => '1', 'Discard' => null, @@ -270,12 +270,12 @@ public function cookieParserDataProvider() 'Path' => '/', 'Secure' => null, 'HttpOnly' => false - ) - ), + ] + ], // Some of the following tests are based on http://framework.zend.com/svn/framework/standard/trunk/tests/Zend/Http/CookieTest.php - array( + [ 'justacookie=foo; domain=example.com', - array( + [ 'Name' => 'justacookie', 'Value' => 'foo', 'Domain' => 'example.com', @@ -285,11 +285,11 @@ public function cookieParserDataProvider() 'Path' => '/', 'Secure' => null, 'HttpOnly' => false - ) - ), - array( + ] + ], + [ 'expires=tomorrow; secure; path=/Space Out/; expires=Tue, 21-Nov-2006 08:33:44 GMT; domain=.example.com', - array( + [ 'Name' => 'expires', 'Value' => 'tomorrow', 'Domain' => '.example.com', @@ -299,11 +299,11 @@ public function cookieParserDataProvider() 'Secure' => true, 'Max-Age' => null, 'HttpOnly' => false - ) - ), - array( + ] + ], + [ 'domain=unittests; expires=Tue, 21-Nov-2006 08:33:44 GMT; domain=example.com; path=/some value/', - array( + [ 'Name' => 'domain', 'Value' => 'unittests', 'Domain' => 'example.com', @@ -313,11 +313,11 @@ public function cookieParserDataProvider() 'Discard' => null, 'Max-Age' => null, 'HttpOnly' => false - ) - ), - array( + ] + ], + [ 'path=indexAction; path=/; domain=.foo.com; expires=Tue, 21-Nov-2006 08:33:44 GMT', - array( + [ 'Name' => 'path', 'Value' => 'indexAction', 'Domain' => '.foo.com', @@ -327,11 +327,11 @@ public function cookieParserDataProvider() 'Discard' => null, 'Max-Age' => null, 'HttpOnly' => false - ) - ), - array( + ] + ], + [ 'secure=sha1; secure; SECURE; domain=some.really.deep.domain.com; version=1; Max-Age=86400', - array( + [ 'Name' => 'secure', 'Value' => 'sha1', 'Domain' => 'some.really.deep.domain.com', @@ -342,11 +342,11 @@ public function cookieParserDataProvider() 'Max-Age' => 86400, 'HttpOnly' => false, 'version' => '1' - ) - ), - array( + ] + ], + [ 'PHPSESSID=123456789+abcd%2Cef; secure; discard; domain=.localdomain; path=/foo/baz; expires=Tue, 21-Nov-2006 08:33:44 GMT;', - array( + [ 'Name' => 'PHPSESSID', 'Value' => '123456789+abcd%2Cef', 'Domain' => '.localdomain', @@ -356,9 +356,9 @@ public function cookieParserDataProvider() 'Discard' => true, 'Max-Age' => null, 'HttpOnly' => false - ) - ), - ); + ] + ], + ]; } /** @@ -380,13 +380,13 @@ public function testParseCookie($cookie, $parsed) if (!empty($parsed)) { foreach ($parsed as $key => $value) { - $this->assertEquals($parsed[$key], $p[$key], 'Comparing ' . $key . ' ' . var_export($value, true) . ' : ' . var_export($parsed, true) . ' | ' . var_export($p, true)); + self::assertEquals($parsed[$key], $p[$key], 'Comparing ' . $key . ' ' . var_export($value, true) . ' : ' . var_export($parsed, true) . ' | ' . var_export($p, true)); } foreach ($p as $key => $value) { - $this->assertEquals($p[$key], $parsed[$key], 'Comparing ' . $key . ' ' . var_export($value, true) . ' : ' . var_export($parsed, true) . ' | ' . var_export($p, true)); + self::assertEquals($p[$key], $parsed[$key], 'Comparing ' . $key . ' ' . var_export($value, true) . ' : ' . var_export($parsed, true) . ' | ' . var_export($p, true)); } } else { - $this->assertEquals([ + self::assertSame([ 'Name' => null, 'Value' => null, 'Domain' => null, @@ -408,28 +408,28 @@ public function testParseCookie($cookie, $parsed) */ public function isExpiredProvider() { - return array( - array( + return [ + [ 'FOO=bar; expires=Thu, 01 Jan 1970 00:00:00 GMT;', true, - ), - array( + ], + [ 'FOO=bar; expires=Thu, 01 Jan 1970 00:00:01 GMT;', true, - ), - array( - 'FOO=bar; expires='.date(\DateTime::RFC1123, time()+10).';', + ], + [ + 'FOO=bar; expires=' . date(\DateTime::RFC1123, time()+10) . ';', false, - ), - array( - 'FOO=bar; expires='.date(\DateTime::RFC1123, time()-10).';', + ], + [ + 'FOO=bar; expires=' . date(\DateTime::RFC1123, time()-10) . ';', true, - ), - array( + ], + [ 'FOO=bar;', false, - ), - ); + ], + ]; } /** @@ -437,7 +437,7 @@ public function isExpiredProvider() */ public function testIsExpired($cookie, $expired) { - $this->assertEquals( + self::assertSame( $expired, SetCookie::fromString($cookie)->isExpired() ); diff --git a/tests/Exception/ConnectExceptionTest.php b/tests/Exception/ConnectExceptionTest.php index e518c0dcb..553af403e 100644 --- a/tests/Exception/ConnectExceptionTest.php +++ b/tests/Exception/ConnectExceptionTest.php @@ -1,12 +1,12 @@ 'bar']); - $this->assertSame($req, $e->getRequest()); - $this->assertNull($e->getResponse()); - $this->assertFalse($e->hasResponse()); - $this->assertEquals('foo', $e->getMessage()); - $this->assertEquals('bar', $e->getHandlerContext()['foo']); - $this->assertSame($prev, $e->getPrevious()); + self::assertSame($req, $e->getRequest()); + self::assertNull($e->getResponse()); + self::assertFalse($e->hasResponse()); + self::assertSame('foo', $e->getMessage()); + self::assertSame('bar', $e->getHandlerContext()['foo']); + self::assertSame($prev, $e->getPrevious()); } } diff --git a/tests/Exception/RequestExceptionTest.php b/tests/Exception/RequestExceptionTest.php index 6f70b3985..64d50d671 100644 --- a/tests/Exception/RequestExceptionTest.php +++ b/tests/Exception/RequestExceptionTest.php @@ -1,13 +1,14 @@ assertSame($req, $e->getRequest()); - $this->assertSame($res, $e->getResponse()); - $this->assertTrue($e->hasResponse()); - $this->assertEquals('foo', $e->getMessage()); + self::assertSame($req, $e->getRequest()); + self::assertSame($res, $e->getResponse()); + self::assertTrue($e->hasResponse()); + self::assertSame('foo', $e->getMessage()); } public function testCreatesGenerateException() { $e = RequestException::create(new Request('GET', '/')); - $this->assertEquals('Error completing request', $e->getMessage()); - $this->assertInstanceOf('GuzzleHttp\Exception\RequestException', $e); + self::assertSame('Error completing request', $e->getMessage()); + self::assertInstanceOf('GuzzleHttp\Exception\RequestException', $e); } public function testCreatesClientErrorResponseException() { $e = RequestException::create(new Request('GET', '/'), new Response(400)); - $this->assertContains( + self::assertContains( 'GET /', $e->getMessage() ); - $this->assertContains( + self::assertContains( '400 Bad Request', $e->getMessage() ); - $this->assertInstanceOf('GuzzleHttp\Exception\ClientException', $e); + self::assertInstanceOf('GuzzleHttp\Exception\ClientException', $e); } public function testCreatesServerErrorResponseException() { $e = RequestException::create(new Request('GET', '/'), new Response(500)); - $this->assertContains( + self::assertContains( 'GET /', $e->getMessage() ); - $this->assertContains( + self::assertContains( '500 Internal Server Error', $e->getMessage() ); - $this->assertInstanceOf('GuzzleHttp\Exception\ServerException', $e); + self::assertInstanceOf('GuzzleHttp\Exception\ServerException', $e); } public function testCreatesGenericErrorResponseException() { - $e = RequestException::create(new Request('GET', '/'), new Response(600)); - $this->assertContains( + $e = RequestException::create(new Request('GET', '/'), new Response(300)); + self::assertContains( 'GET /', $e->getMessage() ); - $this->assertContains( - '600 ', + self::assertContains( + '300 ', $e->getMessage() ); - $this->assertInstanceOf('GuzzleHttp\Exception\RequestException', $e); + self::assertInstanceOf('GuzzleHttp\Exception\RequestException', $e); + } + + /** + * @expectedException InvalidArgumentException + * @expectedExceptionMessage Status code must be an integer value between 1xx and 5xx. + */ + public function testThrowsInvalidArgumentExceptionOnOutOfBoundsResponseCode() + { + throw RequestException::create(new Request('GET', '/'), new Response(600)); } public function dataPrintableResponses() @@ -94,11 +104,11 @@ public function testCreatesExceptionWithPrintableBodySummary($content) $content ); $e = RequestException::create(new Request('GET', '/'), $response); - $this->assertContains( + self::assertContains( $content, $e->getMessage() ); - $this->assertInstanceOf('GuzzleHttp\Exception\RequestException', $e); + self::assertInstanceOf('GuzzleHttp\Exception\RequestException', $e); } public function testCreatesExceptionWithTruncatedSummary() @@ -107,34 +117,19 @@ public function testCreatesExceptionWithTruncatedSummary() $response = new Response(500, [], $content); $e = RequestException::create(new Request('GET', '/'), $response); $expected = str_repeat('+', 120) . ' (truncated...)'; - $this->assertContains($expected, $e->getMessage()); + self::assertContains($expected, $e->getMessage()); } public function testExceptionMessageIgnoresEmptyBody() { $e = RequestException::create(new Request('GET', '/'), new Response(500)); - $this->assertStringEndsWith('response', $e->getMessage()); - } - - public function testCreatesExceptionWithoutPrintableBody() - { - $response = new Response( - 500, - ['Content-Type' => 'image/gif'], - $content = base64_decode('R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7') // 1x1 gif - ); - $e = RequestException::create(new Request('GET', '/'), $response); - $this->assertNotContains( - $content, - $e->getMessage() - ); - $this->assertInstanceOf('GuzzleHttp\Exception\RequestException', $e); + self::assertStringEndsWith('response', $e->getMessage()); } public function testHasStatusCodeAsExceptionCode() { $e = RequestException::create(new Request('GET', '/'), new Response(442)); - $this->assertEquals(442, $e->getCode()); + self::assertSame(442, $e->getCode()); } public function testWrapsRequestExceptions() @@ -142,8 +137,8 @@ public function testWrapsRequestExceptions() $e = new \Exception('foo'); $r = new Request('GET', 'http://www.oo.com'); $ex = RequestException::wrapException($r, $e); - $this->assertInstanceOf('GuzzleHttp\Exception\RequestException', $ex); - $this->assertSame($e, $ex->getPrevious()); + self::assertInstanceOf('GuzzleHttp\Exception\RequestException', $ex); + self::assertSame($e, $ex->getPrevious()); } public function testDoesNotWrapExistingRequestExceptions() @@ -151,27 +146,50 @@ public function testDoesNotWrapExistingRequestExceptions() $r = new Request('GET', 'http://www.oo.com'); $e = new RequestException('foo', $r); $e2 = RequestException::wrapException($r, $e); - $this->assertSame($e, $e2); + self::assertSame($e, $e2); } public function testCanProvideHandlerContext() { $r = new Request('GET', 'http://www.oo.com'); $e = new RequestException('foo', $r, null, null, ['bar' => 'baz']); - $this->assertEquals(['bar' => 'baz'], $e->getHandlerContext()); + self::assertSame(['bar' => 'baz'], $e->getHandlerContext()); } public function testObfuscateUrlWithUsername() { $r = new Request('GET', 'http://username@www.oo.com'); $e = RequestException::create($r, new Response(500)); - $this->assertContains('http://username@www.oo.com', $e->getMessage()); + self::assertContains('http://username@www.oo.com', $e->getMessage()); } public function testObfuscateUrlWithUsernameAndPassword() { $r = new Request('GET', 'http://user:password@www.oo.com'); $e = RequestException::create($r, new Response(500)); - $this->assertContains('http://user:***@www.oo.com', $e->getMessage()); + self::assertContains('http://user:***@www.oo.com', $e->getMessage()); + } + + public function testGetResponseBodySummaryOfNonReadableStream() + { + self::assertNull(RequestException::getResponseBodySummary(new Response(500, [], new ReadSeekOnlyStream()))); + } +} + +final class ReadSeekOnlyStream extends Stream +{ + public function __construct() + { + parent::__construct(fopen('php://memory', 'wb')); + } + + public function isSeekable() + { + return true; + } + + public function isReadable() + { + return false; } } diff --git a/tests/Exception/SeekExceptionTest.php b/tests/Exception/SeekExceptionTest.php index 81b7db3b6..f12ac5904 100644 --- a/tests/Exception/SeekExceptionTest.php +++ b/tests/Exception/SeekExceptionTest.php @@ -11,7 +11,7 @@ public function testHasStream() { $s = Psr7\stream_for('foo'); $e = new SeekException($s, 10); - $this->assertSame($s, $e->getStream()); - $this->assertContains('10', $e->getMessage()); + self::assertSame($s, $e->getStream()); + self::assertContains('10', $e->getMessage()); } } diff --git a/tests/Handler/CurlFactoryTest.php b/tests/Handler/CurlFactoryTest.php index ce29978ac..a4cb3c4e9 100644 --- a/tests/Handler/CurlFactoryTest.php +++ b/tests/Handler/CurlFactoryTest.php @@ -1,14 +1,14 @@ create($request, ['sink' => $stream]); - $this->assertInstanceOf(EasyHandle::class, $result); - $this->assertInternalType('resource', $result->handle); - $this->assertInternalType('array', $result->headers); - $this->assertSame($stream, $result->sink); + self::assertInstanceOf(EasyHandle::class, $result); + self::assertInternalType('resource', $result->handle); + self::assertInternalType('array', $result->headers); + self::assertSame($stream, $result->sink); curl_close($result->handle); - $this->assertEquals('PUT', $_SERVER['_curl'][CURLOPT_CUSTOMREQUEST]); - $this->assertEquals( + self::assertSame('PUT', $_SERVER['_curl'][CURLOPT_CUSTOMREQUEST]); + self::assertSame( 'http://127.0.0.1:8126/', $_SERVER['_curl'][CURLOPT_URL] ); // Sends via post fields when the request is small enough - $this->assertEquals('testing', $_SERVER['_curl'][CURLOPT_POSTFIELDS]); - $this->assertEquals(0, $_SERVER['_curl'][CURLOPT_RETURNTRANSFER]); - $this->assertEquals(0, $_SERVER['_curl'][CURLOPT_HEADER]); - $this->assertEquals(150, $_SERVER['_curl'][CURLOPT_CONNECTTIMEOUT]); - $this->assertInstanceOf('Closure', $_SERVER['_curl'][CURLOPT_HEADERFUNCTION]); + self::assertSame('testing', $_SERVER['_curl'][CURLOPT_POSTFIELDS]); + self::assertEquals(0, $_SERVER['_curl'][CURLOPT_RETURNTRANSFER]); + self::assertEquals(0, $_SERVER['_curl'][CURLOPT_HEADER]); + self::assertSame(150, $_SERVER['_curl'][CURLOPT_CONNECTTIMEOUT]); + self::assertInstanceOf('Closure', $_SERVER['_curl'][CURLOPT_HEADERFUNCTION]); if (defined('CURLOPT_PROTOCOLS')) { - $this->assertEquals( + self::assertSame( CURLPROTO_HTTP | CURLPROTO_HTTPS, $_SERVER['_curl'][CURLOPT_PROTOCOLS] ); } - $this->assertContains('Expect:', $_SERVER['_curl'][CURLOPT_HTTPHEADER]); - $this->assertContains('Accept:', $_SERVER['_curl'][CURLOPT_HTTPHEADER]); - $this->assertContains('Content-Type:', $_SERVER['_curl'][CURLOPT_HTTPHEADER]); - $this->assertContains('Hi: 123', $_SERVER['_curl'][CURLOPT_HTTPHEADER]); - $this->assertContains('Host: 127.0.0.1:8126', $_SERVER['_curl'][CURLOPT_HTTPHEADER]); + self::assertContains('Expect:', $_SERVER['_curl'][CURLOPT_HTTPHEADER]); + self::assertContains('Accept:', $_SERVER['_curl'][CURLOPT_HTTPHEADER]); + self::assertContains('Content-Type:', $_SERVER['_curl'][CURLOPT_HTTPHEADER]); + self::assertContains('Hi: 123', $_SERVER['_curl'][CURLOPT_HTTPHEADER]); + self::assertContains('Host: 127.0.0.1:8126', $_SERVER['_curl'][CURLOPT_HTTPHEADER]); } public function testSendsHeadRequests() @@ -79,12 +79,12 @@ public function testSendsHeadRequests() $a = new Handler\CurlMultiHandler(); $response = $a(new Psr7\Request('HEAD', Server::$url), []); $response->wait(); - $this->assertEquals(true, $_SERVER['_curl'][CURLOPT_NOBODY]); + self::assertEquals(true, $_SERVER['_curl'][CURLOPT_NOBODY]); $checks = [CURLOPT_WRITEFUNCTION, CURLOPT_READFUNCTION, CURLOPT_INFILE]; foreach ($checks as $check) { - $this->assertArrayNotHasKey($check, $_SERVER['_curl']); + self::assertArrayNotHasKey($check, $_SERVER['_curl']); } - $this->assertEquals('HEAD', Server::received()[0]->getMethod()); + self::assertEquals('HEAD', Server::received()[0]->getMethod()); } public function testCanAddCustomCurlOptions() @@ -94,7 +94,7 @@ public function testCanAddCustomCurlOptions() $a = new Handler\CurlMultiHandler(); $req = new Psr7\Request('GET', Server::$url); $a($req, ['curl' => [CURLOPT_LOW_SPEED_LIMIT => 10]]); - $this->assertEquals(10, $_SERVER['_curl'][CURLOPT_LOW_SPEED_LIMIT]); + self::assertEquals(10, $_SERVER['_curl'][CURLOPT_LOW_SPEED_LIMIT]); } public function testCanChangeCurlOptions() @@ -104,7 +104,7 @@ public function testCanChangeCurlOptions() $a = new Handler\CurlMultiHandler(); $req = new Psr7\Request('GET', Server::$url); $a($req, ['curl' => [CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_0]]); - $this->assertEquals(CURL_HTTP_VERSION_1_0, $_SERVER['_curl'][CURLOPT_HTTP_VERSION]); + self::assertEquals(CURL_HTTP_VERSION_1_0, $_SERVER['_curl'][CURLOPT_HTTP_VERSION]); } /** @@ -121,42 +121,42 @@ public function testCanSetVerifyToFile() { $f = new Handler\CurlFactory(3); $f->create(new Psr7\Request('GET', 'http://foo.com'), ['verify' => __FILE__]); - $this->assertEquals(__FILE__, $_SERVER['_curl'][CURLOPT_CAINFO]); - $this->assertEquals(2, $_SERVER['_curl'][CURLOPT_SSL_VERIFYHOST]); - $this->assertEquals(true, $_SERVER['_curl'][CURLOPT_SSL_VERIFYPEER]); + self::assertEquals(__FILE__, $_SERVER['_curl'][CURLOPT_CAINFO]); + self::assertEquals(2, $_SERVER['_curl'][CURLOPT_SSL_VERIFYHOST]); + self::assertEquals(true, $_SERVER['_curl'][CURLOPT_SSL_VERIFYPEER]); } public function testCanSetVerifyToDir() { $f = new Handler\CurlFactory(3); $f->create(new Psr7\Request('GET', 'http://foo.com'), ['verify' => __DIR__]); - $this->assertEquals(__DIR__, $_SERVER['_curl'][CURLOPT_CAPATH]); - $this->assertEquals(2, $_SERVER['_curl'][CURLOPT_SSL_VERIFYHOST]); - $this->assertEquals(true, $_SERVER['_curl'][CURLOPT_SSL_VERIFYPEER]); + self::assertEquals(__DIR__, $_SERVER['_curl'][CURLOPT_CAPATH]); + self::assertEquals(2, $_SERVER['_curl'][CURLOPT_SSL_VERIFYHOST]); + self::assertEquals(true, $_SERVER['_curl'][CURLOPT_SSL_VERIFYPEER]); } public function testAddsVerifyAsTrue() { $f = new Handler\CurlFactory(3); $f->create(new Psr7\Request('GET', Server::$url), ['verify' => true]); - $this->assertEquals(2, $_SERVER['_curl'][CURLOPT_SSL_VERIFYHOST]); - $this->assertEquals(true, $_SERVER['_curl'][CURLOPT_SSL_VERIFYPEER]); - $this->assertArrayNotHasKey(CURLOPT_CAINFO, $_SERVER['_curl']); + self::assertEquals(2, $_SERVER['_curl'][CURLOPT_SSL_VERIFYHOST]); + self::assertEquals(true, $_SERVER['_curl'][CURLOPT_SSL_VERIFYPEER]); + self::assertArrayNotHasKey(CURLOPT_CAINFO, $_SERVER['_curl']); } public function testCanDisableVerify() { $f = new Handler\CurlFactory(3); $f->create(new Psr7\Request('GET', Server::$url), ['verify' => false]); - $this->assertEquals(0, $_SERVER['_curl'][CURLOPT_SSL_VERIFYHOST]); - $this->assertEquals(false, $_SERVER['_curl'][CURLOPT_SSL_VERIFYPEER]); + self::assertEquals(0, $_SERVER['_curl'][CURLOPT_SSL_VERIFYHOST]); + self::assertEquals(false, $_SERVER['_curl'][CURLOPT_SSL_VERIFYPEER]); } public function testAddsProxy() { $f = new Handler\CurlFactory(3); $f->create(new Psr7\Request('GET', Server::$url), ['proxy' => 'http://bar.com']); - $this->assertEquals('http://bar.com', $_SERVER['_curl'][CURLOPT_PROXY]); + self::assertEquals('http://bar.com', $_SERVER['_curl'][CURLOPT_PROXY]); } public function testAddsViaScheme() @@ -165,7 +165,7 @@ public function testAddsViaScheme() $f->create(new Psr7\Request('GET', Server::$url), [ 'proxy' => ['http' => 'http://bar.com', 'https' => 'https://t'], ]); - $this->assertEquals('http://bar.com', $_SERVER['_curl'][CURLOPT_PROXY]); + self::assertEquals('http://bar.com', $_SERVER['_curl'][CURLOPT_PROXY]); $this->checkNoProxyForHost('http://test.test.com', ['test.test.com'], false); $this->checkNoProxyForHost('http://test.test.com', ['.test.com'], false); $this->checkNoProxyForHost('http://test.test.com', ['*.test.com'], true); @@ -184,9 +184,9 @@ private function checkNoProxyForHost($url, $noProxy, $assertUseProxy) ], ]); if ($assertUseProxy) { - $this->assertArrayHasKey(CURLOPT_PROXY, $_SERVER['_curl']); + self::assertArrayHasKey(CURLOPT_PROXY, $_SERVER['_curl']); } else { - $this->assertArrayNotHasKey(CURLOPT_PROXY, $_SERVER['_curl']); + self::assertArrayNotHasKey(CURLOPT_PROXY, $_SERVER['_curl']); } } @@ -205,15 +205,23 @@ public function testAddsSslKey() { $f = new Handler\CurlFactory(3); $f->create(new Psr7\Request('GET', Server::$url), ['ssl_key' => __FILE__]); - $this->assertEquals(__FILE__, $_SERVER['_curl'][CURLOPT_SSLKEY]); + self::assertEquals(__FILE__, $_SERVER['_curl'][CURLOPT_SSLKEY]); } public function testAddsSslKeyWithPassword() { $f = new Handler\CurlFactory(3); $f->create(new Psr7\Request('GET', Server::$url), ['ssl_key' => [__FILE__, 'test']]); - $this->assertEquals(__FILE__, $_SERVER['_curl'][CURLOPT_SSLKEY]); - $this->assertEquals('test', $_SERVER['_curl'][CURLOPT_SSLKEYPASSWD]); + self::assertEquals(__FILE__, $_SERVER['_curl'][CURLOPT_SSLKEY]); + self::assertEquals('test', $_SERVER['_curl'][CURLOPT_SSLKEYPASSWD]); + } + + public function testAddsSslKeyWhenUsingArraySyntaxButNoPassword() + { + $f = new Handler\CurlFactory(3); + $f->create(new Psr7\Request('GET', Server::$url), ['ssl_key' => [__FILE__]]); + + self::assertEquals(__FILE__, $_SERVER['_curl'][CURLOPT_SSLKEY]); } /** @@ -230,15 +238,15 @@ public function testAddsCert() { $f = new Handler\CurlFactory(3); $f->create(new Psr7\Request('GET', Server::$url), ['cert' => __FILE__]); - $this->assertEquals(__FILE__, $_SERVER['_curl'][CURLOPT_SSLCERT]); + self::assertEquals(__FILE__, $_SERVER['_curl'][CURLOPT_SSLCERT]); } public function testAddsCertWithPassword() { $f = new Handler\CurlFactory(3); $f->create(new Psr7\Request('GET', Server::$url), ['cert' => [__FILE__, 'test']]); - $this->assertEquals(__FILE__, $_SERVER['_curl'][CURLOPT_SSLCERT]); - $this->assertEquals('test', $_SERVER['_curl'][CURLOPT_SSLCERTPASSWD]); + self::assertEquals(__FILE__, $_SERVER['_curl'][CURLOPT_SSLCERT]); + self::assertEquals('test', $_SERVER['_curl'][CURLOPT_SSLCERTPASSWD]); } /** @@ -261,8 +269,8 @@ public function testEmitsDebugInfoToStream() $response->wait(); rewind($res); $output = str_replace("\r", '', stream_get_contents($res)); - $this->assertContains("> HEAD / HTTP/1.1", $output); - $this->assertContains("< HTTP/1.1 200", $output); + self::assertContains("> HEAD / HTTP/1.1", $output); + self::assertContains("< HTTP/1.1 200", $output); fclose($res); } @@ -279,9 +287,9 @@ public function testEmitsProgressToFunction() }, ]); $response->wait(); - $this->assertNotEmpty($called); + self::assertNotEmpty($called); foreach ($called as $call) { - $this->assertCount(4, $call); + self::assertCount(4, $call); } } @@ -305,10 +313,10 @@ public function testDecodesGzippedResponses() $request = new Psr7\Request('GET', Server::$url); $response = $handler($request, ['decode_content' => true]); $response = $response->wait(); - $this->assertEquals('test', (string) $response->getBody()); - $this->assertEquals('', $_SERVER['_curl'][CURLOPT_ENCODING]); + self::assertEquals('test', (string) $response->getBody()); + self::assertEquals('', $_SERVER['_curl'][CURLOPT_ENCODING]); $sent = Server::received()[0]; - $this->assertFalse($sent->hasHeader('Accept-Encoding')); + self::assertFalse($sent->hasHeader('Accept-Encoding')); } public function testReportsOriginalSizeAndContentEncodingAfterDecoding() @@ -318,11 +326,11 @@ public function testReportsOriginalSizeAndContentEncodingAfterDecoding() $request = new Psr7\Request('GET', Server::$url); $response = $handler($request, ['decode_content' => true]); $response = $response->wait(); - $this->assertSame( + self::assertSame( 'gzip', $response->getHeaderLine('x-encoded-content-encoding') ); - $this->assertSame( + self::assertSame( strlen(gzencode('test')), (int) $response->getHeaderLine('x-encoded-content-length') ); @@ -335,12 +343,15 @@ public function testDecodesGzippedResponsesWithHeader() $request = new Psr7\Request('GET', Server::$url, ['Accept-Encoding' => 'gzip']); $response = $handler($request, ['decode_content' => true]); $response = $response->wait(); - $this->assertEquals('gzip', $_SERVER['_curl'][CURLOPT_ENCODING]); + self::assertEquals('gzip', $_SERVER['_curl'][CURLOPT_ENCODING]); $sent = Server::received()[0]; - $this->assertEquals('gzip', $sent->getHeaderLine('Accept-Encoding')); - $this->assertEquals('test', (string) $response->getBody()); - $this->assertFalse($response->hasHeader('content-encoding')); - $this->assertTrue(!$response->hasHeader('content-length') || $response->getHeaderLine('content-length') == $response->getBody()->getSize()); + self::assertEquals('gzip', $sent->getHeaderLine('Accept-Encoding')); + self::assertEquals('test', (string) $response->getBody()); + self::assertFalse($response->hasHeader('content-encoding')); + self::assertTrue( + !$response->hasHeader('content-length') || + $response->getHeaderLine('content-length') == $response->getBody()->getSize() + ); } public function testDoesNotForceDecode() @@ -351,8 +362,8 @@ public function testDoesNotForceDecode() $response = $handler($request, ['decode_content' => false]); $response = $response->wait(); $sent = Server::received()[0]; - $this->assertFalse($sent->hasHeader('Accept-Encoding')); - $this->assertEquals($content, (string) $response->getBody()); + self::assertFalse($sent->hasHeader('Accept-Encoding')); + self::assertEquals($content, (string) $response->getBody()); } public function testProtocolVersion() @@ -362,7 +373,7 @@ public function testProtocolVersion() $a = new Handler\CurlMultiHandler(); $request = new Psr7\Request('GET', Server::$url, [], null, '1.0'); $a($request, []); - $this->assertEquals(CURL_HTTP_VERSION_1_0, $_SERVER['_curl'][CURLOPT_HTTP_VERSION]); + self::assertEquals(CURL_HTTP_VERSION_1_0, $_SERVER['_curl'][CURLOPT_HTTP_VERSION]); } public function testSavesToStream() @@ -377,7 +388,7 @@ public function testSavesToStream() ]); $response->wait(); rewind($stream); - $this->assertEquals('test', stream_get_contents($stream)); + self::assertEquals('test', stream_get_contents($stream)); } public function testSavesToGuzzleStream() @@ -391,7 +402,7 @@ public function testSavesToGuzzleStream() 'sink' => $stream, ]); $response->wait(); - $this->assertEquals('test', (string) $stream); + self::assertEquals('test', (string) $stream); } public function testSavesToFileOnDisk() @@ -405,7 +416,7 @@ public function testSavesToFileOnDisk() 'sink' => $tmpfile, ]); $response->wait(); - $this->assertStringEqualsFile($tmpfile, 'test'); + self::assertStringEqualsFile($tmpfile, 'test'); unlink($tmpfile); } @@ -417,9 +428,9 @@ public function testDoesNotAddMultipleContentLengthHeaders() $response = $handler($request, []); $response->wait(); $sent = Server::received()[0]; - $this->assertEquals(3, $sent->getHeaderLine('Content-Length')); - $this->assertFalse($sent->hasHeader('Transfer-Encoding')); - $this->assertEquals('foo', (string) $sent->getBody()); + self::assertEquals(3, $sent->getHeaderLine('Content-Length')); + self::assertFalse($sent->hasHeader('Transfer-Encoding')); + self::assertEquals('foo', (string) $sent->getBody()); } public function testSendsPostWithNoBodyOrDefaultContentType() @@ -431,9 +442,9 @@ public function testSendsPostWithNoBodyOrDefaultContentType() $response = $handler($request, []); $response->wait(); $received = Server::received()[0]; - $this->assertEquals('POST', $received->getMethod()); - $this->assertFalse($received->hasHeader('content-type')); - $this->assertSame('0', $received->getHeaderLine('content-length')); + self::assertEquals('POST', $received->getMethod()); + self::assertFalse($received->hasHeader('content-type')); + self::assertSame('0', $received->getHeaderLine('content-length')); } /** @@ -464,8 +475,12 @@ public function testRetriesWhenBodyCanBeRewound() }; $bd = Psr7\FnStream::decorate(Psr7\stream_for('test'), [ - 'tell' => function () { return 1; }, - 'rewind' => function () use (&$called) { $called = true; } + 'tell' => function () { + return 1; + }, + 'rewind' => function () use (&$called) { + $called = true; + } ]); $factory = new Handler\CurlFactory(1); @@ -473,9 +488,9 @@ public function testRetriesWhenBodyCanBeRewound() $easy = $factory->create($req, []); $res = Handler\CurlFactory::finish($fn, $easy, $factory); $res = $res->wait(); - $this->assertTrue($callHandler); - $this->assertTrue($called); - $this->assertEquals('200', $res->getStatusCode()); + self::assertTrue($callHandler); + self::assertTrue($called); + self::assertEquals('200', $res->getStatusCode()); } /** @@ -494,7 +509,7 @@ public function testFailsWhenRetryMoreThanThreeTimes() $mock = new Handler\MockHandler([$fn, $fn, $fn]); $p = $mock(new Psr7\Request('PUT', Server::$url, [], 'test'), []); $p->wait(false); - $this->assertEquals(3, $call); + self::assertEquals(3, $call); $p->wait(true); } @@ -509,11 +524,11 @@ public function testHandles100Continue() ], 'test'); $handler = new Handler\CurlMultiHandler(); $response = $handler($request, [])->wait(); - $this->assertEquals(200, $response->getStatusCode()); - $this->assertEquals('OK', $response->getReasonPhrase()); - $this->assertEquals('Hello', $response->getHeaderLine('Test')); - $this->assertEquals('4', $response->getHeaderLine('Content-Length')); - $this->assertEquals('test', (string) $response->getBody()); + self::assertSame(200, $response->getStatusCode()); + self::assertSame('OK', $response->getReasonPhrase()); + self::assertSame('Hello', $response->getHeaderLine('Test')); + self::assertSame('4', $response->getHeaderLine('Content-Length')); + self::assertSame('test', (string) $response->getBody()); } /** @@ -528,7 +543,8 @@ public function testCreatesConnectException() $easy->errno = CURLE_COULDNT_CONNECT; $response = $m->invoke( null, - function () {}, + function () { + }, $easy, $factory ); @@ -542,8 +558,8 @@ public function testAddsTimeouts() 'timeout' => 0.1, 'connect_timeout' => 0.2 ]); - $this->assertEquals(100, $_SERVER['_curl'][CURLOPT_TIMEOUT_MS]); - $this->assertEquals(200, $_SERVER['_curl'][CURLOPT_CONNECTTIMEOUT_MS]); + self::assertEquals(100, $_SERVER['_curl'][CURLOPT_TIMEOUT_MS]); + self::assertEquals(200, $_SERVER['_curl'][CURLOPT_CONNECTTIMEOUT_MS]); } public function testAddsStreamingBody() @@ -556,8 +572,8 @@ public function testAddsStreamingBody() ]); $request = new Psr7\Request('PUT', Server::$url, [], $bd); $f->create($request, []); - $this->assertEquals(1, $_SERVER['_curl'][CURLOPT_UPLOAD]); - $this->assertInternalType('callable', $_SERVER['_curl'][CURLOPT_READFUNCTION]); + self::assertEquals(1, $_SERVER['_curl'][CURLOPT_UPLOAD]); + self::assertInternalType('callable', $_SERVER['_curl'][CURLOPT_READFUNCTION]); } /** @@ -579,20 +595,20 @@ public function testClosesIdleHandles() $easy = $f->create($req, []); $h1 = $easy->handle; $f->release($easy); - $this->assertCount(1, $this->readAttribute($f, 'handles')); + self::assertCount(1, self::readAttribute($f, 'handles')); $easy = $f->create($req, []); - $this->assertSame($easy->handle, $h1); + self::assertSame($easy->handle, $h1); $easy2 = $f->create($req, []); $easy3 = $f->create($req, []); $easy4 = $f->create($req, []); $f->release($easy); - $this->assertCount(1, $this->readAttribute($f, 'handles')); + self::assertCount(1, self::readAttribute($f, 'handles')); $f->release($easy2); - $this->assertCount(2, $this->readAttribute($f, 'handles')); + self::assertCount(2, self::readAttribute($f, 'handles')); $f->release($easy3); - $this->assertCount(3, $this->readAttribute($f, 'handles')); + self::assertCount(3, self::readAttribute($f, 'handles')); $f->release($easy4); - $this->assertCount(3, $this->readAttribute($f, 'handles')); + self::assertCount(3, self::readAttribute($f, 'handles')); } /** @@ -638,7 +654,7 @@ public function testSuccessfullyCallsOnHeadersBeforeWritingToSink() $stream = Psr7\stream_for(); $stream = Psr7\FnStream::decorate($stream, [ 'write' => function ($data) use ($stream, &$got) { - $this->assertNotNull($got); + self::assertNotNull($got); return $stream->write($data); } ]); @@ -648,14 +664,14 @@ public function testSuccessfullyCallsOnHeadersBeforeWritingToSink() 'sink' => $stream, 'on_headers' => function (ResponseInterface $res) use (&$got) { $got = $res; - $this->assertEquals('bar', $res->getHeaderLine('X-Foo')); + self::assertEquals('bar', $res->getHeaderLine('X-Foo')); } ]); $response = $promise->wait(); - $this->assertEquals(200, $response->getStatusCode()); - $this->assertEquals('bar', $response->getHeaderLine('X-Foo')); - $this->assertEquals('abc 123', (string) $response->getBody()); + self::assertSame(200, $response->getStatusCode()); + self::assertSame('bar', $response->getHeaderLine('X-Foo')); + self::assertSame('abc 123', (string) $response->getBody()); } public function testInvokesOnStatsOnSuccess() @@ -671,17 +687,18 @@ public function testInvokesOnStatsOnSuccess() } ]); $response = $promise->wait(); - $this->assertEquals(200, $response->getStatusCode()); - $this->assertEquals(200, $gotStats->getResponse()->getStatusCode()); - $this->assertEquals( + self::assertSame(200, $response->getStatusCode()); + self::assertSame(200, $gotStats->getResponse()->getStatusCode()); + self::assertSame( Server::$url, (string) $gotStats->getEffectiveUri() ); - $this->assertEquals( + self::assertSame( Server::$url, (string) $gotStats->getRequest()->getUri() ); - $this->assertGreaterThan(0, $gotStats->getTransferTime()); + self::assertGreaterThan(0, $gotStats->getTransferTime()); + self::assertArrayHasKey('appconnect_time', $gotStats->getHandlerStats()); } public function testInvokesOnStatsOnError() @@ -697,24 +714,25 @@ public function testInvokesOnStatsOnError() } ]); $promise->wait(false); - $this->assertFalse($gotStats->hasResponse()); - $this->assertEquals( + self::assertFalse($gotStats->hasResponse()); + self::assertSame( 'http://127.0.0.1:123', - $gotStats->getEffectiveUri() + (string) $gotStats->getEffectiveUri() ); - $this->assertEquals( + self::assertSame( 'http://127.0.0.1:123', - $gotStats->getRequest()->getUri() + (string) $gotStats->getRequest()->getUri() ); - $this->assertInternalType('float', $gotStats->getTransferTime()); - $this->assertInternalType('int', $gotStats->getHandlerErrorData()); + self::assertInternalType('float', $gotStats->getTransferTime()); + self::assertInternalType('int', $gotStats->getHandlerErrorData()); + self::assertArrayHasKey('appconnect_time', $gotStats->getHandlerStats()); } public function testRewindsBodyIfPossible() { $body = Psr7\stream_for(str_repeat('x', 1024 * 1024 * 2)); $body->seek(1024 * 1024); - $this->assertSame(1024 * 1024, $body->tell()); + self::assertSame(1024 * 1024, $body->tell()); $req = new Psr7\Request('POST', 'https://www.example.com', [ 'Content-Length' => 1024 * 1024 * 2, @@ -722,7 +740,7 @@ public function testRewindsBodyIfPossible() $factory = new CurlFactory(1); $factory->create($req, []); - $this->assertSame(0, $body->tell()); + self::assertSame(0, $body->tell()); } public function testDoesNotRewindUnseekableBody() @@ -730,7 +748,7 @@ public function testDoesNotRewindUnseekableBody() $body = Psr7\stream_for(str_repeat('x', 1024 * 1024 * 2)); $body->seek(1024 * 1024); $body = new Psr7\NoSeekStream($body); - $this->assertSame(1024 * 1024, $body->tell()); + self::assertSame(1024 * 1024, $body->tell()); $req = new Psr7\Request('POST', 'https://www.example.com', [ 'Content-Length' => 1024 * 1024, @@ -738,6 +756,15 @@ public function testDoesNotRewindUnseekableBody() $factory = new CurlFactory(1); $factory->create($req, []); - $this->assertSame(1024 * 1024, $body->tell()); + self::assertSame(1024 * 1024, $body->tell()); + } + + public function testRelease() + { + $factory = new CurlFactory(1); + $easyHandle = new EasyHandle(); + $easyHandle->handle = curl_init(); + + self::assertEmpty($factory->release($easyHandle)); } } diff --git a/tests/Handler/CurlHandlerTest.php b/tests/Handler/CurlHandlerTest.php index 8c380fa3b..189a0a966 100644 --- a/tests/Handler/CurlHandlerTest.php +++ b/tests/Handler/CurlHandlerTest.php @@ -7,6 +7,7 @@ use GuzzleHttp\Psr7\Request; use GuzzleHttp\Psr7\Response; use GuzzleHttp\Tests\Server; +use GuzzleHttp\Utils; use PHPUnit\Framework\TestCase; /** @@ -37,8 +38,8 @@ public function testReusesHandles() Server::enqueue([$response, $response]); $a = new CurlHandler(); $request = new Request('GET', Server::$url); - $a($request, []); - $a($request, []); + self::assertInstanceOf('GuzzleHttp\Promise\FulfilledPromise', $a($request, [])); + self::assertInstanceOf('GuzzleHttp\Promise\FulfilledPromise', $a($request, [])); } public function testDoesSleep() @@ -47,9 +48,9 @@ public function testDoesSleep() Server::enqueue([$response]); $a = new CurlHandler(); $request = new Request('GET', Server::$url); - $s = microtime(true); + $s = Utils::currentTime(); $a($request, ['delay' => 0.1])->wait(); - $this->assertGreaterThan(0.0001, microtime(true) - $s); + self::assertGreaterThan(0.0001, Utils::currentTime() - $s); } public function testCreatesCurlErrorsWithContext() @@ -60,10 +61,10 @@ public function testCreatesCurlErrorsWithContext() $p = $handler($request, ['timeout' => 0.001, 'connect_timeout' => 0.001]) ->otherwise(function (ConnectException $e) use (&$called) { $called = true; - $this->assertArrayHasKey('errno', $e->getHandlerContext()); + self::assertArrayHasKey('errno', $e->getHandlerContext()); }); $p->wait(); - $this->assertTrue($called); + self::assertTrue($called); } public function testUsesContentLengthWhenOverInMemorySize() @@ -80,7 +81,7 @@ public function testUsesContentLengthWhenOverInMemorySize() ); $handler($request, [])->wait(); $received = Server::received()[0]; - $this->assertEquals(1000000, $received->getHeaderLine('Content-Length')); - $this->assertFalse($received->hasHeader('Transfer-Encoding')); + self::assertEquals(1000000, $received->getHeaderLine('Content-Length')); + self::assertFalse($received->hasHeader('Transfer-Encoding')); } } diff --git a/tests/Handler/CurlMultiHandlerTest.php b/tests/Handler/CurlMultiHandlerTest.php index e9b140651..8e2d499ac 100644 --- a/tests/Handler/CurlMultiHandlerTest.php +++ b/tests/Handler/CurlMultiHandlerTest.php @@ -5,17 +5,41 @@ use GuzzleHttp\Psr7\Request; use GuzzleHttp\Psr7\Response; use GuzzleHttp\Tests\Server; +use GuzzleHttp\Utils; use PHPUnit\Framework\TestCase; class CurlMultiHandlerTest extends TestCase { + public function setUp() + { + $_SERVER['curl_test'] = true; + unset($_SERVER['_curl_multi']); + } + + public function tearDown() + { + unset($_SERVER['_curl_multi'], $_SERVER['curl_test']); + } + + public function testCanAddCustomCurlOptions() + { + Server::flush(); + Server::enqueue([new Response()]); + $a = new CurlMultiHandler(['options' => [ + CURLMOPT_MAXCONNECTS => 5, + ]]); + $request = new Request('GET', Server::$url); + $a($request, []); + self::assertEquals(5, $_SERVER['_curl_multi'][CURLMOPT_MAXCONNECTS]); + } + public function testSendsRequest() { Server::enqueue([new Response()]); $a = new CurlMultiHandler(); $request = new Request('GET', Server::$url); $response = $a($request, [])->wait(); - $this->assertEquals(200, $response->getStatusCode()); + self::assertSame(200, $response->getStatusCode()); } /** @@ -31,7 +55,7 @@ public function testCreatesExceptions() public function testCanSetSelectTimeout() { $a = new CurlMultiHandler(['select_timeout' => 2]); - $this->assertEquals(2, $this->readAttribute($a, 'selectTimeout')); + self::assertEquals(2, self::readAttribute($a, 'selectTimeout')); } public function testCanCancel() @@ -46,6 +70,10 @@ public function testCanCancel() $response->cancel(); $responses[] = $response; } + + foreach ($responses as $r) { + self::assertSame('rejected', $response->getState()); + } } public function testCannotCancelFinished() @@ -56,6 +84,7 @@ public function testCannotCancelFinished() $response = $a(new Request('GET', Server::$url), []); $response->wait(); $response->cancel(); + self::assertSame('fulfilled', $response->getState()); } public function testDelaysConcurrently() @@ -63,10 +92,24 @@ public function testDelaysConcurrently() Server::flush(); Server::enqueue([new Response()]); $a = new CurlMultiHandler(); - $expected = microtime(true) + (100 / 1000); + $expected = Utils::currentTime() + (100 / 1000); $response = $a(new Request('GET', Server::$url), ['delay' => 100]); $response->wait(); - $this->assertGreaterThanOrEqual($expected, microtime(true)); + self::assertGreaterThanOrEqual($expected, Utils::currentTime()); + } + + public function testUsesTimeoutEnvironmentVariables() + { + $a = new CurlMultiHandler(); + + //default if no options are given and no environment variable is set + self::assertEquals(1, self::readAttribute($a, 'selectTimeout')); + + putenv("GUZZLE_CURL_SELECT_TIMEOUT=3"); + $a = new CurlMultiHandler(); + $selectTimeout = getenv('GUZZLE_CURL_SELECT_TIMEOUT'); + //Handler reads from the environment if no options are given + self::assertEquals($selectTimeout, self::readAttribute($a, 'selectTimeout')); } /** diff --git a/tests/Handler/MockHandlerTest.php b/tests/Handler/MockHandlerTest.php index 94a5c4a20..b63ee46be 100644 --- a/tests/Handler/MockHandlerTest.php +++ b/tests/Handler/MockHandlerTest.php @@ -19,19 +19,19 @@ public function testReturnsMockResponse() $mock = new MockHandler([$res]); $request = new Request('GET', 'http://example.com'); $p = $mock($request, []); - $this->assertSame($res, $p->wait()); + self::assertSame($res, $p->wait()); } public function testIsCountable() { $res = new Response(); $mock = new MockHandler([$res, $res]); - $this->assertCount(2, $mock); + self::assertCount(2, $mock); } public function testEmptyHandlerIsCountable() { - $this->assertCount(0, new MockHandler()); + self::assertCount(0, new MockHandler()); } /** @@ -52,9 +52,9 @@ public function testCanQueueExceptions() $p = $mock($request, []); try { $p->wait(); - $this->fail(); + self::fail(); } catch (\Exception $e2) { - $this->assertSame($e, $e2); + self::assertSame($e, $e2); } } @@ -64,21 +64,21 @@ public function testCanGetLastRequestAndOptions() $mock = new MockHandler([$res]); $request = new Request('GET', 'http://example.com'); $mock($request, ['foo' => 'bar']); - $this->assertSame($request, $mock->getLastRequest()); - $this->assertEquals(['foo' => 'bar'], $mock->getLastOptions()); + self::assertSame($request, $mock->getLastRequest()); + self::assertSame(['foo' => 'bar'], $mock->getLastOptions()); } public function testSinkFilename() { - $filename = sys_get_temp_dir().'/mock_test_'.uniqid(); + $filename = sys_get_temp_dir() . '/mock_test_' . uniqid(); $res = new Response(200, [], 'TEST CONTENT'); $mock = new MockHandler([$res]); $request = new Request('GET', '/'); $p = $mock($request, ['sink' => $filename]); $p->wait(); - $this->assertFileExists($filename); - $this->assertStringEqualsFile($filename, 'TEST CONTENT'); + self::assertFileExists($filename); + self::assertStringEqualsFile($filename, 'TEST CONTENT'); unlink($filename); } @@ -93,8 +93,8 @@ public function testSinkResource() $p = $mock($request, ['sink' => $file]); $p->wait(); - $this->assertFileExists($meta['uri']); - $this->assertStringEqualsFile($meta['uri'], 'TEST CONTENT'); + self::assertFileExists($meta['uri']); + self::assertStringEqualsFile($meta['uri'], 'TEST CONTENT'); } public function testSinkStream() @@ -106,18 +106,20 @@ public function testSinkStream() $p = $mock($request, ['sink' => $stream]); $p->wait(); - $this->assertFileExists($stream->getMetadata('uri')); - $this->assertStringEqualsFile($stream->getMetadata('uri'), 'TEST CONTENT'); + self::assertFileExists($stream->getMetadata('uri')); + self::assertStringEqualsFile($stream->getMetadata('uri'), 'TEST CONTENT'); } public function testCanEnqueueCallables() { $r = new Response(); - $fn = function ($req, $o) use ($r) { return $r; }; + $fn = function ($req, $o) use ($r) { + return $r; + }; $mock = new MockHandler([$fn]); $request = new Request('GET', 'http://example.com'); $p = $mock($request, ['foo' => 'bar']); - $this->assertSame($r, $p->wait()); + self::assertSame($r, $p->wait()); } /** @@ -157,17 +159,19 @@ public function testInvokesOnFulfilled() }); $request = new Request('GET', 'http://example.com'); $mock($request, [])->wait(); - $this->assertSame($res, $c); + self::assertSame($res, $c); } public function testInvokesOnRejected() { $e = new \Exception('a'); $c = null; - $mock = new MockHandler([$e], null, function ($v) use (&$c) { $c = $v; }); + $mock = new MockHandler([$e], null, function ($v) use (&$c) { + $c = $v; + }); $request = new Request('GET', 'http://example.com'); $mock($request, [])->wait(false); - $this->assertSame($e, $c); + self::assertSame($e, $c); } /** @@ -196,29 +200,62 @@ public function testInvokesOnStatsFunctionForResponse() $res = new Response(); $mock = new MockHandler([$res]); $request = new Request('GET', 'http://example.com'); + /** @var TransferStats|null $stats */ $stats = null; $onStats = function (TransferStats $s) use (&$stats) { $stats = $s; }; $p = $mock($request, ['on_stats' => $onStats]); $p->wait(); - $this->assertSame($res, $stats->getResponse()); - $this->assertSame($request, $stats->getRequest()); + self::assertSame($res, $stats->getResponse()); + self::assertSame($request, $stats->getRequest()); } public function testInvokesOnStatsFunctionForError() { $e = new \Exception('a'); $c = null; - $mock = new MockHandler([$e], null, function ($v) use (&$c) { $c = $v; }); + $mock = new MockHandler([$e], null, function ($v) use (&$c) { + $c = $v; + }); $request = new Request('GET', 'http://example.com'); + + /** @var TransferStats|null $stats */ $stats = null; $onStats = function (TransferStats $s) use (&$stats) { $stats = $s; }; $mock($request, ['on_stats' => $onStats])->wait(false); - $this->assertSame($e, $stats->getHandlerErrorData()); - $this->assertNull($stats->getResponse()); - $this->assertSame($request, $stats->getRequest()); + self::assertSame($e, $stats->getHandlerErrorData()); + self::assertNull($stats->getResponse()); + self::assertSame($request, $stats->getRequest()); + } + + public function testTransferTime() + { + $e = new \Exception('a'); + $c = null; + $mock = new MockHandler([$e], null, function ($v) use (&$c) { + $c = $v; + }); + $request = new Request('GET', 'http://example.com'); + $stats = null; + $onStats = function (TransferStats $s) use (&$stats) { + $stats = $s; + }; + $mock($request, [ 'on_stats' => $onStats, 'transfer_time' => 0.4 ])->wait(false); + self::assertEquals(0.4, $stats->getTransferTime()); + } + + public function testResetQueue() + { + $mock = new MockHandler([new Response(200), new Response(204)]); + self::assertCount(2, $mock); + + $mock->reset(); + self::assertEmpty($mock); + + $mock->append(new Response(500)); + self::assertCount(1, $mock); } } diff --git a/tests/Handler/ProxyTest.php b/tests/Handler/ProxyTest.php index 34236afd3..c5e8b1e5a 100644 --- a/tests/Handler/ProxyTest.php +++ b/tests/Handler/ProxyTest.php @@ -15,44 +15,60 @@ class ProxyTest extends TestCase public function testSendsToNonSync() { $a = $b = null; - $m1 = new MockHandler([function ($v) use (&$a) { $a = $v; }]); - $m2 = new MockHandler([function ($v) use (&$b) { $b = $v; }]); + $m1 = new MockHandler([function ($v) use (&$a) { + $a = $v; + }]); + $m2 = new MockHandler([function ($v) use (&$b) { + $b = $v; + }]); $h = Proxy::wrapSync($m1, $m2); $h(new Request('GET', 'http://foo.com'), []); - $this->assertNotNull($a); - $this->assertNull($b); + self::assertNotNull($a); + self::assertNull($b); } public function testSendsToSync() { $a = $b = null; - $m1 = new MockHandler([function ($v) use (&$a) { $a = $v; }]); - $m2 = new MockHandler([function ($v) use (&$b) { $b = $v; }]); + $m1 = new MockHandler([function ($v) use (&$a) { + $a = $v; + }]); + $m2 = new MockHandler([function ($v) use (&$b) { + $b = $v; + }]); $h = Proxy::wrapSync($m1, $m2); $h(new Request('GET', 'http://foo.com'), [RequestOptions::SYNCHRONOUS => true]); - $this->assertNull($a); - $this->assertNotNull($b); + self::assertNull($a); + self::assertNotNull($b); } public function testSendsToStreaming() { $a = $b = null; - $m1 = new MockHandler([function ($v) use (&$a) { $a = $v; }]); - $m2 = new MockHandler([function ($v) use (&$b) { $b = $v; }]); + $m1 = new MockHandler([function ($v) use (&$a) { + $a = $v; + }]); + $m2 = new MockHandler([function ($v) use (&$b) { + $b = $v; + }]); $h = Proxy::wrapStreaming($m1, $m2); $h(new Request('GET', 'http://foo.com'), []); - $this->assertNotNull($a); - $this->assertNull($b); + self::assertNotNull($a); + self::assertNull($b); } public function testSendsToNonStreaming() { $a = $b = null; - $m1 = new MockHandler([function ($v) use (&$a) { $a = $v; }]); - $m2 = new MockHandler([function ($v) use (&$b) { $b = $v; }]); + $m1 = new MockHandler([function ($v) use (&$a) { + $a = $v; + }]); + $m2 = new MockHandler([function ($v) use (&$b) { + $b = $v; + }]); $h = Proxy::wrapStreaming($m1, $m2); $h(new Request('GET', 'http://foo.com'), ['stream' => true]); - $this->assertNull($a); - $this->assertNotNull($b); + self::assertNull($a); + self::assertNotNull($b); } } diff --git a/tests/Handler/StreamHandlerTest.php b/tests/Handler/StreamHandlerTest.php index 208c48893..674a4ad7f 100644 --- a/tests/Handler/StreamHandlerTest.php +++ b/tests/Handler/StreamHandlerTest.php @@ -4,14 +4,15 @@ use GuzzleHttp\Exception\ConnectException; use GuzzleHttp\Handler\StreamHandler; use GuzzleHttp\Psr7; +use GuzzleHttp\Psr7\FnStream; use GuzzleHttp\Psr7\Request; use GuzzleHttp\Psr7\Response; -use GuzzleHttp\Psr7\FnStream; use GuzzleHttp\RequestOptions; use GuzzleHttp\Tests\Server; use GuzzleHttp\TransferStats; -use Psr\Http\Message\ResponseInterface; +use GuzzleHttp\Utils; use PHPUnit\Framework\TestCase; +use Psr\Http\Message\ResponseInterface; /** * @covers \GuzzleHttp\Handler\StreamHandler @@ -37,16 +38,16 @@ public function testReturnsResponseForSuccessfulRequest() new Request('GET', Server::$url, ['Foo' => 'Bar']), [] )->wait(); - $this->assertEquals(200, $response->getStatusCode()); - $this->assertEquals('OK', $response->getReasonPhrase()); - $this->assertEquals('Bar', $response->getHeaderLine('Foo')); - $this->assertEquals('8', $response->getHeaderLine('Content-Length')); - $this->assertEquals('hi there', (string) $response->getBody()); + self::assertSame(200, $response->getStatusCode()); + self::assertSame('OK', $response->getReasonPhrase()); + self::assertSame('Bar', $response->getHeaderLine('Foo')); + self::assertSame('8', $response->getHeaderLine('Content-Length')); + self::assertSame('hi there', (string) $response->getBody()); $sent = Server::received()[0]; - $this->assertEquals('GET', $sent->getMethod()); - $this->assertEquals('/', $sent->getUri()->getPath()); - $this->assertEquals('127.0.0.1:8126', $sent->getHeaderLine('Host')); - $this->assertEquals('Bar', $sent->getHeaderLine('foo')); + self::assertSame('GET', $sent->getMethod()); + self::assertSame('/', $sent->getUri()->getPath()); + self::assertSame('127.0.0.1:8126', $sent->getHeaderLine('Host')); + self::assertSame('Bar', $sent->getHeaderLine('foo')); } /** @@ -72,20 +73,20 @@ public function testStreamAttributeKeepsStreamOpen() 'test' ); $response = $handler($request, ['stream' => true])->wait(); - $this->assertEquals(200, $response->getStatusCode()); - $this->assertEquals('OK', $response->getReasonPhrase()); - $this->assertEquals('8', $response->getHeaderLine('Content-Length')); + self::assertSame(200, $response->getStatusCode()); + self::assertSame('OK', $response->getReasonPhrase()); + self::assertSame('8', $response->getHeaderLine('Content-Length')); $body = $response->getBody(); $stream = $body->detach(); - $this->assertInternalType('resource', $stream); - $this->assertEquals('http', stream_get_meta_data($stream)['wrapper_type']); - $this->assertEquals('hi there', stream_get_contents($stream)); + self::assertInternalType('resource', $stream); + self::assertSame('http', stream_get_meta_data($stream)['wrapper_type']); + self::assertSame('hi there', stream_get_contents($stream)); fclose($stream); $sent = Server::received()[0]; - $this->assertEquals('PUT', $sent->getMethod()); - $this->assertEquals('http://127.0.0.1:8126/foo?baz=bar', (string) $sent->getUri()); - $this->assertEquals('Bar', $sent->getHeaderLine('Foo')); - $this->assertEquals('test', (string) $sent->getBody()); + self::assertSame('PUT', $sent->getMethod()); + self::assertSame('http://127.0.0.1:8126/foo?baz=bar', (string) $sent->getUri()); + self::assertSame('Bar', $sent->getHeaderLine('Foo')); + self::assertSame('test', (string) $sent->getBody()); } public function testDrainsResponseIntoTempStream() @@ -96,8 +97,8 @@ public function testDrainsResponseIntoTempStream() $response = $handler($request, [])->wait(); $body = $response->getBody(); $stream = $body->detach(); - $this->assertEquals('php://temp', stream_get_meta_data($stream)['uri']); - $this->assertEquals('hi', fread($stream, 2)); + self::assertSame('php://temp', stream_get_meta_data($stream)['uri']); + self::assertSame('hi', fread($stream, 2)); fclose($stream); } @@ -109,9 +110,9 @@ public function testDrainsResponseIntoSaveToBody() $request = new Request('GET', Server::$url); $response = $handler($request, ['sink' => $r])->wait(); $body = $response->getBody()->detach(); - $this->assertEquals('php://temp', stream_get_meta_data($body)['uri']); - $this->assertEquals('hi', fread($body, 2)); - $this->assertEquals(' there', stream_get_contents($r)); + self::assertSame('php://temp', stream_get_meta_data($body)['uri']); + self::assertSame('hi', fread($body, 2)); + self::assertSame(' there', stream_get_contents($r)); fclose($r); } @@ -123,8 +124,8 @@ public function testDrainsResponseIntoSaveToBodyAtPath() $request = new Request('GET', Server::$url); $response = $handler($request, ['sink' => $tmpfname])->wait(); $body = $response->getBody(); - $this->assertEquals($tmpfname, $body->getMetadata('uri')); - $this->assertEquals('hi', $body->read(2)); + self::assertSame($tmpfname, $body->getMetadata('uri')); + self::assertSame('hi', $body->read(2)); $body->close(); unlink($tmpfname); } @@ -138,8 +139,8 @@ public function testDrainsResponseIntoSaveToBodyAtNonExistentPath() $request = new Request('GET', Server::$url); $response = $handler($request, ['sink' => $tmpfname])->wait(); $body = $response->getBody(); - $this->assertEquals($tmpfname, $body->getMetadata('uri')); - $this->assertEquals('hi', $body->read(2)); + self::assertSame($tmpfname, $body->getMetadata('uri')); + self::assertSame('hi', $body->read(2)); $body->close(); unlink($tmpfname); } @@ -158,7 +159,7 @@ public function testDrainsResponseAndReadsOnlyContentLengthBytes() $response = $handler($request, [])->wait(); $body = $response->getBody(); $stream = $body->detach(); - $this->assertEquals('hi there', stream_get_contents($stream)); + self::assertSame('hi there', stream_get_contents($stream)); fclose($stream); } @@ -177,7 +178,7 @@ public function testDoesNotDrainWhenHeadRequest() $response = $handler($request, [])->wait(); $body = $response->getBody(); $stream = $body->detach(); - $this->assertEquals('', stream_get_contents($stream)); + self::assertSame('', stream_get_contents($stream)); fclose($stream); } @@ -194,9 +195,9 @@ public function testAutomaticallyDecompressGzip() $handler = new StreamHandler(); $request = new Request('GET', Server::$url); $response = $handler($request, ['decode_content' => true])->wait(); - $this->assertEquals('test', (string) $response->getBody()); - $this->assertFalse($response->hasHeader('content-encoding')); - $this->assertTrue(!$response->hasHeader('content-length') || $response->getHeaderLine('content-length') == $response->getBody()->getSize()); + self::assertSame('test', (string) $response->getBody()); + self::assertFalse($response->hasHeader('content-encoding')); + self::assertTrue(!$response->hasHeader('content-length') || $response->getHeaderLine('content-length') == $response->getBody()->getSize()); } public function testReportsOriginalSizeAndContentEncodingAfterDecoding() @@ -213,11 +214,11 @@ public function testReportsOriginalSizeAndContentEncodingAfterDecoding() $request = new Request('GET', Server::$url); $response = $handler($request, ['decode_content' => true])->wait(); - $this->assertSame( + self::assertSame( 'gzip', $response->getHeaderLine('x-encoded-content-encoding') ); - $this->assertSame( + self::assertSame( strlen($content), (int) $response->getHeaderLine('x-encoded-content-length') ); @@ -236,9 +237,9 @@ public function testDoesNotForceGzipDecode() $handler = new StreamHandler(); $request = new Request('GET', Server::$url); $response = $handler($request, ['decode_content' => false])->wait(); - $this->assertSame($content, (string) $response->getBody()); - $this->assertEquals('gzip', $response->getHeaderLine('content-encoding')); - $this->assertEquals(strlen($content), $response->getHeaderLine('content-length')); + self::assertSame($content, (string) $response->getBody()); + self::assertSame('gzip', $response->getHeaderLine('content-encoding')); + self::assertEquals(strlen($content), $response->getHeaderLine('content-length')); } public function testProtocolVersion() @@ -247,7 +248,7 @@ public function testProtocolVersion() $handler = new StreamHandler(); $request = new Request('GET', Server::$url, [], null, '1.0'); $handler($request, []); - $this->assertEquals('1.0', Server::received()[0]->getProtocolVersion()); + self::assertSame('1.0', Server::received()[0]->getProtocolVersion()); } protected function getSendResult(array $opts) @@ -275,7 +276,7 @@ public function testAddsProxyByProtocol() $url = rtrim($url, '/'); $res = $this->getSendResult(['proxy' => ['http' => $url]]); $opts = stream_context_get_options($res->getBody()->detach()); - $this->assertEquals($url, $opts['http']['proxy']); + self::assertSame($url, $opts['http']['proxy']); } public function testAddsProxyButHonorsNoProxy() @@ -286,14 +287,14 @@ public function testAddsProxyButHonorsNoProxy() 'no' => ['*'] ]]); $opts = stream_context_get_options($res->getBody()->detach()); - $this->assertTrue(empty($opts['http']['proxy'])); + self::assertArrayNotHasKey('proxy', $opts['http']); } public function testAddsTimeout() { $res = $this->getSendResult(['stream' => true, 'timeout' => 200]); $opts = stream_context_get_options($res->getBody()->detach()); - $this->assertEquals(200, $opts['http']['timeout']); + self::assertEquals(200, $opts['http']['timeout']); } /** @@ -307,7 +308,8 @@ public function testVerifiesVerifyIsValidIfPath() public function testVerifyCanBeDisabled() { - $this->getSendResult(['verify' => false]); + $handler = $this->getSendResult(['verify' => false]); + self::assertInstanceOf('GuzzleHttp\Psr7\Response', $handler); } /** @@ -324,10 +326,10 @@ public function testVerifyCanBeSetToPath() $path = $path = \GuzzleHttp\default_ca_bundle(); $res = $this->getSendResult(['verify' => $path]); $opts = stream_context_get_options($res->getBody()->detach()); - $this->assertTrue($opts['ssl']['verify_peer']); - $this->assertTrue($opts['ssl']['verify_peer_name']); - $this->assertEquals($path, $opts['ssl']['cafile']); - $this->assertFileExists($opts['ssl']['cafile']); + self::assertTrue($opts['ssl']['verify_peer']); + self::assertTrue($opts['ssl']['verify_peer_name']); + self::assertSame($path, $opts['ssl']['cafile']); + self::assertFileExists($opts['ssl']['cafile']); } public function testUsesSystemDefaultBundle() @@ -336,7 +338,9 @@ public function testUsesSystemDefaultBundle() $res = $this->getSendResult(['verify' => true]); $opts = stream_context_get_options($res->getBody()->detach()); if (PHP_VERSION_ID < 50600) { - $this->assertEquals($path, $opts['ssl']['cafile']); + self::assertSame($path, $opts['ssl']['cafile']); + } else { + self::assertArrayNotHasKey('cafile', $opts['ssl']); } } @@ -354,8 +358,8 @@ public function testCanSetPasswordWhenSettingCert() $path = __FILE__; $res = $this->getSendResult(['cert' => [$path, 'foo']]); $opts = stream_context_get_options($res->getBody()->detach()); - $this->assertEquals($path, $opts['ssl']['local_cert']); - $this->assertEquals('foo', $opts['ssl']['passphrase']); + self::assertSame($path, $opts['ssl']['local_cert']); + self::assertSame('foo', $opts['ssl']['passphrase']); } public function testDebugAttributeWritesToStream() @@ -365,9 +369,9 @@ public function testDebugAttributeWritesToStream() $this->getSendResult(['debug' => $f]); fseek($f, 0); $contents = stream_get_contents($f); - $this->assertContains(' [CONNECT]', $contents); - $this->assertContains(' [FILE_SIZE_IS]', $contents); - $this->assertContains(' [PROGRESS]', $contents); + self::assertContains(' [CONNECT]', $contents); + self::assertContains(' [FILE_SIZE_IS]', $contents); + self::assertContains(' [PROGRESS]', $contents); } public function testDebugAttributeWritesStreamInfoToBuffer() @@ -376,15 +380,17 @@ public function testDebugAttributeWritesStreamInfoToBuffer() $this->queueRes(); $buffer = fopen('php://temp', 'r+'); $this->getSendResult([ - 'progress' => function () use (&$called) { $called = true; }, + 'progress' => function () use (&$called) { + $called = true; + }, 'debug' => $buffer, ]); fseek($buffer, 0); $contents = stream_get_contents($buffer); - $this->assertContains(' [CONNECT]', $contents); - $this->assertContains(' [FILE_SIZE_IS] message: "Content-Length: 8"', $contents); - $this->assertContains(' [PROGRESS] bytes_max: "8"', $contents); - $this->assertTrue($called); + self::assertContains(' [CONNECT]', $contents); + self::assertContains(' [FILE_SIZE_IS] message: "Content-Length: 8"', $contents); + self::assertContains(' [PROGRESS] bytes_max: "8"', $contents); + self::assertTrue($called); } public function testEmitsProgressInformation() @@ -396,9 +402,9 @@ public function testEmitsProgressInformation() $called[] = func_get_args(); }, ]); - $this->assertNotEmpty($called); - $this->assertEquals(8, $called[0][0]); - $this->assertEquals(0, $called[0][1]); + self::assertNotEmpty($called); + self::assertEquals(8, $called[0][0]); + self::assertEquals(0, $called[0][1]); } public function testEmitsProgressInformationAndDebugInformation() @@ -412,11 +418,11 @@ public function testEmitsProgressInformationAndDebugInformation() $called[] = func_get_args(); }, ]); - $this->assertNotEmpty($called); - $this->assertEquals(8, $called[0][0]); - $this->assertEquals(0, $called[0][1]); + self::assertNotEmpty($called); + self::assertEquals(8, $called[0][0]); + self::assertEquals(0, $called[0][1]); rewind($buffer); - $this->assertNotEmpty(stream_get_contents($buffer)); + self::assertNotEmpty(stream_get_contents($buffer)); fclose($buffer); } @@ -437,10 +443,10 @@ public function testPerformsShallowMergeOfCustomContextOptions() ], ]); $opts = stream_context_get_options($res->getBody()->detach()); - $this->assertEquals('HEAD', $opts['http']['method']); - $this->assertTrue($opts['http']['request_fulluri']); - $this->assertEquals('127.0.0.1:0', $opts['socket']['bindto']); - $this->assertFalse($opts['ssl']['verify_peer']); + self::assertSame('HEAD', $opts['http']['method']); + self::assertTrue($opts['http']['request_fulluri']); + self::assertSame('127.0.0.1:0', $opts['socket']['bindto']); + self::assertFalse($opts['ssl']['verify_peer']); } /** @@ -459,8 +465,8 @@ public function testDoesNotAddContentTypeByDefault() $request = new Request('PUT', Server::$url, ['Content-Length' => 3], 'foo'); $handler($request, []); $req = Server::received()[0]; - $this->assertEquals('', $req->getHeaderLine('Content-Type')); - $this->assertEquals(3, $req->getHeaderLine('Content-Length')); + self::assertEquals('', $req->getHeaderLine('Content-Type')); + self::assertEquals(3, $req->getHeaderLine('Content-Length')); } public function testAddsContentLengthByDefault() @@ -470,7 +476,7 @@ public function testAddsContentLengthByDefault() $request = new Request('PUT', Server::$url, [], 'foo'); $handler($request, []); $req = Server::received()[0]; - $this->assertEquals(3, $req->getHeaderLine('Content-Length')); + self::assertEquals(3, $req->getHeaderLine('Content-Length')); } public function testAddsContentLengthEvenWhenEmpty() @@ -480,7 +486,7 @@ public function testAddsContentLengthEvenWhenEmpty() $request = new Request('PUT', Server::$url, [], ''); $handler($request, []); $req = Server::received()[0]; - $this->assertEquals(0, $req->getHeaderLine('Content-Length')); + self::assertEquals(0, $req->getHeaderLine('Content-Length')); } public function testSupports100Continue() @@ -491,10 +497,10 @@ public function testSupports100Continue() $request = new Request('PUT', Server::$url, ['Expect' => '100-Continue'], 'test'); $handler = new StreamHandler(); $response = $handler($request, [])->wait(); - $this->assertEquals(200, $response->getStatusCode()); - $this->assertEquals('Hello', $response->getHeaderLine('Test')); - $this->assertEquals('4', $response->getHeaderLine('Content-Length')); - $this->assertEquals('test', (string) $response->getBody()); + self::assertSame(200, $response->getStatusCode()); + self::assertSame('Hello', $response->getHeaderLine('Test')); + self::assertSame('4', $response->getHeaderLine('Content-Length')); + self::assertSame('test', (string) $response->getBody()); } public function testDoesSleep() @@ -503,9 +509,9 @@ public function testDoesSleep() Server::enqueue([$response]); $a = new StreamHandler(); $request = new Request('GET', Server::$url); - $s = microtime(true); + $s = Utils::currentTime(); $a($request, ['delay' => 0.1])->wait(); - $this->assertGreaterThan(0.0001, microtime(true) - $s); + self::assertGreaterThan(0.0001, Utils::currentTime() - $s); } /** @@ -551,7 +557,7 @@ public function testSuccessfullyCallsOnHeadersBeforeWritingToSink() $stream = Psr7\stream_for(); $stream = FnStream::decorate($stream, [ 'write' => function ($data) use ($stream, &$got) { - $this->assertNotNull($got); + self::assertNotNull($got); return $stream->write($data); } ]); @@ -561,14 +567,14 @@ public function testSuccessfullyCallsOnHeadersBeforeWritingToSink() 'sink' => $stream, 'on_headers' => function (ResponseInterface $res) use (&$got) { $got = $res; - $this->assertEquals('bar', $res->getHeaderLine('X-Foo')); + self::assertSame('bar', $res->getHeaderLine('X-Foo')); } ]); $response = $promise->wait(); - $this->assertEquals(200, $response->getStatusCode()); - $this->assertEquals('bar', $response->getHeaderLine('X-Foo')); - $this->assertEquals('abc 123', (string) $response->getBody()); + self::assertSame(200, $response->getStatusCode()); + self::assertSame('bar', $response->getHeaderLine('X-Foo')); + self::assertSame('abc 123', (string) $response->getBody()); } public function testInvokesOnStatsOnSuccess() @@ -584,17 +590,17 @@ public function testInvokesOnStatsOnSuccess() } ]); $response = $promise->wait(); - $this->assertEquals(200, $response->getStatusCode()); - $this->assertEquals(200, $gotStats->getResponse()->getStatusCode()); - $this->assertEquals( + self::assertSame(200, $response->getStatusCode()); + self::assertSame(200, $gotStats->getResponse()->getStatusCode()); + self::assertSame( Server::$url, (string) $gotStats->getEffectiveUri() ); - $this->assertEquals( + self::assertSame( Server::$url, (string) $gotStats->getRequest()->getUri() ); - $this->assertGreaterThan(0, $gotStats->getTransferTime()); + self::assertGreaterThan(0, $gotStats->getTransferTime()); } public function testInvokesOnStatsOnError() @@ -610,17 +616,17 @@ public function testInvokesOnStatsOnError() } ]); $promise->wait(false); - $this->assertFalse($gotStats->hasResponse()); - $this->assertEquals( + self::assertFalse($gotStats->hasResponse()); + self::assertSame( 'http://127.0.0.1:123', (string) $gotStats->getEffectiveUri() ); - $this->assertEquals( + self::assertSame( 'http://127.0.0.1:123', (string) $gotStats->getRequest()->getUri() ); - $this->assertInternalType('float', $gotStats->getTransferTime()); - $this->assertInstanceOf( + self::assertInternalType('float', $gotStats->getTransferTime()); + self::assertInstanceOf( ConnectException::class, $gotStats->getHandlerErrorData() ); @@ -638,7 +644,7 @@ public function testStreamIgnoresZeroTimeout() 'timeout' => 0 ]); $response = $promise->wait(); - $this->assertEquals(200, $response->getStatusCode()); + self::assertSame(200, $response->getStatusCode()); } public function testDrainsResponseAndReadsAllContentWhenContentLengthIsZero() @@ -655,7 +661,7 @@ public function testDrainsResponseAndReadsAllContentWhenContentLengthIsZero() $response = $handler($request, [])->wait(); $body = $response->getBody(); $stream = $body->detach(); - $this->assertEquals('hi there... This has a lot of data!', stream_get_contents($stream)); + self::assertSame('hi there... This has a lot of data!', stream_get_contents($stream)); fclose($stream); } @@ -670,14 +676,14 @@ public function testHonorsReadTimeout() RequestOptions::STREAM => true, ] )->wait(); - $this->assertEquals(200, $response->getStatusCode()); - $this->assertEquals('OK', $response->getReasonPhrase()); + self::assertSame(200, $response->getStatusCode()); + self::assertSame('OK', $response->getReasonPhrase()); $body = $response->getBody()->detach(); $line = fgets($body); - $this->assertEquals("sleeping 60 seconds ...\n", $line); + self::assertSame("sleeping 60 seconds ...\n", $line); $line = fgets($body); - $this->assertFalse($line); - $this->assertTrue(stream_get_meta_data($body)['timed_out']); - $this->assertFalse(feof($body)); + self::assertFalse($line); + self::assertTrue(stream_get_meta_data($body)['timed_out']); + self::assertFalse(feof($body)); } } diff --git a/tests/HandlerStackTest.php b/tests/HandlerStackTest.php index 1d86c356f..af7ba8c37 100644 --- a/tests/HandlerStackTest.php +++ b/tests/HandlerStackTest.php @@ -12,15 +12,21 @@ class HandlerStackTest extends TestCase { public function testSetsHandlerInCtor() { - $f = function () {}; - $m1 = function () {}; + $f = function () { + }; + $m1 = function () { + }; $h = new HandlerStack($f, [$m1]); - $this->assertTrue($h->hasHandler()); + self::assertTrue($h->hasHandler()); } + /** + * @doesNotPerformAssertions + */ public function testCanSetDifferentHandlerAfterConstruction() { - $f = function () {}; + $f = function () { + }; $h = new HandlerStack(); $h->setHandler($f); $h->resolve(); @@ -44,8 +50,8 @@ public function testPushInOrder() $builder->push($meths[3]); $builder->push($meths[4]); $composed = $builder->resolve(); - $this->assertEquals('Hello - test123', $composed('test')); - $this->assertEquals( + self::assertSame('Hello - test123', $composed('test')); + self::assertSame( [['a', 'test'], ['b', 'test1'], ['c', 'test12']], $meths[0] ); @@ -60,8 +66,8 @@ public function testUnshiftsInReverseOrder() $builder->unshift($meths[3]); $builder->unshift($meths[4]); $composed = $builder->resolve(); - $this->assertEquals('Hello - test321', $composed('test')); - $this->assertEquals( + self::assertSame('Hello - test321', $composed('test')); + self::assertSame( [['c', 'test'], ['b', 'test3'], ['a', 'test32']], $meths[0] ); @@ -79,7 +85,7 @@ public function testCanRemoveMiddlewareByInstance() $builder->push($meths[2]); $builder->remove($meths[3]); $composed = $builder->resolve(); - $this->assertEquals('Hello - test1131', $composed('test')); + self::assertSame('Hello - test1131', $composed('test')); } public function testCanPrintMiddleware() @@ -92,15 +98,15 @@ public function testCanPrintMiddleware() $builder->push([$this, 'bar']); $builder->push(__CLASS__ . '::' . 'foo'); $lines = explode("\n", (string) $builder); - $this->assertContains("> 4) Name: 'a', Function: callable(", $lines[0]); - $this->assertContains("> 3) Name: '', Function: callable(GuzzleHttp\\Tests\\HandlerStackTest::foo)", $lines[1]); - $this->assertContains("> 2) Name: '', Function: callable(['GuzzleHttp\\Tests\\HandlerStackTest', 'bar'])", $lines[2]); - $this->assertContains("> 1) Name: '', Function: callable(GuzzleHttp\\Tests\\HandlerStackTest::foo)", $lines[3]); - $this->assertContains("< 0) Handler: callable(", $lines[4]); - $this->assertContains("< 1) Name: '', Function: callable(GuzzleHttp\\Tests\\HandlerStackTest::foo)", $lines[5]); - $this->assertContains("< 2) Name: '', Function: callable(['GuzzleHttp\\Tests\\HandlerStackTest', 'bar'])", $lines[6]); - $this->assertContains("< 3) Name: '', Function: callable(GuzzleHttp\\Tests\\HandlerStackTest::foo)", $lines[7]); - $this->assertContains("< 4) Name: 'a', Function: callable(", $lines[8]); + self::assertContains("> 4) Name: 'a', Function: callable(", $lines[0]); + self::assertContains("> 3) Name: '', Function: callable(GuzzleHttp\\Tests\\HandlerStackTest::foo)", $lines[1]); + self::assertContains("> 2) Name: '', Function: callable(['GuzzleHttp\\Tests\\HandlerStackTest', 'bar'])", $lines[2]); + self::assertContains("> 1) Name: '', Function: callable(GuzzleHttp\\Tests\\HandlerStackTest::foo)", $lines[3]); + self::assertContains("< 0) Handler: callable(", $lines[4]); + self::assertContains("< 1) Name: '', Function: callable(GuzzleHttp\\Tests\\HandlerStackTest::foo)", $lines[5]); + self::assertContains("< 2) Name: '', Function: callable(['GuzzleHttp\\Tests\\HandlerStackTest', 'bar'])", $lines[6]); + self::assertContains("< 3) Name: '', Function: callable(GuzzleHttp\\Tests\\HandlerStackTest::foo)", $lines[7]); + self::assertContains("< 4) Name: 'a', Function: callable(", $lines[8]); } public function testCanAddBeforeByName() @@ -113,10 +119,10 @@ public function testCanAddBeforeByName() $builder->before('baz', $meths[4], 'bar'); $builder->before('baz', $meths[4], 'qux'); $lines = explode("\n", (string) $builder); - $this->assertContains('> 4) Name: \'bar\'', $lines[0]); - $this->assertContains('> 3) Name: \'qux\'', $lines[1]); - $this->assertContains('> 2) Name: \'baz\'', $lines[2]); - $this->assertContains('> 1) Name: \'foo\'', $lines[3]); + self::assertContains('> 4) Name: \'bar\'', $lines[0]); + self::assertContains('> 3) Name: \'qux\'', $lines[1]); + self::assertContains('> 2) Name: \'baz\'', $lines[2]); + self::assertContains('> 1) Name: \'foo\'', $lines[3]); } /** @@ -125,7 +131,8 @@ public function testCanAddBeforeByName() public function testEnsuresHandlerExistsByName() { $builder = new HandlerStack(); - $builder->before('foo', function () {}); + $builder->before('foo', function () { + }); } public function testCanAddAfterByName() @@ -138,10 +145,10 @@ public function testCanAddAfterByName() $builder->after('a', $meths[4], 'c'); $builder->after('b', $meths[4], 'd'); $lines = explode("\n", (string) $builder); - $this->assertContains('4) Name: \'a\'', $lines[0]); - $this->assertContains('3) Name: \'c\'', $lines[1]); - $this->assertContains('2) Name: \'b\'', $lines[2]); - $this->assertContains('1) Name: \'d\'', $lines[3]); + self::assertContains('4) Name: \'a\'', $lines[0]); + self::assertContains('3) Name: \'c\'', $lines[1]); + self::assertContains('2) Name: \'b\'', $lines[2]); + self::assertContains('1) Name: \'d\'', $lines[3]); } public function testPicksUpCookiesFromRedirects() @@ -160,10 +167,10 @@ public function testPicksUpCookiesFromRedirects() 'allow_redirects' => true, 'cookies' => $jar ])->wait(); - $this->assertEquals(200, $response->getStatusCode()); + self::assertSame(200, $response->getStatusCode()); $lastRequest = $mock->getLastRequest(); - $this->assertEquals('http://foo.com/baz', (string) $lastRequest->getUri()); - $this->assertEquals('foo=bar', $lastRequest->getHeaderLine('Cookie')); + self::assertSame('http://foo.com/baz', (string) $lastRequest->getUri()); + self::assertSame('foo=bar', $lastRequest->getHeaderLine('Cookie')); } private function getFunctions() @@ -198,6 +205,10 @@ private function getFunctions() return [&$calls, $handler, $a, $b, $c]; } - public static function foo() {} - public function bar () {} + public static function foo() + { + } + public function bar() + { + } } diff --git a/tests/InternalUtilsTest.php b/tests/InternalUtilsTest.php new file mode 100644 index 000000000..b465ff046 --- /dev/null +++ b/tests/InternalUtilsTest.php @@ -0,0 +1,21 @@ +getHost()); + } +} diff --git a/tests/MessageFormatterTest.php b/tests/MessageFormatterTest.php index 1e1f5471b..44d4e5a5d 100644 --- a/tests/MessageFormatterTest.php +++ b/tests/MessageFormatterTest.php @@ -2,10 +2,10 @@ namespace GuzzleHttp\Tests; use GuzzleHttp\Exception\RequestException; +use GuzzleHttp\MessageFormatter; use GuzzleHttp\Psr7; use GuzzleHttp\Psr7\Request; use GuzzleHttp\Psr7\Response; -use GuzzleHttp\MessageFormatter; use PHPUnit\Framework\TestCase; /** @@ -16,9 +16,9 @@ class MessageFormatterTest extends TestCase public function testCreatesWithClfByDefault() { $f = new MessageFormatter(); - $this->assertEquals(MessageFormatter::CLF, $this->readAttribute($f, 'template')); + self::assertEquals(MessageFormatter::CLF, self::readAttribute($f, 'template')); $f = new MessageFormatter(null); - $this->assertEquals(MessageFormatter::CLF, $this->readAttribute($f, 'template')); + self::assertEquals(MessageFormatter::CLF, self::readAttribute($f, 'template')); } public function dateProvider() @@ -38,7 +38,7 @@ public function testFormatsTimestamps($format, $pattern) $f = new MessageFormatter($format); $request = new Request('GET', '/'); $result = $f->format($request); - $this->assertEquals(1, preg_match($pattern, $result)); + self::assertRegExp($pattern, $result); } public function formatProvider() @@ -88,6 +88,6 @@ public function formatProvider() public function testFormatsMessages($template, $args, $result) { $f = new MessageFormatter($template); - $this->assertEquals((string) $result, call_user_func_array(array($f, 'format'), $args)); + self::assertSame((string) $result, call_user_func_array([$f, 'format'], $args)); } } diff --git a/tests/MiddlewareTest.php b/tests/MiddlewareTest.php index 88013c9ef..b80888e95 100644 --- a/tests/MiddlewareTest.php +++ b/tests/MiddlewareTest.php @@ -9,14 +9,12 @@ use GuzzleHttp\MessageFormatter; use GuzzleHttp\Middleware; use GuzzleHttp\Promise\PromiseInterface; -use GuzzleHttp\Psr7; use GuzzleHttp\Psr7\Request; use GuzzleHttp\Psr7\Response; +use PHPUnit\Framework\TestCase; use Psr\Http\Message\RequestInterface; use Psr\Http\Message\ResponseInterface; -use Psr\Log\LoggerInterface; -use Psr\Log\LoggerTrait; -use PHPUnit\Framework\TestCase; +use Psr\Log\Test\TestLogger; class MiddlewareTest extends TestCase { @@ -28,7 +26,7 @@ public function testAddsCookiesToRequests() [ function (RequestInterface $request) { return new Response(200, [ - 'Set-Cookie' => new SetCookie([ + 'Set-Cookie' => (string) new SetCookie([ 'Name' => 'name', 'Value' => 'value', 'Domain' => 'foo.com' @@ -39,7 +37,7 @@ function (RequestInterface $request) { ); $f = $m($h); $f(new Request('GET', 'http://foo.com'), ['cookies' => $jar])->wait(); - $this->assertCount(1, $jar); + self::assertCount(1, $jar); } /** @@ -51,9 +49,9 @@ public function testThrowsExceptionOnHttpClientError() $h = new MockHandler([new Response(404)]); $f = $m($h); $p = $f(new Request('GET', 'http://foo.com'), ['http_errors' => true]); - $this->assertEquals('pending', $p->getState()); + self::assertSame('pending', $p->getState()); $p->wait(); - $this->assertEquals('rejected', $p->getState()); + self::assertSame('rejected', $p->getState()); } /** @@ -65,9 +63,9 @@ public function testThrowsExceptionOnHttpServerError() $h = new MockHandler([new Response(500)]); $f = $m($h); $p = $f(new Request('GET', 'http://foo.com'), ['http_errors' => true]); - $this->assertEquals('pending', $p->getState()); + self::assertSame('pending', $p->getState()); $p->wait(); - $this->assertEquals('rejected', $p->getState()); + self::assertSame('rejected', $p->getState()); } /** @@ -82,13 +80,13 @@ public function testTracksHistory($container) $p2 = $f(new Request('HEAD', 'http://foo.com'), ['headers' => ['foo' => 'baz']]); $p1->wait(); $p2->wait(); - $this->assertCount(2, $container); - $this->assertEquals(200, $container[0]['response']->getStatusCode()); - $this->assertEquals(201, $container[1]['response']->getStatusCode()); - $this->assertEquals('GET', $container[0]['request']->getMethod()); - $this->assertEquals('HEAD', $container[1]['request']->getMethod()); - $this->assertEquals('bar', $container[0]['options']['headers']['foo']); - $this->assertEquals('baz', $container[1]['options']['headers']['foo']); + self::assertCount(2, $container); + self::assertSame(200, $container[0]['response']->getStatusCode()); + self::assertSame(201, $container[1]['response']->getStatusCode()); + self::assertSame('GET', $container[0]['request']->getMethod()); + self::assertSame('HEAD', $container[1]['request']->getMethod()); + self::assertSame('bar', $container[0]['options']['headers']['foo']); + self::assertSame('baz', $container[1]['options']['headers']['foo']); } public function getHistoryUseCases() @@ -107,9 +105,9 @@ public function testTracksHistoryForFailures() $h = new MockHandler([new RequestException('error', $request)]); $f = $m($h); $f($request, [])->wait(false); - $this->assertCount(1, $container); - $this->assertEquals('GET', $container[0]['request']->getMethod()); - $this->assertInstanceOf(RequestException::class, $container[0]['error']); + self::assertCount(1, $container); + self::assertSame('GET', $container[0]['request']->getMethod()); + self::assertInstanceOf(RequestException::class, $container[0]['error']); } public function testTapsBeforeAndAfter() @@ -137,16 +135,16 @@ function (RequestInterface $request, array $options, PromiseInterface $p) use (& $b->push($m); $comp = $b->resolve(); $p = $comp(new Request('GET', 'http://foo.com'), []); - $this->assertEquals('123', implode('', $calls)); - $this->assertInstanceOf(PromiseInterface::class, $p); - $this->assertEquals(200, $p->wait()->getStatusCode()); + self::assertSame('123', implode('', $calls)); + self::assertInstanceOf(PromiseInterface::class, $p); + self::assertSame(200, $p->wait()->getStatusCode()); } public function testMapsRequest() { $h = new MockHandler([ function (RequestInterface $request, array $options) { - $this->assertEquals('foo', $request->getHeaderLine('Bar')); + self::assertSame('foo', $request->getHeaderLine('Bar')); return new Response(200); } ]); @@ -156,7 +154,7 @@ function (RequestInterface $request, array $options) { })); $comp = $stack->resolve(); $p = $comp(new Request('PUT', 'http://www.google.com'), []); - $this->assertInstanceOf(PromiseInterface::class, $p); + self::assertInstanceOf(PromiseInterface::class, $p); } public function testMapsResponse() @@ -169,62 +167,51 @@ public function testMapsResponse() $comp = $stack->resolve(); $p = $comp(new Request('PUT', 'http://www.google.com'), []); $p->wait(); - $this->assertEquals('foo', $p->wait()->getHeaderLine('Bar')); + self::assertSame('foo', $p->wait()->getHeaderLine('Bar')); } public function testLogsRequestsAndResponses() { $h = new MockHandler([new Response(200)]); $stack = new HandlerStack($h); - $logger = new Logger(); + $logger = new TestLogger(); $formatter = new MessageFormatter(); $stack->push(Middleware::log($logger, $formatter)); $comp = $stack->resolve(); $p = $comp(new Request('PUT', 'http://www.google.com'), []); $p->wait(); - $this->assertContains('"PUT / HTTP/1.1" 200', $logger->output); + self::assertCount(1, $logger->records); + self::assertContains('"PUT / HTTP/1.1" 200', $logger->records[0]['message']); } public function testLogsRequestsAndResponsesCustomLevel() { $h = new MockHandler([new Response(200)]); $stack = new HandlerStack($h); - $logger = new Logger(); + $logger = new TestLogger(); $formatter = new MessageFormatter(); $stack->push(Middleware::log($logger, $formatter, 'debug')); $comp = $stack->resolve(); $p = $comp(new Request('PUT', 'http://www.google.com'), []); $p->wait(); - $this->assertContains('"PUT / HTTP/1.1" 200', $logger->output); - $this->assertContains('[debug]', $logger->output); + self::assertCount(1, $logger->records); + self::assertContains('"PUT / HTTP/1.1" 200', $logger->records[0]['message']); + self::assertSame('debug', $logger->records[0]['level']); } public function testLogsRequestsAndErrors() { $h = new MockHandler([new Response(404)]); $stack = new HandlerStack($h); - $logger = new Logger(); + $logger = new TestLogger(); $formatter = new MessageFormatter('{code} {error}'); $stack->push(Middleware::log($logger, $formatter)); $stack->push(Middleware::httpErrors()); $comp = $stack->resolve(); $p = $comp(new Request('PUT', 'http://www.google.com'), ['http_errors' => true]); $p->wait(false); - $this->assertContains('PUT http://www.google.com', $logger->output); - $this->assertContains('404 Not Found', $logger->output); - } -} - -/** - * @internal - */ -class Logger implements LoggerInterface -{ - use LoggerTrait; - public $output; - - public function log($level, $message, array $context = []) - { - $this->output .= "[{$level}] {$message}\n"; + self::assertCount(1, $logger->records); + self::assertContains('PUT http://www.google.com', $logger->records[0]['message']); + self::assertContains('404 Not Found', $logger->records[0]['message']); } } diff --git a/tests/PoolTest.php b/tests/PoolTest.php index 3d3d22589..43a0796e6 100644 --- a/tests/PoolTest.php +++ b/tests/PoolTest.php @@ -1,16 +1,16 @@ promise()->wait(); } + /** + * @doesNotPerformAssertions + */ public function testSendsAndRealizesFuture() { $c = $this->getClient(); @@ -41,11 +44,20 @@ public function testSendsAndRealizesFuture() $p->promise()->wait(); } + /** + * @doesNotPerformAssertions + */ public function testExecutesPendingWhenWaiting() { - $r1 = new Promise(function () use (&$r1) { $r1->resolve(new Response()); }); - $r2 = new Promise(function () use (&$r2) { $r2->resolve(new Response()); }); - $r3 = new Promise(function () use (&$r3) { $r3->resolve(new Response()); }); + $r1 = new Promise(function () use (&$r1) { + $r1->resolve(new Response()); + }); + $r2 = new Promise(function () use (&$r2) { + $r2->resolve(new Response()); + }); + $r3 = new Promise(function () use (&$r3) { + $r3->resolve(new Response()); + }); $handler = new MockHandler([$r1, $r2, $r3]); $c = new Client(['handler' => $handler]); $p = new Pool($c, [ @@ -69,8 +81,8 @@ function (RequestInterface $request) use (&$h) { $opts = ['options' => ['headers' => ['x-foo' => 'bar']]]; $p = new Pool($c, [new Request('GET', 'http://example.com')], $opts); $p->promise()->wait(); - $this->assertCount(1, $h); - $this->assertTrue($h[0]->hasHeader('x-foo')); + self::assertCount(1, $h); + self::assertTrue($h[0]->hasHeader('x-foo')); } public function testCanProvideCallablesThatReturnResponses() @@ -91,8 +103,8 @@ function (RequestInterface $request) use (&$h) { $opts = ['options' => ['headers' => ['x-foo' => 'bar']]]; $p = new Pool($c, [$fn], $opts); $p->promise()->wait(); - $this->assertCount(1, $h); - $this->assertTrue($h[0]->hasHeader('x-foo')); + self::assertCount(1, $h); + self::assertTrue($h[0]->hasHeader('x-foo')); } public function testBatchesResults() @@ -110,12 +122,12 @@ public function testBatchesResults() $handler = HandlerStack::create($mock); $client = new Client(['handler' => $handler]); $results = Pool::batch($client, $requests); - $this->assertCount(4, $results); - $this->assertEquals([0, 1, 2, 3], array_keys($results)); - $this->assertEquals(200, $results[0]->getStatusCode()); - $this->assertEquals(201, $results[1]->getStatusCode()); - $this->assertEquals(202, $results[2]->getStatusCode()); - $this->assertInstanceOf(ClientException::class, $results[3]); + self::assertCount(4, $results); + self::assertSame([0, 1, 2, 3], array_keys($results)); + self::assertSame(200, $results[0]->getStatusCode()); + self::assertSame(201, $results[1]->getStatusCode()); + self::assertSame(202, $results[2]->getStatusCode()); + self::assertInstanceOf(ClientException::class, $results[3]); } public function testBatchesResultsWithCallbacks() @@ -131,17 +143,25 @@ function (RequestInterface $request) { ]); $client = new Client(['handler' => $mock]); $results = Pool::batch($client, $requests, [ - 'fulfilled' => function ($value) use (&$called) { $called = true; } + 'fulfilled' => function ($value) use (&$called) { + $called = true; + } ]); - $this->assertCount(2, $results); - $this->assertTrue($called); + self::assertCount(2, $results); + self::assertTrue($called); } public function testUsesYieldedKeyInFulfilledCallback() { - $r1 = new Promise(function () use (&$r1) { $r1->resolve(new Response()); }); - $r2 = new Promise(function () use (&$r2) { $r2->resolve(new Response()); }); - $r3 = new Promise(function () use (&$r3) { $r3->resolve(new Response()); }); + $r1 = new Promise(function () use (&$r1) { + $r1->resolve(new Response()); + }); + $r2 = new Promise(function () use (&$r2) { + $r2->resolve(new Response()); + }); + $r3 = new Promise(function () use (&$r3) { + $r3->resolve(new Response()); + }); $handler = new MockHandler([$r1, $r2, $r3]); $c = new Client(['handler' => $handler]); $keys = []; @@ -152,11 +172,13 @@ public function testUsesYieldedKeyInFulfilledCallback() ]; $p = new Pool($c, $requests, [ 'pool_size' => 2, - 'fulfilled' => function($res, $index) use (&$keys) { $keys[] = $index; } + 'fulfilled' => function ($res, $index) use (&$keys) { + $keys[] = $index; + } ]); $p->promise()->wait(); - $this->assertCount(3, $keys); - $this->assertSame($keys, array_keys($requests)); + self::assertCount(3, $keys); + self::assertSame($keys, array_keys($requests)); } private function getClient($total = 1) diff --git a/tests/PrepareBodyMiddlewareTest.php b/tests/PrepareBodyMiddlewareTest.php index 1876a00f0..883c0771c 100644 --- a/tests/PrepareBodyMiddlewareTest.php +++ b/tests/PrepareBodyMiddlewareTest.php @@ -9,8 +9,8 @@ use GuzzleHttp\Psr7\FnStream; use GuzzleHttp\Psr7\Request; use GuzzleHttp\Psr7\Response; -use Psr\Http\Message\RequestInterface; use PHPUnit\Framework\TestCase; +use Psr\Http\Message\RequestInterface; class PrepareBodyMiddlewareTest extends TestCase { @@ -33,9 +33,9 @@ public function testAddsContentLengthWhenMissingAndPossible($method, $body) function (RequestInterface $request) use ($body) { $length = strlen($body); if ($length > 0) { - $this->assertEquals($length, $request->getHeaderLine('Content-Length')); + self::assertEquals($length, $request->getHeaderLine('Content-Length')); } else { - $this->assertFalse($request->hasHeader('Content-Length')); + self::assertFalse($request->hasHeader('Content-Length')); } return new Response(200); } @@ -45,20 +45,22 @@ function (RequestInterface $request) use ($body) { $stack->push($m); $comp = $stack->resolve(); $p = $comp(new Request($method, 'http://www.google.com', [], $body), []); - $this->assertInstanceOf(PromiseInterface::class, $p); + self::assertInstanceOf(PromiseInterface::class, $p); $response = $p->wait(); - $this->assertEquals(200, $response->getStatusCode()); + self::assertSame(200, $response->getStatusCode()); } public function testAddsTransferEncodingWhenNoContentLength() { $body = FnStream::decorate(Psr7\stream_for('foo'), [ - 'getSize' => function () { return null; } + 'getSize' => function () { + return null; + } ]); $h = new MockHandler([ function (RequestInterface $request) { - $this->assertFalse($request->hasHeader('Content-Length')); - $this->assertEquals('chunked', $request->getHeaderLine('Transfer-Encoding')); + self::assertFalse($request->hasHeader('Content-Length')); + self::assertSame('chunked', $request->getHeaderLine('Transfer-Encoding')); return new Response(200); } ]); @@ -67,9 +69,9 @@ function (RequestInterface $request) { $stack->push($m); $comp = $stack->resolve(); $p = $comp(new Request('PUT', 'http://www.google.com', [], $body), []); - $this->assertInstanceOf(PromiseInterface::class, $p); + self::assertInstanceOf(PromiseInterface::class, $p); $response = $p->wait(); - $this->assertEquals(200, $response->getStatusCode()); + self::assertSame(200, $response->getStatusCode()); } public function testAddsContentTypeWhenMissingAndPossible() @@ -77,8 +79,8 @@ public function testAddsContentTypeWhenMissingAndPossible() $bd = Psr7\stream_for(fopen(__DIR__ . '/../composer.json', 'r')); $h = new MockHandler([ function (RequestInterface $request) { - $this->assertEquals('application/json', $request->getHeaderLine('Content-Type')); - $this->assertTrue($request->hasHeader('Content-Length')); + self::assertSame('application/json', $request->getHeaderLine('Content-Type')); + self::assertTrue($request->hasHeader('Content-Length')); return new Response(200); } ]); @@ -87,9 +89,9 @@ function (RequestInterface $request) { $stack->push($m); $comp = $stack->resolve(); $p = $comp(new Request('PUT', 'http://www.google.com', [], $bd), []); - $this->assertInstanceOf(PromiseInterface::class, $p); + self::assertInstanceOf(PromiseInterface::class, $p); $response = $p->wait(); - $this->assertEquals(200, $response->getStatusCode()); + self::assertSame(200, $response->getStatusCode()); } public function expectProvider() @@ -111,7 +113,7 @@ public function testAddsExpect($value, $result) $h = new MockHandler([ function (RequestInterface $request) use ($result) { - $this->assertEquals($result, $request->getHeader('Expect')); + self::assertSame($result, $request->getHeader('Expect')); return new Response(200); } ]); @@ -123,9 +125,9 @@ function (RequestInterface $request) use ($result) { $p = $comp(new Request('PUT', 'http://www.google.com', [], $bd), [ 'expect' => $value ]); - $this->assertInstanceOf(PromiseInterface::class, $p); + self::assertInstanceOf(PromiseInterface::class, $p); $response = $p->wait(); - $this->assertEquals(200, $response->getStatusCode()); + self::assertSame(200, $response->getStatusCode()); } public function testIgnoresIfExpectIsPresent() @@ -133,7 +135,7 @@ public function testIgnoresIfExpectIsPresent() $bd = Psr7\stream_for(fopen(__DIR__ . '/../composer.json', 'r')); $h = new MockHandler([ function (RequestInterface $request) { - $this->assertEquals(['Foo'], $request->getHeader('Expect')); + self::assertSame(['Foo'], $request->getHeader('Expect')); return new Response(200); } ]); @@ -146,8 +148,8 @@ function (RequestInterface $request) { new Request('PUT', 'http://www.google.com', ['Expect' => 'Foo'], $bd), ['expect' => true] ); - $this->assertInstanceOf(PromiseInterface::class, $p); + self::assertInstanceOf(PromiseInterface::class, $p); $response = $p->wait(); - $this->assertEquals(200, $response->getStatusCode()); + self::assertSame(200, $response->getStatusCode()); } } diff --git a/tests/RedirectMiddlewareTest.php b/tests/RedirectMiddlewareTest.php index 16e1e2439..73f333a27 100644 --- a/tests/RedirectMiddlewareTest.php +++ b/tests/RedirectMiddlewareTest.php @@ -8,8 +8,8 @@ use GuzzleHttp\Psr7\Request; use GuzzleHttp\Psr7\Response; use GuzzleHttp\RedirectMiddleware; -use Psr\Http\Message\RequestInterface; use PHPUnit\Framework\TestCase; +use Psr\Http\Message\RequestInterface; /** * @covers GuzzleHttp\RedirectMiddleware @@ -25,7 +25,7 @@ public function testIgnoresNonRedirects() $request = new Request('GET', 'http://example.com'); $promise = $handler($request, []); $response = $promise->wait(); - $this->assertEquals(200, $response->getStatusCode()); + self::assertSame(200, $response->getStatusCode()); } public function testIgnoresWhenNoLocation() @@ -37,7 +37,7 @@ public function testIgnoresWhenNoLocation() $request = new Request('GET', 'http://example.com'); $promise = $handler($request, []); $response = $promise->wait(); - $this->assertEquals(304, $response->getStatusCode()); + self::assertSame(304, $response->getStatusCode()); } public function testRedirectsWithAbsoluteUri() @@ -54,8 +54,8 @@ public function testRedirectsWithAbsoluteUri() 'allow_redirects' => ['max' => 2] ]); $response = $promise->wait(); - $this->assertEquals(200, $response->getStatusCode()); - $this->assertEquals('http://test.com', $mock->getLastRequest()->getUri()); + self::assertSame(200, $response->getStatusCode()); + self::assertSame('http://test.com', (string)$mock->getLastRequest()->getUri()); } public function testRedirectsWithRelativeUri() @@ -72,8 +72,8 @@ public function testRedirectsWithRelativeUri() 'allow_redirects' => ['max' => 2] ]); $response = $promise->wait(); - $this->assertEquals(200, $response->getStatusCode()); - $this->assertEquals('http://example.com/foo', $mock->getLastRequest()->getUri()); + self::assertSame(200, $response->getStatusCode()); + self::assertSame('http://example.com/foo', (string)$mock->getLastRequest()->getUri()); } /** @@ -126,7 +126,27 @@ public function testAddsRefererHeader() 'allow_redirects' => ['max' => 2, 'referer' => true] ]); $promise->wait(); - $this->assertEquals( + self::assertSame( + 'http://example.com?a=b', + $mock->getLastRequest()->getHeaderLine('Referer') + ); + } + + public function testAddsRefererHeaderButClearsUserInfo() + { + $mock = new MockHandler([ + new Response(302, ['Location' => 'http://test.com']), + new Response(200) + ]); + $stack = new HandlerStack($mock); + $stack->push(Middleware::redirect()); + $handler = $stack->resolve(); + $request = new Request('GET', 'http://foo:bar@example.com?a=b'); + $promise = $handler($request, [ + 'allow_redirects' => ['max' => 2, 'referer' => true] + ]); + $promise->wait(); + self::assertSame( 'http://example.com?a=b', $mock->getLastRequest()->getHeaderLine('Referer') ); @@ -149,7 +169,7 @@ public function testAddsGuzzleRedirectHeader() 'allow_redirects' => ['track_redirects' => true] ]); $response = $promise->wait(true); - $this->assertEquals( + self::assertSame( [ 'http://example.com', 'http://example.com/foo', @@ -177,12 +197,12 @@ public function testAddsGuzzleRedirectStatusHeader() 'allow_redirects' => ['track_redirects' => true] ]); $response = $promise->wait(true); - $this->assertEquals( + self::assertSame( [ - 301, - 302, - 301, - 302, + '301', + '302', + '301', + '302', ], $response->getHeader(RedirectMiddleware::STATUS_HISTORY_HEADER) ); @@ -202,7 +222,7 @@ public function testDoesNotAddRefererWhenGoingFromHttpsToHttp() 'allow_redirects' => ['max' => 2, 'referer' => true] ]); $promise->wait(); - $this->assertFalse($mock->getLastRequest()->hasHeader('Referer')); + self::assertFalse($mock->getLastRequest()->hasHeader('Referer')); } public function testInvokesOnRedirectForRedirects() @@ -220,42 +240,200 @@ public function testInvokesOnRedirectForRedirects() 'allow_redirects' => [ 'max' => 2, 'on_redirect' => function ($request, $response, $uri) use (&$call) { - $this->assertEquals(302, $response->getStatusCode()); - $this->assertEquals('GET', $request->getMethod()); - $this->assertEquals('http://test.com', (string) $uri); + self::assertSame(302, $response->getStatusCode()); + self::assertSame('GET', $request->getMethod()); + self::assertSame('http://test.com', (string) $uri); $call = true; } ] ]); $promise->wait(); - $this->assertTrue($call); + self::assertTrue($call); } - public function testRemoveAuthorizationHeaderOnRedirect() + /** + * @testWith ["digest"] + * ["ntlm"] + */ + public function testRemoveCurlAuthorizationOptionsOnRedirectCrossHost($auth) { + if (!defined('\CURLOPT_HTTPAUTH')) { + self::markTestSkipped('ext-curl is required for this test'); + } + $mock = new MockHandler([ new Response(302, ['Location' => 'http://test.com']), - function (RequestInterface $request) { - $this->assertFalse($request->hasHeader('Authorization')); + static function (RequestInterface $request, $options) { + self::assertFalse( + isset($options['curl'][\CURLOPT_HTTPAUTH]), + 'curl options still contain CURLOPT_HTTPAUTH entry' + ); + self::assertFalse( + isset($options['curl'][\CURLOPT_USERPWD]), + 'curl options still contain CURLOPT_USERPWD entry' + ); + return new Response(200); + } + ]); + $handler = HandlerStack::create($mock); + $client = new Client(['handler' => $handler]); + $client->get('http://example.com?a=b', ['auth' => ['testuser', 'testpass', $auth]]); + } + + /** + * @testWith ["digest"] + * ["ntlm"] + */ + public function testRemoveCurlAuthorizationOptionsOnRedirectCrossPort($auth) + { + if (!defined('\CURLOPT_HTTPAUTH')) { + self::markTestSkipped('ext-curl is required for this test'); + } + + $mock = new MockHandler([ + new Response(302, ['Location' => 'http://example.com:81/']), + static function (RequestInterface $request, $options) { + self::assertFalse( + isset($options['curl'][\CURLOPT_HTTPAUTH]), + 'curl options still contain CURLOPT_HTTPAUTH entry' + ); + self::assertFalse( + isset($options['curl'][\CURLOPT_USERPWD]), + 'curl options still contain CURLOPT_USERPWD entry' + ); + return new Response(200); + } + ]); + $handler = HandlerStack::create($mock); + $client = new Client(['handler' => $handler]); + $client->get('http://example.com?a=b', ['auth' => ['testuser', 'testpass', $auth]]); + } + + /** + * @testWith ["digest"] + * ["ntlm"] + */ + public function testRemoveCurlAuthorizationOptionsOnRedirectCrossScheme($auth) + { + if (!defined('\CURLOPT_HTTPAUTH')) { + self::markTestSkipped('ext-curl is required for this test'); + } + + $mock = new MockHandler([ + new Response(302, ['Location' => 'http://example.com?a=b']), + static function (RequestInterface $request, $options) { + self::assertFalse( + isset($options['curl'][\CURLOPT_HTTPAUTH]), + 'curl options still contain CURLOPT_HTTPAUTH entry' + ); + self::assertFalse( + isset($options['curl'][\CURLOPT_USERPWD]), + 'curl options still contain CURLOPT_USERPWD entry' + ); + return new Response(200); + } + ]); + $handler = HandlerStack::create($mock); + $client = new Client(['handler' => $handler]); + $client->get('https://example.com?a=b', ['auth' => ['testuser', 'testpass', $auth]]); + } + + /** + * @testWith ["digest"] + * ["ntlm"] + */ + public function testRemoveCurlAuthorizationOptionsOnRedirectCrossSchemeSamePort($auth) + { + if (!defined('\CURLOPT_HTTPAUTH')) { + self::markTestSkipped('ext-curl is required for this test'); + } + + $mock = new MockHandler([ + new Response(302, ['Location' => 'http://example.com:80?a=b']), + static function (RequestInterface $request, $options) { + self::assertFalse( + isset($options['curl'][\CURLOPT_HTTPAUTH]), + 'curl options still contain CURLOPT_HTTPAUTH entry' + ); + self::assertFalse( + isset($options['curl'][\CURLOPT_USERPWD]), + 'curl options still contain CURLOPT_USERPWD entry' + ); return new Response(200); } ]); $handler = HandlerStack::create($mock); $client = new Client(['handler' => $handler]); - $client->get('http://example.com?a=b', ['auth' => ['testuser', 'testpass']]); + $client->get('https://example.com?a=b', ['auth' => ['testuser', 'testpass', $auth]]); } - public function testNotRemoveAuthorizationHeaderOnRedirect() + /** + * @testWith ["digest"] + * ["ntlm"] + */ + public function testNotRemoveCurlAuthorizationOptionsOnRedirect($auth) { + if (!defined('\CURLOPT_HTTPAUTH') || !defined('\CURLOPT_USERPWD')) { + self::markTestSkipped('ext-curl is required for this test'); + } + $mock = new MockHandler([ new Response(302, ['Location' => 'http://example.com/2']), - function (RequestInterface $request) { - $this->assertTrue($request->hasHeader('Authorization')); + static function (RequestInterface $request, $options) { + self::assertTrue( + isset($options['curl'][\CURLOPT_HTTPAUTH]), + 'curl options does not contain expected CURLOPT_HTTPAUTH entry' + ); + self::assertTrue( + isset($options['curl'][\CURLOPT_USERPWD]), + 'curl options does not contain expected CURLOPT_USERPWD entry' + ); + return new Response(200); + } + ]); + $handler = HandlerStack::create($mock); + $client = new Client(['handler' => $handler]); + $client->get('http://example.com?a=b', ['auth' => ['testuser', 'testpass', $auth]]); + } + + public function crossOriginRedirectProvider() + { + return [ + ['http://example.com/123', 'http://example.com/', false], + ['http://example.com/123', 'http://example.com:80/', false], + ['http://example.com:80/123', 'http://example.com/', false], + ['http://example.com:80/123', 'http://example.com:80/', false], + ['http://example.com/123', 'https://example.com/', true], + ['http://example.com/123', 'http://www.example.com/', true], + ['http://example.com/123', 'http://example.com:81/', true], + ['http://example.com:80/123', 'http://example.com:81/', true], + ['https://example.com/123', 'https://example.com/', false], + ['https://example.com/123', 'https://example.com:443/', false], + ['https://example.com:443/123', 'https://example.com/', false], + ['https://example.com:443/123', 'https://example.com:443/', false], + ['https://example.com/123', 'http://example.com/', true], + ['https://example.com/123', 'https://www.example.com/', true], + ['https://example.com/123', 'https://example.com:444/', true], + ['https://example.com:443/123', 'https://example.com:444/', true], + ]; + } + + /** + * @dataProvider crossOriginRedirectProvider + */ + public function testHeadersTreatmentOnRedirect($originalUri, $targetUri, $isCrossOrigin) + { + $mock = new MockHandler([ + new Response(302, ['Location' => $targetUri]), + function (RequestInterface $request) use ($isCrossOrigin) { + self::assertSame(!$isCrossOrigin, $request->hasHeader('Authorization')); + self::assertSame(!$isCrossOrigin, $request->hasHeader('Cookie')); + return new Response(200); } ]); $handler = HandlerStack::create($mock); $client = new Client(['handler' => $handler]); - $client->get('http://example.com?a=b', ['auth' => ['testuser', 'testpass']]); + $client->get($originalUri, ['auth' => ['testuser', 'testpass'], 'headers' => ['Cookie' => 'foo=bar']]); } } diff --git a/tests/RetryMiddlewareTest.php b/tests/RetryMiddlewareTest.php index 063c2bca6..ed23c32ae 100644 --- a/tests/RetryMiddlewareTest.php +++ b/tests/RetryMiddlewareTest.php @@ -22,8 +22,8 @@ public function testRetriesWhenDeciderReturnsTrue() }; $delay = function ($retries, $response) use (&$delayCalls) { $delayCalls++; - $this->assertEquals($retries, $delayCalls); - $this->assertInstanceOf(Response::class, $response); + self::assertSame($retries, $delayCalls); + self::assertInstanceOf(Response::class, $response); return 1; }; $m = Middleware::retry($decider, $delay); @@ -32,19 +32,21 @@ public function testRetriesWhenDeciderReturnsTrue() $c = new Client(['handler' => $f]); $p = $c->sendAsync(new Request('GET', 'http://test.com'), []); $p->wait(); - $this->assertCount(3, $calls); - $this->assertEquals(2, $delayCalls); - $this->assertEquals(202, $p->wait()->getStatusCode()); + self::assertCount(3, $calls); + self::assertSame(2, $delayCalls); + self::assertSame(202, $p->wait()->getStatusCode()); } public function testDoesNotRetryWhenDeciderReturnsFalse() { - $decider = function () { return false; }; + $decider = function () { + return false; + }; $m = Middleware::retry($decider); $h = new MockHandler([new Response(200)]); $c = new Client(['handler' => $m($h)]); $p = $c->sendAsync(new Request('GET', 'http://test.com'), []); - $this->assertEquals(200, $p->wait()->getStatusCode()); + self::assertSame(200, $p->wait()->getStatusCode()); } public function testCanRetryExceptions() @@ -58,22 +60,22 @@ public function testCanRetryExceptions() $h = new MockHandler([new \Exception(), new Response(201)]); $c = new Client(['handler' => $m($h)]); $p = $c->sendAsync(new Request('GET', 'http://test.com'), []); - $this->assertEquals(201, $p->wait()->getStatusCode()); - $this->assertCount(2, $calls); - $this->assertEquals(0, $calls[0][0]); - $this->assertNull($calls[0][2]); - $this->assertInstanceOf('Exception', $calls[0][3]); - $this->assertEquals(1, $calls[1][0]); - $this->assertInstanceOf(Response::class, $calls[1][2]); - $this->assertNull($calls[1][3]); + self::assertSame(201, $p->wait()->getStatusCode()); + self::assertCount(2, $calls); + self::assertSame(0, $calls[0][0]); + self::assertNull($calls[0][2]); + self::assertInstanceOf('Exception', $calls[0][3]); + self::assertSame(1, $calls[1][0]); + self::assertInstanceOf(Response::class, $calls[1][2]); + self::assertNull($calls[1][3]); } public function testBackoffCalculateDelay() { - $this->assertEquals(0, RetryMiddleware::exponentialDelay(0)); - $this->assertEquals(1, RetryMiddleware::exponentialDelay(1)); - $this->assertEquals(2, RetryMiddleware::exponentialDelay(2)); - $this->assertEquals(4, RetryMiddleware::exponentialDelay(3)); - $this->assertEquals(8, RetryMiddleware::exponentialDelay(4)); + self::assertSame(0, RetryMiddleware::exponentialDelay(0)); + self::assertSame(1000, RetryMiddleware::exponentialDelay(1)); + self::assertSame(2000, RetryMiddleware::exponentialDelay(2)); + self::assertSame(4000, RetryMiddleware::exponentialDelay(3)); + self::assertSame(8000, RetryMiddleware::exponentialDelay(4)); } } diff --git a/tests/TransferStatsTest.php b/tests/TransferStatsTest.php index 247e4e86d..f8bbe5316 100644 --- a/tests/TransferStatsTest.php +++ b/tests/TransferStatsTest.php @@ -1,8 +1,8 @@ 'bar'] ); - $this->assertSame($request, $stats->getRequest()); - $this->assertSame($response, $stats->getResponse()); - $this->assertTrue($stats->hasResponse()); - $this->assertEquals(['foo' => 'bar'], $stats->getHandlerStats()); - $this->assertEquals('bar', $stats->getHandlerStat('foo')); - $this->assertSame($request->getUri(), $stats->getEffectiveUri()); - $this->assertEquals(10.5, $stats->getTransferTime()); - $this->assertNull($stats->getHandlerErrorData()); + self::assertSame($request, $stats->getRequest()); + self::assertSame($response, $stats->getResponse()); + self::assertTrue($stats->hasResponse()); + self::assertSame(['foo' => 'bar'], $stats->getHandlerStats()); + self::assertSame('bar', $stats->getHandlerStat('foo')); + self::assertSame($request->getUri(), $stats->getEffectiveUri()); + self::assertEquals(10.5, $stats->getTransferTime()); + self::assertNull($stats->getHandlerErrorData()); } } diff --git a/tests/UriTemplateTest.php b/tests/UriTemplateTest.php index a5f33402b..c06856e91 100644 --- a/tests/UriTemplateTest.php +++ b/tests/UriTemplateTest.php @@ -14,7 +14,7 @@ class UriTemplateTest extends TestCase */ public function templateProvider() { - $params = array( + $params = [ 'var' => 'value', 'hello' => 'Hello World!', 'empty' => '', @@ -22,97 +22,97 @@ public function templateProvider() 'x' => '1024', 'y' => '768', 'null' => null, - 'list' => array('red', 'green', 'blue'), - 'keys' => array( + 'list' => ['red', 'green', 'blue'], + 'keys' => [ "semi" => ';', "dot" => '.', "comma" => ',' - ), - 'empty_keys' => array(), - ); + ], + 'empty_keys' => [], + ]; return array_map(function ($t) use ($params) { $t[] = $params; return $t; - }, array( - array('foo', 'foo'), - array('{var}', 'value'), - array('{hello}', 'Hello%20World%21'), - array('{+var}', 'value'), - array('{+hello}', 'Hello%20World!'), - array('{+path}/here', '/foo/bar/here'), - array('here?ref={+path}', 'here?ref=/foo/bar'), - array('X{#var}', 'X#value'), - array('X{#hello}', 'X#Hello%20World!'), - array('map?{x,y}', 'map?1024,768'), - array('{x,hello,y}', '1024,Hello%20World%21,768'), - array('{+x,hello,y}', '1024,Hello%20World!,768'), - array('{+path,x}/here', '/foo/bar,1024/here'), - array('{#x,hello,y}', '#1024,Hello%20World!,768'), - array('{#path,x}/here', '#/foo/bar,1024/here'), - array('X{.var}', 'X.value'), - array('X{.x,y}', 'X.1024.768'), - array('{/var}', '/value'), - array('{/var,x}/here', '/value/1024/here'), - array('{;x,y}', ';x=1024;y=768'), - array('{;x,y,empty}', ';x=1024;y=768;empty'), - array('{?x,y}', '?x=1024&y=768'), - array('{?x,y,empty}', '?x=1024&y=768&empty='), - array('?fixed=yes{&x}', '?fixed=yes&x=1024'), - array('{&x,y,empty}', '&x=1024&y=768&empty='), - array('{var:3}', 'val'), - array('{var:30}', 'value'), - array('{list}', 'red,green,blue'), - array('{list*}', 'red,green,blue'), - array('{keys}', 'semi,%3B,dot,.,comma,%2C'), - array('{keys*}', 'semi=%3B,dot=.,comma=%2C'), - array('{+path:6}/here', '/foo/b/here'), - array('{+list}', 'red,green,blue'), - array('{+list*}', 'red,green,blue'), - array('{+keys}', 'semi,;,dot,.,comma,,'), - array('{+keys*}', 'semi=;,dot=.,comma=,'), - array('{#path:6}/here', '#/foo/b/here'), - array('{#list}', '#red,green,blue'), - array('{#list*}', '#red,green,blue'), - array('{#keys}', '#semi,;,dot,.,comma,,'), - array('{#keys*}', '#semi=;,dot=.,comma=,'), - array('X{.var:3}', 'X.val'), - array('X{.list}', 'X.red,green,blue'), - array('X{.list*}', 'X.red.green.blue'), - array('X{.keys}', 'X.semi,%3B,dot,.,comma,%2C'), - array('X{.keys*}', 'X.semi=%3B.dot=..comma=%2C'), - array('{/var:1,var}', '/v/value'), - array('{/list}', '/red,green,blue'), - array('{/list*}', '/red/green/blue'), - array('{/list*,path:4}', '/red/green/blue/%2Ffoo'), - array('{/keys}', '/semi,%3B,dot,.,comma,%2C'), - array('{/keys*}', '/semi=%3B/dot=./comma=%2C'), - array('{;hello:5}', ';hello=Hello'), - array('{;list}', ';list=red,green,blue'), - array('{;list*}', ';list=red;list=green;list=blue'), - array('{;keys}', ';keys=semi,%3B,dot,.,comma,%2C'), - array('{;keys*}', ';semi=%3B;dot=.;comma=%2C'), - array('{?var:3}', '?var=val'), - array('{?list}', '?list=red,green,blue'), - array('{?list*}', '?list=red&list=green&list=blue'), - array('{?keys}', '?keys=semi,%3B,dot,.,comma,%2C'), - array('{?keys*}', '?semi=%3B&dot=.&comma=%2C'), - array('{&var:3}', '&var=val'), - array('{&list}', '&list=red,green,blue'), - array('{&list*}', '&list=red&list=green&list=blue'), - array('{&keys}', '&keys=semi,%3B,dot,.,comma,%2C'), - array('{&keys*}', '&semi=%3B&dot=.&comma=%2C'), - array('{.null}', ''), - array('{.null,var}', '.value'), - array('X{.empty_keys*}', 'X'), - array('X{.empty_keys}', 'X'), + }, [ + ['foo', 'foo'], + ['{var}', 'value'], + ['{hello}', 'Hello%20World%21'], + ['{+var}', 'value'], + ['{+hello}', 'Hello%20World!'], + ['{+path}/here', '/foo/bar/here'], + ['here?ref={+path}', 'here?ref=/foo/bar'], + ['X{#var}', 'X#value'], + ['X{#hello}', 'X#Hello%20World!'], + ['map?{x,y}', 'map?1024,768'], + ['{x,hello,y}', '1024,Hello%20World%21,768'], + ['{+x,hello,y}', '1024,Hello%20World!,768'], + ['{+path,x}/here', '/foo/bar,1024/here'], + ['{#x,hello,y}', '#1024,Hello%20World!,768'], + ['{#path,x}/here', '#/foo/bar,1024/here'], + ['X{.var}', 'X.value'], + ['X{.x,y}', 'X.1024.768'], + ['{/var}', '/value'], + ['{/var,x}/here', '/value/1024/here'], + ['{;x,y}', ';x=1024;y=768'], + ['{;x,y,empty}', ';x=1024;y=768;empty'], + ['{?x,y}', '?x=1024&y=768'], + ['{?x,y,empty}', '?x=1024&y=768&empty='], + ['?fixed=yes{&x}', '?fixed=yes&x=1024'], + ['{&x,y,empty}', '&x=1024&y=768&empty='], + ['{var:3}', 'val'], + ['{var:30}', 'value'], + ['{list}', 'red,green,blue'], + ['{list*}', 'red,green,blue'], + ['{keys}', 'semi,%3B,dot,.,comma,%2C'], + ['{keys*}', 'semi=%3B,dot=.,comma=%2C'], + ['{+path:6}/here', '/foo/b/here'], + ['{+list}', 'red,green,blue'], + ['{+list*}', 'red,green,blue'], + ['{+keys}', 'semi,;,dot,.,comma,,'], + ['{+keys*}', 'semi=;,dot=.,comma=,'], + ['{#path:6}/here', '#/foo/b/here'], + ['{#list}', '#red,green,blue'], + ['{#list*}', '#red,green,blue'], + ['{#keys}', '#semi,;,dot,.,comma,,'], + ['{#keys*}', '#semi=;,dot=.,comma=,'], + ['X{.var:3}', 'X.val'], + ['X{.list}', 'X.red,green,blue'], + ['X{.list*}', 'X.red.green.blue'], + ['X{.keys}', 'X.semi,%3B,dot,.,comma,%2C'], + ['X{.keys*}', 'X.semi=%3B.dot=..comma=%2C'], + ['{/var:1,var}', '/v/value'], + ['{/list}', '/red,green,blue'], + ['{/list*}', '/red/green/blue'], + ['{/list*,path:4}', '/red/green/blue/%2Ffoo'], + ['{/keys}', '/semi,%3B,dot,.,comma,%2C'], + ['{/keys*}', '/semi=%3B/dot=./comma=%2C'], + ['{;hello:5}', ';hello=Hello'], + ['{;list}', ';list=red,green,blue'], + ['{;list*}', ';list=red;list=green;list=blue'], + ['{;keys}', ';keys=semi,%3B,dot,.,comma,%2C'], + ['{;keys*}', ';semi=%3B;dot=.;comma=%2C'], + ['{?var:3}', '?var=val'], + ['{?list}', '?list=red,green,blue'], + ['{?list*}', '?list=red&list=green&list=blue'], + ['{?keys}', '?keys=semi,%3B,dot,.,comma,%2C'], + ['{?keys*}', '?semi=%3B&dot=.&comma=%2C'], + ['{&var:3}', '&var=val'], + ['{&list}', '&list=red,green,blue'], + ['{&list*}', '&list=red&list=green&list=blue'], + ['{&keys}', '&keys=semi,%3B,dot,.,comma,%2C'], + ['{&keys*}', '&semi=%3B&dot=.&comma=%2C'], + ['{.null}', ''], + ['{.null,var}', '.value'], + ['X{.empty_keys*}', 'X'], + ['X{.empty_keys}', 'X'], // Test that missing expansions are skipped - array('test{&missing*}', 'test'), + ['test{&missing*}', 'test'], // Test that multiple expansions can be set - array('http://{var}/{var:2}{?keys*}', 'http://value/va?semi=%3B&dot=.&comma=%2C'), + ['http://{var}/{var:2}{?keys*}', 'http://value/va?semi=%3B&dot=.&comma=%2C'], // Test more complex query string stuff - array('http://www.test.com{+path}{?var,keys*}', 'http://www.test.com/foo/bar?var=value&semi=%3B&dot=.&comma=%2C') - )); + ['http://www.test.com{+path}{?var,keys*}', 'http://www.test.com/foo/bar?var=value&semi=%3B&dot=.&comma=%2C'] + ]); } /** @@ -121,41 +121,41 @@ public function templateProvider() public function testExpandsUriTemplates($template, $expansion, $params) { $uri = new UriTemplate(); - $this->assertEquals($expansion, $uri->expand($template, $params)); + self::assertSame($expansion, $uri->expand($template, $params)); } public function expressionProvider() { - return array( - array( - '{+var*}', array( + return [ + [ + '{+var*}', [ 'operator' => '+', - 'values' => array( - array('value' => 'var', 'modifier' => '*') - ) - ), - ), - array( - '{?keys,var,val}', array( + 'values' => [ + ['modifier' => '*', 'value' => 'var'] + ] + ], + ], + [ + '{?keys,var,val}', [ 'operator' => '?', - 'values' => array( - array('value' => 'keys', 'modifier' => ''), - array('value' => 'var', 'modifier' => ''), - array('value' => 'val', 'modifier' => '') - ) - ), - ), - array( - '{+x,hello,y}', array( + 'values' => [ + ['value' => 'keys', 'modifier' => ''], + ['value' => 'var', 'modifier' => ''], + ['value' => 'val', 'modifier' => ''] + ] + ], + ], + [ + '{+x,hello,y}', [ 'operator' => '+', - 'values' => array( - array('value' => 'x', 'modifier' => ''), - array('value' => 'hello', 'modifier' => ''), - array('value' => 'y', 'modifier' => '') - ) - ) - ) - ); + 'values' => [ + ['value' => 'x', 'modifier' => ''], + ['value' => 'hello', 'modifier' => ''], + ['value' => 'y', 'modifier' => ''] + ] + ] + ] + ]; } /** @@ -171,7 +171,7 @@ public function testParsesExpressions($exp, $data) $method->setAccessible(true); $exp = substr($exp, 1, -1); - $this->assertEquals($data, $method->invokeArgs($template, array($exp))); + self::assertSame($data, $method->invokeArgs($template, [$exp])); } /** @@ -181,22 +181,22 @@ public function testAllowsNestedArrayExpansion() { $template = new UriTemplate(); - $result = $template->expand('http://example.com{+path}{/segments}{?query,data*,foo*}', array( + $result = $template->expand('http://example.com{+path}{/segments}{?query,data*,foo*}', [ 'path' => '/foo/bar', - 'segments' => array('one', 'two'), + 'segments' => ['one', 'two'], 'query' => 'test', - 'data' => array( - 'more' => array('fun', 'ice cream') - ), - 'foo' => array( - 'baz' => array( + 'data' => [ + 'more' => ['fun', 'ice cream'] + ], + 'foo' => [ + 'baz' => [ 'bar' => 'fizz', 'test' => 'buzz' - ), + ], 'bam' => 'boo' - ) - )); + ] + ]); - $this->assertEquals('http://example.com/foo/bar/one,two?query=test&more%5B0%5D=fun&more%5B1%5D=ice%20cream&baz%5Bbar%5D=fizz&baz%5Btest%5D=buzz&bam=boo', $result); + self::assertSame('http://example.com/foo/bar/one,two?query=test&more%5B0%5D=fun&more%5B1%5D=ice%20cream&baz%5Bbar%5D=fizz&baz%5Btest%5D=buzz&bam=boo', $result); } } diff --git a/tests/bootstrap.php b/tests/bootstrap.php index 1cf17b6df..1854520e4 100644 --- a/tests/bootstrap.php +++ b/tests/bootstrap.php @@ -1,4 +1,9 @@ assertEquals( + self::assertSame( 'foo/123', GuzzleHttp\uri_template('foo/{bar}', ['bar' => '123']) ); @@ -21,7 +21,7 @@ public function noBodyProvider() public function testProvidesDefaultUserAgent() { $ua = GuzzleHttp\default_user_agent(); - $this->assertEquals(1, preg_match('#^GuzzleHttp/.+ curl/.+ PHP/.+$#', $ua)); + self::assertRegExp('#^GuzzleHttp/.+ curl/.+ PHP/.+$#', $ua); } public function typeProvider() @@ -41,13 +41,13 @@ public function typeProvider() */ public function testDescribesType($input, $output) { - $this->assertEquals($output, GuzzleHttp\describe_type($input)); + self::assertSame($output, GuzzleHttp\describe_type($input)); } public function testParsesHeadersFromLines() { $lines = ['Foo: bar', 'Foo: baz', 'Abc: 123', 'Def: a, b']; - $this->assertEquals([ + self::assertSame([ 'Foo' => ['bar', 'baz'], 'Abc' => ['123'], 'Def' => ['a, b'], @@ -57,19 +57,19 @@ public function testParsesHeadersFromLines() public function testParsesHeadersFromLinesWithMultipleLines() { $lines = ['Foo: bar', 'Foo: baz', 'Foo: 123']; - $this->assertEquals([ + self::assertSame([ 'Foo' => ['bar', 'baz', '123'], ], GuzzleHttp\headers_from_lines($lines)); } public function testReturnsDebugResource() { - $this->assertInternalType('resource', GuzzleHttp\debug_resource()); + self::assertInternalType('resource', GuzzleHttp\debug_resource()); } public function testProvidesDefaultCaBundler() { - $this->assertFileExists(GuzzleHttp\default_ca_bundle()); + self::assertFileExists(GuzzleHttp\default_ca_bundle()); } public function noProxyProvider() @@ -89,7 +89,7 @@ public function noProxyProvider() */ public function testChecksNoProxyList($host, $list, $result) { - $this->assertSame( + self::assertSame( $result, \GuzzleHttp\is_host_in_noproxy($host, $list) ); @@ -105,7 +105,7 @@ public function testEnsuresNoProxyCheckHostIsSet() public function testEncodesJson() { - $this->assertEquals('true', \GuzzleHttp\json_encode(true)); + self::assertSame('true', \GuzzleHttp\json_encode(true)); } /** @@ -118,7 +118,7 @@ public function testEncodesJsonAndThrowsOnError() public function testDecodesJson() { - $this->assertTrue(\GuzzleHttp\json_decode('true')); + self::assertTrue(\GuzzleHttp\json_decode('true')); } /** diff --git a/tests/server.js b/tests/server.js index 3148c354c..f6c336a5a 100644 --- a/tests/server.js +++ b/tests/server.js @@ -175,10 +175,10 @@ var GuzzleServer = function(port, log) { } res.writeHead(400, 'NO RESPONSES IN REQUEST', { 'Content-Length': 0 }); } else { - that.responses = eval('(' + request.body + ')'); + that.responses = JSON.parse(request.body); for (var i = 0; i < that.responses.length; i++) { if (that.responses[i].body) { - that.responses[i].body = new Buffer(that.responses[i].body, 'base64'); + that.responses[i].body = Buffer.from(that.responses[i].body, 'base64'); } } if (that.log) {