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. 1204