sabre/http 库提供了处理 HTTP 请求和响应的工具。

2.0.4 2014-07-14 18:13 UTC

README

此库提供了一套工具,使使用 HTTP 协议变得更容易。

大多数 PHP 脚本在 HTTP 请求中运行,但访问 HTTP 请求信息至少是繁琐的,这主要是由于超全局变量和 CGI 标准。

存在一些不良做法、不一致性和困惑。此库实际上是以下 PHP 构造的包装器

输入

  • $_GET
  • $_POST
  • $_SERVER
  • php://input$HTTP_RAW_POST_DATA

输出

  • php://outputecho
  • header()

此库提供的是 Request 对象和 Response 对象。

这些对象是可扩展的,并且易于模拟。

构建状态

安装

请确保已安装 composer。在您的项目目录中,创建或编辑一个 composer.json 文件,并确保它包含类似以下内容

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

之后,只需运行 composer install,您就应该可以开始了。

快速历史

此库于 2009 年出现,作为 SabreDAV 项目的一部分,该项目大量使用了它。

它被分离成单独的库,以便更容易管理版本,并希望使其在 SabreDAV 范围之外也能使用。

尽管完全独立开发,但此库与 symfony 的 HttpFoundation 有很多重叠。

该库做了更多的事情,并且非常受欢迎,所以如果您正在寻找满足特定需求的东西,我建议您也考虑 HttpFoundation

入门指南

首先,此库包装了超全局变量。实例化请求对象的简单方法如下

use Sabre\HTTP;

include 'vendor/autoload.php';

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

此行应该只在整个应用程序中出现一次。在其他地方,您应该使用依赖注入传递此请求对象。

您应该始终对此接口进行类型提示

function handleRequest(HTTP\RequestInterface $request) {

    // Do something with this request :)

}

您可以这样创建响应对象

use Sabre\HTTP;

include 'vendor/autoload.php';

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

在您完全构建了响应之后,必须调用

HTTP\Sapi::sendResponse($response);

此行通常也只出现在您的应用程序中(在最后)。

装饰器

如果您的应用程序需要请求和响应对象携带更多关于当前请求的信息,则扩展这些对象可能很有用。

例如,您可能想向请求对象添加一个 isLoggedIn() 方法。

简单地扩展请求和响应可能会带来一些问题

  1. 您可能希望以不同的方式在不同应用程序子系统中对对象进行扩展以添加新的行为。
  2. Sapi::getRequest 工厂始终返回 Request 的实例,因此您还必须覆盖工厂方法。
  3. 通过控制实例化和依赖特定 RequestResponse 实例,您会使得与其他使用 sabre/http 的应用程序协同工作变得更加困难。

简而言之:这将是糟糕的设计。相反,建议使用装饰器模式在需要添加新行为的地方进行操作。sabre/http 提供了辅助类以快速完成此操作。

示例

use Sabre\HTTP;

class MyRequest extends HTTP\RequestDecorator {

    function isLoggedIn() {

        return true;

    }

}

我们的应用程序假设真正的 Request 对象在其他地方由其他子系统实例化。这可以简单地是一个像在应用程序顶部调用 $request = Sapi::getRequest() 的操作,但也可以在单元测试的某个地方。

在当前子系统,我们只知道我们接收了一个 $request 并且它实现了 Sabre\HTTP\RequestInterface。要装饰这个对象,我们只需要做

$request = new MyRequest($request);

就是这样,我们现在有了 isLoggedIn 方法,而不必触碰核心实例。

客户端

此包还包含了一个简单的cURL包装器,这将允许您使用已经熟悉的 RequestResponse 对象编写简单的客户端。

这绝对不是像guzzle这样的替代品,但它提供了一个简单且轻量级的API,用于执行偶尔的API调用。

用法

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();

客户端使用sabre/event发出3个事件:beforeRequestafterRequesterror

$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 a 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;

});

异步请求

客户端还支持异步请求。如果您需要执行多个允许并行执行请求,这将特别有用。

这个底层系统简单地是curl的多请求处理器,但它提供了一个更友好的API来处理这个问题。

示例用法

use Sabre\HTTP;

$request = new Request('GET', 'https:///');
$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();

查看 examples/asyncclient.php 获取更多信息。

编写反向代理

结合所有这些工具,编写简单的反向HTTP代理变得非常容易。

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 youre $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);

请求和响应API

请求

/**
 * 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 incoded as
 * ISO-8859-1, it will convert it to UTF-8.
 *
 * If the path is outside of 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 superglobal.
 *
 * @return array
 */
function getQueryParameters();

/**
 * Returns the POST data.
 *
 * This is equivalent to PHP's $_POST superglobal.
 *
 * @return array
 */
function getPostData();

/**
 * Sets the post data.
 *
 * This is equivalent to PHP's $_POST superglobal.
 *
 * This would not have been needed, if POST data was accessible as
 * php://input, but unfortunately we need to special case it.
 *
 * @param array $postData
 * @return void
 */
function setPostData(array $postData);

