Attribute
2 years ago
Bundle
1 year ago
CacheClearer
2 years ago
CacheWarmer
1 year ago
Config
1 year ago
Controller
1 year ago
ControllerMetadata
1 year ago
DataCollector
1 year ago
Debug
1 year ago
DependencyInjection
1 year ago
Event
1 year ago
EventListener
1 year ago
Exception
1 year ago
Fragment
1 year ago
HttpCache
1 year ago
Log
1 year ago
Profiler
1 year ago
Resources
2 years ago
HttpClientKernel.php
1 year ago
HttpKernel.php
1 year ago
HttpKernelBrowser.php
1 year ago
HttpKernelInterface.php
1 year ago
Kernel.php
3 months ago
KernelEvents.php
2 years ago
KernelInterface.php
2 years ago
LICENSE
2 years ago
README.md
2 years ago
RebootableInterface.php
2 years ago
TerminableInterface.php
2 years ago
UriSigner.php
1 year ago
HttpKernel.php
252 lines
| 1 | <?php |
| 2 | |
| 3 | /* |
| 4 | * This file is part of the Symfony package. |
| 5 | * |
| 6 | * (c) Fabien Potencier <fabien@symfony.com> |
| 7 | * |
| 8 | * For the full copyright and license information, please view the LICENSE |
| 9 | * file that was distributed with this source code. |
| 10 | */ |
| 11 | namespace Matomo\Dependencies\Symfony\Component\HttpKernel; |
| 12 | |
| 13 | use Matomo\Dependencies\Symfony\Component\HttpFoundation\Exception\RequestExceptionInterface; |
| 14 | use Matomo\Dependencies\Symfony\Component\HttpFoundation\Request; |
| 15 | use Matomo\Dependencies\Symfony\Component\HttpFoundation\RequestStack; |
| 16 | use Matomo\Dependencies\Symfony\Component\HttpFoundation\Response; |
| 17 | use Matomo\Dependencies\Symfony\Component\HttpKernel\Controller\ArgumentResolver; |
| 18 | use Matomo\Dependencies\Symfony\Component\HttpKernel\Controller\ArgumentResolverInterface; |
| 19 | use Matomo\Dependencies\Symfony\Component\HttpKernel\Controller\ControllerResolverInterface; |
| 20 | use Matomo\Dependencies\Symfony\Component\HttpKernel\Event\ControllerArgumentsEvent; |
| 21 | use Matomo\Dependencies\Symfony\Component\HttpKernel\Event\ControllerEvent; |
| 22 | use Matomo\Dependencies\Symfony\Component\HttpKernel\Event\ExceptionEvent; |
| 23 | use Matomo\Dependencies\Symfony\Component\HttpKernel\Event\FinishRequestEvent; |
| 24 | use Matomo\Dependencies\Symfony\Component\HttpKernel\Event\RequestEvent; |
| 25 | use Matomo\Dependencies\Symfony\Component\HttpKernel\Event\ResponseEvent; |
| 26 | use Matomo\Dependencies\Symfony\Component\HttpKernel\Event\TerminateEvent; |
| 27 | use Matomo\Dependencies\Symfony\Component\HttpKernel\Event\ViewEvent; |
| 28 | use Matomo\Dependencies\Symfony\Component\HttpKernel\Exception\BadRequestHttpException; |
| 29 | use Matomo\Dependencies\Symfony\Component\HttpKernel\Exception\ControllerDoesNotReturnResponseException; |
| 30 | use Matomo\Dependencies\Symfony\Component\HttpKernel\Exception\HttpExceptionInterface; |
| 31 | use Matomo\Dependencies\Symfony\Component\HttpKernel\Exception\NotFoundHttpException; |
| 32 | use Matomo\Dependencies\Symfony\Contracts\EventDispatcher\EventDispatcherInterface; |
| 33 | // Help opcache.preload discover always-needed symbols |
| 34 | class_exists(ControllerArgumentsEvent::class); |
| 35 | class_exists(ControllerEvent::class); |
| 36 | class_exists(ExceptionEvent::class); |
| 37 | class_exists(FinishRequestEvent::class); |
| 38 | class_exists(RequestEvent::class); |
| 39 | class_exists(ResponseEvent::class); |
| 40 | class_exists(TerminateEvent::class); |
| 41 | class_exists(ViewEvent::class); |
| 42 | class_exists(KernelEvents::class); |
| 43 | /** |
| 44 | * HttpKernel notifies events to convert a Request object to a Response one. |
| 45 | * |
| 46 | * @author Fabien Potencier <fabien@symfony.com> |
| 47 | */ |
| 48 | class HttpKernel implements HttpKernelInterface, TerminableInterface |
| 49 | { |
| 50 | protected $dispatcher; |
| 51 | protected $resolver; |
| 52 | protected $requestStack; |
| 53 | private $argumentResolver; |
| 54 | public function __construct(EventDispatcherInterface $dispatcher, ControllerResolverInterface $resolver, ?RequestStack $requestStack = null, ?ArgumentResolverInterface $argumentResolver = null) |
| 55 | { |
| 56 | $this->dispatcher = $dispatcher; |
| 57 | $this->resolver = $resolver; |
| 58 | $this->requestStack = $requestStack ?? new RequestStack(); |
| 59 | $this->argumentResolver = $argumentResolver ?? new ArgumentResolver(); |
| 60 | } |
| 61 | /** |
| 62 | * {@inheritdoc} |
| 63 | */ |
| 64 | public function handle(Request $request, int $type = HttpKernelInterface::MAIN_REQUEST, bool $catch = \true) |
| 65 | { |
| 66 | $request->headers->set('X-Php-Ob-Level', (string) ob_get_level()); |
| 67 | $this->requestStack->push($request); |
| 68 | try { |
| 69 | return $this->handleRaw($request, $type); |
| 70 | } catch (\Exception $e) { |
| 71 | if ($e instanceof RequestExceptionInterface) { |
| 72 | $e = new BadRequestHttpException($e->getMessage(), $e); |
| 73 | } |
| 74 | if (\false === $catch) { |
| 75 | $this->finishRequest($request, $type); |
| 76 | throw $e; |
| 77 | } |
| 78 | return $this->handleThrowable($e, $request, $type); |
| 79 | } finally { |
| 80 | $this->requestStack->pop(); |
| 81 | } |
| 82 | } |
| 83 | /** |
| 84 | * {@inheritdoc} |
| 85 | */ |
| 86 | public function terminate(Request $request, Response $response) |
| 87 | { |
| 88 | $this->dispatcher->dispatch(new TerminateEvent($this, $request, $response), KernelEvents::TERMINATE); |
| 89 | } |
| 90 | /** |
| 91 | * @internal |
| 92 | */ |
| 93 | public function terminateWithException(\Throwable $exception, ?Request $request = null) |
| 94 | { |
| 95 | if (!($request = $request ?: $this->requestStack->getMainRequest())) { |
| 96 | throw $exception; |
| 97 | } |
| 98 | if ($pop = $request !== $this->requestStack->getMainRequest()) { |
| 99 | $this->requestStack->push($request); |
| 100 | } |
| 101 | try { |
| 102 | $response = $this->handleThrowable($exception, $request, self::MAIN_REQUEST); |
| 103 | } finally { |
| 104 | if ($pop) { |
| 105 | $this->requestStack->pop(); |
| 106 | } |
| 107 | } |
| 108 | $response->sendHeaders(); |
| 109 | $response->sendContent(); |
| 110 | $this->terminate($request, $response); |
| 111 | } |
| 112 | /** |
| 113 | * Handles a request to convert it to a response. |
| 114 | * |
| 115 | * Exceptions are not caught. |
| 116 | * |
| 117 | * @throws \LogicException If one of the listener does not behave as expected |
| 118 | * @throws NotFoundHttpException When controller cannot be found |
| 119 | */ |
| 120 | private function handleRaw(Request $request, int $type = self::MAIN_REQUEST) : Response |
| 121 | { |
| 122 | // request |
| 123 | $event = new RequestEvent($this, $request, $type); |
| 124 | $this->dispatcher->dispatch($event, KernelEvents::REQUEST); |
| 125 | if ($event->hasResponse()) { |
| 126 | return $this->filterResponse($event->getResponse(), $request, $type); |
| 127 | } |
| 128 | // load controller |
| 129 | if (\false === ($controller = $this->resolver->getController($request))) { |
| 130 | throw new NotFoundHttpException(sprintf('Unable to find the controller for path "%s". The route is wrongly configured.', $request->getPathInfo())); |
| 131 | } |
| 132 | $event = new ControllerEvent($this, $controller, $request, $type); |
| 133 | $this->dispatcher->dispatch($event, KernelEvents::CONTROLLER); |
| 134 | $controller = $event->getController(); |
| 135 | // controller arguments |
| 136 | $arguments = $this->argumentResolver->getArguments($request, $controller); |
| 137 | $event = new ControllerArgumentsEvent($this, $controller, $arguments, $request, $type); |
| 138 | $this->dispatcher->dispatch($event, KernelEvents::CONTROLLER_ARGUMENTS); |
| 139 | $controller = $event->getController(); |
| 140 | $arguments = $event->getArguments(); |
| 141 | // call controller |
| 142 | $response = $controller(...$arguments); |
| 143 | // view |
| 144 | if (!$response instanceof Response) { |
| 145 | $event = new ViewEvent($this, $request, $type, $response); |
| 146 | $this->dispatcher->dispatch($event, KernelEvents::VIEW); |
| 147 | if ($event->hasResponse()) { |
| 148 | $response = $event->getResponse(); |
| 149 | } else { |
| 150 | $msg = sprintf('The controller must return a "Symfony\\Component\\HttpFoundation\\Response" object but it returned %s.', $this->varToString($response)); |
| 151 | // the user may have forgotten to return something |
| 152 | if (null === $response) { |
| 153 | $msg .= ' Did you forget to add a return statement somewhere in your controller?'; |
| 154 | } |
| 155 | throw new ControllerDoesNotReturnResponseException($msg, $controller, __FILE__, __LINE__ - 17); |
| 156 | } |
| 157 | } |
| 158 | return $this->filterResponse($response, $request, $type); |
| 159 | } |
| 160 | /** |
| 161 | * Filters a response object. |
| 162 | * |
| 163 | * @throws \RuntimeException if the passed object is not a Response instance |
| 164 | */ |
| 165 | private function filterResponse(Response $response, Request $request, int $type) : Response |
| 166 | { |
| 167 | $event = new ResponseEvent($this, $request, $type, $response); |
| 168 | $this->dispatcher->dispatch($event, KernelEvents::RESPONSE); |
| 169 | $this->finishRequest($request, $type); |
| 170 | return $event->getResponse(); |
| 171 | } |
| 172 | /** |
| 173 | * Publishes the finish request event, then pop the request from the stack. |
| 174 | * |
| 175 | * Note that the order of the operations is important here, otherwise |
| 176 | * operations such as {@link RequestStack::getParentRequest()} can lead to |
| 177 | * weird results. |
| 178 | */ |
| 179 | private function finishRequest(Request $request, int $type) |
| 180 | { |
| 181 | $this->dispatcher->dispatch(new FinishRequestEvent($this, $request, $type), KernelEvents::FINISH_REQUEST); |
| 182 | } |
| 183 | /** |
| 184 | * Handles a throwable by trying to convert it to a Response. |
| 185 | * |
| 186 | * @throws \Exception |
| 187 | */ |
| 188 | private function handleThrowable(\Throwable $e, Request $request, int $type) : Response |
| 189 | { |
| 190 | $event = new ExceptionEvent($this, $request, $type, $e); |
| 191 | $this->dispatcher->dispatch($event, KernelEvents::EXCEPTION); |
| 192 | // a listener might have replaced the exception |
| 193 | $e = $event->getThrowable(); |
| 194 | if (!$event->hasResponse()) { |
| 195 | $this->finishRequest($request, $type); |
| 196 | throw $e; |
| 197 | } |
| 198 | $response = $event->getResponse(); |
| 199 | // the developer asked for a specific status code |
| 200 | if (!$event->isAllowingCustomResponseCode() && !$response->isClientError() && !$response->isServerError() && !$response->isRedirect()) { |
| 201 | // ensure that we actually have an error response |
| 202 | if ($e instanceof HttpExceptionInterface) { |
| 203 | // keep the HTTP status code and headers |
| 204 | $response->setStatusCode($e->getStatusCode()); |
| 205 | $response->headers->add($e->getHeaders()); |
| 206 | } else { |
| 207 | $response->setStatusCode(500); |
| 208 | } |
| 209 | } |
| 210 | try { |
| 211 | return $this->filterResponse($response, $request, $type); |
| 212 | } catch (\Exception $e) { |
| 213 | return $response; |
| 214 | } |
| 215 | } |
| 216 | /** |
| 217 | * Returns a human-readable string for the specified variable. |
| 218 | */ |
| 219 | private function varToString($var) : string |
| 220 | { |
| 221 | if (\is_object($var)) { |
| 222 | return sprintf('an object of type %s', \get_class($var)); |
| 223 | } |
| 224 | if (\is_array($var)) { |
| 225 | $a = []; |
| 226 | foreach ($var as $k => $v) { |
| 227 | $a[] = sprintf('%s => ...', $k); |
| 228 | } |
| 229 | return sprintf('an array ([%s])', mb_substr(implode(', ', $a), 0, 255)); |
| 230 | } |
| 231 | if (\is_resource($var)) { |
| 232 | return sprintf('a resource (%s)', get_resource_type($var)); |
| 233 | } |
| 234 | if (null === $var) { |
| 235 | return 'null'; |
| 236 | } |
| 237 | if (\false === $var) { |
| 238 | return 'a boolean value (false)'; |
| 239 | } |
| 240 | if (\true === $var) { |
| 241 | return 'a boolean value (true)'; |
| 242 | } |
| 243 | if (\is_string($var)) { |
| 244 | return sprintf('a string ("%s%s")', mb_substr($var, 0, 255), mb_strlen($var) > 255 ? '...' : ''); |
| 245 | } |
| 246 | if (is_numeric($var)) { |
| 247 | return sprintf('a number (%s)', (string) $var); |
| 248 | } |
| 249 | return (string) $var; |
| 250 | } |
| 251 | } |
| 252 |