11
11
12
12
namespace FOS \RestBundle \EventListener ;
13
13
14
+ use Doctrine \Common \Annotations \Reader ;
15
+ use Doctrine \Persistence \Proxy ;
14
16
use FOS \RestBundle \Controller \Annotations \View as ViewAnnotation ;
15
17
use FOS \RestBundle \FOSRestBundle ;
16
18
use FOS \RestBundle \View \View ;
17
19
use FOS \RestBundle \View \ViewHandlerInterface ;
18
20
use Symfony \Component \EventDispatcher \EventSubscriberInterface ;
19
21
use Symfony \Component \HttpFoundation \Response ;
22
+ use Symfony \Component \HttpKernel \Event \ControllerEvent ;
20
23
use Symfony \Component \HttpKernel \Event \ViewEvent ;
21
24
use Symfony \Component \HttpKernel \KernelEvents ;
22
25
23
26
/**
24
- * The ViewResponseListener class handles the View core event as well as the "@extra:Template" annotation .
27
+ * The ViewResponseListener class handles the kernel.view event and creates a {@see Response} for a {@see View} provided by the controller result .
25
28
*
26
29
* @author Lukas Kahwe Smith <smith@pooteeweet.org>
27
30
*
28
31
* @internal
29
32
*/
30
33
class ViewResponseListener implements EventSubscriberInterface
31
34
{
35
+ /**
36
+ * @var ViewHandlerInterface
37
+ */
32
38
private $ viewHandler ;
39
+
33
40
private $ forceView ;
34
41
35
- public function __construct (ViewHandlerInterface $ viewHandler , bool $ forceView )
42
+ /**
43
+ * @var Reader|null
44
+ */
45
+ private $ annotationReader ;
46
+
47
+ public function __construct (ViewHandlerInterface $ viewHandler , bool $ forceView , ?Reader $ annotationReader = null )
36
48
{
37
49
$ this ->viewHandler = $ viewHandler ;
38
50
$ this ->forceView = $ forceView ;
51
+ $ this ->annotationReader = $ annotationReader ;
52
+ }
53
+
54
+ /**
55
+ * Extracts configuration for a {@see ViewAnnotation} from the controller if present.
56
+ */
57
+ public function onKernelController (ControllerEvent $ event )
58
+ {
59
+ $ request = $ event ->getRequest ();
60
+
61
+ if (!$ request ->attributes ->get (FOSRestBundle::ZONE_ATTRIBUTE , true )) {
62
+ return ;
63
+ }
64
+
65
+ $ controller = $ event ->getController ();
66
+
67
+ if (!\is_array ($ controller ) && method_exists ($ controller , '__invoke ' )) {
68
+ $ controller = [$ controller , '__invoke ' ];
69
+ }
70
+
71
+ if (!\is_array ($ controller )) {
72
+ return ;
73
+ }
74
+
75
+ $ className = $ this ->getRealClass (\get_class ($ controller [0 ]));
76
+ $ object = new \ReflectionClass ($ className );
77
+ $ method = $ object ->getMethod ($ controller [1 ]);
78
+
79
+ /** @var ViewAnnotation|null $classConfiguration */
80
+ $ classConfiguration = null ;
81
+
82
+ /** @var ViewAnnotation|null $methodConfiguration */
83
+ $ methodConfiguration = null ;
84
+
85
+ if (null !== $ this ->annotationReader ) {
86
+ $ classConfiguration = $ this ->getViewConfiguration ($ this ->annotationReader ->getClassAnnotations ($ object ));
87
+ $ methodConfiguration = $ this ->getViewConfiguration ($ this ->annotationReader ->getMethodAnnotations ($ method ));
88
+ }
89
+
90
+ if (80000 <= \PHP_VERSION_ID ) {
91
+ if (null === $ classConfiguration ) {
92
+ $ classAttributes = array_map (
93
+ function (\ReflectionAttribute $ attribute ) {
94
+ return $ attribute ->newInstance ();
95
+ },
96
+ $ object ->getAttributes (ViewAnnotation::class, \ReflectionAttribute::IS_INSTANCEOF )
97
+ );
98
+
99
+ $ classConfiguration = $ this ->getViewConfiguration ($ classAttributes );
100
+ }
101
+
102
+ if (null === $ methodConfiguration ) {
103
+ $ methodAttributes = array_map (
104
+ function (\ReflectionAttribute $ attribute ) {
105
+ return $ attribute ->newInstance ();
106
+ },
107
+ $ method ->getAttributes (ViewAnnotation::class, \ReflectionAttribute::IS_INSTANCEOF )
108
+ );
109
+
110
+ $ methodConfiguration = $ this ->getViewConfiguration ($ methodAttributes );
111
+ }
112
+ }
113
+
114
+ // An annotation/attribute on the method takes precedence over the class level
115
+ if (null !== $ methodConfiguration ) {
116
+ $ request ->attributes ->set (FOSRestBundle::VIEW_ATTRIBUTE , $ methodConfiguration );
117
+ } elseif (null !== $ classConfiguration ) {
118
+ $ request ->attributes ->set (FOSRestBundle::VIEW_ATTRIBUTE , $ classConfiguration );
119
+ }
39
120
}
40
121
41
122
public function onKernelView (ViewEvent $ event ): void
@@ -46,7 +127,8 @@ public function onKernelView(ViewEvent $event): void
46
127
return ;
47
128
}
48
129
49
- $ configuration = $ request ->attributes ->get ('_template ' );
130
+ /** @var ViewAnnotation|null $configuration */
131
+ $ configuration = $ request ->attributes ->get (FOSRestBundle::VIEW_ATTRIBUTE );
50
132
51
133
$ view = $ event ->getControllerResult ();
52
134
if (!$ view instanceof View) {
@@ -81,16 +163,49 @@ public function onKernelView(ViewEvent $event): void
81
163
$ view ->setFormat ($ request ->getRequestFormat ());
82
164
}
83
165
84
- $ response = $ this ->viewHandler ->handle ($ view , $ request );
85
-
86
- $ event ->setResponse ($ response );
166
+ $ event ->setResponse ($ this ->viewHandler ->handle ($ view , $ request ));
87
167
}
88
168
89
169
public static function getSubscribedEvents (): array
90
170
{
91
- // Must be executed before SensioFrameworkExtraBundle's listener
92
171
return [
93
- KernelEvents::VIEW => ['onKernelView ' , 30 ],
172
+ KernelEvents::CONTROLLER => 'onKernelController ' ,
173
+ KernelEvents::VIEW => ['onKernelView ' , -128 ],
94
174
];
95
175
}
176
+
177
+ /**
178
+ * @param object[] $annotations
179
+ */
180
+ private function getViewConfiguration (array $ annotations ): ?ViewAnnotation
181
+ {
182
+ $ viewAnnotation = null ;
183
+
184
+ foreach ($ annotations as $ annotation ) {
185
+ if (!$ annotation instanceof ViewAnnotation) {
186
+ continue ;
187
+ }
188
+
189
+ if (null === $ viewAnnotation ) {
190
+ $ viewAnnotation = $ annotation ;
191
+ } else {
192
+ throw new \LogicException ('Multiple "view" annotations are not allowed. ' );
193
+ }
194
+ }
195
+
196
+ return $ viewAnnotation ;
197
+ }
198
+
199
+ private function getRealClass (string $ class ): string
200
+ {
201
+ if (class_exists (Proxy::class)) {
202
+ if (false === $ pos = strrpos ($ class , '\\' .Proxy::MARKER .'\\' )) {
203
+ return $ class ;
204
+ }
205
+
206
+ return substr ($ class , $ pos + Proxy::MARKER_LENGTH + 2 );
207
+ }
208
+
209
+ return $ class ;
210
+ }
96
211
}
0 commit comments