/**
 * Returns an item from the _SERVER array.
 *
 * If the value does not exist in the array, null is returned.
 *
 * @param string $valueName
 * @return string|null
 */
function getRawServerValue($valueName);

/**
 * Sets the _SERVER array.
 *
 * @param array $data
 * @return void
 */
function setRawServerData(array $data);

/**
 * Returns the body as a readable stream resource.
 *
 * Note that the stream may not be rewindable, and therefore may only be
 * read once.
 *
 * @return resource
 */
function getBodyAsStream();

/**
 * Returns the body as a string.
 *
 * Note that because the underlying data may be based on a stream, this
 * method could only work correctly the first time.
 *
 * @return string
 */
function getBodyAsString();

/**
 * Returns the message body, as it's internal representation.
 *
 * This could be either a string or a stream.
 *
 * @return resource|string
 */
function getBody();

/**
 * Updates the body resource with a new stream.
 *
 * @param resource $body
 * @return void
 */
function setBody($body);

/**
 * Returns all the HTTP headers as an array.
 *
 * @return array
 */
function getHeaders();

/**
 * Returns a specific HTTP header, based on it's name.
 *
 * The name must be treated as case-insensitive.
 *
 * If the header does not exist, this method must return null.
 *
 * @param string $name
 * @return string|null
 */
function getHeader($name);

/**
 * Updates a HTTP header.
 *
 * The case-sensitity of the name value must be retained as-is.
 *
 * @param string $name
 * @param string $value
 * @return void
 */
function setHeader($name, $value);

/**
 * Resets HTTP headers
 *
 * This method overwrites all existing HTTP headers
 *
 * @param array $headers
 * @return void
 */
function setHeaders(array $headers);

/**
 * Adds a new set of HTTP headers.
 *
 * Any header specified in the array that already exists will be
 * overwritten, but any other existing headers will be retained.
 *
 * @param array $headers
 * @return void
 */
function addHeaders(array $headers);

/**
 * Removes a HTTP header.
 *
 * The specified header name must be treated as case-insenstive.
 * This method should return true if the header was successfully deleted,
 * and false if the header did not exist.
 *
 * @return bool
 */
function removeHeader($name);

/**
 * Sets the HTTP version.
 *
 * Should be 1.0 or 1.1.
 *
 * @param string $version
 * @return void
 */
function setHttpVersion($version);

/**
 * Returns the HTTP version.
 *
 * @return string
 */
function getHttpVersion();

响应

/**
 * Returns the current HTTP status.
 *
 * This is the status-code as well as the human readable string.
 *
 * @return string
 */
function getStatus();

/**
 * Sets the HTTP status code.
 *
 * This can be either the full HTTP status code with human readable string,
 * for example: "403 I can't let you do that, Dave".
 *
 * Or just the code, in which case the appropriate default message will be
 * added.
 *
 * @param string|int $status
 * @throws \InvalidArgumentExeption
 * @return void
 */
function setStatus($status);

/**
 * Returns the body as a readable stream resource.
 *
 * Note that the stream may not be rewindable, and therefore may only be
 * read once.
 *
 * @return resource
 */
function getBodyAsStream();

/**
 * Returns the body as a string.
 *
 * Note that because the underlying data may be based on a stream, this
 * method could only work correctly the first time.
 *
 * @return string
 */
function getBodyAsString();

/**
 * Returns the message body, as it's internal representation.
 *
 * This could be either a string or a stream.
 *
 * @return resource|string
 */
function getBody();


/**
 * Updates the body resource with a new stream.
 *
 * @param resource $body
 * @return void
 */
function setBody($body);

/**
 * Returns all the HTTP headers as an array.
 *
 * @return array
 */
function getHeaders();

/**
 * Returns a specific HTTP header, based on it's name.
 *
 * The name must be treated as case-insensitive.
 *
 * If the header does not exist, this method must return null.
 *
 * @param string $name
 * @return string|null
 */
function getHeader($name);

/**
 * Updates a HTTP header.
 *
 * The case-sensitity of the name value must be retained as-is.
 *
 * @param string $name
 * @param string $value
 * @return void
 */
function setHeader($name, $value);

/**
 * Resets HTTP headers
 *
 * This method overwrites all existing HTTP headers
 *
 * @param array $headers
 * @return void
 */
function setHeaders(array $headers);

/**
 * Adds a new set of HTTP headers.
 *
 * Any header specified in the array that already exists will be
 * overwritten, but any other existing headers will be retained.
 *
 * @param array $headers
 * @return void
 */
function addHeaders(array $headers);

/**
 * Removes a HTTP header.
 *
 * The specified header name must be treated as case-insenstive.
 * This method should return true if the header was successfully deleted,
 * and false if the header did not exist.
 *
 * @return bool
 */
function removeHeader($name);

/**
 * Sets the HTTP version.
 *
 * Should be 1.0 or 1.1.
 *
 * @param string $version
 * @return void
 */
function setHttpVersion($version);

/**
 * Returns the HTTP version.
 *
 * @return string
 */
function getHttpVersion();

由 fruux 制作

此库由fruux开发。如有商业服务或企业支持需求,请与我们联系。