Commit a96b259d by 冷斌

fix bug

parent c4f649cb
{ {
"require": { "require": {
"php": ">=5.4.0",
"yansongda/pay": "2.7.8" "yansongda/pay": "2.7.8"
} },
"minimum-stability": "dev"
} }
...@@ -6,4 +6,6 @@ $vendorDir = dirname(dirname(__FILE__)); ...@@ -6,4 +6,6 @@ $vendorDir = dirname(dirname(__FILE__));
$baseDir = dirname($vendorDir); $baseDir = dirname($vendorDir);
return array( return array(
'Stringable' => $vendorDir . '/symfony/polyfill-php80/Resources/stubs/Stringable.php',
'ValueError' => $vendorDir . '/symfony/polyfill-php80/Resources/stubs/ValueError.php',
); );
...@@ -9,6 +9,7 @@ return array( ...@@ -9,6 +9,7 @@ return array(
'0e6d7bf4a5811bfa5cf40c5ccd6fae6a' => $vendorDir . '/symfony/polyfill-mbstring/bootstrap.php', '0e6d7bf4a5811bfa5cf40c5ccd6fae6a' => $vendorDir . '/symfony/polyfill-mbstring/bootstrap.php',
'25072dd6e2470089de65ae7bf11d3109' => $vendorDir . '/symfony/polyfill-php72/bootstrap.php', '25072dd6e2470089de65ae7bf11d3109' => $vendorDir . '/symfony/polyfill-php72/bootstrap.php',
'f598d06aa772fa33d905e87be6398fb1' => $vendorDir . '/symfony/polyfill-intl-idn/bootstrap.php', 'f598d06aa772fa33d905e87be6398fb1' => $vendorDir . '/symfony/polyfill-intl-idn/bootstrap.php',
'a4a119a56e50fbb293281d9a48007e0e' => $vendorDir . '/symfony/polyfill-php80/bootstrap.php',
'7b11c4dc42b3b3023073cb14e519683c' => $vendorDir . '/ralouphie/getallheaders/src/getallheaders.php', '7b11c4dc42b3b3023073cb14e519683c' => $vendorDir . '/ralouphie/getallheaders/src/getallheaders.php',
'c964ee0ededf28c96ebd9db5099ef910' => $vendorDir . '/guzzlehttp/promises/src/functions_include.php', 'c964ee0ededf28c96ebd9db5099ef910' => $vendorDir . '/guzzlehttp/promises/src/functions_include.php',
'a0edc8309cc5e1d60e3047b5df6b7052' => $vendorDir . '/guzzlehttp/psr7/src/functions_include.php', 'a0edc8309cc5e1d60e3047b5df6b7052' => $vendorDir . '/guzzlehttp/psr7/src/functions_include.php',
......
...@@ -8,6 +8,7 @@ $baseDir = dirname($vendorDir); ...@@ -8,6 +8,7 @@ $baseDir = dirname($vendorDir);
return array( return array(
'Yansongda\\Supports\\' => array($vendorDir . '/yansongda/supports/src'), 'Yansongda\\Supports\\' => array($vendorDir . '/yansongda/supports/src'),
'Yansongda\\Pay\\' => array($vendorDir . '/yansongda/pay/src'), 'Yansongda\\Pay\\' => array($vendorDir . '/yansongda/pay/src'),
'Symfony\\Polyfill\\Php80\\' => array($vendorDir . '/symfony/polyfill-php80'),
'Symfony\\Polyfill\\Php72\\' => array($vendorDir . '/symfony/polyfill-php72'), 'Symfony\\Polyfill\\Php72\\' => array($vendorDir . '/symfony/polyfill-php72'),
'Symfony\\Polyfill\\Mbstring\\' => array($vendorDir . '/symfony/polyfill-mbstring'), 'Symfony\\Polyfill\\Mbstring\\' => array($vendorDir . '/symfony/polyfill-mbstring'),
'Symfony\\Polyfill\\Intl\\Idn\\' => array($vendorDir . '/symfony/polyfill-intl-idn'), 'Symfony\\Polyfill\\Intl\\Idn\\' => array($vendorDir . '/symfony/polyfill-intl-idn'),
......
...@@ -10,6 +10,7 @@ class ComposerStaticInit6ca204ee4b311fba144444fd09f9bb55 ...@@ -10,6 +10,7 @@ class ComposerStaticInit6ca204ee4b311fba144444fd09f9bb55
'0e6d7bf4a5811bfa5cf40c5ccd6fae6a' => __DIR__ . '/..' . '/symfony/polyfill-mbstring/bootstrap.php', '0e6d7bf4a5811bfa5cf40c5ccd6fae6a' => __DIR__ . '/..' . '/symfony/polyfill-mbstring/bootstrap.php',
'25072dd6e2470089de65ae7bf11d3109' => __DIR__ . '/..' . '/symfony/polyfill-php72/bootstrap.php', '25072dd6e2470089de65ae7bf11d3109' => __DIR__ . '/..' . '/symfony/polyfill-php72/bootstrap.php',
'f598d06aa772fa33d905e87be6398fb1' => __DIR__ . '/..' . '/symfony/polyfill-intl-idn/bootstrap.php', 'f598d06aa772fa33d905e87be6398fb1' => __DIR__ . '/..' . '/symfony/polyfill-intl-idn/bootstrap.php',
'a4a119a56e50fbb293281d9a48007e0e' => __DIR__ . '/..' . '/symfony/polyfill-php80/bootstrap.php',
'7b11c4dc42b3b3023073cb14e519683c' => __DIR__ . '/..' . '/ralouphie/getallheaders/src/getallheaders.php', '7b11c4dc42b3b3023073cb14e519683c' => __DIR__ . '/..' . '/ralouphie/getallheaders/src/getallheaders.php',
'c964ee0ededf28c96ebd9db5099ef910' => __DIR__ . '/..' . '/guzzlehttp/promises/src/functions_include.php', 'c964ee0ededf28c96ebd9db5099ef910' => __DIR__ . '/..' . '/guzzlehttp/promises/src/functions_include.php',
'a0edc8309cc5e1d60e3047b5df6b7052' => __DIR__ . '/..' . '/guzzlehttp/psr7/src/functions_include.php', 'a0edc8309cc5e1d60e3047b5df6b7052' => __DIR__ . '/..' . '/guzzlehttp/psr7/src/functions_include.php',
...@@ -24,6 +25,7 @@ class ComposerStaticInit6ca204ee4b311fba144444fd09f9bb55 ...@@ -24,6 +25,7 @@ class ComposerStaticInit6ca204ee4b311fba144444fd09f9bb55
), ),
'S' => 'S' =>
array ( array (
'Symfony\\Polyfill\\Php80\\' => 23,
'Symfony\\Polyfill\\Php72\\' => 23, 'Symfony\\Polyfill\\Php72\\' => 23,
'Symfony\\Polyfill\\Mbstring\\' => 26, 'Symfony\\Polyfill\\Mbstring\\' => 26,
'Symfony\\Polyfill\\Intl\\Idn\\' => 26, 'Symfony\\Polyfill\\Intl\\Idn\\' => 26,
...@@ -58,6 +60,10 @@ class ComposerStaticInit6ca204ee4b311fba144444fd09f9bb55 ...@@ -58,6 +60,10 @@ class ComposerStaticInit6ca204ee4b311fba144444fd09f9bb55
array ( array (
0 => __DIR__ . '/..' . '/yansongda/pay/src', 0 => __DIR__ . '/..' . '/yansongda/pay/src',
), ),
'Symfony\\Polyfill\\Php80\\' =>
array (
0 => __DIR__ . '/..' . '/symfony/polyfill-php80',
),
'Symfony\\Polyfill\\Php72\\' => 'Symfony\\Polyfill\\Php72\\' =>
array ( array (
0 => __DIR__ . '/..' . '/symfony/polyfill-php72', 0 => __DIR__ . '/..' . '/symfony/polyfill-php72',
...@@ -112,11 +118,17 @@ class ComposerStaticInit6ca204ee4b311fba144444fd09f9bb55 ...@@ -112,11 +118,17 @@ class ComposerStaticInit6ca204ee4b311fba144444fd09f9bb55
), ),
); );
public static $classMap = array (
'Stringable' => __DIR__ . '/..' . '/symfony/polyfill-php80/Resources/stubs/Stringable.php',
'ValueError' => __DIR__ . '/..' . '/symfony/polyfill-php80/Resources/stubs/ValueError.php',
);
public static function getInitializer(ClassLoader $loader) public static function getInitializer(ClassLoader $loader)
{ {
return \Closure::bind(function () use ($loader) { return \Closure::bind(function () use ($loader) {
$loader->prefixLengthsPsr4 = ComposerStaticInit6ca204ee4b311fba144444fd09f9bb55::$prefixLengthsPsr4; $loader->prefixLengthsPsr4 = ComposerStaticInit6ca204ee4b311fba144444fd09f9bb55::$prefixLengthsPsr4;
$loader->prefixDirsPsr4 = ComposerStaticInit6ca204ee4b311fba144444fd09f9bb55::$prefixDirsPsr4; $loader->prefixDirsPsr4 = ComposerStaticInit6ca204ee4b311fba144444fd09f9bb55::$prefixDirsPsr4;
$loader->classMap = ComposerStaticInit6ca204ee4b311fba144444fd09f9bb55::$classMap;
}, null, ClassLoader::class); }, null, ClassLoader::class);
} }
......
# EditorConfig is awesome: http://EditorConfig.org
# top-most EditorConfig file
root = true
# Unix-style newlines with a newline ending for every file
# Indent with 4 spaces
[php]
end_of_line = lf
indent_style = space
indent_size = 4
.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
/phpstan-baseline.neon export-ignore
/phpstan.neon.dist export-ignore
/phpunit.xml.dist export-ignore
/tests/ export-ignore
# Contributing
Please see our [contributing guide](http://docs.guzzlephp.org/en/latest/overview.html#contributing).
Please consider using one of the issue templates (bug report, feature request).
---
name: 🐛 Bug Report
about: Report errors and problems
---
**Guzzle version(s) affected**: x.y.z
**Description**
<!-- A clear and concise description of the problem. -->
**How to reproduce**
<!-- Code and/or config needed to reproduce the problem. -->
**Possible Solution**
<!--- Optional: only if you have suggestions on a fix/reason for the bug -->
**Additional context**
<!-- Optional: any other context about the problem: log messages, screenshots, etc. -->
---
name: 🚀 Feature Request
about: RFC and ideas for new features and improvements
---
**Description**
<!-- A clear and concise description of the new feature. -->
**Example**
<!-- A simple example of the new feature in action (include PHP code, YAML config, etc.)
If the new feature changes an existing feature, include a simple before/after comparison. -->
**Additional context**
<!-- Add any other context or screenshots about the feature request here. -->
---
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.
---
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!
# 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.
#!/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
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@v1
- name: Roave BC Check
uses: docker://nyholm/roave-bc-check-ga
with:
entrypoint: ./.github/workflows/bc.entrypoint
name: Static analysis
on:
push:
branches:
- master
pull_request:
jobs:
phpstan:
name: PHPStan
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v1
- name: PHPStan
uses: docker://oskarstark/phpstan-ga
with:
args: analyze --no-progress
php-cs-fixer:
name: PHP-CS-Fixer
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v1
- name: PHP-CS-Fixer
uses: docker://oskarstark/php-cs-fixer-ga
with:
args: --dry-run --diff-format udiff
phpunit.xml
composer.phar
composer.lock
composer-test.lock
vendor/
build/artifacts/
artifacts/
docs/_build
docs/*.pyc
.idea
.DS_STORE
language: php
matrix:
include:
- php: 5.5
dist: trusty
- php: 5.6
dist: xenial
- php: 7.0
dist: xenial
- php: 7.1
dist: bionic
- php: 7.2
dist: bionic
- php: 7.3
dist: bionic
- php: 7.4
dist: bionic
- php: nightly
dist: bionic
env: COMPOSER_ARGS="--ignore-platform-reqs"
- php: hhvm-3.24
dist: trusty
allow_failures:
- php: hhvm-3.24
- php: nightly
fast_finish: true
install:
- if [[ "$TRAVIS_PHP_VERSION" != "hhvm-3.24" || "$TRAVIS_PHP_VERSION" != "nightly" ]]; then echo "xdebug.overload_var_dump = 1" >> ~/.phpenv/versions/$(phpenv version-name)/etc/php.ini || true; fi
- composer install --prefer-dist $COMPOSER_ARGS
- ~/.nvm/nvm.sh install v10.18.0
- ~/.nvm/nvm.sh run v10.18.0
before_script:
- curl --version
script:
- make test
before_deploy:
- make package
deploy:
provider: releases
skip_cleanup: true
api_key:
secure: mz9H1B4cPH7dW9hTzgHnbh75+HJ6fJZ9S/1nMWFaqgj5C0wDzTqkJ+BbwiCEiqXGh6VGZbM4EmO1/wnZ7B+Hk8zsB1PP+GKVkq8+7a/261o60W3OS4gQpZQ9R68dyEO1EyZBJvL1Lzc03rkt/0WnKiAjg7nsc1j4aLKhWMDQ6x8=
file:
- build/artifacts/guzzle.phar
- build/artifacts/guzzle.zip
on:
repo: guzzle/guzzle
tags: true
all_branches: true
php: 5.5
help:
@echo "Please use \`make <target>' where <target> is one of"
@echo " start-server to start the test server"
@echo " stop-server to stop the test server"
@echo " test to perform unit tests. Provide TEST to perform a specific test."
@echo " coverage to perform unit tests with code coverage. Provide TEST to perform a specific test."
@echo " coverage-show to show the code coverage report"
@echo " clean to remove build artifacts"
@echo " docs to build the Sphinx docs"
@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"
start-server: stop-server
node tests/server.js &> /dev/null &
stop-server:
@PID=$(shell ps axo pid,command \
| grep 'tests/server.js' \
| grep -v grep \
| cut -f 1 -d " "\
) && [ -n "$$PID" ] && kill $$PID || true
test: start-server
vendor/bin/phpunit
$(MAKE) stop-server
coverage: start-server
vendor/bin/phpunit --coverage-html=build/artifacts/coverage
$(MAKE) stop-server
coverage-show: view-coverage
view-coverage:
open build/artifacts/coverage/index.html
clean:
rm -rf artifacts/*
docs:
cd docs && make html && cd ..
docs-show:
open docs/_build/html/index.html
tag:
$(if $(TAG),,$(error TAG is not defined. Pass via "make tag TAG=4.2.1"))
@echo Tagging $(TAG)
chag update $(TAG)
sed -i '' -e "s/VERSION = '.*'/VERSION = '$(TAG)'/" src/ClientInterface.php
php -l src/ClientInterface.php
git add -A
git commit -m '$(TAG) release'
chag tag
package:
php build/packager.php
.PHONY: docs burgomaster coverage-show view-coverage
<?php
require __DIR__ . '/Burgomaster.php';
$stageDirectory = __DIR__ . '/artifacts/staging';
$projectRoot = __DIR__ . '/../';
$packager = new \Burgomaster($stageDirectory, $projectRoot);
// Copy basic files to the stage directory. Note that we have chdir'd onto
// the $projectRoot directory, so use relative paths.
foreach (['README.md', 'LICENSE'] as $file) {
$packager->deepCopy($file, $file);
}
// Copy each dependency to the staging directory. Copy *.php and *.pem files.
$packager->recursiveCopy('src', 'GuzzleHttp', ['php']);
$packager->recursiveCopy('vendor/guzzlehttp/promises/src', 'GuzzleHttp/Promise');
$packager->recursiveCopy('vendor/guzzlehttp/psr7/src', 'GuzzleHttp/Psr7');
$packager->recursiveCopy('vendor/psr/http-message/src', 'Psr/Http/Message');
$packager->createAutoloader([
'GuzzleHttp/functions_include.php',
'GuzzleHttp/Psr7/functions_include.php',
'GuzzleHttp/Promise/functions_include.php',
]);
$packager->createPhar(__DIR__ . '/artifacts/guzzle.phar');
$packager->createZip(__DIR__ . '/artifacts/guzzle.zip');
# Makefile for Sphinx documentation
#
# You can set these variables from the command line.
SPHINXOPTS =
SPHINXBUILD = sphinx-build
PAPER =
BUILDDIR = _build
# Internal variables.
PAPEROPT_a4 = -D latex_paper_size=a4
PAPEROPT_letter = -D latex_paper_size=letter
ALLSPHINXOPTS = -d $(BUILDDIR)/doctrees $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) .
# the i18n builder cannot share the environment and doctrees with the others
I18NSPHINXOPTS = $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) .
.PHONY: help clean html dirhtml singlehtml pickle json htmlhelp qthelp devhelp epub latex latexpdf text man changes linkcheck doctest gettext
help:
@echo "Please use \`make <target>' where <target> is one of"
@echo " html to make standalone HTML files"
@echo " dirhtml to make HTML files named index.html in directories"
@echo " singlehtml to make a single large HTML file"
@echo " pickle to make pickle files"
@echo " json to make JSON files"
@echo " htmlhelp to make HTML files and a HTML help project"
@echo " qthelp to make HTML files and a qthelp project"
@echo " devhelp to make HTML files and a Devhelp project"
@echo " epub to make an epub"
@echo " latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter"
@echo " latexpdf to make LaTeX files and run them through pdflatex"
@echo " text to make text files"
@echo " man to make manual pages"
@echo " texinfo to make Texinfo files"
@echo " info to make Texinfo files and run them through makeinfo"
@echo " gettext to make PO message catalogs"
@echo " changes to make an overview of all changed/added/deprecated items"
@echo " linkcheck to check all external links for integrity"
@echo " doctest to run all doctests embedded in the documentation (if enabled)"
clean:
-rm -rf $(BUILDDIR)/*
html:
$(SPHINXBUILD) -b html $(ALLSPHINXOPTS) $(BUILDDIR)/html
@echo
@echo "Build finished. The HTML pages are in $(BUILDDIR)/html."
dirhtml:
$(SPHINXBUILD) -b dirhtml $(ALLSPHINXOPTS) $(BUILDDIR)/dirhtml
@echo
@echo "Build finished. The HTML pages are in $(BUILDDIR)/dirhtml."
singlehtml:
$(SPHINXBUILD) -b singlehtml $(ALLSPHINXOPTS) $(BUILDDIR)/singlehtml
@echo
@echo "Build finished. The HTML page is in $(BUILDDIR)/singlehtml."
pickle:
$(SPHINXBUILD) -b pickle $(ALLSPHINXOPTS) $(BUILDDIR)/pickle
@echo
@echo "Build finished; now you can process the pickle files."
json:
$(SPHINXBUILD) -b json $(ALLSPHINXOPTS) $(BUILDDIR)/json
@echo
@echo "Build finished; now you can process the JSON files."
htmlhelp:
$(SPHINXBUILD) -b htmlhelp $(ALLSPHINXOPTS) $(BUILDDIR)/htmlhelp
@echo
@echo "Build finished; now you can run HTML Help Workshop with the" \
".hhp project file in $(BUILDDIR)/htmlhelp."
qthelp:
$(SPHINXBUILD) -b qthelp $(ALLSPHINXOPTS) $(BUILDDIR)/qthelp
@echo
@echo "Build finished; now you can run "qcollectiongenerator" with the" \
".qhcp project file in $(BUILDDIR)/qthelp, like this:"
@echo "# qcollectiongenerator $(BUILDDIR)/qthelp/Guzzle.qhcp"
@echo "To view the help file:"
@echo "# assistant -collectionFile $(BUILDDIR)/qthelp/Guzzle.qhc"
devhelp:
$(SPHINXBUILD) -b devhelp $(ALLSPHINXOPTS) $(BUILDDIR)/devhelp
@echo
@echo "Build finished."
@echo "To view the help file:"
@echo "# mkdir -p $$HOME/.local/share/devhelp/Guzzle"
@echo "# ln -s $(BUILDDIR)/devhelp $$HOME/.local/share/devhelp/Guzzle"
@echo "# devhelp"
epub:
$(SPHINXBUILD) -b epub $(ALLSPHINXOPTS) $(BUILDDIR)/epub
@echo
@echo "Build finished. The epub file is in $(BUILDDIR)/epub."
latex:
$(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex
@echo
@echo "Build finished; the LaTeX files are in $(BUILDDIR)/latex."
@echo "Run \`make' in that directory to run these through (pdf)latex" \
"(use \`make latexpdf' here to do that automatically)."
latexpdf:
$(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex
@echo "Running LaTeX files through pdflatex..."
$(MAKE) -C $(BUILDDIR)/latex all-pdf
@echo "pdflatex finished; the PDF files are in $(BUILDDIR)/latex."
text:
$(SPHINXBUILD) -b text $(ALLSPHINXOPTS) $(BUILDDIR)/text
@echo
@echo "Build finished. The text files are in $(BUILDDIR)/text."
man:
$(SPHINXBUILD) -b man $(ALLSPHINXOPTS) $(BUILDDIR)/man
@echo
@echo "Build finished. The manual pages are in $(BUILDDIR)/man."
texinfo:
$(SPHINXBUILD) -b texinfo $(ALLSPHINXOPTS) $(BUILDDIR)/texinfo
@echo
@echo "Build finished. The Texinfo files are in $(BUILDDIR)/texinfo."
@echo "Run \`make' in that directory to run these through makeinfo" \
"(use \`make info' here to do that automatically)."
info:
$(SPHINXBUILD) -b texinfo $(ALLSPHINXOPTS) $(BUILDDIR)/texinfo
@echo "Running Texinfo files through makeinfo..."
make -C $(BUILDDIR)/texinfo info
@echo "makeinfo finished; the Info files are in $(BUILDDIR)/texinfo."
gettext:
$(SPHINXBUILD) -b gettext $(I18NSPHINXOPTS) $(BUILDDIR)/locale
@echo
@echo "Build finished. The message catalogs are in $(BUILDDIR)/locale."
changes:
$(SPHINXBUILD) -b changes $(ALLSPHINXOPTS) $(BUILDDIR)/changes
@echo
@echo "The overview file is in $(BUILDDIR)/changes."
linkcheck:
$(SPHINXBUILD) -b linkcheck $(ALLSPHINXOPTS) $(BUILDDIR)/linkcheck
@echo
@echo "Link check complete; look for any errors in the above output " \
"or in $(BUILDDIR)/linkcheck/output.txt."
doctest:
$(SPHINXBUILD) -b doctest $(ALLSPHINXOPTS) $(BUILDDIR)/doctest
@echo "Testing of doctests in the sources finished, look at the " \
"results in $(BUILDDIR)/doctest/output.txt."
import sys, os
from sphinx.highlighting import lexers
from pygments.lexers.web import PhpLexer
lexers['php'] = PhpLexer(startinline=True, linenos=1)
lexers['php-annotations'] = PhpLexer(startinline=True, linenos=1)
primary_domain = 'php'
extensions = []
templates_path = ['_templates']
source_suffix = '.rst'
master_doc = 'index'
project = u'Guzzle'
copyright = u'2015, Michael Dowling'
version = '6'
html_title = "Guzzle Documentation"
html_short_title = "Guzzle 6"
exclude_patterns = ['_build']
html_static_path = ['_static']
##### Guzzle sphinx theme
import guzzle_sphinx_theme
html_translator_class = 'guzzle_sphinx_theme.HTMLTranslator'
html_theme_path = guzzle_sphinx_theme.html_theme_path()
html_theme = 'guzzle_sphinx_theme'
# Custom sidebar templates, maps document names to template names.
html_sidebars = {
'**': ['logo-text.html', 'globaltoc.html', 'searchbox.html']
}
# Register the theme as an extension to generate a sitemap.xml
extensions.append("guzzle_sphinx_theme")
# Guzzle theme options (see theme.conf for more information)
html_theme_options = {
# Set the path to a special layout to include for the homepage
# "index_template": "homepage.html",
# Allow a separate homepage from the master_doc
# homepage = index
# Set the name of the project to appear in the nav menu
# "project_nav_name": "Guzzle",
# Set your Disqus short name to enable comments
# "disqus_comments_shortname": "my_disqus_comments_short_name",
# Set you GA account ID to enable tracking
# "google_analytics_account": "my_ga_account",
# Path to a touch icon
# "touch_icon": "",
# Specify a base_url used to generate sitemap.xml links. If not
# specified, then no sitemap will be built.
"base_url": "http://guzzlephp.org"
# Allow the "Table of Contents" page to be defined separately from "master_doc"
# tocpage = Contents
# Allow the project link to be overriden to a custom URL.
# projectlink = http://myproject.url
}
===
FAQ
===
Does Guzzle require cURL?
=========================
No. Guzzle can use any HTTP handler to send requests. This means that Guzzle
can be used with cURL, PHP's stream wrapper, sockets, and non-blocking libraries
like `React <http://reactphp.org/>`_. You just need to configure an HTTP handler
to use a different method of sending requests.
.. note::
Guzzle has historically only utilized cURL to send HTTP requests. cURL is
an amazing HTTP client (arguably the best), and Guzzle will continue to use
it by default when it is available. It is rare, but some developers don't
have cURL installed on their systems or run into version specific issues.
By allowing swappable HTTP handlers, Guzzle is now much more customizable
and able to adapt to fit the needs of more developers.
Can Guzzle send asynchronous requests?
======================================
Yes. You can use the ``requestAsync``, ``sendAsync``, ``getAsync``,
``headAsync``, ``putAsync``, ``postAsync``, ``deleteAsync``, and ``patchAsync``
methods of a client to send an asynchronous request. The client will return a
``GuzzleHttp\Promise\PromiseInterface`` object. You can chain ``then``
functions off of the promise.
.. code-block:: php
$promise = $client->requestAsync('GET', 'http://httpbin.org/get');
$promise->then(function ($response) {
echo 'Got a response! ' . $response->getStatusCode();
});
You can force an asynchronous response to complete using the ``wait()`` method
of the returned promise.
.. code-block:: php
$promise = $client->requestAsync('GET', 'http://httpbin.org/get');
$response = $promise->wait();
How can I add custom cURL options?
==================================
cURL offers a huge number of `customizable options <http://us1.php.net/curl_setopt>`_.
While Guzzle normalizes many of these options across different handlers, there
are times when you need to set custom cURL options. This can be accomplished
by passing an associative array of cURL settings in the **curl** key of a
request.
For example, let's say you need to customize the outgoing network interface
used with a client.
.. code-block:: php
$client->request('GET', '/', [
'curl' => [
CURLOPT_INTERFACE => 'xxx.xxx.xxx.xxx'
]
]);
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?
============================================
You can pass custom `stream context options <http://www.php.net/manual/en/context.php>`_
using the **stream_context** key of the request option. The **stream_context**
array is an associative array where each key is a PHP transport, and each value
is an associative array of transport options.
For example, let's say you need to customize the outgoing network interface
used with a client and allow self-signed certificates.
.. code-block:: php
$client->request('GET', '/', [
'stream' => true,
'stream_context' => [
'ssl' => [
'allow_self_signed' => true
],
'socket' => [
'bindto' => 'xxx.xxx.xxx.xxx'
]
]
]);
Why am I getting an SSL verification error?
===========================================
You need to specify the path on disk to the CA bundle used by Guzzle for
verifying the peer certificate. See :ref:`verify-option`.
What is this Maximum function nesting error?
============================================
Maximum function nesting level of '100' reached, aborting
You could run into this error if you have the XDebug extension installed and
you execute a lot of requests in callbacks. This error message comes
specifically from the XDebug extension. PHP itself does not have a function
nesting limit. Change this setting in your php.ini to increase the limit::
xdebug.max_nesting_level = 1000
Why am I getting a 417 error response?
======================================
This can occur for a number of reasons, but if you are sending PUT, POST, or
PATCH requests with an ``Expect: 100-Continue`` header, a server that does not
support this header will return a 417 response. You can work around this by
setting the ``expect`` request option to ``false``:
.. code-block:: php
$client = new GuzzleHttp\Client();
// Disable the expect header on a single request
$response = $client->request('PUT', '/', ['expect' => false]);
// Disable the expect header on all client requests
$client = new GuzzleHttp\Client(['expect' => false]);
How can I track redirected requests?
====================================
You can enable tracking of redirected URIs and status codes via the
`track_redirects` option. Each redirected URI and status code will be stored in the
``X-Guzzle-Redirect-History`` and the ``X-Guzzle-Redirect-Status-History``
header respectively.
The initial request's URI and the final status code will be excluded from the results.
With this in mind you should be able to easily track a request's full redirect path.
For example, let's say you need to track redirects and provide both results
together in a single report:
.. code-block:: php
// First you configure Guzzle with redirect tracking and make a request
$client = new Client([
RequestOptions::ALLOW_REDIRECTS => [
'max' => 10, // allow at most 10 redirects.
'strict' => true, // use "strict" RFC compliant redirects.
'referer' => true, // add a Referer header
'track_redirects' => true,
],
]);
$initialRequest = '/redirect/3'; // Store the request URI for later use
$response = $client->request('GET', $initialRequest); // Make your request
// Retrieve both Redirect History headers
$redirectUriHistory = $response->getHeader('X-Guzzle-Redirect-History')[0]; // retrieve Redirect URI history
$redirectCodeHistory = $response->getHeader('X-Guzzle-Redirect-Status-History')[0]; // retrieve Redirect HTTP Status history
// Add the initial URI requested to the (beginning of) URI history
array_unshift($redirectUriHistory, $initialRequest);
// Add the final HTTP status code to the end of HTTP response history
array_push($redirectCodeHistory, $response->getStatusCode());
// (Optional) Combine the items of each array into a single result set
$fullRedirectReport = [];
foreach ($redirectUriHistory as $key => $value) {
$fullRedirectReport[$key] = ['location' => $value, 'code' => $redirectCodeHistory[$key]];
}
echo json_encode($fullRedirectReport);
.. title:: Guzzle, PHP HTTP client
====================
Guzzle Documentation
====================
Guzzle is a PHP HTTP client that makes it easy to send HTTP requests and
trivial to integrate with web services.
- Simple interface for building query strings, POST requests, streaming large
uploads, streaming large downloads, using HTTP cookies, uploading JSON data,
etc...
- Can send both synchronous and asynchronous requests using the same interface.
- Uses PSR-7 interfaces for requests, responses, and streams. This allows you
to utilize other PSR-7 compatible libraries with Guzzle.
- Abstracts away the underlying HTTP transport, allowing you to write
environment and transport agnostic code; i.e., no hard dependency on cURL,
PHP streams, sockets, or non-blocking event loops.
- Middleware system allows you to augment and compose client behavior.
.. code-block:: php
$client = new GuzzleHttp\Client();
$res = $client->request('GET', 'https://api.github.com/user', [
'auth' => ['user', 'pass']
]);
echo $res->getStatusCode();
// "200"
echo $res->getHeader('content-type')[0];
// 'application/json; charset=utf8'
echo $res->getBody();
// {"type":"User"...'
// 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();
User Guide
==========
.. toctree::
:maxdepth: 3
overview
quickstart
request-options
psr7
handlers-and-middleware
testing
faq
========
Overview
========
Requirements
============
#. PHP 5.5.0
#. To use the PHP stream handler, ``allow_url_fopen`` must be enabled in your
system's php.ini.
#. To use the cURL handler, you must have a recent version of cURL >= 7.19.4
compiled with OpenSSL and zlib.
.. note::
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:
Installation
============
The recommended way to install Guzzle is with
`Composer <http://getcomposer.org>`_. Composer is a dependency management tool
for PHP that allows you to declare the dependencies your project needs and
installs them into your project.
.. code-block:: bash
# Install Composer
curl -sS https://getcomposer.org/installer | php
You can add Guzzle as a dependency using the composer.phar CLI:
.. code-block:: bash
php composer.phar require guzzlehttp/guzzle:~6.0
Alternatively, you can specify Guzzle as a dependency in your project's
existing composer.json file:
.. code-block:: js
{
"require": {
"guzzlehttp/guzzle": "~6.0"
}
}
After installing, you need to require Composer's autoloader:
.. code-block:: php
require 'vendor/autoload.php';
You can find out more on how to install Composer, configure autoloading, and
other best-practices for defining dependencies at `getcomposer.org <http://getcomposer.org>`_.
Bleeding edge
-------------
During your development, you can keep up with the latest changes on the master
branch by setting the version requirement for Guzzle to ``~6.0@dev``.
.. code-block:: js
{
"require": {
"guzzlehttp/guzzle": "~6.0@dev"
}
}
License
=======
Licensed using the `MIT license <http://opensource.org/licenses/MIT>`_.
Copyright (c) 2015 Michael Dowling <https://github.com/mtdowling>
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
Contributing
============
Guidelines
----------
1. Guzzle utilizes PSR-1, PSR-2, PSR-4, and PSR-7.
2. Guzzle is meant to be lean and fast with very few dependencies. This means
that not every feature request will be accepted.
3. Guzzle has a minimum PHP version requirement of PHP 5.5. Pull requests must
not require a PHP version greater than PHP 5.5 unless the feature is only
utilized conditionally.
4. All pull requests must include unit tests to ensure the change works as
expected and to prevent regressions.
Running the tests
-----------------
In order to contribute, you'll need to checkout the source from GitHub and
install Guzzle's dependencies using Composer:
.. code-block:: bash
git clone https://github.com/guzzle/guzzle.git
cd guzzle && curl -s http://getcomposer.org/installer | php && ./composer.phar install --dev
Guzzle is unit tested with PHPUnit. Run the tests using the Makefile:
.. code-block:: bash
make test
.. note::
You'll need to install node.js v0.5.0 or newer in order to perform
integration tests on Guzzle's HTTP handlers.
Reporting a security vulnerability
==================================
We want to ensure that Guzzle is a secure HTTP client library for everyone. If
you've discovered a security vulnerability in Guzzle, we appreciate your help
in disclosing it to us in a `responsible manner <http://en.wikipedia.org/wiki/Responsible_disclosure>`_.
Publicly disclosing a vulnerability can put the entire community at risk. If
you've discovered a security concern, please email us at
security@guzzlephp.org. We'll work with you to make sure that we understand the
scope of the issue, and that we fully address your concern. We consider
correspondence sent to security@guzzlephp.org our highest priority, and work to
address any issues that arise as quickly as possible.
After a security vulnerability has been corrected, a security hotfix release will
be deployed as soon as possible.
======================
Testing Guzzle Clients
======================
Guzzle provides several tools that will enable you to easily mock the HTTP
layer without needing to send requests over the internet.
* Mock handler
* History middleware
* Node.js web server for integration testing
Mock Handler
============
When testing HTTP clients, you often need to simulate specific scenarios like
returning a successful response, returning an error, or returning specific
responses in a certain order. Because unit tests need to be predictable, easy
to bootstrap, and fast, hitting an actual remote API is a test smell.
Guzzle provides a mock handler that can be used to fulfill HTTP requests with
a response or exception by shifting return values off of a queue.
.. code-block:: php
use GuzzleHttp\Client;
use GuzzleHttp\Handler\MockHandler;
use GuzzleHttp\HandlerStack;
use GuzzleHttp\Psr7\Response;
use GuzzleHttp\Psr7\Request;
use GuzzleHttp\Exception\RequestException;
// Create a mock and queue two responses.
$mock = new MockHandler([
new Response(200, ['X-Foo' => 'Bar'], 'Hello, World'),
new Response(202, ['Content-Length' => 0]),
new RequestException('Error Communicating with Server', new Request('GET', 'test'))
]);
$handlerStack = HandlerStack::create($mock);
$client = new Client(['handler' => $handlerStack]);
// The first request is intercepted with the first response.
$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
==================
When using things like the ``Mock`` handler, you often need to know if the
requests you expected to send were sent exactly as you intended. While the mock
handler responds with mocked responses, the history middleware maintains a
history of the requests that were sent by a client.
.. code-block:: php
use GuzzleHttp\Client;
use GuzzleHttp\HandlerStack;
use GuzzleHttp\Middleware;
$container = [];
$history = Middleware::history($container);
$handlerStack = HandlerStack::create();
// or $handlerStack = HandlerStack::create($mock); if using the Mock handler.
// Add the history middleware to the handler stack.
$handlerStack->push($history);
$client = new Client(['handler' => $handlerStack]);
$client->request('GET', 'http://httpbin.org/get');
$client->request('HEAD', 'http://httpbin.org/get');
// Count the number of transactions
echo count($container);
//> 2
// Iterate over the requests and responses
foreach ($container as $transaction) {
echo $transaction['request']->getMethod();
//> GET, HEAD
if ($transaction['response']) {
echo $transaction['response']->getStatusCode();
//> 200, 200
} elseif ($transaction['error']) {
echo $transaction['error'];
//> exception
}
var_dump($transaction['options']);
//> dumps the request options of the sent request.
}
Test Web Server
===============
Using mock responses is almost always enough when testing a web service client.
When implementing custom :doc:`HTTP handlers <handlers-and-middleware>`, you'll
need to send actual HTTP requests in order to sufficiently test the handler.
However, a best practice is to contact a local web server rather than a server
over the internet.
- Tests are more reliable
- Tests do not require a network connection
- Tests have no external dependencies
Using the test server
---------------------
.. warning::
The following functionality is provided to help developers of Guzzle
develop HTTP handlers. There is no promise of backwards compatibility
when it comes to the node.js test server or the ``GuzzleHttp\Tests\Server``
class. If you are using the test server or ``Server`` class outside of
guzzlehttp/guzzle, then you will need to configure autoloading and
ensure the web server is started manually.
.. hint::
You almost never need to use this test web server. You should only ever
consider using it when developing HTTP handlers. The test web server
is not necessary for mocking requests. For that, please use the
Mock handler and history middleware.
Guzzle ships with a node.js test server that receives requests and returns
responses from a queue. The test server exposes a simple API that is used to
enqueue responses and inspect the requests that it has received.
Any operation on the ``Server`` object will ensure that
the server is running and wait until it is able to receive requests before
returning.
``GuzzleHttp\Tests\Server`` provides a static interface to the test server. You
can queue an HTTP response or an array of responses by calling
``Server::enqueue()``. This method accepts an array of
``Psr\Http\Message\ResponseInterface`` and ``Exception`` objects.
.. code-block:: php
use GuzzleHttp\Client;
use GuzzleHttp\Psr7\Response;
use GuzzleHttp\Tests\Server;
// Start the server and queue a response
Server::enqueue([
new Response(200, ['Content-Length' => 0])
]);
$client = new Client(['base_uri' => Server::$url]);
echo $client->request('GET', '/foo')->getStatusCode();
// 200
When a response is queued on the test server, the test server will remove any
previously queued responses. As the server receives requests, queued responses
are dequeued and returned to the request. When the queue is empty, the server
will return a 500 response.
You can inspect the requests that the server has retrieved by calling
``Server::received()``.
.. code-block:: php
foreach (Server::received() as $response) {
echo $response->getStatusCode();
}
You can clear the list of received requests from the web server using the
``Server::flush()`` method.
.. code-block:: php
Server::flush();
echo count(Server::received());
// 0
includes:
- phpstan-baseline.neon
parameters:
level: max
paths:
- src
bootstrap: tests/bootstrap-phpstan.php
\ No newline at end of file
<?xml version="1.0" encoding="UTF-8"?>
<phpunit xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:noNamespaceSchemaLocation="vendor/phpunit/phpunit/phpunit.xsd"
bootstrap="./tests/bootstrap.php"
backupGlobals="true"
colors="true"
executionOrder="random"
>
<testsuites>
<testsuite name="Test Suite">
<directory>tests</directory>
</testsuite>
</testsuites>
<filter>
<whitelist>
<directory suffix=".php">src</directory>
<exclude>
<directory suffix="Interface.php">src/</directory>
</exclude>
</whitelist>
</filter>
</phpunit>
<?php
namespace GuzzleHttp\Tests\CookieJar;
use GuzzleHttp\Cookie\FileCookieJar;
use GuzzleHttp\Cookie\SetCookie;
use PHPUnit\Framework\TestCase;
/**
* @covers GuzzleHttp\Cookie\FileCookieJar
*/
class FileCookieJarTest extends TestCase
{
private $file;
public function setUp()
{
$this->file = tempnam('/tmp', 'file-cookies');
}
/**
* @expectedException \RuntimeException
*/
public function testValidatesCookieFile()
{
file_put_contents($this->file, 'true');
new FileCookieJar($this->file);
}
public function testLoadsFromFile()
{
$jar = new FileCookieJar($this->file);
self::assertSame([], $jar->getIterator()->getArrayCopy());
unlink($this->file);
}
/**
* @dataProvider providerPersistsToFileFileParameters
*/
public function testPersistsToFile($testSaveSessionCookie = false)
{
$jar = new FileCookieJar($this->file, $testSaveSessionCookie);
$jar->setCookie(new SetCookie([
'Name' => 'foo',
'Value' => 'bar',
'Domain' => 'foo.com',
'Expires' => time() + 1000
]));
$jar->setCookie(new SetCookie([
'Name' => 'baz',
'Value' => 'bar',
'Domain' => 'foo.com',
'Expires' => time() + 1000
]));
$jar->setCookie(new SetCookie([
'Name' => 'boo',
'Value' => 'bar',
'Domain' => 'foo.com',
]));
self::assertCount(3, $jar);
unset($jar);
// Make sure it wrote to the file
$contents = file_get_contents($this->file);
self::assertNotEmpty($contents);
// Load the cookieJar from the file
$jar = new FileCookieJar($this->file);
if ($testSaveSessionCookie) {
self::assertCount(3, $jar);
} else {
// Weeds out temporary and session cookies
self::assertCount(2, $jar);
}
unset($jar);
unlink($this->file);
}
public function providerPersistsToFileFileParameters()
{
return [
[false],
[true]
];
}
}
<?php
namespace GuzzleHttp\Tests\CookieJar;
use GuzzleHttp\Cookie\SessionCookieJar;
use GuzzleHttp\Cookie\SetCookie;
use PHPUnit\Framework\TestCase;
/**
* @covers GuzzleHttp\Cookie\SessionCookieJar
*/
class SessionCookieJarTest extends TestCase
{
private $sessionVar;
public function setUp()
{
$this->sessionVar = 'sessionKey';
if (!isset($_SESSION)) {
$_SESSION = [];
}
}
/**
* @expectedException \RuntimeException
*/
public function testValidatesCookieSession()
{
$_SESSION[$this->sessionVar] = 'true';
new SessionCookieJar($this->sessionVar);
}
public function testLoadsFromSession()
{
$jar = new SessionCookieJar($this->sessionVar);
self::assertSame([], $jar->getIterator()->getArrayCopy());
unset($_SESSION[$this->sessionVar]);
}
/**
* @dataProvider providerPersistsToSessionParameters
*/
public function testPersistsToSession($testSaveSessionCookie = false)
{
$jar = new SessionCookieJar($this->sessionVar, $testSaveSessionCookie);
$jar->setCookie(new SetCookie([
'Name' => 'foo',
'Value' => 'bar',
'Domain' => 'foo.com',
'Expires' => time() + 1000
]));
$jar->setCookie(new SetCookie([
'Name' => 'baz',
'Value' => 'bar',
'Domain' => 'foo.com',
'Expires' => time() + 1000
]));
$jar->setCookie(new SetCookie([
'Name' => 'boo',
'Value' => 'bar',
'Domain' => 'foo.com',
]));
self::assertCount(3, $jar);
unset($jar);
// Make sure it wrote to the sessionVar in $_SESSION
$contents = $_SESSION[$this->sessionVar];
self::assertNotEmpty($contents);
// Load the cookieJar from the file
$jar = new SessionCookieJar($this->sessionVar);
if ($testSaveSessionCookie) {
self::assertCount(3, $jar);
} else {
// Weeds out temporary and session cookies
self::assertCount(2, $jar);
}
unset($jar);
unset($_SESSION[$this->sessionVar]);
}
public function providerPersistsToSessionParameters()
{
return [
[false],
[true]
];
}
}
<?php
namespace GuzzleHttp\Tests\Exception;
use GuzzleHttp\Exception\ConnectException;
use GuzzleHttp\Psr7\Request;
use PHPUnit\Framework\TestCase;
/**
* @covers \GuzzleHttp\Exception\ConnectException
*/
class ConnectExceptionTest extends TestCase
{
public function testHasNoResponse()
{
$req = new Request('GET', '/');
$prev = new \Exception();
$e = new ConnectException('foo', $req, $prev, ['foo' => 'bar']);
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());
}
}
<?php
namespace GuzzleHttp\Tests\Exception;
use GuzzleHttp\Exception\RequestException;
use GuzzleHttp\Psr7\Request;
use GuzzleHttp\Psr7\Response;
use GuzzleHttp\Psr7\Stream;
use PHPUnit\Framework\TestCase;
/**
* @covers \GuzzleHttp\Exception\RequestException
*/
class RequestExceptionTest extends TestCase
{
public function testHasRequestAndResponse()
{
$req = new Request('GET', '/');
$res = new Response(200);
$e = new RequestException('foo', $req, $res);
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', '/'));
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));
self::assertContains(
'GET /',
$e->getMessage()
);
self::assertContains(
'400 Bad Request',
$e->getMessage()
);
self::assertInstanceOf('GuzzleHttp\Exception\ClientException', $e);
}
public function testCreatesServerErrorResponseException()
{
$e = RequestException::create(new Request('GET', '/'), new Response(500));
self::assertContains(
'GET /',
$e->getMessage()
);
self::assertContains(
'500 Internal Server Error',
$e->getMessage()
);
self::assertInstanceOf('GuzzleHttp\Exception\ServerException', $e);
}
public function testCreatesGenericErrorResponseException()
{
$e = RequestException::create(new Request('GET', '/'), new Response(300));
self::assertContains(
'GET /',
$e->getMessage()
);
self::assertContains(
'300 ',
$e->getMessage()
);
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()
{
return [
['You broke the test!'],
['<h1>zlomený zkouška</h1>'],
['{"tester": "Philépe Gonzalez"}'],
["<xml>\n\t<text>Your friendly test</text>\n</xml>"],
['document.body.write("here comes a test");'],
["body:before {\n\tcontent: 'test style';\n}"],
];
}
/**
* @dataProvider dataPrintableResponses
*/
public function testCreatesExceptionWithPrintableBodySummary($content)
{
$response = new Response(
500,
[],
$content
);
$e = RequestException::create(new Request('GET', '/'), $response);
self::assertContains(
$content,
$e->getMessage()
);
self::assertInstanceOf('GuzzleHttp\Exception\RequestException', $e);
}
public function testCreatesExceptionWithTruncatedSummary()
{
$content = str_repeat('+', 121);
$response = new Response(500, [], $content);
$e = RequestException::create(new Request('GET', '/'), $response);
$expected = str_repeat('+', 120) . ' (truncated...)';
self::assertContains($expected, $e->getMessage());
}
public function testExceptionMessageIgnoresEmptyBody()
{
$e = RequestException::create(new Request('GET', '/'), new Response(500));
self::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);
self::assertNotContains(
$content,
$e->getMessage()
);
self::assertInstanceOf('GuzzleHttp\Exception\RequestException', $e);
}
public function testHasStatusCodeAsExceptionCode()
{
$e = RequestException::create(new Request('GET', '/'), new Response(442));
self::assertSame(442, $e->getCode());
}
public function testWrapsRequestExceptions()
{
$e = new \Exception('foo');
$r = new Request('GET', 'http://www.oo.com');
$ex = RequestException::wrapException($r, $e);
self::assertInstanceOf('GuzzleHttp\Exception\RequestException', $ex);
self::assertSame($e, $ex->getPrevious());
}
public function testDoesNotWrapExistingRequestExceptions()
{
$r = new Request('GET', 'http://www.oo.com');
$e = new RequestException('foo', $r);
$e2 = RequestException::wrapException($r, $e);
self::assertSame($e, $e2);
}
public function testCanProvideHandlerContext()
{
$r = new Request('GET', 'http://www.oo.com');
$e = new RequestException('foo', $r, null, null, ['bar' => 'baz']);
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));
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));
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;
}
}
<?php
namespace GuzzleHttp\Tests\Exception;
use GuzzleHttp\Exception\SeekException;
use GuzzleHttp\Psr7;
use PHPUnit\Framework\TestCase;
class SeekExceptionTest extends TestCase
{
public function testHasStream()
{
$s = Psr7\stream_for('foo');
$e = new SeekException($s, 10);
self::assertSame($s, $e->getStream());
self::assertContains('10', $e->getMessage());
}
}
<?php
namespace GuzzleHttp\Test\Handler;
use GuzzleHttp\Exception\ConnectException;
use GuzzleHttp\Handler\CurlHandler;
use GuzzleHttp\Psr7;
use GuzzleHttp\Psr7\Request;
use GuzzleHttp\Psr7\Response;
use GuzzleHttp\Tests\Server;
use PHPUnit\Framework\TestCase;
/**
* @covers \GuzzleHttp\Handler\CurlHandler
*/
class CurlHandlerTest extends TestCase
{
protected function getHandler($options = [])
{
return new CurlHandler($options);
}
/**
* @expectedException \GuzzleHttp\Exception\ConnectException
* @expectedExceptionMessage cURL
*/
public function testCreatesCurlErrors()
{
$handler = new CurlHandler();
$request = new Request('GET', 'http://localhost:123');
$handler($request, ['timeout' => 0.001, 'connect_timeout' => 0.001])->wait();
}
public function testReusesHandles()
{
Server::flush();
$response = new response(200);
Server::enqueue([$response, $response]);
$a = new CurlHandler();
$request = new Request('GET', Server::$url);
self::assertInstanceOf('GuzzleHttp\Promise\FulfilledPromise', $a($request, []));
self::assertInstanceOf('GuzzleHttp\Promise\FulfilledPromise', $a($request, []));
}
public function testDoesSleep()
{
$response = new response(200);
Server::enqueue([$response]);
$a = new CurlHandler();
$request = new Request('GET', Server::$url);
$s = \GuzzleHttp\_current_time();
$a($request, ['delay' => 0.1])->wait();
self::assertGreaterThan(0.0001, \GuzzleHttp\_current_time() - $s);
}
public function testCreatesCurlErrorsWithContext()
{
$handler = new CurlHandler();
$request = new Request('GET', 'http://localhost:123');
$called = false;
$p = $handler($request, ['timeout' => 0.001, 'connect_timeout' => 0.001])
->otherwise(function (ConnectException $e) use (&$called) {
$called = true;
self::assertArrayHasKey('errno', $e->getHandlerContext());
});
$p->wait();
self::assertTrue($called);
}
public function testUsesContentLengthWhenOverInMemorySize()
{
Server::flush();
Server::enqueue([new Response()]);
$stream = Psr7\stream_for(str_repeat('.', 1000000));
$handler = new CurlHandler();
$request = new Request(
'PUT',
Server::$url,
['Content-Length' => 1000000],
$stream
);
$handler($request, [])->wait();
$received = Server::received()[0];
self::assertEquals(1000000, $received->getHeaderLine('Content-Length'));
self::assertFalse($received->hasHeader('Transfer-Encoding'));
}
}
<?php
namespace GuzzleHttp\Tests\Handler;
use GuzzleHttp\Handler\CurlMultiHandler;
use GuzzleHttp\Psr7\Request;
use GuzzleHttp\Psr7\Response;
use GuzzleHttp\Tests\Server;
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();
self::assertSame(200, $response->getStatusCode());
}
/**
* @expectedException \GuzzleHttp\Exception\ConnectException
* @expectedExceptionMessage cURL error
*/
public function testCreatesExceptions()
{
$a = new CurlMultiHandler();
$a(new Request('GET', 'http://localhost:123'), [])->wait();
}
public function testCanSetSelectTimeout()
{
$a = new CurlMultiHandler(['select_timeout' => 2]);
self::assertEquals(2, self::readAttribute($a, 'selectTimeout'));
}
public function testCanCancel()
{
Server::flush();
$response = new Response(200);
Server::enqueue(array_fill_keys(range(0, 10), $response));
$a = new CurlMultiHandler();
$responses = [];
for ($i = 0; $i < 10; $i++) {
$response = $a(new Request('GET', Server::$url), []);
$response->cancel();
$responses[] = $response;
}
foreach ($responses as $r) {
self::assertSame('rejected', $response->getState());
}
}
public function testCannotCancelFinished()
{
Server::flush();
Server::enqueue([new Response(200)]);
$a = new CurlMultiHandler();
$response = $a(new Request('GET', Server::$url), []);
$response->wait();
$response->cancel();
self::assertSame('fulfilled', $response->getState());
}
public function testDelaysConcurrently()
{
Server::flush();
Server::enqueue([new Response()]);
$a = new CurlMultiHandler();
$expected = \GuzzleHttp\_current_time() + (100 / 1000);
$response = $a(new Request('GET', Server::$url), ['delay' => 100]);
$response->wait();
self::assertGreaterThanOrEqual($expected, \GuzzleHttp\_current_time());
}
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'));
}
/**
* @expectedException \BadMethodCallException
*/
public function throwsWhenAccessingInvalidProperty()
{
$h = new CurlMultiHandler();
$h->foo;
}
}
<?php
namespace GuzzleHttp\Test\Handler;
use GuzzleHttp\Handler\EasyHandle;
use GuzzleHttp\Psr7;
use PHPUnit\Framework\TestCase;
/**
* @covers \GuzzleHttp\Handler\EasyHandle
*/
class EasyHandleTest extends TestCase
{
/**
* @expectedException \BadMethodCallException
* @expectedExceptionMessage The EasyHandle has been released
*/
public function testEnsuresHandleExists()
{
$easy = new EasyHandle;
unset($easy->handle);
$easy->handle;
}
}
<?php
namespace GuzzleHttp\Test\Handler;
use GuzzleHttp\Handler\MockHandler;
use GuzzleHttp\Promise\PromiseInterface;
use GuzzleHttp\Psr7\Request;
use GuzzleHttp\Psr7\Response;
use GuzzleHttp\TransferStats;
use PHPUnit\Framework\TestCase;
/**
* @covers \GuzzleHttp\Handler\MockHandler
*/
class MockHandlerTest extends TestCase
{
public function testReturnsMockResponse()
{
$res = new Response();
$mock = new MockHandler([$res]);
$request = new Request('GET', 'http://example.com');
$p = $mock($request, []);
self::assertSame($res, $p->wait());
}
public function testIsCountable()
{
$res = new Response();
$mock = new MockHandler([$res, $res]);
self::assertCount(2, $mock);
}
public function testEmptyHandlerIsCountable()
{
self::assertCount(0, new MockHandler());
}
/**
* @expectedException \InvalidArgumentException
*/
public function testEnsuresEachAppendIsValid()
{
$mock = new MockHandler(['a']);
$request = new Request('GET', 'http://example.com');
$mock($request, []);
}
public function testCanQueueExceptions()
{
$e = new \Exception('a');
$mock = new MockHandler([$e]);
$request = new Request('GET', 'http://example.com');
$p = $mock($request, []);
try {
$p->wait();
self::fail();
} catch (\Exception $e2) {
self::assertSame($e, $e2);
}
}
public function testCanGetLastRequestAndOptions()
{
$res = new Response();
$mock = new MockHandler([$res]);
$request = new Request('GET', 'http://example.com');
$mock($request, ['foo' => 'bar']);
self::assertSame($request, $mock->getLastRequest());
self::assertSame(['foo' => 'bar'], $mock->getLastOptions());
}
public function testSinkFilename()
{
$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();
self::assertFileExists($filename);
self::assertStringEqualsFile($filename, 'TEST CONTENT');
unlink($filename);
}
public function testSinkResource()
{
$file = tmpfile();
$meta = stream_get_meta_data($file);
$res = new Response(200, [], 'TEST CONTENT');
$mock = new MockHandler([$res]);
$request = new Request('GET', '/');
$p = $mock($request, ['sink' => $file]);
$p->wait();
self::assertFileExists($meta['uri']);
self::assertStringEqualsFile($meta['uri'], 'TEST CONTENT');
}
public function testSinkStream()
{
$stream = new \GuzzleHttp\Psr7\Stream(tmpfile());
$res = new Response(200, [], 'TEST CONTENT');
$mock = new MockHandler([$res]);
$request = new Request('GET', '/');
$p = $mock($request, ['sink' => $stream]);
$p->wait();
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;
};
$mock = new MockHandler([$fn]);
$request = new Request('GET', 'http://example.com');
$p = $mock($request, ['foo' => 'bar']);
self::assertSame($r, $p->wait());
}
/**
* @expectedException \InvalidArgumentException
*/
public function testEnsuresOnHeadersIsCallable()
{
$res = new Response();
$mock = new MockHandler([$res]);
$request = new Request('GET', 'http://example.com');
$mock($request, ['on_headers' => 'error!']);
}
/**
* @expectedException \GuzzleHttp\Exception\RequestException
* @expectedExceptionMessage An error was encountered during the on_headers event
* @expectedExceptionMessage test
*/
public function testRejectsPromiseWhenOnHeadersFails()
{
$res = new Response();
$mock = new MockHandler([$res]);
$request = new Request('GET', 'http://example.com');
$promise = $mock($request, [
'on_headers' => function () {
throw new \Exception('test');
}
]);
$promise->wait();
}
public function testInvokesOnFulfilled()
{
$res = new Response();
$mock = new MockHandler([$res], function ($v) use (&$c) {
$c = $v;
});
$request = new Request('GET', 'http://example.com');
$mock($request, [])->wait();
self::assertSame($res, $c);
}
public function testInvokesOnRejected()
{
$e = new \Exception('a');
$c = null;
$mock = new MockHandler([$e], null, function ($v) use (&$c) {
$c = $v;
});
$request = new Request('GET', 'http://example.com');
$mock($request, [])->wait(false);
self::assertSame($e, $c);
}
/**
* @expectedException \OutOfBoundsException
*/
public function testThrowsWhenNoMoreResponses()
{
$mock = new MockHandler();
$request = new Request('GET', 'http://example.com');
$mock($request, []);
}
/**
* @expectedException \GuzzleHttp\Exception\BadResponseException
*/
public function testCanCreateWithDefaultMiddleware()
{
$r = new Response(500);
$mock = MockHandler::createWithMiddleware([$r]);
$request = new Request('GET', 'http://example.com');
$mock($request, ['http_errors' => true])->wait();
}
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();
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;
});
$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);
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);
}
}
<?php
namespace GuzzleHttp\Test\Handler;
use GuzzleHttp\Handler\MockHandler;
use GuzzleHttp\Handler\Proxy;
use GuzzleHttp\Psr7\Request;
use GuzzleHttp\RequestOptions;
use PHPUnit\Framework\TestCase;
/**
* @covers \GuzzleHttp\Handler\Proxy
*/
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;
}]);
$h = Proxy::wrapSync($m1, $m2);
$h(new Request('GET', 'http://foo.com'), []);
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;
}]);
$h = Proxy::wrapSync($m1, $m2);
$h(new Request('GET', 'http://foo.com'), [RequestOptions::SYNCHRONOUS => true]);
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;
}]);
$h = Proxy::wrapStreaming($m1, $m2);
$h(new Request('GET', 'http://foo.com'), []);
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;
}]);
$h = Proxy::wrapStreaming($m1, $m2);
$h(new Request('GET', 'http://foo.com'), ['stream' => true]);
self::assertNull($a);
self::assertNotNull($b);
}
}
<?php
namespace GuzzleHttp\Tests;
use GuzzleHttp\Cookie\CookieJar;
use GuzzleHttp\Handler\MockHandler;
use GuzzleHttp\HandlerStack;
use GuzzleHttp\Psr7\Request;
use GuzzleHttp\Psr7\Response;
use PHPUnit\Framework\TestCase;
class HandlerStackTest extends TestCase
{
public function testSetsHandlerInCtor()
{
$f = function () {
};
$m1 = function () {
};
$h = new HandlerStack($f, [$m1]);
self::assertTrue($h->hasHandler());
}
/**
* @doesNotPerformAssertions
*/
public function testCanSetDifferentHandlerAfterConstruction()
{
$f = function () {
};
$h = new HandlerStack();
$h->setHandler($f);
$h->resolve();
}
/**
* @expectedException \LogicException
*/
public function testEnsuresHandlerIsSet()
{
$h = new HandlerStack();
$h->resolve();
}
public function testPushInOrder()
{
$meths = $this->getFunctions();
$builder = new HandlerStack();
$builder->setHandler($meths[1]);
$builder->push($meths[2]);
$builder->push($meths[3]);
$builder->push($meths[4]);
$composed = $builder->resolve();
self::assertSame('Hello - test123', $composed('test'));
self::assertSame(
[['a', 'test'], ['b', 'test1'], ['c', 'test12']],
$meths[0]
);
}
public function testUnshiftsInReverseOrder()
{
$meths = $this->getFunctions();
$builder = new HandlerStack();
$builder->setHandler($meths[1]);
$builder->unshift($meths[2]);
$builder->unshift($meths[3]);
$builder->unshift($meths[4]);
$composed = $builder->resolve();
self::assertSame('Hello - test321', $composed('test'));
self::assertSame(
[['c', 'test'], ['b', 'test3'], ['a', 'test32']],
$meths[0]
);
}
public function testCanRemoveMiddlewareByInstance()
{
$meths = $this->getFunctions();
$builder = new HandlerStack();
$builder->setHandler($meths[1]);
$builder->push($meths[2]);
$builder->push($meths[2]);
$builder->push($meths[3]);
$builder->push($meths[4]);
$builder->push($meths[2]);
$builder->remove($meths[3]);
$composed = $builder->resolve();
self::assertSame('Hello - test1131', $composed('test'));
}
public function testCanPrintMiddleware()
{
$meths = $this->getFunctions();
$builder = new HandlerStack();
$builder->setHandler($meths[1]);
$builder->push($meths[2], 'a');
$builder->push([__CLASS__, 'foo']);
$builder->push([$this, 'bar']);
$builder->push(__CLASS__ . '::' . 'foo');
$lines = explode("\n", (string) $builder);
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()
{
$meths = $this->getFunctions();
$builder = new HandlerStack();
$builder->setHandler($meths[1]);
$builder->push($meths[2], 'foo');
$builder->before('foo', $meths[3], 'baz');
$builder->before('baz', $meths[4], 'bar');
$builder->before('baz', $meths[4], 'qux');
$lines = explode("\n", (string) $builder);
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]);
}
/**
* @expectedException \InvalidArgumentException
*/
public function testEnsuresHandlerExistsByName()
{
$builder = new HandlerStack();
$builder->before('foo', function () {
});
}
public function testCanAddAfterByName()
{
$meths = $this->getFunctions();
$builder = new HandlerStack();
$builder->setHandler($meths[1]);
$builder->push($meths[2], 'a');
$builder->push($meths[3], 'b');
$builder->after('a', $meths[4], 'c');
$builder->after('b', $meths[4], 'd');
$lines = explode("\n", (string) $builder);
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()
{
$mock = new MockHandler([
new Response(301, [
'Location' => 'http://foo.com/baz',
'Set-Cookie' => 'foo=bar; Domain=foo.com'
]),
new Response(200)
]);
$handler = HandlerStack::create($mock);
$request = new Request('GET', 'http://foo.com/bar');
$jar = new CookieJar();
$response = $handler($request, [
'allow_redirects' => true,
'cookies' => $jar
])->wait();
self::assertSame(200, $response->getStatusCode());
$lastRequest = $mock->getLastRequest();
self::assertSame('http://foo.com/baz', (string) $lastRequest->getUri());
self::assertSame('foo=bar', $lastRequest->getHeaderLine('Cookie'));
}
private function getFunctions()
{
$calls = [];
$a = function (callable $next) use (&$calls) {
return function ($v) use ($next, &$calls) {
$calls[] = ['a', $v];
return $next($v . '1');
};
};
$b = function (callable $next) use (&$calls) {
return function ($v) use ($next, &$calls) {
$calls[] = ['b', $v];
return $next($v . '2');
};
};
$c = function (callable $next) use (&$calls) {
return function ($v) use ($next, &$calls) {
$calls[] = ['c', $v];
return $next($v . '3');
};
};
$handler = function ($v) {
return 'Hello - ' . $v;
};
return [&$calls, $handler, $a, $b, $c];
}
public static function foo()
{
}
public function bar()
{
}
}
<?php
namespace GuzzleHttp\Tests;
use GuzzleHttp\Exception\RequestException;
use GuzzleHttp\MessageFormatter;
use GuzzleHttp\Psr7;
use GuzzleHttp\Psr7\Request;
use GuzzleHttp\Psr7\Response;
use PHPUnit\Framework\TestCase;
/**
* @covers GuzzleHttp\MessageFormatter
*/
class MessageFormatterTest extends TestCase
{
public function testCreatesWithClfByDefault()
{
$f = new MessageFormatter();
self::assertEquals(MessageFormatter::CLF, self::readAttribute($f, 'template'));
$f = new MessageFormatter(null);
self::assertEquals(MessageFormatter::CLF, self::readAttribute($f, 'template'));
}
public function dateProvider()
{
return [
['{ts}', '/^[0-9]{4}\-[0-9]{2}\-[0-9]{2}/'],
['{date_iso_8601}', '/^[0-9]{4}\-[0-9]{2}\-[0-9]{2}/'],
['{date_common_log}', '/^\d\d\/[A-Z][a-z]{2}\/[0-9]{4}/']
];
}
/**
* @dataProvider dateProvider
*/
public function testFormatsTimestamps($format, $pattern)
{
$f = new MessageFormatter($format);
$request = new Request('GET', '/');
$result = $f->format($request);
self::assertRegExp($pattern, $result);
}
public function formatProvider()
{
$request = new Request('PUT', '/', ['x-test' => 'abc'], Psr7\stream_for('foo'));
$response = new Response(200, ['X-Baz' => 'Bar'], Psr7\stream_for('baz'));
$err = new RequestException('Test', $request, $response);
return [
['{request}', [$request], Psr7\str($request)],
['{response}', [$request, $response], Psr7\str($response)],
['{request} {response}', [$request, $response], Psr7\str($request) . ' ' . Psr7\str($response)],
// Empty response yields no value
['{request} {response}', [$request], Psr7\str($request) . ' '],
['{req_headers}', [$request], "PUT / HTTP/1.1\r\nx-test: abc"],
['{res_headers}', [$request, $response], "HTTP/1.1 200 OK\r\nX-Baz: Bar"],
['{res_headers}', [$request], 'NULL'],
['{req_body}', [$request], 'foo'],
['{res_body}', [$request, $response], 'baz'],
['{res_body}', [$request], 'NULL'],
['{method}', [$request], $request->getMethod()],
['{url}', [$request], $request->getUri()],
['{target}', [$request], $request->getRequestTarget()],
['{req_version}', [$request], $request->getProtocolVersion()],
['{res_version}', [$request, $response], $response->getProtocolVersion()],
['{res_version}', [$request], 'NULL'],
['{host}', [$request], $request->getHeaderLine('Host')],
['{hostname}', [$request, $response], gethostname()],
['{hostname}{hostname}', [$request, $response], gethostname() . gethostname()],
['{code}', [$request, $response], $response->getStatusCode()],
['{code}', [$request], 'NULL'],
['{phrase}', [$request, $response], $response->getReasonPhrase()],
['{phrase}', [$request], 'NULL'],
['{error}', [$request, $response, $err], 'Test'],
['{error}', [$request], 'NULL'],
['{req_header_x-test}', [$request], 'abc'],
['{req_header_x-not}', [$request], ''],
['{res_header_X-Baz}', [$request, $response], 'Bar'],
['{res_header_x-not}', [$request, $response], ''],
['{res_header_X-Baz}', [$request], 'NULL'],
];
}
/**
* @dataProvider formatProvider
*/
public function testFormatsMessages($template, $args, $result)
{
$f = new MessageFormatter($template);
self::assertSame((string) $result, call_user_func_array([$f, 'format'], $args));
}
}
<?php
namespace GuzzleHttp\Tests;
use GuzzleHttp\Cookie\CookieJar;
use GuzzleHttp\Cookie\SetCookie;
use GuzzleHttp\Exception\RequestException;
use GuzzleHttp\Handler\MockHandler;
use GuzzleHttp\HandlerStack;
use GuzzleHttp\MessageFormatter;
use GuzzleHttp\Middleware;
use GuzzleHttp\Promise\PromiseInterface;
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\Test\TestLogger;
class MiddlewareTest extends TestCase
{
public function testAddsCookiesToRequests()
{
$jar = new CookieJar();
$m = Middleware::cookies($jar);
$h = new MockHandler(
[
function (RequestInterface $request) {
return new Response(200, [
'Set-Cookie' => (string) new SetCookie([
'Name' => 'name',
'Value' => 'value',
'Domain' => 'foo.com'
])
]);
}
]
);
$f = $m($h);
$f(new Request('GET', 'http://foo.com'), ['cookies' => $jar])->wait();
self::assertCount(1, $jar);
}
/**
* @expectedException \GuzzleHttp\Exception\ClientException
*/
public function testThrowsExceptionOnHttpClientError()
{
$m = Middleware::httpErrors();
$h = new MockHandler([new Response(404)]);
$f = $m($h);
$p = $f(new Request('GET', 'http://foo.com'), ['http_errors' => true]);
self::assertSame('pending', $p->getState());
$p->wait();
self::assertSame('rejected', $p->getState());
}
/**
* @expectedException \GuzzleHttp\Exception\ServerException
*/
public function testThrowsExceptionOnHttpServerError()
{
$m = Middleware::httpErrors();
$h = new MockHandler([new Response(500)]);
$f = $m($h);
$p = $f(new Request('GET', 'http://foo.com'), ['http_errors' => true]);
self::assertSame('pending', $p->getState());
$p->wait();
self::assertSame('rejected', $p->getState());
}
/**
* @dataProvider getHistoryUseCases
*/
public function testTracksHistory($container)
{
$m = Middleware::history($container);
$h = new MockHandler([new Response(200), new Response(201)]);
$f = $m($h);
$p1 = $f(new Request('GET', 'http://foo.com'), ['headers' => ['foo' => 'bar']]);
$p2 = $f(new Request('HEAD', 'http://foo.com'), ['headers' => ['foo' => 'baz']]);
$p1->wait();
$p2->wait();
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()
{
return [
[[]], // 1. Container is an array
[new \ArrayObject()] // 2. Container is an ArrayObject
];
}
public function testTracksHistoryForFailures()
{
$container = [];
$m = Middleware::history($container);
$request = new Request('GET', 'http://foo.com');
$h = new MockHandler([new RequestException('error', $request)]);
$f = $m($h);
$f($request, [])->wait(false);
self::assertCount(1, $container);
self::assertSame('GET', $container[0]['request']->getMethod());
self::assertInstanceOf(RequestException::class, $container[0]['error']);
}
public function testTapsBeforeAndAfter()
{
$calls = [];
$m = function ($handler) use (&$calls) {
return function ($request, $options) use ($handler, &$calls) {
$calls[] = '2';
return $handler($request, $options);
};
};
$m2 = Middleware::tap(
function (RequestInterface $request, array $options) use (&$calls) {
$calls[] = '1';
},
function (RequestInterface $request, array $options, PromiseInterface $p) use (&$calls) {
$calls[] = '3';
}
);
$h = new MockHandler([new Response()]);
$b = new HandlerStack($h);
$b->push($m2);
$b->push($m);
$comp = $b->resolve();
$p = $comp(new Request('GET', 'http://foo.com'), []);
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) {
self::assertSame('foo', $request->getHeaderLine('Bar'));
return new Response(200);
}
]);
$stack = new HandlerStack($h);
$stack->push(Middleware::mapRequest(function (RequestInterface $request) {
return $request->withHeader('Bar', 'foo');
}));
$comp = $stack->resolve();
$p = $comp(new Request('PUT', 'http://www.google.com'), []);
self::assertInstanceOf(PromiseInterface::class, $p);
}
public function testMapsResponse()
{
$h = new MockHandler([new Response(200)]);
$stack = new HandlerStack($h);
$stack->push(Middleware::mapResponse(function (ResponseInterface $response) {
return $response->withHeader('Bar', 'foo');
}));
$comp = $stack->resolve();
$p = $comp(new Request('PUT', 'http://www.google.com'), []);
$p->wait();
self::assertSame('foo', $p->wait()->getHeaderLine('Bar'));
}
public function testLogsRequestsAndResponses()
{
$h = new MockHandler([new Response(200)]);
$stack = new HandlerStack($h);
$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();
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 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();
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 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);
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']);
}
}
<?php
namespace GuzzleHttp\Tests;
use GuzzleHttp\Client;
use GuzzleHttp\Exception\ClientException;
use GuzzleHttp\Handler\MockHandler;
use GuzzleHttp\HandlerStack;
use GuzzleHttp\Pool;
use GuzzleHttp\Promise\Promise;
use GuzzleHttp\Psr7\Request;
use GuzzleHttp\Psr7\Response;
use PHPUnit\Framework\TestCase;
use Psr\Http\Message\RequestInterface;
class PoolTest extends TestCase
{
/**
* @expectedException \InvalidArgumentException
*/
public function testValidatesIterable()
{
$p = new Pool(new Client(), 'foo');
$p->promise()->wait();
}
/**
* @expectedException \InvalidArgumentException
*/
public function testValidatesEachElement()
{
$c = new Client();
$requests = ['foo'];
$p = new Pool($c, new \ArrayIterator($requests));
$p->promise()->wait();
}
/**
* @doesNotPerformAssertions
*/
public function testSendsAndRealizesFuture()
{
$c = $this->getClient();
$p = new Pool($c, [new Request('GET', 'http://example.com')]);
$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());
});
$handler = new MockHandler([$r1, $r2, $r3]);
$c = new Client(['handler' => $handler]);
$p = new Pool($c, [
new Request('GET', 'http://example.com'),
new Request('GET', 'http://example.com'),
new Request('GET', 'http://example.com'),
], ['pool_size' => 2]);
$p->promise()->wait();
}
public function testUsesRequestOptions()
{
$h = [];
$handler = new MockHandler([
function (RequestInterface $request) use (&$h) {
$h[] = $request;
return new Response();
}
]);
$c = new Client(['handler' => $handler]);
$opts = ['options' => ['headers' => ['x-foo' => 'bar']]];
$p = new Pool($c, [new Request('GET', 'http://example.com')], $opts);
$p->promise()->wait();
self::assertCount(1, $h);
self::assertTrue($h[0]->hasHeader('x-foo'));
}
public function testCanProvideCallablesThatReturnResponses()
{
$h = [];
$handler = new MockHandler([
function (RequestInterface $request) use (&$h) {
$h[] = $request;
return new Response();
}
]);
$c = new Client(['handler' => $handler]);
$optHistory = [];
$fn = function (array $opts) use (&$optHistory, $c) {
$optHistory = $opts;
return $c->request('GET', 'http://example.com', $opts);
};
$opts = ['options' => ['headers' => ['x-foo' => 'bar']]];
$p = new Pool($c, [$fn], $opts);
$p->promise()->wait();
self::assertCount(1, $h);
self::assertTrue($h[0]->hasHeader('x-foo'));
}
public function testBatchesResults()
{
$requests = [
new Request('GET', 'http://foo.com/200'),
new Request('GET', 'http://foo.com/201'),
new Request('GET', 'http://foo.com/202'),
new Request('GET', 'http://foo.com/404'),
];
$fn = function (RequestInterface $request) {
return new Response(substr($request->getUri()->getPath(), 1));
};
$mock = new MockHandler([$fn, $fn, $fn, $fn]);
$handler = HandlerStack::create($mock);
$client = new Client(['handler' => $handler]);
$results = Pool::batch($client, $requests);
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()
{
$requests = [
new Request('GET', 'http://foo.com/200'),
new Request('GET', 'http://foo.com/201')
];
$mock = new MockHandler([
function (RequestInterface $request) {
return new Response(substr($request->getUri()->getPath(), 1));
}
]);
$client = new Client(['handler' => $mock]);
$results = Pool::batch($client, $requests, [
'fulfilled' => function ($value) use (&$called) {
$called = true;
}
]);
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());
});
$handler = new MockHandler([$r1, $r2, $r3]);
$c = new Client(['handler' => $handler]);
$keys = [];
$requests = [
'request_1' => new Request('GET', 'http://example.com'),
'request_2' => new Request('GET', 'http://example.com'),
'request_3' => new Request('GET', 'http://example.com'),
];
$p = new Pool($c, $requests, [
'pool_size' => 2,
'fulfilled' => function ($res, $index) use (&$keys) {
$keys[] = $index;
}
]);
$p->promise()->wait();
self::assertCount(3, $keys);
self::assertSame($keys, array_keys($requests));
}
private function getClient($total = 1)
{
$queue = [];
for ($i = 0; $i < $total; $i++) {
$queue[] = new Response();
}
$handler = new MockHandler($queue);
return new Client(['handler' => $handler]);
}
}
<?php
namespace GuzzleHttp\Tests;
use GuzzleHttp\Handler\MockHandler;
use GuzzleHttp\HandlerStack;
use GuzzleHttp\Middleware;
use GuzzleHttp\Promise\PromiseInterface;
use GuzzleHttp\Psr7;
use GuzzleHttp\Psr7\FnStream;
use GuzzleHttp\Psr7\Request;
use GuzzleHttp\Psr7\Response;
use PHPUnit\Framework\TestCase;
use Psr\Http\Message\RequestInterface;
class PrepareBodyMiddlewareTest extends TestCase
{
public function methodProvider()
{
$methods = ['GET', 'PUT', 'POST'];
$bodies = ['Test', ''];
foreach ($methods as $method) {
foreach ($bodies as $body) {
yield [$method, $body];
}
}
}
/**
* @dataProvider methodProvider
*/
public function testAddsContentLengthWhenMissingAndPossible($method, $body)
{
$h = new MockHandler([
function (RequestInterface $request) use ($body) {
$length = strlen($body);
if ($length > 0) {
self::assertEquals($length, $request->getHeaderLine('Content-Length'));
} else {
self::assertFalse($request->hasHeader('Content-Length'));
}
return new Response(200);
}
]);
$m = Middleware::prepareBody();
$stack = new HandlerStack($h);
$stack->push($m);
$comp = $stack->resolve();
$p = $comp(new Request($method, 'http://www.google.com', [], $body), []);
self::assertInstanceOf(PromiseInterface::class, $p);
$response = $p->wait();
self::assertSame(200, $response->getStatusCode());
}
public function testAddsTransferEncodingWhenNoContentLength()
{
$body = FnStream::decorate(Psr7\stream_for('foo'), [
'getSize' => function () {
return null;
}
]);
$h = new MockHandler([
function (RequestInterface $request) {
self::assertFalse($request->hasHeader('Content-Length'));
self::assertSame('chunked', $request->getHeaderLine('Transfer-Encoding'));
return new Response(200);
}
]);
$m = Middleware::prepareBody();
$stack = new HandlerStack($h);
$stack->push($m);
$comp = $stack->resolve();
$p = $comp(new Request('PUT', 'http://www.google.com', [], $body), []);
self::assertInstanceOf(PromiseInterface::class, $p);
$response = $p->wait();
self::assertSame(200, $response->getStatusCode());
}
public function testAddsContentTypeWhenMissingAndPossible()
{
$bd = Psr7\stream_for(fopen(__DIR__ . '/../composer.json', 'r'));
$h = new MockHandler([
function (RequestInterface $request) {
self::assertSame('application/json', $request->getHeaderLine('Content-Type'));
self::assertTrue($request->hasHeader('Content-Length'));
return new Response(200);
}
]);
$m = Middleware::prepareBody();
$stack = new HandlerStack($h);
$stack->push($m);
$comp = $stack->resolve();
$p = $comp(new Request('PUT', 'http://www.google.com', [], $bd), []);
self::assertInstanceOf(PromiseInterface::class, $p);
$response = $p->wait();
self::assertSame(200, $response->getStatusCode());
}
public function expectProvider()
{
return [
[true, ['100-Continue']],
[false, []],
[10, ['100-Continue']],
[500000, []]
];
}
/**
* @dataProvider expectProvider
*/
public function testAddsExpect($value, $result)
{
$bd = Psr7\stream_for(fopen(__DIR__ . '/../composer.json', 'r'));
$h = new MockHandler([
function (RequestInterface $request) use ($result) {
self::assertSame($result, $request->getHeader('Expect'));
return new Response(200);
}
]);
$m = Middleware::prepareBody();
$stack = new HandlerStack($h);
$stack->push($m);
$comp = $stack->resolve();
$p = $comp(new Request('PUT', 'http://www.google.com', [], $bd), [
'expect' => $value
]);
self::assertInstanceOf(PromiseInterface::class, $p);
$response = $p->wait();
self::assertSame(200, $response->getStatusCode());
}
public function testIgnoresIfExpectIsPresent()
{
$bd = Psr7\stream_for(fopen(__DIR__ . '/../composer.json', 'r'));
$h = new MockHandler([
function (RequestInterface $request) {
self::assertSame(['Foo'], $request->getHeader('Expect'));
return new Response(200);
}
]);
$m = Middleware::prepareBody();
$stack = new HandlerStack($h);
$stack->push($m);
$comp = $stack->resolve();
$p = $comp(
new Request('PUT', 'http://www.google.com', ['Expect' => 'Foo'], $bd),
['expect' => true]
);
self::assertInstanceOf(PromiseInterface::class, $p);
$response = $p->wait();
self::assertSame(200, $response->getStatusCode());
}
}
<?php
namespace GuzzleHttp\Tests;
use GuzzleHttp\Client;
use GuzzleHttp\Handler\MockHandler;
use GuzzleHttp\Middleware;
use GuzzleHttp\Psr7;
use GuzzleHttp\Psr7\Request;
use GuzzleHttp\Psr7\Response;
use GuzzleHttp\RetryMiddleware;
use PHPUnit\Framework\TestCase;
class RetryMiddlewareTest extends TestCase
{
public function testRetriesWhenDeciderReturnsTrue()
{
$delayCalls = 0;
$calls = [];
$decider = function ($retries, $request, $response, $error) use (&$calls) {
$calls[] = func_get_args();
return count($calls) < 3;
};
$delay = function ($retries, $response) use (&$delayCalls) {
$delayCalls++;
self::assertSame($retries, $delayCalls);
self::assertInstanceOf(Response::class, $response);
return 1;
};
$m = Middleware::retry($decider, $delay);
$h = new MockHandler([new Response(200), new Response(201), new Response(202)]);
$f = $m($h);
$c = new Client(['handler' => $f]);
$p = $c->sendAsync(new Request('GET', 'http://test.com'), []);
$p->wait();
self::assertCount(3, $calls);
self::assertSame(2, $delayCalls);
self::assertSame(202, $p->wait()->getStatusCode());
}
public function testDoesNotRetryWhenDeciderReturnsFalse()
{
$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'), []);
self::assertSame(200, $p->wait()->getStatusCode());
}
public function testCanRetryExceptions()
{
$calls = [];
$decider = function ($retries, $request, $response, $error) use (&$calls) {
$calls[] = func_get_args();
return $error instanceof \Exception;
};
$m = Middleware::retry($decider);
$h = new MockHandler([new \Exception(), new Response(201)]);
$c = new Client(['handler' => $m($h)]);
$p = $c->sendAsync(new Request('GET', 'http://test.com'), []);
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()
{
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));
}
}
<?php
namespace GuzzleHttp\Tests;
use GuzzleHttp\Client;
use GuzzleHttp\Psr7;
use Psr\Http\Message\ResponseInterface;
/**
* The Server class is used to control a scripted webserver using node.js that
* will respond to HTTP requests with queued responses.
*
* Queued responses will be served to requests using a FIFO order. All requests
* received by the server are stored on the node.js server and can be retrieved
* by calling {@see Server::received()}.
*
* Mock responses that don't require data to be transmitted over HTTP a great
* for testing. Mock response, however, cannot test the actual sending of an
* HTTP request using cURL. This test server allows the simulation of any
* number of HTTP request response transactions to test the actual sending of
* requests over the wire without having to leave an internal network.
*/
class Server
{
/** @var Client */
private static $client;
private static $started = false;
public static $url = 'http://127.0.0.1:8126/';
public static $port = 8126;
/**
* Flush the received requests from the server
* @throws \RuntimeException
*/
public static function flush()
{
return self::getClient()->request('DELETE', 'guzzle-server/requests');
}
/**
* Queue an array of responses or a single response on the server.
*
* Any currently queued responses will be overwritten. Subsequent requests
* on the server will return queued responses in FIFO order.
*
* @param array|ResponseInterface $responses A single or array of Responses
* to queue.
* @throws \Exception
*/
public static function enqueue($responses)
{
$data = [];
foreach ((array) $responses as $response) {
if (!($response instanceof ResponseInterface)) {
throw new \Exception('Invalid response given.');
}
$headers = array_map(function ($h) {
return implode(' ,', $h);
}, $response->getHeaders());
$data[] = [
'status' => (string) $response->getStatusCode(),
'reason' => $response->getReasonPhrase(),
'headers' => $headers,
'body' => base64_encode((string) $response->getBody())
];
}
self::getClient()->request('PUT', 'guzzle-server/responses', [
'json' => $data
]);
}
/**
* Get all of the received requests
*
* @return ResponseInterface[]
* @throws \RuntimeException
*/
public static function received()
{
if (!self::$started) {
return [];
}
$response = self::getClient()->request('GET', 'guzzle-server/requests');
$data = json_decode($response->getBody(), true);
return array_map(
function ($message) {
$uri = $message['uri'];
if (isset($message['query_string'])) {
$uri .= '?' . $message['query_string'];
}
$response = new Psr7\Request(
$message['http_method'],
$uri,
$message['headers'],
$message['body'],
$message['version']
);
return $response->withUri(
$response->getUri()
->withScheme('http')
->withHost($response->getHeaderLine('host'))
);
},
$data
);
}
/**
* Stop running the node.js server
*/
public static function stop()
{
if (self::$started) {
self::getClient()->request('DELETE', 'guzzle-server');
}
self::$started = false;
}
public static function wait($maxTries = 5)
{
$tries = 0;
while (!self::isListening() && ++$tries < $maxTries) {
usleep(100000);
}
if (!self::isListening()) {
throw new \RuntimeException('Unable to contact node.js server');
}
}
public static function start()
{
if (self::$started) {
return;
}
if (!self::isListening()) {
exec('node ' . __DIR__ . '/server.js '
. self::$port . ' >> /tmp/server.log 2>&1 &');
self::wait();
}
self::$started = true;
}
private static function isListening()
{
try {
self::getClient()->request('GET', 'guzzle-server/perf', [
'connect_timeout' => 5,
'timeout' => 5
]);
return true;
} catch (\Exception $e) {
return false;
}
}
private static function getClient()
{
if (!self::$client) {
self::$client = new Client([
'base_uri' => self::$url,
'sync' => true,
]);
}
return self::$client;
}
}
<?php
namespace GuzzleHttp\Tests;
use GuzzleHttp\Psr7;
use GuzzleHttp\TransferStats;
use PHPUnit\Framework\TestCase;
class TransferStatsTest extends TestCase
{
public function testHasData()
{
$request = new Psr7\Request('GET', 'http://foo.com');
$response = new Psr7\Response();
$stats = new TransferStats(
$request,
$response,
10.5,
null,
['foo' => 'bar']
);
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());
}
}
<?php
namespace GuzzleHttp\Tests;
use GuzzleHttp\UriTemplate;
use PHPUnit\Framework\TestCase;
/**
* @covers GuzzleHttp\UriTemplate
*/
class UriTemplateTest extends TestCase
{
/**
* @return array
*/
public function templateProvider()
{
$params = [
'var' => 'value',
'hello' => 'Hello World!',
'empty' => '',
'path' => '/foo/bar',
'x' => '1024',
'y' => '768',
'null' => null,
'list' => ['red', 'green', 'blue'],
'keys' => [
"semi" => ';',
"dot" => '.',
"comma" => ','
],
'empty_keys' => [],
];
return array_map(function ($t) use ($params) {
$t[] = $params;
return $t;
}, [
['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
['test{&missing*}', 'test'],
// Test that multiple expansions can be set
['http://{var}/{var:2}{?keys*}', 'http://value/va?semi=%3B&dot=.&comma=%2C'],
// Test more complex query string stuff
['http://www.test.com{+path}{?var,keys*}', 'http://www.test.com/foo/bar?var=value&semi=%3B&dot=.&comma=%2C']
]);
}
/**
* @dataProvider templateProvider
*/
public function testExpandsUriTemplates($template, $expansion, $params)
{
$uri = new UriTemplate();
self::assertSame($expansion, $uri->expand($template, $params));
}
public function expressionProvider()
{
return [
[
'{+var*}', [
'operator' => '+',
'values' => [
['modifier' => '*', 'value' => 'var']
]
],
],
[
'{?keys,var,val}', [
'operator' => '?',
'values' => [
['value' => 'keys', 'modifier' => ''],
['value' => 'var', 'modifier' => ''],
['value' => 'val', 'modifier' => '']
]
],
],
[
'{+x,hello,y}', [
'operator' => '+',
'values' => [
['value' => 'x', 'modifier' => ''],
['value' => 'hello', 'modifier' => ''],
['value' => 'y', 'modifier' => '']
]
]
]
];
}
/**
* @dataProvider expressionProvider
*/
public function testParsesExpressions($exp, $data)
{
$template = new UriTemplate();
// Access the config object
$class = new \ReflectionClass($template);
$method = $class->getMethod('parseExpression');
$method->setAccessible(true);
$exp = substr($exp, 1, -1);
self::assertSame($data, $method->invokeArgs($template, [$exp]));
}
/**
* @ticket https://github.com/guzzle/guzzle/issues/90
*/
public function testAllowsNestedArrayExpansion()
{
$template = new UriTemplate();
$result = $template->expand('http://example.com{+path}{/segments}{?query,data*,foo*}', [
'path' => '/foo/bar',
'segments' => ['one', 'two'],
'query' => 'test',
'data' => [
'more' => ['fun', 'ice cream']
],
'foo' => [
'baz' => [
'bar' => 'fizz',
'test' => 'buzz'
],
'bam' => 'boo'
]
]);
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);
}
}
<?php
if (!defined('IDNA_DEFAULT')) {
define('IDNA_DEFAULT', 0);
}
if (!defined('INTL_IDNA_VARIANT_UTS46')) {
define('INTL_IDNA_VARIANT_UTS46', 1);
}
<?php
namespace {
setlocale(LC_ALL, 'C');
}
namespace GuzzleHttp\Test {
require __DIR__ . '/../vendor/autoload.php';
require __DIR__ . '/Server.php';
use GuzzleHttp\Tests\Server;
Server::start();
register_shutdown_function(function () {
Server::stop();
});
}
// Override curl_setopt_array() and curl_multi_setopt() to get the last set curl options
namespace GuzzleHttp\Handler {
function curl_setopt_array($handle, array $options)
{
if (!empty($_SERVER['curl_test'])) {
$_SERVER['_curl'] = $options;
} else {
unset($_SERVER['_curl']);
}
return \curl_setopt_array($handle, $options);
}
function curl_multi_setopt($handle, $option, $value)
{
if (!empty($_SERVER['curl_test'])) {
$_SERVER['_curl_multi'][$option] = $value;
} else {
unset($_SERVER['_curl_multi']);
}
return \curl_multi_setopt($handle, $option, $value);
}
}
<?php
namespace GuzzleHttp\Test;
use GuzzleHttp;
use PHPUnit\Framework\TestCase;
class FunctionsTest extends TestCase
{
public function testExpandsTemplate()
{
self::assertSame(
'foo/123',
GuzzleHttp\uri_template('foo/{bar}', ['bar' => '123'])
);
}
public function noBodyProvider()
{
return [['get'], ['head'], ['delete']];
}
public function testProvidesDefaultUserAgent()
{
$ua = GuzzleHttp\default_user_agent();
self::assertRegExp('#^GuzzleHttp/.+ curl/.+ PHP/.+$#', $ua);
}
public function typeProvider()
{
return [
['foo', 'string(3) "foo"'],
[true, 'bool(true)'],
[false, 'bool(false)'],
[10, 'int(10)'],
[1.0, 'float(1)'],
[new StrClass(), 'object(GuzzleHttp\Test\StrClass)'],
[['foo'], 'array(1)']
];
}
/**
* @dataProvider typeProvider
*/
public function testDescribesType($input, $output)
{
self::assertSame($output, GuzzleHttp\describe_type($input));
}
public function testParsesHeadersFromLines()
{
$lines = ['Foo: bar', 'Foo: baz', 'Abc: 123', 'Def: a, b'];
self::assertSame([
'Foo' => ['bar', 'baz'],
'Abc' => ['123'],
'Def' => ['a, b'],
], GuzzleHttp\headers_from_lines($lines));
}
public function testParsesHeadersFromLinesWithMultipleLines()
{
$lines = ['Foo: bar', 'Foo: baz', 'Foo: 123'];
self::assertSame([
'Foo' => ['bar', 'baz', '123'],
], GuzzleHttp\headers_from_lines($lines));
}
public function testReturnsDebugResource()
{
self::assertInternalType('resource', GuzzleHttp\debug_resource());
}
public function testProvidesDefaultCaBundler()
{
self::assertFileExists(GuzzleHttp\default_ca_bundle());
}
public function noProxyProvider()
{
return [
['mit.edu', ['.mit.edu'], false],
['foo.mit.edu', ['.mit.edu'], true],
['mit.edu', ['mit.edu'], true],
['mit.edu', ['baz', 'mit.edu'], true],
['mit.edu', ['', '', 'mit.edu'], true],
['mit.edu', ['baz', '*'], true],
];
}
/**
* @dataProvider noproxyProvider
*/
public function testChecksNoProxyList($host, $list, $result)
{
self::assertSame(
$result,
\GuzzleHttp\is_host_in_noproxy($host, $list)
);
}
/**
* @expectedException \InvalidArgumentException
*/
public function testEnsuresNoProxyCheckHostIsSet()
{
\GuzzleHttp\is_host_in_noproxy('', []);
}
public function testEncodesJson()
{
self::assertSame('true', \GuzzleHttp\json_encode(true));
}
/**
* @expectedException \InvalidArgumentException
*/
public function testEncodesJsonAndThrowsOnError()
{
\GuzzleHttp\json_encode("\x99");
}
public function testDecodesJson()
{
self::assertTrue(\GuzzleHttp\json_decode('true'));
}
/**
* @expectedException \InvalidArgumentException
*/
public function testDecodesJsonAndThrowsOnError()
{
\GuzzleHttp\json_decode('{{]]');
}
public function testCurrentTime()
{
self::assertGreaterThan(0, GuzzleHttp\_current_time());
}
public function testIdnConvert()
{
if (!extension_loaded('intl')) {
self::markTestSkipped('intl PHP extension is not loaded');
}
$uri = GuzzleHttp\Psr7\uri_for('https://яндекс.рф/images');
$uri = GuzzleHttp\_idn_uri_convert($uri);
self::assertSame('xn--d1acpjx3f.xn--p1ai', $uri->getHost());
}
}
final class StrClass
{
public function __toString()
{
return 'foo';
}
}
/**
* Guzzle node.js test server to return queued responses to HTTP requests and
* expose a RESTful API for enqueueing responses and retrieving the requests
* that have been received.
*
* - Delete all requests that have been received:
* > DELETE /guzzle-server/requests
* > Host: 127.0.0.1:8126
*
* - Enqueue responses
* > PUT /guzzle-server/responses
* > Host: 127.0.0.1:8126
* >
* > [{'status': 200, 'reason': 'OK', 'headers': {}, 'body': '' }]
*
* - Get the received requests
* > GET /guzzle-server/requests
* > Host: 127.0.0.1:8126
*
* < HTTP/1.1 200 OK
* <
* < [{'http_method': 'GET', 'uri': '/', 'headers': {}, 'body': 'string'}]
*
* - Attempt access to the secure area
* > GET /secure/by-digest/qop-auth/guzzle-server/requests
* > Host: 127.0.0.1:8126
*
* < HTTP/1.1 401 Unauthorized
* < WWW-Authenticate: Digest realm="Digest Test", qop="auth", nonce="0796e98e1aeef43141fab2a66bf4521a", algorithm="MD5", stale="false"
* <
* < 401 Unauthorized
*
* - Shutdown the server
* > DELETE /guzzle-server
* > Host: 127.0.0.1:8126
*
* @package Guzzle PHP <http://www.guzzlephp.org>
* @license See the LICENSE file that was distributed with this source code.
*/
var http = require('http');
var url = require('url');
/**
* Guzzle node.js server
* @class
*/
var GuzzleServer = function(port, log) {
this.port = port;
this.log = log;
this.responses = [];
this.requests = [];
var that = this;
var md5 = function(input) {
var crypto = require('crypto');
var hasher = crypto.createHash('md5');
hasher.update(input);
return hasher.digest('hex');
};
/**
* Node.js HTTP server authentication module.
*
* It is only initialized on demand (by loadAuthentifier). This avoids
* requiring the dependency to http-auth on standard operations, and the
* performance hit at startup.
*/
var auth;
/**
* Provides authentication handlers (Basic, Digest).
*/
var loadAuthentifier = function(type, options) {
var typeId = type;
if (type == 'digest') {
typeId += '.'+(options && options.qop ? options.qop : 'none');
}
if (!loadAuthentifier[typeId]) {
if (!auth) {
try {
auth = require('http-auth');
} catch (e) {
if (e.code == 'MODULE_NOT_FOUND') {
return;
}
}
}
switch (type) {
case 'digest':
var digestParams = {
realm: 'Digest Test',
login: 'me',
password: 'test'
};
if (options && options.qop) {
digestParams.qop = options.qop;
}
loadAuthentifier[typeId] = auth.digest(digestParams, function(username, callback) {
callback(md5(digestParams.login + ':' + digestParams.realm + ':' + digestParams.password));
});
break
}
}
return loadAuthentifier[typeId];
};
var firewallRequest = function(request, req, res, requestHandlerCallback) {
var securedAreaUriParts = request.uri.match(/^\/secure\/by-(digest)(\/qop-([^\/]*))?(\/.*)$/);
if (securedAreaUriParts) {
var authentifier = loadAuthentifier(securedAreaUriParts[1], { qop: securedAreaUriParts[2] });
if (!authentifier) {
res.writeHead(501, 'HTTP authentication not implemented', { 'Content-Length': 0 });
res.end();
return;
}
authentifier.check(req, res, function(req, res) {
req.url = securedAreaUriParts[4];
requestHandlerCallback(request, req, res);
});
} else {
requestHandlerCallback(request, req, res);
}
};
var controlRequest = function(request, req, res) {
if (req.url == '/guzzle-server/perf') {
res.writeHead(200, 'OK', {'Content-Length': 16});
res.end('Body of response');
} else if (req.method == 'DELETE') {
if (req.url == '/guzzle-server/requests') {
// Clear the received requests
that.requests = [];
res.writeHead(200, 'OK', { 'Content-Length': 0 });
res.end();
if (that.log) {
console.log('Flushing requests');
}
} else if (req.url == '/guzzle-server') {
// Shutdown the server
res.writeHead(200, 'OK', { 'Content-Length': 0, 'Connection': 'close' });
res.end();
if (that.log) {
console.log('Shutting down');
}
that.server.close();
}
} else if (req.method == 'GET') {
if (req.url === '/guzzle-server/requests') {
if (that.log) {
console.log('Sending received requests');
}
// Get received requests
var body = JSON.stringify(that.requests);
res.writeHead(200, 'OK', { 'Content-Length': body.length });
res.end(body);
} else if (req.url == '/guzzle-server/read-timeout') {
if (that.log) {
console.log('Sleeping');
}
res.writeHead(200, 'OK');
res.write("sleeping 60 seconds ...\n");
setTimeout(function () {
res.end("slept 60 seconds\n");
}, 60*1000);
}
} else if (req.method == 'PUT' && req.url == '/guzzle-server/responses') {
if (that.log) {
console.log('Adding responses...');
}
if (!request.body) {
if (that.log) {
console.log('No response data was provided');
}
res.writeHead(400, 'NO RESPONSES IN REQUEST', { 'Content-Length': 0 });
} else {
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.from(that.responses[i].body, 'base64');
}
}
if (that.log) {
console.log(that.responses);
}
res.writeHead(200, 'OK', { 'Content-Length': 0 });
}
res.end();
}
};
var receivedRequest = function(request, req, res) {
if (req.url.indexOf('/guzzle-server') === 0) {
controlRequest(request, req, res);
} else if (req.url.indexOf('/guzzle-server') == -1 && !that.responses.length) {
res.writeHead(500);
res.end('No responses in queue');
} else {
if (that.log) {
console.log('Returning response from queue and adding request');
}
that.requests.push(request);
var response = that.responses.shift();
res.writeHead(response.status, response.reason, response.headers);
res.end(response.body);
}
};
this.start = function() {
that.server = http.createServer(function(req, res) {
var parts = url.parse(req.url, false);
var request = {
http_method: req.method,
scheme: parts.scheme,
uri: parts.pathname,
query_string: parts.query,
headers: req.headers,
version: req.httpVersion,
body: ''
};
// Receive each chunk of the request body
req.addListener('data', function(chunk) {
request.body += chunk;
});
// Called when the request completes
req.addListener('end', function() {
firewallRequest(request, req, res, receivedRequest);
});
});
that.server.listen(this.port, '127.0.0.1');
if (this.log) {
console.log('Server running at http://127.0.0.1:8126/');
}
};
};
// Get the port from the arguments
port = process.argv.length >= 3 ? process.argv[2] : 8126;
log = process.argv.length >= 4 ? process.argv[3] : false;
// Start the server
server = new GuzzleServer(port, log);
server.start();
root = true
[*]
charset = utf-8
end_of_line = lf
indent_size = 4
indent_style = space
insert_final_newline = true
trim_trailing_whitespace = true
.editorconfig export-ignore
.gitattributes export-ignore
/.github/ export-ignore
.gitignore export-ignore
/.travis.yml export-ignore
/phpunit.xml.dist export-ignore
/tests/ export-ignore
artifacts/
vendor/
composer.lock
phpunit.xml
language: php
dist: xenial
cache:
directories:
- $HOME/.composer/cache/files
php:
- 5.6
- 7.0
- 7.1
- 7.2
- 7.3
- 7.4
env:
global:
- TEST_COMMAND="composer test"
matrix:
fast_finish: true
include:
- php: 5.6
env: COMPOSER_FLAGS="--prefer-stable --prefer-lowest" COVERAGE=true TEST_COMMAND="composer test-ci"
before_install:
- if [[ $COVERAGE != true ]]; then phpenv config-rm xdebug.ini || true; fi
install:
- travis_retry composer update ${COMPOSER_FLAGS} --prefer-dist --no-interaction
script:
- $TEST_COMMAND
...@@ -88,7 +88,7 @@ $promise ...@@ -88,7 +88,7 @@ $promise
}); });
// Resolving the promise triggers the $onFulfilled callbacks and outputs // Resolving the promise triggers the $onFulfilled callbacks and outputs
// "Hello, reader". // "Hello, reader."
$promise->resolve('reader.'); $promise->resolve('reader.');
``` ```
......
...@@ -11,10 +11,10 @@ ...@@ -11,10 +11,10 @@
} }
], ],
"require": { "require": {
"php": ">=5.5.0" "php": ">=5.6"
}, },
"require-dev": { "require-dev": {
"phpunit/phpunit": "^4.0" "phpunit/phpunit": "^5.7.27 || ^7.5"
}, },
"autoload": { "autoload": {
"psr-4": { "psr-4": {
...@@ -22,6 +22,11 @@ ...@@ -22,6 +22,11 @@
}, },
"files": ["src/functions_include.php"] "files": ["src/functions_include.php"]
}, },
"autoload-dev": {
"psr-4": {
"GuzzleHttp\\Promise\\Tests\\": "tests/"
}
},
"scripts": { "scripts": {
"test": "vendor/bin/phpunit", "test": "vendor/bin/phpunit",
"test-ci": "vendor/bin/phpunit --coverage-text" "test-ci": "vendor/bin/phpunit --coverage-text"
......
<?xml version="1.0" encoding="UTF-8"?>
<phpunit
colors="true"
beStrictAboutOutputDuringTests="true"
beStrictAboutTestsThatDoNotTestAnything="true"
bootstrap="vendor/autoload.php"
>
<testsuites>
<testsuite name="GuzzleHttp Promise Test Suite">
<directory>tests/</directory>
</testsuite>
</testsuites>
<filter>
<whitelist>
<directory>src/</directory>
<exclude>
<directory suffix="Interface.php">src/</directory>
</exclude>
</whitelist>
</filter>
</phpunit>
...@@ -65,7 +65,13 @@ final class Coroutine implements PromiseInterface ...@@ -65,7 +65,13 @@ final class Coroutine implements PromiseInterface
$this->currentPromise->wait(); $this->currentPromise->wait();
} }
}); });
try {
$this->nextCoroutine($this->generator->current()); $this->nextCoroutine($this->generator->current());
} catch (\Exception $exception) {
$this->result->reject($exception);
} catch (Throwable $throwable) {
$this->result->reject($throwable);
}
} }
public function then( public function then(
......
...@@ -74,7 +74,9 @@ class EachPromise implements PromisorInterface ...@@ -74,7 +74,9 @@ class EachPromise implements PromisorInterface
try { try {
$this->createPromise(); $this->createPromise();
$this->iterable->rewind(); $this->iterable->rewind();
if (!$this->checkIfFinished()) {
$this->refillPending(); $this->refillPending();
}
} catch (\Throwable $e) { } catch (\Throwable $e) {
$this->aggregate->reject($e); $this->aggregate->reject($e);
} catch (\Exception $e) { } catch (\Exception $e) {
...@@ -89,11 +91,6 @@ class EachPromise implements PromisorInterface ...@@ -89,11 +91,6 @@ class EachPromise implements PromisorInterface
$this->mutex = false; $this->mutex = false;
$this->aggregate = new Promise(function () { $this->aggregate = new Promise(function () {
reset($this->pending); reset($this->pending);
if (empty($this->pending) && !$this->iterable->valid()) {
$this->aggregate->resolve(null);
return;
}
// Consume a potentially fluctuating list of promises while // Consume a potentially fluctuating list of promises while
// ensuring that indexes are maintained (precluding array_shift). // ensuring that indexes are maintained (precluding array_shift).
while ($promise = current($this->pending)) { while ($promise = current($this->pending)) {
...@@ -149,21 +146,27 @@ class EachPromise implements PromisorInterface ...@@ -149,21 +146,27 @@ class EachPromise implements PromisorInterface
} }
$promise = promise_for($this->iterable->current()); $promise = promise_for($this->iterable->current());
$idx = $this->iterable->key(); $key = $this->iterable->key();
// Iterable keys may not be unique, so we add the promises at the end
// of the pending array and retrieve the array index being used
$this->pending[] = null;
end($this->pending);
$idx = key($this->pending);
$this->pending[$idx] = $promise->then( $this->pending[$idx] = $promise->then(
function ($value) use ($idx) { function ($value) use ($idx, $key) {
if ($this->onFulfilled) { if ($this->onFulfilled) {
call_user_func( call_user_func(
$this->onFulfilled, $value, $idx, $this->aggregate $this->onFulfilled, $value, $key, $this->aggregate
); );
} }
$this->step($idx); $this->step($idx);
}, },
function ($reason) use ($idx) { function ($reason) use ($idx, $key) {
if ($this->onRejected) { if ($this->onRejected) {
call_user_func( call_user_func(
$this->onRejected, $reason, $idx, $this->aggregate $this->onRejected, $reason, $key, $this->aggregate
); );
} }
$this->step($idx); $this->step($idx);
......
polyfill-php80 @ 8854dc88
This diff is collapsed. Click to expand it.
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment