request.rst 32 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667
  1. =====================
  2. Using Request objects
  3. =====================
  4. HTTP request messages
  5. ---------------------
  6. Request objects are all about building an HTTP message. Each part of an HTTP request message can be set individually
  7. using methods on the request object or set in bulk using the ``setUrl()`` method. Here's the format of an HTTP request
  8. with each part of the request referencing the method used to change it::
  9. PUT(a) /path(b)?query=123(c) HTTP/1.1(d)
  10. X-Header(e): header
  11. Content-Length(e): 4
  12. data(f)
  13. +-------------------------+---------------------------------------------------------------------------------+
  14. | a. **Method** | The request method can only be set when instantiating a request |
  15. +-------------------------+---------------------------------------------------------------------------------+
  16. | b. **Path** | ``$request->setPath('/path');`` |
  17. +-------------------------+---------------------------------------------------------------------------------+
  18. | c. **Query** | ``$request->getQuery()->set('query', '123');`` |
  19. +-------------------------+---------------------------------------------------------------------------------+
  20. | d. **Protocol version** | ``$request->setProtocolVersion('1.1');`` |
  21. +-------------------------+---------------------------------------------------------------------------------+
  22. | e. **Header** | ``$request->setHeader('X-Header', 'header');`` |
  23. +-------------------------+---------------------------------------------------------------------------------+
  24. | f. **Entity Body** | ``$request->setBody('data'); // Only available with PUT, POST, PATCH, DELETE`` |
  25. +-------------------------+---------------------------------------------------------------------------------+
  26. Creating requests with a client
  27. -------------------------------
  28. Client objects are responsible for creating HTTP request objects.
  29. GET requests
  30. ~~~~~~~~~~~~
  31. `GET requests <http://www.w3.org/Protocols/rfc2616/rfc2616-sec9.html#sec9.3>`_ are the most common form of HTTP
  32. requests. When you visit a website in your browser, the HTML of the website is downloaded using a GET request. GET
  33. requests are idempotent requests that are typically used to download content (an entity) identified by a request URL.
  34. .. code-block:: php
  35. use Guzzle\Http\Client;
  36. $client = new Client();
  37. // Create a request that has a query string and an X-Foo header
  38. $request = $client->get('http://www.amazon.com?a=1', array('X-Foo' => 'Bar'));
  39. // Send the request and get the response
  40. $response = $request->send();
  41. You can change where the body of a response is downloaded on any request using the
  42. ``$request->setResponseBody(string|EntityBodyInterface|resource)`` method of a request. You can also set the ``save_to``
  43. option of a request:
  44. .. code-block:: php
  45. // Send the response body to a file
  46. $request = $client->get('http://test.com', array(), array('save_to' => '/path/to/file'));
  47. // Send the response body to an fopen resource
  48. $request = $client->get('http://test.com', array(), array('save_to' => fopen('/path/to/file', 'w')));
  49. HEAD requests
  50. ~~~~~~~~~~~~~
  51. `HEAD requests <http://www.w3.org/Protocols/rfc2616/rfc2616-sec9.html#sec9.4>`_ work exactly like GET requests except
  52. that they do not actually download the response body (entity) of the response message. HEAD requests are useful for
  53. retrieving meta information about an entity identified by a Request-URI.
  54. .. code-block:: php
  55. $client = new Guzzle\Http\Client();
  56. $request = $client->head('http://www.amazon.com');
  57. $response = $request->send();
  58. echo $response->getContentLength();
  59. // >>> Will output the Content-Length header value
  60. DELETE requests
  61. ~~~~~~~~~~~~~~~
  62. A `DELETE method <http://www.w3.org/Protocols/rfc2616/rfc2616-sec9.html#sec9.7>`_ requests that the origin server
  63. delete the resource identified by the Request-URI.
  64. .. code-block:: php
  65. $client = new Guzzle\Http\Client();
  66. $request = $client->delete('http://example.com');
  67. $response = $request->send();
  68. POST requests
  69. ~~~~~~~~~~~~~
  70. While `POST requests <http://www.w3.org/Protocols/rfc2616/rfc2616-sec9.html#sec9.5>`_ can be used for a number of
  71. reasons, POST requests are often used when submitting HTML form data to a website. POST requests can include an entity
  72. body in the HTTP request.
  73. POST requests in Guzzle are sent with an ``application/x-www-form-urlencoded`` Content-Type header if POST fields are
  74. present but no files are being sent in the POST. If files are specified in the POST request, then the Content-Type
  75. header will become ``multipart/form-data``.
  76. The ``post()`` method of a client object accepts four arguments: the URL, optional headers, post fields, and an array of
  77. request options. To send files in the POST request, prepend the ``@`` symbol to the array value (just like you would if
  78. you were using the PHP ``curl_setopt`` function).
  79. Here's how to create a multipart/form-data POST request containing files and fields:
  80. .. code-block:: php
  81. $request = $client->post('http://httpbin.org/post', array(), array(
  82. 'custom_field' => 'my custom value',
  83. 'file_field' => '@/path/to/file.xml'
  84. ));
  85. $response = $request->send();
  86. .. note::
  87. Remember to **always** sanitize user input when sending POST requests:
  88. .. code-block:: php
  89. // Prevent users from accessing sensitive files by sanitizing input
  90. $_POST = array('firstname' => '@/etc/passwd');
  91. $request = $client->post('http://www.example.com', array(), array (
  92. 'firstname' => str_replace('@', '', $_POST['firstname'])
  93. ));
  94. You can alternatively build up the contents of a POST request.
  95. .. code-block:: php
  96. $request = $client->post('http://httpbin.org/post')
  97. ->setPostField('custom_field', 'my custom value')
  98. ->addPostFile('file', '/path/to/file.xml');
  99. $response = $request->send();
  100. Raw POST data
  101. ^^^^^^^^^^^^^
  102. POST requests can also contain raw POST data that is not related to HTML forms.
  103. .. code-block:: php
  104. $request = $client->post('http://httpbin.org/post', array(), 'this is the body');
  105. $response = $request->send();
  106. You can set the body of POST request using the ``setBody()`` method of the
  107. ``Guzzle\Http\Message\EntityEnclosingRequest`` object. This method accepts a string, a resource returned from
  108. ``fopen``, or a ``Guzzle\Http\EntityBodyInterface`` object.
  109. .. code-block:: php
  110. $request = $client->post('http://httpbin.org/post');
  111. // Set the body of the POST to stream the contents of /path/to/large_body.txt
  112. $request->setBody(fopen('/path/to/large_body.txt', 'r'));
  113. $response = $request->send();
  114. PUT requests
  115. ~~~~~~~~~~~~
  116. The `PUT method <http://www.w3.org/Protocols/rfc2616/rfc2616-sec9.html#sec9.6>`_ requests that the enclosed entity be
  117. stored under the supplied Request-URI. PUT requests are similar to POST requests in that they both can send an entity
  118. body in the request message.
  119. The body of a PUT request (any any ``Guzzle\Http\Message\EntityEnclosingRequestInterface`` object) is always stored as
  120. a ``Guzzle\Http\Message\EntityBodyInterface`` object. This allows a great deal of flexibility when sending data to a
  121. remote server. For example, you can stream the contents of a stream returned by fopen, stream the contents of a
  122. callback function, or simply send a string of data.
  123. .. code-block:: php
  124. $request = $client->put('http://httpbin.org/put', array(), 'this is the body');
  125. $response = $request->send();
  126. Just like with POST, PATH, and DELETE requests, you can set the body of a PUT request using the ``setBody()`` method.
  127. .. code-block:: php
  128. $request = $client->put('http://httpbin.org/put');
  129. $request->setBody(fopen('/path/to/large_body.txt', 'r'));
  130. $response = $request->send();
  131. PATCH requests
  132. ~~~~~~~~~~~~~~
  133. `PATCH requests <http://tools.ietf.org/html/rfc5789>`_ are used to modify a resource.
  134. .. code-block:: php
  135. $request = $client->patch('http://httpbin.org', array(), 'this is the body');
  136. $response = $request->send();
  137. OPTIONS requests
  138. ~~~~~~~~~~~~~~~~
  139. The `OPTIONS method <http://www.w3.org/Protocols/rfc2616/rfc2616-sec9.html#sec9.2>`_ represents a request for
  140. information about the communication options available on the request/response chain identified by the Request-URI.
  141. .. code-block:: php
  142. $request = $client->options('http://httpbin.org');
  143. $response = $request->send();
  144. // Check if the PUT method is supported by this resource
  145. var_export($response->isMethodAllows('PUT'));
  146. Custom requests
  147. ~~~~~~~~~~~~~~~
  148. You can create custom HTTP requests that use non-standard HTTP methods using the ``createRequest()`` method of a
  149. client object.
  150. .. code-block:: php
  151. $request = $client->createRequest('COPY', 'http://example.com/foo', array(
  152. 'Destination' => 'http://example.com/bar',
  153. 'Overwrite' => 'T'
  154. ));
  155. $response = $request->send();
  156. Query string parameters
  157. -----------------------
  158. Query string parameters of a request are owned by a request's ``Guzzle\Http\Query`` object that is accessible by
  159. calling ``$request->getQuery()``. The Query class extends from ``Guzzle\Common\Collection`` and allows you to set one
  160. or more query string parameters as key value pairs. You can set a parameter on a Query object using the
  161. ``set($key, $value)`` method or access the query string object like an associative array. Any previously specified
  162. value for a key will be overwritten when using ``set()``. Use ``add($key, $value)`` to add a value to query string
  163. object, and in the event of a collision with an existing value at a specific key, the value will be converted to an
  164. array that contains all of the previously set values.
  165. .. code-block:: php
  166. $request = new Guzzle\Http\Message\Request('GET', 'http://www.example.com?foo=bar&abc=123');
  167. $query = $request->getQuery();
  168. echo "{$query}\n";
  169. // >>> foo=bar&abc=123
  170. $query->remove('abc');
  171. echo "{$query}\n";
  172. // >>> foo=bar
  173. $query->set('foo', 'baz');
  174. echo "{$query}\n";
  175. // >>> foo=baz
  176. $query->add('foo', 'bar');
  177. echo "{$query}\n";
  178. // >>> foo%5B0%5D=baz&foo%5B1%5D=bar
  179. Whoah! What happened there? When ``foo=bar`` was added to the existing ``foo=baz`` query string parameter, the
  180. aggregator associated with the Query object was used to help convert multi-value query string parameters into a string.
  181. Let's disable URL-encoding to better see what's happening.
  182. .. code-block:: php
  183. $query->useUrlEncoding(false);
  184. echo "{$query}\n";
  185. // >>> foo[0]=baz&foo[1]=bar
  186. .. note::
  187. URL encoding can be disabled by passing false, enabled by passing true, set to use RFC 1738 by passing
  188. ``Query::FORM_URLENCODED`` (internally uses PHP's ``urlencode`` function), or set to RFC 3986 by passing
  189. ``Query::RFC_3986`` (this is the default and internally uses PHP's ``rawurlencode`` function).
  190. As you can see, the multiple values were converted into query string parameters following the default PHP convention of
  191. adding numerically indexed square bracket suffixes to each key (``foo[0]=baz&foo[1]=bar``). The strategy used to convert
  192. multi-value parameters into a string can be customized using the ``setAggregator()`` method of the Query class. Guzzle
  193. ships with the following query string aggregators by default:
  194. 1. ``Guzzle\Http\QueryAggregator\PhpAggregator``: Aggregates using PHP style brackets (e.g. ``foo[0]=baz&foo[1]=bar``)
  195. 2. ``Guzzle\Http\QueryAggregator\DuplicateAggregator``: Performs no aggregation and allows for key value pairs to be
  196. repeated in a URL (e.g. ``foo=baz&foo=bar``)
  197. 3. ``Guzzle\Http\QueryAggregator\CommaAggregator``: Aggregates using commas (e.g. ``foo=baz,bar``)
  198. .. _http-message-headers:
  199. HTTP Message Headers
  200. --------------------
  201. HTTP message headers are case insensitive, multiple occurrences of any header can be present in an HTTP message
  202. (whether it's valid or not), and some servers require specific casing of particular headers. Because of this, request
  203. and response headers are stored in ``Guzzle\Http\Message\Header`` objects. The Header object can be cast as a string,
  204. counted, or iterated to retrieve each value from the header. Casting a Header object to a string will return all of
  205. the header values concatenated together using a glue string (typically ", ").
  206. A request (and response) object have several methods that allow you to retrieve and modify headers.
  207. * ``getHeaders()``: Get all of the headers of a message as a ``Guzzle\Http\Message\Header\HeaderCollection`` object.
  208. * ``getHeader($header)``: Get a specific header from a message. If the header exists, you'll get a
  209. ``Guzzle\Http\Message\Header`` object. If the header does not exist, this methods returns ``null``.
  210. * ``hasHeader($header)``: Returns true or false based on if the message has a particular header.
  211. * ``setHeader($header, $value)``: Set a header value and overwrite any previously set value for this header.
  212. * ``addHeader($header, $value)``: Add a header with a particular name. If a previous value was already set by the same,
  213. then the header will contain multiple values.
  214. * ``removeHeader($header)``: Remove a header by name from the message.
  215. .. code-block:: php
  216. $request = new Request('GET', 'http://httpbin.com/cookies');
  217. // addHeader will set and append to any existing header values
  218. $request->addHeader('Foo', 'bar');
  219. $request->addHeader('foo', 'baz');
  220. // setHeader overwrites any existing values
  221. $request->setHeader('Test', '123');
  222. // Request headers can be cast as a string
  223. echo $request->getHeader('Foo');
  224. // >>> bar, baz
  225. echo $request->getHeader('Test');
  226. // >>> 123
  227. // You can count the number of headers of a particular case insensitive name
  228. echo count($request->getHeader('foO'));
  229. // >>> 2
  230. // You can iterate over Header objects
  231. foreach ($request->getHeader('foo') as $header) {
  232. echo $header . "\n";
  233. }
  234. // You can get all of the request headers as a Guzzle\Http\Message\Header\HeaderCollection object
  235. $headers = $request->getHeaders();
  236. // Missing headers return NULL
  237. var_export($request->getHeader('Missing'));
  238. // >>> null
  239. // You can see all of the different variations of a header by calling raw() on the Header
  240. var_export($request->getHeader('foo')->raw());
  241. Setting the body of a request
  242. -----------------------------
  243. Requests that can send a body (e.g. PUT, POST, DELETE, PATCH) are instances of
  244. ``Guzzle\Http\Message\EntityEnclosingRequestInterface``. Entity enclosing requests contain several methods that allow
  245. you to specify the body to send with a request.
  246. Use the ``setBody()`` method of a request to set the body that will be sent with a request. This method accepts a
  247. string, a resource returned by ``fopen()``, an array, or an instance of ``Guzzle\Http\EntityBodyInterface``. The body
  248. will then be streamed from the underlying ``EntityBodyInterface`` object owned by the request. When setting the body
  249. of the request, you can optionally specify a Content-Type header and whether or not to force the request to use
  250. chunked Transfer-Encoding.
  251. .. code-block:: php
  252. $request = $client->put('/user.json');
  253. $request->setBody('{"foo":"baz"}', 'application/json');
  254. Content-Type header
  255. ~~~~~~~~~~~~~~~~~~~
  256. Guzzle will automatically add a Content-Type header to a request if the Content-Type can be guessed based on the file
  257. extension of the payload being sent or the file extension present in the path of a request.
  258. .. code-block:: php
  259. $request = $client->put('/user.json', array(), '{"foo":"bar"}');
  260. // The Content-Type was guessed based on the path of the request
  261. echo $request->getHeader('Content-Type');
  262. // >>> application/json
  263. $request = $client->put('/user.json');
  264. $request->setBody(fopen('/tmp/user_data.json', 'r'));
  265. // The Content-Type was guessed based on the path of the entity body
  266. echo $request->getHeader('Content-Type');
  267. // >>> application/json
  268. Transfer-Encoding: chunked header
  269. ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  270. When sending HTTP requests that contain a payload, you must let the remote server know how to determine when the entire
  271. message has been sent. This usually is done by supplying a ``Content-Length`` header that tells the origin server the
  272. size of the body that is to be sent. In some cases, the size of the payload being sent in a request cannot be known
  273. before initiating the transfer. In these cases (when using HTTP/1.1), you can use the ``Transfer-Encoding: chunked``
  274. header.
  275. If the Content-Length cannot be determined (i.e. using a PHP ``http://`` stream), then Guzzle will automatically add
  276. the ``Transfer-Encoding: chunked`` header to the request.
  277. .. code-block:: php
  278. $request = $client->put('/user.json');
  279. $request->setBody(fopen('http://httpbin.org/get', 'r'));
  280. // The Content-Length could not be determined
  281. echo $request->getHeader('Transfer-Encoding');
  282. // >>> chunked
  283. See :doc:`/http-client/entity-bodies` for more information on entity bodies.
  284. Expect: 100-Continue header
  285. ~~~~~~~~~~~~~~~~~~~~~~~~~~~
  286. The ``Expect: 100-Continue`` header is used to help a client prevent sending a large payload to a server that will
  287. reject the request. This allows clients to fail fast rather than waste bandwidth sending an erroneous payload. Guzzle
  288. will automatically add the ``Expect: 100-Continue`` header to a request when the size of the payload exceeds 1MB or if
  289. the body of the request is not seekable (this helps to prevent errors when a non-seekable body request is redirected).
  290. .. note::
  291. If you find that your larger requests are taking too long to complete, you should first check if the
  292. ``Expect: 100-Continue`` header is being sent with the request. Some servers do not respond well to this header,
  293. which causes cURL to sleep for `1 second <http://curl.haxx.se/mail/lib-2010-01/0182.html>`_.
  294. POST fields and files
  295. ~~~~~~~~~~~~~~~~~~~~~
  296. Any entity enclosing request can send POST style fields and files. This includes POST, PUT, PATCH, and DELETE requests.
  297. Any request that has set POST fields or files will use cURL's POST message functionality.
  298. .. code-block:: php
  299. $request = $client->post('/post');
  300. // Set an overwrite any previously specified value
  301. $request->setPostField('foo', 'bar');
  302. // Append a value to any existing values
  303. $request->getPostFields()->add('foo', 'baz');
  304. // Remove a POST field by name
  305. $request->removePostField('fizz');
  306. // Add a file to upload (forces multipart/form-data)
  307. $request->addPostFile('my_file', '/path/to/file', 'plain/text');
  308. // Remove a POST file by POST key name
  309. $request->removePostFile('my_other_file');
  310. .. tip::
  311. Adding a large number of POST fields to a POST request is faster if you use the ``addPostFields()`` method so that
  312. you can add and process multiple fields with a single call. Adding multiple POST files is also faster using
  313. ``addPostFiles()``.
  314. Working with cookies
  315. --------------------
  316. Cookies can be modified and retrieved from a request using the following methods:
  317. .. code-block:: php
  318. $request->addCookie($name, $value);
  319. $request->removeCookie($name);
  320. $value = $request->getCookie($name);
  321. $valueArray = $request->getCookies();
  322. Use the :doc:`cookie plugin </plugins/cookie-plugin>` if you need to reuse cookies between requests.
  323. .. _request-set-response-body:
  324. Changing where a response is downloaded
  325. ----------------------------------------
  326. When a request is sent, the body of the response will be stored in a PHP temp stream by default. You can change the
  327. location in which the response will be downloaded using ``$request->setResponseBody($body)`` or the ``save_to`` request
  328. option. This can be useful for downloading the contents of a URL to a specific file.
  329. Here's an example of using request options:
  330. .. code-block:: php
  331. $request = $this->client->get('http://example.com/large.mov', array(), array(
  332. 'save_to' => '/tmp/large_file.mov'
  333. ));
  334. $request->send();
  335. var_export(file_exists('/tmp/large_file.mov'));
  336. // >>> true
  337. Here's an example of using ``setResponseBody()``:
  338. .. code-block:: php
  339. $body = fopen('/tmp/large_file.mov', 'w');
  340. $request = $this->client->get('http://example.com/large.mov');
  341. $request->setResponseBody($body);
  342. // You can more easily specify the name of a file to save the contents
  343. // of the response to by passing a string to ``setResponseBody()``.
  344. $request = $this->client->get('http://example.com/large.mov');
  345. $request->setResponseBody('/tmp/large_file.mov');
  346. Custom cURL options
  347. -------------------
  348. Most of the functionality implemented in the libcurl bindings has been simplified and abstracted by Guzzle. Developers
  349. who need access to `cURL specific functionality <http://www.php.net/curl_setopt>`_ can still add cURL handle
  350. specific behavior to Guzzle HTTP requests by modifying the cURL options collection of a request:
  351. .. code-block:: php
  352. $request->getCurlOptions()->set(CURLOPT_LOW_SPEED_LIMIT, 200);
  353. Other special options that can be set in the ``curl.options`` array include:
  354. +-------------------------+---------------------------------------------------------------------------------+
  355. | debug | Adds verbose cURL output to a temp stream owned by the cURL handle object |
  356. +-------------------------+---------------------------------------------------------------------------------+
  357. | progress | Instructs cURL to emit events when IO events occur. This allows you to be |
  358. | | notified when bytes are transferred over the wire by subscribing to a request's |
  359. | | ``curl.callback.read``, ``curl.callback.write``, and ``curl.callback.progress`` |
  360. | | events. |
  361. +-------------------------+---------------------------------------------------------------------------------+
  362. Request options
  363. ---------------
  364. Requests options can be specified when creating a request or in the ``request.options`` parameter of a client. These
  365. options can control various aspects of a request including: headers to send, query string data, where the response
  366. should be downloaded, proxies, auth, etc.
  367. .. code-block:: php
  368. $request = $client->get($url, $headers, array('proxy' => 'http://proxy.com'));
  369. See :ref:`Request options <request-options>` for more information.
  370. Working with errors
  371. -------------------
  372. HTTP errors
  373. ~~~~~~~~~~~
  374. Requests that receive a 4xx or 5xx response will throw a ``Guzzle\Http\Exception\BadResponseException``. More
  375. specifically, 4xx errors throw a ``Guzzle\Http\Exception\ClientErrorResponseException``, and 5xx errors throw a
  376. ``Guzzle\Http\Exception\ServerErrorResponseException``. You can catch the specific exceptions or just catch the
  377. BadResponseException to deal with either type of error. Here's an example of catching a generic BadResponseException:
  378. .. code-block:: php
  379. try {
  380. $response = $client->get('/not_found.xml')->send();
  381. } catch (Guzzle\Http\Exception\BadResponseException $e) {
  382. echo 'Uh oh! ' . $e->getMessage();
  383. echo 'HTTP request URL: ' . $e->getRequest()->getUrl() . "\n";
  384. echo 'HTTP request: ' . $e->getRequest() . "\n";
  385. echo 'HTTP response status: ' . $e->getResponse()->getStatusCode() . "\n";
  386. echo 'HTTP response: ' . $e->getResponse() . "\n";
  387. }
  388. Throwing an exception when a 4xx or 5xx response is encountered is the default behavior of Guzzle requests. This
  389. behavior can be overridden by adding an event listener with a higher priority than -255 that stops event propagation.
  390. You can subscribe to ``request.error`` to receive notifications any time an unsuccessful response is received.
  391. You can change the response that will be associated with the request by calling ``setResponse()`` on the
  392. ``$event['request']`` object passed into your listener, or by changing the ``$event['response']`` value of the
  393. ``Guzzle\Common\Event`` object that is passed to your listener. Transparently changing the response associated with a
  394. request by modifying the event allows you to retry failed requests without complicating the code that uses the client.
  395. This might be useful for sending requests to a web service that has expiring auth tokens. When a response shows that
  396. your token has expired, you can get a new token, retry the request with the new token, and return the successful
  397. response to the user.
  398. Here's an example of retrying a request using updated authorization credentials when a 401 response is received,
  399. overriding the response of the original request with the new response, and still allowing the default exception
  400. behavior to be called when other non-200 response status codes are encountered:
  401. .. code-block:: php
  402. // Add custom error handling to any request created by this client
  403. $client->getEventDispatcher()->addListener('request.error', function(Event $event) {
  404. if ($event['response']->getStatusCode() == 401) {
  405. $newRequest = $event['request']->clone();
  406. $newRequest->setHeader('X-Auth-Header', MyApplication::getNewAuthToken());
  407. $newResponse = $newRequest->send();
  408. // Set the response object of the request without firing more events
  409. $event['response'] = $newResponse;
  410. // You can also change the response and fire the normal chain of
  411. // events by calling $event['request']->setResponse($newResponse);
  412. // Stop other events from firing when you override 401 responses
  413. $event->stopPropagation();
  414. }
  415. });
  416. cURL errors
  417. ~~~~~~~~~~~
  418. Connection problems and cURL specific errors can also occur when transferring requests using Guzzle. When Guzzle
  419. encounters cURL specific errors while transferring a single request, a ``Guzzle\Http\Exception\CurlException`` is
  420. thrown with an informative error message and access to the cURL error message.
  421. A ``Guzzle\Http\Exception\MultiTransferException`` exception is thrown when a cURL specific error occurs while
  422. transferring multiple requests in parallel. You can then iterate over all of the exceptions encountered during the
  423. transfer.
  424. Plugins and events
  425. ------------------
  426. Guzzle request objects expose various events that allow you to hook in custom logic. A request object owns a
  427. ``Symfony\Component\EventDispatcher\EventDispatcher`` object that can be accessed by calling
  428. ``$request->getEventDispatcher()``. You can use the event dispatcher to add listeners (a simple callback function) or
  429. event subscribers (classes that listen to specific events of a dispatcher). You can add event subscribers to a request
  430. directly by just calling ``$request->addSubscriber($mySubscriber);``.
  431. .. _request-events:
  432. Events emitted from a request
  433. ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  434. A ``Guzzle\Http\Message\Request`` and ``Guzzle\Http\Message\EntityEnclosingRequest`` object emit the following events:
  435. +------------------------------+--------------------------------------------+------------------------------------------+
  436. | Event name | Description | Event data |
  437. +==============================+============================================+==========================================+
  438. | request.before_send | About to send request | * request: Request to be sent |
  439. +------------------------------+--------------------------------------------+------------------------------------------+
  440. | request.sent | Sent the request | * request: Request that was sent |
  441. | | | * response: Received response |
  442. +------------------------------+--------------------------------------------+------------------------------------------+
  443. | request.complete | Completed a full HTTP transaction | * request: Request that was sent |
  444. | | | * response: Received response |
  445. +------------------------------+--------------------------------------------+------------------------------------------+
  446. | request.success | Completed a successful request | * request: Request that was sent |
  447. | | | * response: Received response |
  448. +------------------------------+--------------------------------------------+------------------------------------------+
  449. | request.error | Completed an unsuccessful request | * request: Request that was sent |
  450. | | | * response: Received response |
  451. +------------------------------+--------------------------------------------+------------------------------------------+
  452. | request.exception | An unsuccessful response was | * request: Request |
  453. | | received. | * response: Received response |
  454. | | | * exception: BadResponseException |
  455. +------------------------------+--------------------------------------------+------------------------------------------+
  456. | request.receive.status_line | Received the start of a response | * line: Full response start line |
  457. | | | * status_code: Status code |
  458. | | | * reason_phrase: Reason phrase |
  459. | | | * previous_response: (e.g. redirect) |
  460. +------------------------------+--------------------------------------------+------------------------------------------+
  461. | curl.callback.progress | cURL progress event (only dispatched when | * handle: CurlHandle |
  462. | | ``emit_io`` is set on a request's curl | * download_size: Total download size |
  463. | | options) | * downloaded: Bytes downloaded |
  464. | | | * upload_size: Total upload bytes |
  465. | | | * uploaded: Bytes uploaded |
  466. +------------------------------+--------------------------------------------+------------------------------------------+
  467. | curl.callback.write | cURL event called when data is written to | * request: Request |
  468. | | an outgoing stream | * write: Data being written |
  469. +------------------------------+--------------------------------------------+------------------------------------------+
  470. | curl.callback.read | cURL event called when data is written to | * request: Request |
  471. | | an incoming stream | * read: Data being read |
  472. +------------------------------+--------------------------------------------+------------------------------------------+
  473. Creating a request event listener
  474. ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  475. Here's an example that listens to the ``request.complete`` event of a request and prints the request and response.
  476. .. code-block:: php
  477. use Guzzle\Common\Event;
  478. $request = $client->get('http://www.google.com');
  479. // Echo out the response that was received
  480. $request->getEventDispatcher()->addListener('request.complete', function (Event $e) {
  481. echo $e['request'] . "\n\n";
  482. echo $e['response'];
  483. });