These results are based on our legacy PHP analysis, consider migrating to our new PHP analysis engine instead. Learn more
1 | <?php |
||
2 | |||
3 | /* |
||
4 | * This file is part of the API Platform project. |
||
5 | * |
||
6 | * (c) Kévin Dunglas <[email protected]> |
||
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\DependencyInjection; |
||
15 | |||
16 | use ApiPlatform\Core\Bridge\Doctrine\Orm\Extension\QueryCollectionExtensionInterface; |
||
17 | use ApiPlatform\Core\Bridge\Doctrine\Orm\Extension\QueryItemExtensionInterface; |
||
18 | use ApiPlatform\Core\DataProvider\CollectionDataProviderInterface; |
||
19 | use ApiPlatform\Core\DataProvider\ItemDataProviderInterface; |
||
20 | use ApiPlatform\Core\Exception\RuntimeException; |
||
21 | use Doctrine\Common\Annotations\Annotation; |
||
22 | use Doctrine\ORM\Version; |
||
23 | use phpDocumentor\Reflection\DocBlockFactoryInterface; |
||
24 | use Symfony\Component\Cache\Adapter\ArrayAdapter; |
||
25 | use Symfony\Component\Config\FileLocator; |
||
26 | use Symfony\Component\Config\Resource\DirectoryResource; |
||
27 | use Symfony\Component\DependencyInjection\ChildDefinition; |
||
28 | use Symfony\Component\DependencyInjection\ContainerBuilder; |
||
29 | use Symfony\Component\DependencyInjection\Extension\PrependExtensionInterface; |
||
30 | use Symfony\Component\DependencyInjection\Loader\XmlFileLoader; |
||
31 | use Symfony\Component\DependencyInjection\Reference; |
||
32 | use Symfony\Component\Finder\Finder; |
||
33 | use Symfony\Component\HttpKernel\DependencyInjection\Extension; |
||
34 | use Symfony\Component\Validator\Validator\ValidatorInterface; |
||
35 | use Symfony\Component\Yaml\Yaml; |
||
36 | |||
37 | /** |
||
38 | * The extension of this bundle. |
||
39 | * |
||
40 | * @author Kévin Dunglas <[email protected]> |
||
41 | */ |
||
42 | final class ApiPlatformExtension extends Extension implements PrependExtensionInterface |
||
43 | { |
||
44 | /** |
||
45 | * {@inheritdoc} |
||
46 | */ |
||
47 | public function prepend(ContainerBuilder $container) |
||
48 | { |
||
49 | if (!$frameworkConfiguration = $container->getExtensionConfig('framework')) { |
||
50 | return; |
||
51 | } |
||
52 | |||
53 | foreach ($frameworkConfiguration as $frameworkParameters) { |
||
54 | if (isset($frameworkParameters['serializer'])) { |
||
55 | $serializerConfig = $serializerConfig ?? $frameworkParameters['serializer']; |
||
56 | } |
||
57 | |||
58 | if (isset($frameworkParameters['property_info'])) { |
||
59 | $propertyInfoConfig = $propertyInfoConfig ?? $frameworkParameters['property_info']; |
||
60 | } |
||
61 | } |
||
62 | |||
63 | View Code Duplication | if (!isset($serializerConfig['enabled'])) { |
|
64 | $container->prependExtensionConfig('framework', ['serializer' => ['enabled' => true]]); |
||
65 | } |
||
66 | |||
67 | View Code Duplication | if (!isset($propertyInfoConfig['enabled'])) { |
|
68 | $container->prependExtensionConfig('framework', ['property_info' => ['enabled' => true]]); |
||
69 | } |
||
70 | |||
71 | if (isset($serializerConfig['name_converter'])) { |
||
72 | $container->prependExtensionConfig('api_platform', ['name_converter' => $serializerConfig['name_converter']]); |
||
73 | } |
||
74 | } |
||
75 | |||
76 | /** |
||
77 | * {@inheritdoc} |
||
78 | */ |
||
79 | public function load(array $configs, ContainerBuilder $container) |
||
80 | { |
||
81 | $configuration = new Configuration(); |
||
82 | $config = $this->processConfiguration($configuration, $configs); |
||
83 | $formats = $this->getFormats($config['formats']); |
||
84 | $errorFormats = $this->getFormats($config['error_formats']); |
||
85 | $this->handleConfig($container, $config, $formats, $errorFormats); |
||
86 | |||
87 | $loader = new XmlFileLoader($container, new FileLocator(__DIR__.'/../Resources/config')); |
||
88 | $loader->load('api.xml'); |
||
89 | $loader->load('data_provider.xml'); |
||
90 | $loader->load('filter.xml'); |
||
91 | |||
92 | $container->registerForAutoconfiguration(ItemDataProviderInterface::class) |
||
93 | ->addTag('api_platform.item_data_provider'); |
||
94 | $container->registerForAutoconfiguration(CollectionDataProviderInterface::class) |
||
95 | ->addTag('api_platform.collection_data_provider'); |
||
96 | $container->registerForAutoconfiguration(QueryItemExtensionInterface::class) |
||
97 | ->addTag('api_platform.doctrine.orm.query_extension.item'); |
||
98 | $container->registerForAutoconfiguration(QueryCollectionExtensionInterface::class) |
||
99 | ->addTag('api_platform.doctrine.orm.query_extension.collection'); |
||
100 | |||
101 | if (interface_exists(ValidatorInterface::class)) { |
||
102 | $loader->load('validator.xml'); |
||
103 | } |
||
104 | |||
105 | $bundles = $container->getParameter('kernel.bundles'); |
||
106 | if (isset($bundles['SecurityBundle'])) { |
||
107 | $loader->load('security.xml'); |
||
108 | } |
||
109 | |||
110 | $useDoctrine = isset($bundles['DoctrineBundle']) && class_exists(Version::class); |
||
111 | |||
112 | $this->registerMetadataConfiguration($container, $config, $loader); |
||
113 | $this->registerOAuthConfiguration($container, $config, $loader); |
||
114 | $this->registerApiKeysConfiguration($container, $config, $loader); |
||
115 | $this->registerSwaggerConfiguration($container, $config, $loader); |
||
116 | $this->registerJsonLdConfiguration($formats, $loader); |
||
117 | $this->registerJsonHalConfiguration($formats, $loader); |
||
118 | $this->registerJsonProblemConfiguration($errorFormats, $loader); |
||
119 | $this->registerBundlesConfiguration($bundles, $config, $loader, $useDoctrine); |
||
120 | $this->registerCacheConfiguration($container); |
||
121 | $this->registerDoctrineExtensionConfiguration($container, $config, $useDoctrine); |
||
122 | $this->registerHttpCache($container, $config, $loader, $useDoctrine); |
||
123 | } |
||
124 | |||
125 | /** |
||
126 | * Handles configuration. |
||
127 | * |
||
128 | * @param ContainerBuilder $container |
||
129 | * @param array $config |
||
130 | * @param array $formats |
||
131 | * @param array $errorFormats |
||
132 | */ |
||
133 | private function handleConfig(ContainerBuilder $container, array $config, array $formats, array $errorFormats) |
||
134 | { |
||
135 | $container->setParameter('api_platform.title', $config['title']); |
||
136 | $container->setParameter('api_platform.description', $config['description']); |
||
137 | $container->setParameter('api_platform.version', $config['version']); |
||
138 | $container->setParameter('api_platform.exception_to_status', $config['exception_to_status']); |
||
139 | $container->setParameter('api_platform.formats', $formats); |
||
140 | $container->setParameter('api_platform.error_formats', $errorFormats); |
||
141 | $container->setParameter('api_platform.eager_loading.enabled', $config['eager_loading']['enabled']); |
||
142 | $container->setParameter('api_platform.eager_loading.max_joins', $config['eager_loading']['max_joins']); |
||
143 | $container->setParameter('api_platform.eager_loading.fetch_partial', $config['eager_loading']['fetch_partial']); |
||
144 | $container->setParameter('api_platform.eager_loading.force_eager', $config['eager_loading']['force_eager']); |
||
145 | $container->setParameter('api_platform.collection.order', $config['collection']['order']); |
||
146 | $container->setParameter('api_platform.collection.order_parameter_name', $config['collection']['order_parameter_name']); |
||
147 | $container->setParameter('api_platform.collection.pagination.enabled', $config['collection']['pagination']['enabled']); |
||
148 | $container->setParameter('api_platform.collection.pagination.client_enabled', $config['collection']['pagination']['client_enabled']); |
||
149 | $container->setParameter('api_platform.collection.pagination.client_items_per_page', $config['collection']['pagination']['client_items_per_page']); |
||
150 | $container->setParameter('api_platform.collection.pagination.items_per_page', $config['collection']['pagination']['items_per_page']); |
||
151 | $container->setParameter('api_platform.collection.pagination.maximum_items_per_page', $config['collection']['pagination']['maximum_items_per_page']); |
||
152 | $container->setParameter('api_platform.collection.pagination.page_parameter_name', $config['collection']['pagination']['page_parameter_name']); |
||
153 | $container->setParameter('api_platform.collection.pagination.enabled_parameter_name', $config['collection']['pagination']['enabled_parameter_name']); |
||
154 | $container->setParameter('api_platform.collection.pagination.items_per_page_parameter_name', $config['collection']['pagination']['items_per_page_parameter_name']); |
||
155 | $container->setParameter('api_platform.http_cache.etag', $config['http_cache']['etag']); |
||
156 | $container->setParameter('api_platform.http_cache.max_age', $config['http_cache']['max_age']); |
||
157 | $container->setParameter('api_platform.http_cache.shared_max_age', $config['http_cache']['shared_max_age']); |
||
158 | $container->setParameter('api_platform.http_cache.vary', $config['http_cache']['vary']); |
||
159 | $container->setParameter('api_platform.http_cache.public', $config['http_cache']['public']); |
||
160 | |||
161 | $container->setAlias('api_platform.operation_path_resolver.default', $config['default_operation_path_resolver']); |
||
162 | |||
163 | if ($config['name_converter']) { |
||
164 | $container->setAlias('api_platform.name_converter', $config['name_converter']); |
||
165 | } |
||
166 | } |
||
167 | |||
168 | /** |
||
169 | * Registers metadata configuration. |
||
170 | * |
||
171 | * @param ContainerBuilder $container |
||
172 | * @param array $config |
||
173 | * @param XmlFileLoader $loader |
||
174 | */ |
||
175 | private function registerMetadataConfiguration(ContainerBuilder $container, array $config, XmlFileLoader $loader) |
||
176 | { |
||
177 | $loader->load('metadata/metadata.xml'); |
||
178 | $loader->load('metadata/xml.xml'); |
||
179 | |||
180 | list($xmlResources, $yamlResources) = $this->getResourcesToWatch($container, $config['mapping']['paths']); |
||
181 | |||
182 | $container->getDefinition('api_platform.metadata.extractor.xml')->addArgument($xmlResources); |
||
183 | |||
184 | if (class_exists(Annotation::class)) { |
||
185 | $loader->load('metadata/annotation.xml'); |
||
186 | } |
||
187 | |||
188 | if (class_exists(Yaml::class)) { |
||
189 | $loader->load('metadata/yaml.xml'); |
||
190 | $container->getDefinition('api_platform.metadata.extractor.yaml')->addArgument($yamlResources); |
||
191 | } |
||
192 | |||
193 | if (interface_exists(DocBlockFactoryInterface::class)) { |
||
194 | $loader->load('metadata/php_doc.xml'); |
||
195 | } |
||
196 | } |
||
197 | |||
198 | private function getBundlesResourcesPaths(ContainerBuilder $container): array |
||
199 | { |
||
200 | $bundlesResourcesPaths = []; |
||
201 | |||
202 | foreach ($container->getParameter('kernel.bundles_metadata') as $bundle) { |
||
203 | $dirname = $bundle['path']; |
||
204 | |||
205 | foreach (['.yaml', '.yml', '.xml', ''] as $extension) { |
||
206 | $paths[] = $dirname.'/Resources/config/api_resources'.$extension; |
||
207 | } |
||
208 | |||
209 | $paths[] = $dirname.'/Entity'; |
||
210 | |||
211 | foreach ($paths as $path) { |
||
212 | if ($container->fileExists($path, false)) { |
||
213 | $bundlesResourcesPaths[] = $path; |
||
214 | } |
||
215 | } |
||
216 | } |
||
217 | |||
218 | return $bundlesResourcesPaths; |
||
219 | } |
||
220 | |||
221 | private function getResourcesToWatch(ContainerBuilder $container, array $resourcesPaths): array |
||
222 | { |
||
223 | $paths = array_unique(array_merge($resourcesPaths, $this->getBundlesResourcesPaths($container))); |
||
224 | $resources = ['yml' => [], 'xml' => [], 'dir' => []]; |
||
225 | |||
226 | foreach ($paths as $path) { |
||
227 | if (is_dir($path)) { |
||
228 | foreach (Finder::create()->followLinks()->files()->in($path)->name('/\.(xml|ya?ml)$/') as $file) { |
||
229 | $resources['yaml' === ($extension = $file->getExtension()) ? 'yml' : $extension][] = $file->getRealPath(); |
||
230 | } |
||
231 | |||
232 | $resources['dir'][] = $path; |
||
233 | $container->addResource(new DirectoryResource($path, '/\.(xml|ya?ml|php)$/')); |
||
234 | } elseif ($container->fileExists($path, false)) { |
||
235 | if (!preg_match('/\.(xml|ya?ml)$/', $path, $matches)) { |
||
236 | throw new RuntimeException(sprintf('Unsupported mapping type in "%s", supported types are XML & Yaml.', $path)); |
||
237 | } |
||
238 | |||
239 | $resources['yaml' === $matches[1] ? 'yml' : $matches[1]][] = $path; |
||
240 | } else { |
||
241 | throw new RuntimeException(sprintf('Could not open file or directory "%s".', $path)); |
||
242 | } |
||
243 | } |
||
244 | |||
245 | $container->setParameter('api_platform.resource_class_directories', $resources['dir']); |
||
246 | |||
247 | return [$resources['xml'], $resources['yml']]; |
||
248 | } |
||
249 | |||
250 | /** |
||
251 | * Registers the OAuth configuration. |
||
252 | * |
||
253 | * @param ContainerBuilder $container |
||
254 | * @param array $config |
||
255 | * @param XmlFileLoader $loader |
||
256 | */ |
||
257 | private function registerOAuthConfiguration(ContainerBuilder $container, array $config, XmlFileLoader $loader) |
||
0 ignored issues
–
show
|
|||
258 | { |
||
259 | if (!$config['oauth']) { |
||
260 | return; |
||
261 | } |
||
262 | |||
263 | $container->setParameter('api_platform.oauth.enabled', $config['oauth']['enabled']); |
||
264 | $container->setParameter('api_platform.oauth.clientId', $config['oauth']['clientId']); |
||
265 | $container->setParameter('api_platform.oauth.clientSecret', $config['oauth']['clientSecret']); |
||
266 | $container->setParameter('api_platform.oauth.type', $config['oauth']['type']); |
||
267 | $container->setParameter('api_platform.oauth.flow', $config['oauth']['flow']); |
||
268 | $container->setParameter('api_platform.oauth.tokenUrl', $config['oauth']['tokenUrl']); |
||
269 | $container->setParameter('api_platform.oauth.authorizationUrl', $config['oauth']['authorizationUrl']); |
||
270 | $container->setParameter('api_platform.oauth.scopes', $config['oauth']['scopes']); |
||
271 | } |
||
272 | |||
273 | /** |
||
274 | * Registers the api keys configuration. |
||
275 | * |
||
276 | * @param ContainerBuilder $container |
||
277 | * @param array $config |
||
278 | * @param XmlFileLoader $loader |
||
279 | */ |
||
280 | private function registerApiKeysConfiguration(ContainerBuilder $container, array $config, XmlFileLoader $loader) |
||
0 ignored issues
–
show
|
|||
281 | { |
||
282 | $container->setParameter('api_platform.swagger.api_keys', $config['swagger']['api_keys']); |
||
283 | } |
||
284 | |||
285 | /** |
||
286 | * Registers the Swagger and Swagger UI configuration. |
||
287 | * |
||
288 | * @param ContainerBuilder $container |
||
289 | * @param array $config |
||
290 | * @param XmlFileLoader $loader |
||
291 | */ |
||
292 | private function registerSwaggerConfiguration(ContainerBuilder $container, array $config, XmlFileLoader $loader) |
||
293 | { |
||
294 | if (!$config['enable_swagger']) { |
||
295 | return; |
||
296 | } |
||
297 | |||
298 | $loader->load('swagger.xml'); |
||
299 | |||
300 | if ($config['enable_swagger_ui']) { |
||
301 | $loader->load('swagger-ui.xml'); |
||
302 | $container->setParameter('api_platform.enable_swagger_ui', $config['enable_swagger_ui']); |
||
303 | } |
||
304 | |||
305 | $container->setParameter('api_platform.enable_swagger', $config['enable_swagger']); |
||
306 | } |
||
307 | |||
308 | /** |
||
309 | * Registers the JSON-LD and Hydra configuration. |
||
310 | * |
||
311 | * @param array $formats |
||
312 | * @param XmlFileLoader $loader |
||
313 | */ |
||
314 | private function registerJsonLdConfiguration(array $formats, XmlFileLoader $loader) |
||
315 | { |
||
316 | if (!isset($formats['jsonld'])) { |
||
317 | return; |
||
318 | } |
||
319 | |||
320 | $loader->load('jsonld.xml'); |
||
321 | $loader->load('hydra.xml'); |
||
322 | } |
||
323 | |||
324 | /** |
||
325 | * Registers the HAL configuration. |
||
326 | * |
||
327 | * @param array $formats |
||
328 | * @param XmlFileLoader $loader |
||
329 | */ |
||
330 | private function registerJsonHalConfiguration(array $formats, XmlFileLoader $loader) |
||
331 | { |
||
332 | if (!isset($formats['jsonhal'])) { |
||
333 | return; |
||
334 | } |
||
335 | |||
336 | $loader->load('hal.xml'); |
||
337 | } |
||
338 | |||
339 | /** |
||
340 | * Registers the JSON Problem configuration. |
||
341 | * |
||
342 | * @param array $errorFormats |
||
343 | * @param XmlFileLoader $loader |
||
344 | */ |
||
345 | private function registerJsonProblemConfiguration(array $errorFormats, XmlFileLoader $loader) |
||
346 | { |
||
347 | if (!isset($errorFormats['jsonproblem'])) { |
||
348 | return; |
||
349 | } |
||
350 | |||
351 | $loader->load('problem.xml'); |
||
352 | } |
||
353 | |||
354 | /** |
||
355 | * Registers configuration for integration with third-party bundles. |
||
356 | * |
||
357 | * @param string[] $bundles |
||
358 | * @param array $config |
||
359 | * @param XmlFileLoader $loader |
||
360 | * @param bool $useDoctrine |
||
361 | */ |
||
362 | private function registerBundlesConfiguration(array $bundles, array $config, XmlFileLoader $loader, bool $useDoctrine) |
||
363 | { |
||
364 | // Doctrine ORM support |
||
365 | if ($useDoctrine) { |
||
366 | $loader->load('doctrine_orm.xml'); |
||
367 | } |
||
368 | |||
369 | // FOSUser support |
||
370 | if (isset($bundles['FOSUserBundle']) && $config['enable_fos_user']) { |
||
371 | $loader->load('fos_user.xml'); |
||
372 | } |
||
373 | |||
374 | // NelmioApiDoc support |
||
375 | if (isset($bundles['NelmioApiDocBundle']) && $config['enable_nelmio_api_doc']) { |
||
376 | $loader->load('nelmio_api_doc.xml'); |
||
377 | } |
||
378 | } |
||
379 | |||
380 | /** |
||
381 | * Registers the cache configuration. |
||
382 | * |
||
383 | * @param ContainerBuilder $container |
||
384 | */ |
||
385 | private function registerCacheConfiguration(ContainerBuilder $container) |
||
386 | { |
||
387 | // Don't use system cache pool in dev |
||
388 | if (!$container->getParameter('kernel.debug')) { |
||
389 | return; |
||
390 | } |
||
391 | |||
392 | $container->register('api_platform.cache.metadata.property', ArrayAdapter::class); |
||
393 | $container->register('api_platform.cache.metadata.resource', ArrayAdapter::class); |
||
394 | $container->register('api_platform.cache.route_name_resolver', ArrayAdapter::class); |
||
395 | } |
||
396 | |||
397 | /** |
||
398 | * Manipulate doctrine extension services according to the configuration. |
||
399 | * |
||
400 | * @param ContainerBuilder $container |
||
401 | * @param array $config |
||
402 | * @param bool $useDoctrine |
||
403 | */ |
||
404 | private function registerDoctrineExtensionConfiguration(ContainerBuilder $container, array $config, bool $useDoctrine) |
||
405 | { |
||
406 | if (!$useDoctrine || $config['eager_loading']['enabled']) { |
||
407 | return; |
||
408 | } |
||
409 | |||
410 | $container->removeDefinition('api_platform.doctrine.orm.query_extension.eager_loading'); |
||
411 | $container->removeDefinition('api_platform.doctrine.orm.query_extension.filter_eager_loading'); |
||
412 | } |
||
413 | |||
414 | private function registerHttpCache(ContainerBuilder $container, array $config, XmlFileLoader $loader, bool $useDoctrine) |
||
415 | { |
||
416 | $loader->load('http_cache.xml'); |
||
417 | |||
418 | if (!$config['http_cache']['invalidation']['enabled']) { |
||
419 | return; |
||
420 | } |
||
421 | |||
422 | if ($useDoctrine) { |
||
423 | $loader->load('doctrine_orm_http_cache_purger.xml'); |
||
424 | } |
||
425 | |||
426 | $loader->load('http_cache_tags.xml'); |
||
427 | |||
428 | if (!$config['http_cache']['invalidation']['varnish_urls']) { |
||
429 | return; |
||
430 | } |
||
431 | |||
432 | $references = []; |
||
433 | foreach ($config['http_cache']['invalidation']['varnish_urls'] as $url) { |
||
434 | $id = sprintf('api_platform.http_cache.purger.varnish_client.%s', $url); |
||
435 | $references[] = new Reference($id); |
||
436 | |||
437 | $definition = new ChildDefinition('api_platform.http_cache.purger.varnish_client'); |
||
438 | $definition->addArgument(['base_uri' => $url]); |
||
439 | $container->setDefinition($id, $definition); |
||
440 | } |
||
441 | |||
442 | $container->getDefinition('api_platform.http_cache.purger.varnish')->addArgument($references); |
||
443 | $container->setAlias('api_platform.http_cache.purger', 'api_platform.http_cache.purger.varnish'); |
||
444 | } |
||
445 | |||
446 | /** |
||
447 | * Normalizes the format from config to the one accepted by Symfony HttpFoundation. |
||
448 | * |
||
449 | * @param array $configFormats |
||
450 | * |
||
451 | * @return array |
||
452 | */ |
||
453 | private function getFormats(array $configFormats): array |
||
454 | { |
||
455 | $formats = []; |
||
456 | foreach ($configFormats as $format => $value) { |
||
457 | foreach ($value['mime_types'] as $mimeType) { |
||
458 | $formats[$format][] = $mimeType; |
||
459 | } |
||
460 | } |
||
461 | |||
462 | return $formats; |
||
463 | } |
||
464 | } |
||
465 |
This check looks from parameters that have been defined for a function or method, but which are not used in the method body.