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