Working with errors¶

HTTP errors

Requests that receive a 4xx or 5xx response will throw a Guzzle\Http\Exception\BadResponseException. More specifically, 4xx errors throw a Guzzle\Http\Exception\ClientErrorResponseException, and 5xx errors throw a Guzzle\Http\Exception\ServerErrorResponseException. You can catch the specific exceptions or just catch the BadResponseException to deal with either type of error. Here’s an example of catching a generic BadResponseException:

try {
    $response = $client->get('/not_found.xml')->send();
} catch (Guzzle\Http\Exception\BadResponseException $e) {
    echo 'Uh oh! ' . $e->getMessage();
    echo 'HTTP request URL: ' . $e->getRequest()->getUrl() . "\n";
    echo 'HTTP request: ' . $e->getRequest() . "\n";
    echo 'HTTP response status: ' . $e->getResponse()->getStatusCode() . "\n";
    echo 'HTTP response: ' . $e->getResponse() . "\n";

Throwing an exception when a 4xx or 5xx response is encountered is the default behavior of Guzzle requests. This behavior can be overridden by adding an event listener with a higher priority than -255 that stops event propagation. You can subscribe to request.error to receive notifications any time an unsuccessful response is received.

You can change the response that will be associated with the request by calling setResponse() on the $event object passed into your listener, or by changing the $event value of the Guzzle\Common\Event object that is passed to your listener. Transparently changing the response associated with a request by modifying the event allows you to retry failed requests without complicating the code that uses the client. This might be useful for sending requests to a web service that has expiring auth tokens. When a response shows that your token has expired, you can get a new token, retry the request with the new token, and return the successful response to the user.

Here’s an example of retrying a request using updated authorization credentials when a 401 response is received, overriding the response of the original request with the new response, and still allowing the default exception behavior to be called when other non-200 response status codes are encountered:

// Add custom error handling to any request created by this client
$client->getEventDispatcher()->addListener('request.error', function(Event $event) {

    if ($event'response'->getStatusCode() == 401) {

        $newRequest = $event'request'->clone();
        $newRequest->setHeader('X-Auth-Header', MyApplication::getNewAuthToken());
        $newResponse = $newRequest->send();

        // Set the response object of the request without firing more events
        $event'response' = $newResponse;

        // You can also change the response and fire the normal chain of
        // events by calling $event->setResponse($newResponse);

        // Stop other events from firing when you override 401 responses





GET /path/to/resource.html HTTP/1.0
GET http://domain.name/path/to/resource.html HTTP/1.0



Host в HTTP/1.1

GET http://domain.name/path/to/resource.html HTTP/1.1
Host: любой_текст_тут
GET /path/to/resource.html HTTP/1.1
Host: domain.name


документации по mod_rewrite

GET http://domain.name/path/to/resource.html HTTP/1.1
Host: www.domain.name

%{REQUEST_URI}$_SERVERдокументации к PHP

HTTP Message Headers¶

HTTP message headers are case insensitive, multiple occurrences of any header can be present in an HTTP message (whether it’s valid or not), and some servers require specific casing of particular headers. Because of this, request and response headers are stored in Guzzle\Http\Message\Header objects. The Header object can be cast as a string, counted, or iterated to retrieve each value from the header. Casting a Header object to a string will return all of the header values concatenated together using a glue string (typically «, «).

A request (and response) object have several methods that allow you to retrieve and modify headers.

  • getHeaders(): Get all of the headers of a message as a Guzzle\Http\Message\Header\HeaderCollection object.
  • getHeader($header): Get a specific header from a message. If the header exists, you’ll get a Guzzle\Http\Message\Header object. If the header does not exist, this methods returns null.
  • hasHeader($header): Returns true or false based on if the message has a particular header.
  • setHeader($header, $value): Set a header value and overwrite any previously set value for this header.
  • addHeader($header, $value): Add a header with a particular name. If a previous value was already set by the same, then the header will contain multiple values.
  • removeHeader($header): Remove a header by name from the message.


// Make sure you have Composer's autoload file included
require 'vendor/autoload.php';

// Create a connection, once only.
$config = [
            'driver'    => 'mysql', // Db driver
            'host'      => 'localhost',
            'database'  => 'your-database',
            'username'  => 'root',
            'password'  => 'your-password',
            'charset'   => 'utf8', // Optional
            'collation' => 'utf8_unicode_ci', // Optional
            'prefix'    => 'cb_', // Table prefix, optional
            'options'   => [ // PDO constructor options, optional
                PDO::ATTR_TIMEOUT => 5,
                PDO::ATTR_EMULATE_PREPARES => false,

new \Pixie\Connection('mysql', $config, 'QB');

Simple Query:

The query below returns the row where id = 3, null if no rows.

$row = QB::table('my_table')->find(3);

Full Queries:

$query = QB::table('my_table')->where('name', '=', 'Sana');

// Get result

Query Events:

After the code below, every time a select query occurs on table, it will add this where criteria, so banned users don’t get access.

QB::registerEvent('before-select', 'users', function($qb)
    $qb->where('status', '!=', 'banned');

There are many advanced options which are documented below. Sold? Let’s install.

Built-in web server


This web server was designed to aid application development. It may also be useful for testing purposes or for application demonstrations that are run in controlled environments. It is not intended to be a full-featured web server. It should not be used on a public network.

As of PHP 5.4.0, the CLI SAPI provides a built-in web server.

The web server runs only one single-threaded process, so PHP applications will stall if a request is blocked.

URI requests are served from the current working directory where PHP was started, unless the -t option is used to specify an explicit document root. If a URI request does not specify a file, then either index.php or index.html in the given directory are returned. If neither file exists, the lookup for index.php and index.html will be continued in the parent directory and so on until one is found or the document root has been reached. If an index.php or index.html is found, it is returned and $_SERVER is set to the trailing part of the URI. Otherwise a 404 response code is returned.

If a PHP file is given on the command line when the web server is started it is treated as a «router» script. The script is run at the start of each HTTP request. If this script returns , then the requested resource is returned as-is. Otherwise the script’s output is returned to the browser.

Standard MIME types are returned for files with extensions: .3gp, .apk, .avi, .bmp, .css, .csv, .doc, .docx, .flac, .gif, .gz, .gzip, .htm, .html, .ics, .jpe, .jpeg, .jpg, .js, .kml, .kmz, .m4a, .mov, .mp3, .mp4, .mpeg, .mpg, .odp, .ods, .odt, .oga, .ogg, .ogv, .pdf, .pdf, .png, .pps, .pptx, .qt, .svg, .swf, .tar, .text, .tif, .txt, .wav, .webm, .wmv, .xls, .xlsx, .xml, .xsl, .xsd, and .zip.

Changelog: Supported MIME Types (file extensions)
Version Description
5.5.12 .xml, .xsl, and .xsd
5.5.7 .3gp, .apk, .avi, .bmp, .csv, .doc, .docx, .flac, .gz, .gzip, .ics, .kml, .kmz, .m4a, .mp3, .mp4, .mpg, .mpeg, .mov, .odp, .ods, .odt, .oga, .pdf, .pptx, .pps, .qt, .swf, .tar, .text, .tif, .wav, .wmv, .xls, .xlsx, and .zip
5.5.5 .pdf
5.4.11 .ogg, .ogv, and .webm
5.4.4 .htm and .svg
Version Description
7.4.0 You can configure the built-in webserver to fork multiple workers in order to test code that requires multiple concurrent requests to the built-in webserver. Set the PHP_CLI_SERVER_WORKERS environment variable to the number of desired workers before starting the server. This is not supported on Windows.

Example #1 Starting the web server

$ cd ~/public_html
$ php -S localhost:8000

The terminal will show:

PHP 5.4.0 Development Server started at Thu Jul 21 10:43:28 2011
Listening on localhost:8000
Document root is /home/me/public_html
Press Ctrl-C to quit

After URI requests for http://localhost:8000/ and http://localhost:8000/myscript.html the terminal will show something similar to:

PHP 5.4.0 Development Server started at Thu Jul 21 10:43:28 2011
Listening on localhost:8000
Document root is /home/me/public_html
Press Ctrl-C to quit.
 ::1:39144 GET /favicon.ico - Request read
 ::1:39146 GET / - Request read
 ::1:39147 GET /favicon.ico - Request read
 ::1:39148 GET /myscript.html - Request read
 ::1:39149 GET /favicon.ico - Request read

Note that prior to PHP 7.4.0, symlinked statical resources have not been accessible on Windows, unless the router script would handle these.

Example #2 Starting with a specific document root directory

$ cd ~/public_html
$ php -S localhost:8000 -t foo/

The terminal will show:

PHP 5.4.0 Development Server started at Thu Jul 21 10:50:26 2011
Listening on localhost:8000
Document root is /home/me/public_html/foo
Press Ctrl-C to quit

Example #3 Using a Router Script

In this example, requests for images will display them, but requests for HTML files will display «Welcome to PHP»:

$ php -S localhost:8000 router.php

Example #4 Checking for CLI Web Server Use

To reuse a framework router script during development with the CLI web server and later also with a production web server:

$ php -S localhost:8000 router.php

Example #5 Handling Unsupported File Types

If you need to serve a static resource whose MIME type is not handled by the CLI web server, use:

$ php -S localhost:8000 router.php

Example #6 Accessing the CLI Web Server From Remote Machines

You can make the web server accessible on port 8000 to any interface with:

$ php -S

Plugins and events¶

Guzzle request objects expose various events that allow you to hook in custom logic. A request object owns a Symfony\Component\EventDispatcher\EventDispatcher object that can be accessed by calling $request->getEventDispatcher(). You can use the event dispatcher to add listeners (a simple callback function) or event subscribers (classes that listen to specific events of a dispatcher). You can add event subscribers to a request directly by just calling $request->addSubscriber($mySubscriber);.

Events emitted from a request

A Guzzle\Http\Message\Request and Guzzle\Http\Message\EntityEnclosingRequest object emit the following events:

Event name Description Event data
request.before_send About to send request
request.sent Sent the request
  • request: Request that was sent
  • response: Received response
request.complete Completed a full HTTP transaction
  • request: Request that was sent
  • response: Received response
request.success Completed a successful request
  • request: Request that was sent
  • response: Received response
request.error Completed an unsuccessful request
  • request: Request that was sent
  • response: Received response
request.exception An unsuccessful response was received.
  • request: Request
  • response: Received response
  • exception: BadResponseException
request.receive.status_line Received the start of a response
  • line: Full response start line
  • status_code: Status code
  • reason_phrase: Reason phrase
  • previous_response: (e.g. redirect)
curl.callback.progress cURL progress event (only dispatched when emit_io is set on a request’s curl options)
  • handle: CurlHandle
  • download_size: Total download size
  • downloaded: Bytes downloaded
  • upload_size: Total upload bytes
  • uploaded: Bytes uploaded
curl.callback.write cURL event called when data is written to an outgoing stream
  • request: Request
  • write: Data being written
curl.callback.read cURL event called when data is written to an incoming stream
  • request: Request
  • read: Data being read

Creating a request event listener

Here’s an example that listens to the request.complete event of a request and prints the request and response.

use Guzzle\Common\Event;

$request = $client->get('http://www.google.com');

// Echo out the response that was received
$request->getEventDispatcher()->addListener('request.complete', function (Event $e) {
    echo $e'request' . "\n\n";
    echo $e'response'];

Changing where a response is downloaded¶

When a request is sent, the body of the response will be stored in a PHP temp stream by default. You can change the location in which the response will be downloaded using $request->setResponseBody($body) or the save_to request option. This can be useful for downloading the contents of a URL to a specific file.

Here’s an example of using request options:

$request = $this->client->get('http://example.com/large.mov', array(), array(
    'save_to' => '/tmp/large_file.mov'
// >>> true

Here’s an example of using setResponseBody():

2. The Builder ↑

The SQL Query Builder allows to generate complex SQL queries standard using the dialect (default) and the dialect, that extends the dialect.

2.1. Generic Builder

The Generic Query Builder is the default builder for this class and writes standard SQL-2003.

All column aliases are escaped using the sign by default.


use NilPortugues\Sql\QueryBuilder\Builder\GenericBuilder;

$builder = new GenericBuilder(); 

$query = $builder->select()->setTable('user');    

echo $builder->write($query);    
SELECT user.* FROM user

2.2. MySQL Builder

The MySQL Query Builder has its own class, that inherits from the SQL-2003 builder. All columns will be wrapped with the tilde ` sign.

All table and column aliases are escaped using the tilde sign by default.


use NilPortugues\Sql\QueryBuilder\Builder\MySqlBuilder;

$builder = new MySqlBuilder(); 

$query = $builder->select()->setTable('user'); 

echo $builder->write($query);    
SELECT user.* FROM `user` 

2.3. Human Readable Output

Both Generic and MySQL Query Builder can write complex SQL queries.

Every developer out there needs at some point revising the output of a complicated query, the SQL Query Builder includes a human-friendly output method, and therefore the method is there to aid the developer when need.

Keep in mind is to be avoided at all cost in production mode as it adds unneeded overhead due to parsing and re-formatting of the generated statement.


use NilPortugues\Sql\QueryBuilder\Builder\GenericBuilder;

$builder = new GenericBuilder(); 

$query = $builder->select()->setTable('user');  

echo $builder->writeFormatted($query);    


Query is a package for querying julia data sources. It can filter, project, join and group data from any iterable data source, including all the sources supported in IterableTables.jl. One can for example query any of the following data sources: any array, DataFrames, DataStreams (including CSV, Feather, SQLite, ODBC), DataTables, IndexedTables, TimeSeries, Temporal, TypedTables and DifferentialEquations (any ).

The package currently provides working implementations for in-memory data sources, but will eventually be able to translate queries into e.g. SQL. There is a prototype implementation of such a «query provider» for SQLite in the package, but it is experimental at this point and only works for a very small subset of queries.

Setting the body of a request¶

Requests that can send a body (e.g. PUT, POST, DELETE, PATCH) are instances of Guzzle\Http\Message\EntityEnclosingRequestInterface. Entity enclosing requests contain several methods that allow you to specify the body to send with a request.

Use the setBody() method of a request to set the body that will be sent with a request. This method accepts a string, a resource returned by fopen(), an array, or an instance of Guzzle\Http\EntityBodyInterface. The body will then be streamed from the underlying EntityBodyInterface object owned by the request. When setting the body of the request, you can optionally specify a Content-Type header and whether or not to force the request to use chunked Transfer-Encoding.

$request = $client->put('/user.json');
$request->setBody('{"foo":"baz"}', 'application/json');

Content-Type header

Guzzle will automatically add a Content-Type header to a request if the Content-Type can be guessed based on the file extension of the payload being sent or the file extension present in the path of a request.

$request = $client->put('/user.json', array(), '{"foo":"bar"}');
// The Content-Type was guessed based on the path of the request
echo $request->getHeader('Content-Type');
// >>> application/json

$request = $client->put('/user.json');
$request->setBody(fopen('/tmp/user_data.json', 'r'));
// The Content-Type was guessed based on the path of the entity body
echo $request->getHeader('Content-Type');
// >>> application/json

Transfer-Encoding: chunked header

When sending HTTP requests that contain a payload, you must let the remote server know how to determine when the entire message has been sent. This usually is done by supplying a Content-Length header that tells the origin server the 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 before initiating the transfer. In these cases (when using HTTP/1.1), you can use the Transfer-Encoding: chunked header.

If the Content-Length cannot be determined (i.e. using a PHP http:// stream), then Guzzle will automatically add the Transfer-Encoding: chunked header to the request.

$request = $client->put('/user.json');
$request->setBody(fopen('http://httpbin.org/get', 'r'));

// The Content-Length could not be determined
echo $request->getHeader('Transfer-Encoding');
// >>> chunked

See Request and response bodies for more information on entity bodies.

Expect: 100-Continue header

The Expect: 100-Continue header is used to help a client prevent sending a large payload to a server that will reject the request. This allows clients to fail fast rather than waste bandwidth sending an erroneous payload. Guzzle will automatically add the Expect: 100-Continue header to a request when the size of the payload exceeds 1MB or if the body of the request is not seekable (this helps to prevent errors when a non-seekable body request is redirected).


If you find that your larger requests are taking too long to complete, you should first check if the Expect: 100-Continue header is being sent with the request. Some servers do not respond well to this header, which causes cURL to sleep for 1 second.


Pixie supports three database drivers, MySQL, SQLite and PostgreSQL. You can specify the driver during connection and the associated configuration when creating a new connection. You can also create multiple connections, but you can use alias for only one connection at a time.;

// Make sure you have Composer's autoload file included
require 'vendor/autoload.php';

$config = array(
            'driver'    => 'mysql', // Db driver
            'host'      => 'localhost',
            'database'  => 'your-database',
            'username'  => 'root',
            'password'  => 'your-password',
            'charset'   => 'utf8', // Optional
            'collation' => 'utf8_unicode_ci', // Optional
            'prefix'    => 'cb_', // Table prefix, optional

new \Pixie\Connection('mysql', $config, 'QB');

// Run query
$query = QB::table('my_table')->where('name', '=', 'Sana');


When you create a connection:

new \Pixie\Connection('mysql', $config, 'MyAlias');

is the name for the class alias you want to use (like ), you can use whatever name (with Namespace also, ) you like or you may skip it if you don’t need an alias. Alias gives you the ability to easily access the QueryBuilder class across your application.

When not using an alias you can instantiate the QueryBuilder handler separately, helpful for Dependency Injection and Testing.

$connection = new \Pixie\Connection('mysql', $config);
$qb = new \Pixie\QueryBuilder\QueryBuilderHandler($connection);

$query = $qb->table('my_table')->where('name', '=', 'Sana');


here is optional, if not given it will always associate itself to the first connection, but it can be useful when you have multiple database connections.

SQLite and PostgreSQL Config Sample

new \Pixie\Connection('sqlite', array(
                'driver'   => 'sqlite',
			    'database' => 'your-file.sqlite',
			    'prefix'   => 'cb_',
		    ), 'QB');
new \Pixie\Connection('pgsql', array(
                    'driver'   => 'pgsql',
                    'host'     => 'localhost',
                    'database' => 'your-database',
                    'username' => 'postgres',
                    'password' => 'your-password',
                    'charset'  => 'utf8',
                    'prefix'   => 'cb_',
                    'schema'   => 'public',
                ), 'QB');



  • — default ‘&’
  • — default ‘=’
  • — string to prepend to numeric keys
  • — ‘PHP_QUERY_RFC1738’ (default) encodes spaces as ‘+’, ‘PHP_QUERY_RFC3986’ encodes spaces as ‘%20’
  • — encode as «a=3» and not «a%5B0%5D=3»

parse an http query, the counterpart to http_build_query. Similar to the php

build up the parameters hash from the PHP-style query string. Parses name-value pairs as expected, is . names value-less names as if set to one, i.e. becomes {a:1, b:1}. Unlike PHP, gathers repeated parameters into arrays (e.g., is and not a=2. Like PHP, parses hierarchical values like into .

Todo: flat numerical hierarchies should be converted to arrays, not objects.. Currently parses a into the object and not . This is not symmetric with http_build_query() behavior.

parse simple URL strings the way does, but 4x faster. The parsed results are returned in a hash (without the url.parse functions), and all fields set by url.parse are set to the same values as by url.parse. The urls should be of the form or , eg.

  • : or (http:)
  • : or (user:pass)
  • : the non-auth string after the and before the first (example.com:80)
  • : the trailing end of the url string, not including the hash (/path/name?a=1&b=2)
  • : the portion of the string from the first ‘?’ to the hash (?a=1&b=2)
  • : the portion of the string from the first ‘#’ to the end (#tag1)

In addition to the above fields, url.parse and parseUrl also separate

  • : host without the port (example.com)

  • : host port (80)

  • : user part of auth (user) (parseUrl only, not url.parse)

  • : password part of auth (pass) (parseUrl only, not url.parse)

  • : search without the leading ‘?’ (a=1&b=2)

  • : path without the search component (/path/name)

  • : the normalized url string, from protocol to hash. This is often the same as the input url, but can differ if eg no path was specified => .

Return a newly constructed HttpClient object preconfigured with the given options. See below. Also available as for compatibility.

Convenience web request calls using the built-in singleton.

web request runner, built on top of


  • — base url to call, eg «http://host.com». A call to a bare path will be combined with the base url to form «http://host.com/rest/path» for the request.
  • — headers to pass to each request. Additional headers may be passed at call time
  • — http request function. Default is
  • — https request function. Default is
  • — gather up the response body and return it in the callback (default ). If set to , the web request callbacks will get just the response object, which can then be gathered or piped by the caller.
  • — http Basic authorization object containing and or authorization string in the form «username:password». and are also ok.
  • — function to use for url string parsing. Default is , which parses only well-formatted urls in the form but is 4x faster than Url.parse and reduces overall round-trip call latency by 10%.

The options are passed to , so in addition to the above HttpClient options, options are also allowed. Null and undefined values are ignored.

Notable request options:

agent — the http agent to use with request. Default is http.globalAgent See also agentkeepalive and qhttp-agent for faster, more predictable connection agents.

make a web request

  • — the http method to use, eg GET or POST
  • — the service address to load, either a query string or an compatible object
  • (optional) — the message to send in the body of the request
  • — function that will be called with the response

Returns via the callback the http response, with set to a Buffer containing the raw unparsed reply message.

The may be a string, an object, or a Buffer. String is sent as content-type ‘text/plain’. Object is json-encoded and sent as ‘application/json’. Buffer is sent as binary data as type ‘application/octet-stream’.

The call always sets to match the body (unless already set), and .

Shortcuts to etc., similar to .

Modify the HttpClient object for improved compatbility. New methods are added, error reporting changes, the response message is decoded into an object, and the get/post etc convenience methods callbacks are called with different arguments.

The methods added to httpClient are:

Sign requests with an «Authorization: Basic» header out of username:password

make a GET request

make a POST request

make a PUT request

make a DELETE request. For compatbility, can also be called as .

Query string parameters¶

Query string parameters of a request are owned by a request’s Guzzle\Http\Query object that is accessible by calling $request->getQuery(). The Query class extends from Guzzle\Common\Collection and allows you to set one or more query string parameters as key value pairs. You can set a parameter on a Query object using the set($key, $value) method or access the query string object like an associative array. Any previously specified value for a key will be overwritten when using set(). Use add($key, $value) to add a value to query string object, and in the event of a collision with an existing value at a specific key, the value will be converted to an array that contains all of the previously set values.

$request = new Guzzle\Http\Message\Request('GET', 'http://www.example.com?foo=bar&abc=123');

$query = $request->getQuery();
echo "{$query}\n";
// >>> foo=bar&abc=123

echo "{$query}\n";
// >>> foo=bar

$query->set('foo', 'baz');
echo "{$query}\n";
// >>> foo=baz

$query->add('foo', 'bar');
echo "{$query}\n";
// >>> foo%5B0%5D=baz&foo%5B1%5D=bar

Whoah! What happened there? When foo=bar was added to the existing foo=baz query string parameter, the aggregator associated with the Query object was used to help convert multi-value query string parameters into a string. Let’s disable URL-encoding to better see what’s happening.

echo "{$query}\n";
// >>> foo=baz&foo=bar


URL encoding can be disabled by passing false, enabled by passing true, set to use RFC 1738 by passing Query::FORM_URLENCODED (internally uses PHP’s urlencode function), or set to RFC 3986 by passing Query::RFC_3986 (this is the default and internally uses PHP’s rawurlencode function).

As you can see, the multiple values were converted into query string parameters following the default PHP convention of adding numerically indexed square bracket suffixes to each key (foo=baz&foo=bar). The strategy used to convert multi-value parameters into a string can be customized using the setAggregator() method of the Query class. Guzzle ships with the following query string aggregators by default:



fix http_parse_query() to decode + into ‘ ‘ space


  • encode dates as toISOString
  • encode only array contents, not array properties


fix parseUrl export


  • fix dev dependency name
  • accept the body parameter from the uri object as well


  • have the package export a singleton with methods , , etc
  • fix Content-Length for multi-byte utf8 characters
  • fix header handling to not clobber pre-configured headers at call time
  • factory method (aka )
  • expose singleton method


  • HttpClient:
    • returnBody:false option to not wait for response to arrive
    • auth: options for http basic auth
    • factory method
    • get, post, put, delete shortcuts
    • fix: do not overwrite user-supplied Content-Type
    • fix: do not call callback twice if req error
    • make unit tests standalone (do not hit localhost:80)
    • expose module-level get, post, etc functions eg
  • new function
  • switch unit tests from nodeunit to qunit


return body as third arg of callback

С этим читают