Issue #2276119 by damiankloip: Upgrade Guzzle to version 4.1.0.

8.0.x
Alex Pott 2014-06-03 11:29:35 -05:00
parent c8faf220f0
commit 116b4f7cee
55 changed files with 1046 additions and 417 deletions

View File

@ -19,7 +19,7 @@
"twig/twig": "1.15.*",
"doctrine/common": "dev-master#a45d110f71c323e29f41eb0696fa230e3fa1b1b5",
"doctrine/annotations": "dev-master#463d926a8dcc49271cb7db5a08364a70ed6e3cd3",
"guzzlehttp/guzzle": "4.0.*",
"guzzlehttp/guzzle": "4.1.*",
"kriswallsmith/assetic": "1.1.*@alpha",
"symfony-cmf/routing": "1.1.*@alpha",
"easyrdf/easyrdf": "0.8.*",

37
composer.lock generated
View File

@ -1,9 +1,10 @@
{
"_readme": [
"This file locks the dependencies of your project to a known state",
"Read more about it at http://getcomposer.org/doc/01-basic-usage.md#composer-lock-the-lock-file"
"Read more about it at http://getcomposer.org/doc/01-basic-usage.md#composer-lock-the-lock-file",
"This file is @generated automatically"
],
"hash": "cc429f39777a4435a2d1415648978f97",
"hash": "15b33bcba2392fb947bc320f35fcc14e",
"packages": [
{
"name": "doctrine/annotations",
@ -458,26 +459,30 @@
},
{
"name": "guzzlehttp/guzzle",
"version": "4.0.0",
"version": "4.1.0",
"source": {
"type": "git",
"url": "https://github.com/guzzle/guzzle.git",
"reference": "4063f08ca434efac12bf7a3db0d370b1c451345b"
"reference": "85a0ba7de064493c928a8bcdc5eef01e0bde9953"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/guzzle/guzzle/zipball/4063f08ca434efac12bf7a3db0d370b1c451345b",
"reference": "4063f08ca434efac12bf7a3db0d370b1c451345b",
"url": "https://api.github.com/repos/guzzle/guzzle/zipball/85a0ba7de064493c928a8bcdc5eef01e0bde9953",
"reference": "85a0ba7de064493c928a8bcdc5eef01e0bde9953",
"shasum": ""
},
"require": {
"guzzlehttp/streams": "1.*",
"ext-json": "*",
"guzzlehttp/streams": "~1.0",
"php": ">=5.4.0"
},
"require-dev": {
"ext-curl": "*",
"phpunit/phpunit": "4.*",
"psr/log": "~1"
"phpunit/phpunit": "~4.0",
"psr/log": "~1.0"
},
"suggest": {
"ext-curl": "Guzzle will use specific adapters if cURL is present"
},
"type": "library",
"extra": {
@ -515,7 +520,7 @@
"rest",
"web service"
],
"time": "2014-03-29 23:11:36"
"time": "2014-05-28 05:13:19"
},
{
"name": "guzzlehttp/streams",
@ -2098,12 +2103,8 @@
"time": "2013-06-12 19:46:58"
}
],
"packages-dev": [
],
"aliases": [
],
"packages-dev": [],
"aliases": [],
"minimum-stability": "stable",
"stability-flags": {
"symfony/yaml": 20,
@ -2115,7 +2116,5 @@
"platform": {
"php": ">=5.4.2"
},
"platform-dev": [
]
"platform-dev": []
}

View File

@ -266,7 +266,7 @@ class ClassLoader
public function loadClass($class)
{
if ($file = $this->findFile($class)) {
include $file;
includeFile($file);
return true;
}
@ -291,8 +291,25 @@ class ClassLoader
return $this->classMap[$class];
}
$file = $this->findFileWithExtension($class, '.php');
// Search for Hack files if we are running on HHVM
if ($file === null && defined('HHVM_VERSION')) {
$file = $this->findFileWithExtension($class, '.hh');
}
if ($file === null) {
// Remember that this class does not exist.
return $this->classMap[$class] = false;
}
return $file;
}
private function findFileWithExtension($class, $ext)
{
// PSR-4 lookup
$logicalPathPsr4 = strtr($class, '\\', DIRECTORY_SEPARATOR) . '.php';
$logicalPathPsr4 = strtr($class, '\\', DIRECTORY_SEPARATOR) . $ext;
$first = $class[0];
if (isset($this->prefixLengthsPsr4[$first])) {
@ -321,7 +338,7 @@ class ClassLoader
. strtr(substr($logicalPathPsr4, $pos + 1), '_', DIRECTORY_SEPARATOR);
} else {
// PEAR-like class name
$logicalPathPsr0 = strtr($class, '_', DIRECTORY_SEPARATOR) . '.php';
$logicalPathPsr0 = strtr($class, '_', DIRECTORY_SEPARATOR) . $ext;
}
if (isset($this->prefixesPsr0[$first])) {
@ -347,8 +364,15 @@ class ClassLoader
if ($this->useIncludePath && $file = stream_resolve_include_path($logicalPathPsr0)) {
return $file;
}
// Remember that this class does not exist.
return $this->classMap[$class] = false;
}
}
/**
* Scope isolated include.
*
* Prevents access to $this/self from included files.
*/
function includeFile($file)
{
include $file;
}

View File

@ -6,8 +6,8 @@ $vendorDir = dirname(dirname(__FILE__));
$baseDir = dirname(dirname($vendorDir));
return array(
$vendorDir . '/kriswallsmith/assetic/src/functions.php',
$vendorDir . '/guzzlehttp/streams/src/functions.php',
$vendorDir . '/kriswallsmith/assetic/src/functions.php',
$vendorDir . '/guzzlehttp/guzzle/src/functions.php',
$baseDir . '/core/lib/Drupal.php',
);

View File

@ -49,9 +49,14 @@ class ComposerAutoloaderInitDrupal8
$includeFiles = require __DIR__ . '/autoload_files.php';
foreach ($includeFiles as $file) {
require $file;
composerRequireDrupal8($file);
}
return $loader;
}
}
function composerRequireDrupal8($file)
{
require $file;
}

View File

@ -2049,69 +2049,6 @@
"stream"
]
},
{
"name": "guzzlehttp/guzzle",
"version": "4.0.0",
"version_normalized": "4.0.0.0",
"source": {
"type": "git",
"url": "https://github.com/guzzle/guzzle.git",
"reference": "4063f08ca434efac12bf7a3db0d370b1c451345b"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/guzzle/guzzle/zipball/4063f08ca434efac12bf7a3db0d370b1c451345b",
"reference": "4063f08ca434efac12bf7a3db0d370b1c451345b",
"shasum": ""
},
"require": {
"guzzlehttp/streams": "1.*",
"php": ">=5.4.0"
},
"require-dev": {
"ext-curl": "*",
"phpunit/phpunit": "4.*",
"psr/log": "~1"
},
"time": "2014-03-29 23:11:36",
"type": "library",
"extra": {
"branch-alias": {
"dev-master": "4.0.x-dev"
}
},
"installation-source": "dist",
"autoload": {
"psr-4": {
"GuzzleHttp\\": "src/"
},
"files": [
"src/functions.php"
]
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Michael Dowling",
"email": "mtdowling@gmail.com",
"homepage": "https://github.com/mtdowling"
}
],
"description": "Guzzle is a PHP HTTP client library and framework for building RESTful web service clients",
"homepage": "http://guzzlephp.org/",
"keywords": [
"client",
"curl",
"framework",
"http",
"http client",
"rest",
"web service"
]
},
{
"name": "symfony/css-selector",
"version": "v2.4.4",
@ -2166,5 +2103,72 @@
],
"description": "Symfony CssSelector Component",
"homepage": "http://symfony.com"
},
{
"name": "guzzlehttp/guzzle",
"version": "4.1.0",
"version_normalized": "4.1.0.0",
"source": {
"type": "git",
"url": "https://github.com/guzzle/guzzle.git",
"reference": "85a0ba7de064493c928a8bcdc5eef01e0bde9953"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/guzzle/guzzle/zipball/85a0ba7de064493c928a8bcdc5eef01e0bde9953",
"reference": "85a0ba7de064493c928a8bcdc5eef01e0bde9953",
"shasum": ""
},
"require": {
"ext-json": "*",
"guzzlehttp/streams": "~1.0",
"php": ">=5.4.0"
},
"require-dev": {
"ext-curl": "*",
"phpunit/phpunit": "~4.0",
"psr/log": "~1.0"
},
"suggest": {
"ext-curl": "Guzzle will use specific adapters if cURL is present"
},
"time": "2014-05-28 05:13:19",
"type": "library",
"extra": {
"branch-alias": {
"dev-master": "4.0.x-dev"
}
},
"installation-source": "dist",
"autoload": {
"psr-4": {
"GuzzleHttp\\": "src/"
},
"files": [
"src/functions.php"
]
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Michael Dowling",
"email": "mtdowling@gmail.com",
"homepage": "https://github.com/mtdowling"
}
],
"description": "Guzzle is a PHP HTTP client library and framework for building RESTful web service clients",
"homepage": "http://guzzlephp.org/",
"keywords": [
"client",
"curl",
"framework",
"http",
"http client",
"rest",
"web service"
]
}
]

View File

