SkillAgentSearch skills...

Http

The sabre/http library provides utilities for dealing with http requests and responses.

Install / Use

/learn @sabre-io/Http
About this skill

Quality Score

0/100

Supported Platforms

Universal

README

sabre/http

This library provides a toolkit to make working with the HTTP protocol easier.

Most PHP scripts run within an HTTP request but accessing information about the HTTP request is cumbersome at least.

There's bad practices, inconsistencies and confusion. This library is effectively a wrapper around the following PHP constructs:

For Input:

  • $_GET,
  • $_POST,
  • $_SERVER,
  • php://input or $HTTP_RAW_POST_DATA.

For output:

  • php://output or echo,
  • header().

What this library provides, is a Request object, and a Response object.

The objects are extendable and easily mockable.

Build status

Build Status

| release | minimum PHP version | |---------|---------------------| | master | PHP 7.4 | | 7.0 | PHP 7.4 | | 6.0 | PHP 7.4 | | 5.1 | PHP 7.1 | | 4.2 | PHP 5.4 | | 3.0 | PHP 5.4 |

Installation

Make sure you have [composer][1] installed. In your project directory, create, or edit a composer.json file, and make sure it contains something like this:

{
    "require" : {
        "sabre/http" : "~5.0.0"
    }
}

After that, just hit composer install and you should be rolling.

Quick history

This library came to existence in 2009, as a part of the [sabre/dav][2] project, which uses it heavily.

It got split off into a separate library to make it easier to manage releases and hopefully giving it use outside the scope of just sabre/dav.

