<?php
declare(strict_types=1);
/**
* Даны два разных механизма расширения обработки Http-сообщений:
* один в стиле PSR-7, другой — в стиле Symfony HttpFoundation.
*
* Нужно написать класс, который позволит интегрировать
* подписчик от Symfony в PSR фреймворк.
*
* Всё необходимое для решения задачи есть в этом файле.
*/
namespace Psr;
/**
* Request иммутабельный, как в реальном PSR-7.
* Для простоты оставим только тело запроса.
*/
final readonly class Request
{
public function __construct(
public string $body,
) {}
}
/**
* Для простоты представим, что Response — это любое mixed значение.
*/
interface RequestHandler
{
public function handle(Request $request): mixed;
}
interface Middleware
{
public function process(Request $request, RequestHandler $handler): mixed;
}
namespace Symfony;
/**
* Здесь уже Request мутабельный, как в реальном Symfony HttpFoundation.
*/
final class Request
{
public function __construct(
public string $body,
) {}
}
final readonly class RequestEvent
{
public function __construct(
public Request $request,
) {}
}
/**
* Тут Response тоже представлен любым mixed значением.
* Обрати внимание, что $response можно менять на объекте ивента.
*/
final class ResponseEvent
{
public function __construct(
public readonly Request $request,
public mixed $response,
) {}
}
interface Subscriber
{
public function onRequest(RequestEvent $event): void;
public function onResponse(ResponseEvent $event): void;
}
namespace Solution;
use Psr\Middleware;
use Psr\Request as PsrRequest;
use Psr\RequestHandler;
use Symfony\RequestEvent;
use Symfony\ResponseEvent;
use Symfony\Subscriber;
use Symfony\Request as SymfonyRequest;
final readonly class FiberRequestHandler implements RequestHandler
{
public function handle(PsrRequest $request): mixed
{
return \Fiber::suspend($request);
}
}
final class MiddlewareToSubscriber implements Subscriber
{
/**
* @var \WeakMap<SymfonyRequest, \Fiber>
*/
private \WeakMap $requestFibers;
public function __construct(
private readonly Middleware $middleware,
) {
/** @var \WeakMap<SymfonyRequest, \Fiber> */
$this->requestFibers = new \WeakMap();
}
public function onRequest(RequestEvent $event): void
{
$fiber = new \Fiber(fn(): mixed => $this->middleware->process(new PsrRequest($event->request->body), new FiberRequestHandler()));
$newRequest = $fiber->start();
$event->request->body = $newRequest->body;
$this->requestFibers[$event->request] = $fiber;
}
public function onResponse(ResponseEvent $event): void
{
$fiber = $this->requestFibers[$event->request];
$fiber->resume($event->response);
$event->response = $fiber->getReturn();
unset($this->requestFibers[$event->request]);
}
}
final class DumpMiddleware implements Middleware
{
public function process(PsrRequest $request, RequestHandler $handler): mixed
{
var_dump($request);
$response = $handler->handle($request);
var_dump($response);
return 'modified response';
}
}
$subscriber = new MiddlewareToSubscriber(new DumpMiddleware());
$requestEvent = new RequestEvent(new SymfonyRequest('body'));
$subscriber->onRequest($requestEvent);
var_dump('Handling request');
$responseEvent = new ResponseEvent($requestEvent->request, 'response');
$subscriber->onResponse($responseEvent);
var_dump($responseEvent->response);
Parse error: syntax error, unexpected token "readonly", expecting "abstract" or "final" or "class" in /in/0SjiQ on line 20
Process exited with code 255.