Перейти к содержимому
docs.
WORKSPACE

Документации проектов

Открыть каталог проектов
Localzet ServerАсинхронный PHP-серверTriangleФреймворк и компонентыMIX IDИдентификация и интеграцииHack APIПубличные API хакатонаACNПроект и компонентыUserlay / UserNetПроект и компонентыAxiomПроект и компонентыМесседжинг через почтуПроект и компонентыLWT / JWT / JOSEПроект и компонентыФинансовая аналитика KimaiПроект и компонентыМультипарадигмальная БДПроект и компонентыZorin PlatformПроект и компонентыZorin TrustПроект и компонентыFloraScope / VegwatchПроект и компонентыaegis-qДокументация проектаaevumdbДокументация проектаauditДокументация проектаauto-invest-managerАвтотрейдерcaine-companionДокументация проектаcertbot-cloudflareДокументация проектаClusterLocalzet Server / ClustercmdveilДокументация проектаConsolePHP CLICronБиблиотеки Localzet Serverdata-connectorДокументация проектаdevice-controlDevice Controlenn-projectДокументация проектаEventsБиблиотеки Localzet ServerflowsintДокументация проектаforgeДокументация проектаHttpClientБиблиотеки Localzet ServerinterpreterСобственный языкinvariaДокументация проектаkeynotesДокументация проектаknotrouteKnotroutelaravel-personal-analyticsДокументация проектаlocalzet-osДокументация проектаmeridianДокументация проектаMQTTБиблиотеки Localzet Servernano-kernelСобственная ОСNoterДокументация проектаpersonal-analyticsДокументация проектаProxyБиблиотеки Localzet ServerPSRБиблиотеки Localzet ServerPSR-7Библиотеки Localzet Server✓rschs-monitorДокументация проектаsemsealДокументация проектаshardsealДокументация проектаSocketIOБиблиотеки Localzet ServerSOCKS5Библиотеки Localzet Serverstandalone-analyticsДокументация проектаstatic-php-cliДокументация проектаStorageБиблиотеки Localzet ServersutureДокументация проектаtempoweaveДокументация проектаttsСинтез речиTunnelБиблиотеки Localzet Serverui-kitUI KitvesperДокументация проектаWebAnalyzerWebAnalyzerzorin-access-brokerДокументация проектаzorin-authority-brokerДокументация проектаzorin-authority-sdk-phpДокументация проектаzorin-authority-sdk-pythonДокументация проектаzorin-authority-sdk-tsДокументация проектаzorin-client-intelДокументация проектаzorin-device-core-aospДокументация проектаzorin-host-agentДокументация проектаzorin-native-labДокументация проектаzorin-nodeДокументация проектаzorin-opsДокументация проектаdonstu-python-sdkДокументация проектаfloortraceFloortraceserver-rsLocalzet Server / Cluster
Каталог

PSR-7 предоставляет объекты HTTP-запросов и ответов, URI, потоки и вспомогательные функции для разбора HTTP-строк. Основные классы находятся в localzet\PSR7, но реализуют интерфейсы Psr\Http\Message из psr/http-message.

В этой реализации важно учитывать различия между объектами: часть with*-методов меняет текущий объект, часть возвращает копию. Нельзя переносить в код предположение о неизменяемости всех HTTP-сообщений.

Манифест требует PHP ^8.2, Localzet Server ^4.1 и psr/http-message ^2.0:

Окно терминала
composer config repositories.localzet-psr7 vcs https://github.com/localzet/PSR-7
composer require localzet/psr7:dev-main

Composer подключает классы через PSR-4 и функции из src/functions_include.php. Отдельный пакет localzet/psr не подменяет зависимость psr/http-message: namespaces у них различаются.

use localzet\PSR7\Request;
use localzet\PSR7\Response;
$request = new Request('get', 'https://example.test/items?page=2');
echo $request->getMethod(); // GET
echo $request->getRequestTarget(); // /items?page=2
$response = new Response(200, ['Content-Type' => 'application/json'], '{"ok":true}');
echo $response->getHeaderLine('content-type');
echo (string) $response->getBody();

Конструктор Request приводит метод к верхнему регистру. Если заголовок Host не задан, он формируется из URI с портом, когда он есть. Request target по умолчанию состоит из пути и query; для пустого пути используется /.

Response получает status, headers и строковое тело. Для известных кодов reason phrase подставляется автоматически, если не задан явно. Создание объекта не отправляет его клиенту — для этого нужен HTTP-сервер или клиентский transport.

$original = new Response(200);
$modified = $original->withHeader('X-Mode', 'preview');
var_dump($original === $modified); // true

withHeader, withAddedHeader, withoutHeader, withHeaders, withBody и withProtocolVersion меняют текущий объект и возвращают его. В Request это относится также к withMethod, withRequestTarget и withUri.

