vendor/friendsofsymfony/rest-bundle/EventListener/ViewResponseListener.php line 57

Open in your IDE?
  1. <?php
  2. /*
  3. * This file is part of the FOSRestBundle package.
  4. *
  5. * (c) FriendsOfSymfony <http://friendsofsymfony.github.com/>
  6. *
  7. * For the full copyright and license information, please view the LICENSE
  8. * file that was distributed with this source code.
  9. */
  10. namespace FOS\RestBundle\EventListener;
  11. use Doctrine\Common\Annotations\Reader;
  12. use Doctrine\Persistence\Proxy;
  13. use FOS\RestBundle\Controller\Annotations\View as ViewAnnotation;
  14. use FOS\RestBundle\FOSRestBundle;
  15. use FOS\RestBundle\View\View;
  16. use FOS\RestBundle\View\ViewHandlerInterface;
  17. use Symfony\Component\EventDispatcher\EventSubscriberInterface;
  18. use Symfony\Component\HttpFoundation\Response;
  19. use Symfony\Component\HttpKernel\Event\ControllerEvent;
  20. use Symfony\Component\HttpKernel\Event\ViewEvent;
  21. use Symfony\Component\HttpKernel\KernelEvents;
  22. /**
  23. * The ViewResponseListener class handles the kernel.view event and creates a {@see Response} for a {@see View} provided by the controller result.
  24. *
  25. * @author Lukas Kahwe Smith <smith@pooteeweet.org>
  26. *
  27. * @internal
  28. */
  29. class ViewResponseListener implements EventSubscriberInterface
  30. {
  31. /**
  32. * @var ViewHandlerInterface
  33. */
  34. private $viewHandler;
  35. private $forceView;
  36. /**
  37. * @var Reader|null
  38. */
  39. private $annotationReader;
  40. public function __construct(ViewHandlerInterface $viewHandler, bool $forceView, ?Reader $annotationReader = null)
  41. {
  42. $this->viewHandler = $viewHandler;
  43. $this->forceView = $forceView;
  44. $this->annotationReader = $annotationReader;
  45. }
  46. /**
  47. * Extracts configuration for a {@see ViewAnnotation} from the controller if present.
  48. */
  49. public function onKernelController(ControllerEvent $event)
  50. {
  51. $request = $event->getRequest();
  52. if (!$request->attributes->get(FOSRestBundle::ZONE_ATTRIBUTE, true)) {
  53. return;
  54. }
  55. $controller = $event->getController();
  56. if (!\is_array($controller) && method_exists($controller, '__invoke')) {
  57. $controller = [$controller, '__invoke'];
  58. }
  59. if (!\is_array($controller)) {
  60. return;
  61. }
  62. $className = $this->getRealClass(\get_class($controller[0]));
  63. $object = new \ReflectionClass($className);
  64. $method = $object->getMethod($controller[1]);
  65. /** @var ViewAnnotation|null $classConfiguration */
  66. $classConfiguration = null;
  67. /** @var ViewAnnotation|null $methodConfiguration */
  68. $methodConfiguration = null;
  69. if (null !== $this->annotationReader) {
  70. $classConfiguration = $this->getViewConfiguration($this->annotationReader->getClassAnnotations($object));
  71. $methodConfiguration = $this->getViewConfiguration($this->annotationReader->getMethodAnnotations($method));
  72. }
  73. if (80000 <= \PHP_VERSION_ID) {
  74. if (null === $classConfiguration) {
  75. $classAttributes = array_map(
  76. function (\ReflectionAttribute $attribute) {
  77. return $attribute->newInstance();
  78. },
  79. $object->getAttributes(ViewAnnotation::class, \ReflectionAttribute::IS_INSTANCEOF)
  80. );
  81. $classConfiguration = $this->getViewConfiguration($classAttributes);
  82. }
  83. if (null === $methodConfiguration) {
  84. $methodAttributes = array_map(
  85. function (\ReflectionAttribute $attribute) {
  86. return $attribute->newInstance();
  87. },
  88. $method->getAttributes(ViewAnnotation::class, \ReflectionAttribute::IS_INSTANCEOF)
  89. );
  90. $methodConfiguration = $this->getViewConfiguration($methodAttributes);
  91. }
  92. }
  93. // An annotation/attribute on the method takes precedence over the class level
  94. if (null !== $methodConfiguration) {
  95. $request->attributes->set(FOSRestBundle::VIEW_ATTRIBUTE, $methodConfiguration);
  96. } elseif (null !== $classConfiguration) {
  97. $request->attributes->set(FOSRestBundle::VIEW_ATTRIBUTE, $classConfiguration);
  98. }
  99. }
  100. public function onKernelView(ViewEvent $event): void
  101. {
  102. $request = $event->getRequest();
  103. if (!$request->attributes->get(FOSRestBundle::ZONE_ATTRIBUTE, true)) {
  104. return;
  105. }
  106. /** @var ViewAnnotation|null $configuration */
  107. $configuration = $request->attributes->get(FOSRestBundle::VIEW_ATTRIBUTE);
  108. $view = $event->getControllerResult();
  109. if (!$view instanceof View) {
  110. if (!$configuration instanceof ViewAnnotation && !$this->forceView) {
  111. return;
  112. }
  113. $view = new View($view);
  114. }
  115. if ($configuration instanceof ViewAnnotation) {
  116. if (null !== $configuration->getStatusCode() && (null === $view->getStatusCode() || Response::HTTP_OK === $view->getStatusCode())) {
  117. $view->setStatusCode($configuration->getStatusCode());
  118. }
  119. $context = $view->getContext();
  120. if ($configuration->getSerializerGroups()) {
  121. if (null === $context->getGroups()) {
  122. $context->setGroups($configuration->getSerializerGroups());
  123. } else {
  124. $context->setGroups(array_merge($context->getGroups(), $configuration->getSerializerGroups()));
  125. }
  126. }
  127. if (true === $configuration->getSerializerEnableMaxDepthChecks()) {
  128. $context->enableMaxDepth();
  129. } elseif (false === $configuration->getSerializerEnableMaxDepthChecks()) {
  130. $context->disableMaxDepth();
  131. }
  132. }
  133. if (null === $view->getFormat()) {
  134. $view->setFormat($request->getRequestFormat());
  135. }
  136. $event->setResponse($this->viewHandler->handle($view, $request));
  137. }
  138. public static function getSubscribedEvents(): array
  139. {
  140. return [
  141. KernelEvents::CONTROLLER => 'onKernelController',
  142. KernelEvents::VIEW => ['onKernelView', 30],
  143. ];
  144. }
  145. /**
  146. * @param object[] $annotations
  147. */
  148. private function getViewConfiguration(array $annotations): ?ViewAnnotation
  149. {
  150. $viewAnnotation = null;
  151. foreach ($annotations as $annotation) {
  152. if (!$annotation instanceof ViewAnnotation) {
  153. continue;
  154. }
  155. if (null === $viewAnnotation) {
  156. $viewAnnotation = $annotation;
  157. } else {
  158. throw new \LogicException('Multiple "view" annotations are not allowed.');
  159. }
  160. }
  161. return $viewAnnotation;
  162. }
  163. private function getRealClass(string $class): string
  164. {
  165. if (class_exists(Proxy::class)) {
  166. if (false === $pos = strrpos($class, '\\'.Proxy::MARKER.'\\')) {
  167. return $class;
  168. }
  169. return substr($class, $pos + Proxy::MARKER_LENGTH + 2);
  170. }
  171. return $class;
  172. }
  173. }