summaryrefslogtreecommitdiffstats
path: root/vendor/guzzlehttp/guzzle/UPGRADING.md
diff options
context:
space:
mode:
Diffstat (limited to 'vendor/guzzlehttp/guzzle/UPGRADING.md')
-rw-r--r--vendor/guzzlehttp/guzzle/UPGRADING.md2406
1 files changed, 1203 insertions, 1203 deletions
diff --git a/vendor/guzzlehttp/guzzle/UPGRADING.md b/vendor/guzzlehttp/guzzle/UPGRADING.md
index 91d1dcc..3636fc6 100644
--- a/vendor/guzzlehttp/guzzle/UPGRADING.md
+++ b/vendor/guzzlehttp/guzzle/UPGRADING.md
@@ -1,1203 +1,1203 @@
-Guzzle Upgrade Guide
-====================
-
-5.0 to 6.0
-----------
-
-Guzzle now uses [PSR-7](http://www.php-fig.org/psr/psr-7/) for HTTP messages.
-Due to the fact that these messages are immutable, this prompted a refactoring
-of Guzzle to use a middleware based system rather than an event system. Any
-HTTP message interaction (e.g., `GuzzleHttp\Message\Request`) need to be
-updated to work with the new immutable PSR-7 request and response objects. Any
-event listeners or subscribers need to be updated to become middleware
-functions that wrap handlers (or are injected into a
-`GuzzleHttp\HandlerStack`).
-
-- Removed `GuzzleHttp\BatchResults`
-- Removed `GuzzleHttp\Collection`
-- Removed `GuzzleHttp\HasDataTrait`
-- Removed `GuzzleHttp\ToArrayInterface`
-- The `guzzlehttp/streams` dependency has been removed. Stream functionality
- is now present in the `GuzzleHttp\Psr7` namespace provided by the
- `guzzlehttp/psr7` package.
-- Guzzle no longer uses ReactPHP promises and now uses the
- `guzzlehttp/promises` library. We use a custom promise library for three
- significant reasons:
- 1. React promises (at the time of writing this) are recursive. Promise
- chaining and promise resolution will eventually blow the stack. Guzzle
- promises are not recursive as they use a sort of trampolining technique.
- Note: there has been movement in the React project to modify promises to
- no longer utilize recursion.
- 2. Guzzle needs to have the ability to synchronously block on a promise to
- wait for a result. Guzzle promises allows this functionality (and does
- not require the use of recursion).
- 3. Because we need to be able to wait on a result, doing so using React
- promises requires wrapping react promises with RingPHP futures. This
- overhead is no longer needed, reducing stack sizes, reducing complexity,
- and improving performance.
-- `GuzzleHttp\Mimetypes` has been moved to a function in
- `GuzzleHttp\Psr7\mimetype_from_extension` and
- `GuzzleHttp\Psr7\mimetype_from_filename`.
-- `GuzzleHttp\Query` and `GuzzleHttp\QueryParser` have been removed. Query
- strings must now be passed into request objects as strings, or provided to
- the `query` request option when creating requests with clients. The `query`
- option uses PHP's `http_build_query` to convert an array to a string. If you
- need a different serialization technique, you will need to pass the query
- string in as a string. There are a couple helper functions that will make
- working with query strings easier: `GuzzleHttp\Psr7\parse_query` and
- `GuzzleHttp\Psr7\build_query`.
-- Guzzle no longer has a dependency on RingPHP. Due to the use of a middleware
- system based on PSR-7, using RingPHP and it's middleware system as well adds
- more complexity than the benefits it provides. All HTTP handlers that were
- present in RingPHP have been modified to work directly with PSR-7 messages
- and placed in the `GuzzleHttp\Handler` namespace. This significantly reduces
- complexity in Guzzle, removes a dependency, and improves performance. RingPHP
- will be maintained for Guzzle 5 support, but will no longer be a part of
- Guzzle 6.
-- As Guzzle now uses a middleware based systems the event system and RingPHP
- integration has been removed. Note: while the event system has been removed,
- it is possible to add your own type of event system that is powered by the
- middleware system.
- - Removed the `Event` namespace.
- - Removed the `Subscriber` namespace.
- - Removed `Transaction` class
- - Removed `RequestFsm`
- - Removed `RingBridge`
- - `GuzzleHttp\Subscriber\Cookie` is now provided by
- `GuzzleHttp\Middleware::cookies`
- - `GuzzleHttp\Subscriber\HttpError` is now provided by
- `GuzzleHttp\Middleware::httpError`
- - `GuzzleHttp\Subscriber\History` is now provided by
- `GuzzleHttp\Middleware::history`
- - `GuzzleHttp\Subscriber\Mock` is now provided by
- `GuzzleHttp\Handler\MockHandler`
- - `GuzzleHttp\Subscriber\Prepare` is now provided by
- `GuzzleHttp\PrepareBodyMiddleware`
- - `GuzzleHttp\Subscriber\Redirect` is now provided by
- `GuzzleHttp\RedirectMiddleware`
-- Guzzle now uses `Psr\Http\Message\UriInterface` (implements in
- `GuzzleHttp\Psr7\Uri`) for URI support. `GuzzleHttp\Url` is now gone.
-- Static functions in `GuzzleHttp\Utils` have been moved to namespaced
- functions under the `GuzzleHttp` namespace. This requires either a Composer
- based autoloader or you to include functions.php.
-- `GuzzleHttp\ClientInterface::getDefaultOption` has been renamed to
- `GuzzleHttp\ClientInterface::getConfig`.
-- `GuzzleHttp\ClientInterface::setDefaultOption` has been removed.
-- The `json` and `xml` methods of response objects has been removed. With the
- migration to strictly adhering to PSR-7 as the interface for Guzzle messages,
- adding methods to message interfaces would actually require Guzzle messages
- to extend from PSR-7 messages rather then work with them directly.
-
-## Migrating to middleware
-
-The change to PSR-7 unfortunately required significant refactoring to Guzzle
-due to the fact that PSR-7 messages are immutable. Guzzle 5 relied on an event
-system from plugins. The event system relied on mutability of HTTP messages and
-side effects in order to work. With immutable messages, you have to change your
-workflow to become more about either returning a value (e.g., functional
-middlewares) or setting a value on an object. Guzzle v6 has chosen the
-functional middleware approach.
-
-Instead of using the event system to listen for things like the `before` event,
-you now create a stack based middleware function that intercepts a request on
-the way in and the promise of the response on the way out. This is a much
-simpler and more predictable approach than the event system and works nicely
-with PSR-7 middleware. Due to the use of promises, the middleware system is
-also asynchronous.
-
-v5:
-
-```php
-use GuzzleHttp\Event\BeforeEvent;
-$client = new GuzzleHttp\Client();
-// Get the emitter and listen to the before event.
-$client->getEmitter()->on('before', function (BeforeEvent $e) {
- // Guzzle v5 events relied on mutation
- $e->getRequest()->setHeader('X-Foo', 'Bar');
-});
-```
-
-v6:
-
-In v6, you can modify the request before it is sent using the `mapRequest`
-middleware. The idiomatic way in v6 to modify the request/response lifecycle is
-to setup a handler middleware stack up front and inject the handler into a
-client.
-
-```php
-use GuzzleHttp\Middleware;
-// Create a handler stack that has all of the default middlewares attached
-$handler = GuzzleHttp\HandlerStack::create();
-// Push the handler onto the handler stack
-$handler->push(Middleware::mapRequest(function (RequestInterface $request) {
- // Notice that we have to return a request object
- return $request->withHeader('X-Foo', 'Bar');
-}));
-// Inject the handler into the client
-$client = new GuzzleHttp\Client(['handler' => $handler]);
-```
-
-## POST Requests
-
-This version added the [`form_params`](http://guzzle.readthedocs.org/en/latest/request-options.html#form_params)
-and `multipart` request options. `form_params` is an associative array of
-strings or array of strings and is used to serialize an
-`application/x-www-form-urlencoded` POST request. The
-[`multipart`](http://guzzle.readthedocs.org/en/latest/request-options.html#multipart)
-option is now used to send a multipart/form-data POST request.
-
-`GuzzleHttp\Post\PostFile` has been removed. Use the `multipart` option to add
-POST files to a multipart/form-data request.
-
-The `body` option no longer accepts an array to send POST requests. Please use
-`multipart` or `form_params` instead.
-
-The `base_url` option has been renamed to `base_uri`.
-
-4.x to 5.0
-----------
-
-## Rewritten Adapter Layer
-
-Guzzle now uses [RingPHP](http://ringphp.readthedocs.org/en/latest) to send
-HTTP requests. The `adapter` option in a `GuzzleHttp\Client` constructor
-is still supported, but it has now been renamed to `handler`. Instead of
-passing a `GuzzleHttp\Adapter\AdapterInterface`, you must now pass a PHP
-`callable` that follows the RingPHP specification.
-
-## Removed Fluent Interfaces
-
-[Fluent interfaces were removed](http://ocramius.github.io/blog/fluent-interfaces-are-evil)
-from the following classes:
-
-- `GuzzleHttp\Collection`
-- `GuzzleHttp\Url`
-- `GuzzleHttp\Query`
-- `GuzzleHttp\Post\PostBody`
-- `GuzzleHttp\Cookie\SetCookie`
-
-## Removed functions.php
-
-Removed "functions.php", so that Guzzle is truly PSR-4 compliant. The following
-functions can be used as replacements.
-
-- `GuzzleHttp\json_decode` -> `GuzzleHttp\Utils::jsonDecode`
-- `GuzzleHttp\get_path` -> `GuzzleHttp\Utils::getPath`
-- `GuzzleHttp\Utils::setPath` -> `GuzzleHttp\set_path`
-- `GuzzleHttp\Pool::batch` -> `GuzzleHttp\batch`. This function is, however,
- deprecated in favor of using `GuzzleHttp\Pool::batch()`.
-
-The "procedural" global client has been removed with no replacement (e.g.,
-`GuzzleHttp\get()`, `GuzzleHttp\post()`, etc.). Use a `GuzzleHttp\Client`
-object as a replacement.
-
-## `throwImmediately` has been removed
-
-The concept of "throwImmediately" has been removed from exceptions and error
-events. This control mechanism was used to stop a transfer of concurrent
-requests from completing. This can now be handled by throwing the exception or
-by cancelling a pool of requests or each outstanding future request
-individually.
-
-## headers event has been removed
-
-Removed the "headers" event. This event was only useful for changing the
-body a response once the headers of the response were known. You can implement
-a similar behavior in a number of ways. One example might be to use a
-FnStream that has access to the transaction being sent. For example, when the
-first byte is written, you could check if the response headers match your
-expectations, and if so, change the actual stream body that is being
-written to.
-
-## Updates to HTTP Messages
-
-Removed the `asArray` parameter from
-`GuzzleHttp\Message\MessageInterface::getHeader`. If you want to get a header
-value as an array, then use the newly added `getHeaderAsArray()` method of
-`MessageInterface`. This change makes the Guzzle interfaces compatible with
-the PSR-7 interfaces.
-
-3.x to 4.0
-----------
-
-## Overarching changes:
-
-- Now requires PHP 5.4 or greater.
-- No longer requires cURL to send requests.
-- Guzzle no longer wraps every exception it throws. Only exceptions that are
- recoverable are now wrapped by Guzzle.
-- Various namespaces have been removed or renamed.
-- No longer requiring the Symfony EventDispatcher. A custom event dispatcher
- based on the Symfony EventDispatcher is
- now utilized in `GuzzleHttp\Event\EmitterInterface` (resulting in significant
- speed and functionality improvements).
-
-Changes per Guzzle 3.x namespace are described below.
-
-## Batch
-
-The `Guzzle\Batch` namespace has been removed. This is best left to
-third-parties to implement on top of Guzzle's core HTTP library.
-
-## Cache
-
-The `Guzzle\Cache` namespace has been removed. (Todo: No suitable replacement
-has been implemented yet, but hoping to utilize a PSR cache interface).
-
-## Common
-
-- Removed all of the wrapped exceptions. It's better to use the standard PHP
- library for unrecoverable exceptions.
-- `FromConfigInterface` has been removed.
-- `Guzzle\Common\Version` has been removed. The VERSION constant can be found
- at `GuzzleHttp\ClientInterface::VERSION`.
-
-### Collection
-
-- `getAll` has been removed. Use `toArray` to convert a collection to an array.
-- `inject` has been removed.
-- `keySearch` has been removed.
-- `getPath` no longer supports wildcard expressions. Use something better like
- JMESPath for this.
-- `setPath` now supports appending to an existing array via the `[]` notation.
-
-### Events
-
-Guzzle no longer requires Symfony's EventDispatcher component. Guzzle now uses
-`GuzzleHttp\Event\Emitter`.
-
-- `Symfony\Component\EventDispatcher\EventDispatcherInterface` is replaced by
- `GuzzleHttp\Event\EmitterInterface`.
-- `Symfony\Component\EventDispatcher\EventDispatcher` is replaced by
- `GuzzleHttp\Event\Emitter`.
-- `Symfony\Component\EventDispatcher\Event` is replaced by
- `GuzzleHttp\Event\Event`, and Guzzle now has an EventInterface in
- `GuzzleHttp\Event\EventInterface`.
-- `AbstractHasDispatcher` has moved to a trait, `HasEmitterTrait`, and
- `HasDispatcherInterface` has moved to `HasEmitterInterface`. Retrieving the
- event emitter of a request, client, etc. now uses the `getEmitter` method
- rather than the `getDispatcher` method.
-
-#### Emitter
-
-- Use the `once()` method to add a listener that automatically removes itself
- the first time it is invoked.
-- Use the `listeners()` method to retrieve a list of event listeners rather than
- the `getListeners()` method.
-- Use `emit()` instead of `dispatch()` to emit an event from an emitter.
-- Use `attach()` instead of `addSubscriber()` and `detach()` instead of
- `removeSubscriber()`.
-
-```php
-$mock = new Mock();
-// 3.x
-$request->getEventDispatcher()->addSubscriber($mock);
-$request->getEventDispatcher()->removeSubscriber($mock);
-// 4.x
-$request->getEmitter()->attach($mock);
-$request->getEmitter()->detach($mock);
-```
-
-Use the `on()` method to add a listener rather than the `addListener()` method.
-
-```php
-// 3.x
-$request->getEventDispatcher()->addListener('foo', function (Event $event) { /* ... */ } );
-// 4.x
-$request->getEmitter()->on('foo', function (Event $event, $name) { /* ... */ } );
-```
-
-## Http
-
-### General changes
-
-- The cacert.pem certificate has been moved to `src/cacert.pem`.
-- Added the concept of adapters that are used to transfer requests over the
- wire.
-- Simplified the event system.
-- Sending requests in parallel is still possible, but batching is no longer a
- concept of the HTTP layer. Instead, you must use the `complete` and `error`
- events to asynchronously manage parallel request transfers.
-- `Guzzle\Http\Url` has moved to `GuzzleHttp\Url`.
-- `Guzzle\Http\QueryString` has moved to `GuzzleHttp\Query`.
-- QueryAggregators have been rewritten so that they are simply callable
- functions.
-- `GuzzleHttp\StaticClient` has been removed. Use the functions provided in
- `functions.php` for an easy to use static client instance.
-- Exceptions in `GuzzleHttp\Exception` have been updated to all extend from
- `GuzzleHttp\Exception\TransferException`.
-
-### Client
-
-Calling methods like `get()`, `post()`, `head()`, etc. no longer create and
-return a request, but rather creates a request, sends the request, and returns
-the response.
-
-```php
-// 3.0
-$request = $client->get('/');
-$response = $request->send();
-
-// 4.0
-$response = $client->get('/');
-
-// or, to mirror the previous behavior
-$request = $client->createRequest('GET', '/');
-$response = $client->send($request);
-```
-
-`GuzzleHttp\ClientInterface` has changed.
-
-- The `send` method no longer accepts more than one request. Use `sendAll` to
- send multiple requests in parallel.
-- `setUserAgent()` has been removed. Use a default request option instead. You
- could, for example, do something like:
- `$client->setConfig('defaults/headers/User-Agent', 'Foo/Bar ' . $client::getDefaultUserAgent())`.
-- `setSslVerification()` has been removed. Use default request options instead,
- like `$client->setConfig('defaults/verify', true)`.
-
-`GuzzleHttp\Client` has changed.
-
-- The constructor now accepts only an associative array. You can include a
- `base_url` string or array to use a URI template as the base URL of a client.
- You can also specify a `defaults` key that is an associative array of default
- request options. You can pass an `adapter` to use a custom adapter,
- `batch_adapter` to use a custom adapter for sending requests in parallel, or
- a `message_factory` to change the factory used to create HTTP requests and
- responses.
-- The client no longer emits a `client.create_request` event.
-- Creating requests with a client no longer automatically utilize a URI
- template. You must pass an array into a creational method (e.g.,
- `createRequest`, `get`, `put`, etc.) in order to expand a URI template.
-
-### Messages
-
-Messages no longer have references to their counterparts (i.e., a request no
-longer has a reference to it's response, and a response no loger has a
-reference to its request). This association is now managed through a
-`GuzzleHttp\Adapter\TransactionInterface` object. You can get references to
-these transaction objects using request events that are emitted over the
-lifecycle of a request.
-
-#### Requests with a body
-
-- `GuzzleHttp\Message\EntityEnclosingRequest` and
- `GuzzleHttp\Message\EntityEnclosingRequestInterface` have been removed. The
- separation between requests that contain a body and requests that do not
- contain a body has been removed, and now `GuzzleHttp\Message\RequestInterface`
- handles both use cases.
-- Any method that previously accepts a `GuzzleHttp\Response` object now accept a
- `GuzzleHttp\Message\ResponseInterface`.
-- `GuzzleHttp\Message\RequestFactoryInterface` has been renamed to
- `GuzzleHttp\Message\MessageFactoryInterface`. This interface is used to create
- both requests and responses and is implemented in
- `GuzzleHttp\Message\MessageFactory`.
-- POST field and file methods have been removed from the request object. You
- must now use the methods made available to `GuzzleHttp\Post\PostBodyInterface`
- to control the format of a POST body. Requests that are created using a
- standard `GuzzleHttp\Message\MessageFactoryInterface` will automatically use
- a `GuzzleHttp\Post\PostBody` body if the body was passed as an array or if
- the method is POST and no body is provided.
-
-```php
-$request = $client->createRequest('POST', '/');
-$request->getBody()->setField('foo', 'bar');
-$request->getBody()->addFile(new PostFile('file_key', fopen('/path/to/content', 'r')));
-```
-
-#### Headers
-
-- `GuzzleHttp\Message\Header` has been removed. Header values are now simply
- represented by an array of values or as a string. Header values are returned
- as a string by default when retrieving a header value from a message. You can
- pass an optional argument of `true` to retrieve a header value as an array
- of strings instead of a single concatenated string.
-- `GuzzleHttp\PostFile` and `GuzzleHttp\PostFileInterface` have been moved to
- `GuzzleHttp\Post`. This interface has been simplified and now allows the
- addition of arbitrary headers.
-- Custom headers like `GuzzleHttp\Message\Header\Link` have been removed. Most
- of the custom headers are now handled separately in specific
- subscribers/plugins, and `GuzzleHttp\Message\HeaderValues::parseParams()` has
- been updated to properly handle headers that contain parameters (like the
- `Link` header).
-
-#### Responses
-
-- `GuzzleHttp\Message\Response::getInfo()` and
- `GuzzleHttp\Message\Response::setInfo()` have been removed. Use the event
- system to retrieve this type of information.
-- `GuzzleHttp\Message\Response::getRawHeaders()` has been removed.
-- `GuzzleHttp\Message\Response::getMessage()` has been removed.
-- `GuzzleHttp\Message\Response::calculateAge()` and other cache specific
- methods have moved to the CacheSubscriber.
-- Header specific helper functions like `getContentMd5()` have been removed.
- Just use `getHeader('Content-MD5')` instead.
-- `GuzzleHttp\Message\Response::setRequest()` and
- `GuzzleHttp\Message\Response::getRequest()` have been removed. Use the event
- system to work with request and response objects as a transaction.
-- `GuzzleHttp\Message\Response::getRedirectCount()` has been removed. Use the
- Redirect subscriber instead.
-- `GuzzleHttp\Message\Response::isSuccessful()` and other related methods have
- been removed. Use `getStatusCode()` instead.
-
-#### Streaming responses
-
-Streaming requests can now be created by a client directly, returning a
-`GuzzleHttp\Message\ResponseInterface` object that contains a body stream
-referencing an open PHP HTTP stream.
-
-```php
-// 3.0
-use Guzzle\Stream\PhpStreamRequestFactory;
-$request = $client->get('/');
-$factory = new PhpStreamRequestFactory();
-$stream = $factory->fromRequest($request);
-$data = $stream->read(1024);
-
-// 4.0
-$response = $client->get('/', ['stream' => true]);
-// Read some data off of the stream in the response body
-$data = $response->getBody()->read(1024);
-```
-
-#### Redirects
-
-The `configureRedirects()` method has been removed in favor of a
-`allow_redirects` request option.
-
-```php
-// Standard redirects with a default of a max of 5 redirects
-$request = $client->createRequest('GET', '/', ['allow_redirects' => true]);
-
-// Strict redirects with a custom number of redirects
-$request = $client->createRequest('GET', '/', [
- 'allow_redirects' => ['max' => 5, 'strict' => true]
-]);
-```
-
-#### EntityBody
-
-EntityBody interfaces and classes have been removed or moved to
-`GuzzleHttp\Stream`. All classes and interfaces that once required
-`GuzzleHttp\EntityBodyInterface` now require
-`GuzzleHttp\Stream\StreamInterface`. Creating a new body for a request no
-longer uses `GuzzleHttp\EntityBody::factory` but now uses
-`GuzzleHttp\Stream\Stream::factory` or even better:
-`GuzzleHttp\Stream\create()`.
-
-- `Guzzle\Http\EntityBodyInterface` is now `GuzzleHttp\Stream\StreamInterface`
-- `Guzzle\Http\EntityBody` is now `GuzzleHttp\Stream\Stream`
-- `Guzzle\Http\CachingEntityBody` is now `GuzzleHttp\Stream\CachingStream`
-- `Guzzle\Http\ReadLimitEntityBody` is now `GuzzleHttp\Stream\LimitStream`
-- `Guzzle\Http\IoEmittyinEntityBody` has been removed.
-
-#### Request lifecycle events
-
-Requests previously submitted a large number of requests. The number of events
-emitted over the lifecycle of a request has been significantly reduced to make
-it easier to understand how to extend the behavior of a request. All events
-emitted during the lifecycle of a request now emit a custom
-`GuzzleHttp\Event\EventInterface` object that contains context providing
-methods and a way in which to modify the transaction at that specific point in
-time (e.g., intercept the request and set a response on the transaction).
-
-- `request.before_send` has been renamed to `before` and now emits a
- `GuzzleHttp\Event\BeforeEvent`
-- `request.complete` has been renamed to `complete` and now emits a
- `GuzzleHttp\Event\CompleteEvent`.
-- `request.sent` has been removed. Use `complete`.
-- `request.success` has been removed. Use `complete`.
-- `error` is now an event that emits a `GuzzleHttp\Event\ErrorEvent`.
-- `request.exception` has been removed. Use `error`.
-- `request.receive.status_line` has been removed.
-- `curl.callback.progress` has been removed. Use a custom `StreamInterface` to
- maintain a status update.
-- `curl.callback.write` has been removed. Use a custom `StreamInterface` to
- intercept writes.
-- `curl.callback.read` has been removed. Use a custom `StreamInterface` to
- intercept reads.
-
-`headers` is a new event that is emitted after the response headers of a
-request have been received before the body of the response is downloaded. This
-event emits a `GuzzleHttp\Event\HeadersEvent`.
-
-You can intercept a request and inject a response using the `intercept()` event
-of a `GuzzleHttp\Event\BeforeEvent`, `GuzzleHttp\Event\CompleteEvent`, and
-`GuzzleHttp\Event\ErrorEvent` event.
-
-See: http://docs.guzzlephp.org/en/latest/events.html
-
-## Inflection
-
-The `Guzzle\Inflection` namespace has been removed. This is not a core concern
-of Guzzle.
-
-## Iterator
-
-The `Guzzle\Iterator` namespace has been removed.
-
-- `Guzzle\Iterator\AppendIterator`, `Guzzle\Iterator\ChunkedIterator`, and
- `Guzzle\Iterator\MethodProxyIterator` are nice, but not a core requirement of
- Guzzle itself.
-- `Guzzle\Iterator\FilterIterator` is no longer needed because an equivalent
- class is shipped with PHP 5.4.
-- `Guzzle\Iterator\MapIterator` is not really needed when using PHP 5.5 because
- it's easier to just wrap an iterator in a generator that maps values.
-
-For a replacement of these iterators, see https://github.com/nikic/iter
-
-## Log
-
-The LogPlugin has moved to https://github.com/guzzle/log-subscriber. The
-`Guzzle\Log` namespace has been removed. Guzzle now relies on
-`Psr\Log\LoggerInterface` for all logging. The MessageFormatter class has been
-moved to `GuzzleHttp\Subscriber\Log\Formatter`.
-
-## Parser
-
-The `Guzzle\Parser` namespace has been removed. This was previously used to
-make it possible to plug in custom parsers for cookies, messages, URI
-templates, and URLs; however, this level of complexity is not needed in Guzzle
-so it has been removed.
-
-- Cookie: Cookie parsing logic has been moved to
- `GuzzleHttp\Cookie\SetCookie::fromString`.
-- Message: Message parsing logic for both requests and responses has been moved
- to `GuzzleHttp\Message\MessageFactory::fromMessage`. Message parsing is only
- used in debugging or deserializing messages, so it doesn't make sense for
- Guzzle as a library to add this level of complexity to parsing messages.
-- UriTemplate: URI template parsing has been moved to
- `GuzzleHttp\UriTemplate`. The Guzzle library will automatically use the PECL
- URI template library if it is installed.
-- Url: URL parsing is now performed in `GuzzleHttp\Url::fromString` (previously
- it was `Guzzle\Http\Url::factory()`). If custom URL parsing is necessary,
- then developers are free to subclass `GuzzleHttp\Url`.
-
-## Plugin
-
-The `Guzzle\Plugin` namespace has been renamed to `GuzzleHttp\Subscriber`.
-Several plugins are shipping with the core Guzzle library under this namespace.
-
-- `GuzzleHttp\Subscriber\Cookie`: Replaces the old CookiePlugin. Cookie jar
- code has moved to `GuzzleHttp\Cookie`.
-- `GuzzleHttp\Subscriber\History`: Replaces the old HistoryPlugin.
-- `GuzzleHttp\Subscriber\HttpError`: Throws errors when a bad HTTP response is
- received.
-- `GuzzleHttp\Subscriber\Mock`: Replaces the old MockPlugin.
-- `GuzzleHttp\Subscriber\Prepare`: Prepares the body of a request just before
- sending. This subscriber is attached to all requests by default.
-- `GuzzleHttp\Subscriber\Redirect`: Replaces the RedirectPlugin.
-
-The following plugins have been removed (third-parties are free to re-implement
-these if needed):
-
-- `GuzzleHttp\Plugin\Async` has been removed.
-- `GuzzleHttp\Plugin\CurlAuth` has been removed.
-- `GuzzleHttp\Plugin\ErrorResponse\ErrorResponsePlugin` has been removed. This
- functionality should instead be implemented with event listeners that occur
- after normal response parsing occurs in the guzzle/command package.
-
-The following plugins are not part of the core Guzzle package, but are provided
-in separate repositories:
-
-- `Guzzle\Http\Plugin\BackoffPlugin` has been rewritten to be much simpler
- to build custom retry policies using simple functions rather than various
- chained classes. See: https://github.com/guzzle/retry-subscriber
-- `Guzzle\Http\Plugin\Cache\CachePlugin` has moved to
- https://github.com/guzzle/cache-subscriber
-- `Guzzle\Http\Plugin\Log\LogPlugin` has moved to
- https://github.com/guzzle/log-subscriber
-- `Guzzle\Http\Plugin\Md5\Md5Plugin` has moved to
- https://github.com/guzzle/message-integrity-subscriber
-- `Guzzle\Http\Plugin\Mock\MockPlugin` has moved to
- `GuzzleHttp\Subscriber\MockSubscriber`.
-- `Guzzle\Http\Plugin\Oauth\OauthPlugin` has moved to
- https://github.com/guzzle/oauth-subscriber
-
-## Service
-
-The service description layer of Guzzle has moved into two separate packages:
-
-- http://github.com/guzzle/command Provides a high level abstraction over web
- services by representing web service operations using commands.
-- http://github.com/guzzle/guzzle-services Provides an implementation of
- guzzle/command that provides request serialization and response parsing using
- Guzzle service descriptions.
-
-## Stream
-
-Stream have moved to a separate package available at
-https://github.com/guzzle/streams.
-
-`Guzzle\Stream\StreamInterface` has been given a large update to cleanly take
-on the responsibilities of `Guzzle\Http\EntityBody` and
-`Guzzle\Http\EntityBodyInterface` now that they have been removed. The number
-of methods implemented by the `StreamInterface` has been drastically reduced to
-allow developers to more easily extend and decorate stream behavior.
-
-## Removed methods from StreamInterface
-
-- `getStream` and `setStream` have been removed to better encapsulate streams.
-- `getMetadata` and `setMetadata` have been removed in favor of
- `GuzzleHttp\Stream\MetadataStreamInterface`.
-- `getWrapper`, `getWrapperData`, `getStreamType`, and `getUri` have all been
- removed. This data is accessible when
- using streams that implement `GuzzleHttp\Stream\MetadataStreamInterface`.
-- `rewind` has been removed. Use `seek(0)` for a similar behavior.
-
-## Renamed methods
-
-- `detachStream` has been renamed to `detach`.
-- `feof` has been renamed to `eof`.
-- `ftell` has been renamed to `tell`.
-- `readLine` has moved from an instance method to a static class method of
- `GuzzleHttp\Stream\Stream`.
-
-## Metadata streams
-
-`GuzzleHttp\Stream\MetadataStreamInterface` has been added to denote streams
-that contain additional metadata accessible via `getMetadata()`.
-`GuzzleHttp\Stream\StreamInterface::getMetadata` and
-`GuzzleHttp\Stream\StreamInterface::setMetadata` have been removed.
-
-## StreamRequestFactory
-
-The entire concept of the StreamRequestFactory has been removed. The way this
-was used in Guzzle 3 broke the actual interface of sending streaming requests
-(instead of getting back a Response, you got a StreamInterface). Streaming
-PHP requests are now implemented through the `GuzzleHttp\Adapter\StreamAdapter`.
-
-3.6 to 3.7
-----------
-
-### Deprecations
-
-- You can now enable E_USER_DEPRECATED warnings to see if you are using any deprecated methods.:
-
-```php
-\Guzzle\Common\Version::$emitWarnings = true;
-```
-
-The following APIs and options have been marked as deprecated:
-
-- Marked `Guzzle\Http\Message\Request::isResponseBodyRepeatable()` as deprecated. Use `$request->getResponseBody()->isRepeatable()` instead.
-- Marked `Guzzle\Http\Message\Request::canCache()` as deprecated. Use `Guzzle\Plugin\Cache\DefaultCanCacheStrategy->canCacheRequest()` instead.
-- Marked `Guzzle\Http\Message\Request::canCache()` as deprecated. Use `Guzzle\Plugin\Cache\DefaultCanCacheStrategy->canCacheRequest()` instead.
-- Marked `Guzzle\Http\Message\Request::setIsRedirect()` as deprecated. Use the HistoryPlugin instead.
-- Marked `Guzzle\Http\Message\Request::isRedirect()` as deprecated. Use the HistoryPlugin instead.
-- Marked `Guzzle\Cache\CacheAdapterFactory::factory()` as deprecated
-- Marked `Guzzle\Service\Client::enableMagicMethods()` as deprecated. Magic methods can no longer be disabled on a Guzzle\Service\Client.
-- Marked `Guzzle\Parser\Url\UrlParser` as deprecated. Just use PHP's `parse_url()` and percent encode your UTF-8.
-- Marked `Guzzle\Common\Collection::inject()` as deprecated.
-- Marked `Guzzle\Plugin\CurlAuth\CurlAuthPlugin` as deprecated. Use
- `$client->getConfig()->setPath('request.options/auth', array('user', 'pass', 'Basic|Digest|NTLM|Any'));` or
- `$client->setDefaultOption('auth', array('user', 'pass', 'Basic|Digest|NTLM|Any'));`
-
-3.7 introduces `request.options` as a parameter for a client configuration and as an optional argument to all creational
-request methods. When paired with a client's configuration settings, these options allow you to specify default settings
-for various aspects of a request. Because these options make other previous configuration options redundant, several
-configuration options and methods of a client and AbstractCommand have been deprecated.
-
-- Marked `Guzzle\Service\Client::getDefaultHeaders()` as deprecated. Use `$client->getDefaultOption('headers')`.
-- Marked `Guzzle\Service\Client::setDefaultHeaders()` as deprecated. Use `$client->setDefaultOption('headers/{header_name}', 'value')`.
-- Marked 'request.params' for `Guzzle\Http\Client` as deprecated. Use `$client->setDefaultOption('params/{param_name}', 'value')`
-- Marked 'command.headers', 'command.response_body' and 'command.on_complete' as deprecated for AbstractCommand. These will work through Guzzle 4.0
-
- $command = $client->getCommand('foo', array(
- 'command.headers' => array('Test' => '123'),
- 'command.response_body' => '/path/to/file'
- ));
-
- // Should be changed to:
-
- $command = $client->getCommand('foo', array(
- 'command.request_options' => array(
- 'headers' => array('Test' => '123'),
- 'save_as' => '/path/to/file'
- )
- ));
-
-### Interface changes
-
-Additions and changes (you will need to update any implementations or subclasses you may have created):
-
-- Added an `$options` argument to the end of the following methods of `Guzzle\Http\ClientInterface`:
- createRequest, head, delete, put, patch, post, options, prepareRequest
-- Added an `$options` argument to the end of `Guzzle\Http\Message\Request\RequestFactoryInterface::createRequest()`
-- Added an `applyOptions()` method to `Guzzle\Http\Message\Request\RequestFactoryInterface`
-- Changed `Guzzle\Http\ClientInterface::get($uri = null, $headers = null, $body = null)` to
- `Guzzle\Http\ClientInterface::get($uri = null, $headers = null, $options = array())`. You can still pass in a
- resource, string, or EntityBody into the $options parameter to specify the download location of the response.
-- Changed `Guzzle\Common\Collection::__construct($data)` to no longer accepts a null value for `$data` but a
- default `array()`
-- Added `Guzzle\Stream\StreamInterface::isRepeatable`
-- Made `Guzzle\Http\Client::expandTemplate` and `getUriTemplate` protected methods.
-
-The following methods were removed from interfaces. All of these methods are still available in the concrete classes
-that implement them, but you should update your code to use alternative methods:
-
-- Removed `Guzzle\Http\ClientInterface::setDefaultHeaders(). Use
- `$client->getConfig()->setPath('request.options/headers/{header_name}', 'value')`. or
- `$client->getConfig()->setPath('request.options/headers', array('header_name' => 'value'))` or
- `$client->setDefaultOption('headers/{header_name}', 'value')`. or
- `$client->setDefaultOption('headers', array('header_name' => 'value'))`.
-- Removed `Guzzle\Http\ClientInterface::getDefaultHeaders(). Use `$client->getConfig()->getPath('request.options/headers')`.
-- Removed `Guzzle\Http\ClientInterface::expandTemplate()`. This is an implementation detail.
-- Removed `Guzzle\Http\ClientInterface::setRequestFactory()`. This is an implementation detail.
-- Removed `Guzzle\Http\ClientInterface::getCurlMulti()`. This is a very specific implementation detail.
-- Removed `Guzzle\Http\Message\RequestInterface::canCache`. Use the CachePlugin.
-- Removed `Guzzle\Http\Message\RequestInterface::setIsRedirect`. Use the HistoryPlugin.
-- Removed `Guzzle\Http\Message\RequestInterface::isRedirect`. Use the HistoryPlugin.
-
-### Cache plugin breaking changes
-
-- CacheKeyProviderInterface and DefaultCacheKeyProvider are no longer used. All of this logic is handled in a
- CacheStorageInterface. These two objects and interface will be removed in a future version.
-- Always setting X-cache headers on cached responses
-- Default cache TTLs are now handled by the CacheStorageInterface of a CachePlugin
-- `CacheStorageInterface::cache($key, Response $response, $ttl = null)` has changed to `cache(RequestInterface
- $request, Response $response);`
-- `CacheStorageInterface::fetch($key)` has changed to `fetch(RequestInterface $request);`
-- `CacheStorageInterface::delete($key)` has changed to `delete(RequestInterface $request);`
-- Added `CacheStorageInterface::purge($url)`
-- `DefaultRevalidation::__construct(CacheKeyProviderInterface $cacheKey, CacheStorageInterface $cache, CachePlugin
- $plugin)` has changed to `DefaultRevalidation::__construct(CacheStorageInterface $cache,
- CanCacheStrategyInterface $canCache = null)`
-- Added `RevalidationInterface::shouldRevalidate(RequestInterface $request, Response $response)`
-
-3.5 to 3.6
-----------
-
-* Mixed casing of headers are now forced to be a single consistent casing across all values for that header.
-* Messages internally use a HeaderCollection object to delegate handling case-insensitive header resolution
-* Removed the whole changedHeader() function system of messages because all header changes now go through addHeader().
- For example, setHeader() first removes the header using unset on a HeaderCollection and then calls addHeader().
- Keeping the Host header and URL host in sync is now handled by overriding the addHeader method in Request.
-* Specific header implementations can be created for complex headers. When a message creates a header, it uses a
- HeaderFactory which can map specific headers to specific header classes. There is now a Link header and
- CacheControl header implementation.
-* Moved getLinks() from Response to just be used on a Link header object.
-
-If you previously relied on Guzzle\Http\Message\Header::raw(), then you will need to update your code to use the
-HeaderInterface (e.g. toArray(), getAll(), etc.).
-
-### Interface changes
-
-* Removed from interface: Guzzle\Http\ClientInterface::setUriTemplate
-* Removed from interface: Guzzle\Http\ClientInterface::setCurlMulti()
-* Removed Guzzle\Http\Message\Request::receivedRequestHeader() and implemented this functionality in
- Guzzle\Http\Curl\RequestMediator
-* Removed the optional $asString parameter from MessageInterface::getHeader(). Just cast the header to a string.
-* Removed the optional $tryChunkedTransfer option from Guzzle\Http\Message\EntityEnclosingRequestInterface
-* Removed the $asObjects argument from Guzzle\Http\Message\MessageInterface::getHeaders()
-
-### Removed deprecated functions
-
-* Removed Guzzle\Parser\ParserRegister::get(). Use getParser()
-* Removed Guzzle\Parser\ParserRegister::set(). Use registerParser().
-
-### Deprecations
-
-* The ability to case-insensitively search for header values
-* Guzzle\Http\Message\Header::hasExactHeader
-* Guzzle\Http\Message\Header::raw. Use getAll()
-* Deprecated cache control specific methods on Guzzle\Http\Message\AbstractMessage. Use the CacheControl header object
- instead.
-
-### Other changes
-
-* All response header helper functions return a string rather than mixing Header objects and strings inconsistently
-* Removed cURL blacklist support. This is no longer necessary now that Expect, Accept, etc. are managed by Guzzle
- directly via interfaces
-* Removed the injecting of a request object onto a response object. The methods to get and set a request still exist
- but are a no-op until removed.
-* Most classes that used to require a `Guzzle\Service\Command\CommandInterface` typehint now request a
- `Guzzle\Service\Command\ArrayCommandInterface`.
-* Added `Guzzle\Http\Message\RequestInterface::startResponse()` to the RequestInterface to handle injecting a response
- on a request while the request is still being transferred
-* `Guzzle\Service\Command\CommandInterface` now extends from ToArrayInterface and ArrayAccess
-
-3.3 to 3.4
-----------
-
-Base URLs of a client now follow the rules of http://tools.ietf.org/html/rfc3986#section-5.2.2 when merging URLs.
-
-3.2 to 3.3
-----------
-
-### Response::getEtag() quote stripping removed
-
-`Guzzle\Http\Message\Response::getEtag()` no longer strips quotes around the ETag response header
-
-### Removed `Guzzle\Http\Utils`
-
-The `Guzzle\Http\Utils` class was removed. This class was only used for testing.
-
-### Stream wrapper and type
-
-`Guzzle\Stream\Stream::getWrapper()` and `Guzzle\Stream\Stream::getStreamType()` are no longer converted to lowercase.
-
-### curl.emit_io became emit_io
-
-Emitting IO events from a RequestMediator is now a parameter that must be set in a request's curl options using the
-'emit_io' key. This was previously set under a request's parameters using 'curl.emit_io'
-
-3.1 to 3.2
-----------
-
-### CurlMulti is no longer reused globally
-
-Before 3.2, the same CurlMulti object was reused globally for each client. This can cause issue where plugins added
-to a single client can pollute requests dispatched from other clients.
-
-If you still wish to reuse the same CurlMulti object with each client, then you can add a listener to the
-ServiceBuilder's `service_builder.create_client` event to inject a custom CurlMulti object into each client as it is
-created.
-
-```php
-$multi = new Guzzle\Http\Curl\CurlMulti();
-$builder = Guzzle\Service\Builder\ServiceBuilder::factory('/path/to/config.json');
-$builder->addListener('service_builder.create_client', function ($event) use ($multi) {
- $event['client']->setCurlMulti($multi);
-}
-});
-```
-
-### No default path
-
-URLs no longer have a default path value of '/' if no path was specified.
-
-Before:
-
-```php
-$request = $client->get('http://www.foo.com');
-echo $request->getUrl();
-// >> http://www.foo.com/
-```
-
-After:
-
-```php
-$request = $client->get('http://www.foo.com');
-echo $request->getUrl();
-// >> http://www.foo.com
-```
-
-### Less verbose BadResponseException
-
-The exception message for `Guzzle\Http\Exception\BadResponseException` no longer contains the full HTTP request and
-response information. You can, however, get access to the request and response object by calling `getRequest()` or
-`getResponse()` on the exception object.
-
-### Query parameter aggregation
-
-Multi-valued query parameters are no longer aggregated using a callback function. `Guzzle\Http\Query` now has a
-setAggregator() method that accepts a `Guzzle\Http\QueryAggregator\QueryAggregatorInterface` object. This object is
-responsible for handling the aggregation of multi-valued query string variables into a flattened hash.
-
-2.8 to 3.x
-----------
-
-### Guzzle\Service\Inspector
-
-Change `\Guzzle\Service\Inspector::fromConfig` to `\Guzzle\Common\Collection::fromConfig`
-
-**Before**
-
-```php
-use Guzzle\Service\Inspector;
-
-class YourClient extends \Guzzle\Service\Client
-{
- public static function factory($config = array())
- {
- $default = array();
- $required = array('base_url', 'username', 'api_key');
- $config = Inspector::fromConfig($config, $default, $required);
-
- $client = new self(
- $config->get('base_url'),
- $config->get('username'),
- $config->get('api_key')
- );
- $client->setConfig($config);
-
- $client->setDescription(ServiceDescription::factory(__DIR__ . DIRECTORY_SEPARATOR . 'client.json'));
-
- return $client;
- }
-```
-
-**After**
-
-```php
-use Guzzle\Common\Collection;
-
-class YourClient extends \Guzzle\Service\Client
-{
- public static function factory($config = array())
- {
- $default = array();
- $required = array('base_url', 'username', 'api_key');
- $config = Collection::fromConfig($config, $default, $required);
-
- $client = new self(
- $config->get('base_url'),
- $config->get('username'),
- $config->get('api_key')
- );
- $client->setConfig($config);
-
- $client->setDescription(ServiceDescription::factory(__DIR__ . DIRECTORY_SEPARATOR . 'client.json'));
-
- return $client;
- }
-```
-
-### Convert XML Service Descriptions to JSON
-
-**Before**
-
-```xml
-<?xml version="1.0" encoding="UTF-8"?>
-<client>
- <commands>
- <!-- Groups -->
- <command name="list_groups" method="GET" uri="groups.json">
- <doc>Get a list of groups</doc>
- </command>
- <command name="search_groups" method="GET" uri='search.json?query="{{query}} type:group"'>
- <doc>Uses a search query to get a list of groups</doc>
- <param name="query" type="string" required="true" />
- </command>
- <command name="create_group" method="POST" uri="groups.json">
- <doc>Create a group</doc>
- <param name="data" type="array" location="body" filters="json_encode" doc="Group JSON"/>
- <param name="Content-Type" location="header" static="application/json"/>
- </command>
- <command name="delete_group" method="DELETE" uri="groups/{{id}}.json">
- <doc>Delete a group by ID</doc>
- <param name="id" type="integer" required="true"/>
- </command>
- <command name="get_group" method="GET" uri="groups/{{id}}.json">
- <param name="id" type="integer" required="true"/>
- </command>
- <command name="update_group" method="PUT" uri="groups/{{id}}.json">
- <doc>Update a group</doc>
- <param name="id" type="integer" required="true"/>
- <param name="data" type="array" location="body" filters="json_encode" doc="Group JSON"/>
- <param name="Content-Type" location="header" static="application/json"/>
- </command>
- </commands>
-</client>
-```
-
-**After**
-
-```json
-{
- "name": "Zendesk REST API v2",
- "apiVersion": "2012-12-31",
- "description":"Provides access to Zendesk views, groups, tickets, ticket fields, and users",
- "operations": {
- "list_groups": {
- "httpMethod":"GET",
- "uri": "groups.json",
- "summary": "Get a list of groups"
- },
- "search_groups":{
- "httpMethod":"GET",
- "uri": "search.json?query=\"{query} type:group\"",
- "summary": "Uses a search query to get a list of groups",
- "parameters":{
- "query":{
- "location": "uri",
- "description":"Zendesk Search Query",
- "type": "string",
- "required": true
- }
- }
- },
- "create_group": {
- "httpMethod":"POST",
- "uri": "groups.json",
- "summary": "Create a group",
- "parameters":{
- "data": {
- "type": "array",
- "location": "body",
- "description":"Group JSON",
- "filters": "json_encode",
- "required": true
- },
- "Content-Type":{
- "type": "string",
- "location":"header",
- "static": "application/json"
- }
- }
- },
- "delete_group": {
- "httpMethod":"DELETE",
- "uri": "groups/{id}.json",
- "summary": "Delete a group",
- "parameters":{
- "id":{
- "location": "uri",
- "description":"Group to delete by ID",
- "type": "integer",
- "required": true
- }
- }
- },
- "get_group": {
- "httpMethod":"GET",
- "uri": "groups/{id}.json",
- "summary": "Get a ticket",
- "parameters":{
- "id":{
- "location": "uri",
- "description":"Group to get by ID",
- "type": "integer",
- "required": true
- }
- }
- },
- "update_group": {
- "httpMethod":"PUT",
- "uri": "groups/{id}.json",
- "summary": "Update a group",
- "parameters":{
- "id": {
- "location": "uri",
- "description":"Group to update by ID",
- "type": "integer",
- "required": true
- },
- "data": {
- "type": "array",
- "location": "body",
- "description":"Group JSON",
- "filters": "json_encode",
- "required": true
- },
- "Content-Type":{
- "type": "string",
- "location":"header",
- "static": "application/json"
- }
- }
- }
-}
-```
-
-### Guzzle\Service\Description\ServiceDescription
-
-Commands are now called Operations
-
-**Before**
-
-```php
-use Guzzle\Service\Description\ServiceDescription;
-
-$sd = new ServiceDescription();
-$sd->getCommands(); // @returns ApiCommandInterface[]
-$sd->hasCommand($name);
-$sd->getCommand($name); // @returns ApiCommandInterface|null
-$sd->addCommand($command); // @param ApiCommandInterface $command
-```
-
-**After**
-
-```php
-use Guzzle\Service\Description\ServiceDescription;
-
-$sd = new ServiceDescription();
-$sd->getOperations(); // @returns OperationInterface[]
-$sd->hasOperation($name);
-$sd->getOperation($name); // @returns OperationInterface|null
-$sd->addOperation($operation); // @param OperationInterface $operation
-```
-
-### Guzzle\Common\Inflection\Inflector
-
-Namespace is now `Guzzle\Inflection\Inflector`
-
-### Guzzle\Http\Plugin
-
-Namespace is now `Guzzle\Plugin`. Many other changes occur within this namespace and are detailed in their own sections below.
-
-### Guzzle\Http\Plugin\LogPlugin and Guzzle\Common\Log
-
-Now `Guzzle\Plugin\Log\LogPlugin` and `Guzzle\Log` respectively.
-
-**Before**
-
-```php
-use Guzzle\Common\Log\ClosureLogAdapter;
-use Guzzle\Http\Plugin\LogPlugin;
-
-/** @var \Guzzle\Http\Client */
-$client;
-
-// $verbosity is an integer indicating desired message verbosity level
-$client->addSubscriber(new LogPlugin(new ClosureLogAdapter(function($m) { echo $m; }, $verbosity = LogPlugin::LOG_VERBOSE);
-```
-
-**After**
-
-```php
-use Guzzle\Log\ClosureLogAdapter;
-use Guzzle\Log\MessageFormatter;
-use Guzzle\Plugin\Log\LogPlugin;
-
-/** @var \Guzzle\Http\Client */
-$client;
-
-// $format is a string indicating desired message format -- @see MessageFormatter
-$client->addSubscriber(new LogPlugin(new ClosureLogAdapter(function($m) { echo $m; }, $format = MessageFormatter::DEBUG_FORMAT);
-```
-
-### Guzzle\Http\Plugin\CurlAuthPlugin
-
-Now `Guzzle\Plugin\CurlAuth\CurlAuthPlugin`.
-
-### Guzzle\Http\Plugin\ExponentialBackoffPlugin
-
-Now `Guzzle\Plugin\Backoff\BackoffPlugin`, and other changes.
-
-**Before**
-
-```php
-use Guzzle\Http\Plugin\ExponentialBackoffPlugin;
-
-$backoffPlugin = new ExponentialBackoffPlugin($maxRetries, array_merge(
- ExponentialBackoffPlugin::getDefaultFailureCodes(), array(429)
- ));
-
-$client->addSubscriber($backoffPlugin);
-```
-
-**After**
-
-```php
-use Guzzle\Plugin\Backoff\BackoffPlugin;
-use Guzzle\Plugin\Backoff\HttpBackoffStrategy;
-
-// Use convenient factory method instead -- see implementation for ideas of what
-// you can do with chaining backoff strategies
-$backoffPlugin = BackoffPlugin::getExponentialBackoff($maxRetries, array_merge(
- HttpBackoffStrategy::getDefaultFailureCodes(), array(429)
- ));
-$client->addSubscriber($backoffPlugin);
-```
-
-### Known Issues
-
-#### [BUG] Accept-Encoding header behavior changed unintentionally.
-
-(See #217) (Fixed in 09daeb8c666fb44499a0646d655a8ae36456575e)
-
-In version 2.8 setting the `Accept-Encoding` header would set the CURLOPT_ENCODING option, which permitted cURL to
-properly handle gzip/deflate compressed responses from the server. In versions affected by this bug this does not happen.
-See issue #217 for a workaround, or use a version containing the fix.
+Guzzle Upgrade Guide
+====================
+
+5.0 to 6.0
+----------
+
+Guzzle now uses [PSR-7](http://www.php-fig.org/psr/psr-7/) for HTTP messages.
+Due to the fact that these messages are immutable, this prompted a refactoring
+of Guzzle to use a middleware based system rather than an event system. Any
+HTTP message interaction (e.g., `GuzzleHttp\Message\Request`) need to be
+updated to work with the new immutable PSR-7 request and response objects. Any
+event listeners or subscribers need to be updated to become middleware
+functions that wrap handlers (or are injected into a
+`GuzzleHttp\HandlerStack`).
+
+- Removed `GuzzleHttp\BatchResults`
+- Removed `GuzzleHttp\Collection`
+- Removed `GuzzleHttp\HasDataTrait`
+- Removed `GuzzleHttp\ToArrayInterface`
+- The `guzzlehttp/streams` dependency has been removed. Stream functionality
+ is now present in the `GuzzleHttp\Psr7` namespace provided by the
+ `guzzlehttp/psr7` package.
+- Guzzle no longer uses ReactPHP promises and now uses the
+ `guzzlehttp/promises` library. We use a custom promise library for three
+ significant reasons:
+ 1. React promises (at the time of writing this) are recursive. Promise
+ chaining and promise resolution will eventually blow the stack. Guzzle
+ promises are not recursive as they use a sort of trampolining technique.
+ Note: there has been movement in the React project to modify promises to
+ no longer utilize recursion.
+ 2. Guzzle needs to have the ability to synchronously block on a promise to
+ wait for a result. Guzzle promises allows this functionality (and does
+ not require the use of recursion).
+ 3. Because we need to be able to wait on a result, doing so using React
+ promises requires wrapping react promises with RingPHP futures. This
+ overhead is no longer needed, reducing stack sizes, reducing complexity,
+ and improving performance.
+- `GuzzleHttp\Mimetypes` has been moved to a function in
+ `GuzzleHttp\Psr7\mimetype_from_extension` and
+ `GuzzleHttp\Psr7\mimetype_from_filename`.
+- `GuzzleHttp\Query` and `GuzzleHttp\QueryParser` have been removed. Query
+ strings must now be passed into request objects as strings, or provided to
+ the `query` request option when creating requests with clients. The `query`
+ option uses PHP's `http_build_query` to convert an array to a string. If you
+ need a different serialization technique, you will need to pass the query
+ string in as a string. There are a couple helper functions that will make
+ working with query strings easier: `GuzzleHttp\Psr7\parse_query` and
+ `GuzzleHttp\Psr7\build_query`.
+- Guzzle no longer has a dependency on RingPHP. Due to the use of a middleware
+ system based on PSR-7, using RingPHP and it's middleware system as well adds
+ more complexity than the benefits it provides. All HTTP handlers that were
+ present in RingPHP have been modified to work directly with PSR-7 messages
+ and placed in the `GuzzleHttp\Handler` namespace. This significantly reduces
+ complexity in Guzzle, removes a dependency, and improves performance. RingPHP
+ will be maintained for Guzzle 5 support, but will no longer be a part of
+ Guzzle 6.
+- As Guzzle now uses a middleware based systems the event system and RingPHP
+ integration has been removed. Note: while the event system has been removed,
+ it is possible to add your own type of event system that is powered by the
+ middleware system.
+ - Removed the `Event` namespace.
+ - Removed the `Subscriber` namespace.
+ - Removed `Transaction` class
+ - Removed `RequestFsm`
+ - Removed `RingBridge`
+ - `GuzzleHttp\Subscriber\Cookie` is now provided by
+ `GuzzleHttp\Middleware::cookies`
+ - `GuzzleHttp\Subscriber\HttpError` is now provided by
+ `GuzzleHttp\Middleware::httpError`
+ - `GuzzleHttp\Subscriber\History` is now provided by
+ `GuzzleHttp\Middleware::history`
+ - `GuzzleHttp\Subscriber\Mock` is now provided by
+ `GuzzleHttp\Handler\MockHandler`
+ - `GuzzleHttp\Subscriber\Prepare` is now provided by
+ `GuzzleHttp\PrepareBodyMiddleware`
+ - `GuzzleHttp\Subscriber\Redirect` is now provided by
+ `GuzzleHttp\RedirectMiddleware`
+- Guzzle now uses `Psr\Http\Message\UriInterface` (implements in
+ `GuzzleHttp\Psr7\Uri`) for URI support. `GuzzleHttp\Url` is now gone.
+- Static functions in `GuzzleHttp\Utils` have been moved to namespaced
+ functions under the `GuzzleHttp` namespace. This requires either a Composer
+ based autoloader or you to include functions.php.
+- `GuzzleHttp\ClientInterface::getDefaultOption` has been renamed to
+ `GuzzleHttp\ClientInterface::getConfig`.
+- `GuzzleHttp\ClientInterface::setDefaultOption` has been removed.
+- The `json` and `xml` methods of response objects has been removed. With the
+ migration to strictly adhering to PSR-7 as the interface for Guzzle messages,
+ adding methods to message interfaces would actually require Guzzle messages
+ to extend from PSR-7 messages rather then work with them directly.
+
+## Migrating to middleware
+
+The change to PSR-7 unfortunately required significant refactoring to Guzzle
+due to the fact that PSR-7 messages are immutable. Guzzle 5 relied on an event
+system from plugins. The event system relied on mutability of HTTP messages and
+side effects in order to work. With immutable messages, you have to change your
+workflow to become more about either returning a value (e.g., functional
+middlewares) or setting a value on an object. Guzzle v6 has chosen the
+functional middleware approach.
+
+Instead of using the event system to listen for things like the `before` event,
+you now create a stack based middleware function that intercepts a request on
+the way in and the promise of the response on the way out. This is a much
+simpler and more predictable approach than the event system and works nicely
+with PSR-7 middleware. Due to the use of promises, the middleware system is
+also asynchronous.
+
+v5:
+
+```php
+use GuzzleHttp\Event\BeforeEvent;
+$client = new GuzzleHttp\Client();
+// Get the emitter and listen to the before event.
+$client->getEmitter()->on('before', function (BeforeEvent $e) {
+ // Guzzle v5 events relied on mutation
+ $e->getRequest()->setHeader('X-Foo', 'Bar');
+});
+```
+
+v6:
+
+In v6, you can modify the request before it is sent using the `mapRequest`
+middleware. The idiomatic way in v6 to modify the request/response lifecycle is
+to setup a handler middleware stack up front and inject the handler into a
+client.
+
+```php
+use GuzzleHttp\Middleware;
+// Create a handler stack that has all of the default middlewares attached
+$handler = GuzzleHttp\HandlerStack::create();
+// Push the handler onto the handler stack
+$handler->push(Middleware::mapRequest(function (RequestInterface $request) {
+ // Notice that we have to return a request object
+ return $request->withHeader('X-Foo', 'Bar');
+}));
+// Inject the handler into the client
+$client = new GuzzleHttp\Client(['handler' => $handler]);
+```
+
+## POST Requests
+
+This version added the [`form_params`](http://guzzle.readthedocs.org/en/latest/request-options.html#form_params)
+and `multipart` request options. `form_params` is an associative array of
+strings or array of strings and is used to serialize an
+`application/x-www-form-urlencoded` POST request. The
+[`multipart`](http://guzzle.readthedocs.org/en/latest/request-options.html#multipart)
+option is now used to send a multipart/form-data POST request.
+
+`GuzzleHttp\Post\PostFile` has been removed. Use the `multipart` option to add
+POST files to a multipart/form-data request.
+
+The `body` option no longer accepts an array to send POST requests. Please use
+`multipart` or `form_params` instead.
+
+The `base_url` option has been renamed to `base_uri`.
+
+4.x to 5.0
+----------
+
+## Rewritten Adapter Layer
+
+Guzzle now uses [RingPHP](http://ringphp.readthedocs.org/en/latest) to send
+HTTP requests. The `adapter` option in a `GuzzleHttp\Client` constructor
+is still supported, but it has now been renamed to `handler`. Instead of
+passing a `GuzzleHttp\Adapter\AdapterInterface`, you must now pass a PHP
+`callable` that follows the RingPHP specification.
+
+## Removed Fluent Interfaces
+
+[Fluent interfaces were removed](http://ocramius.github.io/blog/fluent-interfaces-are-evil)
+from the following classes:
+
+- `GuzzleHttp\Collection`
+- `GuzzleHttp\Url`
+- `GuzzleHttp\Query`
+- `GuzzleHttp\Post\PostBody`
+- `GuzzleHttp\Cookie\SetCookie`
+
+## Removed functions.php
+
+Removed "functions.php", so that Guzzle is truly PSR-4 compliant. The following
+functions can be used as replacements.
+
+- `GuzzleHttp\json_decode` -> `GuzzleHttp\Utils::jsonDecode`
+- `GuzzleHttp\get_path` -> `GuzzleHttp\Utils::getPath`
+- `GuzzleHttp\Utils::setPath` -> `GuzzleHttp\set_path`
+- `GuzzleHttp\Pool::batch` -> `GuzzleHttp\batch`. This function is, however,
+ deprecated in favor of using `GuzzleHttp\Pool::batch()`.
+
+The "procedural" global client has been removed with no replacement (e.g.,
+`GuzzleHttp\get()`, `GuzzleHttp\post()`, etc.). Use a `GuzzleHttp\Client`
+object as a replacement.
+
+## `throwImmediately` has been removed
+
+The concept of "throwImmediately" has been removed from exceptions and error
+events. This control mechanism was used to stop a transfer of concurrent
+requests from completing. This can now be handled by throwing the exception or
+by cancelling a pool of requests or each outstanding future request
+individually.
+
+## headers event has been removed
+
+Removed the "headers" event. This event was only useful for changing the
+body a response once the headers of the response were known. You can implement
+a similar behavior in a number of ways. One example might be to use a
+FnStream that has access to the transaction being sent. For example, when the
+first byte is written, you could check if the response headers match your
+expectations, and if so, change the actual stream body that is being
+written to.
+
+## Updates to HTTP Messages
+
+Removed the `asArray` parameter from
+`GuzzleHttp\Message\MessageInterface::getHeader`. If you want to get a header
+value as an array, then use the newly added `getHeaderAsArray()` method of
+`MessageInterface`. This change makes the Guzzle interfaces compatible with
+the PSR-7 interfaces.
+
+3.x to 4.0
+----------
+
+## Overarching changes:
+
+- Now requires PHP 5.4 or greater.
+- No longer requires cURL to send requests.
+- Guzzle no longer wraps every exception it throws. Only exceptions that are
+ recoverable are now wrapped by Guzzle.
+- Various namespaces have been removed or renamed.
+- No longer requiring the Symfony EventDispatcher. A custom event dispatcher
+ based on the Symfony EventDispatcher is
+ now utilized in `GuzzleHttp\Event\EmitterInterface` (resulting in significant
+ speed and functionality improvements).
+
+Changes per Guzzle 3.x namespace are described below.
+
+## Batch
+
+The `Guzzle\Batch` namespace has been removed. This is best left to
+third-parties to implement on top of Guzzle's core HTTP library.
+
+## Cache
+
+The `Guzzle\Cache` namespace has been removed. (Todo: No suitable replacement
+has been implemented yet, but hoping to utilize a PSR cache interface).
+
+## Common
+
+- Removed all of the wrapped exceptions. It's better to use the standard PHP
+ library for unrecoverable exceptions.
+- `FromConfigInterface` has been removed.
+- `Guzzle\Common\Version` has been removed. The VERSION constant can be found
+ at `GuzzleHttp\ClientInterface::VERSION`.
+
+### Collection
+
+- `getAll` has been removed. Use `toArray` to convert a collection to an array.
+- `inject` has been removed.
+- `keySearch` has been removed.
+- `getPath` no longer supports wildcard expressions. Use something better like
+ JMESPath for this.
+- `setPath` now supports appending to an existing array via the `[]` notation.
+
+### Events
+
+Guzzle no longer requires Symfony's EventDispatcher component. Guzzle now uses
+`GuzzleHttp\Event\Emitter`.
+
+- `Symfony\Component\EventDispatcher\EventDispatcherInterface` is replaced by
+ `GuzzleHttp\Event\EmitterInterface`.
+- `Symfony\Component\EventDispatcher\EventDispatcher` is replaced by
+ `GuzzleHttp\Event\Emitter`.
+- `Symfony\Component\EventDispatcher\Event` is replaced by
+ `GuzzleHttp\Event\Event`, and Guzzle now has an EventInterface in
+ `GuzzleHttp\Event\EventInterface`.
+- `AbstractHasDispatcher` has moved to a trait, `HasEmitterTrait`, and
+ `HasDispatcherInterface` has moved to `HasEmitterInterface`. Retrieving the
+ event emitter of a request, client, etc. now uses the `getEmitter` method
+ rather than the `getDispatcher` method.
+
+#### Emitter
+
+- Use the `once()` method to add a listener that automatically removes itself
+ the first time it is invoked.
+- Use the `listeners()` method to retrieve a list of event listeners rather than
+ the `getListeners()` method.
+- Use `emit()` instead of `dispatch()` to emit an event from an emitter.
+- Use `attach()` instead of `addSubscriber()` and `detach()` instead of
+ `removeSubscriber()`.
+
+```php
+$mock = new Mock();
+// 3.x
+$request->getEventDispatcher()->addSubscriber($mock);
+$request->getEventDispatcher()->removeSubscriber($mock);
+// 4.x
+$request->getEmitter()->attach($mock);
+$request->getEmitter()->detach($mock);
+```
+
+Use the `on()` method to add a listener rather than the `addListener()` method.
+
+```php
+// 3.x
+$request->getEventDispatcher()->addListener('foo', function (Event $event) { /* ... */ } );
+// 4.x
+$request->getEmitter()->on('foo', function (Event $event, $name) { /* ... */ } );
+```
+
+## Http
+
+### General changes
+
+- The cacert.pem certificate has been moved to `src/cacert.pem`.
+- Added the concept of adapters that are used to transfer requests over the
+ wire.
+- Simplified the event system.
+- Sending requests in parallel is still possible, but batching is no longer a
+ concept of the HTTP layer. Instead, you must use the `complete` and `error`
+ events to asynchronously manage parallel request transfers.
+- `Guzzle\Http\Url` has moved to `GuzzleHttp\Url`.
+- `Guzzle\Http\QueryString` has moved to `GuzzleHttp\Query`.
+- QueryAggregators have been rewritten so that they are simply callable
+ functions.
+- `GuzzleHttp\StaticClient` has been removed. Use the functions provided in
+ `functions.php` for an easy to use static client instance.
+- Exceptions in `GuzzleHttp\Exception` have been updated to all extend from
+ `GuzzleHttp\Exception\TransferException`.
+
+### Client
+
+Calling methods like `get()`, `post()`, `head()`, etc. no longer create and
+return a request, but rather creates a request, sends the request, and returns
+the response.
+
+```php
+// 3.0
+$request = $client->get('/');
+$response = $request->send();
+
+// 4.0
+$response = $client->get('/');
+
+// or, to mirror the previous behavior
+$request = $client->createRequest('GET', '/');
+$response = $client->send($request);
+```
+
+`GuzzleHttp\ClientInterface` has changed.
+
+- The `send` method no longer accepts more than one request. Use `sendAll` to
+ send multiple requests in parallel.
+- `setUserAgent()` has been removed. Use a default request option instead. You
+ could, for example, do something like:
+ `$client->setConfig('defaults/headers/User-Agent', 'Foo/Bar ' . $client::getDefaultUserAgent())`.
+- `setSslVerification()` has been removed. Use default request options instead,
+ like `$client->setConfig('defaults/verify', true)`.
+
+`GuzzleHttp\Client` has changed.
+
+- The constructor now accepts only an associative array. You can include a
+ `base_url` string or array to use a URI template as the base URL of a client.
+ You can also specify a `defaults` key that is an associative array of default
+ request options. You can pass an `adapter` to use a custom adapter,
+ `batch_adapter` to use a custom adapter for sending requests in parallel, or
+ a `message_factory` to change the factory used to create HTTP requests and
+ responses.
+- The client no longer emits a `client.create_request` event.
+- Creating requests with a client no longer automatically utilize a URI
+ template. You must pass an array into a creational method (e.g.,
+ `createRequest`, `get`, `put`, etc.) in order to expand a URI template.
+
+### Messages
+
+Messages no longer have references to their counterparts (i.e., a request no
+longer has a reference to it's response, and a response no loger has a
+reference to its request). This association is now managed through a
+`GuzzleHttp\Adapter\TransactionInterface` object. You can get references to
+these transaction objects using request events that are emitted over the
+lifecycle of a request.
+
+#### Requests with a body
+
+- `GuzzleHttp\Message\EntityEnclosingRequest` and
+ `GuzzleHttp\Message\EntityEnclosingRequestInterface` have been removed. The
+ separation between requests that contain a body and requests that do not
+ contain a body has been removed, and now `GuzzleHttp\Message\RequestInterface`
+ handles both use cases.
+- Any method that previously accepts a `GuzzleHttp\Response` object now accept a
+ `GuzzleHttp\Message\ResponseInterface`.
+- `GuzzleHttp\Message\RequestFactoryInterface` has been renamed to
+ `GuzzleHttp\Message\MessageFactoryInterface`. This interface is used to create
+ both requests and responses and is implemented in
+ `GuzzleHttp\Message\MessageFactory`.
+- POST field and file methods have been removed from the request object. You
+ must now use the methods made available to `GuzzleHttp\Post\PostBodyInterface`
+ to control the format of a POST body. Requests that are created using a
+ standard `GuzzleHttp\Message\MessageFactoryInterface` will automatically use
+ a `GuzzleHttp\Post\PostBody` body if the body was passed as an array or if
+ the method is POST and no body is provided.
+
+```php
+$request = $client->createRequest('POST', '/');
+$request->getBody()->setField('foo', 'bar');
+$request->getBody()->addFile(new PostFile('file_key', fopen('/path/to/content', 'r')));
+```
+
+#### Headers
+
+- `GuzzleHttp\Message\Header` has been removed. Header values are now simply
+ represented by an array of values or as a string. Header values are returned
+ as a string by default when retrieving a header value from a message. You can
+ pass an optional argument of `true` to retrieve a header value as an array
+ of strings instead of a single concatenated string.
+- `GuzzleHttp\PostFile` and `GuzzleHttp\PostFileInterface` have been moved to
+ `GuzzleHttp\Post`. This interface has been simplified and now allows the
+ addition of arbitrary headers.
+- Custom headers like `GuzzleHttp\Message\Header\Link` have been removed. Most
+ of the custom headers are now handled separately in specific
+ subscribers/plugins, and `GuzzleHttp\Message\HeaderValues::parseParams()` has
+ been updated to properly handle headers that contain parameters (like the
+ `Link` header).
+
+#### Responses
+
+- `GuzzleHttp\Message\Response::getInfo()` and
+ `GuzzleHttp\Message\Response::setInfo()` have been removed. Use the event
+ system to retrieve this type of information.
+- `GuzzleHttp\Message\Response::getRawHeaders()` has been removed.
+- `GuzzleHttp\Message\Response::getMessage()` has been removed.
+- `GuzzleHttp\Message\Response::calculateAge()` and other cache specific
+ methods have moved to the CacheSubscriber.
+- Header specific helper functions like `getContentMd5()` have been removed.
+ Just use `getHeader('Content-MD5')` instead.
+- `GuzzleHttp\Message\Response::setRequest()` and
+ `GuzzleHttp\Message\Response::getRequest()` have been removed. Use the event
+ system to work with request and response objects as a transaction.
+- `GuzzleHttp\Message\Response::getRedirectCount()` has been removed. Use the
+ Redirect subscriber instead.
+- `GuzzleHttp\Message\Response::isSuccessful()` and other related methods have
+ been removed. Use `getStatusCode()` instead.
+
+#### Streaming responses
+
+Streaming requests can now be created by a client directly, returning a
+`GuzzleHttp\Message\ResponseInterface` object that contains a body stream
+referencing an open PHP HTTP stream.
+
+```php
+// 3.0
+use Guzzle\Stream\PhpStreamRequestFactory;
+$request = $client->get('/');
+$factory = new PhpStreamRequestFactory();
+$stream = $factory->fromRequest($request);
+$data = $stream->read(1024);
+
+// 4.0
+$response = $client->get('/', ['stream' => true]);
+// Read some data off of the stream in the response body
+$data = $response->getBody()->read(1024);
+```
+
+#### Redirects
+
+The `configureRedirects()` method has been removed in favor of a
+`allow_redirects` request option.
+
+```php
+// Standard redirects with a default of a max of 5 redirects
+$request = $client->createRequest('GET', '/', ['allow_redirects' => true]);
+
+// Strict redirects with a custom number of redirects
+$request = $client->createRequest('GET', '/', [
+ 'allow_redirects' => ['max' => 5, 'strict' => true]
+]);
+```
+
+#### EntityBody
+
+EntityBody interfaces and classes have been removed or moved to
+`GuzzleHttp\Stream`. All classes and interfaces that once required
+`GuzzleHttp\EntityBodyInterface` now require
+`GuzzleHttp\Stream\StreamInterface`. Creating a new body for a request no
+longer uses `GuzzleHttp\EntityBody::factory` but now uses
+`GuzzleHttp\Stream\Stream::factory` or even better:
+`GuzzleHttp\Stream\create()`.
+
+- `Guzzle\Http\EntityBodyInterface` is now `GuzzleHttp\Stream\StreamInterface`
+- `Guzzle\Http\EntityBody` is now `GuzzleHttp\Stream\Stream`
+- `Guzzle\Http\CachingEntityBody` is now `GuzzleHttp\Stream\CachingStream`
+- `Guzzle\Http\ReadLimitEntityBody` is now `GuzzleHttp\Stream\LimitStream`
+- `Guzzle\Http\IoEmittyinEntityBody` has been removed.
+
+#### Request lifecycle events
+
+Requests previously submitted a large number of requests. The number of events
+emitted over the lifecycle of a request has been significantly reduced to make
+it easier to understand how to extend the behavior of a request. All events
+emitted during the lifecycle of a request now emit a custom
+`GuzzleHttp\Event\EventInterface` object that contains context providing
+methods and a way in which to modify the transaction at that specific point in
+time (e.g., intercept the request and set a response on the transaction).
+
+- `request.before_send` has been renamed to `before` and now emits a
+ `GuzzleHttp\Event\BeforeEvent`
+- `request.complete` has been renamed to `complete` and now emits a
+ `GuzzleHttp\Event\CompleteEvent`.
+- `request.sent` has been removed. Use `complete`.
+- `request.success` has been removed. Use `complete`.
+- `error` is now an event that emits a `GuzzleHttp\Event\ErrorEvent`.
+- `request.exception` has been removed. Use `error`.
+- `request.receive.status_line` has been removed.
+- `curl.callback.progress` has been removed. Use a custom `StreamInterface` to
+ maintain a status update.
+- `curl.callback.write` has been removed. Use a custom `StreamInterface` to
+ intercept writes.
+- `curl.callback.read` has been removed. Use a custom `StreamInterface` to
+ intercept reads.
+
+`headers` is a new event that is emitted after the response headers of a
+request have been received before the body of the response is downloaded. This
+event emits a `GuzzleHttp\Event\HeadersEvent`.
+
+You can intercept a request and inject a response using the `intercept()` event
+of a `GuzzleHttp\Event\BeforeEvent`, `GuzzleHttp\Event\CompleteEvent`, and
+`GuzzleHttp\Event\ErrorEvent` event.
+
+See: http://docs.guzzlephp.org/en/latest/events.html
+
+## Inflection
+
+The `Guzzle\Inflection` namespace has been removed. This is not a core concern
+of Guzzle.
+
+## Iterator
+
+The `Guzzle\Iterator` namespace has been removed.
+
+- `Guzzle\Iterator\AppendIterator`, `Guzzle\Iterator\ChunkedIterator`, and
+ `Guzzle\Iterator\MethodProxyIterator` are nice, but not a core requirement of
+ Guzzle itself.
+- `Guzzle\Iterator\FilterIterator` is no longer needed because an equivalent
+ class is shipped with PHP 5.4.
+- `Guzzle\Iterator\MapIterator` is not really needed when using PHP 5.5 because
+ it's easier to just wrap an iterator in a generator that maps values.
+
+For a replacement of these iterators, see https://github.com/nikic/iter
+
+## Log
+
+The LogPlugin has moved to https://github.com/guzzle/log-subscriber. The
+`Guzzle\Log` namespace has been removed. Guzzle now relies on
+`Psr\Log\LoggerInterface` for all logging. The MessageFormatter class has been
+moved to `GuzzleHttp\Subscriber\Log\Formatter`.
+
+## Parser
+
+The `Guzzle\Parser` namespace has been removed. This was previously used to
+make it possible to plug in custom parsers for cookies, messages, URI
+templates, and URLs; however, this level of complexity is not needed in Guzzle
+so it has been removed.
+
+- Cookie: Cookie parsing logic has been moved to
+ `GuzzleHttp\Cookie\SetCookie::fromString`.
+- Message: Message parsing logic for both requests and responses has been moved
+ to `GuzzleHttp\Message\MessageFactory::fromMessage`. Message parsing is only
+ used in debugging or deserializing messages, so it doesn't make sense for
+ Guzzle as a library to add this level of complexity to parsing messages.
+- UriTemplate: URI template parsing has been moved to
+ `GuzzleHttp\UriTemplate`. The Guzzle library will automatically use the PECL
+ URI template library if it is installed.
+- Url: URL parsing is now performed in `GuzzleHttp\Url::fromString` (previously
+ it was `Guzzle\Http\Url::factory()`). If custom URL parsing is necessary,
+ then developers are free to subclass `GuzzleHttp\Url`.
+
+## Plugin
+
+The `Guzzle\Plugin` namespace has been renamed to `GuzzleHttp\Subscriber`.
+Several plugins are shipping with the core Guzzle library under this namespace.
+
+- `GuzzleHttp\Subscriber\Cookie`: Replaces the old CookiePlugin. Cookie jar
+ code has moved to `GuzzleHttp\Cookie`.
+- `GuzzleHttp\Subscriber\History`: Replaces the old HistoryPlugin.
+- `GuzzleHttp\Subscriber\HttpError`: Throws errors when a bad HTTP response is
+ received.
+- `GuzzleHttp\Subscriber\Mock`: Replaces the old MockPlugin.
+- `GuzzleHttp\Subscriber\Prepare`: Prepares the body of a request just before
+ sending. This subscriber is attached to all requests by default.
+- `GuzzleHttp\Subscriber\Redirect`: Replaces the RedirectPlugin.
+
+The following plugins have been removed (third-parties are free to re-implement
+these if needed):
+
+- `GuzzleHttp\Plugin\Async` has been removed.
+- `GuzzleHttp\Plugin\CurlAuth` has been removed.
+- `GuzzleHttp\Plugin\ErrorResponse\ErrorResponsePlugin` has been removed. This
+ functionality should instead be implemented with event listeners that occur
+ after normal response parsing occurs in the guzzle/command package.
+
+The following plugins are not part of the core Guzzle package, but are provided
+in separate repositories:
+
+- `Guzzle\Http\Plugin\BackoffPlugin` has been rewritten to be much simpler
+ to build custom retry policies using simple functions rather than various
+ chained classes. See: https://github.com/guzzle/retry-subscriber
+- `Guzzle\Http\Plugin\Cache\CachePlugin` has moved to
+ https://github.com/guzzle/cache-subscriber
+- `Guzzle\Http\Plugin\Log\LogPlugin` has moved to
+ https://github.com/guzzle/log-subscriber
+- `Guzzle\Http\Plugin\Md5\Md5Plugin` has moved to
+ https://github.com/guzzle/message-integrity-subscriber
+- `Guzzle\Http\Plugin\Mock\MockPlugin` has moved to
+ `GuzzleHttp\Subscriber\MockSubscriber`.
+- `Guzzle\Http\Plugin\Oauth\OauthPlugin` has moved to
+ https://github.com/guzzle/oauth-subscriber
+
+## Service
+
+The service description layer of Guzzle has moved into two separate packages:
+
+- http://github.com/guzzle/command Provides a high level abstraction over web
+ services by representing web service operations using commands.
+- http://github.com/guzzle/guzzle-services Provides an implementation of
+ guzzle/command that provides request serialization and response parsing using
+ Guzzle service descriptions.
+
+## Stream
+
+Stream have moved to a separate package available at
+https://github.com/guzzle/streams.
+
+`Guzzle\Stream\StreamInterface` has been given a large update to cleanly take
+on the responsibilities of `Guzzle\Http\EntityBody` and
+`Guzzle\Http\EntityBodyInterface` now that they have been removed. The number
+of methods implemented by the `StreamInterface` has been drastically reduced to
+allow developers to more easily extend and decorate stream behavior.
+
+## Removed methods from StreamInterface
+
+- `getStream` and `setStream` have been removed to better encapsulate streams.
+- `getMetadata` and `setMetadata` have been removed in favor of
+ `GuzzleHttp\Stream\MetadataStreamInterface`.
+- `getWrapper`, `getWrapperData`, `getStreamType`, and `getUri` have all been
+ removed. This data is accessible when
+ using streams that implement `GuzzleHttp\Stream\MetadataStreamInterface`.
+- `rewind` has been removed. Use `seek(0)` for a similar behavior.
+
+## Renamed methods
+
+- `detachStream` has been renamed to `detach`.
+- `feof` has been renamed to `eof`.
+- `ftell` has been renamed to `tell`.
+- `readLine` has moved from an instance method to a static class method of
+ `GuzzleHttp\Stream\Stream`.
+
+## Metadata streams
+
+`GuzzleHttp\Stream\MetadataStreamInterface` has been added to denote streams
+that contain additional metadata accessible via `getMetadata()`.
+`GuzzleHttp\Stream\StreamInterface::getMetadata` and
+`GuzzleHttp\Stream\StreamInterface::setMetadata` have been removed.
+
+## StreamRequestFactory
+
+The entire concept of the StreamRequestFactory has been removed. The way this
+was used in Guzzle 3 broke the actual interface of sending streaming requests
+(instead of getting back a Response, you got a StreamInterface). Streaming
+PHP requests are now implemented through the `GuzzleHttp\Adapter\StreamAdapter`.
+
+3.6 to 3.7
+----------
+
+### Deprecations
+
+- You can now enable E_USER_DEPRECATED warnings to see if you are using any deprecated methods.:
+
+```php
+\Guzzle\Common\Version::$emitWarnings = true;
+```
+
+The following APIs and options have been marked as deprecated:
+
+- Marked `Guzzle\Http\Message\Request::isResponseBodyRepeatable()` as deprecated. Use `$request->getResponseBody()->isRepeatable()` instead.
+- Marked `Guzzle\Http\Message\Request::canCache()` as deprecated. Use `Guzzle\Plugin\Cache\DefaultCanCacheStrategy->canCacheRequest()` instead.
+- Marked `Guzzle\Http\Message\Request::canCache()` as deprecated. Use `Guzzle\Plugin\Cache\DefaultCanCacheStrategy->canCacheRequest()` instead.
+- Marked `Guzzle\Http\Message\Request::setIsRedirect()` as deprecated. Use the HistoryPlugin instead.
+- Marked `Guzzle\Http\Message\Request::isRedirect()` as deprecated. Use the HistoryPlugin instead.
+- Marked `Guzzle\Cache\CacheAdapterFactory::factory()` as deprecated
+- Marked `Guzzle\Service\Client::enableMagicMethods()` as deprecated. Magic methods can no longer be disabled on a Guzzle\Service\Client.
+- Marked `Guzzle\Parser\Url\UrlParser` as deprecated. Just use PHP's `parse_url()` and percent encode your UTF-8.
+- Marked `Guzzle\Common\Collection::inject()` as deprecated.
+- Marked `Guzzle\Plugin\CurlAuth\CurlAuthPlugin` as deprecated. Use
+ `$client->getConfig()->setPath('request.options/auth', array('user', 'pass', 'Basic|Digest|NTLM|Any'));` or
+ `$client->setDefaultOption('auth', array('user', 'pass', 'Basic|Digest|NTLM|Any'));`
+
+3.7 introduces `request.options` as a parameter for a client configuration and as an optional argument to all creational
+request methods. When paired with a client's configuration settings, these options allow you to specify default settings
+for various aspects of a request. Because these options make other previous configuration options redundant, several
+configuration options and methods of a client and AbstractCommand have been deprecated.
+
+- Marked `Guzzle\Service\Client::getDefaultHeaders()` as deprecated. Use `$client->getDefaultOption('headers')`.
+- Marked `Guzzle\Service\Client::setDefaultHeaders()` as deprecated. Use `$client->setDefaultOption('headers/{header_name}', 'value')`.
+- Marked 'request.params' for `Guzzle\Http\Client` as deprecated. Use `$client->setDefaultOption('params/{param_name}', 'value')`
+- Marked 'command.headers', 'command.response_body' and 'command.on_complete' as deprecated for AbstractCommand. These will work through Guzzle 4.0
+
+ $command = $client->getCommand('foo', array(
+ 'command.headers' => array('Test' => '123'),
+ 'command.response_body' => '/path/to/file'
+ ));
+
+ // Should be changed to:
+
+ $command = $client->getCommand('foo', array(
+ 'command.request_options' => array(
+ 'headers' => array('Test' => '123'),
+ 'save_as' => '/path/to/file'
+ )
+ ));
+
+### Interface changes
+
+Additions and changes (you will need to update any implementations or subclasses you may have created):
+
+- Added an `$options` argument to the end of the following methods of `Guzzle\Http\ClientInterface`:
+ createRequest, head, delete, put, patch, post, options, prepareRequest
+- Added an `$options` argument to the end of `Guzzle\Http\Message\Request\RequestFactoryInterface::createRequest()`
+- Added an `applyOptions()` method to `Guzzle\Http\Message\Request\RequestFactoryInterface`
+- Changed `Guzzle\Http\ClientInterface::get($uri = null, $headers = null, $body = null)` to
+ `Guzzle\Http\ClientInterface::get($uri = null, $headers = null, $options = array())`. You can still pass in a
+ resource, string, or EntityBody into the $options parameter to specify the download location of the response.
+- Changed `Guzzle\Common\Collection::__construct($data)` to no longer accepts a null value for `$data` but a
+ default `array()`
+- Added `Guzzle\Stream\StreamInterface::isRepeatable`
+- Made `Guzzle\Http\Client::expandTemplate` and `getUriTemplate` protected methods.
+
+The following methods were removed from interfaces. All of these methods are still available in the concrete classes
+that implement them, but you should update your code to use alternative methods:
+
+- Removed `Guzzle\Http\ClientInterface::setDefaultHeaders(). Use
+ `$client->getConfig()->setPath('request.options/headers/{header_name}', 'value')`. or
+ `$client->getConfig()->setPath('request.options/headers', array('header_name' => 'value'))` or
+ `$client->setDefaultOption('headers/{header_name}', 'value')`. or
+ `$client->setDefaultOption('headers', array('header_name' => 'value'))`.
+- Removed `Guzzle\Http\ClientInterface::getDefaultHeaders(). Use `$client->getConfig()->getPath('request.options/headers')`.
+- Removed `Guzzle\Http\ClientInterface::expandTemplate()`. This is an implementation detail.
+- Removed `Guzzle\Http\ClientInterface::setRequestFactory()`. This is an implementation detail.
+- Removed `Guzzle\Http\ClientInterface::getCurlMulti()`. This is a very specific implementation detail.
+- Removed `Guzzle\Http\Message\RequestInterface::canCache`. Use the CachePlugin.
+- Removed `Guzzle\Http\Message\RequestInterface::setIsRedirect`. Use the HistoryPlugin.
+- Removed `Guzzle\Http\Message\RequestInterface::isRedirect`. Use the HistoryPlugin.
+
+### Cache plugin breaking changes
+
+- CacheKeyProviderInterface and DefaultCacheKeyProvider are no longer used. All of this logic is handled in a
+ CacheStorageInterface. These two objects and interface will be removed in a future version.
+- Always setting X-cache headers on cached responses
+- Default cache TTLs are now handled by the CacheStorageInterface of a CachePlugin
+- `CacheStorageInterface::cache($key, Response $response, $ttl = null)` has changed to `cache(RequestInterface
+ $request, Response $response);`
+- `CacheStorageInterface::fetch($key)` has changed to `fetch(RequestInterface $request);`
+- `CacheStorageInterface::delete($key)` has changed to `delete(RequestInterface $request);`
+- Added `CacheStorageInterface::purge($url)`
+- `DefaultRevalidation::__construct(CacheKeyProviderInterface $cacheKey, CacheStorageInterface $cache, CachePlugin
+ $plugin)` has changed to `DefaultRevalidation::__construct(CacheStorageInterface $cache,
+ CanCacheStrategyInterface $canCache = null)`
+- Added `RevalidationInterface::shouldRevalidate(RequestInterface $request, Response $response)`
+
+3.5 to 3.6
+----------
+
+* Mixed casing of headers are now forced to be a single consistent casing across all values for that header.
+* Messages internally use a HeaderCollection object to delegate handling case-insensitive header resolution
+* Removed the whole changedHeader() function system of messages because all header changes now go through addHeader().
+ For example, setHeader() first removes the header using unset on a HeaderCollection and then calls addHeader().
+ Keeping the Host header and URL host in sync is now handled by overriding the addHeader method in Request.
+* Specific header implementations can be created for complex headers. When a message creates a header, it uses a
+ HeaderFactory which can map specific headers to specific header classes. There is now a Link header and
+ CacheControl header implementation.
+* Moved getLinks() from Response to just be used on a Link header object.
+
+If you previously relied on Guzzle\Http\Message\Header::raw(), then you will need to update your code to use the
+HeaderInterface (e.g. toArray(), getAll(), etc.).
+
+### Interface changes
+
+* Removed from interface: Guzzle\Http\ClientInterface::setUriTemplate
+* Removed from interface: Guzzle\Http\ClientInterface::setCurlMulti()
+* Removed Guzzle\Http\Message\Request::receivedRequestHeader() and implemented this functionality in
+ Guzzle\Http\Curl\RequestMediator
+* Removed the optional $asString parameter from MessageInterface::getHeader(). Just cast the header to a string.
+* Removed the optional $tryChunkedTransfer option from Guzzle\Http\Message\EntityEnclosingRequestInterface
+* Removed the $asObjects argument from Guzzle\Http\Message\MessageInterface::getHeaders()
+
+### Removed deprecated functions
+
+* Removed Guzzle\Parser\ParserRegister::get(). Use getParser()
+* Removed Guzzle\Parser\ParserRegister::set(). Use registerParser().
+
+### Deprecations
+
+* The ability to case-insensitively search for header values
+* Guzzle\Http\Message\Header::hasExactHeader
+* Guzzle\Http\Message\Header::raw. Use getAll()
+* Deprecated cache control specific methods on Guzzle\Http\Message\AbstractMessage. Use the CacheControl header object
+ instead.
+
+### Other changes
+
+* All response header helper functions return a string rather than mixing Header objects and strings inconsistently
+* Removed cURL blacklist support. This is no longer necessary now that Expect, Accept, etc. are managed by Guzzle
+ directly via interfaces
+* Removed the injecting of a request object onto a response object. The methods to get and set a request still exist
+ but are a no-op until removed.
+* Most classes that used to require a `Guzzle\Service\Command\CommandInterface` typehint now request a
+ `Guzzle\Service\Command\ArrayCommandInterface`.
+* Added `Guzzle\Http\Message\RequestInterface::startResponse()` to the RequestInterface to handle injecting a response
+ on a request while the request is still being transferred
+* `Guzzle\Service\Command\CommandInterface` now extends from ToArrayInterface and ArrayAccess
+
+3.3 to 3.4
+----------
+
+Base URLs of a client now follow the rules of http://tools.ietf.org/html/rfc3986#section-5.2.2 when merging URLs.
+
+3.2 to 3.3
+----------
+
+### Response::getEtag() quote stripping removed
+
+`Guzzle\Http\Message\Response::getEtag()` no longer strips quotes around the ETag response header
+
+### Removed `Guzzle\Http\Utils`
+
+The `Guzzle\Http\Utils` class was removed. This class was only used for testing.
+
+### Stream wrapper and type
+
+`Guzzle\Stream\Stream::getWrapper()` and `Guzzle\Stream\Stream::getStreamType()` are no longer converted to lowercase.
+
+### curl.emit_io became emit_io
+
+Emitting IO events from a RequestMediator is now a parameter that must be set in a request's curl options using the
+'emit_io' key. This was previously set under a request's parameters using 'curl.emit_io'
+
+3.1 to 3.2
+----------
+
+### CurlMulti is no longer reused globally
+
+Before 3.2, the same CurlMulti object was reused globally for each client. This can cause issue where plugins added
+to a single client can pollute requests dispatched from other clients.
+
+If you still wish to reuse the same CurlMulti object with each client, then you can add a listener to the
+ServiceBuilder's `service_builder.create_client` event to inject a custom CurlMulti object into each client as it is
+created.
+
+```php
+$multi = new Guzzle\Http\Curl\CurlMulti();
+$builder = Guzzle\Service\Builder\ServiceBuilder::factory('/path/to/config.json');
+$builder->addListener('service_builder.create_client', function ($event) use ($multi) {
+ $event['client']->setCurlMulti($multi);
+}
+});
+```
+
+### No default path
+
+URLs no longer have a default path value of '/' if no path was specified.
+
+Before:
+
+```php
+$request = $client->get('http://www.foo.com');
+echo $request->getUrl();
+// >> http://www.foo.com/
+```
+
+After:
+
+```php
+$request = $client->get('http://www.foo.com');
+echo $request->getUrl();
+// >> http://www.foo.com
+```
+
+### Less verbose BadResponseException
+
+The exception message for `Guzzle\Http\Exception\BadResponseException` no longer contains the full HTTP request and
+response information. You can, however, get access to the request and response object by calling `getRequest()` or
+`getResponse()` on the exception object.
+
+### Query parameter aggregation
+
+Multi-valued query parameters are no longer aggregated using a callback function. `Guzzle\Http\Query` now has a
+setAggregator() method that accepts a `Guzzle\Http\QueryAggregator\QueryAggregatorInterface` object. This object is
+responsible for handling the aggregation of multi-valued query string variables into a flattened hash.
+
+2.8 to 3.x
+----------
+
+### Guzzle\Service\Inspector
+
+Change `\Guzzle\Service\Inspector::fromConfig` to `\Guzzle\Common\Collection::fromConfig`
+
+**Before**
+
+```php
+use Guzzle\Service\Inspector;
+
+class YourClient extends \Guzzle\Service\Client
+{
+ public static function factory($config = array())
+ {
+ $default = array();
+ $required = array('base_url', 'username', 'api_key');
+ $config = Inspector::fromConfig($config, $default, $required);
+
+ $client = new self(
+ $config->get('base_url'),
+ $config->get('username'),
+ $config->get('api_key')
+ );
+ $client->setConfig($config);
+
+ $client->setDescription(ServiceDescription::factory(__DIR__ . DIRECTORY_SEPARATOR . 'client.json'));
+
+ return $client;
+ }
+```
+
+**After**
+
+```php
+use Guzzle\Common\Collection;
+
+class YourClient extends \Guzzle\Service\Client
+{
+ public static function factory($config = array())
+ {
+ $default = array();
+ $required = array('base_url', 'username', 'api_key');
+ $config = Collection::fromConfig($config, $default, $required);
+
+ $client = new self(
+ $config->get('base_url'),
+ $config->get('username'),
+ $config->get('api_key')
+ );
+ $client->setConfig($config);
+
+ $client->setDescription(ServiceDescription::factory(__DIR__ . DIRECTORY_SEPARATOR . 'client.json'));
+
+ return $client;
+ }
+```
+
+### Convert XML Service Descriptions to JSON
+
+**Before**
+
+```xml
+<?xml version="1.0" encoding="UTF-8"?>
+<client>
+ <commands>
+ <!-- Groups -->
+ <command name="list_groups" method="GET" uri="groups.json">
+ <doc>Get a list of groups</doc>
+ </command>
+ <command name="search_groups" method="GET" uri='search.json?query="{{query}} type:group"'>
+ <doc>Uses a search query to get a list of groups</doc>
+ <param name="query" type="string" required="true" />
+ </command>
+ <command name="create_group" method="POST" uri="groups.json">
+ <doc>Create a group</doc>
+ <param name="data" type="array" location="body" filters="json_encode" doc="Group JSON"/>
+ <param name="Content-Type" location="header" static="application/json"/>
+ </command>
+ <command name="delete_group" method="DELETE" uri="groups/{{id}}.json">
+ <doc>Delete a group by ID</doc>
+ <param name="id" type="integer" required="true"/>
+ </command>
+ <command name="get_group" method="GET" uri="groups/{{id}}.json">
+ <param name="id" type="integer" required="true"/>
+ </command>
+ <command name="update_group" method="PUT" uri="groups/{{id}}.json">
+ <doc>Update a group</doc>
+ <param name="id" type="integer" required="true"/>
+ <param name="data" type="array" location="body" filters="json_encode" doc="Group JSON"/>
+ <param name="Content-Type" location="header" static="application/json"/>
+ </command>
+ </commands>
+</client>
+```
+
+**After**
+
+```json
+{
+ "name": "Zendesk REST API v2",
+ "apiVersion": "2012-12-31",
+ "description":"Provides access to Zendesk views, groups, tickets, ticket fields, and users",
+ "operations": {
+ "list_groups": {
+ "httpMethod":"GET",
+ "uri": "groups.json",
+ "summary": "Get a list of groups"
+ },
+ "search_groups":{
+ "httpMethod":"GET",
+ "uri": "search.json?query=\"{query} type:group\"",
+ "summary": "Uses a search query to get a list of groups",
+ "parameters":{
+ "query":{
+ "location": "uri",
+ "description":"Zendesk Search Query",
+ "type": "string",
+ "required": true
+ }
+ }
+ },
+ "create_group": {
+ "httpMethod":"POST",
+ "uri": "groups.json",
+ "summary": "Create a group",
+ "parameters":{
+ "data": {
+ "type": "array",
+ "location": "body",
+ "description":"Group JSON",
+ "filters": "json_encode",
+ "required": true
+ },
+ "Content-Type":{
+ "type": "string",
+ "location":"header",
+ "static": "application/json"
+ }
+ }
+ },
+ "delete_group": {
+ "httpMethod":"DELETE",
+ "uri": "groups/{id}.json",
+ "summary": "Delete a group",
+ "parameters":{
+ "id":{
+ "location": "uri",
+ "description":"Group to delete by ID",
+ "type": "integer",
+ "required": true
+ }
+ }
+ },
+ "get_group": {
+ "httpMethod":"GET",
+ "uri": "groups/{id}.json",
+ "summary": "Get a ticket",
+ "parameters":{
+ "id":{
+ "location": "uri",
+ "description":"Group to get by ID",
+ "type": "integer",
+ "required": true
+ }
+ }
+ },
+ "update_group": {
+ "httpMethod":"PUT",
+ "uri": "groups/{id}.json",
+ "summary": "Update a group",
+ "parameters":{
+ "id": {
+ "location": "uri",
+ "description":"Group to update by ID",
+ "type": "integer",
+ "required": true
+ },
+ "data": {
+ "type": "array",
+ "location": "body",
+ "description":"Group JSON",
+ "filters": "json_encode",
+ "required": true
+ },
+ "Content-Type":{
+ "type": "string",
+ "location":"header",
+ "static": "application/json"
+ }
+ }
+ }
+}
+```
+
+### Guzzle\Service\Description\ServiceDescription
+
+Commands are now called Operations
+
+**Before**
+
+```php
+use Guzzle\Service\Description\ServiceDescription;
+
+$sd = new ServiceDescription();
+$sd->getCommands(); // @returns ApiCommandInterface[]
+$sd->hasCommand($name);
+$sd->getCommand($name); // @returns ApiCommandInterface|null
+$sd->addCommand($command); // @param ApiCommandInterface $command
+```
+
+**After**
+
+```php
+use Guzzle\Service\Description\ServiceDescription;
+
+$sd = new ServiceDescription();
+$sd->getOperations(); // @returns OperationInterface[]
+$sd->hasOperation($name);
+$sd->getOperation($name); // @returns OperationInterface|null
+$sd->addOperation($operation); // @param OperationInterface $operation
+```
+
+### Guzzle\Common\Inflection\Inflector
+
+Namespace is now `Guzzle\Inflection\Inflector`
+
+### Guzzle\Http\Plugin
+
+Namespace is now `Guzzle\Plugin`. Many other changes occur within this namespace and are detailed in their own sections below.
+
+### Guzzle\Http\Plugin\LogPlugin and Guzzle\Common\Log
+
+Now `Guzzle\Plugin\Log\LogPlugin` and `Guzzle\Log` respectively.
+
+**Before**
+
+```php
+use Guzzle\Common\Log\ClosureLogAdapter;
+use Guzzle\Http\Plugin\LogPlugin;
+
+/** @var \Guzzle\Http\Client */
+$client;
+
+// $verbosity is an integer indicating desired message verbosity level
+$client->addSubscriber(new LogPlugin(new ClosureLogAdapter(function($m) { echo $m; }, $verbosity = LogPlugin::LOG_VERBOSE);
+```
+
+**After**
+
+```php
+use Guzzle\Log\ClosureLogAdapter;
+use Guzzle\Log\MessageFormatter;
+use Guzzle\Plugin\Log\LogPlugin;
+
+/** @var \Guzzle\Http\Client */
+$client;
+
+// $format is a string indicating desired message format -- @see MessageFormatter
+$client->addSubscriber(new LogPlugin(new ClosureLogAdapter(function($m) { echo $m; }, $format = MessageFormatter::DEBUG_FORMAT);
+```
+
+### Guzzle\Http\Plugin\CurlAuthPlugin
+
+Now `Guzzle\Plugin\CurlAuth\CurlAuthPlugin`.
+
+### Guzzle\Http\Plugin\ExponentialBackoffPlugin
+
+Now `Guzzle\Plugin\Backoff\BackoffPlugin`, and other changes.
+
+**Before**
+
+```php
+use Guzzle\Http\Plugin\ExponentialBackoffPlugin;
+
+$backoffPlugin = new ExponentialBackoffPlugin($maxRetries, array_merge(
+ ExponentialBackoffPlugin::getDefaultFailureCodes(), array(429)
+ ));
+
+$client->addSubscriber($backoffPlugin);
+```
+
+**After**
+
+```php
+use Guzzle\Plugin\Backoff\BackoffPlugin;
+use Guzzle\Plugin\Backoff\HttpBackoffStrategy;
+
+// Use convenient factory method instead -- see implementation for ideas of what
+// you can do with chaining backoff strategies
+$backoffPlugin = BackoffPlugin::getExponentialBackoff($maxRetries, array_merge(
+ HttpBackoffStrategy::getDefaultFailureCodes(), array(429)
+ ));
+$client->addSubscriber($backoffPlugin);
+```
+
+### Known Issues
+
+#### [BUG] Accept-Encoding header behavior changed unintentionally.
+
+(See #217) (Fixed in 09daeb8c666fb44499a0646d655a8ae36456575e)
+
+In version 2.8 setting the `Accept-Encoding` header would set the CURLOPT_ENCODING option, which permitted cURL to
+properly handle gzip/deflate compressed responses from the server. In versions affected by this bug this does not happen.
+See issue #217 for a workaround, or use a version containing the fix.