@ -1,6 +1,36 @@
CHANGELOG
=========
4.1.0 (2014-05-27)
------------------
* Added a `json` request option to easily serialize JSON payloads.
* Added a `GuzzleHttp\json_decode()` wrapper to safely parse JSON.
* Added `setPort()` and `getPort()` to `GuzzleHttp\Message\RequestInterface`.
* Added the ability to provide an emitter to a client in the client constructor.
* Added the ability to persist a cookie session using $_SESSION.
* Added a trait that can be used to add event listeners to an iterator.
* Removed request method constants from RequestInterface.
* Fixed warning when invalid request start-lines are received.
* Updated MessageFactory to work with custom request option methods.
* Updated cacert bundle to latest build.
4.0.2 (2014-04-16)
------------------
* Proxy requests using the StreamAdapter now properly use request_fulluri (#632)
* Added the ability to set scalars as POST fields (#628)
4.0.1 (2014-04-04)
------------------
* The HTTP status code of a response is now set as the exception code of
RequestException objects.
* 303 redirects will now correctly switch from POST to GET requests.
* The default parallel adapter of a client now correctly uses the MultiAdapter.
* HasDataTrait now initializes the internal data array as an empty array so
that the toArray() method always returns an array.
4.0.0 (2014-03-29)
------------------
@ -18,7 +48,7 @@ CHANGELOG
-----------------------
* Removed `getConfig()` and `setConfig()` from clients to avoid confusion
around whether things like base_url, message_factory, etc should be able to
around whether things like base_url, message_factory, etc. should be able to
be retrieved or modified.
* Added `getDefaultOption()` and `setDefaultOption()` to ClientInterface
* functions.php functions were renamed using snake_case to match PHP idioms
@ -150,11 +180,11 @@ CHANGELOG
* See UPGRADING.md for more information on how to upgrade.
* Requests now support the ability to specify an array of $options when creating a request to more easily modify a
request. You can pass a 'request.options' configuration setting to a client to apply default request options to
every request created by a client (e.g. default query string variables, headers, curl options, etc).
every request created by a client (e.g. default query string variables, headers, curl options, etc.).
* Added a static facade class that allows you to use Guzzle with static methods and mount the class to `\Guzzle`.
See `Guzzle\Http\StaticClient::mount`.
* Added `command.request_options` to `Guzzle\Service\Command\AbstractCommand` to pass request options to requests
created by a command (e.g. custom headers, query string variables, timeout settings, etc).
created by a command (e.g. custom headers, query string variables, timeout settings, etc.).
* Stream size in `Guzzle\Stream\PhpStreamRequestFactory` will now be set if Content-Length is returned in the
headers of a response
* Added `Guzzle\Common\Collection::setPath($path, $value)` to set a value into an array using a nested key
@ -251,7 +281,7 @@ CHANGELOG
* Removed Guzzle\Parser\ParserRegister::get(). Use getParser()
* Removed Guzzle\Parser\ParserRegister::set(). Use registerParser().
* All response header helper functions return a string rather than mixing Header objects and strings inconsistently
* Removed cURL blacklist support. This is no longer necessary now that Expect, Accept, etc are managed by Guzzle
* Removed cURL blacklist support. This is no longer necessary now that Expect, Accept, etc. are managed by Guzzle
directly via interfaces
* Removed the injecting of a request object onto a response object. The methods to get and set a request still exist
but are a no-op until removed.
@ -508,7 +538,7 @@ CHANGELOG
3.0.1 (2012-10-22)
------------------
* Models can now be used like regular collection objects by calling filter, map, etc
* Models can now be used like regular collection objects by calling filter, map, etc.
* Models no longer require a Parameter structure or initial data in the constructor
* Added a custom AppendIterator to get around a PHP bug with the `\AppendIterator`
@ -800,6 +830,6 @@ CHANGELOG
* Emitting an event each time a client is generated by a ServiceBuilder
* Using an ApiParams object instead of a Collection for parameters of an ApiCommand
* cache.* request parameters should be renamed to params.cache.*
* Added the ability to set arbitrary curl options on requests (disable_wire, progress, etc). See CurlHandle.
* Added the ability to set arbitrary curl options on requests (disable_wire, progress, etc.). See CurlHandle.
* Added the ability to disable type validation of service descriptions
* ServiceDescriptions and ServiceBuilders are now Serializable

View File

@ -24,7 +24,7 @@ var_export($res->json());
- Doesn't require cURL, but uses cURL by default
- Streams data for both uploads and downloads
- Provides event hooks & plugins for cookies, caching, logging, OAuth, mocks,
etc...
etc.
- Keep-Alive & connection pooling
- SSL Verification
- Automatic decompression of response bodies

View File

@ -59,7 +59,7 @@ Guzzle no longer requires Symfony's EventDispatcher component. Guzzle now uses
`GuzzleHttp\Event\EventInterface`.
- `AbstractHasDispatcher` has moved to a trait, `HasEmitterTrait`, and
`HasDispatcherInterface` has moved to `HasEmitterInterface`. Retrieving the
event emitter of a request, client, etc now uses the `getEmitter` method
event emitter of a request, client, etc. now uses the `getEmitter` method
rather than the `getDispatcher` method.
#### Emitter
@ -152,7 +152,7 @@ $response = $client->send($request);
- The client no longer emits a `client.create_request` event.
- Creating requests with a client no longer automatically utilize a URI
template. You must pass an array into a creational method (e.g.,
`createRequest`, `get`, `put`, etc...) in order to expand a URI template.
`createRequest`, `get`, `put`, etc.) in order to expand a URI template.
### Messages
@ -444,7 +444,7 @@ that contain additonal metadata accessible via `getMetadata()`.
`GuzzleHttp\Stream\StreamInterface::getMetadata` and
`GuzzleHttp\Stream\StreamInterface::setMetadata` have been removed.
## SteamRequestFactory
## StreamRequestFactory
The entire concept of the StreamRequestFactory has been removed. The way this
was used in Guzzle 3 broke the actual interface of sending streaming requests
@ -563,7 +563,7 @@ that implement them, but you should update your code to use alternative methods:
* Moved getLinks() from Response to just be used on a Link header object.
If you previously relied on Guzzle\Http\Message\Header::raw(), then you will need to update your code to use the
HeaderInterface (e.g. toArray(), getAll(), etc).
HeaderInterface (e.g. toArray(), getAll(), etc.).
### Interface changes
@ -591,7 +591,7 @@ HeaderInterface (e.g. toArray(), getAll(), etc).
### Other changes
* All response header helper functions return a string rather than mixing Header objects and strings inconsistently
* Removed cURL blacklist support. This is no longer necessary now that Expect, Accept, etc are managed by Guzzle
* Removed cURL blacklist support. This is no longer necessary now that Expect, Accept, etc. are managed by Guzzle
directly via interfaces
* Removed the injecting of a request object onto a response object. The methods to get and set a request still exist
but are a no-op until removed.
@ -619,7 +619,7 @@ The `Guzzle\Http\Utils` class was removed. This class was only used for testing.
### Stream wrapper and type
`Guzzle\Stream\Stream::getWrapper()` and `Guzzle\Stream\Stream::getSteamType()` are no longer converted to lowercase.
`Guzzle\Stream\Stream::getWrapper()` and `Guzzle\Stream\Stream::getStreamType()` are no longer converted to lowercase.
### curl.emit_io became emit_io

View File

@ -16,7 +16,12 @@
"require": {
"php": ">=5.4.0",
"guzzlehttp/streams": "1.*"
"ext-json": "*",
"guzzlehttp/streams": "~1.0"
},
"suggest": {
"ext-curl": "Guzzle will use specific adapters if cURL is present"
},
"autoload": {
@ -28,8 +33,8 @@
"require-dev": {
"ext-curl": "*",
"psr/log": "~1",
"phpunit/phpunit": "4.*"
"psr/log": "~1.0",
"phpunit/phpunit": "~4.0"
},
"extra": {

View File

@ -18,7 +18,7 @@ If cURL is present, Guzzle will use the following adapters by default:
``GuzzleHttp\Adapter\StreamingProxyAdapter`` is added so that streaming
requests are sent using the PHP stream wrapper. If this setting is disabled,
then streaming requests are sent through a cURL adapter.
- If using PHP 5.5 or greater, then a a ``GuzzleHttp\Adapter\Curl\CurlAdapter``
- If using PHP 5.5 or greater, then a ``GuzzleHttp\Adapter\Curl\CurlAdapter``
is used to send serial requests. Otherwise, the
``GuzzleHttp\Adapter\Curl\MultiAdapter`` is used for serial and parallel
requests.

View File

@ -5,7 +5,7 @@ Clients
Clients are used to create requests, create transactions, send requests
through an HTTP adapter, and return a response. You can add default request
options to a client that are applied to every request (e.g., default headers,
default query string parameters, etc), and you can add event listeners and
default query string parameters, etc.), and you can add event listeners and
subscribers to every request created by a client.
Creating a client
@ -56,6 +56,11 @@ defaults
default headers (e.g., User-Agent), default query string parameters, SSL
configurations, and any other supported request options.
emitter
Specifies an event emitter (``GuzzleHttp\Event\EmitterInterface``) instance
to be used by the client to emit request events. This option is useful if
you need to inject an emitter with listeners/subscribers already attached.
Here's an example of creating a client with various options, including using
a mock adapter that just returns the result of a callable function and a
base URL that is a URI template with parameters.
@ -245,7 +250,7 @@ request using event callbacks.
// Do something with the completion of the request...
},
'error' => function (ErrorEvent $event) {
echo 'Request failed: ' . $event->getRequest()->getUrl() . "\n"
echo 'Request failed: ' . $event->getRequest()->getUrl() . "\n";
echo $event->getException();
// Do something to handle the error...
}
@ -297,7 +302,7 @@ immeditaley and prevent subsequent requests from being sent.
}
]);
.. _request-options:
.. _batch-requests:
Batching Requests
-----------------
@ -338,6 +343,8 @@ events as well as specify the maximum number of request to send in parallel
using the 'parallel' option key. This options array is the exact same format as
the options array exposed in ``GuzzleHttp\ClientInterface::sendAll()``.
.. _request-options:
Request Options
===============
@ -425,6 +432,33 @@ This setting can be set to any of the following types:
$stream = GuzzleHttp\Stream\Stream::factory('contents...');
$client->post('/post', ['body' => $stream]);
json
----
:Summary: The ``json`` option is used to easily upload JSON encoded data as the
body of a request. A Content-Type header of ``application/json`` will be
added if no Content-Type header is already present on the message.
:Types:
Any PHP type that can be operated on by PHP's ``json_encode()`` function.
:Default: None
.. code-block:: php
$request = $client->createRequest('/put', ['json' => ['foo' => 'bar']]);
echo $request->getHeader('Content-Type');
// application/json
echo $request->getBody();
// {"foo":"bar"}
.. note::
This request option does not support customizing the Content-Type header
or any of the options from PHP's `json_encode() <http://www.php.net/manual/en/function.json-encode.php>`_
function. If you need to customize these settings, then you must pass the
JSON encoded data into the request yourself using the ``body`` request
option and you must specify the correct Content-Type header using the
``headers`` request option.
query
-----
@ -437,7 +471,7 @@ query
.. code-block:: php
// Send a GET request to /get?foo=bar
$client->get('/get', ['query' => ['foo' => 'bar']);
$client->get('/get', ['query' => ['foo' => 'bar']]);
Query strings specified in the ``query`` option are combined with any query
string values that are parsed from the URL.
@ -445,7 +479,7 @@ string values that are parsed from the URL.
.. code-block:: php
// Send a GET request to /get?abc=123&foo=bar
$client->get('/get?abc=123', ['query' => ['foo' => 'bar']);
$client->get('/get?abc=123', ['query' => ['foo' => 'bar']]);
auth
----
@ -510,7 +544,7 @@ to ensure that they are fired last or near last in the event chain.
* Listens to the "before" event of a request and only modifies the request
* when the "auth" config setting of the request is "foo".
*/
class FooAuth implements GuzzleHttp\Common\SubscriberInterface
class FooAuth implements GuzzleHttp\Event\SubscriberInterface
{
private $password;
@ -532,14 +566,14 @@ to ensure that they are fired last or near last in the event chain.
}
}
$client->getEmitter->attach(new FooAuth('password'));
$client->getEmitter()->attach(new FooAuth('password'));
$client->get('/', ['auth' => 'foo']);
Adapter Specific Authentication Schemes
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
If you need to use authentication methods provided by cURL (e.g., NTLM, GSS,
etc...), then you need to specify a curl adapter option in the ``options``
etc.), then you need to specify a curl adapter option in the ``options``
request option array. See :ref:`config-option` for more information.
.. _cookies-option:

View File

@ -83,7 +83,7 @@ Adding Event Listeners
After you have the emitter, you can register event listeners that listen to
specific events using the ``on()`` method. When registering an event listener,
you must tell the emitter what event to listen to (e.g., "before", "after",
"headers", "complete", "error", etc...), what callable to invoke when the
"headers", "complete", "error", etc.), what callable to invoke when the
event is triggered, and optionally provide a priority.
.. code-block:: php
@ -323,7 +323,7 @@ a ``GuzzleHttp\Event\BeforeEvent``.
echo $name . "\n";
// "before"
echo $e->getRequest()->getMethod() . "\n";
// "GET" / "POST" / "PUT" / etc...
// "GET" / "POST" / "PUT" / etc.
echo get_class($e->getClient());
// "GuzzleHttp\Client"
}

View File