Напротив, Response::withStatus(), методы Uri::with*() и дополнительные ServerRequest::with*() для query/cookies/body/attributes/uploads возвращают копию, когда меняют значение. Не выводите семантику одного метода из поведения другого.

Если нужно изменить заголовки, сохранив исходный response, явно создайте копию:

$modified = clone $original;
$modified->withHeader('X-Mode', 'download');

Это поверхностная копия. Уже существующий body-stream остаётся общим объектом: чтение, запись или закрытие потока через одну копию влияет на другую. Для независимого тела создайте новый stream и назначьте его через withBody().

Имена ищутся без учёта регистра; исходное написание имени сохраняется в карте. getHeader() возвращает массив значений, getHeaderLine() соединяет их через , .

$response->withHeader('X-Tag', 'first');
$response->withAddedHeader('x-tag', 'second');
$values = $response->getHeader('X-TAG');
// ['first', 'second']

withHeader() заменяет значения, withAddedHeader() добавляет к ним. Дополнительный метод withHeaders() объединяет переданные значения с существующими; это не полная замена всей карты заголовков. setHeaders() вызывает ту же операцию объединения.

Не используйте getHeaderLine() для выдачи нескольких Set-Cookie: их нужно сохранять отдельными значениями. Перед установкой имени и значения из внешнего ввода валидируйте их; общие setters здесь в основном приводят значения к массиву и обрезают пробелы/табуляции, а не обеспечивают полную проверку HTTP-синтаксиса.

use localzet\PSR7\Stream;
$resource = fopen('php://temp', 'r+');
fwrite($resource, 'abcdef');
rewind($resource);
$stream = new Stream($resource);
echo $stream->read(2); // ab
echo $stream->getContents(); // cdef
$stream->rewind();
echo $stream->getContents(); // abcdef
$stream->close();

getContents() читает с текущей позиции, а не всегда с начала. (string) $stream пытается перейти к началу, если seek поддерживается, и читает содержимое. Конвертация в строку может загрузить всё тело в память.

close() закрывает underlying resource, detach() отдаёт resource вызывающему коду и отсоединяет его. После detach чтение, запись и seek больше недоступны. У сетевого/генерируемого потока может не быть размера или возможности перемотки.

Функция stream_for() имеет строковый параметр в реальной сигнатуре. Не ориентируйтесь на более широкий перечень типов в её старом DocBlock: для resource используйте new Stream($resource), для callback — new PumpStream($callback), для готового потока — withBody($stream).

use localzet\PSR7\Uri;
use localzet\PSR7\UriResolver;
$base = new Uri('https://example.test/api/items');
$next = UriResolver::resolve($base, new Uri('../users?page=2'));
$changed = $base->withQuery('page=3');

Uri разделяет scheme, user info, host, port, path, query и fragment. Методы изменения URI возвращают копию; исходный URI сохраняется. Query передаётся строкой без ?, fragment — без #.

В библиотеке есть helpers parse_query() и build_query(). Они оперируют параметрами, а не проверяют разрешённый hostname или право на исходящее соединение. URI-объект сам по себе не защищает от SSRF; такую проверку должен делать transport/приложение.

new ServerRequest($httpBuffer) принимает сырую HTTP-строку и использует парсер Localzet Server. Это не конструктор вида (method, uri), как у исходящего Request. Он заполняет server params из $_SERVER, а query/cookies/uploads — из разобранного запроса.

Для явного parsed body используйте результат withParsedBody($data); изменение query-параметров не переписывает query исходного URI. Attributes предназначены для данных middleware, а не для доверия входным HTTP-полям.

UploadedFile предоставляет поток загруженного файла и moveTo(). Имя и media type от клиента — metadata, а не основание доверять расширению или содержимому. Проверяйте ошибку загрузки и назначение пути до перемещения.

Классы, потоки, функции и ошибки

<?php
declare(strict_types=1);
use localzet\PSR7\Request;
use localzet\PSR7\Response;
use localzet\PSR7\Stream;
require __DIR__ . '/vendor/autoload.php';
$request = new Request('get', 'https://example.test/items?page=2');
$response = new Response(200, ['Content-Type' => 'application/json'], '{"ok":true}');
$tagged = $response->withHeader('X-Mode', 'preview');
$accepted = $response->withStatus(202);
$resource = fopen('php://temp', 'r+');
fwrite($resource, 'abcdef');
rewind($resource);
$stream = new Stream($resource);
try {
echo json_encode([
'method' => $request->getMethod(),
'target' => $request->getRequestTarget(),
'same_header_object' => $tagged === $response,
'original_status' => $response->getStatusCode(),
'new_status' => $accepted->getStatusCode(),
'first_bytes' => $stream->read(2),
'remaining_bytes' => $stream->getContents(),
], JSON_THROW_ON_ERROR), PHP_EOL;
} finally {
$stream->close();
}

Исходный код · AGPL-3.0