hashhesh/psr7

PSR-7 消息实现,同时提供常用工具方法

dev-master / 2.4.x-dev 2023-01-13 08:08 UTC

This package is not auto-updated.

Last update: 2024-09-21 14:19:28 UTC


README

此仓库包含完整的 PSR-7 消息实现,多个流装饰器以及一些有用的功能,如查询字符串解析。

CI Static analysis

注意

此包与原始包相同,唯一不同的是命名空间。如果您正在开发一个系统,它使用旧的 psr7 版本或导致冲突,或者您无法升级核心应用程序的包版本,则可以使用此包。此包版本为 2.4.3

流实现

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

AppendStream

GuzzleHttp\Psr7\AppendStream

从多个流中按顺序读取。

use GuzzleHttp\Psr7;

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

$composed->addStream(Psr7\Utils::streamFor(' Above all listen to me'));

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

BufferStream

GuzzleHttp\Psr7\BufferStream

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

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

use GuzzleHttp\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 GuzzleHttp\Psr7;

$original = Psr7\Utils::streamFor(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

GuzzleHttp\Psr7\DroppingStream

当底层流的大小变得过大时,开始丢弃数据的流装饰器。

use GuzzleHttp\Psr7;

// Create an empty stream
$stream = Psr7\Utils::streamFor();

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

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

FnStream

GuzzleHttp\Psr7\FnStream

基于函数哈希的组合流实现。

允许轻松测试和扩展提供的流,而无需为简单的扩展点创建具体类。

use GuzzleHttp\Psr7;

$stream = Psr7\Utils::streamFor('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

GuzzleHttp\Psr7\InflateStream

使用 PHP 的 zlib.inflate 过滤器来膨胀 zlib(HTTP 缩放,RFC1950)或 gzipped(RFC1952)内容。

此流装饰器将提供的流转换为 PHP 流资源,然后附加 zlib.inflate 过滤器。然后将流转换回 Guzzle 流资源以用作 Guzzle 流。

LazyOpenStream

GuzzleHttp\Psr7\LazyOpenStream

在流上执行 IO 操作后才懒洋洋地读取或写入文件。

use GuzzleHttp\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

GuzzleHttp\Psr7\LimitStream

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

use GuzzleHttp\Psr7;

$original = Psr7\Utils::streamFor(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

GuzzleHttp\Psr7\MultipartStream

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

NoSeekStream

GuzzleHttp\Psr7\NoSeekStream

NoSeekStream 包装一个流,不允许定位。

use GuzzleHttp\Psr7;

$original = Psr7\Utils::streamFor('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

GuzzleHttp\Psr7\PumpStream

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

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

实现流装饰器

由于存在GuzzleHttp\Psr7\StreamDecoratorTrait,创建流装饰器非常简单。此特质提供了通过代理到底层流来实现Psr\Http\Message\StreamInterface的方法。只需使用use语句和实现您自己的自定义方法。

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

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

class EofCallbackStream implements StreamInterface
{
    use StreamDecoratorTrait;

    private $callback;

    private $stream;

    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 GuzzleHttp\Psr7;

$original = Psr7\Utils::streamFor('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 StreamWrapper

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

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

use GuzzleHttp\Psr7\StreamWrapper;

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

静态API

GuzzleHttp\Psr7命名空间下提供了各种静态方法。

GuzzleHttp\Psr7\Message::toString

public static function toString(MessageInterface $message): string

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

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

GuzzleHttp\Psr7\Message::bodySummary

public static function bodySummary(MessageInterface $message, int $truncateAt = 120): string|null

获取消息体的简短摘要。

如果响应不可打印,将返回null

GuzzleHttp\Psr7\Message::rewindBody

public static function rewindBody(MessageInterface $message): void

尝试重置消息体,失败时抛出异常。

只有当tell()调用返回值不等于0时,消息体才会被重置。

GuzzleHttp\Psr7\Message::parseMessage

public static function parseMessage(string $message): array

将HTTP消息解析为关联数组。

数组包含“start-line”键,包含消息的开始行,“headers”键,包含头数组值的关联数组,以及包含消息体的“body”键。

GuzzleHttp\Psr7\Message::parseRequestUri

public static function parseRequestUri(string $path, array $headers): string

为HTTP请求消息构造URI。

GuzzleHttp\Psr7\Message::parseRequest

public static function parseRequest(string $message): Request

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

GuzzleHttp\Psr7\Message::parseResponse

public static function parseResponse(string $message): Response

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

GuzzleHttp\Psr7\Header::parse

public static function parse(string|array $header): array

解析包含";"分隔数据的头值数组,将其解析为表示头键值对的关联数组。如果参数不包含值,只包含键,则此函数将注入一个空字符串值的键。

GuzzleHttp\Psr7\Header::splitList

public static function splitList(string|string[] $header): string[]

将定义为包含逗号分隔列表的HTTP头分割为每个单独的值

$knownEtags = Header::splitList($request->getHeader('if-none-match'));

示例头包括acceptcache-controlif-none-match

GuzzleHttp\Psr7\Header::normalize (已弃用)

public static function normalize(string|array $header): array

Header::normalize()已弃用,改用Header::splitList(),它具有更干净的API和改进的文档。

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

GuzzleHttp\Psr7\Query::parse

public static function parse(string $str, int|bool $urlEncoding = true): array

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

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

GuzzleHttp\Psr7\Query::build

public static function build(array $params, int|false $encoding = PHP_QUERY_RFC3986): string

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

此函数可以使用 parse() 的返回值来构建查询字符串。当遇到数组时,此函数不会修改提供的键(类似于 http_build_query())。

GuzzleHttp\Psr7\Utils::caselessRemove

public static function caselessRemove(iterable $keys, $keys, array $data): array

从数据中,根据键不区分大小写地移除项目。

GuzzleHttp\Psr7\Utils::copyToStream

public static function copyToStream(StreamInterface $source, StreamInterface $dest, int $maxLen = -1): void

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

GuzzleHttp\Psr7\Utils::copyToString

public static function copyToString(StreamInterface $stream, int $maxLen = -1): string

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

GuzzleHttp\Psr7\Utils::hash

public static function hash(StreamInterface $stream, string $algo, bool $rawOutput = false): string

计算流的一个哈希值。

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

GuzzleHttp\Psr7\Utils::modifyRequest

public static function modifyRequest(RequestInterface $request, array $changes): RequestInterface

克隆并修改具有给定更改的请求。

此方法对于减少变异消息所需的克隆数量非常有用。

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

GuzzleHttp\Psr7\Utils::readLine

public static function readLine(StreamInterface $stream, int $maxLength = null): string

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

GuzzleHttp\Psr7\Utils::streamFor

public static function streamFor(resource|string|null|int|float|bool|StreamInterface|callable|\Iterator $resource = '', array $options = []): StreamInterface

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

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

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

此方法接受以下 $resource 类型

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

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

$stream = GuzzleHttp\Psr7\Utils::streamFor($generator(100));

GuzzleHttp\Psr7\Utils::tryFopen

public static function tryFopen(string $filename, string $mode): resource

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

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

GuzzleHttp\Psr7\Utils::tryGetContents

public static function tryGetContents(resource $stream): string

安全地获取给定流的全部内容。

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

GuzzleHttp\Psr7\Utils::uriFor

public static function uriFor(string|UriInterface $uri): UriInterface

为给定值返回一个 UriInterface。

此函数接受一个字符串或 UriInterface,并返回给定值的 UriInterface。如果值已经是 UriInterface,则直接返回。

GuzzleHttp\Psr7\MimeType::fromFilename

public static function fromFilename(string $filename): string|null

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

GuzzleHttp\Psr7\MimeType::fromExtension

public static function fromExtension(string $extension): string|null

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

从函数API升级

静态API首次在1.7.0版本中引入,以减轻全局和局部包副本之间函数冲突的问题。在2.0.0版本中移除了函数API。为了方便,这里提供了一份迁移表。

额外的URI方法

除了以 GuzzleHttp\Psr7\Uri 类的形式提供的标准 Psr\Http\Message\UriInterface 实现,此库还提供了在处理URI时的额外功能,作为静态方法。

URI类型

Psr\Http\Message\UriInterface 的实例可以是绝对URI或相对引用。绝对URI有一个方案。相对引用用于表达相对于另一个URI(基础URI)的URI。相对引用可以根据 RFC 3986第4.2节 分为几种形式。

  • 网络路径引用,例如 //example.com/path
  • 绝对路径引用,例如 /path
  • 相对路径引用,例如 subpath

可以使用以下方法来识别URI的类型。

GuzzleHttp\Psr7\Uri::isAbsolute

public static function isAbsolute(UriInterface $uri): bool

URI是否为绝对URI,即它有一个方案。

GuzzleHttp\Psr7\Uri::isNetworkPathReference

public static function isNetworkPathReference(UriInterface $uri): bool

URI是否为网络路径引用。以两个斜杠字符开始的相对引用称为网络路径引用。

GuzzleHttp\Psr7\Uri::isAbsolutePathReference

public static function isAbsolutePathReference(UriInterface $uri): bool

URI是否为绝对路径引用。以单个斜杠字符开始的相对引用称为绝对路径引用。

GuzzleHttp\Psr7\Uri::isRelativePathReference

public static function isRelativePathReference(UriInterface $uri): bool

URI是否为相对路径引用。不以斜杠字符开始的相对引用称为相对路径引用。

GuzzleHttp\Psr7\Uri::isSameDocumentReference

public static function isSameDocumentReference(UriInterface $uri, UriInterface $base = null): bool

URI是否是同一文档引用。同一文档引用是指除了其片段组件外,与基本URI相同的URI。如果没有提供基本URI,则只有空URI引用(除了其片段)被认为是同一文档引用。

URI组件

处理URI组件的额外方法。

GuzzleHttp\Psr7\Uri::isDefaultPort

public static function isDefaultPort(UriInterface $uri): bool

URI是否具有当前方案的默认端口。Psr\Http\Message\UriInterface::getPort可能返回null或标准端口。此方法可以独立于实现使用。

GuzzleHttp\Psr7\Uri::composeComponents

public static function composeComponents($scheme, $authority, $path, $query, $fragment): string

根据RFC 3986第5.3节,从其各种组件组合成URI引用字符串。通常,不需要手动调用此方法,而是通过Psr\Http\Message\UriInterface::__toString间接使用。

GuzzleHttp\Psr7\Uri::fromParts

public static function fromParts(array $parts): UriInterface

parse_url组件的哈希中创建URI。

GuzzleHttp\Psr7\Uri::withQueryValue

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

创建一个新的URI,具有特定的查询字符串值。任何与提供的键完全匹配的现有查询字符串值都会被移除并替换为给定的键值对。null值将设置查询字符串键而没有值,例如“key”而不是“key=value”。

GuzzleHttp\Psr7\Uri::withQueryValues

public static function withQueryValues(UriInterface $uri, array $keyValueArray): UriInterface

创建一个新的URI,具有多个查询字符串值。它具有与withQueryValue()相同的行为,但用于键 => 值关联数组。

GuzzleHttp\Psr7\Uri::withoutQueryValue

public static function withoutQueryValue(UriInterface $uri, $key): UriInterface

创建一个新的URI,移除特定的查询字符串值。任何与提供的键完全匹配的现有查询字符串值都会被移除。

跨源检测

GuzzleHttp\Psr7\UriComparator提供方法以确定修改后的URL是否应被视为跨源。

GuzzleHttp\Psr7\UriComparator::isCrossOrigin

public static function isCrossOrigin(UriInterface $original, UriInterface $modified): bool

确定修改后的URL是否应被视为相对于原始URL的跨源。

引用解析

GuzzleHttp\Psr7\UriResolver提供方法,根据RFC 3986第5节在基本URI的上下文中解析URI引用。这例如也是网络浏览器在基于当前请求URI解析网站中的链接时所执行的操作。

GuzzleHttp\Psr7\UriResolver::resolve

public static function resolve(UriInterface $base, UriInterface $rel): UriInterface

将相对URI转换为相对于基本URI解析的新URI。

GuzzleHttp\Psr7\UriResolver::removeDotSegments

public static function removeDotSegments(string $path): string

根据RFC 3986第5.2.4节,从路径中删除点段并返回新的路径。

GuzzleHttp\Psr7\UriResolver::relativize

public static function relativize(UriInterface $base, UriInterface $target): UriInterface

将目标URI作为从基本URI的相对引用返回。此方法与resolve()方法相对应。

(string) $target === (string) UriResolver::resolve($base, UriResolver::relativize($base, $target))

一个用例是使用当前请求URI作为基本URI,然后在文档中生成相对链接以减少文档大小或提供自包含的可下载文档存档。

$base = new Uri('http://example.com/a/b/');
echo UriResolver::relativize($base, new Uri('http://example.com/a/b/c'));  // prints 'c'.
echo UriResolver::relativize($base, new Uri('http://example.com/a/x/y'));  // prints '../x/y'.
echo UriResolver::relativize($base, new Uri('http://example.com/a/b/?q')); // prints '?q'.
echo UriResolver::relativize($base, new Uri('http://example.org/a/b/'));   // prints '//example.org/a/b/'.

规范化和比较

GuzzleHttp\Psr7\UriNormalizer提供方法以根据RFC 3986第6节规范化和比较URI。

GuzzleHttp\Psr7\UriNormalizer::normalize

public static function normalize(UriInterface $uri, $flags = self::PRESERVING_NORMALIZATIONS): UriInterface

返回一个标准化后的URI。方案和主机组件已根据PSR-7 UriInterface规范转换为小写。此方法通过$flags参数添加额外的标准化,该参数是一个要应用的标准化的位掩码。以下是一些可用的标准化:

  • UriNormalizer::PRESERVING_NORMALIZATIONS

    默认的标准化,仅包括保留语义的标准化。

  • UriNormalizer::CAPITALIZE_PERCENT_ENCODING

    百分编码三元组中的所有字母(例如,"%3A")不区分大小写,应大写。

    示例: http://example.org/a%c2%b1bhttp://example.org/a%C2%B1b

  • UriNormalizer::DECODE_UNRESERVED_CHARACTERS

    解码未保留字符的百分编码八位字节。为了保持一致性,URI生产者不应创建范围在ALPHA (%41–%5A和%61–%7A)、DIGIT (%30–%39)、连字符 (%2D)、点 (%2E)、下划线 (%5F) 或波浪号 (%7E) 的百分编码八位字节,当它们出现在URI中时,URI标准化器应将它们解码为相应的未保留字符。

    示例: http://example.org/%7Eusern%61me/http://example.org/~username/

  • UriNormalizer::CONVERT_EMPTY_PATH

    将空路径转换为"/"(对于http和https URI)。

    示例: http://example.orghttp://example.org/

  • UriNormalizer::REMOVE_DEFAULT_HOST

    从URI中移除给定URI方案的默认主机。只有“file”方案定义了默认主机“localhost”。所有file:/myfilefile:///myfilefile://localhost/myfile都根据RFC 3986是等效的。

    示例: file://localhost/myfilefile:///myfile

  • UriNormalizer::REMOVE_DEFAULT_PORT

    从URI中移除给定URI方案的默认端口。

    示例: http://example.org:80/http://example.org/

  • UriNormalizer::REMOVE_DOT_SEGMENTS

    移除不必要的点段。相对路径引用中的点段不会被移除,因为这会改变URI引用的语义。

    示例: http://example.org/../a/b/../c/./d.htmlhttp://example.org/a/c/d.html

  • UriNormalizer::REMOVE_DUPLICATE_SLASHES

    包含两个或更多连续斜线的路径被转换为一个。Web服务器通常忽略重复的斜线,并将这些URI视为等效。但从理论上讲,这些URI不需要等效。因此,这种标准化可能会改变语义。编码的斜线(%2F)不会被移除。

    示例: http://example.org//foo///bar.htmlhttp://example.org/foo/bar.html

  • UriNormalizer::SORT_QUERY_PARAMETERS

    按字母顺序对查询参数及其值进行排序。然而,URI中参数的顺序可能很重要(这并非由标准定义)。因此,这种标准化是不安全的,可能会改变URI的语义。

    示例: ?lang=en&article=fred?article=fred&lang=en

GuzzleHttp\Psr7\UriNormalizer::isEquivalent

public static function isEquivalent(UriInterface $uri1, UriInterface $uri2, $normalizations = self::PRESERVING_NORMALIZATIONS): bool

判断两个URI是否可以视为等效。在比较之前,两个URI都会自动根据给定的$normalizations位掩码进行标准化。此方法还接受相对URI引用,并在它们等效时返回true。这当然假设它们将与相同的基URI解析。如果不是这种情况,相对引用的等效性或差异的判断没有任何意义。

版本指导

安全

如果您在此包中发现安全漏洞,请发送电子邮件至[email protected]。所有安全漏洞都将得到及时处理。请在修复宣布之前不要公开披露与安全相关的问题。有关更多信息,请参阅安全策略

许可证

本项目采用MIT许可证(MIT)。请参阅许可证文件获取更多信息。

针对企业

作为Tidelift订阅的一部分提供

Guzzle和其他数千个包的维护者正在与Tidelift合作,为您提供用于构建应用程序的开源依赖项的商业支持和维护。通过支付您所使用确切的依赖项的维护者,节省时间,降低风险,并提高代码质量。了解更多信息。