@ -2,11 +2,91 @@
FAQ
===
Why should I use Guzzle?
========================
Guzzle makes it easy to send HTTP requests and super simple to integrate with
web services. Guzzle manages things like persistent connections, represents
query strings as collections, makes it simple to send streaming POST requests
with fields and files, and abstracts away the underlying HTTP transport layer
(cURL, ``fopen()``, etc.). By providing an object oriented interface for HTTP
clients, requests, responses, headers, and message bodies, Guzzle makes it so
that you no longer need to fool around with cURL options or stream contexts.
To get a feel for how easy it is to use Guzzle, take a look at the
:doc:`quick start guide <quickstart>`.
Swappable HTTP Adapters
-----------------------
Guzzle will use the most appropriate HTTP adapter to send requests based on the
capabilities of your environment and the options applied to a request. When
cURL is available on your system, Guzzle will automatically use cURL. When a
request is sent with the ``stream=true`` request option, Guzzle will
automatically use the PHP stream wrapper HTTP adapter so that bytes are only
read from the HTTP stream as needed.
.. 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 adapters, Guzzle is now much more customizable
and able to adapt to fit the needs of more developers.
HTTP Streams
------------
Request and response message bodies use :doc:`Guzzle Streams <streams>`,
allowing you to stream data without needing to load it all into memory.
Guzzle's stream layer provides a large suite of functionality:
- You can modify streams at runtime using custom or a number of
pre-made decorators.
- You can emit progress events as data is read from a stream.
- You can validate the integrity of a stream using a rolling hash as data is
read from a stream.
Event System
------------
Guzzle's flexible event system allows you to completely modify the behavior
of a client or request at runtime to cater them for any API. You can send a
request with a client, and the client can do things like automatically retry
your request if it fails, automatically redirect, log HTTP messages that are
sent over the wire, emit progress events as data is uploaded and downloaded,
sign requests using OAuth 1.0, verify the integrity of messages before and
after they are sent over the wire, and anything else you might need.
Easy to Test
------------
Another important aspect of Guzzle is that it's really
:doc:`easy to test clients <testing>`. You can mock HTTP responses and when
testing an adapter implementation, Guzzle provides a mock web server that
makes it easy.
Large Ecosystem
---------------
Guzzle has a large `ecosystem of plugins <http://guzzle.readthedocs.org/en/latest/index.html#http-components>`_,
including `service descriptions <https://github.com/guzzle/guzzle-services>`_
which allows you to abstract web services using service descriptions. These
service descriptions define how to serialize an HTTP request and how to parse
an HTTP response into a more meaningful model object.
- `Guzzle Command <https://github.com/guzzle/command>`_: Provides the building
blocks for service description abstraction.
- `Guzzle Services <https://github.com/guzzle/guzzle-services>`_: Provides an
implementation of "Guzzle Command" that utlizes Guzzle's service description
format.
Is it possible to use Guzzle 3 and 4 in the same project?
=========================================================
Yes, because Guzzle 3 and 4 use different Packagist packages and different
namespaced. You simply need to add ``guzzle/guzzle`` (Guzzle 3) and
namespaces. You simply need to add ``guzzle/guzzle`` (Guzzle 3) and
``guzzlehttp/guzzle`` (Guzzle 4+) to your project's composer.json file.
.. code-block:: javascript

View File

@ -70,7 +70,7 @@ You can check to see if a request or response has a body using the
The body used in request and response objects is a
``GuzzleHttp\Stream\StreamInterface``. This stream is used for both uploading
data and downloading data. Guzzle will, by default, store the body of a message
in a stream that uses PHP temp streams. When the size of a the body exceeds
in a stream that uses PHP temp streams. When the size of the body exceeds
2 MB, the stream will automatically switch to storing data on disk rather than
in memory (protecting your application from memory exhaustion).
@ -127,7 +127,7 @@ Request Methods
---------------
When creating a request, you are expected to provide the HTTP method you wish
to perform. You can specfiy any method you'd like, including a custom method
to perform. You can specify any method you'd like, including a custom method
that might not be part of RFC 2616 (like "MOVE").
.. code-block:: php

View File

@ -10,7 +10,7 @@ the pain out of consuming web services.
- Pluggable HTTP adapters that can send requests serially or in parallel
- Doesn't require cURL, but uses cURL by default
- Streams data for both uploads and downloads
- Provides event hooks & plugins for cookies, caching, logging, OAuth, mocks, etc...
- Provides event hooks & plugins for cookies, caching, logging, OAuth, mocks, etc.
- Keep-Alive & connection pooling
- SSL Verification
- Automatic decompression of response bodies
@ -20,16 +20,13 @@ the pain out of consuming web services.
.. code-block:: php
$client = new GuzzleHttp\Client();
$response = $client->get('http://guzzlephp.org');
$res = $client->get('https://api.github.com/user', ['auth' => ['user', 'pass']]);
echo $res->statusCode();
// 200
echo $res->getHeader('content-type');
// 'application/json; charset=utf8'
echo $res->getBody();
// {"type":"User"...'
var_export($res->json());
// Outputs the JSON decoded data
$res = $client->get('https://api.github.com/user', [
'auth' => ['user', 'pass']
]);
echo $res->getStatusCode(); // 200
echo $res->getHeader('content-type'); // 'application/json; charset=utf8'
echo $res->getBody(); // {"type":"User"...'
var_export($res->json()); // Outputs the JSON decoded data
User guide
----------
@ -68,7 +65,7 @@ layer to add capabilities to the client.
`Retry Subscriber <https://github.com/guzzle/retry-subscriber>`_
Retries failed requests using customizable retry strategies (e.g., retry
based on response status code, cURL error codes, etc...)
based on response status code, cURL error codes, etc.)
`Message Integrity Subscriber <https://github.com/guzzle/message-integrity-subscriber>`_
Verifies the message integrity of HTTP responses using customizable

View File

@ -276,19 +276,33 @@ or response object.
echo $request->getHeader('X-Foo');
// Echoes an empty string: ''
POST Requests
=============
Uploading Data
==============
You can send POST requests that contain a raw POST body by passing a
string, resource returned from ``fopen``, or a
``GuzzleHttp\Stream\StreamInterface`` object to the ``body`` request option.
Guzzle provides several methods of uploading data.
You can send requests that contain a stream of data by passing a string,
resource returned from ``fopen``, or a ``GuzzleHttp\Stream\StreamInterface``
object to the ``body`` request option.
.. code-block:: php
$r = $client->post('http://httpbin.org/post', ['body' => 'raw data']);
You can easily upload JSON data using the ``json`` request option.
.. code-block:: php
$r = $client->put('http://httpbin.org/put', ['json' => ['foo' => 'bar']]);
POST Requests
-------------
In addition to specifying the raw data of a request using the ``body`` request
option, Guzzle provides helpful abstractions over sending POST data.
Sending POST Fields
-------------------
~~~~~~~~~~~~~~~~~~~
Sending ``application/x-www-form-urlencoded`` POST requests requires that you
specify the body of a POST request as an array.
@ -321,7 +335,7 @@ You can also build up POST requests before sending them.
$response = $client->send($request);
Sending POST Files
------------------
~~~~~~~~~~~~~~~~~~
Sending ``multipart/form-data`` POST requests (POST requests that contain
files) is the same as sending ``application/x-www-form-urlencoded``, except
@ -404,7 +418,7 @@ Exceptions
Guzzle throws exceptions for errors that occur during a transfer.
- In the event of a networking error (connection timeout, DNS errors, etc),
- In the event of a networking error (connection timeout, DNS errors, etc.),
a ``GuzzleHttp\Exception\RequestException`` is thrown. This exception
extends from ``GuzzleHttp\Exception\TransferException``. Catching this
exception will catch any exception that can be thrown while transferring
@ -424,7 +438,7 @@ Guzzle throws exceptions for errors that occur during a transfer.
}
- A ``GuzzleHttp\Exception\ClientErrorResponseException`` is thrown for 400
level errors if the ``exceptions`` request option is not set to true. This
level errors if the ``exceptions`` request option is set to true. This
exception extends from ``GuzzleHttp\Exception\BadResponseException`` and
``GuzzleHttp\Exception\BadResponseException`` extends from
``GuzzleHttp\Exception\RequestException``.
@ -441,11 +455,10 @@ Guzzle throws exceptions for errors that occur during a transfer.
}
- A ``GuzzleHttp\Exception\ServerErrorResponse`` is thrown for 500 level
errors if the ``exceptions`` request option is not set to true. This
errors if the ``exceptions`` request option is set to true. This
exception extends from ``GuzzleHttp\Exception\BadResponseException``.
- A ``GuzzleHttp\Exception\TooManyRedirectsException`` is thrown when too
many redirects are followed. This exception extends from extends from
``GuzzleHttp\Exception\RequestException``.
many redirects are followed. This exception extends from ``GuzzleHttp\Exception\RequestException``.
- A ``GuzzleHttp\Exception\AdapterException`` is thrown when an error occurs
in an HTTP adapter during a parallel request. This exception is only thrown
when using the ``sendAll()`` method of a client.

View File

@ -32,16 +32,16 @@ remote API.
// Create a mock subscriber and queue two responses.
$mock = new Mock([
new Response(200, ['X-Foo' => 'Bar']), // Use response object
"HTTP/1.1 202 OKr\nContent-Length: 0\r\n\r\n" // Use a response string
"HTTP/1.1 202 OK\r\nContent-Length: 0\r\n\r\n" // Use a response string
]);
// Add the mock subscriber to the client.
$client->getEmitter()->attach($mock);
// The first request is intercepted with the first response.
echo $client->get('/')->getStatus();
echo $client->get('/')->getStatusCode();
//> 200
// The second request is intercepted with the second response.
echo $client->get('/')->getStatus();
echo $client->get('/')->getStatusCode();
//> 202
When no more responses are in the queue and a request is sent, an
@ -175,7 +175,7 @@ enqueue responses and inspect the requests that it has received.
In order to use the web server, you'll need to manually require
``tests/Server.php``. Any operation on the ``Server`` object will ensure that
the server is running and wait until it is able to receive requets before
the server is running and wait until it is able to receive requests before
returning.
.. code-block:: php

View File

@ -122,7 +122,7 @@ class BatchContext
$code = curl_multi_add_handle($this->multi, $handle);
if ($code != CURLM_OK) {
CurlAdapter::throwMultiError($code);
MultiAdapter::throwMultiError($code);
}
$this->handles[$transaction] = $handle;
@ -146,7 +146,7 @@ class BatchContext
$code = curl_multi_remove_handle($this->multi, $handle);
if ($code != CURLM_OK) {
CurlAdapter::throwMultiError($code);
MultiAdapter::throwMultiError($code);
}
$info = curl_getinfo($handle);

View File

