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