scratch – Blame information for rev

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