@ -58,7 +58,6 @@ class MultiAdapter implements AdapterInterface, ParallelAdapterInterface
MessageFactoryInterface $messageFactory,
array $options = []
) {
$this->handles = new \SplObjectStorage();
$this->messageFactory = $messageFactory;
$this->curlFactory = isset($options['handle_factory'])
? $options['handle_factory']

View File

@ -215,10 +215,12 @@ class StreamAdapter implements AdapterInterface
{
if (!is_array($value)) {
$options['http']['proxy'] = $value;
$options['http']['request_fulluri'] = true;
} else {
$scheme = $request->getScheme();
if (isset($value[$scheme])) {
$options['http']['proxy'] = $value[$scheme];
$options['http']['request_fulluri'] = true;
}
}
}

View File

@ -3,6 +3,7 @@
namespace GuzzleHttp\Adapter;
use GuzzleHttp\ClientInterface;
use GuzzleHttp\Event\ListenerAttacherTrait;
use GuzzleHttp\Message\RequestInterface;
/**
@ -11,21 +12,27 @@ use GuzzleHttp\Message\RequestInterface;
*/
class TransactionIterator implements \Iterator
{
use ListenerAttacherTrait;
/** @var \Iterator */
private $source;
/** @var ClientInterface */
private $client;
/** @var array of hashes containing 'name', 'fn', 'priority', and 'once' */
private $eventListeners;
/** @var array Listeners to attach to each request */
private $eventListeners = [];
public function __construct(
$source, ClientInterface $client,
$source,
ClientInterface $client,
array $options
) {
$this->client = $client;
$this->configureEvents($options);
$this->eventListeners = $this->prepareListeners(
$options,
['before', 'complete', 'error']
);
if ($source instanceof \Iterator) {
$this->source = $source;
} elseif (is_array($source)) {
@ -38,21 +45,11 @@ class TransactionIterator implements \Iterator
public function current()
{
$request = $this->source->current();
if (!$request instanceof RequestInterface) {
throw new \RuntimeException('All must implement RequestInterface');
}
if ($this->eventListeners) {
$emitter = $request->getEmitter();
foreach ($this->eventListeners as $ev) {
if ($ev['once']) {
$emitter->once($ev['name'], $ev['fn'], $ev['priority']);
} else {
$emitter->on($ev['name'], $ev['fn'], $ev['priority']);
}
}
}
$this->attachListeners($request, $this->eventListeners);
return new Transaction($this->client, $request);
}
@ -73,44 +70,4 @@ class TransactionIterator implements \Iterator
}
public function rewind() {}
private function configureEvents(array $options)
{
static $namedEvents = ['before', 'complete', 'error'];
foreach ($namedEvents as $event) {
if (isset($options[$event])) {
if (is_callable($options[$event])) {
$this->eventListeners[] = [
'name' => $event,
'fn' => $options[$event],
'priority' => 0,
'once' => false
];
} else {
$this->addEvent($event, $options[$event]);
}
}
}
}
private function addEvent($eventName, $event)
{
static $default = ['priority' => 0, 'once' => false];
if (!is_array($event)) {
throw new \InvalidArgumentException('Each event listener must be a'
. ' callable or an array of associative arrays where each'
. ' associative array contains a "fn" key.');
}
if (isset($event['fn'])) {
$event['name'] = $eventName;
$this->eventListeners[] = $event + $default;
} else {
foreach ($event as $e) {
$this->addEvent($eventName, $e);
}
}
}
}

View File

@ -69,12 +69,16 @@ class Client implements ClientInterface
* - parallel_adapter: Adapter used to transfer requests in parallel
* - message_factory: Factory used to create request and response object
* - defaults: Default request options to apply to each request
* - emitter: Event emitter used for request events
*/
public function __construct(array $config = [])
{
$this->configureBaseUrl($config);
$this->configureDefaults($config);
$this->configureAdapter($config);
if (isset($config['emitter'])) {
$this->emitter = $config['emitter'];
}
}
/**
@ -264,13 +268,12 @@ class Client implements ClientInterface
/**
* Get a default parallel adapter to use based on the environment
*
* @return ParallelAdapterInterface|null
* @throws \RuntimeException
* @return ParallelAdapterInterface
*/
private function getDefaultParallelAdapter()
{
return extension_loaded('curl')
? new CurlAdapter($this->messageFactory)
? new MultiAdapter($this->messageFactory)
: new FakeParallelAdapter($this->adapter);
}

View File

@ -13,7 +13,7 @@ use GuzzleHttp\Exception\AdapterException;
*/
interface ClientInterface extends HasEmitterInterface
{
const VERSION = '4.0.0';
const VERSION = '4.1.0';
/**
* Create and return a new {@see RequestInterface} object.

View File

@ -11,7 +11,7 @@ use GuzzleHttp\Message\ResponseInterface;
* It extracts cookies from HTTP requests, and returns them in HTTP responses.
* CookieJarInterface instances automatically expire contained cookies when
* necessary. Subclasses are also responsible for storing and retrieving
* cookies from a file, database, etc...
* cookies from a file, database, etc.
*
* @link http://docs.python.org/2/library/cookielib.html Inspiration
*/

View File

@ -73,9 +73,9 @@ class FileCookieJar extends CookieJar
// @codeCoverageIgnoreEnd
}
$data = json_decode($json, true);
$data = \GuzzleHttp\json_decode($json, true);
if (is_array($data)) {
foreach (json_decode($json, true) as $cookie) {
foreach (\GuzzleHttp\json_decode($json, true) as $cookie) {
$this->setCookie(new SetCookie($cookie));
}
} elseif (strlen($data)) {

View File

@ -0,0 +1,65 @@
<?php
namespace GuzzleHttp\Cookie;
/**
* Persists cookies in the client session
*/
class SessionCookieJar extends CookieJar
{
/** @var string session key */
private $sessionKey;
/**
* Create a new SessionCookieJar object
*
* @param string $sessionKey Session key name to store the cookie data in session
*/
public function __construct($sessionKey)
{
$this->sessionKey = $sessionKey;
$this->load();
}
/**
* Saves cookies to session when shutting down
*/
public function __destruct()
{
$this->save();
}
/**
* Save cookies to the client session
*/
public function save()
{
$json = [];
foreach ($this as $cookie) {
if ($cookie->getExpires() && !$cookie->getDiscard()) {
$json[] = $cookie->toArray();
}
}
$_SESSION[$this->sessionKey] = json_encode($json);
}
/**
* Load the contents of the client session into the data array
*/
protected function load()
{
$cookieJar = isset($_SESSION[$this->sessionKey])
? $_SESSION[$this->sessionKey]
: null;
$data = \GuzzleHttp\json_decode($cookieJar, true);
if (is_array($data)) {
foreach ($data as $cookie) {
$this->setCookie(new SetCookie($cookie));
}
} elseif (strlen($data)) {
throw new \RuntimeException("Invalid cookie data");
}
}
}

View File

@ -7,7 +7,7 @@ use GuzzleHttp\Message\ResponseInterface;
/**
* Event that contains transaction statistics (time over the wire, lookup time,
* etc).
* etc.).
*
* Adapters that create this event SHOULD add, at a minimum, the 'total_time'
* transfer statistic that measures the amount of time, in seconds, taken to

View File

@ -0,0 +1,89 @@
<?php
namespace GuzzleHttp\Event;
/**
* Trait that provides methods for extract event listeners specified in an array
* and attaching them to an emitter owned by the object or one of its direct
* dependencies.
*/
trait ListenerAttacherTrait
{
/**
* Attaches event listeners and properly sets their priorities and whether
* or not they are are only executed once.
*
* @param HasEmitterInterface $object Object that has the event emitter.
* @param array $listeners Array of hashes representing event
* event listeners. Each item contains
* "name", "fn", "priority", & "once".
*/
private function attachListeners(HasEmitterInterface $object, array $listeners)
{
$emitter = $object->getEmitter();
foreach ($listeners as $el) {
if ($el['once']) {
$emitter->once($el['name'], $el['fn'], $el['priority']);
} else {
$emitter->on($el['name'], $el['fn'], $el['priority']);
}
}
}
/**
* Extracts the allowed events from the provided array, and ignores anything
* else in the array. The event listener must be specified as a callable or
* as an array of event listener data ("name", "fn", "priority", "once").
*
* @param array $source Array containing callables or hashes of data to be
* prepared as event listeners.
* @param array $events Names of events to look for in the provided $source
* array. Other keys are ignored.
* @return array
*/
private function prepareListeners(array $source, array $events)
{
$listeners = [];
foreach ($events as $name) {
if (isset($source[$name])) {
$this->buildListener($name, $source[$name], $listeners);
}
}
return $listeners;
}
/**
* Creates a complete event listener definition from the provided array of
* listener data. Also works recursively if more than one listeners are
* contained in the provided array.
*
* @param string $name Name of the event the listener is for.
* @param array|callable $data Event listener data to prepare.
* @param array $listeners Array of listeners, passed by reference.
*
* @throws \InvalidArgumentException if the event data is malformed.
*/
private function buildListener($name, $data, &$listeners)
{
static $defaults = ['priority' => 0, 'once' => false];
// If a callable is provided, normalize it to the array format.
if (is_callable($data)) {
$data = ['fn' => $data];
}
// Prepare the listener and add it to the array, recursively.
if (isset($data['fn'])) {
$data['name'] = $name;
$listeners[] = $data + $defaults;
} elseif (is_array($data)) {
foreach ($data as $listenerData) {
$this->buildListener($name, $listenerData, $listeners);
}
} else {
throw new \InvalidArgumentException('Each event listener must be a '
. 'callable or an associative array containing a "fn" key.');
}
}
}

View File

@ -25,7 +25,8 @@ class RequestException extends TransferException
ResponseInterface $response = null,
\Exception $previous = null
) {
parent::__construct($message, 0, $previous);
$code = $response ? $response->getStatusCode() : 0;
parent::__construct($message, $code, $previous);
$this->request = $request;
$this->response = $response;
}

View File

@ -9,7 +9,7 @@ namespace GuzzleHttp;
trait HasDataTrait
{
/** @var array */
protected $data;
protected $data = [];
public function getIterator()
{

View File

@ -2,6 +2,7 @@
namespace GuzzleHttp\Message;
use GuzzleHttp\Event\ListenerAttacherTrait;
use GuzzleHttp\Post\PostFileInterface;
use GuzzleHttp\Subscriber\Cookie;
use GuzzleHttp\Cookie\CookieJar;
@ -19,6 +20,8 @@ use GuzzleHttp\Url;
*/
class MessageFactory implements MessageFactoryInterface
{
use ListenerAttacherTrait;
/** @var HttpError */
private $errorPlugin;
@ -123,9 +126,12 @@ class MessageFactory implements MessageFactoryInterface
*/
protected function addPostData(RequestInterface $request, array $body)
{
static $fields = ['string' => true, 'array' => true, 'NULL' => true,
'boolean' => true, 'double' => true, 'integer' => true];
$post = new PostBody();
foreach ($body as $key => $value) {
if (is_string($value) || is_array($value)) {
if (isset($fields[gettype($value)])) {
$post->setField($key, $value);
} elseif ($value instanceof PostFileInterface) {
$post->addFile($value);
@ -148,7 +154,7 @@ class MessageFactory implements MessageFactoryInterface
'debug' => 1, 'save_to' => 1, 'stream' => 1, 'expect' => 1];
static $methods;
if (!$methods) {
$methods = array_flip(get_class_methods(__CLASS__));
$methods = array_flip(get_class_methods($this));
}
// Iterate over each key value pair and attempt to apply a config using
@ -298,27 +304,9 @@ class MessageFactory implements MessageFactoryInterface
throw new \InvalidArgumentException('events value must be an array');
}
$emitter = $request->getEmitter();
foreach ($value as $name => $method) {
if (is_callable($method)) {
$emitter->on($name, $method);
} elseif (!is_array($method) || !isset($method['fn'])) {
throw new \InvalidArgumentException('Each event must be a '
. 'callable or associative array containing a "fn" key');
} elseif (isset($method['once']) && $method['once'] === true) {
$emitter->once(
$name,
$method['fn'],
isset($method['priority']) ? $method['priority'] : 0
);
} else {
$emitter->on(
$name,
$method['fn'],
isset($method['priority']) ? $method['priority'] : 0
);
}
}
$this->attachListeners($request, $this->prepareListeners($value,
['before', 'complete', 'error', 'headers']
));
}
private function add_subscribers(RequestInterface $request, $value)
@ -332,4 +320,13 @@ class MessageFactory implements MessageFactoryInterface
$emitter->attach($subscribers);
}
}
private function add_json(RequestInterface $request, $value)
{
if (!$request->hasHeader('Content-Type')) {
$request->setHeader('Content-Type', 'application/json');
}
$request->setBody(Stream\create(json_encode($value)));
}
}

View File

@ -39,6 +39,7 @@ interface MessageFactoryInterface
*
* - headers: Associative array of headers to add to the request
* - body: string|resource|array|StreamInterface request body to send
* - json: mixed Uploads JSON encoded data using an application/json Content-Type header.
* - query: Associative array of query string values to add to the request
* - auth: array|string HTTP auth settings (user, pass[, type="basic"])
* - version: The HTTP protocol version to use with the request
@ -59,7 +60,7 @@ interface MessageFactoryInterface
* - expect: true/false/integer Controls the "Expect: 100-Continue" header
* - config: Associative array of request config collection options
*
* @param string $method HTTP method (GET, POST, PUT, etc ...)
* @param string $method HTTP method (GET, POST, PUT, etc.)
* @param string|Url $url HTTP URL to connect to
* @param array $options Array of options to apply to the request
*

View File

@ -38,7 +38,8 @@ class MessageParser
'body' => $parts['body']
];
$parsed['request_url'] = $this->getUrlPartsFromMessage($parts['start_line'][1], $parsed);
$parsed['request_url'] = $this->getUrlPartsFromMessage(
(isset($parts['start_line'][1]) ? $parts['start_line'][1] : ''), $parsed);
return $parsed;
}

View File

@ -115,6 +115,19 @@ class Request extends AbstractMessage implements RequestInterface
return $this;
}
public function getPort()
{
return $this->url->getPort();
}
public function setPort($port)
{
$this->url->setPort($port);
$this->updateHostHeaderFromUrl();
return $this;
}
public function getHost()
{
return $this->url->getHost();
@ -194,9 +207,9 @@ class Request extends AbstractMessage implements RequestInterface
if (($port == 80 && $scheme == 'http') ||
($port == 443 && $scheme == 'https')
) {
$this->setHeader('Host', $this->url->getHost());
$this->setHeader('Host', $host);
} else {
$this->setHeader('Host', $this->url->getHost() . ':' . $port);
$this->setHeader('Host', "{$host}:{$port}");
}
}
}

