Skip to content

Commit 890ce6d

Browse files
committed
Request events
Co-authored-by: ArnoudThibaut
1 parent d86f956 commit 890ce6d

File tree

69 files changed

+2023
-497
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

69 files changed

+2023
-497
lines changed

composer.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
"doctrine/inflector": "^1.0",
1818
"psr/cache": "^1.0",
1919
"psr/container": "^1.0",
20+
"symfony/event-dispatcher": "^3.4 || ^4.0",
2021
"symfony/http-foundation": "^3.4 || ^4.0",
2122
"symfony/http-kernel": "^3.4 || ^4.0",
2223
"symfony/property-access": "^3.4 || ^4.0",
@@ -61,7 +62,6 @@
6162
"symfony/dependency-injection": "^3.4 || ^4.0",
6263
"symfony/doctrine-bridge": "^3.4 || ^4.0",
6364
"symfony/dom-crawler": "^3.4 || ^4.0",
64-
"symfony/event-dispatcher": "^3.4 || ^4.0",
6565
"symfony/expression-language": "^3.4 || ^4.0",
6666
"symfony/finder": "^3.4 || ^4.0",
6767
"symfony/form": "^3.4 || ^4.0",

src/Bridge/FosUser/EventListener.php

Lines changed: 35 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313

1414
namespace ApiPlatform\Core\Bridge\FosUser;
1515

16+
use ApiPlatform\Core\Event\EventInterface;
1617
use ApiPlatform\Core\Util\RequestAttributesExtractor;
1718
use FOS\UserBundle\Model\UserInterface;
1819
use FOS\UserBundle\Model\UserManagerInterface;
@@ -35,22 +36,53 @@ public function __construct(UserManagerInterface $userManager)
3536

