Skip to content

Commit cdc9608

Browse files
Internal events
Co-authored-by: ArnoudThibaut <thibaut.arnoud@gmail.com>
1 parent 44a686c commit cdc9608

File tree

78 files changed

+2225
-595
lines changed

Some content is hidden

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

78 files changed

+2225
-595
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",
@@ -65,7 +66,6 @@
6566
"symfony/dependency-injection": "^3.4 || ^4.0",
6667
"symfony/doctrine-bridge": "^3.4 || ^4.0",
6768
"symfony/dom-crawler": "^3.4 || ^4.0",
68-
"symfony/event-dispatcher": "^3.4 || ^4.0",
6969
"symfony/expression-language": "^3.4 || ^4.0",
7070
"symfony/finder": "^3.4 || ^4.0",
7171
"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 be removed in 3.0
3841
*/
3942
public function onKernelView(GetResponseForControllerResultEvent $event): void
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): void
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): void
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): void
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->getBoolean('_api_respond', false))
Lines changed: 143 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,143 @@
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\FilterResponseEvent;
43+
use Symfony\Component\HttpKernel\Event\GetResponseEvent;
44+
use Symfony\Component\HttpKernel\Event\GetResponseForControllerResultEvent;
45+
use Symfony\Component\HttpKernel\Event\GetResponseForExceptionEvent;
46+
use Symfony\Component\HttpKernel\KernelEvents;
47+
48+
/**
49+
* @internal
50+
*
51+
* @author Alan Poulain <contact@alanpoulain.eu>
52+
*/
53+
final class EventDispatcher implements EventSubscriberInterface
54+
{
55+
private const INTERNAL_EVENTS_CONFIGURATION = [
56+
KernelEvents::REQUEST => [
57+
QueryParameterValidateEvent::class => Events::QUERY_PARAMETER_VALIDATE,
58+
FormatAddEvent::class => Events::FORMAT_ADD,
59+
60+
PreReadEvent::class => Events::PRE_READ,
61+
ReadEvent::class => Events::READ,
62+
PostReadEvent::class => Events::POST_READ,
63+
64+
PreDeserializeEvent::class => Events::PRE_DESERIALIZE,
65+
DeserializeEvent::class => Events::DESERIALIZE,
66+
PostDeserializeEvent::class => Events::POST_DESERIALIZE,
67+
],
68+
KernelEvents::VIEW => [
69+
PreValidateEvent::class => Events::PRE_VALIDATE,
70+
ValidateEvent::class => Events::VALIDATE,
71+
PostValidateEvent::class => Events::POST_VALIDATE,
72+
73+
PreWriteEvent::class => Events::PRE_WRITE,
74+
WriteEvent::class => Events::WRITE,
75+
PostWriteEvent::class => Events::POST_WRITE,
76+
77+
PreSerializeEvent::class => Events::PRE_SERIALIZE,
78+
SerializeEvent::class => Events::SERIALIZE,
79+
PostSerializeEvent::class => Events::POST_SERIALIZE,
80+
81+
PreRespondEvent::class => Events::PRE_RESPOND,
82+
RespondEvent::class => Events::RESPOND,
83+
],
84+
KernelEvents::RESPONSE => [
85+
PostRespondEvent::class => Events::POST_RESPOND,
86+
],
87+
KernelEvents::EXCEPTION => [
88+
ValidationExceptionEvent::class => Events::VALIDATE_EXCEPTION,
89+
],
90+
];
91+
92+
private $dispatcher;
93+
94+
public function __construct(EventDispatcherInterface $dispatcher)
95+
{
96+
$this->dispatcher = $dispatcher;
97+
}
98+
99+
public static function getSubscribedEvents(): array
100+
{
101+
return [
102+
KernelEvents::REQUEST => 'dispatch',
103+
KernelEvents::VIEW => 'dispatch',
104+
KernelEvents::RESPONSE => [['dispatch', 1]], // to be before AddLinkHeaderListener in Symfony.
105+
];
106+
}
107+
108+
public function dispatch(Event $event, string $eventName): void
109+
{
110+
$internalEventData = null;
111+
112+
// case order is important because of Symfony events inheritance
113+
switch (true) {
114+
case $event instanceof GetResponseForControllerResultEvent:
115+
$internalEventData = $event->getControllerResult();
116+
$internalEventContext = ['request' => $event->getRequest()];
117+
break;
118+
case $event instanceof GetResponseForExceptionEvent:
119+
$internalEventContext = ['request' => $event->getRequest(), 'exception' => $event->getException()];
120+
break;
121+
case $event instanceof GetResponseEvent:
122+
$internalEventContext = ['request' => $event->getRequest()];
123+
break;
124+
case $event instanceof FilterResponseEvent:
125+
$internalEventContext = ['request' => $event->getRequest(), 'response' => $event->getResponse()];
126+
break;
127+
default:
128+
return;
129+
}
130+
131+
foreach (self::INTERNAL_EVENTS_CONFIGURATION[$eventName] as $internalEventClass => $internalEventName) {
132+
/** @var ApiPlatformEvent $internalEvent */
133+
$internalEvent = new $internalEventClass($internalEventData, $internalEventContext);
134+
135+
$this->dispatcher->dispatch($internalEventName, $internalEvent);
136+
137+
$internalEventData = $internalEvent->getData();
138+
if ($response = $internalEvent->getContext()['response'] ?? null) {
139+
$event->setResponse($response);
140+
}
141+
}
142+
}
143+
}

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

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

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

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