View File

@ -10,21 +10,6 @@ use GuzzleHttp\Query;
*/
interface RequestInterface extends MessageInterface, HasEmitterInterface
{
const GET = 'GET';
const PUT = 'PUT';
const POST = 'POST';
const DELETE = 'DELETE';
const HEAD = 'HEAD';
const CONNECT = 'CONNECT';
const OPTIONS = 'OPTIONS';
const TRACE = 'TRACE';
const PATCH = 'PATCH';
/**
* @return string
*/
public function __toString();
/**
* Sets the request URL.
*
@ -39,7 +24,7 @@ interface RequestInterface extends MessageInterface, HasEmitterInterface
public function setUrl($url);
/**
* Gets the request URL.
* Gets the request URL as a string.
*
* @return string Returns the URL as a string.
*/
@ -71,14 +56,14 @@ interface RequestInterface extends MessageInterface, HasEmitterInterface
public function setQuery($query);
/**
* Get the HTTP method of the request
* Get the HTTP method of the request.
*
* @return string
*/
public function getMethod();
/**
* Set the HTTP method of the request
* Set the HTTP method of the request.
*
* @param string $method HTTP method
*
@ -87,14 +72,14 @@ interface RequestInterface extends MessageInterface, HasEmitterInterface
public function setMethod($method);
/**
* Get the URI scheme of the request (http, https, etc)
* Get the URI scheme of the request (http, https, etc.).
*
* @return string
*/
public function getScheme();
/**
* Set the URI scheme of the request (http, https, etc)
* Set the URI scheme of the request (http, https, etc.).
*
* @param string $scheme Scheme to set
*
@ -103,15 +88,36 @@ interface RequestInterface extends MessageInterface, HasEmitterInterface
public function setScheme($scheme);
/**
* Get the host of the request
* Get the port scheme of the request (e.g., 80, 443, etc.).
*
* @return int
*/
public function getPort();
/**
* Set the port of the request.
*
* Setting a port modifies the Host header of a request as necessary.
*
* @param int $port Port to set
*
* @return self
*/
public function setPort($port);
/**
* Get the host of the request.
*
* @return string
*/
public function getHost();
/**
* Set the host of the request. Including a port in the host will modify
* the port of the request.
* Set the host of the request including an optional port.
*
* Including a port in the host argument will explicitly change the port of
* the request. If no port is found, the default port of the current
* request scheme will be utilized.
*
* @param string $host Host to set (e.g. www.yahoo.com, www.yahoo.com:80)
*
@ -120,14 +126,14 @@ interface RequestInterface extends MessageInterface, HasEmitterInterface
public function setHost($host);
/**
* Get the path of the request (e.g. '/', '/index.html')
* Get the path of the request (e.g. '/', '/index.html').
*
* @return string
*/
public function getPath();
/**
* Set the path of the request (e.g. '/', '/index.html')
* Set the path of the request (e.g. '/', '/index.html').
*
* @param string|array $path Path to set or array of segments to implode
*
@ -136,7 +142,7 @@ interface RequestInterface extends MessageInterface, HasEmitterInterface
public function setPath($path);
/**
* Get the request's configuration options
* Get the request's configuration options.
*
* @return \GuzzleHttp\Collection
*/

View File

@ -126,21 +126,19 @@ class Response extends AbstractMessage implements ResponseInterface
public function json(array $config = [])
{
$data = json_decode(
(string) $this->getBody(),
isset($config['object']) ? !$config['object'] : true,
512,
isset($config['big_int_strings']) ? JSON_BIGINT_AS_STRING : 0
);
if (JSON_ERROR_NONE !== json_last_error()) {
try {
return \GuzzleHttp\json_decode(
(string) $this->getBody(),
isset($config['object']) ? !$config['object'] : true,
512,
isset($config['big_int_strings']) ? JSON_BIGINT_AS_STRING : 0
);
} catch (\InvalidArgumentException $e) {
throw new ParseException(
'Unable to parse response body into JSON: ' . json_last_error(),
$e->getMessage(),
$this
);
}
return $data;
}
public function xml(array $config = [])

View File

@ -8,7 +8,7 @@ namespace GuzzleHttp\Message;
interface ResponseInterface extends MessageInterface
{
/**
* Get the response status code (e.g. "200", "404", etc)
* Get the response status code (e.g. "200", "404", etc.)
*
* @return string
*/

View File

@ -36,7 +36,6 @@ class MultipartBody implements Stream\StreamInterface
$this->boundary = $boundary ?: uniqid();
$this->fields = $fields;
$this->files = $files;
$this->meta['mode'] = 'r';
// Ensure each file is a PostFileInterface
foreach ($this->files as $file) {

View File

@ -118,10 +118,11 @@ class Redirect implements SubscriberInterface
// would do. Be sure to disable redirects on the clone.
$redirectRequest = clone $request;
$redirectRequest->getEmitter()->detach($this);
$statusCode = $response->getStatusCode();
if ($request->getBody() &&
!$config->getPath('redirect/strict') &&
$response->getStatusCode() <= 302
if ($statusCode == 303 ||
($statusCode <= 302 && $request->getBody() &&
!$config->getPath('redirect/strict'))
) {
$redirectRequest->setMethod('GET');
$redirectRequest->setBody(null);

View File

@ -218,7 +218,7 @@ class Url
}
/**
* Set the scheme part of the URL (http, https, ftp, etc)
* Set the scheme part of the URL (http, https, ftp, etc.)
*
* @param string $scheme Scheme to set
*

View File

@ -1,7 +1,7 @@
##
## ca-bundle.crt -- Bundle of CA Root Certificates
##
## Certificate data from Mozilla as of: Tue Jan 28 09:38:07 2014
## Certificate data from Mozilla as of: Tue Apr 22 08:29:31 2014
##
## This is a bundle of X.509 certificates of public Certificate Authorities
## (CA). These were automatically extracted from Mozilla's root certificates
@ -1318,31 +1318,6 @@ CZBUkQM8R+XVyWXgt0t97EfTsws+rZ7QdAAO671RrcDeLMDDav7v3Aun+kbfYNucpllQdSNpc5Oy
+fwC00fmcc4QAu4njIT/rEUNE1yDMuAlpYYsfPQS
-----END CERTIFICATE-----
Firmaprofesional Root CA
========================
-----BEGIN CERTIFICATE-----
MIIEVzCCAz+gAwIBAgIBATANBgkqhkiG9w0BAQUFADCBnTELMAkGA1UEBhMCRVMxIjAgBgNVBAcT
GUMvIE11bnRhbmVyIDI0NCBCYXJjZWxvbmExQjBABgNVBAMTOUF1dG9yaWRhZCBkZSBDZXJ0aWZp
Y2FjaW9uIEZpcm1hcHJvZmVzaW9uYWwgQ0lGIEE2MjYzNDA2ODEmMCQGCSqGSIb3DQEJARYXY2FA
ZmlybWFwcm9mZXNpb25hbC5jb20wHhcNMDExMDI0MjIwMDAwWhcNMTMxMDI0MjIwMDAwWjCBnTEL
MAkGA1UEBhMCRVMxIjAgBgNVBAcTGUMvIE11bnRhbmVyIDI0NCBCYXJjZWxvbmExQjBABgNVBAMT
OUF1dG9yaWRhZCBkZSBDZXJ0aWZpY2FjaW9uIEZpcm1hcHJvZmVzaW9uYWwgQ0lGIEE2MjYzNDA2
ODEmMCQGCSqGSIb3DQEJARYXY2FAZmlybWFwcm9mZXNpb25hbC5jb20wggEiMA0GCSqGSIb3DQEB
AQUAA4IBDwAwggEKAoIBAQDnIwNvbyOlXnjOlSztlB5uCp4Bx+ow0Syd3Tfom5h5VtP8c9/Qit5V
j1H5WuretXDE7aTt/6MNbg9kUDGvASdYrv5sp0ovFy3Tc9UTHI9ZpTQsHVQERc1ouKDAA6XPhUJH
lShbz++AbOCQl4oBPB3zhxAwJkh91/zpnZFx/0GaqUC1N5wpIE8fUuOgfRNtVLcK3ulqTgesrBlf
3H5idPayBQC6haD9HThuy1q7hryUZzM1gywfI834yJFxzJeL764P3CkDG8A563DtwW4O2GcLiam8
NeTvtjS0pbbELaW+0MOUJEjb35bTALVmGotmBQ/dPz/LP6pemkr4tErvlTcbAgMBAAGjgZ8wgZww
KgYDVR0RBCMwIYYfaHR0cDovL3d3dy5maXJtYXByb2Zlc2lvbmFsLmNvbTASBgNVHRMBAf8ECDAG
AQH/AgEBMCsGA1UdEAQkMCKADzIwMDExMDI0MjIwMDAwWoEPMjAxMzEwMjQyMjAwMDBaMA4GA1Ud
DwEB/wQEAwIBBjAdBgNVHQ4EFgQUMwugZtHq2s7eYpMEKFK1FH84aLcwDQYJKoZIhvcNAQEFBQAD
ggEBAEdz/o0nVPD11HecJ3lXV7cVVuzH2Fi3AQL0M+2TUIiefEaxvT8Ub/GzR0iLjJcG1+p+o1wq
u00vR+L4OQbJnC4xGgN49Lw4xiKLMzHwFgQEffl25EvXwOaD7FnMP97/T2u3Z36mhoEyIwOdyPdf
wUpgpZKpsaSgYMN4h7Mi8yrrW6ntBas3D7Hi05V2Y1Z0jFhyGzflZKG+TQyTmAyX9odtsz/ny4Cm
7YjHX1BiAuiZdBbQ5rQ58SfLyEDW44YQqSMSkuBpQWOnryULwMWSyx6Yo1q6xTMPoJcB3X/ge9YG
VM+h4k0460tQtcsm9MracEpqoeJ5quGnM/b9Sh/22WA=
-----END CERTIFICATE-----
Swisscom Root CA 1
==================
-----BEGIN CERTIFICATE-----
@ -3783,3 +3758,109 @@ i4TWnsLrgxifarsbJGAzcMzs9zLzXNl5fe+epP7JI8Mk7hWSsT2RTyaGvWZzJBPqpK5jwa19hAM8
EHiGG3njxPPyBJUgriOCxLM6AGK/5jYk4Ve6xx6QddVfP5VhK8E7zeWzaGHQRiapIVJpLesux+t3
zqY6tQMzT3bR51xUAV3LePTJDL/PEo4XLSNolOer/qmyKwbQBM0=
-----END CERTIFICATE-----
TeliaSonera Root CA v1
======================
-----BEGIN CERTIFICATE-----
MIIFODCCAyCgAwIBAgIRAJW+FqD3LkbxezmCcvqLzZYwDQYJKoZIhvcNAQEFBQAwNzEUMBIGA1UE
CgwLVGVsaWFTb25lcmExHzAdBgNVBAMMFlRlbGlhU29uZXJhIFJvb3QgQ0EgdjEwHhcNMDcxMDE4
MTIwMDUwWhcNMzIxMDE4MTIwMDUwWjA3MRQwEgYDVQQKDAtUZWxpYVNvbmVyYTEfMB0GA1UEAwwW
VGVsaWFTb25lcmEgUm9vdCBDQSB2MTCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAMK+
6yfwIaPzaSZVfp3FVRaRXP3vIb9TgHot0pGMYzHw7CTww6XScnwQbfQ3t+XmfHnqjLWCi65ItqwA
3GV17CpNX8GH9SBlK4GoRz6JI5UwFpB/6FcHSOcZrr9FZ7E3GwYq/t75rH2D+1665I+XZ75Ljo1k
B1c4VWk0Nj0TSO9P4tNmHqTPGrdeNjPUtAa9GAH9d4RQAEX1jF3oI7x+/jXh7VB7qTCNGdMJjmhn
Xb88lxhTuylixcpecsHHltTbLaC0H2kD7OriUPEMPPCs81Mt8Bz17Ww5OXOAFshSsCPN4D7c3TxH
oLs1iuKYaIu+5b9y7tL6pe0S7fyYGKkmdtwoSxAgHNN/Fnct7W+A90m7UwW7XWjH1Mh1Fj+JWov3
F0fUTPHSiXk+TT2YqGHeOh7S+F4D4MHJHIzTjU3TlTazN19jY5szFPAtJmtTfImMMsJu7D0hADnJ
oWjiUIMusDor8zagrC/kb2HCUQk5PotTubtn2txTuXZZNp1D5SDgPTJghSJRt8czu90VL6R4pgd7
gUY2BIbdeTXHlSw7sKMXNeVzH7RcWe/a6hBle3rQf5+ztCo3O3CLm1u5K7fsslESl1MpWtTwEhDc
TwK7EpIvYtQ/aUN8Ddb8WHUBiJ1YFkveupD/RwGJBmr2X7KQarMCpgKIv7NHfirZ1fpoeDVNAgMB
AAGjPzA9MA8GA1UdEwEB/wQFMAMBAf8wCwYDVR0PBAQDAgEGMB0GA1UdDgQWBBTwj1k4ALP1j5qW
DNXr+nuqF+gTEjANBgkqhkiG9w0BAQUFAAOCAgEAvuRcYk4k9AwI//DTDGjkk0kiP0Qnb7tt3oNm
zqjMDfz1mgbldxSR651Be5kqhOX//CHBXfDkH1e3damhXwIm/9fH907eT/j3HEbAek9ALCI18Bmx
0GtnLLCo4MBANzX2hFxc469CeP6nyQ1Q6g2EdvZR74NTxnr/DlZJLo961gzmJ1TjTQpgcmLNkQfW
pb/ImWvtxBnmq0wROMVvMeJuScg/doAmAyYp4Db29iBT4xdwNBedY2gea+zDTYa4EzAvXUYNR0PV
G6pZDrlcjQZIrXSHX8f8MVRBE+LHIQ6e4B4N4cB7Q4WQxYpYxmUKeFfyxiMPAdkgS94P+5KFdSpc
c41teyWRyu5FrgZLAMzTsVlQ2jqIOylDRl6XK1TOU2+NSueW+r9xDkKLfP0ooNBIytrEgUy7onOT
JsjrDNYmiLbAJM+7vVvrdX3pCI6GMyx5dwlppYn8s3CQh3aP0yK7Qs69cwsgJirQmz1wHiRszYd2
qReWt88NkvuOGKmYSdGe/mBEciG5Ge3C9THxOUiIkCR1VBatzvT4aRRkOfujuLpwQMcnHL/EVlP6
Y2XQ8xwOFvVrhlhNGNTkDY6lnVuR3HYkUD/GKvvZt5y11ubQ2egZixVxSK236thZiNSQvxaz2ems
WWFUyBy6ysHK4bkgTI86k4mloMy/0/Z1pHWWbVY=
-----END CERTIFICATE-----
E-Tugra Certification Authority
===============================
-----BEGIN CERTIFICATE-----
MIIGSzCCBDOgAwIBAgIIamg+nFGby1MwDQYJKoZIhvcNAQELBQAwgbIxCzAJBgNVBAYTAlRSMQ8w
DQYDVQQHDAZBbmthcmExQDA+BgNVBAoMN0UtVHXEn3JhIEVCRyBCaWxpxZ9pbSBUZWtub2xvamls
ZXJpIHZlIEhpem1ldGxlcmkgQS7Fni4xJjAkBgNVBAsMHUUtVHVncmEgU2VydGlmaWthc3lvbiBN
ZXJrZXppMSgwJgYDVQQDDB9FLVR1Z3JhIENlcnRpZmljYXRpb24gQXV0aG9yaXR5MB4XDTEzMDMw
NTEyMDk0OFoXDTIzMDMwMzEyMDk0OFowgbIxCzAJBgNVBAYTAlRSMQ8wDQYDVQQHDAZBbmthcmEx
QDA+BgNVBAoMN0UtVHXEn3JhIEVCRyBCaWxpxZ9pbSBUZWtub2xvamlsZXJpIHZlIEhpem1ldGxl
cmkgQS7Fni4xJjAkBgNVBAsMHUUtVHVncmEgU2VydGlmaWthc3lvbiBNZXJrZXppMSgwJgYDVQQD
DB9FLVR1Z3JhIENlcnRpZmljYXRpb24gQXV0aG9yaXR5MIICIjANBgkqhkiG9w0BAQEFAAOCAg8A
MIICCgKCAgEA4vU/kwVRHoViVF56C/UYB4Oufq9899SKa6VjQzm5S/fDxmSJPZQuVIBSOTkHS0vd
hQd2h8y/L5VMzH2nPbxHD5hw+IyFHnSOkm0bQNGZDbt1bsipa5rAhDGvykPL6ys06I+XawGb1Q5K
CKpbknSFQ9OArqGIW66z6l7LFpp3RMih9lRozt6Plyu6W0ACDGQXwLWTzeHxE2bODHnv0ZEoq1+g
ElIwcxmOj+GMB6LDu0rw6h8VqO4lzKRG+Bsi77MOQ7osJLjFLFzUHPhdZL3Dk14opz8n8Y4e0ypQ
BaNV2cvnOVPAmJ6MVGKLJrD3fY185MaeZkJVgkfnsliNZvcHfC425lAcP9tDJMW/hkd5s3kc91r0
E+xs+D/iWR+V7kI+ua2oMoVJl0b+SzGPWsutdEcf6ZG33ygEIqDUD13ieU/qbIWGvaimzuT6w+Gz
rt48Ue7LE3wBf4QOXVGUnhMMti6lTPk5cDZvlsouDERVxcr6XQKj39ZkjFqzAQqptQpHF//vkUAq
jqFGOjGY5RH8zLtJVor8udBhmm9lbObDyz51Sf6Pp+KJxWfXnUYTTjF2OySznhFlhqt/7x3U+Lzn
rFpct1pHXFXOVbQicVtbC/DP3KBhZOqp12gKY6fgDT+gr9Oq0n7vUaDmUStVkhUXU8u3Zg5mTPj5
dUyQ5xJwx0UCAwEAAaNjMGEwHQYDVR0OBBYEFC7j27JJ0JxUeVz6Jyr+zE7S6E5UMA8GA1UdEwEB
/wQFMAMBAf8wHwYDVR0jBBgwFoAULuPbsknQnFR5XPonKv7MTtLoTlQwDgYDVR0PAQH/BAQDAgEG
MA0GCSqGSIb3DQEBCwUAA4ICAQAFNzr0TbdF4kV1JI+2d1LoHNgQk2Xz8lkGpD4eKexd0dCrfOAK
kEh47U6YA5n+KGCRHTAduGN8qOY1tfrTYXbm1gdLymmasoR6d5NFFxWfJNCYExL/u6Au/U5Mh/jO
XKqYGwXgAEZKgoClM4so3O0409/lPun++1ndYYRP0lSWE2ETPo+Aab6TR7U1Q9Jauz1c77NCR807
VRMGsAnb/WP2OogKmW9+4c4bU2pEZiNRCHu8W1Ki/QY3OEBhj0qWuJA3+GbHeJAAFS6LrVE1Uweo
a2iu+U48BybNCAVwzDk/dr2l02cmAYamU9JgO3xDf1WKvJUawSg5TB9D0pH0clmKuVb8P7Sd2nCc
dlqMQ1DujjByTd//SffGqWfZbawCEeI6FiWnWAjLb1NBnEg4R2gz0dfHj9R0IdTDBZB6/86WiLEV
KV0jq9BgoRJP3vQXzTLlyb/IQ639Lo7xr+L0mPoSHyDYwKcMhcWQ9DstliaxLL5Mq+ux0orJ23gT
Dx4JnW2PAJ8C2sH6H3p6CcRK5ogql5+Ji/03X186zjhZhkuvcQu02PJwT58yE+Owp1fl2tpDy4Q0
8ijE6m30Ku/Ba3ba+367hTzSU8JNvnHhRdH9I2cNE3X7z2VnIp2usAnRCf8dNL/+I5c30jn6PQ0G
C7TbO6Orb1wdtn7os4I07QZcJA==
-----END CERTIFICATE-----
T-TeleSec GlobalRoot Class 2
============================
-----BEGIN CERTIFICATE-----
MIIDwzCCAqugAwIBAgIBATANBgkqhkiG9w0BAQsFADCBgjELMAkGA1UEBhMCREUxKzApBgNVBAoM
IlQtU3lzdGVtcyBFbnRlcnByaXNlIFNlcnZpY2VzIEdtYkgxHzAdBgNVBAsMFlQtU3lzdGVtcyBU
cnVzdCBDZW50ZXIxJTAjBgNVBAMMHFQtVGVsZVNlYyBHbG9iYWxSb290IENsYXNzIDIwHhcNMDgx
MDAxMTA0MDE0WhcNMzMxMDAxMjM1OTU5WjCBgjELMAkGA1UEBhMCREUxKzApBgNVBAoMIlQtU3lz
dGVtcyBFbnRlcnByaXNlIFNlcnZpY2VzIEdtYkgxHzAdBgNVBAsMFlQtU3lzdGVtcyBUcnVzdCBD
ZW50ZXIxJTAjBgNVBAMMHFQtVGVsZVNlYyBHbG9iYWxSb290IENsYXNzIDIwggEiMA0GCSqGSIb3
DQEBAQUAA4IBDwAwggEKAoIBAQCqX9obX+hzkeXaXPSi5kfl82hVYAUdAqSzm1nzHoqvNK38DcLZ
SBnuaY/JIPwhqgcZ7bBcrGXHX+0CfHt8LRvWurmAwhiCFoT6ZrAIxlQjgeTNuUk/9k9uN0goOA/F
vudocP05l03Sx5iRUKrERLMjfTlH6VJi1hKTXrcxlkIF+3anHqP1wvzpesVsqXFP6st4vGCvx970
2cu+fjOlbpSD8DT6IavqjnKgP6TeMFvvhk1qlVtDRKgQFRzlAVfFmPHmBiiRqiDFt1MmUUOyCxGV
WOHAD3bZwI18gfNycJ5v/hqO2V81xrJvNHy+SE/iWjnX2J14np+GPgNeGYtEotXHAgMBAAGjQjBA
MA8GA1UdEwEB/wQFMAMBAf8wDgYDVR0PAQH/BAQDAgEGMB0GA1UdDgQWBBS/WSA2AHmgoCJrjNXy
YdK4LMuCSjANBgkqhkiG9w0BAQsFAAOCAQEAMQOiYQsfdOhyNsZt+U2e+iKo4YFWz827n+qrkRk4
r6p8FU3ztqONpfSO9kSpp+ghla0+AGIWiPACuvxhI+YzmzB6azZie60EI4RYZeLbK4rnJVM3YlNf
vNoBYimipidx5joifsFvHZVwIEoHNN/q/xWA5brXethbdXwFeilHfkCoMRN3zUA7tFFHei4R40cR
3p1m0IvVVGb6g1XqfMIpiRvpb7PO4gWEyS8+eIVibslfwXhjdFjASBgMmTnrpMwatXlajRWc2BQN
9noHV8cigwUtPJslJj0Ys6lDfMjIq2SPDqO/nBudMNva0Bkuqjzx+zOAduTNrRlPBSeOE6Fuwg==
-----END CERTIFICATE-----
Atos TrustedRoot 2011
=====================
-----BEGIN CERTIFICATE-----
MIIDdzCCAl+gAwIBAgIIXDPLYixfszIwDQYJKoZIhvcNAQELBQAwPDEeMBwGA1UEAwwVQXRvcyBU
cnVzdGVkUm9vdCAyMDExMQ0wCwYDVQQKDARBdG9zMQswCQYDVQQGEwJERTAeFw0xMTA3MDcxNDU4
MzBaFw0zMDEyMzEyMzU5NTlaMDwxHjAcBgNVBAMMFUF0b3MgVHJ1c3RlZFJvb3QgMjAxMTENMAsG
A1UECgwEQXRvczELMAkGA1UEBhMCREUwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCV
hTuXbyo7LjvPpvMpNb7PGKw+qtn4TaA+Gke5vJrf8v7MPkfoepbCJI419KkM/IL9bcFyYie96mvr
54rMVD6QUM+A1JX76LWC1BTFtqlVJVfbsVD2sGBkWXppzwO3bw2+yj5vdHLqqjAqc2K+SZFhyBH+
DgMq92og3AIVDV4VavzjgsG1xZ1kCWyjWZgHJ8cblithdHFsQ/H3NYkQ4J7sVaE3IqKHBAUsR320
HLliKWYoyrfhk/WklAOZuXCFteZI6o1Q/NnezG8HDt0Lcp2AMBYHlT8oDv3FdU9T1nSatCQujgKR
z3bFmx5VdJx4IbHwLfELn8LVlhgf8FQieowHAgMBAAGjfTB7MB0GA1UdDgQWBBSnpQaxLKYJYO7R
l+lwrrw7GWzbITAPBgNVHRMBAf8EBTADAQH/MB8GA1UdIwQYMBaAFKelBrEspglg7tGX6XCuvDsZ
bNshMBgGA1UdIAQRMA8wDQYLKwYBBAGwLQMEAQEwDgYDVR0PAQH/BAQDAgGGMA0GCSqGSIb3DQEB
CwUAA4IBAQAmdzTblEiGKkGdLD4GkGDEjKwLVLgfuXvTBznk+j57sj1O7Z8jvZfza1zv7v1Apt+h
k6EKhqzvINB5Ab149xnYJDE0BAGmuhWawyfc2E8PzBhj/5kPDpFrdRbhIfzYJsdHt6bPWHJxfrrh
TZVHO8mvbaG0weyJ9rQPOLXiZNwlz6bb65pcmaHFCN795trV1lpFDMS3wrUU77QR/w4VtfX128a9
61qn8FYiqTxlVMYVqL2Gns2Dlmh6cYGJ4Qvh6hEbaAjMaZ7snkGeRDImeuKHCnE96+RapNLbxc3G
3mB/ufNPRJLvKrcYPqcZ2Qt9sTdBQrC6YB3y/gkRsPCHe6ed
-----END CERTIFICATE-----

View File

@ -274,6 +274,43 @@ function uri_template($template, array $variables)
return $uriTemplate->expand($template, $variables);
}
/**
* Wrapper for JSON decode that implements error detection with helpful error
* messages.
*
* @param string $json JSON data to parse
* @param bool $assoc When true, returned objects will be converted into
* associative arrays.
* @param int $depth User specified recursion depth.
* @param int $options Bitmask of JSON decode options.
*
* @return mixed
* @throws \InvalidArgumentException if the JSON cannot be parsed.
* @link http://www.php.net/manual/en/function.json-decode.php
*/
function json_decode($json, $assoc = false, $depth = 512, $options = 0)
{
static $jsonErrors = [
JSON_ERROR_DEPTH => 'JSON_ERROR_DEPTH - Maximum stack depth exceeded',
JSON_ERROR_STATE_MISMATCH => 'JSON_ERROR_STATE_MISMATCH - Underflow or the modes mismatch',
JSON_ERROR_CTRL_CHAR => 'JSON_ERROR_CTRL_CHAR - Unexpected control character found',
JSON_ERROR_SYNTAX => 'JSON_ERROR_SYNTAX - Syntax error, malformed JSON',
JSON_ERROR_UTF8 => 'JSON_ERROR_UTF8 - Malformed UTF-8 characters, possibly incorrectly encoded'
];
$data = \json_decode($json, $assoc, $depth, $options);
if (JSON_ERROR_NONE !== json_last_error()) {
$last = json_last_error();
throw new \InvalidArgumentException(
'Unable to parse JSON data: '
. (isset($jsonErrors[$last]) ? $jsonErrors[$last] : 'Unknown error')
);
}
return $data;
}
/**
* @internal
*/

View File

@ -122,6 +122,9 @@ class StreamAdapterTest extends \PHPUnit_Framework_TestCase
$this->assertEquals('OK', $response->getReasonPhrase());
$this->assertEquals('8', $response->getHeader('Content-Length'));
$body = $response->getBody();
if (defined('HHVM_VERSION')) {
$this->markTestIncomplete('HHVM has not implemented this?');
}
$this->assertEquals('http', $body->getMetadata()['wrapper_type']);
$this->assertEquals(8, $body->getMetadata()['unread_bytes']);
$this->assertEquals(Server::$url . 'foo', $body->getMetadata()['uri']);
@ -222,6 +225,7 @@ class StreamAdapterTest extends \PHPUnit_Framework_TestCase
$body = $this->getSendResult(['stream' => true, 'proxy' => '127.0.0.1:8124'])->getBody();
$opts = stream_context_get_options($this->getStreamFromBody($body));
$this->assertEquals('127.0.0.1:8124', $opts['http']['proxy']);
$this->assertTrue($opts['http']['request_fulluri']);
}
public function testAddsTimeout()
@ -289,6 +293,10 @@ class StreamAdapterTest extends \PHPUnit_Framework_TestCase
public function testDebugAttributeWritesStreamInfoToTempBufferByDefault()
{
if (defined('HHVM_VERSION')) {
$this->markTestSkipped('HHVM has not implemented this?');
return;
}
Server::flush();
Server::enqueue("HTTP/1.1 200 OK\r\nFoo: Bar\r\nContent-Length: 8\r\n\r\nhi there");
@ -306,6 +314,11 @@ class StreamAdapterTest extends \PHPUnit_Framework_TestCase
public function testDebugAttributeWritesStreamInfoToBuffer()
{
if (defined('HHVM_VERSION')) {
$this->markTestSkipped('HHVM has not implemented this?');
return;
}
$buffer = fopen('php://temp', 'r+');
Server::flush();
Server::enqueue("HTTP/1.1 200 OK\r\nContent-Length: 8\r\nContent-Type: text/plain\r\n\r\nhi there");

View File

@ -70,103 +70,4 @@ class TransactionIteratorTest extends \PHPUnit_Framework_TestCase
$t = new TransactionIterator(new \ArrayIterator($requests), $c, []);
iterator_to_array($t);
}
public function testRegistersEvents()
{
$fn = function() {};
$c = new Client();
$requests = [$c->createRequest('GET', 'http://test.com')];
$trans = new TransactionIterator(new \ArrayIterator($requests), $c, [
'before' => $fn,
'complete' => $fn,
'error' => $fn,
]);
$t = iterator_to_array($trans)[0];
$em = $t->getRequest()->getEmitter();
$this->assertSame($fn, $em->listeners('before')[0]);
$this->assertSame($fn, $em->listeners('complete')[2]);
$this->assertSame($fn, $em->listeners('error')[0]);
}
public function testRegistersEventsWithPriorities()
{
$fn = function() {};
$client = new Client();
$requests = [$client->createRequest('GET', 'http://test.com')];
$trans = new TransactionIterator(
new \ArrayIterator($requests),
$client,
[
'before' => [['fn' => $fn, 'priority' => 99]],
'complete' => [['fn' => $fn, 'priority' => 99]],
'error' => [['fn' => $fn, 'priority' => 99]]
]
);
$t = iterator_to_array($trans)[0];
$em = $t->getRequest()->getEmitter();
$this->assertSame($fn, $em->listeners('before')[0]);
$this->assertSame($fn, $em->listeners('complete')[2]);
$this->assertSame($fn, $em->listeners('error')[0]);
}
public function testRegistersMultipleEvents()
{
$fn = function() {};
$c = new Client();
$eventArray = [['fn' => $fn], ['fn' => $fn]];
$requests = [$c->createRequest('GET', 'http://test.com')];
$trans = new TransactionIterator(new \ArrayIterator($requests), $c, [
'before' => $eventArray,
'complete' => $eventArray,
'error' => $eventArray,
]);
$t = iterator_to_array($trans)[0];
$em = $t->getRequest()->getEmitter();
$this->assertSame($fn, $em->listeners('before')[0]);
$this->assertSame($fn, $em->listeners('before')[1]);
$this->assertSame($fn, $em->listeners('complete')[2]);
$this->assertSame($fn, $em->listeners('complete')[3]);
$this->assertSame($fn, $em->listeners('error')[0]);
$this->assertSame($fn, $em->listeners('error')[1]);
}
public function testRegistersEventsWithOnce()
{
$called = 0;
$fn = function () use (&$called) { $called++; };
$client = new Client();
$requests = [$client->createRequest('GET', 'http://test.com')];
// Remove an default listeners
foreach ($requests[0]->getEmitter()->listeners('before') as $l) {
$requests[0]->getEmitter()->removeListener('before', $l);
}
$trans = new TransactionIterator(
new \ArrayIterator($requests),
$client,
['before' => [['fn' => $fn, 'once' => true]]]
);
// Apply the listeners to the request
iterator_to_array($trans)[0];
$ev = $this->getMockBuilder('GuzzleHttp\Event\BeforeEvent')
->disableOriginalConstructor()
->getMock();
$requests[0]->getEmitter()->emit('before', $ev);
$requests[0]->getEmitter()->emit('before', $ev);
$this->assertEquals(1, $called);
}
/**
* @expectedException \InvalidArgumentException
*/
public function testValidatesEvents()
{
$client = new Client();
$requests = [$client->createRequest('GET', 'http://test.com')];
new TransactionIterator(new \ArrayIterator($requests), $client, [
'before' => 'foo'
]);
}
}

View File

@ -103,6 +103,19 @@ class ClientTest extends \PHPUnit_Framework_TestCase
$client->get();
}
public function testCanSpecifyEmitter()
{
$emitter = $this->getMockBuilder('GuzzleHttp\Event\EmitterInterface')
->setMethods(['listeners'])
->getMockForAbstractClass();
$emitter->expects($this->once())
->method('listeners')
->will($this->returnValue('foo'));
$client = new Client(['emitter' => $emitter]);
$this->assertEquals('foo', $client->getEmitter()->listeners());
}
public function testAddsDefaultUserAgentHeaderWithDefaultOptions()
{
$client = new Client(['defaults' => ['allow_redirects' => false]]);

View File

@ -0,0 +1,76 @@
<?php
namespace GuzzleHttp\Tests\CookieJar;
use GuzzleHttp\Cookie\SessionCookieJar;
use GuzzleHttp\Cookie\SetCookie;
/**
* @covers GuzzleHttp\Cookie\SessionCookieJar
*/
class SessionCookieJarTest extends \PHPUnit_Framework_TestCase
{
private $sessionVar;
public function setUp()
{
$this->sessionVar = 'sessionKey';
if (!isset($_SESSION)) {
$_SESSION = array();
}
}
/**
* @expectedException \RuntimeException
*/
public function testValidatesCookieSession()
{
$_SESSION[$this->sessionVar] = 'true';
new SessionCookieJar($this->sessionVar);
}
public function testLoadsFromSession()
{
$jar = new SessionCookieJar($this->sessionVar);
$this->assertEquals([], $jar->getIterator()->getArrayCopy());
unset($_SESSION[$this->sessionVar]);
}
public function testPersistsToSession()
{
$jar = new SessionCookieJar($this->sessionVar);
$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',
]));
$this->assertEquals(3, count($jar));
unset($jar);
// Make sure it wrote to the sessionVar in $_SESSION
$contents = $_SESSION[$this->sessionVar];
$this->assertNotEmpty($contents);
// Load the cookieJar from the file
$jar = new SessionCookieJar($this->sessionVar);
// Weeds out temporary and session cookies
$this->assertEquals(2, count($jar));
unset($jar);
unset($_SESSION[$this->sessionVar]);
}
}

View File

@ -0,0 +1,93 @@
<?php
namespace GuzzleHttp\Tests\Event;
use GuzzleHttp\Event\HasEmitterInterface;
use GuzzleHttp\Event\HasEmitterTrait;
use GuzzleHttp\Event\ListenerAttacherTrait;
class ObjectWithEvents implements HasEmitterInterface
{
use HasEmitterTrait, ListenerAttacherTrait;
public $listeners = [];
public function __construct(array $args = [])
{
$this->listeners = $this->prepareListeners($args, ['foo', 'bar']);
$this->attachListeners($this, $this->listeners);
}
}
class ListenerAttacherTraitTest extends \PHPUnit_Framework_TestCase
{
public function testRegistersEvents()
{
$fn = function() {};
$o = new ObjectWithEvents([
'foo' => $fn,
'bar' => $fn,
]);
$this->assertEquals([
['name' => 'foo', 'fn' => $fn, 'priority' => 0, 'once' => false],
['name' => 'bar', 'fn' => $fn, 'priority' => 0, 'once' => false],
], $o->listeners);
$this->assertCount(1, $o->getEmitter()->listeners('foo'));
$this->assertCount(1, $o->getEmitter()->listeners('bar'));
}
public function testRegistersEventsWithPriorities()
{
$fn = function() {};
$o = new ObjectWithEvents([
'foo' => ['fn' => $fn, 'priority' => 99, 'once' => true],
'bar' => ['fn' => $fn, 'priority' => 50],
]);
$this->assertEquals([
['name' => 'foo', 'fn' => $fn, 'priority' => 99, 'once' => true],
['name' => 'bar', 'fn' => $fn, 'priority' => 50, 'once' => false],
], $o->listeners);
}
public function testRegistersMultipleEvents()
{
$fn = function() {};
$eventArray = [['fn' => $fn], ['fn' => $fn]];
$o = new ObjectWithEvents([
'foo' => $eventArray,
'bar' => $eventArray,
]);
$this->assertEquals([
['name' => 'foo', 'fn' => $fn, 'priority' => 0, 'once' => false],
['name' => 'foo', 'fn' => $fn, 'priority' => 0, 'once' => false],
['name' => 'bar', 'fn' => $fn, 'priority' => 0, 'once' => false],
['name' => 'bar', 'fn' => $fn, 'priority' => 0, 'once' => false],
], $o->listeners);
$this->assertCount(2, $o->getEmitter()->listeners('foo'));
$this->assertCount(2, $o->getEmitter()->listeners('bar'));
}
public function testRegistersEventsWithOnce()
{
$called = 0;
$fn = function () use (&$called) { $called++; };
$o = new ObjectWithEvents(['foo' => ['fn' => $fn, 'once' => true]]);
$ev = $this->getMock('GuzzleHttp\Event\EventInterface');
$o->getEmitter()->emit('foo', $ev);
$o->getEmitter()->emit('foo', $ev);
$this->assertEquals(1, $called);
}
/**
* @expectedException \InvalidArgumentException
*/
public function testValidatesEvents()
{
$o = new ObjectWithEvents(['foo' => 'bar']);
}
}

View File

@ -76,4 +76,9 @@ class RequestExceptionTest extends \PHPUnit_Framework_TestCase
$e->emittedError(true);
$e->emittedError(false);
}
public function testHasStatusCodeAsExceptionCode() {
$e = RequestException::create(new Request('GET', '/'), new Response(442));
$this->assertEquals(442, $e->getCode());
}
}

