Ir a la última revisión | Autoría | Comparar con el anterior | Ultima modificación | Ver Log |
Guzzle Upgrade Guide====================6.0 to 7.0----------In order to take advantage of the new features of PHP, Guzzle dropped the supportof PHP 5. The minimum supported PHP version is now PHP 7.2. Type hints and returntypes for functions and methods have been added wherever possible.Please make sure:- You are calling a function or a method with the correct type.- If you extend a class of Guzzle; update all signatures on methods you override.#### Other backwards compatibility breaking changes- Class `GuzzleHttp\UriTemplate` is removed.- Class `GuzzleHttp\Exception\SeekException` is removed.- Classes `GuzzleHttp\Exception\BadResponseException`, `GuzzleHttp\Exception\ClientException`,`GuzzleHttp\Exception\ServerException` can no longer be initialized with an emptyResponse as argument.- Class `GuzzleHttp\Exception\ConnectException` now extends `GuzzleHttp\Exception\TransferException`instead of `GuzzleHttp\Exception\RequestException`.- Function `GuzzleHttp\Exception\ConnectException::getResponse()` is removed.- Function `GuzzleHttp\Exception\ConnectException::hasResponse()` is removed.- Constant `GuzzleHttp\ClientInterface::VERSION` is removed. Added `GuzzleHttp\ClientInterface::MAJOR_VERSION` instead.- Function `GuzzleHttp\Exception\RequestException::getResponseBodySummary` is removed.Use `\GuzzleHttp\Psr7\get_message_body_summary` as an alternative.- Function `GuzzleHttp\Cookie\CookieJar::getCookieValue` is removed.- Request option `exception` is removed. Please use `http_errors`.- Request option `save_to` is removed. Please use `sink`.- Pool option `pool_size` is removed. Please use `concurrency`.- We now look for environment variables in the `$_SERVER` super global, due to thread safety issues with `getenv`. We continue to fallback to `getenv` in CLI environments, for maximum compatibility.- The `get`, `head`, `put`, `post`, `patch`, `delete`, `getAsync`, `headAsync`, `putAsync`, `postAsync`, `patchAsync`, and `deleteAsync` methods are now implemented as genuine methods on `GuzzleHttp\Client`, with strong typing. The original `__call` implementation remains unchanged for now, for maximum backwards compatibility, but won't be invoked under normal operation.- The `log` middleware will log the errors with level `error` instead of `notice`- Support for international domain names (IDN) is now disabled by default, and enabling it requires installing ext-intl, linked against a modern version of the C library (ICU 4.6 or higher).#### Native functions callsAll internal native functions calls of Guzzle are now prefixed with a slash. Thischange makes it impossible for method overloading by other libraries or applications.Example:```php// Before:curl_version();// After:\curl_version();```For the full diff you can check [here](https://github.com/guzzle/guzzle/compare/6.5.4..master).5.0 to 6.0----------Guzzle now uses [PSR-7](https://www.php-fig.org/psr/psr-7/) for HTTP messages.Due to the fact that these messages are immutable, this prompted a refactoringof Guzzle to use a middleware based system rather than an event system. AnyHTTP message interaction (e.g., `GuzzleHttp\Message\Request`) need to beupdated to work with the new immutable PSR-7 request and response objects. Anyevent listeners or subscribers need to be updated to become middlewarefunctions that wrap handlers (or are injected into a`GuzzleHttp\HandlerStack`).- Removed `GuzzleHttp\BatchResults`- Removed `GuzzleHttp\Collection`- Removed `GuzzleHttp\HasDataTrait`- Removed `GuzzleHttp\ToArrayInterface`- The `guzzlehttp/streams` dependency has been removed. Stream functionalityis now present in the `GuzzleHttp\Psr7` namespace provided by the`guzzlehttp/psr7` package.- Guzzle no longer uses ReactPHP promises and now uses the`guzzlehttp/promises` library. We use a custom promise library for threesignificant reasons:1. React promises (at the time of writing this) are recursive. Promisechaining and promise resolution will eventually blow the stack. Guzzlepromises are not recursive as they use a sort of trampolining technique.Note: there has been movement in the React project to modify promises tono longer utilize recursion.2. Guzzle needs to have the ability to synchronously block on a promise towait for a result. Guzzle promises allows this functionality (and doesnot require the use of recursion).3. Because we need to be able to wait on a result, doing so using Reactpromises requires wrapping react promises with RingPHP futures. Thisoverhead is no longer needed, reducing stack sizes, reducing complexity,and improving performance.- `GuzzleHttp\Mimetypes` has been moved to a function in`GuzzleHttp\Psr7\mimetype_from_extension` and`GuzzleHttp\Psr7\mimetype_from_filename`.- `GuzzleHttp\Query` and `GuzzleHttp\QueryParser` have been removed. Querystrings must now be passed into request objects as strings, or provided tothe `query` request option when creating requests with clients. The `query`option uses PHP's `http_build_query` to convert an array to a string. If youneed a different serialization technique, you will need to pass the querystring in as a string. There are a couple helper functions that will makeworking with query strings easier: `GuzzleHttp\Psr7\parse_query` and`GuzzleHttp\Psr7\build_query`.- Guzzle no longer has a dependency on RingPHP. Due to the use of a middlewaresystem based on PSR-7, using RingPHP and it's middleware system as well addsmore complexity than the benefits it provides. All HTTP handlers that werepresent in RingPHP have been modified to work directly with PSR-7 messagesand placed in the `GuzzleHttp\Handler` namespace. This significantly reducescomplexity in Guzzle, removes a dependency, and improves performance. RingPHPwill be maintained for Guzzle 5 support, but will no longer be a part ofGuzzle 6.- As Guzzle now uses a middleware based systems the event system and RingPHPintegration has been removed. Note: while the event system has been removed,it is possible to add your own type of event system that is powered by themiddleware system.- Removed the `Event` namespace.- Removed the `Subscriber` namespace.- Removed `Transaction` class- Removed `RequestFsm`- Removed `RingBridge`- `GuzzleHttp\Subscriber\Cookie` is now provided by`GuzzleHttp\Middleware::cookies`- `GuzzleHttp\Subscriber\HttpError` is now provided by`GuzzleHttp\Middleware::httpError`- `GuzzleHttp\Subscriber\History` is now provided by`GuzzleHttp\Middleware::history`- `GuzzleHttp\Subscriber\Mock` is now provided by`GuzzleHttp\Handler\MockHandler`- `GuzzleHttp\Subscriber\Prepare` is now provided by`GuzzleHttp\PrepareBodyMiddleware`- `GuzzleHttp\Subscriber\Redirect` is now provided by`GuzzleHttp\RedirectMiddleware`- Guzzle now uses `Psr\Http\Message\UriInterface` (implements in`GuzzleHttp\Psr7\Uri`) for URI support. `GuzzleHttp\Url` is now gone.- Static functions in `GuzzleHttp\Utils` have been moved to namespacedfunctions under the `GuzzleHttp` namespace. This requires either a Composerbased autoloader or you to include functions.php.- `GuzzleHttp\ClientInterface::getDefaultOption` has been renamed to`GuzzleHttp\ClientInterface::getConfig`.- `GuzzleHttp\ClientInterface::setDefaultOption` has been removed.- The `json` and `xml` methods of response objects has been removed. With themigration to strictly adhering to PSR-7 as the interface for Guzzle messages,adding methods to message interfaces would actually require Guzzle messagesto extend from PSR-7 messages rather then work with them directly.## Migrating to middlewareThe change to PSR-7 unfortunately required significant refactoring to Guzzledue to the fact that PSR-7 messages are immutable. Guzzle 5 relied on an eventsystem from plugins. The event system relied on mutability of HTTP messages andside effects in order to work. With immutable messages, you have to change yourworkflow to become more about either returning a value (e.g., functionalmiddlewares) or setting a value on an object. Guzzle v6 has chosen thefunctional middleware approach.Instead of using the event system to listen for things like the `before` event,you now create a stack based middleware function that intercepts a request onthe way in and the promise of the response on the way out. This is a muchsimpler and more predictable approach than the event system and works nicelywith PSR-7 middleware. Due to the use of promises, the middleware system isalso asynchronous.v5:```phpuse GuzzleHttp\Event\BeforeEvent;$client = new GuzzleHttp\Client();// Get the emitter and listen to the before event.$client->getEmitter()->on('before', function (BeforeEvent $e) {// Guzzle v5 events relied on mutation$e->getRequest()->setHeader('X-Foo', 'Bar');});```v6:In v6, you can modify the request before it is sent using the `mapRequest`middleware. The idiomatic way in v6 to modify the request/response lifecycle isto setup a handler middleware stack up front and inject the handler into aclient.```phpuse GuzzleHttp\Middleware;// Create a handler stack that has all of the default middlewares attached$handler = GuzzleHttp\HandlerStack::create();// Push the handler onto the handler stack$handler->push(Middleware::mapRequest(function (RequestInterface $request) {// Notice that we have to return a request objectreturn $request->withHeader('X-Foo', 'Bar');}));// Inject the handler into the client$client = new GuzzleHttp\Client(['handler' => $handler]);```## POST RequestsThis version added the [`form_params`](http://guzzle.readthedocs.org/en/latest/request-options.html#form_params)and `multipart` request options. `form_params` is an associative array ofstrings or array of strings and is used to serialize an`application/x-www-form-urlencoded` POST request. The[`multipart`](http://guzzle.readthedocs.org/en/latest/request-options.html#multipart)option is now used to send a multipart/form-data POST request.`GuzzleHttp\Post\PostFile` has been removed. Use the `multipart` option to addPOST files to a multipart/form-data request.The `body` option no longer accepts an array to send POST requests. Please use`multipart` or `form_params` instead.The `base_url` option has been renamed to `base_uri`.4.x to 5.0----------## Rewritten Adapter LayerGuzzle now uses [RingPHP](http://ringphp.readthedocs.org/en/latest) to sendHTTP requests. The `adapter` option in a `GuzzleHttp\Client` constructoris still supported, but it has now been renamed to `handler`. Instead ofpassing a `GuzzleHttp\Adapter\AdapterInterface`, you must now pass a PHP`callable` that follows the RingPHP specification.## Removed Fluent Interfaces[Fluent interfaces were removed](https://ocramius.github.io/blog/fluent-interfaces-are-evil/)from the following classes:- `GuzzleHttp\Collection`- `GuzzleHttp\Url`- `GuzzleHttp\Query`- `GuzzleHttp\Post\PostBody`- `GuzzleHttp\Cookie\SetCookie`## Removed functions.phpRemoved "functions.php", so that Guzzle is truly PSR-4 compliant. The followingfunctions can be used as replacements.- `GuzzleHttp\json_decode` -> `GuzzleHttp\Utils::jsonDecode`- `GuzzleHttp\get_path` -> `GuzzleHttp\Utils::getPath`- `GuzzleHttp\Utils::setPath` -> `GuzzleHttp\set_path`- `GuzzleHttp\Pool::batch` -> `GuzzleHttp\batch`. This function is, however,deprecated in favor of using `GuzzleHttp\Pool::batch()`.The "procedural" global client has been removed with no replacement (e.g.,`GuzzleHttp\get()`, `GuzzleHttp\post()`, etc.). Use a `GuzzleHttp\Client`object as a replacement.## `throwImmediately` has been removedThe concept of "throwImmediately" has been removed from exceptions and errorevents. This control mechanism was used to stop a transfer of concurrentrequests from completing. This can now be handled by throwing the exception orby cancelling a pool of requests or each outstanding future requestindividually.## headers event has been removedRemoved the "headers" event. This event was only useful for changing thebody a response once the headers of the response were known. You can implementa similar behavior in a number of ways. One example might be to use aFnStream that has access to the transaction being sent. For example, when thefirst byte is written, you could check if the response headers match yourexpectations, and if so, change the actual stream body that is beingwritten to.## Updates to HTTP MessagesRemoved the `asArray` parameter from`GuzzleHttp\Message\MessageInterface::getHeader`. If you want to get a headervalue as an array, then use the newly added `getHeaderAsArray()` method of`MessageInterface`. This change makes the Guzzle interfaces compatible withthe PSR-7 interfaces.3.x to 4.0----------## Overarching changes:- Now requires PHP 5.4 or greater.- No longer requires cURL to send requests.- Guzzle no longer wraps every exception it throws. Only exceptions that arerecoverable are now wrapped by Guzzle.- Various namespaces have been removed or renamed.- No longer requiring the Symfony EventDispatcher. A custom event dispatcherbased on the Symfony EventDispatcher isnow utilized in `GuzzleHttp\Event\EmitterInterface` (resulting in significantspeed and functionality improvements).Changes per Guzzle 3.x namespace are described below.## BatchThe `Guzzle\Batch` namespace has been removed. This is best left tothird-parties to implement on top of Guzzle's core HTTP library.## CacheThe `Guzzle\Cache` namespace has been removed. (Todo: No suitable replacementhas been implemented yet, but hoping to utilize a PSR cache interface).## Common- Removed all of the wrapped exceptions. It's better to use the standard PHPlibrary for unrecoverable exceptions.- `FromConfigInterface` has been removed.- `Guzzle\Common\Version` has been removed. The VERSION constant can be foundat `GuzzleHttp\ClientInterface::VERSION`.### Collection- `getAll` has been removed. Use `toArray` to convert a collection to an array.- `inject` has been removed.- `keySearch` has been removed.- `getPath` no longer supports wildcard expressions. Use something better likeJMESPath for this.- `setPath` now supports appending to an existing array via the `[]` notation.### EventsGuzzle no longer requires Symfony's EventDispatcher component. Guzzle now uses`GuzzleHttp\Event\Emitter`.- `Symfony\Component\EventDispatcher\EventDispatcherInterface` is replaced by`GuzzleHttp\Event\EmitterInterface`.- `Symfony\Component\EventDispatcher\EventDispatcher` is replaced by`GuzzleHttp\Event\Emitter`.- `Symfony\Component\EventDispatcher\Event` is replaced by`GuzzleHttp\Event\Event`, and Guzzle now has an EventInterface in`GuzzleHttp\Event\EventInterface`.- `AbstractHasDispatcher` has moved to a trait, `HasEmitterTrait`, and`HasDispatcherInterface` has moved to `HasEmitterInterface`. Retrieving theevent emitter of a request, client, etc. now uses the `getEmitter` methodrather than the `getDispatcher` method.#### Emitter- Use the `once()` method to add a listener that automatically removes itselfthe first time it is invoked.- Use the `listeners()` method to retrieve a list of event listeners rather thanthe `getListeners()` method.- Use `emit()` instead of `dispatch()` to emit an event from an emitter.- Use `attach()` instead of `addSubscriber()` and `detach()` instead of`removeSubscriber()`.```php$mock = new Mock();// 3.x$request->getEventDispatcher()->addSubscriber($mock);$request->getEventDispatcher()->removeSubscriber($mock);// 4.x$request->getEmitter()->attach($mock);$request->getEmitter()->detach($mock);```Use the `on()` method to add a listener rather than the `addListener()` method.```php// 3.x$request->getEventDispatcher()->addListener('foo', function (Event $event) { /* ... */ } );// 4.x$request->getEmitter()->on('foo', function (Event $event, $name) { /* ... */ } );```## Http### General changes- The cacert.pem certificate has been moved to `src/cacert.pem`.- Added the concept of adapters that are used to transfer requests over thewire.- Simplified the event system.- Sending requests in parallel is still possible, but batching is no longer aconcept of the HTTP layer. Instead, you must use the `complete` and `error`events to asynchronously manage parallel request transfers.- `Guzzle\Http\Url` has moved to `GuzzleHttp\Url`.- `Guzzle\Http\QueryString` has moved to `GuzzleHttp\Query`.- QueryAggregators have been rewritten so that they are simply callablefunctions.- `GuzzleHttp\StaticClient` has been removed. Use the functions provided in`functions.php` for an easy to use static client instance.- Exceptions in `GuzzleHttp\Exception` have been updated to all extend from`GuzzleHttp\Exception\TransferException`.### ClientCalling methods like `get()`, `post()`, `head()`, etc. no longer create andreturn a request, but rather creates a request, sends the request, and returnsthe response.```php// 3.0$request = $client->get('/');$response = $request->send();// 4.0$response = $client->get('/');// or, to mirror the previous behavior$request = $client->createRequest('GET', '/');$response = $client->send($request);````GuzzleHttp\ClientInterface` has changed.- The `send` method no longer accepts more than one request. Use `sendAll` tosend multiple requests in parallel.- `setUserAgent()` has been removed. Use a default request option instead. Youcould, for example, do something like:`$client->setConfig('defaults/headers/User-Agent', 'Foo/Bar ' . $client::getDefaultUserAgent())`.- `setSslVerification()` has been removed. Use default request options instead,like `$client->setConfig('defaults/verify', true)`.`GuzzleHttp\Client` has changed.- The constructor now accepts only an associative array. You can include a`base_url` string or array to use a URI template as the base URL of a client.You can also specify a `defaults` key that is an associative array of defaultrequest options. You can pass an `adapter` to use a custom adapter,`batch_adapter` to use a custom adapter for sending requests in parallel, ora `message_factory` to change the factory used to create HTTP requests andresponses.- The client no longer emits a `client.create_request` event.- Creating requests with a client no longer automatically utilize a URItemplate. You must pass an array into a creational method (e.g.,`createRequest`, `get`, `put`, etc.) in order to expand a URI template.### MessagesMessages no longer have references to their counterparts (i.e., a request nolonger has a reference to it's response, and a response no loger has areference to its request). This association is now managed through a`GuzzleHttp\Adapter\TransactionInterface` object. You can get references tothese transaction objects using request events that are emitted over thelifecycle of a request.#### Requests with a body- `GuzzleHttp\Message\EntityEnclosingRequest` and`GuzzleHttp\Message\EntityEnclosingRequestInterface` have been removed. Theseparation between requests that contain a body and requests that do notcontain a body has been removed, and now `GuzzleHttp\Message\RequestInterface`handles both use cases.- Any method that previously accepts a `GuzzleHttp\Response` object now accept a`GuzzleHttp\Message\ResponseInterface`.- `GuzzleHttp\Message\RequestFactoryInterface` has been renamed to`GuzzleHttp\Message\MessageFactoryInterface`. This interface is used to createboth requests and responses and is implemented in`GuzzleHttp\Message\MessageFactory`.- POST field and file methods have been removed from the request object. Youmust now use the methods made available to `GuzzleHttp\Post\PostBodyInterface`to control the format of a POST body. Requests that are created using astandard `GuzzleHttp\Message\MessageFactoryInterface` will automatically usea `GuzzleHttp\Post\PostBody` body if the body was passed as an array or ifthe method is POST and no body is provided.```php$request = $client->createRequest('POST', '/');$request->getBody()->setField('foo', 'bar');$request->getBody()->addFile(new PostFile('file_key', fopen('/path/to/content', 'r')));```#### Headers- `GuzzleHttp\Message\Header` has been removed. Header values are now simplyrepresented by an array of values or as a string. Header values are returnedas a string by default when retrieving a header value from a message. You canpass an optional argument of `true` to retrieve a header value as an arrayof strings instead of a single concatenated string.- `GuzzleHttp\PostFile` and `GuzzleHttp\PostFileInterface` have been moved to`GuzzleHttp\Post`. This interface has been simplified and now allows theaddition of arbitrary headers.- Custom headers like `GuzzleHttp\Message\Header\Link` have been removed. Mostof the custom headers are now handled separately in specificsubscribers/plugins, and `GuzzleHttp\Message\HeaderValues::parseParams()` hasbeen updated to properly handle headers that contain parameters (like the`Link` header).#### Responses- `GuzzleHttp\Message\Response::getInfo()` and`GuzzleHttp\Message\Response::setInfo()` have been removed. Use the eventsystem to retrieve this type of information.- `GuzzleHttp\Message\Response::getRawHeaders()` has been removed.- `GuzzleHttp\Message\Response::getMessage()` has been removed.- `GuzzleHttp\Message\Response::calculateAge()` and other cache specificmethods have moved to the CacheSubscriber.- Header specific helper functions like `getContentMd5()` have been removed.Just use `getHeader('Content-MD5')` instead.- `GuzzleHttp\Message\Response::setRequest()` and`GuzzleHttp\Message\Response::getRequest()` have been removed. Use the eventsystem to work with request and response objects as a transaction.- `GuzzleHttp\Message\Response::getRedirectCount()` has been removed. Use theRedirect subscriber instead.- `GuzzleHttp\Message\Response::isSuccessful()` and other related methods havebeen removed. Use `getStatusCode()` instead.#### Streaming responsesStreaming requests can now be created by a client directly, returning a`GuzzleHttp\Message\ResponseInterface` object that contains a body streamreferencing an open PHP HTTP stream.```php// 3.0use Guzzle\Stream\PhpStreamRequestFactory;$request = $client->get('/');$factory = new PhpStreamRequestFactory();$stream = $factory->fromRequest($request);$data = $stream->read(1024);// 4.0$response = $client->get('/', ['stream' => true]);// Read some data off of the stream in the response body$data = $response->getBody()->read(1024);```#### RedirectsThe `configureRedirects()` method has been removed in favor of a`allow_redirects` request option.```php// Standard redirects with a default of a max of 5 redirects$request = $client->createRequest('GET', '/', ['allow_redirects' => true]);// Strict redirects with a custom number of redirects$request = $client->createRequest('GET', '/', ['allow_redirects' => ['max' => 5, 'strict' => true]]);```#### EntityBodyEntityBody interfaces and classes have been removed or moved to`GuzzleHttp\Stream`. All classes and interfaces that once required`GuzzleHttp\EntityBodyInterface` now require`GuzzleHttp\Stream\StreamInterface`. Creating a new body for a request nolonger uses `GuzzleHttp\EntityBody::factory` but now uses`GuzzleHttp\Stream\Stream::factory` or even better:`GuzzleHttp\Stream\create()`.- `Guzzle\Http\EntityBodyInterface` is now `GuzzleHttp\Stream\StreamInterface`- `Guzzle\Http\EntityBody` is now `GuzzleHttp\Stream\Stream`- `Guzzle\Http\CachingEntityBody` is now `GuzzleHttp\Stream\CachingStream`- `Guzzle\Http\ReadLimitEntityBody` is now `GuzzleHttp\Stream\LimitStream`- `Guzzle\Http\IoEmittyinEntityBody` has been removed.#### Request lifecycle eventsRequests previously submitted a large number of requests. The number of eventsemitted over the lifecycle of a request has been significantly reduced to makeit easier to understand how to extend the behavior of a request. All eventsemitted during the lifecycle of a request now emit a custom`GuzzleHttp\Event\EventInterface` object that contains context providingmethods and a way in which to modify the transaction at that specific point intime (e.g., intercept the request and set a response on the transaction).- `request.before_send` has been renamed to `before` and now emits a`GuzzleHttp\Event\BeforeEvent`- `request.complete` has been renamed to `complete` and now emits a`GuzzleHttp\Event\CompleteEvent`.- `request.sent` has been removed. Use `complete`.- `request.success` has been removed. Use `complete`.- `error` is now an event that emits a `GuzzleHttp\Event\ErrorEvent`.- `request.exception` has been removed. Use `error`.- `request.receive.status_line` has been removed.- `curl.callback.progress` has been removed. Use a custom `StreamInterface` tomaintain a status update.- `curl.callback.write` has been removed. Use a custom `StreamInterface` tointercept writes.- `curl.callback.read` has been removed. Use a custom `StreamInterface` tointercept reads.`headers` is a new event that is emitted after the response headers of arequest have been received before the body of the response is downloaded. Thisevent emits a `GuzzleHttp\Event\HeadersEvent`.You can intercept a request and inject a response using the `intercept()` eventof a `GuzzleHttp\Event\BeforeEvent`, `GuzzleHttp\Event\CompleteEvent`, and`GuzzleHttp\Event\ErrorEvent` event.See: http://docs.guzzlephp.org/en/latest/events.html## InflectionThe `Guzzle\Inflection` namespace has been removed. This is not a core concernof Guzzle.## IteratorThe `Guzzle\Iterator` namespace has been removed.- `Guzzle\Iterator\AppendIterator`, `Guzzle\Iterator\ChunkedIterator`, and`Guzzle\Iterator\MethodProxyIterator` are nice, but not a core requirement ofGuzzle itself.- `Guzzle\Iterator\FilterIterator` is no longer needed because an equivalentclass is shipped with PHP 5.4.- `Guzzle\Iterator\MapIterator` is not really needed when using PHP 5.5 becauseit's easier to just wrap an iterator in a generator that maps values.For a replacement of these iterators, see https://github.com/nikic/iter## LogThe LogPlugin has moved to https://github.com/guzzle/log-subscriber. The`Guzzle\Log` namespace has been removed. Guzzle now relies on`Psr\Log\LoggerInterface` for all logging. The MessageFormatter class has beenmoved to `GuzzleHttp\Subscriber\Log\Formatter`.## ParserThe `Guzzle\Parser` namespace has been removed. This was previously used tomake it possible to plug in custom parsers for cookies, messages, URItemplates, and URLs; however, this level of complexity is not needed in Guzzleso it has been removed.- Cookie: Cookie parsing logic has been moved to`GuzzleHttp\Cookie\SetCookie::fromString`.- Message: Message parsing logic for both requests and responses has been movedto `GuzzleHttp\Message\MessageFactory::fromMessage`. Message parsing is onlyused in debugging or deserializing messages, so it doesn't make sense forGuzzle as a library to add this level of complexity to parsing messages.- UriTemplate: URI template parsing has been moved to`GuzzleHttp\UriTemplate`. The Guzzle library will automatically use the PECLURI template library if it is installed.- Url: URL parsing is now performed in `GuzzleHttp\Url::fromString` (previouslyit was `Guzzle\Http\Url::factory()`). If custom URL parsing is necessary,then developers are free to subclass `GuzzleHttp\Url`.## PluginThe `Guzzle\Plugin` namespace has been renamed to `GuzzleHttp\Subscriber`.Several plugins are shipping with the core Guzzle library under this namespace.- `GuzzleHttp\Subscriber\Cookie`: Replaces the old CookiePlugin. Cookie jarcode has moved to `GuzzleHttp\Cookie`.- `GuzzleHttp\Subscriber\History`: Replaces the old HistoryPlugin.- `GuzzleHttp\Subscriber\HttpError`: Throws errors when a bad HTTP response isreceived.- `GuzzleHttp\Subscriber\Mock`: Replaces the old MockPlugin.- `GuzzleHttp\Subscriber\Prepare`: Prepares the body of a request just beforesending. This subscriber is attached to all requests by default.- `GuzzleHttp\Subscriber\Redirect`: Replaces the RedirectPlugin.The following plugins have been removed (third-parties are free to re-implementthese if needed):- `GuzzleHttp\Plugin\Async` has been removed.- `GuzzleHttp\Plugin\CurlAuth` has been removed.- `GuzzleHttp\Plugin\ErrorResponse\ErrorResponsePlugin` has been removed. Thisfunctionality should instead be implemented with event listeners that occurafter normal response parsing occurs in the guzzle/command package.The following plugins are not part of the core Guzzle package, but are providedin separate repositories:- `Guzzle\Http\Plugin\BackoffPlugin` has been rewritten to be much simplerto build custom retry policies using simple functions rather than variouschained classes. See: https://github.com/guzzle/retry-subscriber- `Guzzle\Http\Plugin\Cache\CachePlugin` has moved tohttps://github.com/guzzle/cache-subscriber- `Guzzle\Http\Plugin\Log\LogPlugin` has moved tohttps://github.com/guzzle/log-subscriber- `Guzzle\Http\Plugin\Md5\Md5Plugin` has moved tohttps://github.com/guzzle/message-integrity-subscriber- `Guzzle\Http\Plugin\Mock\MockPlugin` has moved to`GuzzleHttp\Subscriber\MockSubscriber`.- `Guzzle\Http\Plugin\Oauth\OauthPlugin` has moved tohttps://github.com/guzzle/oauth-subscriber## ServiceThe service description layer of Guzzle has moved into two separate packages:- http://github.com/guzzle/command Provides a high level abstraction over webservices by representing web service operations using commands.- http://github.com/guzzle/guzzle-services Provides an implementation ofguzzle/command that provides request serialization and response parsing usingGuzzle service descriptions.## StreamStream have moved to a separate package available athttps://github.com/guzzle/streams.`Guzzle\Stream\StreamInterface` has been given a large update to cleanly takeon the responsibilities of `Guzzle\Http\EntityBody` and`Guzzle\Http\EntityBodyInterface` now that they have been removed. The numberof methods implemented by the `StreamInterface` has been drastically reduced toallow developers to more easily extend and decorate stream behavior.## Removed methods from StreamInterface- `getStream` and `setStream` have been removed to better encapsulate streams.- `getMetadata` and `setMetadata` have been removed in favor of`GuzzleHttp\Stream\MetadataStreamInterface`.- `getWrapper`, `getWrapperData`, `getStreamType`, and `getUri` have all beenremoved. This data is accessible whenusing streams that implement `GuzzleHttp\Stream\MetadataStreamInterface`.- `rewind` has been removed. Use `seek(0)` for a similar behavior.## Renamed methods- `detachStream` has been renamed to `detach`.- `feof` has been renamed to `eof`.- `ftell` has been renamed to `tell`.- `readLine` has moved from an instance method to a static class method of`GuzzleHttp\Stream\Stream`.## Metadata streams`GuzzleHttp\Stream\MetadataStreamInterface` has been added to denote streamsthat contain additional metadata accessible via `getMetadata()`.`GuzzleHttp\Stream\StreamInterface::getMetadata` and`GuzzleHttp\Stream\StreamInterface::setMetadata` have been removed.## StreamRequestFactoryThe entire concept of the StreamRequestFactory has been removed. The way thiswas used in Guzzle 3 broke the actual interface of sending streaming requests(instead of getting back a Response, you got a StreamInterface). StreamingPHP requests are now implemented through the `GuzzleHttp\Adapter\StreamAdapter`.3.6 to 3.7----------### Deprecations- You can now enable E_USER_DEPRECATED warnings to see if you are using any deprecated methods.:```php\Guzzle\Common\Version::$emitWarnings = true;```The following APIs and options have been marked as deprecated:- Marked `Guzzle\Http\Message\Request::isResponseBodyRepeatable()` as deprecated. Use `$request->getResponseBody()->isRepeatable()` instead.- Marked `Guzzle\Http\Message\Request::canCache()` as deprecated. Use `Guzzle\Plugin\Cache\DefaultCanCacheStrategy->canCacheRequest()` instead.- Marked `Guzzle\Http\Message\Request::canCache()` as deprecated. Use `Guzzle\Plugin\Cache\DefaultCanCacheStrategy->canCacheRequest()` instead.- Marked `Guzzle\Http\Message\Request::setIsRedirect()` as deprecated. Use the HistoryPlugin instead.- Marked `Guzzle\Http\Message\Request::isRedirect()` as deprecated. Use the HistoryPlugin instead.- Marked `Guzzle\Cache\CacheAdapterFactory::factory()` as deprecated- Marked `Guzzle\Service\Client::enableMagicMethods()` as deprecated. Magic methods can no longer be disabled on a Guzzle\Service\Client.- Marked `Guzzle\Parser\Url\UrlParser` as deprecated. Just use PHP's `parse_url()` and percent encode your UTF-8.- Marked `Guzzle\Common\Collection::inject()` as deprecated.- Marked `Guzzle\Plugin\CurlAuth\CurlAuthPlugin` as deprecated. Use`$client->getConfig()->setPath('request.options/auth', array('user', 'pass', 'Basic|Digest|NTLM|Any'));` or`$client->setDefaultOption('auth', array('user', 'pass', 'Basic|Digest|NTLM|Any'));`3.7 introduces `request.options` as a parameter for a client configuration and as an optional argument to all creationalrequest methods. When paired with a client's configuration settings, these options allow you to specify default settingsfor various aspects of a request. Because these options make other previous configuration options redundant, severalconfiguration options and methods of a client and AbstractCommand have been deprecated.- Marked `Guzzle\Service\Client::getDefaultHeaders()` as deprecated. Use `$client->getDefaultOption('headers')`.- Marked `Guzzle\Service\Client::setDefaultHeaders()` as deprecated. Use `$client->setDefaultOption('headers/{header_name}', 'value')`.- Marked 'request.params' for `Guzzle\Http\Client` as deprecated. Use `$client->setDefaultOption('params/{param_name}', 'value')`- Marked 'command.headers', 'command.response_body' and 'command.on_complete' as deprecated for AbstractCommand. These will work through Guzzle 4.0$command = $client->getCommand('foo', array('command.headers' => array('Test' => '123'),'command.response_body' => '/path/to/file'));// Should be changed to:$command = $client->getCommand('foo', array('command.request_options' => array('headers' => array('Test' => '123'),'save_as' => '/path/to/file')));### Interface changesAdditions and changes (you will need to update any implementations or subclasses you may have created):- Added an `$options` argument to the end of the following methods of `Guzzle\Http\ClientInterface`:createRequest, head, delete, put, patch, post, options, prepareRequest- Added an `$options` argument to the end of `Guzzle\Http\Message\Request\RequestFactoryInterface::createRequest()`- Added an `applyOptions()` method to `Guzzle\Http\Message\Request\RequestFactoryInterface`- Changed `Guzzle\Http\ClientInterface::get($uri = null, $headers = null, $body = null)` to`Guzzle\Http\ClientInterface::get($uri = null, $headers = null, $options = array())`. You can still pass in aresource, string, or EntityBody into the $options parameter to specify the download location of the response.- Changed `Guzzle\Common\Collection::__construct($data)` to no longer accepts a null value for `$data` but adefault `array()`- Added `Guzzle\Stream\StreamInterface::isRepeatable`- Made `Guzzle\Http\Client::expandTemplate` and `getUriTemplate` protected methods.The following methods were removed from interfaces. All of these methods are still available in the concrete classesthat implement them, but you should update your code to use alternative methods:- Removed `Guzzle\Http\ClientInterface::setDefaultHeaders(). Use`$client->getConfig()->setPath('request.options/headers/{header_name}', 'value')`. or`$client->getConfig()->setPath('request.options/headers', array('header_name' => 'value'))` or`$client->setDefaultOption('headers/{header_name}', 'value')`. or`$client->setDefaultOption('headers', array('header_name' => 'value'))`.- Removed `Guzzle\Http\ClientInterface::getDefaultHeaders(). Use `$client->getConfig()->getPath('request.options/headers')`.- Removed `Guzzle\Http\ClientInterface::expandTemplate()`. This is an implementation detail.- Removed `Guzzle\Http\ClientInterface::setRequestFactory()`. This is an implementation detail.- Removed `Guzzle\Http\ClientInterface::getCurlMulti()`. This is a very specific implementation detail.- Removed `Guzzle\Http\Message\RequestInterface::canCache`. Use the CachePlugin.- Removed `Guzzle\Http\Message\RequestInterface::setIsRedirect`. Use the HistoryPlugin.- Removed `Guzzle\Http\Message\RequestInterface::isRedirect`. Use the HistoryPlugin.### Cache plugin breaking changes- CacheKeyProviderInterface and DefaultCacheKeyProvider are no longer used. All of this logic is handled in aCacheStorageInterface. These two objects and interface will be removed in a future version.- Always setting X-cache headers on cached responses- Default cache TTLs are now handled by the CacheStorageInterface of a CachePlugin- `CacheStorageInterface::cache($key, Response $response, $ttl = null)` has changed to `cache(RequestInterface$request, Response $response);`- `CacheStorageInterface::fetch($key)` has changed to `fetch(RequestInterface $request);`- `CacheStorageInterface::delete($key)` has changed to `delete(RequestInterface $request);`- Added `CacheStorageInterface::purge($url)`- `DefaultRevalidation::__construct(CacheKeyProviderInterface $cacheKey, CacheStorageInterface $cache, CachePlugin$plugin)` has changed to `DefaultRevalidation::__construct(CacheStorageInterface $cache,CanCacheStrategyInterface $canCache = null)`- Added `RevalidationInterface::shouldRevalidate(RequestInterface $request, Response $response)`3.5 to 3.6----------* Mixed casing of headers are now forced to be a single consistent casing across all values for that header.* Messages internally use a HeaderCollection object to delegate handling case-insensitive header resolution* Removed the whole changedHeader() function system of messages because all header changes now go through addHeader().For example, setHeader() first removes the header using unset on a HeaderCollection and then calls addHeader().Keeping the Host header and URL host in sync is now handled by overriding the addHeader method in Request.* Specific header implementations can be created for complex headers. When a message creates a header, it uses aHeaderFactory which can map specific headers to specific header classes. There is now a Link header andCacheControl header implementation.* Moved getLinks() from Response to just be used on a Link header object.If you previously relied on Guzzle\Http\Message\Header::raw(), then you will need to update your code to use theHeaderInterface (e.g. toArray(), getAll(), etc.).### Interface changes* Removed from interface: Guzzle\Http\ClientInterface::setUriTemplate* Removed from interface: Guzzle\Http\ClientInterface::setCurlMulti()* Removed Guzzle\Http\Message\Request::receivedRequestHeader() and implemented this functionality inGuzzle\Http\Curl\RequestMediator* Removed the optional $asString parameter from MessageInterface::getHeader(). Just cast the header to a string.* Removed the optional $tryChunkedTransfer option from Guzzle\Http\Message\EntityEnclosingRequestInterface* Removed the $asObjects argument from Guzzle\Http\Message\MessageInterface::getHeaders()### Removed deprecated functions* Removed Guzzle\Parser\ParserRegister::get(). Use getParser()* Removed Guzzle\Parser\ParserRegister::set(). Use registerParser().### Deprecations* The ability to case-insensitively search for header values* Guzzle\Http\Message\Header::hasExactHeader* Guzzle\Http\Message\Header::raw. Use getAll()* Deprecated cache control specific methods on Guzzle\Http\Message\AbstractMessage. Use the CacheControl header objectinstead.### Other changes* All response header helper functions return a string rather than mixing Header objects and strings inconsistently* Removed cURL blacklist support. This is no longer necessary now that Expect, Accept, etc. are managed by Guzzledirectly via interfaces* Removed the injecting of a request object onto a response object. The methods to get and set a request still existbut are a no-op until removed.* Most classes that used to require a `Guzzle\Service\Command\CommandInterface` typehint now request a`Guzzle\Service\Command\ArrayCommandInterface`.* Added `Guzzle\Http\Message\RequestInterface::startResponse()` to the RequestInterface to handle injecting a responseon a request while the request is still being transferred* `Guzzle\Service\Command\CommandInterface` now extends from ToArrayInterface and ArrayAccess3.3 to 3.4----------Base URLs of a client now follow the rules of https://tools.ietf.org/html/rfc3986#section-5.2.2 when merging URLs.3.2 to 3.3----------### Response::getEtag() quote stripping removed`Guzzle\Http\Message\Response::getEtag()` no longer strips quotes around the ETag response header### Removed `Guzzle\Http\Utils`The `Guzzle\Http\Utils` class was removed. This class was only used for testing.### Stream wrapper and type`Guzzle\Stream\Stream::getWrapper()` and `Guzzle\Stream\Stream::getStreamType()` are no longer converted to lowercase.### curl.emit_io became emit_ioEmitting IO events from a RequestMediator is now a parameter that must be set in a request's curl options using the'emit_io' key. This was previously set under a request's parameters using 'curl.emit_io'3.1 to 3.2----------### CurlMulti is no longer reused globallyBefore 3.2, the same CurlMulti object was reused globally for each client. This can cause issue where plugins addedto a single client can pollute requests dispatched from other clients.If you still wish to reuse the same CurlMulti object with each client, then you can add a listener to theServiceBuilder's `service_builder.create_client` event to inject a custom CurlMulti object into each client as it iscreated.```php$multi = new Guzzle\Http\Curl\CurlMulti();$builder = Guzzle\Service\Builder\ServiceBuilder::factory('/path/to/config.json');$builder->addListener('service_builder.create_client', function ($event) use ($multi) {$event['client']->setCurlMulti($multi);}});```### No default pathURLs no longer have a default path value of '/' if no path was specified.Before:```php$request = $client->get('http://www.foo.com');echo $request->getUrl();// >> http://www.foo.com/```After:```php$request = $client->get('http://www.foo.com');echo $request->getUrl();// >> http://www.foo.com```### Less verbose BadResponseExceptionThe exception message for `Guzzle\Http\Exception\BadResponseException` no longer contains the full HTTP request andresponse information. You can, however, get access to the request and response object by calling `getRequest()` or`getResponse()` on the exception object.### Query parameter aggregationMulti-valued query parameters are no longer aggregated using a callback function. `Guzzle\Http\Query` now has asetAggregator() method that accepts a `Guzzle\Http\QueryAggregator\QueryAggregatorInterface` object. This object isresponsible for handling the aggregation of multi-valued query string variables into a flattened hash.2.8 to 3.x----------### Guzzle\Service\InspectorChange `\Guzzle\Service\Inspector::fromConfig` to `\Guzzle\Common\Collection::fromConfig`**Before**```phpuse Guzzle\Service\Inspector;class YourClient extends \Guzzle\Service\Client{public static function factory($config = array()){$default = array();$required = array('base_url', 'username', 'api_key');$config = Inspector::fromConfig($config, $default, $required);$client = new self($config->get('base_url'),$config->get('username'),$config->get('api_key'));$client->setConfig($config);$client->setDescription(ServiceDescription::factory(__DIR__ . DIRECTORY_SEPARATOR . 'client.json'));return $client;}```**After**```phpuse Guzzle\Common\Collection;class YourClient extends \Guzzle\Service\Client{public static function factory($config = array()){$default = array();$required = array('base_url', 'username', 'api_key');$config = Collection::fromConfig($config, $default, $required);$client = new self($config->get('base_url'),$config->get('username'),$config->get('api_key'));$client->setConfig($config);$client->setDescription(ServiceDescription::factory(__DIR__ . DIRECTORY_SEPARATOR . 'client.json'));return $client;}```### Convert XML Service Descriptions to JSON**Before**```xml<?xml version="1.0" encoding="UTF-8"?><client><commands><!-- Groups --><command name="list_groups" method="GET" uri="groups.json"><doc>Get a list of groups</doc></command><command name="search_groups" method="GET" uri='search.json?query="{{query}} type:group"'><doc>Uses a search query to get a list of groups</doc><param name="query" type="string" required="true" /></command><command name="create_group" method="POST" uri="groups.json"><doc>Create a group</doc><param name="data" type="array" location="body" filters="json_encode" doc="Group JSON"/><param name="Content-Type" location="header" static="application/json"/></command><command name="delete_group" method="DELETE" uri="groups/{{id}}.json"><doc>Delete a group by ID</doc><param name="id" type="integer" required="true"/></command><command name="get_group" method="GET" uri="groups/{{id}}.json"><param name="id" type="integer" required="true"/></command><command name="update_group" method="PUT" uri="groups/{{id}}.json"><doc>Update a group</doc><param name="id" type="integer" required="true"/><param name="data" type="array" location="body" filters="json_encode" doc="Group JSON"/><param name="Content-Type" location="header" static="application/json"/></command></commands></client>```**After**```json{"name": "Zendesk REST API v2","apiVersion": "2012-12-31","description":"Provides access to Zendesk views, groups, tickets, ticket fields, and users","operations": {"list_groups": {"httpMethod":"GET","uri": "groups.json","summary": "Get a list of groups"},"search_groups":{"httpMethod":"GET","uri": "search.json?query=\"{query} type:group\"","summary": "Uses a search query to get a list of groups","parameters":{"query":{"location": "uri","description":"Zendesk Search Query","type": "string","required": true}}},"create_group": {"httpMethod":"POST","uri": "groups.json","summary": "Create a group","parameters":{"data": {"type": "array","location": "body","description":"Group JSON","filters": "json_encode","required": true},"Content-Type":{"type": "string","location":"header","static": "application/json"}}},"delete_group": {"httpMethod":"DELETE","uri": "groups/{id}.json","summary": "Delete a group","parameters":{"id":{"location": "uri","description":"Group to delete by ID","type": "integer","required": true}}},"get_group": {"httpMethod":"GET","uri": "groups/{id}.json","summary": "Get a ticket","parameters":{"id":{"location": "uri","description":"Group to get by ID","type": "integer","required": true}}},"update_group": {"httpMethod":"PUT","uri": "groups/{id}.json","summary": "Update a group","parameters":{"id": {"location": "uri","description":"Group to update by ID","type": "integer","required": true},"data": {"type": "array","location": "body","description":"Group JSON","filters": "json_encode","required": true},"Content-Type":{"type": "string","location":"header","static": "application/json"}}}}```### Guzzle\Service\Description\ServiceDescriptionCommands are now called Operations**Before**```phpuse Guzzle\Service\Description\ServiceDescription;$sd = new ServiceDescription();$sd->getCommands(); // @returns ApiCommandInterface[]$sd->hasCommand($name);$sd->getCommand($name); // @returns ApiCommandInterface|null$sd->addCommand($command); // @param ApiCommandInterface $command```**After**```phpuse Guzzle\Service\Description\ServiceDescription;$sd = new ServiceDescription();$sd->getOperations(); // @returns OperationInterface[]$sd->hasOperation($name);$sd->getOperation($name); // @returns OperationInterface|null$sd->addOperation($operation); // @param OperationInterface $operation```### Guzzle\Common\Inflection\InflectorNamespace is now `Guzzle\Inflection\Inflector`### Guzzle\Http\PluginNamespace is now `Guzzle\Plugin`. Many other changes occur within this namespace and are detailed in their own sections below.### Guzzle\Http\Plugin\LogPlugin and Guzzle\Common\LogNow `Guzzle\Plugin\Log\LogPlugin` and `Guzzle\Log` respectively.**Before**```phpuse Guzzle\Common\Log\ClosureLogAdapter;use Guzzle\Http\Plugin\LogPlugin;/** @var \Guzzle\Http\Client */$client;// $verbosity is an integer indicating desired message verbosity level$client->addSubscriber(new LogPlugin(new ClosureLogAdapter(function($m) { echo $m; }, $verbosity = LogPlugin::LOG_VERBOSE);```**After**```phpuse Guzzle\Log\ClosureLogAdapter;use Guzzle\Log\MessageFormatter;use Guzzle\Plugin\Log\LogPlugin;/** @var \Guzzle\Http\Client */$client;// $format is a string indicating desired message format -- @see MessageFormatter$client->addSubscriber(new LogPlugin(new ClosureLogAdapter(function($m) { echo $m; }, $format = MessageFormatter::DEBUG_FORMAT);```### Guzzle\Http\Plugin\CurlAuthPluginNow `Guzzle\Plugin\CurlAuth\CurlAuthPlugin`.### Guzzle\Http\Plugin\ExponentialBackoffPluginNow `Guzzle\Plugin\Backoff\BackoffPlugin`, and other changes.**Before**```phpuse Guzzle\Http\Plugin\ExponentialBackoffPlugin;$backoffPlugin = new ExponentialBackoffPlugin($maxRetries, array_merge(ExponentialBackoffPlugin::getDefaultFailureCodes(), array(429)));$client->addSubscriber($backoffPlugin);```**After**```phpuse Guzzle\Plugin\Backoff\BackoffPlugin;use Guzzle\Plugin\Backoff\HttpBackoffStrategy;// Use convenient factory method instead -- see implementation for ideas of what// you can do with chaining backoff strategies$backoffPlugin = BackoffPlugin::getExponentialBackoff($maxRetries, array_merge(HttpBackoffStrategy::getDefaultFailureCodes(), array(429)));$client->addSubscriber($backoffPlugin);```### Known Issues#### [BUG] Accept-Encoding header behavior changed unintentionally.(See #217) (Fixed in 09daeb8c666fb44499a0646d655a8ae36456575e)In version 2.8 setting the `Accept-Encoding` header would set the CURLOPT_ENCODING option, which permitted cURL toproperly handle gzip/deflate compressed responses from the server. In versions affected by this bug this does not happen.See issue #217 for a workaround, or use a version containing the fix.