3637
/**
3738
* Persists, updates or delete data return by the controller if applicable.
39+
*
40+
* @deprecated since version 2.5, to bo removed in 3.0
3841
*/
3942
public function onKernelView(GetResponseForControllerResultEvent $event)
4043
{
41-
$request = $event->getRequest();
44+
@trigger_error(sprintf('The method %s() is deprecated since 2.5 and will be removed in 3.0.', __METHOD__), E_USER_DEPRECATED);
45+
46+
$this->handleEvent($event);
47+
}
48+
49+
/**
50+
* Persists, updates or delete data return by the controller if applicable.
51+
*/
52+
public function handleEvent(/* EventInterface */ $event)
53+
{
54+
if ($event instanceof EventInterface) {
55+
$request = $event->getContext()['request'];
56+
} elseif ($event instanceof GetResponseForControllerResultEvent) {
57+
@trigger_error(sprintf('Passing an instance of "%s" as argument of "%s" is deprecated since 2.5 and will not be possible anymore in 3.0. Pass an instance of "%s" instead.', GetResponseForControllerResultEvent::class, __METHOD__, EventInterface::class), E_USER_DEPRECATED);
58+
59+
$request = $event->getRequest();
60+
} else {
61+
return;
62+
}
63+
4264
if (!RequestAttributesExtractor::extractAttributes($request)) {
4365
return;
4466
}
4567

46-
$user = $event->getControllerResult();
68+
if ($event instanceof EventInterface) {
69+
$user = $event->getData();
70+
} elseif ($event instanceof GetResponseForControllerResultEvent) {
71+
$user = $event->getControllerResult();
72+
} else {
73+
return;
74+
}
4775
if (!$user instanceof UserInterface || $request->isMethodSafe(false)) {
4876
return;
4977
}
5078

5179
if ('DELETE' === $request->getMethod()) {
5280
$this->userManager->deleteUser($user);
53-
$event->setControllerResult(null);
81+
if ($event instanceof EventInterface) {
82+
$event->setData(null);
83+
} elseif ($event instanceof GetResponseForControllerResultEvent) {
84+
$event->setControllerResult(null);
85+
}
5486
} else {
5587
$this->userManager->updateUser($user);
5688
}

src/Bridge/Symfony/Bundle/EventListener/SwaggerUiListener.php

Lines changed: 22 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,16 +13,37 @@
1313

1414
namespace ApiPlatform\Core\Bridge\Symfony\Bundle\EventListener;
1515

16+
use ApiPlatform\Core\Event\EventInterface;
1617
use Symfony\Component\HttpKernel\Event\GetResponseEvent;
1718

1819
final class SwaggerUiListener
1920
{
2021
/**
2122
* Sets SwaggerUiAction as controller if the requested format is HTML.
23+
*
24+
* @deprecated since version 2.5, to be removed in 3.0.
2225
*/
2326
public function onKernelRequest(GetResponseEvent $event)
2427
{
25-
$request = $event->getRequest();
28+
@trigger_error(sprintf('The method %s() is deprecated since 2.5 and will be removed in 3.0.', __METHOD__), E_USER_DEPRECATED);
29+
30+
$this->handleEvent($event);
31+
}
32+
33+
/**
34+
* Sets SwaggerUiAction as controller if the requested format is HTML.
35+
*/
36+
public function handleEvent(/*EventInterface */$event)
37+
{
38+
if ($event instanceof EventInterface) {
39+
$request = $event->getContext()['request'];
40+
} elseif ($event instanceof GetResponseEvent) {
41+
@trigger_error(sprintf('Passing an instance of "%s" as argument of "%s" is deprecated since 2.5 and will not be possible anymore in 3.0. Pass an instance of "%s" instead.', GetResponseEvent::class, __METHOD__, EventInterface::class), E_USER_DEPRECATED);
42+
43+
$request = $event->getRequest();
44+
} else {
45+
return;
46+
}
2647
if (
2748
'html' !== $request->getRequestFormat('') ||
2849
(!$request->attributes->has('_api_resource_class') && !$request->attributes->has('_api_respond'))
Lines changed: 136 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,136 @@
1+
<?php
2+
3+
/*
4+
* This file is part of the API Platform project.
5+
*
6+
* (c) Kévin Dunglas <dunglas@gmail.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+
12+
declare(strict_types=1);
13+
14+
namespace ApiPlatform\Core\Bridge\Symfony\Bundle\EventSubscriber;
15+
16+
use ApiPlatform\Core\Event\DeserializeEvent;
17+
use ApiPlatform\Core\Event\Event as ApiPlatformEvent;
18+
use ApiPlatform\Core\Event\FormatAddEvent;
19+
use ApiPlatform\Core\Event\PostDeserializeEvent;
20+
use ApiPlatform\Core\Event\PostReadEvent;
21+
use ApiPlatform\Core\Event\PostRespondEvent;
22+
use ApiPlatform\Core\Event\PostSerializeEvent;
23+
use ApiPlatform\Core\Event\PostValidateEvent;
24+
use ApiPlatform\Core\Event\PostWriteEvent;
25+
use ApiPlatform\Core\Event\PreDeserializeEvent;
26+
use ApiPlatform\Core\Event\PreReadEvent;
27+
use ApiPlatform\Core\Event\PreRespondEvent;
28+
use ApiPlatform\Core\Event\PreSerializeEvent;
29+
use ApiPlatform\Core\Event\PreValidateEvent;
30+
use ApiPlatform\Core\Event\PreWriteEvent;
31+
use ApiPlatform\Core\Event\QueryParameterValidateEvent;
32+
use ApiPlatform\Core\Event\ReadEvent;
33+
use ApiPlatform\Core\Event\RespondEvent;
34+
use ApiPlatform\Core\Event\SerializeEvent;
35+
use ApiPlatform\Core\Event\ValidateEvent;
36+
use ApiPlatform\Core\Event\ValidationExceptionEvent;
37+
use ApiPlatform\Core\Event\WriteEvent;
38+
use ApiPlatform\Core\Events;
39+
use Symfony\Component\EventDispatcher\Event;
40+
use Symfony\Component\EventDispatcher\EventDispatcherInterface;
41+
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
42+
use Symfony\Component\HttpKernel\Event\GetResponseEvent;
43+
use Symfony\Component\HttpKernel\Event\GetResponseForControllerResultEvent;
44+
use Symfony\Component\HttpKernel\Event\GetResponseForExceptionEvent;
45+
use Symfony\Component\HttpKernel\KernelEvents;
46+
47+
/**
48+
* @internal
49+
*
50+
* @author Alan Poulain <contact@alanpoulain.eu>
51+
*/
52+
final class EventDispatcher implements EventSubscriberInterface
53+
{
54+
private const INTERNAL_EVENTS_CONFIGURATION = [
55+
KernelEvents::REQUEST => [
56+
QueryParameterValidateEvent::class => Events::QUERY_PARAMETER_VALIDATE,
57+
FormatAddEvent::class => Events::FORMAT_ADD,
58+
59+
PreReadEvent::class => Events::PRE_READ,
60+
ReadEvent::class => Events::READ,
61+
PostReadEvent::class => Events::POST_READ,
62+
63+
PreDeserializeEvent::class => Events::PRE_DESERIALIZE,
64+
DeserializeEvent::class => Events::DESERIALIZE,
65+
PostDeserializeEvent::class => Events::POST_DESERIALIZE,
66+
],
67+
KernelEvents::VIEW => [
68+
PreValidateEvent::class => Events::PRE_VALIDATE,
69+
ValidateEvent::class => Events::VALIDATE,
70+
PostValidateEvent::class => Events::POST_VALIDATE,
71+
72+
PreWriteEvent::class => Events::PRE_WRITE,
73+
WriteEvent::class => Events::WRITE,
74+
PostWriteEvent::class => Events::POST_WRITE,
75+
76+
PreSerializeEvent::class => Events::PRE_SERIALIZE,
77+
SerializeEvent::class => Events::SERIALIZE,
78+
PostSerializeEvent::class => Events::POST_SERIALIZE,
79+
80+
PreRespondEvent::class => Events::PRE_RESPOND,
81+
RespondEvent::class => Events::RESPOND,
82+
PostRespondEvent::class => Events::POST_RESPOND, // @todo kernel.response
83+
],
84+
KernelEvents::EXCEPTION => [
85+
ValidationExceptionEvent::class => Events::VALIDATE_EXCEPTION,
86+
],
87+
];
88+
89+
private $dispatcher;
90+
91+
public function __construct(EventDispatcherInterface $dispatcher)
92+
{
93+
$this->dispatcher = $dispatcher;
94+
}
95+
96+
public static function getSubscribedEvents(): array
97+
{
98+
return [
99+
KernelEvents::REQUEST => 'dispatch',
100+
KernelEvents::VIEW => 'dispatch',
101+
];
102+
}
103+
104+
public function dispatch(Event $event, string $eventName): void
105+
{
106+
$internalEventData = null;
107+
$internalEventContext = [];
108+
109+
// case order is important because of Symfony events inheritance
110+
switch (true) {
111+
case $event instanceof GetResponseForControllerResultEvent:
112+
$internalEventData = $event->getControllerResult();
113+
$internalEventContext = ['request' => $event->getRequest()];
114+
break;
115+
case $event instanceof GetResponseForExceptionEvent:
116+
$internalEventContext = ['request' => $event->getRequest(), 'exception' => $event->getException()];
117+
break;
118+
case $event instanceof GetResponseEvent:
119+
$internalEventContext = ['request' => $event->getRequest()];
120+
break;
121+
}
122+
123+
foreach (self::INTERNAL_EVENTS_CONFIGURATION[$eventName] as $internalEventClass => $internalEventName) {
124+
/** @var ApiPlatformEvent $internalEvent */
125+
$internalEvent = new $internalEventClass($internalEventData, $internalEventContext);
126+
127+
$this->dispatcher->dispatch($internalEventName, $internalEvent);
128+
129+
$internalEventData = $internalEvent->getData();
130+
// @todo set controller result?
131+
if ($event instanceof GetResponseEvent && $response = $internalEvent->getContext()['response']) {
132+
$event->setResponse($response);
133+
}
134+
}
135+
}
136+
}

src/Bridge/Symfony/Bundle/Resources/config/api.xml

Lines changed: 14 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -138,14 +138,21 @@
138138
<service id="api_platform.path_segment_name_generator.underscore" class="ApiPlatform\Core\Operation\UnderscorePathSegmentNameGenerator" public="false" />
139139
<service id="api_platform.path_segment_name_generator.dash" class="ApiPlatform\Core\Operation\DashPathSegmentNameGenerator" public="false" />
140140

141+
<!-- Event dispatcher -->
142+
143+
<service id="api_platform.dispatcher.internal_events" class="ApiPlatform\Core\Bridge\Symfony\Bundle\EventSubscriber\EventDispatcher">
144+
<argument type="service" id="event_dispatcher" />
145+
146+
<tag name="kernel.event_subscriber" />
147+
</service>
148+
141149
<!-- Event listeners -->
142150

143-
<!-- kernel.request priority must be < 8 to be executed after the Firewall -->
144151
<service id="api_platform.listener.request.add_format" class="ApiPlatform\Core\EventListener\AddFormatListener">
145152
<argument type="service" id="api_platform.negotiator" />
146153
<argument type="service" id="api_platform.formats_provider" />
147154

148-
<tag name="kernel.event_listener" event="kernel.request" method="onKernelRequest" priority="7" />
155+
<tag name="kernel.event_listener" event="api_platform.format_add" method="handleEvent" />
149156
</service>
150157

151158
<service id="api_platform.listener.request.read" class="ApiPlatform\Core\EventListener\ReadListener">
@@ -155,35 +162,35 @@
155162
<argument type="service" id="api_platform.serializer.context_builder" />
156163
<argument type="service" id="api_platform.identifier.converter" />
157164

158-
<tag name="kernel.event_listener" event="kernel.request" method="onKernelRequest" priority="4" />
165+
<tag name="kernel.event_listener" event="api_platform.read" method="handleEvent" />
159166
</service>
160167

161168
<service id="api_platform.listener.view.write" class="ApiPlatform\Core\EventListener\WriteListener">
162169
<argument type="service" id="api_platform.data_persister" />
163170
<argument type="service" id="api_platform.iri_converter" on-invalid="null" />
164171
<argument type="service" id="api_platform.metadata.resource.metadata_factory" on-invalid="null" />
165172

166-
<tag name="kernel.event_listener" event="kernel.view" method="onKernelView" priority="32" />
173+
<tag name="kernel.event_listener" event="api_platform.write" method="handleEvent" />
167174
</service>
168175

169176
<service id="api_platform.listener.request.deserialize" class="ApiPlatform\Core\EventListener\DeserializeListener">
170177
<argument type="service" id="api_platform.serializer" />
171178
<argument type="service" id="api_platform.serializer.context_builder" />
172179
<argument type="service" id="api_platform.formats_provider" />
173180

174-
<tag name="kernel.event_listener" event="kernel.request" method="onKernelRequest" priority="2" />
181+
<tag name="kernel.event_listener" event="api_platform.deserialize" method="handleEvent" />
175182
</service>
176183

177184
<service id="api_platform.listener.view.serialize" class="ApiPlatform\Core\EventListener\SerializeListener">
178185
<argument type="service" id="api_platform.serializer" />
179186
<argument type="service" id="api_platform.serializer.context_builder" />
180187

181-
<tag name="kernel.event_listener" event="kernel.view" method="onKernelView" priority="16" />
188+
<tag name="kernel.event_listener" event="api_platform.serialize" method="handleEvent" />
182189
</service>
183190

184191
<service id="api_platform.listener.view.respond" class="ApiPlatform\Core\EventListener\RespondListener">
185192
<argument type="service" id="api_platform.metadata.resource.metadata_factory" />
186-
<tag name="kernel.event_listener" event="kernel.view" method="onKernelView" priority="8" />
193+
<tag name="kernel.event_listener" event="api_platform.respond" method="handleEvent" />
187194
</service>
188195

189196
<service id="api_platform.listener.exception.validation" class="ApiPlatform\Core\Bridge\Symfony\Validator\EventListener\ValidationExceptionListener">

src/Bridge/Symfony/Bundle/Resources/config/fos_user.xml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88
<service id="api_platform.fos_user.event_listener" class="ApiPlatform\Core\Bridge\FosUser\EventListener">
99
<argument type="service" id="fos_user.user_manager" />
1010

11-
<tag name="kernel.event_listener" event="kernel.view" method="onKernelView" priority="24" />
11+
<tag name="kernel.event_listener" event="api_platform.post_write" method="handleEvent" />
1212
</service>
1313
</services>
1414

src/Bridge/Symfony/Bundle/Resources/config/jsonapi.xml

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -63,22 +63,22 @@
6363
<!-- Event listener -->
6464

6565
<service id="api_platform.jsonapi.listener.request.transform_pagination_parameters" class="ApiPlatform\Core\JsonApi\EventListener\TransformPaginationParametersListener">
66-
<tag name="kernel.event_listener" event="kernel.request" method="onKernelRequest" priority="5" />
66+
<tag name="kernel.event_listener" event="api_platform.pre_read" method="handleEvent" />
6767
</service>
6868

6969
<service id="api_platform.jsonapi.listener.request.transform_sorting_parameters" class="ApiPlatform\Core\JsonApi\EventListener\TransformSortingParametersListener">
7070
<argument>%api_platform.collection.order_parameter_name%</argument>
71-
<tag name="kernel.event_listener" event="kernel.request" method="onKernelRequest" priority="5" />
71+
<tag name="kernel.event_listener" event="api_platform.pre_read" method="handleEvent" />
7272
</service>
7373

7474
<service id="api_platform.jsonapi.listener.request.transform_fieldsets_parameters" class="ApiPlatform\Core\JsonApi\EventListener\TransformFieldsetsParametersListener">
7575
<argument type="service" id="api_platform.metadata.resource.metadata_factory" />
7676

77-
<tag name="kernel.event_listener" event="kernel.request" method="onKernelRequest" priority="5" />
77+
<tag name="kernel.event_listener" event="api_platform.pre_read" method="handleEvent" />
7878
</service>
7979

8080
<service id="api_platform.jsonapi.listener.request.transform_filtering_parameters" class="ApiPlatform\Core\JsonApi\EventListener\TransformFilteringParametersListener">
81-
<tag name="kernel.event_listener" event="kernel.request" method="onKernelRequest" priority="5" />
81+
<tag name="kernel.event_listener" event="api_platform.pre_read" method="handleEvent" />
8282
</service>
8383
</services>
8484

src/Bridge/Symfony/Bundle/Resources/config/security.xml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@
2121
<argument type="service" id="api_platform.security.resource_access_checker" />
2222

2323
<!-- This listener must be executed only when the current object is available -->
24-
<tag name="kernel.event_listener" event="kernel.request" method="onKernelRequest" priority="1" />
24+
<tag name="kernel.event_listener" event="api_platform.post_deserialize" method="handleEvent" />
2525
</service>
2626

2727
<service id="api_platform.security.expression_language_provider" class="ApiPlatform\Core\Security\Core\Authorization\ExpressionLanguageProvider" public="false">

src/Bridge/Symfony/Bundle/Resources/config/swagger-ui.xml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
<services>
88

99
<service id="api_platform.swagger.listener.ui" class="ApiPlatform\Core\Bridge\Symfony\Bundle\EventListener\SwaggerUiListener">
10-
<tag name="kernel.event_listener" event="kernel.request" method="onKernelRequest" />
10+
<tag name="kernel.event_listener" event="api_platform.post_deserialize" method="handleEvent" />
1111
</service>
1212

1313
<service id="api_platform.swagger.action.ui" class="ApiPlatform\Core\Bridge\Symfony\Bundle\Action\SwaggerUiAction" public="true">

src/Bridge/Symfony/Bundle/Resources/config/validator.xml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -20,14 +20,14 @@
2020
<argument type="service" id="api_platform.validator" />
2121
<argument type="service" id="api_platform.metadata.resource.metadata_factory" />
2222

23-
<tag name="kernel.event_listener" event="kernel.view" method="onKernelView" priority="64" />
23+
<tag name="kernel.event_listener" event="api_platform.validate" method="handleEvent" />
2424
</service>
2525

2626
<service id="api_platform.listener.view.validate_query_parameters" class="ApiPlatform\Core\Filter\QueryParameterValidateListener" public="false">
2727
<argument type="service" id="api_platform.metadata.resource.metadata_factory" />
2828
<argument type="service" id="api_platform.filter_locator" />
2929

30-
<tag name="kernel.event_listener" event="kernel.request" method="onKernelRequest" priority="16" />
30+
<tag name="kernel.event_listener" event="api_platform.query_parameter_validate" method="handleEvent" />
3131
</service>
3232
</services>
3333

0 commit comments

Comments
 (0)