Исходники: PHP: class Handler { public function handle(Request $request): Response { return new Response(__METHOD__); } } class Pipeline { private $handler; private $middleware; public function __construct(Handler $handler, array $middleware) { $this->handler = $handler; $this->middleware = $middleware; } public function handle(Request $request): Response { $middleware = array_shift($this->middleware); if ($middleware !== null) { return $middleware->handle($request, [$this, 'handle']); } return $this->handler->handle($request); } } class Application { private $middleware; public function __construct(array $middleware) { $this->middleware = $middleware; } public function handle(Request $request): Response { return (new Pipeline(new Handler(), $this->middleware))->handle($request); } } Классы для массива middleware PHP: class A { public function handle(Request $request, callable $next): Response { return $next($request); } } class B { public function handle(Request $request, callable $next): Response { return $next($request); } } Собираем PHP: $application = new Application([ new A(), new B() ]); $response = $application->handle(new Request()); $response->send(); // Handler::handle Теперь, что мне непонятно? Если я сделаю, например: PHP: class A { public function handle(Request $request, callable $next): Response { $response = $next($request); $response->setBody(__METHOD__); return $response; } } class B { public function handle(Request $request, callable $next): Response { $response = $next($request); $response->setBody(__METHOD__); return $response; } } То я имею в выводе - Код (Text): A::handle То есть в этом месте ломается мозг: отрабатывают ВСЕ классы массива middleware (A, B) и основной класс - Handler, я для теста в каждом из них задаю тело ответа, НО в ответе я имею содержимое тела, установленное в первом классе массива, то есть ладно там массив, но ведь в Pipeline по окончанию массива отрабатывает Handler, а в нём явно указано: PHP: class Handler { public function handle(Request $request): Response { return new Response(__METHOD__); // тут мы создаём НОВЫЙ объект, то есть в ответе должно быть - Handler::handle } } Нифига я не врублюсь: почему отработали все классы И каждый по порядку обработки установил новое значение тела ответа, НО в ответе у меня содержимое, установленное в классе, который идёт первым? ))) p.s. оно вроде и правильно, если что-то изменилось в ответе, то оно и должно вывестись только то, которое первое изменилось, но я тупо не пойму, как его не перезаписывают остальные хендлеры, я специально в каждом создал разное содержимое тела ответа ))) они ведь отрабатывают - ПОСЛЕ
блин пока вопрос создавал по ходу догнал: Response создаётся в конце работы, доступ к нему идёт рекурсивно через $next, то есть объект ответа - ОДИН, мы меняем его содержимое типа как по ссылке на него через $next, получается что мы получаем ответ шиворот-навыворот))) то есть в ответе последнего класса Pipeline будет тело наипервейшего класса массива, где оно установлено, изначально оно установлено в основном Handler, а потом мы его меняем в любом middleware разобрался, кому не лень можете поделиться, а так уже не нужно
PHP: class Handler { public function handle(Request $request): Response { return new Response(__METHOD__); // тут мы создаём НОВЫЙ объект, то есть в ответе должно быть - Handler::handle } } Непонятно написал. После return у тебя уже ничего не может выполняться. В ответе должен быть объект класса Response. PHP: $response = $application->handle(new Request()); $response->send(); // Handler::handle В переменной $response будет объект, который ты породил в Handler и потом нигде не изменяешь. При такой организации, каждый из цепочки "посредников" получает управление как до, так и после рождения "ответа". Они выполняются не просто по очереди, а вложены как матрёшки. В зависимости от каких-то условий, "посредники" могут прервать выполнение, или модифицировать ответ. В твоем примере не видно чего ты хотел добиться. Никто ничего не делает и логи не ведутся.
Поясню про до и после: PHP: public function handle(Request $request, callable $next) { // place to do something before Handler, break an exception for example. $response = $next($request); // place to do something after Handler, modify or replace response object, for example. return $response; } и если бы был отладочный вывод, ты мог бы увидеть такое: Код (Text): A before B before Handler::handle() B after A after Response::send()
@artoodetoo ага, я как раз в разных вариантах стал колупать и понял суть, там before и after получается, то есть в Handler мы просто ссылаемся на Response-объект и по дефолту делаем - ДО спс --- Добавлено --- @artoodetoo а можно такой вопрос: вот есть массив middleware, а можно ли в каком-то из классов этого массива, пополнить массив middleware? потому что мы array_shift же используем, а значит наш массив middleware - может мутировать, он не иммутабельный есть ли смысл в этом направлении пилить? хочу типа в каком-то хендлере получать объект в котором есть свои предназначенные для него middleware, добавлять их а общий массив middleware, чтобы они выпонились в общем массиве так возможно? то есть во время обработки массива пополнять его и продолжать его обрабатывать
Реализовать конечно можно всё, в Laravel есть Controller middleware, например. Я не смотрел как это реализовано. Твой конструктор Pipeline наверное мог бы делать array_unshift или array_push к массиву "посредников" если переданный ему объект Handler имеет непустое свойство $middleware. Такое приходит на ум.
PHP: class App extends RouteCollector { private $container; private $middleware = []; public function __construct(Container $container = null) { $this->container = $container ?? new Container(); } public function getContainer(): Container { return $this->container; } public function addMiddleware(string $class): void { $this->middleware[] = $class; } public function mergeMiddleware(array $middleware): void { $this->middleware = array_merge($this->middleware, $middleware); } public function __invoke(Request $request): Response { $middleware = array_shift($this->middleware); if ($middleware !== null) { return (new $middleware)($request, $this); } /** @var Result $result */ $result = $this->container->get(Result::class); return $this->container->get($result->getHandler())($request, new Response(), ...$result->getParameters()); } } class RoutingMiddleware { public function __invoke(Request $request, App $app): Response { foreach ($app->getRoutes() as $route) { if ($route->getMethods() && !in_array($request->getMethod(), $route->getMethods(), true)) { continue; } $pattern = preg_replace('~{([a-z]+):([^}]+)}~', '(?P<$1>$2)', $route->getPattern()); if (preg_match("~^$pattern$~", $request->getPath(), $matches)) { $app->mergeMiddleware($route->getMiddleware()); // добавили массив во время выполнения $app->getContainer()->set(Result::class, function () use ($matches, $route) { return new Result( $route->getHandler(), array_values(array_filter($matches, 'is_string', ARRAY_FILTER_USE_KEY)) ); }); return $app($request); } } throw new Exception("Не найден {$request->getMethod()}-маршрут: {$request->getPath()}"); } } $app = new App(); $app->get('/', HomeAction::class) ->addMiddleware(FirstRouteMiddleware::class) ->addMiddleware(SecondRouteMiddleware::class); $app->addMiddleware(AppMiddleware::class); $app->addMiddleware(RoutingMiddleware::class); $response = $app(new Request()); $response->send(); выхлоп Код (Text): 'App\Support\Middleware\AppMiddleware::__invoke' 'App\Support\Middleware\FirstRouteMiddleware::__invoke' 'App\Support\Middleware\SecondRouteMiddleware::__invoke' 'App\Actions\Guest\HomeAction::__invoke' годиться или фуйню порю?
Во первых почему Мозги App прилеплены с задницей RouteCollector ? Давай торты будут делать со вкусом туалетной бумаги. Тортик Zewa - мягкое как пердышко.