Completed
Pull Request — 2.x (#2227)
by
unknown
08:36
created

FOSRestExtension::loadAllowedMethodsListener()   A

Complexity

Conditions 3
Paths 3

Size

Total Lines 13

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 7
CRAP Score 3

Importance

Changes 0
Metric Value
dl 0
loc 13
ccs 7
cts 7
cp 1
rs 9.8333
c 0
b 0
f 0
cc 3
nc 3
nop 3
crap 3
1
<?php
2
3
/*
4
 * This file is part of the FOSRestBundle package.
5
 *
6
 * (c) FriendsOfSymfony <http://friendsofsymfony.github.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
namespace FOS\RestBundle\DependencyInjection;
13
14
use FOS\RestBundle\ErrorRenderer\SerializerErrorRenderer;
15
use FOS\RestBundle\EventListener\ResponseStatusCodeListener;
16
use FOS\RestBundle\View\ViewHandler;
17
use Symfony\Component\Config\FileLocator;
18
use Symfony\Component\DependencyInjection\Alias;
19
use Symfony\Component\DependencyInjection\ChildDefinition;
20
use Symfony\Component\DependencyInjection\Compiler\ServiceLocatorTagPass;
21
use Symfony\Component\DependencyInjection\ContainerBuilder;
22
use Symfony\Component\DependencyInjection\ContainerInterface;
23
use Symfony\Component\DependencyInjection\Definition;
24
use Symfony\Component\DependencyInjection\Loader\XmlFileLoader;
25
use Symfony\Component\DependencyInjection\Reference;
26
use Symfony\Component\Form\Extension\Core\Type\FormType;
27
use Symfony\Component\HttpFoundation\Response;
28
use Symfony\Component\HttpKernel\DependencyInjection\Extension;
29
use Symfony\Component\Validator\Constraint;
30
31
/**
32
 * @internal
33
 */
34
class FOSRestExtension extends Extension
35
{
36
    /**
37
     * {@inheritdoc}
38
     */
39
    public function getConfiguration(array $config, ContainerBuilder $container): Configuration
40
    {
41
        return new Configuration($container->getParameter('kernel.debug'));
42
    }
43 23
44
    public function load(array $configs, ContainerBuilder $container): void
45 23
    {
46
        $configuration = new Configuration($container->getParameter('kernel.debug'));
47
        $config = $this->processConfiguration($configuration, $configs);
48 74
49
        $loader = new XmlFileLoader($container, new FileLocator(__DIR__.'/../Resources/config'));
50 74
        $loader->load('view.xml');
51 74
        $loader->load('request.xml');
52
        $loader->load('serializer.xml');
53 69
54 69
        foreach ($config['service'] as $key => $service) {
55 69
            if ('validator' === $service && empty($config['body_converter']['validate'])) {
56 69
                continue;
57
            }
58 69
59 69
            if (null !== $service) {
60 69
                if ('view_handler' === $key) {
61
                    $container->setAlias('fos_rest.'.$key, new Alias($service, true));
62 69
                } else {
63 7
                    $container->setAlias('fos_rest.'.$key, $service);
64
                }
65 7
            }
66 7
        }
67 7
68 7
        $this->loadForm($config, $loader, $container);
69
        $this->loadException($config, $loader, $container);
70 7
        $this->loadBodyConverter($config, $loader, $container);
71 7
        $this->loadView($config, $loader, $container);
72
73 7
        $this->loadBodyListener($config, $loader, $container);
74 7
        $this->loadFormatListener($config, $loader, $container);
75 7
        $this->loadVersioning($config, $loader, $container);
76 7
        $this->loadParamFetcherListener($config, $loader, $container);
77
        $this->loadAllowedMethodsListener($config, $loader, $container);
78
        $this->loadZoneMatcherListener($config, $loader, $container);
79 69
80 69
        // Needs RequestBodyParamConverter and View Handler loaded.
81 66
        $this->loadSerializer($config, $container);
82
    }
83
84 69
    private function loadForm(array $config, XmlFileLoader $loader, ContainerBuilder $container): void
85 69
    {
86 69
        if (!empty($config['disable_csrf_role'])) {
87 69
            $loader->load('forms.xml');
88 69
89
            $definition = $container->getDefinition('fos_rest.form.extension.csrf_disable');
90 69
            $definition->replaceArgument(1, $config['disable_csrf_role']);
91 69
            $definition->addTag('form.type_extension', ['extended_type' => FormType::class]);
92
        }
93
    }
94 69
95
    private function loadAllowedMethodsListener(array $config, XmlFileLoader $loader, ContainerBuilder $container): void
96 3
    {
97
        if ($config['allowed_methods_listener']['enabled']) {
98
            if (!empty($config['allowed_methods_listener']['service'])) {
99
                $service = $container->getDefinition('fos_rest.allowed_methods_listener');
100
                $service->clearTag('kernel.event_listener');
101 69
            }
102 69
103 69
            $loader->load('allowed_methods_listener.xml');
104 69
105
            $container->getDefinition('fos_rest.allowed_methods_loader')->replaceArgument(1, $config['cache_dir']);
106 69
        }
107 69
    }
108 69
109 69
    private function loadBodyListener(array $config, XmlFileLoader $loader, ContainerBuilder $container): void
110 69
    {
111 69
        if ($config['body_listener']['enabled']) {
112 69
            $loader->load('body_listener.xml');
113
114
            $service = $container->getDefinition('fos_rest.body_listener');
115 69
116 69
            if (!empty($config['body_listener']['service'])) {
117
                $service->clearTag('kernel.event_listener');
118 69
            }
119
120 69
            $service->replaceArgument(1, $config['body_listener']['throw_exception_on_unsupported_content_type']);
121 2
            $service->addMethodCall('setDefaultFormat', array($config['body_listener']['default_format']));
122
123 2
            $container->getDefinition('fos_rest.decoder_provider')->replaceArgument(1, $config['body_listener']['decoders']);
124 2
125
            $decoderServicesMap = array();
126
127 2
            foreach ($config['body_listener']['decoders'] as $id) {
128
                $decoderServicesMap[$id] = new Reference($id);
129
            }
130 2
131
            $decodersServiceLocator = ServiceLocatorTagPass::register($container, $decoderServicesMap);
132
            $container->getDefinition('fos_rest.decoder_provider')->replaceArgument(0, $decodersServiceLocator);
133 69
134
            $arrayNormalizer = $config['body_listener']['array_normalizer'];
135 69
136
            if (null !== $arrayNormalizer['service']) {
137 69
                $bodyListener = $container->getDefinition('fos_rest.body_listener');
138
                $bodyListener->addArgument(new Reference($arrayNormalizer['service']));
139
                $bodyListener->addArgument($arrayNormalizer['forms']);
140
            }
141
        }
142
    }
143
144
    private function loadFormatListener(array $config, XmlFileLoader $loader, ContainerBuilder $container): void
145
    {
146
        if ($config['format_listener']['enabled'] && !empty($config['format_listener']['rules'])) {
147
            $loader->load('format_listener.xml');
148
149 69
            if (!empty($config['format_listener']['service'])) {
150
                $service = $container->getDefinition('fos_rest.format_listener');
151 69
                $service->clearTag('kernel.event_listener');
152
            }
153 69
154 3
            $container->setParameter(
155
                'fos_rest.format_listener.rules',
156
                $config['format_listener']['rules']
157
            );
158
        }
159 3
    }
160
161 3
    private function loadVersioning(array $config, XmlFileLoader $loader, ContainerBuilder $container): void
162
    {
163 69
        if (!empty($config['versioning']['enabled'])) {
164
            $loader->load('versioning.xml');
165 69
166
            $versionListener = $container->getDefinition('fos_rest.versioning.listener');
167 69
            $versionListener->replaceArgument(1, $config['versioning']['default_version']);
168 15
169
            $resolvers = [];
170 15
            if ($config['versioning']['resolvers']['query']['enabled']) {
171
                $resolvers['query'] = $container->getDefinition('fos_rest.versioning.query_parameter_resolver');
172 15
                $resolvers['query']->replaceArgument(0, $config['versioning']['resolvers']['query']['parameter_name']);
173
            }
174
            if ($config['versioning']['resolvers']['custom_header']['enabled']) {
175
                $resolvers['custom_header'] = $container->getDefinition('fos_rest.versioning.header_resolver');
176 15
                $resolvers['custom_header']->replaceArgument(0, $config['versioning']['resolvers']['custom_header']['header_name']);
177 15
            }
178
            if ($config['versioning']['resolvers']['media_type']['enabled']) {
179 15
                $resolvers['media_type'] = $container->getDefinition('fos_rest.versioning.media_type_resolver');
180
                $resolvers['media_type']->replaceArgument(0, $config['versioning']['resolvers']['media_type']['regex']);
181 15
            }
182 15
183
            $chainResolver = $container->getDefinition('fos_rest.versioning.chain_resolver');
184 15
            foreach ($config['versioning']['guessing_order'] as $resolver) {
185 15
                if (isset($resolvers[$resolver])) {
186
                    $chainResolver->addMethodCall('addResolver', [$resolvers[$resolver]]);
187
                }
188 15
            }
189 15
        }
190
    }
191
192 15
    private function loadParamFetcherListener(array $config, XmlFileLoader $loader, ContainerBuilder $container): void
193
    {
194 15
        if ($config['param_fetcher_listener']['enabled']) {
195 3
            if (!class_exists(Constraint::class)) {
196 3
                throw new \LogicException('Enabling the fos_rest.param_fetcher_listener option when the Symfony Validator component is not installed is not supported. Try installing the symfony/validator package.');
197 3
            }
198
199
            $loader->load('param_fetcher_listener.xml');
200 69
201
            if (!empty($config['param_fetcher_listener']['service'])) {
202 69
                $service = $container->getDefinition('fos_rest.param_fetcher_listener');
203
                $service->clearTag('kernel.event_listener');
204 69
            }
205 6
206
            if ($config['param_fetcher_listener']['force']) {
207 6
                $container->getDefinition('fos_rest.param_fetcher_listener')->replaceArgument(1, true);
208
            }
209
        }
210
    }
211
212 6
    private function loadBodyConverter(array $config, XmlFileLoader $loader, ContainerBuilder $container): void
213 6
    {
214 6
        if (!$this->isConfigEnabled($container, $config['body_converter'])) {
215
            return;
216
        }
217 69
218
        $loader->load('request_body_param_converter.xml');
219 69
220
        if (!empty($config['body_converter']['validation_errors_argument'])) {
221 69
            $container->getDefinition('fos_rest.converter.request_body')->replaceArgument(4, $config['body_converter']['validation_errors_argument']);
222 2
        }
223
    }
224 2
225 2
    private function loadView(array $config, XmlFileLoader $loader, ContainerBuilder $container): void
226
    {
227 2
        if (!empty($config['view']['jsonp_handler'])) {
228 2
            $handler = new ChildDefinition($config['service']['view_handler']);
229 2
            $handler->setPublic(true);
230 2
231
            $jsonpHandler = new Reference('fos_rest.view_handler.jsonp');
232 2
            $handler->addMethodCall('registerHandler', ['jsonp', [$jsonpHandler, 'createResponse']]);
233 2
            $container->setDefinition('fos_rest.view_handler', $handler);
234 2
235
            $container->getDefinition('fos_rest.view_handler.jsonp')->replaceArgument(0, $config['view']['jsonp_handler']['callback_param']);
236 2
237 2
            if (empty($config['view']['mime_types']['jsonp'])) {
238 2
                $config['view']['mime_types']['jsonp'] = $config['view']['jsonp_handler']['mime_type'];
239
            }
240
        }
241 2
242 2
        if ($config['view']['mime_types']['enabled']) {
243 2
            $loader->load('mime_type_listener.xml');
244 2
245
            if (!empty($config['mime_type_listener']['service'])) {
246
                $service = $container->getDefinition('fos_rest.mime_type_listener');
247
                $service->clearTag('kernel.event_listener');
248 69
            }
249
250 69
            $container->getDefinition('fos_rest.mime_type_listener')->replaceArgument(0, $config['view']['mime_types']['formats']);
251
        }
252 69
253 5
        if ($config['view']['view_response_listener']['enabled']) {
254
            $loader->load('view_response_listener.xml');
255
            $service = $container->getDefinition('fos_rest.view_response_listener');
256
257 5
            if (!empty($config['view_response_listener']['service'])) {
258
                $service->clearTag('kernel.event_listener');
259 5
            }
260
261
            $service->replaceArgument(1, $config['view']['view_response_listener']['force']);
262
        }
263
264 5
        $formats = [];
265 1
        foreach ($config['view']['formats'] as $format => $enabled) {
266
            if ($enabled) {
267
                $formats[$format] = false;
268 69
            }
269
        }
270 69
271
        if (!is_numeric($config['view']['failed_validation'])) {
272 69
            $config['view']['failed_validation'] = constant(sprintf('%s::%s', Response::class, $config['view']['failed_validation']));
273 62
        }
274
275
        if (!is_numeric($config['view']['empty_content'])) {
276 7
            $config['view']['empty_content'] = constant(sprintf('%s::%s', Response::class, $config['view']['empty_content']));
277
        }
278 7
279 7
        $defaultViewHandler = $container->getDefinition('fos_rest.view_handler.default');
280
        $defaultViewHandler->setFactory([ViewHandler::class, 'create']);
281 7
        $defaultViewHandler->setArguments([
282
            new Reference('router'),
283 69
            new Reference('fos_rest.serializer'),
284
            new Reference('request_stack'),
285 69
            $formats,
286 1
            $config['view']['failed_validation'],
287 1
            $config['view']['empty_content'],
288 1
            $config['view']['serialize_null'],
289
        ]);
290 1
    }
291 1
292 1
    private function loadException(array $config, XmlFileLoader $loader, ContainerBuilder $container): void
293
    {
294 1
        if ($config['exception']['enabled']) {
295
            $loader->load('exception.xml');
296 1
297 1
            if ($config['exception']['map_exception_codes']) {
298
                $container->register('fos_rest.exception.response_status_code_listener', ResponseStatusCodeListener::class)
299
                    ->setArguments([
300
                        new Reference('fos_rest.exception.codes_map'),
301 69
                    ])
302 3
                    ->addTag('kernel.event_subscriber');
303
            }
304 3
305
            $container->getDefinition('fos_rest.exception.codes_map')
306
                ->replaceArgument(0, $config['exception']['codes']);
307
            $container->getDefinition('fos_rest.exception.messages_map')
308
                ->replaceArgument(0, $config['exception']['messages']);
309 3
310
            $container->getDefinition('fos_rest.serializer.flatten_exception_handler')
311
                ->replaceArgument(1, $config['exception']['debug']);
312 69
            $container->getDefinition('fos_rest.serializer.flatten_exception_handler')
313 11
                ->replaceArgument(2, 'rfc7807' === $config['exception']['flatten_exception_format']);
314 11
            $container->getDefinition('fos_rest.serializer.flatten_exception_normalizer')
315
                ->replaceArgument(1, $config['exception']['debug']);
316 11
            $container->getDefinition('fos_rest.serializer.flatten_exception_normalizer')
317
                ->replaceArgument(2, 'rfc7807' === $config['exception']['flatten_exception_format']);
318
319
            if ($config['exception']['serializer_error_renderer']) {
320 11
                $format = new Definition();
321
                $format->setFactory([SerializerErrorRenderer::class, 'getPreferredFormat']);
322
                $format->setArguments([
323 69
                    new Reference('request_stack'),
324 69
                ]);
325 69
                $debug = new Definition();
326 69
                $debug->setFactory([SerializerErrorRenderer::class, 'isDebug']);
327
                $debug->setArguments([
328
                    new Reference('request_stack'),
329 69
                    '%kernel.debug%',
330 69
                ]);
331 69
                $container->register('fos_rest.error_renderer.serializer', SerializerErrorRenderer::class)
332
                    ->setArguments([
333
                        new Reference('fos_rest.serializer'),
334
                        $format,
335 69
                        new Reference('error_renderer.html', ContainerInterface::NULL_ON_INVALID_REFERENCE),
336 7
                        $debug,
337 7
                    ]);
338 7
                $container->setAlias('error_renderer', 'fos_rest.error_renderer.serializer');
339
            }
340
        }
341 69
    }
342 69
343 69
    private function loadSerializer(array $config, ContainerBuilder $container): void
344
    {
345
        $bodyConverter = $container->hasDefinition('fos_rest.converter.request_body') ? $container->getDefinition('fos_rest.converter.request_body') : null;
346
        $viewHandler = $container->getDefinition('fos_rest.view_handler.default');
347 69
        $options = array();
348
349
        if (!empty($config['serializer']['version'])) {
350
            if ($bodyConverter) {
351 69
                $bodyConverter->replaceArgument(2, $config['serializer']['version']);
352
            }
353
            $options['exclusionStrategyVersion'] = $config['serializer']['version'];
354
        }
355 69
356
        if (!empty($config['serializer']['groups'])) {
357 69
            if ($bodyConverter) {
358 69
                $bodyConverter->replaceArgument(1, $config['serializer']['groups']);
359 69
            }
360 69
            $options['exclusionStrategyGroups'] = $config['serializer']['groups'];
361 69
        }
362 69
363 69
        $options['serializeNullStrategy'] = $config['serializer']['serialize_null'];
364 69
        $viewHandler->addArgument($options);
365 69
366 69
        if ($config['serializer']['disable_custom_jms_registry']) {
367 69
            $container->setParameter('fos_rest.serializer.disable_custom_jms_registry', true);
368
        }
369
    }
370
371 69
    private function loadZoneMatcherListener(array $config, XmlFileLoader $loader, ContainerBuilder $container): void
372
    {
373 69
        if (!empty($config['zone'])) {
374
            $loader->load('zone_matcher_listener.xml');
375 69
            $zoneMatcherListener = $container->getDefinition('fos_rest.zone_matcher_listener');
376 65
377
            foreach ($config['zone'] as $zone) {
378 65
                $matcher = $this->createZoneRequestMatcher(
379 1
                    $container,
380 1
                    $zone['path'],
381 1
                    $zone['host'],
382
                    $zone['methods'],
383 1
                    $zone['ips']
384
                );
385
386 65
                $zoneMatcherListener->addMethodCall('addRequestMatcher', array($matcher));
387 11
            }
388
        }
389
    }
390
391
    private function createZoneRequestMatcher(ContainerBuilder $container, ?string $path = null, ?string $host = null, array $methods = array(), array $ips = null): Reference
392 11
    {
393
        if ($methods) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $methods 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 empty(..) or ! empty(...) instead.

Loading history...
394 11
            $methods = array_map('strtoupper', (array) $methods);
395 11
        }
396 11
397 11
        $serialized = serialize(array($path, $host, $methods, $ips));
398 11
        $id = 'fos_rest.zone_request_matcher.'.md5($serialized).sha1($serialized);
399 11
400
        // only add arguments that are necessary
401 11
        $arguments = array($path, $host, $methods, $ips);
402
        while (count($arguments) > 0 && !end($arguments)) {
403
            array_pop($arguments);
404
        }
405
406
        $container
407
            ->setDefinition($id, new ChildDefinition('fos_rest.zone_request_matcher'))
408
            ->setArguments($arguments)
409
        ;
410
411 11
        return new Reference($id);
412 11
    }
413
}
414