PSR-7 消息实现

维护者

详细信息

github.com/mjcodebase/psr7

源代码

1.3 2024-03-28 09:01 UTC

This package is auto-updated.

Last update: 2024-08-28 09:47:55 UTC


README

此存储库包含部分 PSR-7 消息实现,多个流装饰器以及一些有用的功能,如查询字符串解析。目前缺少 ServerRequestInterface 和 UploadedFileInterface;欢迎对这些功能的 pull request。

流实现

本包包含多个流实现和流装饰器。

AppendStream

RingCentral\Psr7\AppendStream

从多个流中依次读取。

use RingCentral\Psr7;

$a = Psr7\stream_for('abc, ');
$b = Psr7\stream_for('123.');
$composed = new Psr7\AppendStream([$a, $b]);

$composed->addStream(Psr7\stream_for(' Above all listen to me').

echo $composed(); // abc, 123. Above all listen to me.

BufferStream

RingCentral\Psr7\BufferStream

提供可写入以填充缓冲区,并从缓冲区中读取字节的缓冲区流。

此流返回一个 "hwm" 元数据值,告诉上游消费者流配置的高水位标志或缓冲区的最大首选大小。

use RingCentral\Psr7;

// When more than 1024 bytes are in the buffer, it will begin returning
// false to writes. This is an indication that writers should slow down.
$buffer = new Psr7\BufferStream(1024);

CachingStream

CachingStream 用于在非可寻址流上允许在先前读取的字节上搜索。当由于需要回滚流(例如,由于重定向)而传输非可寻址实体主体失败时,这可能很有用。从远程流中读取的数据将被缓存在 PHP 临时流中,以便首先在内存中缓存先前读取的字节,然后在磁盘上。

use RingCentral\Psr7;

$original = Psr7\stream_for(fopen('http://www.google.com', 'r'));
$stream = new Psr7\CachingStream($original);

$stream->read(1024);
echo $stream->tell();
// 1024

$stream->seek(0);
echo $stream->tell();
// 0

DroppingStream

RingCentral\Psr7\DroppingStream

一旦底层流的大小变得过大,此流装饰器就开始丢弃数据。

use RingCentral\Psr7;

// Create an empty stream
$stream = Psr7\stream_for();

// Start dropping data when the stream has more than 10 bytes
$dropping = new Psr7\DroppingStream($stream, 10);

$stream->write('01234567890123456789');
echo $stream; // 0123456789

FnStream

RingCentral\Psr7\FnStream

基于函数散列组合流实现。

允许在不需要为简单的扩展点创建具体类的情况下轻松测试和扩展提供的流。

use RingCentral\Psr7;

$stream = Psr7\stream_for('hi');
$fnStream = Psr7\FnStream::decorate($stream, [
    'rewind' => function () use ($stream) {
        echo 'About to rewind - ';
        $stream->rewind();
        echo 'rewound!';
    }
]);

$fnStream->rewind();
// Outputs: About to rewind - rewound!

InflateStream

RingCentral\Psr7\InflateStream

使用 PHP 的 zlib.inflate 过滤器来解压 deflate 或 gzipped 内容。

此流装饰器跳过给定流的第一个 10 个字节以删除 gzip 头部,将提供的流转换为 PHP 流资源,然后附加 zlibinflate 过滤器。然后,将流转换回 Guzzle 流资源以用作 Guzzle 流。

LazyOpenStream

RingCentral\Psr7\LazyOpenStream

在流上发生 IO 操作后,懒加载地读取或写入文件。

use RingCentral\Psr7;

$stream = new Psr7\LazyOpenStream('/path/to/file', 'r');
// The file has not yet been opened...

echo $stream->read(10);
// The file is opened and read from only when needed.

LimitStream

RingCentral\Psr7\LimitStream

LimitStream 可用于读取现有流对象的一部分或子集。这可以将大文件分割成小块以分块发送(例如,Amazon S3 的多部分上传 API)。

use RingCentral\Psr7;

$original = Psr7\stream_for(fopen('/tmp/test.txt', 'r+'));
echo $original->getSize();
// >>> 1048576

// Limit the size of the body to 1024 bytes and start reading from byte 2048
$stream = new Psr7\LimitStream($original, 1024, 2048);
echo $stream->getSize();
// >>> 1024
echo $stream->tell();
// >>> 0

MultipartStream

RingCentral\Psr7\MultipartStream

当读取时返回流式多部分或多部分/表单数据流的字节的流。

NoSeekStream

RingCentral\Psr7\NoSeekStream

NoSeekStream 包装流并不允许搜索。

use RingCentral\Psr7;

$original = Psr7\stream_for('foo');
$noSeek = new Psr7\NoSeekStream($original);

echo $noSeek->read(3);
// foo
var_export($noSeek->isSeekable());
// false
$noSeek->seek(0);
var_export($noSeek->read(3));
// NULL

PumpStream

RingCentral\Psr7\PumpStream

提供只读流,从 PHP 可调用对象中泵送数据。

调用提供的可调用对象时,PumpStream 将传递请求读取的数据量给可调用对象。可调用对象可以选择忽略此值,并返回少于或多于请求的字节。由提供的可调用对象返回的任何额外数据都将在内部缓冲,直到使用 PumpStream 的 read() 函数排空。提供的可调用对象在没有更多数据可读取时必须返回 false。

实现流装饰器

利用 RingCentral\Psr7\StreamDecoratorTrait,创建流装饰器非常简单。这个特质提供了通过代理到底层流来实现 Psr\Http\Message\StreamInterface 方法的函数。只需 use StreamDecoratorTrait 并实现您自己的自定义方法即可。

例如,假设我们希望在每次从流中读取最后字节时调用一个特定函数。这可以通过重写 read() 方法来实现。

use Psr\Http\Message\StreamInterface;
use RingCentral\Psr7\StreamDecoratorTrait;

class EofCallbackStream implements StreamInterface
{
    use StreamDecoratorTrait;

    private $callback;

    public function __construct(StreamInterface $stream, callable $cb)
    {
        $this->stream = $stream;
        $this->callback = $cb;
    }

    public function read($length)
    {
        $result = $this->stream->read($length);

        // Invoke the callback when EOF is hit.
        if ($this->eof()) {
            call_user_func($this->callback);
        }

        return $result;
    }
}

这个装饰器可以添加到任何现有的流中,并按如下方式使用

use RingCentral\Psr7;

$original = Psr7\stream_for('foo');

$eofStream = new EofCallbackStream($original, function () {
    echo 'EOF!';
});

$eofStream->read(2);
$eofStream->read(1);
// echoes "EOF!"
$eofStream->seek(0);
$eofStream->read(3);
// echoes "EOF!"

PHP 流包装器

如果您需要将 PSR-7 流用作 PHP 流资源,可以使用 RingCentral\Psr7\StreamWrapper 类。

使用 RingCentral\Psr7\StreamWrapper::getResource() 方法从 PSR-7 流创建 PHP 流。

use RingCentral\Psr7\StreamWrapper;

$stream = RingCentral\Psr7\stream_for('hello!');
$resource = StreamWrapper::getResource($stream);
echo fread($resource, 6); // outputs hello!

函数 API

RingCentral\Psr7 命名空间下提供了各种函数。

function str

function str(MessageInterface $message)

返回 HTTP 消息的字符串表示。

$request = new RingCentral\Psr7\Request('GET', 'http://example.com');
echo RingCentral\Psr7\str($request);

function uri_for

function uri_for($uri)

此函数接受一个字符串或 Psr\Http\Message\UriInterface,并返回给定值的 UriInterface。如果值已经是 UriInterface,则原样返回。

$uri = RingCentral\Psr7\uri_for('http://example.com');
assert($uri === RingCentral\Psr7\uri_for($uri));

function stream_for

function stream_for($resource = '', array $options = [])

根据输入类型创建一个新的流。

选项是一个关联数组,可以包含以下键

    • metadata: 自定义元数据的数组。
    • size: 流的大小。

此方法接受以下 $resource 类型

  • Psr\Http\Message\StreamInterface: 原样返回值。
  • string: 使用给定的字符串作为内容创建一个流对象。
  • resource: 创建一个包装给定 PHP 流资源的流对象。
  • Iterator: 如果提供的值实现了 Iterator,则将创建一个包装给定可迭代对象的只读流对象。每次从流中读取时,迭代器中的数据将填充缓冲区,并将连续调用直到缓冲区等于请求的读取大小。后续的读取调用将首先从缓冲区读取,然后在底层的迭代器上调用 next,直到迭代器耗尽。
  • object with __toString(): 如果对象具有 __toString() 方法,则对象将被转换为字符串,然后返回一个使用字符串值的流。
  • NULL: 当传递 null 时,返回一个空流对象。
  • callable 当传递可调用时,将创建一个只读流对象,该对象调用给定的可调用。可调用使用要读取的预期字节数调用。可调用可以返回任意数量的字节,但必须在没有更多数据返回时返回 false。包装可调用的流对象将调用可调用,直到有请求的字节数可用。任何额外的字节都将被缓冲并用于后续的读取。
$stream = RingCentral\Psr7\stream_for('foo');
$stream = RingCentral\Psr7\stream_for(fopen('/path/to/file', 'r'));

$generator function ($bytes) {
    for ($i = 0; $i < $bytes; $i++) {
        yield ' ';
    }
}

$stream = RingCentral\Psr7\stream_for($generator(100));

function parse_header

function parse_header($header)

将包含分号分隔数据的头值数组解析为表示头键值对的关联数组数组。当参数不包含值,只包含键时,此函数将为键注入一个空字符串值。

function normalize_header

function normalize_header($header)

将可能包含逗号分隔头的头值数组转换为没有逗号分隔值的头数组。

function modify_request

function modify_request(RequestInterface $request, array $changes)

克隆并修改具有给定更改的请求。此方法对于减少修改消息所需的克隆数量非常有用。

更改可以是以下之一

  • method: (string) 更改 HTTP 方法。
  • set_headers: (array) 设置给定的头。
  • remove_headers: (array) 删除给定的头。
  • body: (mixed) 设置给定的主体。
  • uri: (UriInterface) 设置 URI。
  • query: (string) 设置 URI 的查询字符串值。
  • version: (string) 设置协议版本。

函数 rewind_body

函数 rewind_body(MessageInterface $message)

尝试回滚消息主体并在失败时抛出异常。如果调用 tell() 返回的值不是 0,则消息的主体才会被回滚。

函数 try_fopen

函数 try_fopen($filename, $mode)

安全地使用文件名打开 PHP 流资源。

当 fopen 失败时,PHP 通常会引发警告。此函数添加了一个错误处理程序,它会检查错误并抛出异常。

函数 copy_to_string

函数 copy_to_string(StreamInterface $stream, $maxLen = -1)

将流的内容复制到字符串中,直到读取指定数量的字节。

函数 copy_to_stream

函数 copy_to_stream(StreamInterface $source, StreamInterface $dest, $maxLen = -1)

将流的内容复制到另一个流中,直到读取指定数量的字节。

函数 hash

函数 hash(StreamInterface $stream, $algo, $rawOutput = false)

计算流的哈希值。此方法读取整个流来计算滚动哈希(基于 PHP 的 hash_init 函数)。

函数 readline

函数 readline(StreamInterface $stream, $maxLength = null)

从流中读取一行,直到最大允许的缓冲区长度。

函数 parse_request

函数 parse_request($message)

将请求消息字符串解析为请求对象。

函数 parse_server_request

函数 parse_server_request($message, array $serverParams = array())

将请求消息字符串解析为服务器端请求对象。

函数 parse_response

函数 parse_response($message)

将响应消息字符串解析为响应对象。

函数 parse_query

函数 parse_query($str, $urlEncoding = true)

将查询字符串解析为关联数组。

如果为同一键找到多个值,则该键值对的值将成为一个数组。此函数不会将嵌套 PHP 风格数组解析为关联数组(例如,foo[a]=1&foo[b]=2 将解析为 ['foo[a]' => '1', 'foo[b]' => '2'])。

函数 build_query

函数 build_query(array $params, $encoding = PHP_QUERY_RFC3986)

从键值对数组构建查询字符串。

此函数可以使用 parseQuery() 的返回值构建查询字符串。此函数在遇到数组时不会修改提供的键(如 http_build_query 会做的那样)。

函数 mimetype_from_filename

函数 mimetype_from_filename($filename)

通过查看扩展名来确定文件的 MIME 类型。

函数 mimetype_from_extension

函数 mimetype_from_extension($extension)

将文件扩展名映射到 MIME 类型。

静态 URI 方法

RingCentral\Psr7\Uri 类有多个静态方法来操作 URI。

RingCentral\Psr7\Uri::removeDotSegments

public static function removeDotSegments($path) -> UriInterface

从路径中删除点段并返回新的路径。

参见 http://tools.ietf.org/html/rfc3986#section-5.2.4

RingCentral\Psr7\Uri::resolve

public static function resolve(UriInterface $base, $rel) -> UriInterface

解析基本 URI 和相对 URI 并返回新的 URI。

参见 http://tools.ietf.org/html/rfc3986#section-5

RingCentral\Psr7\Uri::withQueryValue

public static function withQueryValue(UriInterface $uri, $key, $value) -> UriInterface

创建一个新的 URI,具有特定的查询字符串值。

如果存在与提供的键完全匹配的现有查询字符串值,则将其删除并替换为给定的键值对。

注意:此函数将 "=" 转换为 "%3D" 和 "&" 转换为 "%26"。

RingCentral\Psr7\Uri::withoutQueryValue

public static function withoutQueryValue(UriInterface $uri, $key, $value) -> UriInterface

创建一个新的URI,移除特定的查询字符串值。

移除与提供的键完全匹配的任何现有查询字符串值。

注意:此函数将 "=" 转换为 "%3D" 和 "&" 转换为 "%26"。

RingCentral\Psr7\Uri::fromParts

public static function fromParts(array $parts) -> UriInterface

parse_url部分哈希创建一个RingCentral\Psr7\Uri对象。

未实现

本项目中未实现PSR-7的一些方面。欢迎对这些功能中的任何一个提交pull request。

  • Psr\Http\Message\ServerRequestInterface
  • Psr\Http\Message\UploadedFileInterface