Although completely independently developed, this library has a LOT of overlap with [Symfony's HttpFoundation][3].

Said library does a lot more stuff and is significantly more popular, so if you are looking for something to fulfill this particular requirement, I'd recommend also considering [HttpFoundation][3].

Getting started

First and foremost, this library wraps the superglobals. The easiest way to instantiate a request object is as follows:

use Sabre\HTTP;

include 'vendor/autoload.php';

$request = HTTP\Sapi::getRequest();

This line should only happen once in your entire application. Everywhere else you should pass this request object around using dependency injection.

You should always typehint on its interface:

function handleRequest(HTTP\RequestInterface $request) {

    // Do something with this request :)

}

A response object you can just create as such:

use Sabre\HTTP;

include 'vendor/autoload.php';

$response = new HTTP\Response();
$response->setStatus(201); // created !
$response->setHeader('X-Foo', 'bar');
$response->setBody(
    'success!'
);

After you fully constructed your response, you must call:

HTTP\Sapi::sendResponse($response);

This line should generally also appear once in your application (at the very end).

Decorators

It may be useful to extend the Request and Response objects in your application, if you, for example, would like them to carry a bit more information about the current request.

For instance, you may want to add an isLoggedIn method to the Request object.

Simply extending Request and Response may pose some problems:

  1. You may want to extend the objects with new behaviors differently, in different subsystems of your application,
  2. The Sapi::getRequest factory always returns an instance of Request so you would have to override the factory method as well,
  3. By controlling the instantiation and depend on specific Request and Response instances in your library or application, you make it harder to work with other applications which also use sabre/http.

In short, it would be bad design. Instead, it's recommended to use the [decorator pattern][6] to add new behavior where you need it. sabre/http provides helper classes to quickly do this.

Example:

use Sabre\HTTP;

class MyRequest extends HTTP\RequestDecorator {

    function isLoggedIn() {

        return true;

    }

}

Our application assumes that the true Request object was instantiated somewhere else, by some other subsystem. This could simply be a call like $request = Sapi::getRequest() at the top of your application, but could also be somewhere in a unit test.

All we know in the current subsystem, is that we received a $request and that it implements Sabre\HTTP\RequestInterface. To decorate this object, all we need to do is:

$request = new MyRequest($request);

And that's it; we now have an isLoggedIn method, without having to mess with the core instances.

Client

This package also contains a simple wrapper around [cURL][4], which will allow you to write simple clients, using the Request and Response objects you're already familiar with.

It's by no means a replacement for something like [Guzzle][7], but it provides a simple and lightweight API for making the occasional API call.

Usage

use Sabre\HTTP;

$request = new HTTP\Request('GET', 'http://example.org/');
$request->setHeader('X-Foo', 'Bar');

$client = new HTTP\Client();
$response = $client->send($request);

echo $response->getBodyAsString();

The client emits 3 events using [sabre/event][5]. beforeRequest, afterRequest and error.

$client = new HTTP\Client();
$client->on('beforeRequest', function($request) {

    // You could use beforeRequest to, for example, inject a few extra headers
    // into the Request object.

});

$client->on('afterRequest', function($request, $response) {

    // The afterRequest event could be a good time to do some logging, or
    // do some rewriting in the response.

});

$client->on('error', function($request, $response, &$retry, $retryCount) {

    // The error event is triggered for every response with an HTTP code higher
    // than 399.

});

$client->on('error:401', function($request, $response, &$retry, $retryCount) {

    // You can also listen for specific error codes. This example shows how
    // to inject HTTP authentication headers if a 401 was returned.

    if ($retryCount > 1) {
        // We're only going to retry exactly once.
    }

    $request->setHeader('Authorization', 'Basic xxxxxxxxxx');
    $retry = true;

});

Asynchronous requests

The Client also supports doing asynchronous requests. This is especially handy if you need to perform a number of requests that are allowed to be executed in parallel.

The underlying system for this is simply [cURL's multi request handler][8], but this provides a much nicer API to handle this.

Sample usage:


use Sabre\HTTP;

$request = new Request('GET', 'http://localhost/');
$client = new Client();

// Executing 1000 requests
for ($i = 0; $i < 1000; $i++) {
    $client->sendAsync(
        $request,
        function(ResponseInterface $response) {
            // Success handler
        },
        function($error) {
            // Error handler
        }
    ); 
}

// Wait for all requests to get a result.
$client->wait();

Check out examples/asyncclient.php for more information.

Writing a reverse proxy

With all these tools combined, it becomes very easy to write a simple reverse http proxy.

use
    Sabre\HTTP\Sapi,
    Sabre\HTTP\Client;

// The url we're proxying to.
$remoteUrl = 'http://example.org/';

// The url we're proxying from. Please note that this must be a relative url,
// and basically acts as the base url.
//
// If your $remoteUrl doesn't end with a slash, this one probably shouldn't
// either.
$myBaseUrl = '/reverseproxy.php';
// $myBaseUrl = '/~evert/sabre/http/examples/reverseproxy.php/';

$request = Sapi::getRequest();
$request->setBaseUrl($myBaseUrl);

$subRequest = clone $request;

// Removing the Host header.
$subRequest->removeHeader('Host');

// Rewriting the url.
$subRequest->setUrl($remoteUrl . $request->getPath());

$client = new Client();

// Sends the HTTP request to the server
$response = $client->send($subRequest);

// Sends the response back to the client that connected to the proxy.
Sapi::sendResponse($response);

The Request and Response APIs

Request


/**
 * Creates the request object
 *
 * @param string $method
 * @param string $url
 * @param array $headers
 * @param resource $body
 */
public function __construct($method = null, $url = null, array $headers = null, $body = null);

/**
 * Returns the current HTTP method
 *
 * @return string
 */
function getMethod();

/**
 * Sets the HTTP method
 *
 * @param string $method
 * @return void
 */
function setMethod($method);

/**
 * Returns the request url.
 *
 * @return string
 */
function getUrl();

/**
 * Sets the request url.
 *
 * @param string $url
 * @return void
 */
function setUrl($url);

/**
 * Returns the absolute url.
 *
 * @return string
 */
function getAbsoluteUrl();

/**
 * Sets the absolute url.
 *
 * @param string $url
 * @return void
 */
function setAbsoluteUrl($url);

/**
 * Returns the current base url.
 *
 * @return string
 */
function getBaseUrl();

/**
 * Sets a base url.
 *
 * This url is used for relative path calculations.
 *
 * The base url should default to /
 *
 * @param string $url
 * @return void
 */
function setBaseUrl($url);

/**
 * Returns the relative path.
 *
 * This is being calculated using the base url. This path will not start
 * with a slash, so it will always return something like
 * 'example/path.html'.
 *
 * If the full path is equal to the base url, this method will return an
 * empty string.
 *
 * This method will also urldecode the path, and if the url was encoded as
 * ISO-8859-1, it will convert it to UTF-8.
 *
 * If the path is outside the base url, a LogicException will be thrown.
 *
 * @return string
 */
function getPath();

/**
 * Returns the list of query parameters.
 *
 * This is equivalent to PHP's $_GET superglo
View on GitHub
GitHub Stars190
CategoryDevelopment
Updated2mo ago
Forks59

Languages

PHP

Security Score

95/100

Audited on Jan 28, 2026

No findings