krz/vox-populi

A Tumblr web client.

clone: git clone https://gitbay.org/krz/vox-populi.git

main: vendor/guzzlehttp/guzzle/UPGRADING.md · raw

   1Guzzle Upgrade Guide
   2====================
   3
   45.0 to 6.0
   5----------
   6
   7Guzzle now uses [PSR-7](http://www.php-fig.org/psr/psr-7/) for HTTP messages.
   8Due to the fact that these messages are immutable, this prompted a refactoring
   9of Guzzle to use a middleware based system rather than an event system. Any
  10HTTP message interaction (e.g., `GuzzleHttp\Message\Request`) need to be
  11updated to work with the new immutable PSR-7 request and response objects. Any
  12event listeners or subscribers need to be updated to become middleware
  13functions that wrap handlers (or are injected into a
  14`GuzzleHttp\HandlerStack`).
  15
  16- Removed `GuzzleHttp\BatchResults`
  17- Removed `GuzzleHttp\Collection`
  18- Removed `GuzzleHttp\HasDataTrait`
  19- Removed `GuzzleHttp\ToArrayInterface`
  20- The `guzzlehttp/streams` dependency has been removed. Stream functionality
  21  is now present in the `GuzzleHttp\Psr7` namespace provided by the
  22  `guzzlehttp/psr7` package.
  23- Guzzle no longer uses ReactPHP promises and now uses the
  24  `guzzlehttp/promises` library. We use a custom promise library for three
  25  significant reasons:
  26  1. React promises (at the time of writing this) are recursive. Promise
  27     chaining and promise resolution will eventually blow the stack. Guzzle
  28     promises are not recursive as they use a sort of trampolining technique.
  29     Note: there has been movement in the React project to modify promises to
  30     no longer utilize recursion.
  31  2. Guzzle needs to have the ability to synchronously block on a promise to
  32     wait for a result. Guzzle promises allows this functionality (and does
  33     not require the use of recursion).
  34  3. Because we need to be able to wait on a result, doing so using React
  35     promises requires wrapping react promises with RingPHP futures. This
  36     overhead is no longer needed, reducing stack sizes, reducing complexity,
  37     and improving performance.
  38- `GuzzleHttp\Mimetypes` has been moved to a function in
  39  `GuzzleHttp\Psr7\mimetype_from_extension` and
  40  `GuzzleHttp\Psr7\mimetype_from_filename`.
  41- `GuzzleHttp\Query` and `GuzzleHttp\QueryParser` have been removed. Query
  42  strings must now be passed into request objects as strings, or provided to
  43  the `query` request option when creating requests with clients. The `query`
  44  option uses PHP's `http_build_query` to convert an array to a string. If you
  45  need a different serialization technique, you will need to pass the query
  46  string in as a string. There are a couple helper functions that will make
  47  working with query strings easier: `GuzzleHttp\Psr7\parse_query` and
  48  `GuzzleHttp\Psr7\build_query`.
  49- Guzzle no longer has a dependency on RingPHP. Due to the use of a middleware
  50  system based on PSR-7, using RingPHP and it's middleware system as well adds
  51  more complexity than the benefits it provides. All HTTP handlers that were
  52  present in RingPHP have been modified to work directly with PSR-7 messages
  53  and placed in the `GuzzleHttp\Handler` namespace. This significantly reduces
  54  complexity in Guzzle, removes a dependency, and improves performance. RingPHP
  55  will be maintained for Guzzle 5 support, but will no longer be a part of
  56  Guzzle 6.
  57- As Guzzle now uses a middleware based systems the event system and RingPHP
  58  integration has been removed. Note: while the event system has been removed,
  59  it is possible to add your own type of event system that is powered by the
  60  middleware system.
  61  - Removed the `Event` namespace.
  62  - Removed the `Subscriber` namespace.
  63  - Removed `Transaction` class
  64  - Removed `RequestFsm`
  65  - Removed `RingBridge`
  66  - `GuzzleHttp\Subscriber\Cookie` is now provided by
  67    `GuzzleHttp\Middleware::cookies`
  68  - `GuzzleHttp\Subscriber\HttpError` is now provided by
  69    `GuzzleHttp\Middleware::httpError`
  70  - `GuzzleHttp\Subscriber\History` is now provided by
  71    `GuzzleHttp\Middleware::history`
  72  - `GuzzleHttp\Subscriber\Mock` is now provided by
  73    `GuzzleHttp\Handler\MockHandler`
  74  - `GuzzleHttp\Subscriber\Prepare` is now provided by
  75    `GuzzleHttp\PrepareBodyMiddleware`
  76  - `GuzzleHttp\Subscriber\Redirect` is now provided by
  77    `GuzzleHttp\RedirectMiddleware`
  78- Guzzle now uses `Psr\Http\Message\UriInterface` (implements in
  79  `GuzzleHttp\Psr7\Uri`) for URI support. `GuzzleHttp\Url` is now gone.
  80- Static functions in `GuzzleHttp\Utils` have been moved to namespaced
  81  functions under the `GuzzleHttp` namespace. This requires either a Composer
  82  based autoloader or you to include functions.php.
  83- `GuzzleHttp\ClientInterface::getDefaultOption` has been renamed to
  84  `GuzzleHttp\ClientInterface::getConfig`.
  85- `GuzzleHttp\ClientInterface::setDefaultOption` has been removed.
  86- The `json` and `xml` methods of response objects has been removed. With the
  87  migration to strictly adhering to PSR-7 as the interface for Guzzle messages,
  88  adding methods to message interfaces would actually require Guzzle messages
  89  to extend from PSR-7 messages rather then work with them directly.
  90
  91## Migrating to middleware
  92
  93The change to PSR-7 unfortunately required significant refactoring to Guzzle
  94due to the fact that PSR-7 messages are immutable. Guzzle 5 relied on an event
  95system from plugins. The event system relied on mutability of HTTP messages and
  96side effects in order to work. With immutable messages, you have to change your
  97workflow to become more about either returning a value (e.g., functional
  98middlewares) or setting a value on an object. Guzzle v6 has chosen the
  99functional middleware approach.
 100
 101Instead of using the event system to listen for things like the `before` event,
 102you now create a stack based middleware function that intercepts a request on
 103the way in and the promise of the response on the way out. This is a much
 104simpler and more predictable approach than the event system and works nicely
 105with PSR-7 middleware. Due to the use of promises, the middleware system is
 106also asynchronous.
 107
 108v5:
 109
 110```php
 111use GuzzleHttp\Event\BeforeEvent;
 112$client = new GuzzleHttp\Client();
 113// Get the emitter and listen to the before event.
 114$client->getEmitter()->on('before', function (BeforeEvent $e) {
 115    // Guzzle v5 events relied on mutation
 116    $e->getRequest()->setHeader('X-Foo', 'Bar');
 117});
 118```
 119
 120v6:
 121
 122In v6, you can modify the request before it is sent using the `mapRequest`
 123middleware. The idiomatic way in v6 to modify the request/response lifecycle is
 124to setup a handler middleware stack up front and inject the handler into a
 125client.
 126
 127```php
 128use GuzzleHttp\Middleware;
 129// Create a handler stack that has all of the default middlewares attached
 130$handler = GuzzleHttp\HandlerStack::create();
 131// Push the handler onto the handler stack
 132$handler->push(Middleware::mapRequest(function (RequestInterface $request) {
 133    // Notice that we have to return a request object
 134    return $request->withHeader('X-Foo', 'Bar');
 135}));
 136// Inject the handler into the client
 137$client = new GuzzleHttp\Client(['handler' => $handler]);
 138```
 139
 140## POST Requests
 141
 142This version added the [`form_params`](http://guzzle.readthedocs.org/en/latest/request-options.html#form_params)
 143and `multipart` request options. `form_params` is an associative array of
 144strings or array of strings and is used to serialize an
 145`application/x-www-form-urlencoded` POST request. The
 146[`multipart`](http://guzzle.readthedocs.org/en/latest/request-options.html#multipart)
 147option is now used to send a multipart/form-data POST request.
 148
 149`GuzzleHttp\Post\PostFile` has been removed. Use the `multipart` option to add
 150POST files to a multipart/form-data request.
 151
 152The `body` option no longer accepts an array to send POST requests. Please use
 153`multipart` or `form_params` instead.
 154
 155The `base_url` option has been renamed to `base_uri`.
 156
 1574.x to 5.0
 158----------
 159
 160## Rewritten Adapter Layer
 161
 162Guzzle now uses [RingPHP](http://ringphp.readthedocs.org/en/latest) to send
 163HTTP requests. The `adapter` option in a `GuzzleHttp\Client` constructor
 164is still supported, but it has now been renamed to `handler`. Instead of
 165passing a `GuzzleHttp\Adapter\AdapterInterface`, you must now pass a PHP
 166`callable` that follows the RingPHP specification.
 167
 168## Removed Fluent Interfaces
 169
 170[Fluent interfaces were removed](http://ocramius.github.io/blog/fluent-interfaces-are-evil)
 171from the following classes:
 172
 173- `GuzzleHttp\Collection`
 174- `GuzzleHttp\Url`
 175- `GuzzleHttp\Query`
 176- `GuzzleHttp\Post\PostBody`
 177- `GuzzleHttp\Cookie\SetCookie`
 178
 179## Removed functions.php
 180
 181Removed "functions.php", so that Guzzle is truly PSR-4 compliant. The following
 182functions can be used as replacements.
 183
 184- `GuzzleHttp\json_decode` -> `GuzzleHttp\Utils::jsonDecode`
 185- `GuzzleHttp\get_path` -> `GuzzleHttp\Utils::getPath`
 186- `GuzzleHttp\Utils::setPath` -> `GuzzleHttp\set_path`
 187- `GuzzleHttp\Pool::batch` -> `GuzzleHttp\batch`. This function is, however,
 188  deprecated in favor of using `GuzzleHttp\Pool::batch()`.
 189
 190The "procedural" global client has been removed with no replacement (e.g.,
 191`GuzzleHttp\get()`, `GuzzleHttp\post()`, etc.). Use a `GuzzleHttp\Client`
 192object as a replacement.
 193
 194## `throwImmediately` has been removed
 195
 196The concept of "throwImmediately" has been removed from exceptions and error
 197events. This control mechanism was used to stop a transfer of concurrent
 198requests from completing. This can now be handled by throwing the exception or
 199by cancelling a pool of requests or each outstanding future request
 200individually.
 201
 202## headers event has been removed
 203
 204Removed the "headers" event. This event was only useful for changing the
 205body a response once the headers of the response were known. You can implement
 206a similar behavior in a number of ways. One example might be to use a
 207FnStream that has access to the transaction being sent. For example, when the
 208first byte is written, you could check if the response headers match your
 209expectations, and if so, change the actual stream body that is being
 210written to.
 211
 212## Updates to HTTP Messages
 213
 214Removed the `asArray` parameter from
 215`GuzzleHttp\Message\MessageInterface::getHeader`. If you want to get a header
 216value as an array, then use the newly added `getHeaderAsArray()` method of
 217`MessageInterface`. This change makes the Guzzle interfaces compatible with
 218the PSR-7 interfaces.
 219
 2203.x to 4.0
 221----------
 222
 223## Overarching changes:
 224
 225- Now requires PHP 5.4 or greater.
 226- No longer requires cURL to send requests.
 227- Guzzle no longer wraps every exception it throws. Only exceptions that are
 228  recoverable are now wrapped by Guzzle.
 229- Various namespaces have been removed or renamed.
 230- No longer requiring the Symfony EventDispatcher. A custom event dispatcher
 231  based on the Symfony EventDispatcher is
 232  now utilized in `GuzzleHttp\Event\EmitterInterface` (resulting in significant
 233  speed and functionality improvements).
 234
 235Changes per Guzzle 3.x namespace are described below.
 236
 237## Batch
 238
 239The `Guzzle\Batch` namespace has been removed. This is best left to
 240third-parties to implement on top of Guzzle's core HTTP library.
 241
 242## Cache
 243
 244The `Guzzle\Cache` namespace has been removed. (Todo: No suitable replacement
 245has been implemented yet, but hoping to utilize a PSR cache interface).
 246
 247## Common
 248
 249- Removed all of the wrapped exceptions. It's better to use the standard PHP
 250  library for unrecoverable exceptions.
 251- `FromConfigInterface` has been removed.
 252- `Guzzle\Common\Version` has been removed. The VERSION constant can be found
 253  at `GuzzleHttp\ClientInterface::VERSION`.
 254
 255### Collection
 256
 257- `getAll` has been removed. Use `toArray` to convert a collection to an array.
 258- `inject` has been removed.
 259- `keySearch` has been removed.
 260- `getPath` no longer supports wildcard expressions. Use something better like
 261  JMESPath for this.
 262- `setPath` now supports appending to an existing array via the `[]` notation.
 263
 264### Events
 265
 266Guzzle no longer requires Symfony's EventDispatcher component. Guzzle now uses
 267`GuzzleHttp\Event\Emitter`.
 268
 269- `Symfony\Component\EventDispatcher\EventDispatcherInterface` is replaced by
 270  `GuzzleHttp\Event\EmitterInterface`.
 271- `Symfony\Component\EventDispatcher\EventDispatcher` is replaced by
 272  `GuzzleHttp\Event\Emitter`.
 273- `Symfony\Component\EventDispatcher\Event` is replaced by
 274  `GuzzleHttp\Event\Event`, and Guzzle now has an EventInterface in
 275  `GuzzleHttp\Event\EventInterface`.
 276- `AbstractHasDispatcher` has moved to a trait, `HasEmitterTrait`, and
 277  `HasDispatcherInterface` has moved to `HasEmitterInterface`. Retrieving the
 278  event emitter of a request, client, etc. now uses the `getEmitter` method
 279  rather than the `getDispatcher` method.
 280
 281#### Emitter
 282
 283- Use the `once()` method to add a listener that automatically removes itself
 284  the first time it is invoked.
 285- Use the `listeners()` method to retrieve a list of event listeners rather than
 286  the `getListeners()` method.
 287- Use `emit()` instead of `dispatch()` to emit an event from an emitter.
 288- Use `attach()` instead of `addSubscriber()` and `detach()` instead of
 289  `removeSubscriber()`.
 290
 291```php
 292$mock = new Mock();
 293// 3.x
 294$request->getEventDispatcher()->addSubscriber($mock);
 295$request->getEventDispatcher()->removeSubscriber($mock);
 296// 4.x
 297$request->getEmitter()->attach($mock);
 298$request->getEmitter()->detach($mock);
 299```
 300
 301Use the `on()` method to add a listener rather than the `addListener()` method.
 302
 303```php
 304// 3.x
 305$request->getEventDispatcher()->addListener('foo', function (Event $event) { /* ... */ } );
 306// 4.x
 307$request->getEmitter()->on('foo', function (Event $event, $name) { /* ... */ } );
 308```
 309
 310## Http
 311
 312### General changes
 313
 314- The cacert.pem certificate has been moved to `src/cacert.pem`.
 315- Added the concept of adapters that are used to transfer requests over the
 316  wire.
 317- Simplified the event system.
 318- Sending requests in parallel is still possible, but batching is no longer a
 319  concept of the HTTP layer. Instead, you must use the `complete` and `error`
 320  events to asynchronously manage parallel request transfers.
 321- `Guzzle\Http\Url` has moved to `GuzzleHttp\Url`.
 322- `Guzzle\Http\QueryString` has moved to `GuzzleHttp\Query`.
 323- QueryAggregators have been rewritten so that they are simply callable
 324  functions.
 325- `GuzzleHttp\StaticClient` has been removed. Use the functions provided in
 326  `functions.php` for an easy to use static client instance.
 327- Exceptions in `GuzzleHttp\Exception` have been updated to all extend from
 328  `GuzzleHttp\Exception\TransferException`.
 329
 330### Client
 331
 332Calling methods like `get()`, `post()`, `head()`, etc. no longer create and
 333return a request, but rather creates a request, sends the request, and returns
 334the response.
 335
 336```php
 337// 3.0
 338$request = $client->get('/');
 339$response = $request->send();
 340
 341// 4.0
 342$response = $client->get('/');
 343
 344// or, to mirror the previous behavior
 345$request = $client->createRequest('GET', '/');
 346$response = $client->send($request);
 347```
 348
 349`GuzzleHttp\ClientInterface` has changed.
 350
 351- The `send` method no longer accepts more than one request. Use `sendAll` to
 352  send multiple requests in parallel.
 353- `setUserAgent()` has been removed. Use a default request option instead. You
 354  could, for example, do something like:
 355  `$client->setConfig('defaults/headers/User-Agent', 'Foo/Bar ' . $client::getDefaultUserAgent())`.
 356- `setSslVerification()` has been removed. Use default request options instead,
 357  like `$client->setConfig('defaults/verify', true)`.
 358
 359`GuzzleHttp\Client` has changed.
 360
 361- The constructor now accepts only an associative array. You can include a
 362  `base_url` string or array to use a URI template as the base URL of a client.
 363  You can also specify a `defaults` key that is an associative array of default
 364  request options. You can pass an `adapter` to use a custom adapter,
 365  `batch_adapter` to use a custom adapter for sending requests in parallel, or
 366  a `message_factory` to change the factory used to create HTTP requests and
 367  responses.
 368- The client no longer emits a `client.create_request` event.
 369- Creating requests with a client no longer automatically utilize a URI
 370  template. You must pass an array into a creational method (e.g.,
 371  `createRequest`, `get`, `put`, etc.) in order to expand a URI template.
 372
 373### Messages
 374
 375Messages no longer have references to their counterparts (i.e., a request no
 376longer has a reference to it's response, and a response no loger has a
 377reference to its request). This association is now managed through a
 378`GuzzleHttp\Adapter\TransactionInterface` object. You can get references to
 379these transaction objects using request events that are emitted over the
 380lifecycle of a request.
 381
 382#### Requests with a body
 383
 384- `GuzzleHttp\Message\EntityEnclosingRequest` and
 385  `GuzzleHttp\Message\EntityEnclosingRequestInterface` have been removed. The
 386  separation between requests that contain a body and requests that do not
 387  contain a body has been removed, and now `GuzzleHttp\Message\RequestInterface`
 388  handles both use cases.
 389- Any method that previously accepts a `GuzzleHttp\Response` object now accept a
 390  `GuzzleHttp\Message\ResponseInterface`.
 391- `GuzzleHttp\Message\RequestFactoryInterface` has been renamed to
 392  `GuzzleHttp\Message\MessageFactoryInterface`. This interface is used to create
 393  both requests and responses and is implemented in
 394  `GuzzleHttp\Message\MessageFactory`.
 395- POST field and file methods have been removed from the request object. You
 396  must now use the methods made available to `GuzzleHttp\Post\PostBodyInterface`
 397  to control the format of a POST body. Requests that are created using a
 398  standard `GuzzleHttp\Message\MessageFactoryInterface` will automatically use
 399  a `GuzzleHttp\Post\PostBody` body if the body was passed as an array or if
 400  the method is POST and no body is provided.
 401
 402```php
 403$request = $client->createRequest('POST', '/');
 404$request->getBody()->setField('foo', 'bar');
 405$request->getBody()->addFile(new PostFile('file_key', fopen('/path/to/content', 'r')));
 406```
 407
 408#### Headers
 409
 410- `GuzzleHttp\Message\Header` has been removed. Header values are now simply
 411  represented by an array of values or as a string. Header values are returned
 412  as a string by default when retrieving a header value from a message. You can
 413  pass an optional argument of `true` to retrieve a header value as an array
 414  of strings instead of a single concatenated string.
 415- `GuzzleHttp\PostFile` and `GuzzleHttp\PostFileInterface` have been moved to
 416  `GuzzleHttp\Post`. This interface has been simplified and now allows the
 417  addition of arbitrary headers.
 418- Custom headers like `GuzzleHttp\Message\Header\Link` have been removed. Most
 419  of the custom headers are now handled separately in specific
 420  subscribers/plugins, and `GuzzleHttp\Message\HeaderValues::parseParams()` has
 421  been updated to properly handle headers that contain parameters (like the
 422  `Link` header).
 423
 424#### Responses
 425
 426- `GuzzleHttp\Message\Response::getInfo()` and
 427  `GuzzleHttp\Message\Response::setInfo()` have been removed. Use the event
 428  system to retrieve this type of information.
 429- `GuzzleHttp\Message\Response::getRawHeaders()` has been removed.
 430- `GuzzleHttp\Message\Response::getMessage()` has been removed.
 431- `GuzzleHttp\Message\Response::calculateAge()` and other cache specific
 432  methods have moved to the CacheSubscriber.
 433- Header specific helper functions like `getContentMd5()` have been removed.
 434  Just use `getHeader('Content-MD5')` instead.
 435- `GuzzleHttp\Message\Response::setRequest()` and
 436  `GuzzleHttp\Message\Response::getRequest()` have been removed. Use the event
 437  system to work with request and response objects as a transaction.
 438- `GuzzleHttp\Message\Response::getRedirectCount()` has been removed. Use the
 439  Redirect subscriber instead.
 440- `GuzzleHttp\Message\Response::isSuccessful()` and other related methods have
 441  been removed. Use `getStatusCode()` instead.
 442
 443#### Streaming responses
 444
 445Streaming requests can now be created by a client directly, returning a
 446`GuzzleHttp\Message\ResponseInterface` object that contains a body stream
 447referencing an open PHP HTTP stream.
 448
 449```php
 450// 3.0
 451use Guzzle\Stream\PhpStreamRequestFactory;
 452$request = $client->get('/');
 453$factory = new PhpStreamRequestFactory();
 454$stream = $factory->fromRequest($request);
 455$data = $stream->read(1024);
 456
 457// 4.0
 458$response = $client->get('/', ['stream' => true]);
 459// Read some data off of the stream in the response body
 460$data = $response->getBody()->read(1024);
 461```
 462
 463#### Redirects
 464
 465The `configureRedirects()` method has been removed in favor of a
 466`allow_redirects` request option.
 467
 468```php
 469// Standard redirects with a default of a max of 5 redirects
 470$request = $client->createRequest('GET', '/', ['allow_redirects' => true]);
 471
 472// Strict redirects with a custom number of redirects
 473$request = $client->createRequest('GET', '/', [
 474    'allow_redirects' => ['max' => 5, 'strict' => true]
 475]);
 476```
 477
 478#### EntityBody
 479
 480EntityBody interfaces and classes have been removed or moved to
 481`GuzzleHttp\Stream`. All classes and interfaces that once required
 482`GuzzleHttp\EntityBodyInterface` now require
 483`GuzzleHttp\Stream\StreamInterface`. Creating a new body for a request no
 484longer uses `GuzzleHttp\EntityBody::factory` but now uses
 485`GuzzleHttp\Stream\Stream::factory` or even better:
 486`GuzzleHttp\Stream\create()`.
 487
 488- `Guzzle\Http\EntityBodyInterface` is now `GuzzleHttp\Stream\StreamInterface`
 489- `Guzzle\Http\EntityBody` is now `GuzzleHttp\Stream\Stream`
 490- `Guzzle\Http\CachingEntityBody` is now `GuzzleHttp\Stream\CachingStream`
 491- `Guzzle\Http\ReadLimitEntityBody` is now `GuzzleHttp\Stream\LimitStream`
 492- `Guzzle\Http\IoEmittyinEntityBody` has been removed.
 493
 494#### Request lifecycle events
 495
 496Requests previously submitted a large number of requests. The number of events
 497emitted over the lifecycle of a request has been significantly reduced to make
 498it easier to understand how to extend the behavior of a request. All events
 499emitted during the lifecycle of a request now emit a custom
 500`GuzzleHttp\Event\EventInterface` object that contains context providing
 501methods and a way in which to modify the transaction at that specific point in
 502time (e.g., intercept the request and set a response on the transaction).
 503
 504- `request.before_send` has been renamed to `before` and now emits a
 505  `GuzzleHttp\Event\BeforeEvent`
 506- `request.complete` has been renamed to `complete` and now emits a
 507  `GuzzleHttp\Event\CompleteEvent`.
 508- `request.sent` has been removed. Use `complete`.
 509- `request.success` has been removed. Use `complete`.
 510- `error` is now an event that emits a `GuzzleHttp\Event\ErrorEvent`.
 511- `request.exception` has been removed. Use `error`.
 512- `request.receive.status_line` has been removed.
 513- `curl.callback.progress` has been removed. Use a custom `StreamInterface` to
 514  maintain a status update.
 515- `curl.callback.write` has been removed. Use a custom `StreamInterface` to
 516  intercept writes.
 517- `curl.callback.read` has been removed. Use a custom `StreamInterface` to
 518  intercept reads.
 519
 520`headers` is a new event that is emitted after the response headers of a
 521request have been received before the body of the response is downloaded. This
 522event emits a `GuzzleHttp\Event\HeadersEvent`.
 523
 524You can intercept a request and inject a response using the `intercept()` event
 525of a `GuzzleHttp\Event\BeforeEvent`, `GuzzleHttp\Event\CompleteEvent`, and
 526`GuzzleHttp\Event\ErrorEvent` event.
 527
 528See: http://docs.guzzlephp.org/en/latest/events.html
 529
 530## Inflection
 531
 532The `Guzzle\Inflection` namespace has been removed. This is not a core concern
 533of Guzzle.
 534
 535## Iterator
 536
 537The `Guzzle\Iterator` namespace has been removed.
 538
 539- `Guzzle\Iterator\AppendIterator`, `Guzzle\Iterator\ChunkedIterator`, and
 540  `Guzzle\Iterator\MethodProxyIterator` are nice, but not a core requirement of
 541  Guzzle itself.
 542- `Guzzle\Iterator\FilterIterator` is no longer needed because an equivalent
 543  class is shipped with PHP 5.4.
 544- `Guzzle\Iterator\MapIterator` is not really needed when using PHP 5.5 because
 545  it's easier to just wrap an iterator in a generator that maps values.
 546
 547For a replacement of these iterators, see https://github.com/nikic/iter
 548
 549## Log
 550
 551The LogPlugin has moved to https://github.com/guzzle/log-subscriber. The
 552`Guzzle\Log` namespace has been removed. Guzzle now relies on
 553`Psr\Log\LoggerInterface` for all logging. The MessageFormatter class has been
 554moved to `GuzzleHttp\Subscriber\Log\Formatter`.
 555
 556## Parser
 557
 558The `Guzzle\Parser` namespace has been removed. This was previously used to
 559make it possible to plug in custom parsers for cookies, messages, URI
 560templates, and URLs; however, this level of complexity is not needed in Guzzle
 561so it has been removed.
 562
 563- Cookie: Cookie parsing logic has been moved to
 564  `GuzzleHttp\Cookie\SetCookie::fromString`.
 565- Message: Message parsing logic for both requests and responses has been moved
 566  to `GuzzleHttp\Message\MessageFactory::fromMessage`. Message parsing is only
 567  used in debugging or deserializing messages, so it doesn't make sense for
 568  Guzzle as a library to add this level of complexity to parsing messages.
 569- UriTemplate: URI template parsing has been moved to
 570  `GuzzleHttp\UriTemplate`. The Guzzle library will automatically use the PECL
 571  URI template library if it is installed.
 572- Url: URL parsing is now performed in `GuzzleHttp\Url::fromString` (previously
 573  it was `Guzzle\Http\Url::factory()`). If custom URL parsing is necessary,
 574  then developers are free to subclass `GuzzleHttp\Url`.
 575
 576## Plugin
 577
 578The `Guzzle\Plugin` namespace has been renamed to `GuzzleHttp\Subscriber`.
 579Several plugins are shipping with the core Guzzle library under this namespace.
 580
 581- `GuzzleHttp\Subscriber\Cookie`: Replaces the old CookiePlugin. Cookie jar
 582  code has moved to `GuzzleHttp\Cookie`.
 583- `GuzzleHttp\Subscriber\History`: Replaces the old HistoryPlugin.
 584- `GuzzleHttp\Subscriber\HttpError`: Throws errors when a bad HTTP response is
 585  received.
 586- `GuzzleHttp\Subscriber\Mock`: Replaces the old MockPlugin.
 587- `GuzzleHttp\Subscriber\Prepare`: Prepares the body of a request just before
 588  sending. This subscriber is attached to all requests by default.
 589- `GuzzleHttp\Subscriber\Redirect`: Replaces the RedirectPlugin.
 590
 591The following plugins have been removed (third-parties are free to re-implement
 592these if needed):
 593
 594- `GuzzleHttp\Plugin\Async` has been removed.
 595- `GuzzleHttp\Plugin\CurlAuth` has been removed.
 596- `GuzzleHttp\Plugin\ErrorResponse\ErrorResponsePlugin` has been removed. This
 597  functionality should instead be implemented with event listeners that occur
 598  after normal response parsing occurs in the guzzle/command package.
 599
 600The following plugins are not part of the core Guzzle package, but are provided
 601in separate repositories:
 602
 603- `Guzzle\Http\Plugin\BackoffPlugin` has been rewritten to be much simpler
 604  to build custom retry policies using simple functions rather than various
 605  chained classes. See: https://github.com/guzzle/retry-subscriber
 606- `Guzzle\Http\Plugin\Cache\CachePlugin` has moved to
 607  https://github.com/guzzle/cache-subscriber
 608- `Guzzle\Http\Plugin\Log\LogPlugin` has moved to
 609  https://github.com/guzzle/log-subscriber
 610- `Guzzle\Http\Plugin\Md5\Md5Plugin` has moved to
 611  https://github.com/guzzle/message-integrity-subscriber
 612- `Guzzle\Http\Plugin\Mock\MockPlugin` has moved to
 613  `GuzzleHttp\Subscriber\MockSubscriber`.
 614- `Guzzle\Http\Plugin\Oauth\OauthPlugin` has moved to
 615  https://github.com/guzzle/oauth-subscriber
 616
 617## Service
 618
 619The service description layer of Guzzle has moved into two separate packages:
 620
 621- http://github.com/guzzle/command Provides a high level abstraction over web
 622  services by representing web service operations using commands.
 623- http://github.com/guzzle/guzzle-services Provides an implementation of
 624  guzzle/command that provides request serialization and response parsing using
 625  Guzzle service descriptions.
 626
 627## Stream
 628
 629Stream have moved to a separate package available at
 630https://github.com/guzzle/streams.
 631
 632`Guzzle\Stream\StreamInterface` has been given a large update to cleanly take
 633on the responsibilities of `Guzzle\Http\EntityBody` and
 634`Guzzle\Http\EntityBodyInterface` now that they have been removed. The number
 635of methods implemented by the `StreamInterface` has been drastically reduced to
 636allow developers to more easily extend and decorate stream behavior.
 637
 638## Removed methods from StreamInterface
 639
 640- `getStream` and `setStream` have been removed to better encapsulate streams.
 641- `getMetadata` and `setMetadata` have been removed in favor of
 642  `GuzzleHttp\Stream\MetadataStreamInterface`.
 643- `getWrapper`, `getWrapperData`, `getStreamType`, and `getUri` have all been
 644  removed. This data is accessible when
 645  using streams that implement `GuzzleHttp\Stream\MetadataStreamInterface`.
 646- `rewind` has been removed. Use `seek(0)` for a similar behavior.
 647
 648## Renamed methods
 649
 650- `detachStream` has been renamed to `detach`.
 651- `feof` has been renamed to `eof`.
 652- `ftell` has been renamed to `tell`.
 653- `readLine` has moved from an instance method to a static class method of
 654  `GuzzleHttp\Stream\Stream`.
 655
 656## Metadata streams
 657
 658`GuzzleHttp\Stream\MetadataStreamInterface` has been added to denote streams
 659that contain additional metadata accessible via `getMetadata()`.
 660`GuzzleHttp\Stream\StreamInterface::getMetadata` and
 661`GuzzleHttp\Stream\StreamInterface::setMetadata` have been removed.
 662
 663## StreamRequestFactory
 664
 665The entire concept of the StreamRequestFactory has been removed. The way this
 666was used in Guzzle 3 broke the actual interface of sending streaming requests
 667(instead of getting back a Response, you got a StreamInterface). Streaming
 668PHP requests are now implemented through the `GuzzleHttp\Adapter\StreamAdapter`.
 669
 6703.6 to 3.7
 671----------
 672
 673### Deprecations
 674
 675- You can now enable E_USER_DEPRECATED warnings to see if you are using any deprecated methods.:
 676
 677```php
 678\Guzzle\Common\Version::$emitWarnings = true;
 679```
 680
 681The following APIs and options have been marked as deprecated:
 682
 683- Marked `Guzzle\Http\Message\Request::isResponseBodyRepeatable()` as deprecated. Use `$request->getResponseBody()->isRepeatable()` instead.
 684- Marked `Guzzle\Http\Message\Request::canCache()` as deprecated. Use `Guzzle\Plugin\Cache\DefaultCanCacheStrategy->canCacheRequest()` instead.
 685- Marked `Guzzle\Http\Message\Request::canCache()` as deprecated. Use `Guzzle\Plugin\Cache\DefaultCanCacheStrategy->canCacheRequest()` instead.
 686- Marked `Guzzle\Http\Message\Request::setIsRedirect()` as deprecated. Use the HistoryPlugin instead.
 687- Marked `Guzzle\Http\Message\Request::isRedirect()` as deprecated. Use the HistoryPlugin instead.
 688- Marked `Guzzle\Cache\CacheAdapterFactory::factory()` as deprecated
 689- Marked `Guzzle\Service\Client::enableMagicMethods()` as deprecated. Magic methods can no longer be disabled on a Guzzle\Service\Client.
 690- Marked `Guzzle\Parser\Url\UrlParser` as deprecated. Just use PHP's `parse_url()` and percent encode your UTF-8.
 691- Marked `Guzzle\Common\Collection::inject()` as deprecated.
 692- Marked `Guzzle\Plugin\CurlAuth\CurlAuthPlugin` as deprecated. Use
 693  `$client->getConfig()->setPath('request.options/auth', array('user', 'pass', 'Basic|Digest|NTLM|Any'));` or
 694  `$client->setDefaultOption('auth', array('user', 'pass', 'Basic|Digest|NTLM|Any'));`
 695
 6963.7 introduces `request.options` as a parameter for a client configuration and as an optional argument to all creational
 697request methods. When paired with a client's configuration settings, these options allow you to specify default settings
 698for various aspects of a request. Because these options make other previous configuration options redundant, several
 699configuration options and methods of a client and AbstractCommand have been deprecated.
 700
 701- Marked `Guzzle\Service\Client::getDefaultHeaders()` as deprecated. Use `$client->getDefaultOption('headers')`.
 702- Marked `Guzzle\Service\Client::setDefaultHeaders()` as deprecated. Use `$client->setDefaultOption('headers/{header_name}', 'value')`.
 703- Marked 'request.params' for `Guzzle\Http\Client` as deprecated. Use `$client->setDefaultOption('params/{param_name}', 'value')`
 704- Marked 'command.headers', 'command.response_body' and 'command.on_complete' as deprecated for AbstractCommand. These will work through Guzzle 4.0
 705
 706        $command = $client->getCommand('foo', array(
 707            'command.headers' => array('Test' => '123'),
 708            'command.response_body' => '/path/to/file'
 709        ));
 710
 711        // Should be changed to:
 712
 713        $command = $client->getCommand('foo', array(
 714            'command.request_options' => array(
 715                'headers' => array('Test' => '123'),
 716                'save_as' => '/path/to/file'
 717            )
 718        ));
 719
 720### Interface changes
 721
 722Additions and changes (you will need to update any implementations or subclasses you may have created):
 723
 724- Added an `$options` argument to the end of the following methods of `Guzzle\Http\ClientInterface`:
 725  createRequest, head, delete, put, patch, post, options, prepareRequest
 726- Added an `$options` argument to the end of `Guzzle\Http\Message\Request\RequestFactoryInterface::createRequest()`
 727- Added an `applyOptions()` method to `Guzzle\Http\Message\Request\RequestFactoryInterface`
 728- Changed `Guzzle\Http\ClientInterface::get($uri = null, $headers = null, $body = null)` to
 729  `Guzzle\Http\ClientInterface::get($uri = null, $headers = null, $options = array())`. You can still pass in a
 730  resource, string, or EntityBody into the $options parameter to specify the download location of the response.
 731- Changed `Guzzle\Common\Collection::__construct($data)` to no longer accepts a null value for `$data` but a
 732  default `array()`
 733- Added `Guzzle\Stream\StreamInterface::isRepeatable`
 734- Made `Guzzle\Http\Client::expandTemplate` and `getUriTemplate` protected methods.
 735
 736The following methods were removed from interfaces. All of these methods are still available in the concrete classes
 737that implement them, but you should update your code to use alternative methods:
 738
 739- Removed `Guzzle\Http\ClientInterface::setDefaultHeaders(). Use
 740  `$client->getConfig()->setPath('request.options/headers/{header_name}', 'value')`. or
 741  `$client->getConfig()->setPath('request.options/headers', array('header_name' => 'value'))` or
 742  `$client->setDefaultOption('headers/{header_name}', 'value')`. or
 743  `$client->setDefaultOption('headers', array('header_name' => 'value'))`.
 744- Removed `Guzzle\Http\ClientInterface::getDefaultHeaders(). Use `$client->getConfig()->getPath('request.options/headers')`.
 745- Removed `Guzzle\Http\ClientInterface::expandTemplate()`. This is an implementation detail.
 746- Removed `Guzzle\Http\ClientInterface::setRequestFactory()`. This is an implementation detail.
 747- Removed `Guzzle\Http\ClientInterface::getCurlMulti()`. This is a very specific implementation detail.
 748- Removed `Guzzle\Http\Message\RequestInterface::canCache`. Use the CachePlugin.
 749- Removed `Guzzle\Http\Message\RequestInterface::setIsRedirect`. Use the HistoryPlugin.
 750- Removed `Guzzle\Http\Message\RequestInterface::isRedirect`. Use the HistoryPlugin.
 751
 752### Cache plugin breaking changes
 753
 754- CacheKeyProviderInterface and DefaultCacheKeyProvider are no longer used. All of this logic is handled in a
 755  CacheStorageInterface. These two objects and interface will be removed in a future version.
 756- Always setting X-cache headers on cached responses
 757- Default cache TTLs are now handled by the CacheStorageInterface of a CachePlugin
 758- `CacheStorageInterface::cache($key, Response $response, $ttl = null)` has changed to `cache(RequestInterface
 759  $request, Response $response);`
 760- `CacheStorageInterface::fetch($key)` has changed to `fetch(RequestInterface $request);`
 761- `CacheStorageInterface::delete($key)` has changed to `delete(RequestInterface $request);`
 762- Added `CacheStorageInterface::purge($url)`
 763- `DefaultRevalidation::__construct(CacheKeyProviderInterface $cacheKey, CacheStorageInterface $cache, CachePlugin
 764  $plugin)` has changed to `DefaultRevalidation::__construct(CacheStorageInterface $cache,
 765  CanCacheStrategyInterface $canCache = null)`
 766- Added `RevalidationInterface::shouldRevalidate(RequestInterface $request, Response $response)`
 767
 7683.5 to 3.6
 769----------
 770
 771* Mixed casing of headers are now forced to be a single consistent casing across all values for that header.
 772* Messages internally use a HeaderCollection object to delegate handling case-insensitive header resolution
 773* Removed the whole changedHeader() function system of messages because all header changes now go through addHeader().
 774  For example, setHeader() first removes the header using unset on a HeaderCollection and then calls addHeader().
 775  Keeping the Host header and URL host in sync is now handled by overriding the addHeader method in Request.
 776* Specific header implementations can be created for complex headers. When a message creates a header, it uses a
 777  HeaderFactory which can map specific headers to specific header classes. There is now a Link header and
 778  CacheControl header implementation.
 779* Moved getLinks() from Response to just be used on a Link header object.
 780
 781If you previously relied on Guzzle\Http\Message\Header::raw(), then you will need to update your code to use the
 782HeaderInterface (e.g. toArray(), getAll(), etc.).
 783
 784### Interface changes
 785
 786* Removed from interface: Guzzle\Http\ClientInterface::setUriTemplate
 787* Removed from interface: Guzzle\Http\ClientInterface::setCurlMulti()
 788* Removed Guzzle\Http\Message\Request::receivedRequestHeader() and implemented this functionality in
 789  Guzzle\Http\Curl\RequestMediator
 790* Removed the optional $asString parameter from MessageInterface::getHeader(). Just cast the header to a string.
 791* Removed the optional $tryChunkedTransfer option from Guzzle\Http\Message\EntityEnclosingRequestInterface
 792* Removed the $asObjects argument from Guzzle\Http\Message\MessageInterface::getHeaders()
 793
 794### Removed deprecated functions
 795
 796* Removed Guzzle\Parser\ParserRegister::get(). Use getParser()
 797* Removed Guzzle\Parser\ParserRegister::set(). Use registerParser().
 798
 799### Deprecations
 800
 801* The ability to case-insensitively search for header values
 802* Guzzle\Http\Message\Header::hasExactHeader
 803* Guzzle\Http\Message\Header::raw. Use getAll()
 804* Deprecated cache control specific methods on Guzzle\Http\Message\AbstractMessage. Use the CacheControl header object
 805  instead.
 806
 807### Other changes
 808
 809* All response header helper functions return a string rather than mixing Header objects and strings inconsistently
 810* Removed cURL blacklist support. This is no longer necessary now that Expect, Accept, etc. are managed by Guzzle
 811  directly via interfaces
 812* Removed the injecting of a request object onto a response object. The methods to get and set a request still exist
 813  but are a no-op until removed.
 814* Most classes that used to require a `Guzzle\Service\Command\CommandInterface` typehint now request a
 815  `Guzzle\Service\Command\ArrayCommandInterface`.
 816* Added `Guzzle\Http\Message\RequestInterface::startResponse()` to the RequestInterface to handle injecting a response
 817  on a request while the request is still being transferred
 818* `Guzzle\Service\Command\CommandInterface` now extends from ToArrayInterface and ArrayAccess
 819
 8203.3 to 3.4
 821----------
 822
 823Base URLs of a client now follow the rules of http://tools.ietf.org/html/rfc3986#section-5.2.2 when merging URLs.
 824
 8253.2 to 3.3
 826----------
 827
 828### Response::getEtag() quote stripping removed
 829
 830`Guzzle\Http\Message\Response::getEtag()` no longer strips quotes around the ETag response header
 831
 832### Removed `Guzzle\Http\Utils`
 833
 834The `Guzzle\Http\Utils` class was removed. This class was only used for testing.
 835
 836### Stream wrapper and type
 837
 838`Guzzle\Stream\Stream::getWrapper()` and `Guzzle\Stream\Stream::getStreamType()` are no longer converted to lowercase.
 839
 840### curl.emit_io became emit_io
 841
 842Emitting IO events from a RequestMediator is now a parameter that must be set in a request's curl options using the
 843'emit_io' key. This was previously set under a request's parameters using 'curl.emit_io'
 844
 8453.1 to 3.2
 846----------
 847
 848### CurlMulti is no longer reused globally
 849
 850Before 3.2, the same CurlMulti object was reused globally for each client. This can cause issue where plugins added
 851to a single client can pollute requests dispatched from other clients.
 852
 853If you still wish to reuse the same CurlMulti object with each client, then you can add a listener to the
 854ServiceBuilder's `service_builder.create_client` event to inject a custom CurlMulti object into each client as it is
 855created.
 856
 857```php
 858$multi = new Guzzle\Http\Curl\CurlMulti();
 859$builder = Guzzle\Service\Builder\ServiceBuilder::factory('/path/to/config.json');
 860$builder->addListener('service_builder.create_client', function ($event) use ($multi) {
 861    $event['client']->setCurlMulti($multi);
 862}
 863});
 864```
 865
 866### No default path
 867
 868URLs no longer have a default path value of '/' if no path was specified.
 869
 870Before:
 871
 872```php
 873$request = $client->get('http://www.foo.com');
 874echo $request->getUrl();
 875// >> http://www.foo.com/
 876```
 877
 878After:
 879
 880```php
 881$request = $client->get('http://www.foo.com');
 882echo $request->getUrl();
 883// >> http://www.foo.com
 884```
 885
 886### Less verbose BadResponseException
 887
 888The exception message for `Guzzle\Http\Exception\BadResponseException` no longer contains the full HTTP request and
 889response information. You can, however, get access to the request and response object by calling `getRequest()` or
 890`getResponse()` on the exception object.
 891
 892### Query parameter aggregation
 893
 894Multi-valued query parameters are no longer aggregated using a callback function. `Guzzle\Http\Query` now has a
 895setAggregator() method that accepts a `Guzzle\Http\QueryAggregator\QueryAggregatorInterface` object. This object is
 896responsible for handling the aggregation of multi-valued query string variables into a flattened hash.
 897
 8982.8 to 3.x
 899----------
 900
 901### Guzzle\Service\Inspector
 902
 903Change `\Guzzle\Service\Inspector::fromConfig` to `\Guzzle\Common\Collection::fromConfig`
 904
 905**Before**
 906
 907```php
 908use Guzzle\Service\Inspector;
 909
 910class YourClient extends \Guzzle\Service\Client
 911{
 912    public static function factory($config = array())
 913    {
 914        $default = array();
 915        $required = array('base_url', 'username', 'api_key');
 916        $config = Inspector::fromConfig($config, $default, $required);
 917
 918        $client = new self(
 919            $config->get('base_url'),
 920            $config->get('username'),
 921            $config->get('api_key')
 922        );
 923        $client->setConfig($config);
 924
 925        $client->setDescription(ServiceDescription::factory(__DIR__ . DIRECTORY_SEPARATOR . 'client.json'));
 926
 927        return $client;
 928    }
 929```
 930
 931**After**
 932
 933```php
 934use Guzzle\Common\Collection;
 935
 936class YourClient extends \Guzzle\Service\Client
 937{
 938    public static function factory($config = array())
 939    {
 940        $default = array();
 941        $required = array('base_url', 'username', 'api_key');
 942        $config = Collection::fromConfig($config, $default, $required);
 943
 944        $client = new self(
 945            $config->get('base_url'),
 946            $config->get('username'),
 947            $config->get('api_key')
 948        );
 949        $client->setConfig($config);
 950
 951        $client->setDescription(ServiceDescription::factory(__DIR__ . DIRECTORY_SEPARATOR . 'client.json'));
 952
 953        return $client;
 954    }
 955```
 956
 957### Convert XML Service Descriptions to JSON
 958
 959**Before**
 960
 961```xml
 962<?xml version="1.0" encoding="UTF-8"?>
 963<client>
 964    <commands>
 965        <!-- Groups -->
 966        <command name="list_groups" method="GET" uri="groups.json">
 967            <doc>Get a list of groups</doc>
 968        </command>
 969        <command name="search_groups" method="GET" uri='search.json?query="{{query}} type:group"'>
 970            <doc>Uses a search query to get a list of groups</doc>
 971            <param name="query" type="string" required="true" />
 972        </command>
 973        <command name="create_group" method="POST" uri="groups.json">
 974            <doc>Create a group</doc>
 975            <param name="data" type="array" location="body" filters="json_encode" doc="Group JSON"/>
 976            <param name="Content-Type" location="header" static="application/json"/>
 977        </command>
 978        <command name="delete_group" method="DELETE" uri="groups/{{id}}.json">
 979            <doc>Delete a group by ID</doc>
 980            <param name="id" type="integer" required="true"/>
 981        </command>
 982        <command name="get_group" method="GET" uri="groups/{{id}}.json">
 983            <param name="id" type="integer" required="true"/>
 984        </command>
 985        <command name="update_group" method="PUT" uri="groups/{{id}}.json">
 986            <doc>Update a group</doc>
 987            <param name="id" type="integer" required="true"/>
 988            <param name="data" type="array" location="body" filters="json_encode" doc="Group JSON"/>
 989            <param name="Content-Type" location="header" static="application/json"/>
 990        </command>
 991    </commands>
 992</client>
 993```
 994
 995**After**
 996
 997```json
 998{
 999    "name":       "Zendesk REST API v2",
1000    "apiVersion": "2012-12-31",
1001    "description":"Provides access to Zendesk views, groups, tickets, ticket fields, and users",
1002    "operations": {
1003        "list_groups":  {
1004            "httpMethod":"GET",
1005            "uri":       "groups.json",
1006            "summary":   "Get a list of groups"
1007        },
1008        "search_groups":{
1009            "httpMethod":"GET",
1010            "uri":       "search.json?query=\"{query} type:group\"",
1011            "summary":   "Uses a search query to get a list of groups",
1012            "parameters":{
1013                "query":{
1014                    "location":   "uri",
1015                    "description":"Zendesk Search Query",
1016                    "type":       "string",
1017                    "required":   true
1018                }
1019            }
1020        },
1021        "create_group": {
1022            "httpMethod":"POST",
1023            "uri":       "groups.json",
1024            "summary":   "Create a group",
1025            "parameters":{
1026                "data":        {
1027                    "type":       "array",
1028                    "location":   "body",
1029                    "description":"Group JSON",
1030                    "filters":    "json_encode",
1031                    "required":   true
1032                },
1033                "Content-Type":{
1034                    "type":    "string",
1035                    "location":"header",
1036                    "static":  "application/json"
1037                }
1038            }
1039        },
1040        "delete_group": {
1041            "httpMethod":"DELETE",
1042            "uri":       "groups/{id}.json",
1043            "summary":   "Delete a group",
1044            "parameters":{
1045                "id":{
1046                    "location":   "uri",
1047                    "description":"Group to delete by ID",
1048                    "type":       "integer",
1049                    "required":   true
1050                }
1051            }
1052        },
1053        "get_group":    {
1054            "httpMethod":"GET",
1055            "uri":       "groups/{id}.json",
1056            "summary":   "Get a ticket",
1057            "parameters":{
1058                "id":{
1059                    "location":   "uri",
1060                    "description":"Group to get by ID",
1061                    "type":       "integer",
1062                    "required":   true
1063                }
1064            }
1065        },
1066        "update_group": {
1067            "httpMethod":"PUT",
1068            "uri":       "groups/{id}.json",
1069            "summary":   "Update a group",
1070            "parameters":{
1071                "id":          {
1072                    "location":   "uri",
1073                    "description":"Group to update by ID",
1074                    "type":       "integer",
1075                    "required":   true
1076                },
1077                "data":        {
1078                    "type":       "array",
1079                    "location":   "body",
1080                    "description":"Group JSON",
1081                    "filters":    "json_encode",
1082                    "required":   true
1083                },
1084                "Content-Type":{
1085                    "type":    "string",
1086                    "location":"header",
1087                    "static":  "application/json"
1088                }
1089            }
1090        }
1091}
1092```
1093
1094### Guzzle\Service\Description\ServiceDescription
1095
1096Commands are now called Operations
1097
1098**Before**
1099
1100```php
1101use Guzzle\Service\Description\ServiceDescription;
1102
1103$sd = new ServiceDescription();
1104$sd->getCommands();     // @returns ApiCommandInterface[]
1105$sd->hasCommand($name);
1106$sd->getCommand($name); // @returns ApiCommandInterface|null
1107$sd->addCommand($command); // @param ApiCommandInterface $command
1108```
1109
1110**After**
1111
1112```php
1113use Guzzle\Service\Description\ServiceDescription;
1114
1115$sd = new ServiceDescription();
1116$sd->getOperations();           // @returns OperationInterface[]
1117$sd->hasOperation($name);
1118$sd->getOperation($name);       // @returns OperationInterface|null
1119$sd->addOperation($operation);  // @param OperationInterface $operation
1120```
1121
1122### Guzzle\Common\Inflection\Inflector
1123
1124Namespace is now `Guzzle\Inflection\Inflector`
1125
1126### Guzzle\Http\Plugin
1127
1128Namespace is now `Guzzle\Plugin`. Many other changes occur within this namespace and are detailed in their own sections below.
1129
1130### Guzzle\Http\Plugin\LogPlugin and Guzzle\Common\Log
1131
1132Now `Guzzle\Plugin\Log\LogPlugin` and `Guzzle\Log` respectively.
1133
1134**Before**
1135
1136```php
1137use Guzzle\Common\Log\ClosureLogAdapter;
1138use Guzzle\Http\Plugin\LogPlugin;
1139
1140/** @var \Guzzle\Http\Client */
1141$client;
1142
1143// $verbosity is an integer indicating desired message verbosity level
1144$client->addSubscriber(new LogPlugin(new ClosureLogAdapter(function($m) { echo $m; }, $verbosity = LogPlugin::LOG_VERBOSE);
1145```
1146
1147**After**
1148
1149```php
1150use Guzzle\Log\ClosureLogAdapter;
1151use Guzzle\Log\MessageFormatter;
1152use Guzzle\Plugin\Log\LogPlugin;
1153
1154/** @var \Guzzle\Http\Client */
1155$client;
1156
1157// $format is a string indicating desired message format -- @see MessageFormatter
1158$client->addSubscriber(new LogPlugin(new ClosureLogAdapter(function($m) { echo $m; }, $format = MessageFormatter::DEBUG_FORMAT);
1159```
1160
1161### Guzzle\Http\Plugin\CurlAuthPlugin
1162
1163Now `Guzzle\Plugin\CurlAuth\CurlAuthPlugin`.
1164
1165### Guzzle\Http\Plugin\ExponentialBackoffPlugin
1166
1167Now `Guzzle\Plugin\Backoff\BackoffPlugin`, and other changes.
1168
1169**Before**
1170
1171```php
1172use Guzzle\Http\Plugin\ExponentialBackoffPlugin;
1173
1174$backoffPlugin = new ExponentialBackoffPlugin($maxRetries, array_merge(
1175        ExponentialBackoffPlugin::getDefaultFailureCodes(), array(429)
1176    ));
1177
1178$client->addSubscriber($backoffPlugin);
1179```
1180
1181**After**
1182
1183```php
1184use Guzzle\Plugin\Backoff\BackoffPlugin;
1185use Guzzle\Plugin\Backoff\HttpBackoffStrategy;
1186
1187// Use convenient factory method instead -- see implementation for ideas of what
1188// you can do with chaining backoff strategies
1189$backoffPlugin = BackoffPlugin::getExponentialBackoff($maxRetries, array_merge(
1190        HttpBackoffStrategy::getDefaultFailureCodes(), array(429)
1191    ));
1192$client->addSubscriber($backoffPlugin);
1193```
1194
1195### Known Issues
1196
1197#### [BUG] Accept-Encoding header behavior changed unintentionally.
1198
1199(See #217) (Fixed in 09daeb8c666fb44499a0646d655a8ae36456575e)
1200
1201In version 2.8 setting the `Accept-Encoding` header would set the CURLOPT_ENCODING option, which permitted cURL to
1202properly handle gzip/deflate compressed responses from the server. In versions affected by this bug this does not happen.
1203See issue #217 for a workaround, or use a version containing the fix.