149-
<tag name="kernel.event_listener" event="kernel.request" method="onKernelRequest" priority="7" />
156+
<tag name="kernel.event_listener" event="api_platform.format_add" method="handleEvent" />
150157
</service>
151158

152159
<service id="api_platform.listener.request.read" class="ApiPlatform\Core\EventListener\ReadListener">
@@ -156,35 +163,35 @@
156163
<argument type="service" id="api_platform.serializer.context_builder" />
157164
<argument type="service" id="api_platform.identifier.converter" />
158165

159-
<tag name="kernel.event_listener" event="kernel.request" method="onKernelRequest" priority="4" />
166+
<tag name="kernel.event_listener" event="api_platform.read" method="handleEvent" />
160167
</service>
161168

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

167-
<tag name="kernel.event_listener" event="kernel.view" method="onKernelView" priority="32" />
174+
<tag name="kernel.event_listener" event="api_platform.write" method="handleEvent" />
168175
</service>
169176

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

175-
<tag name="kernel.event_listener" event="kernel.request" method="onKernelRequest" priority="2" />
182+
<tag name="kernel.event_listener" event="api_platform.deserialize" method="handleEvent" />
176183
</service>
177184

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

182-
<tag name="kernel.event_listener" event="kernel.view" method="onKernelView" priority="16" />
189+
<tag name="kernel.event_listener" event="api_platform.serialize" method="handleEvent" />
183190
</service>
184191

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

190197
<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/http_cache.xml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@
1313
<argument>%api_platform.http_cache.public%</argument>
1414
<argument type="service" id="api_platform.metadata.resource.metadata_factory" />
1515

16-
<tag name="kernel.event_listener" event="kernel.response" method="onKernelResponse" priority="-1" />
16+
<tag name="kernel.event_listener" event="api_platform.post_respond" method="handleEvent" priority="-1" />
1717
</service>
1818
</services>
1919
</container>

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@
1111
<service id="api_platform.http_cache.listener.response.add_tags" class="ApiPlatform\Core\HttpCache\EventListener\AddTagsListener">
1212
<argument type="service" id="api_platform.iri_converter" />
1313

14-
<tag name="kernel.event_listener" event="kernel.response" method="onKernelResponse" priority="-2" />
14+
<tag name="kernel.event_listener" event="api_platform.post_respond" method="handleEvent" priority="-2" />
1515
</service>
1616
</services>
1717
</container>

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@
2424
<service id="api_platform.hydra.listener.response.add_link_header" class="ApiPlatform\Core\Hydra\EventListener\AddLinkHeaderListener">
2525
<argument type="service" id="api_platform.router" />
2626

27-
<tag name="kernel.event_listener" event="kernel.response" method="onKernelResponse" />
27+
<tag name="kernel.event_listener" event="api_platform.post_respond" method="handleEvent" />
2828
</service>
2929

3030
<!-- Serializer -->

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

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -73,22 +73,22 @@
7373
<!-- Event listener -->
7474

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

7979
<service id="api_platform.jsonapi.listener.request.transform_sorting_parameters" class="ApiPlatform\Core\JsonApi\EventListener\TransformSortingParametersListener">
8080
<argument>%api_platform.collection.order_parameter_name%</argument>
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

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

87-
<tag name="kernel.event_listener" event="kernel.request" method="onKernelRequest" priority="5" />
87+
<tag name="kernel.event_listener" event="api_platform.pre_read" method="handleEvent" />
8888
</service>
8989

9090
<service id="api_platform.jsonapi.listener.request.transform_filtering_parameters" class="ApiPlatform\Core\JsonApi\EventListener\TransformFilteringParametersListener">
91-
<tag name="kernel.event_listener" event="kernel.request" method="onKernelRequest" priority="5" />
91+
<tag name="kernel.event_listener" event="api_platform.pre_read" method="handleEvent" />
9292
</service>
9393
</services>
9494

0 commit comments

Comments
 (0)