Completed
Pull Request — master (#2170)
by Christian
04:59 queued 02:58
created

FOSRestExtension::loadException()   A

Complexity

Conditions 3
Paths 3

Size

Total Lines 28

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 21
CRAP Score 3

Importance

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