View File

@ -142,6 +142,21 @@ class FunctionsTest extends \PHPUnit_Framework_TestCase
$requests = [$client->createRequest('GET', 'http://foo.com/baz')];
\GuzzleHttp\batch($client, $requests, ['complete' => 'foo']);
}
public function testJsonDecodes()
{
$data = \GuzzleHttp\json_decode('true');
$this->assertTrue($data);
}
/**
* @expectedException \InvalidArgumentException
* @expectedExceptionMessage Unable to parse JSON data: JSON_ERROR_SYNTAX - Syntax error, malformed JSON
*/
public function testJsonDecodesWithErrorMessages()
{
\GuzzleHttp\json_decode('!narf!');
}
}
class HasDeprecations

View File

@ -51,6 +51,26 @@ class MessageFactoryTest extends \PHPUnit_Framework_TestCase
$this->assertEquals('abc=123', $req->getBody());
}
public function testCreatesRequestWithPostBodyScalars()
{
$req = (new MessageFactory())->createRequest(
'GET',
'http://www.foo.com',
['body' => [
'abc' => true,
'123' => false,
'foo' => null,
'baz' => 10,
'bam' => 1.5,
'boo' => [1]]
]
);
$this->assertEquals(
'abc=1&123=&foo&baz=10&bam=1.5&boo%5B0%5D=1',
(string) $req->getBody()
);
}
public function testCreatesRequestWithPostBodyAndPostFiles()
{
$pf = fopen(__FILE__, 'r');
@ -452,7 +472,29 @@ class MessageFactoryTest extends \PHPUnit_Framework_TestCase
public function testCanSetProtocolVersion()
{
$request = (new MessageFactory())->createRequest('GET', 'http://test.com', ['version' => 1.0]);
$request = (new MessageFactory())->createRequest('GET', 'http://t.com', ['version' => 1.0]);
$this->assertEquals(1.0, $request->getProtocolVersion());
}
public function testCanAddJsonData()
{
$request = (new MessageFactory)->createRequest('PUT', 'http://f.com', [
'json' => ['foo' => 'bar']
]);
$this->assertEquals(
'application/json',
$request->getHeader('Content-Type')
);
$this->assertEquals('{"foo":"bar"}', (string) $request->getBody());
}
public function testCanAddJsonDataAndNotOverwriteContentType()
{
$request = (new MessageFactory)->createRequest('PUT', 'http://f.com', [
'headers' => ['Content-Type' => 'foo'],
'json' => null
]);
$this->assertEquals('foo', $request->getHeader('Content-Type'));
$this->assertEquals('null', (string) $request->getBody());
}
}

