[FEATURE] Add support for PSR-15 HTTP middlewares
[Packages/TYPO3.CMS.git] / typo3 / sysext / core / Classes / Http / MiddlewareDispatcher.php
1 <?php
2 declare(strict_types = 1);
3 namespace TYPO3\CMS\Core\Http;
4
5 /*
6 * This file is part of the TYPO3 CMS project.
7 *
8 * It is free software; you can redistribute it and/or modify it under
9 * the terms of the GNU General Public License, either version 2
10 * of the License, or any later version.
11 *
12 * For the full copyright and license information, please read the
13 * LICENSE.txt file that was distributed with this source code.
14 *
15 * The TYPO3 project - inspiring people to share!
16 */
17
18 use Psr\Http\Message\ResponseInterface;
19 use Psr\Http\Message\ServerRequestInterface;
20 use Psr\Http\Server\MiddlewareInterface;
21 use Psr\Http\Server\RequestHandlerInterface;
22 use TYPO3\CMS\Core\Utility\GeneralUtility;
23
24 /**
25 * MiddlewareDispatcher
26 *
27 * This class manages and dispatches a PSR-15 middleware stack.
28 */
29 class MiddlewareDispatcher implements RequestHandlerInterface
30 {
31 /**
32 * Tip of the middleware call stack
33 *
34 * @var RequestHandlerInterface
35 */
36 protected $tip = null;
37
38 /**
39 * @param RequestHandlerInterface $kernel
40 * @param array $middlewares
41 */
42 public function __construct(
43 RequestHandlerInterface $kernel,
44 array $middlewares = []
45 ) {
46 $this->seedMiddlewareStack($kernel);
47
48 foreach ($middlewares as $middleware) {
49 if (is_string($middleware)) {
50 $this->lazy($middleware);
51 } else {
52 $this->add($middleware);
53 }
54 }
55 }
56
57 /**
58 * Invoke the middleware stack
59 *
60 * @param ServerRequestInterface $request
61 * @return ResponseInterface
62 */
63 public function handle(ServerRequestInterface $request): ResponseInterface
64 {
65 return $this->tip->handle($request);
66 }
67
68 /**
69 * Seed the middleware stack with the inner request handler
70 *
71 * @param RequestHandlerInterface $kernel
72 */
73 protected function seedMiddlewareStack(RequestHandlerInterface $kernel)
74 {
75 $this->tip = $kernel;
76 }
77
78 /**
79 * Add a new middleware to the stack
80 *
81 * Middlewares are organized as a stack. That means middlewares
82 * that have been added before will be executed after the newly
83 * added one (last in, first out).
84 *
85 * @param MiddlewareInterface $middleware
86 */
87 public function add(MiddlewareInterface $middleware)
88 {
89 $next = $this->tip;
90 $this->tip = new class($middleware, $next) implements RequestHandlerInterface {
91 private $middleware;
92 private $next;
93
94 public function __construct(MiddlewareInterface $middleware, RequestHandlerInterface $next)
95 {
96 $this->middleware = $middleware;
97 $this->next = $next;
98 }
99
100 public function handle(ServerRequestInterface $request): ResponseInterface
101 {
102 return $this->middleware->process($request, $this->next);
103 }
104 };
105 }
106
107 /**
108 * Add a new middleware by class name
109 *
110 * Middlewares are organized as a stack. That means middlewares
111 * that have been added before will be executed after the newly
112 * added one (last in, first out).
113 *
114 * @param string $middleware
115 */
116 public function lazy(string $middleware)
117 {
118 $next = $this->tip;
119 $this->tip = new class($middleware, $next) implements RequestHandlerInterface {
120 private $middleware;
121 private $next;
122
123 public function __construct(string $middleware, RequestHandlerInterface $next)
124 {
125 $this->middleware = $middleware;
126 $this->next = $next;
127 }
128
129 public function handle(ServerRequestInterface $request): ResponseInterface
130 {
131 $middleware = GeneralUtility::makeInstance($this->middleware);
132
133 if (!$middleware instanceof MiddlewareInterface) {
134 throw new \InvalidArgumentException(get_class($middleware) . ' does not implement ' . MiddlewareInterface::class, 1516821342);
135 }
136 return $middleware->process($request, $this->next);
137 }
138 };
139 }
140 }