1 | <?php |
||
2 | |||
3 | /* |
||
4 | * This file is part of the API Platform project. |
||
5 | * |
||
6 | * (c) Kévin Dunglas <[email protected]> |
||
7 | * |
||
8 | * For the full copyright and license information, please view the LICENSE |
||
9 | * file that was distributed with this source code. |
||
10 | */ |
||
11 | |||
12 | declare(strict_types=1); |
||
13 | |||
14 | namespace ApiPlatform\Core\EventListener; |
||
15 | |||
16 | use ApiPlatform\Core\Exception\RuntimeException; |
||
17 | use ApiPlatform\Core\Metadata\Resource\Factory\ResourceMetadataFactoryInterface; |
||
18 | use ApiPlatform\Core\Metadata\Resource\ToggleableOperationAttributeTrait; |
||
19 | use ApiPlatform\Core\Serializer\ResourceList; |
||
20 | use ApiPlatform\Core\Serializer\SerializerContextBuilderInterface; |
||
21 | use ApiPlatform\Core\Util\RequestAttributesExtractor; |
||
22 | use Fig\Link\GenericLinkProvider; |
||
23 | use Fig\Link\Link; |
||
24 | use Symfony\Component\HttpFoundation\Request; |
||
25 | use Symfony\Component\HttpFoundation\Response; |
||
26 | use Symfony\Component\HttpKernel\Event\GetResponseForControllerResultEvent; |
||
27 | use Symfony\Component\Serializer\Encoder\EncoderInterface; |
||
28 | use Symfony\Component\Serializer\Normalizer\AbstractObjectNormalizer; |
||
29 | use Symfony\Component\Serializer\SerializerInterface; |
||
30 | |||
31 | /** |
||
32 | * Serializes data. |
||
33 | * |
||
34 | * @author Kévin Dunglas <[email protected]> |
||
35 | */ |
||
36 | final class SerializeListener |
||
37 | { |
||
38 | use ToggleableOperationAttributeTrait; |
||
39 | |||
40 | public const OPERATION_ATTRIBUTE_KEY = 'serialize'; |
||
41 | |||
42 | private $serializer; |
||
43 | private $serializerContextBuilder; |
||
44 | |||
45 | public function __construct(SerializerInterface $serializer, SerializerContextBuilderInterface $serializerContextBuilder, ResourceMetadataFactoryInterface $resourceMetadataFactory = null) |
||
46 | { |
||
47 | $this->serializer = $serializer; |
||
48 | $this->serializerContextBuilder = $serializerContextBuilder; |
||
49 | $this->resourceMetadataFactory = $resourceMetadataFactory; |
||
50 | } |
||
51 | |||
52 | /** |
||
53 | * Serializes the data to the requested format. |
||
54 | */ |
||
55 | public function onKernelView(GetResponseForControllerResultEvent $event): void |
||
56 | { |
||
57 | $controllerResult = $event->getControllerResult(); |
||
58 | $request = $event->getRequest(); |
||
59 | |||
60 | if ( |
||
61 | $controllerResult instanceof Response |
||
62 | || !(($attributes = RequestAttributesExtractor::extractAttributes($request))['respond'] ?? $request->attributes->getBoolean('_api_respond', false)) |
||
63 | || $attributes && $this->isOperationAttributeDisabled($attributes, self::OPERATION_ATTRIBUTE_KEY) |
||
0 ignored issues
–
show
|
|||
64 | ) { |
||
65 | return; |
||
66 | } |
||
67 | |||
68 | if (!$attributes) { |
||
0 ignored issues
–
show
The expression
$attributes of type array is implicitly converted to a boolean; are you sure this is intended? If so, consider using empty($expr) instead to make it clear that you intend to check for an array without elements.
This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent. Consider making the comparison explicit by using
Loading history...
|
|||
69 | $this->serializeRawData($event, $request, $controllerResult); |
||
70 | |||
71 | return; |
||
72 | } |
||
73 | |||
74 | $context = $this->serializerContextBuilder->createFromRequest($request, true, $attributes); |
||
75 | |||
76 | if (isset($context['output']) && \array_key_exists('class', $context['output']) && null === $context['output']['class']) { |
||
77 | $event->setControllerResult(null); |
||
78 | |||
79 | return; |
||
80 | } |
||
81 | |||
82 | if ($included = $request->attributes->get('_api_included')) { |
||
83 | $context['api_included'] = $included; |
||
84 | } |
||
85 | $resources = new ResourceList(); |
||
86 | $context['resources'] = &$resources; |
||
87 | $context[AbstractObjectNormalizer::EXCLUDE_FROM_CACHE_KEY][] = 'resources'; |
||
88 | |||
89 | $resourcesToPush = new ResourceList(); |
||
90 | $context['resources_to_push'] = &$resourcesToPush; |
||
91 | $context[AbstractObjectNormalizer::EXCLUDE_FROM_CACHE_KEY][] = 'resources_to_push'; |
||
92 | |||
93 | $request->attributes->set('_api_normalization_context', $context); |
||
94 | |||
95 | $event->setControllerResult($this->serializer->serialize($controllerResult, $request->getRequestFormat(), $context)); |
||
96 | |||
97 | $request->attributes->set('_resources', $request->attributes->get('_resources', []) + (array) $resources); |
||
98 | if (!\count($resourcesToPush)) { |
||
99 | return; |
||
100 | } |
||
101 | |||
102 | $linkProvider = $request->attributes->get('_links', new GenericLinkProvider()); |
||
103 | foreach ($resourcesToPush as $resourceToPush) { |
||
104 | $linkProvider = $linkProvider->withLink(new Link('preload', $resourceToPush)); |
||
105 | } |
||
106 | $request->attributes->set('_links', $linkProvider); |
||
107 | } |
||
108 | |||
109 | /** |
||
110 | * Tries to serialize data that are not API resources (e.g. the entrypoint or data returned by a custom controller). |
||
111 | * |
||
112 | * @param object $controllerResult |
||
113 | * |
||
114 | * @throws RuntimeException |
||
115 | */ |
||
116 | private function serializeRawData(GetResponseForControllerResultEvent $event, Request $request, $controllerResult): void |
||
117 | { |
||
118 | if (\is_object($controllerResult)) { |
||
119 | $event->setControllerResult($this->serializer->serialize($controllerResult, $request->getRequestFormat(), $request->attributes->get('_api_normalization_context', []))); |
||
120 | |||
121 | return; |
||
122 | } |
||
123 | |||
124 | if (!$this->serializer instanceof EncoderInterface) { |
||
125 | throw new RuntimeException(sprintf('The serializer must implement the "%s" interface.', EncoderInterface::class)); |
||
126 | } |
||
127 | |||
128 | $event->setControllerResult($this->serializer->encode($controllerResult, $request->getRequestFormat())); |
||
129 | } |
||
130 | } |
||
131 |
This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.
Consider making the comparison explicit by using
empty(..)
or! empty(...)
instead.