View File

@ -117,4 +117,16 @@ class RequestTest extends \PHPUnit_Framework_TestCase
$this->assertEquals('http://goo', $r->getUrl());
$this->assertEquals('goo', $r->getHeader('host'));
}
public function testCanChangePort()
{
$r = new Request('GET', 'http://www.foo.com:222');
$this->assertSame(222, $r->getPort());
$this->assertEquals('www.foo.com', $r->getHost());
$this->assertEquals('www.foo.com:222', $r->getHeader('host'));
$r->setPort(80);
$this->assertSame(80, $r->getPort());
$this->assertEquals('www.foo.com', $r->getHost());
$this->assertEquals('www.foo.com', $r->getHeader('host'));
}
}

View File

@ -49,8 +49,8 @@ class ResponseTest extends \PHPUnit_Framework_TestCase
}
/**
* @expectedException \RuntimeException
* @expectedExceptionMessage Unable to parse response body into JSON: 4
* @expectedException \GuzzleHttp\Exception\ParseException
* @expectedExceptionMessage Unable to parse JSON data: JSON_ERROR_SYNTAX - Syntax error, malformed JSON
*/
public function testThrowsExceptionWhenFailsToParseJsonResponse()
{

View File

@ -37,7 +37,9 @@ class Server
*/
public static function flush()
{
self::$started && self::$client->delete('guzzle-server/requests');
self::start();
return self::$client->delete('guzzle-server/requests');
}
/**
@ -126,7 +128,7 @@ class Server
self::$started = false;
}
public static function wait($maxTries = 3)
public static function wait($maxTries = 5)
{
$tries = 0;
while (!self::isListening() && ++$tries < $maxTries) {

View File

@ -227,4 +227,20 @@ class RedirectTest extends \PHPUnit_Framework_TestCase
$reqs = $h->getRequests();
$this->assertFalse($reqs[1]->hasHeader('Referer'));
}
public function testRedirectsWithGetOn303()
{
$h = new History();
$mock = new Mock([
"HTTP/1.1 303 Moved Permanently\r\nLocation: /redirect\r\nContent-Length: 0\r\n\r\n",
"HTTP/1.1 200 OK\r\nContent-Length: 0\r\n\r\n",
]);
$client = new Client();
$client->getEmitter()->attach($mock);
$client->getEmitter()->attach($h);
$client->post('http://test.com/foo', ['body' => 'testing']);
$requests = $h->getRequests();
$this->assertEquals('POST', $requests[0]->getMethod());
$this->assertEquals('GET', $requests[1]->getMethod());
}
}