1
|
|
|
<?php |
2
|
|
|
|
3
|
|
|
declare(strict_types=1); |
4
|
|
|
|
5
|
|
|
namespace Sylius\Bundle\ApiBundle\ApiPlatform\Metadata\Property\Factory; |
6
|
|
|
|
7
|
|
|
use ApiPlatform\Core\Metadata\Property\Factory\PropertyMetadataFactoryInterface; |
8
|
|
|
use ApiPlatform\Core\Metadata\Property\PropertyMetadata; |
9
|
|
|
use ApiPlatform\Core\Metadata\Property\SubresourceMetadata; |
10
|
|
|
use Psr\Container\ContainerInterface; |
11
|
|
|
use Symfony\Component\DependencyInjection\ContainerInterface as SymfonyContainerInterface; |
12
|
|
|
|
13
|
|
|
/** |
14
|
|
|
* @internal |
15
|
|
|
*/ |
16
|
|
|
final class ExtractorPropertyMetadataFactory implements PropertyMetadataFactoryInterface |
17
|
|
|
{ |
18
|
|
|
/** @var PropertyMetadataFactoryInterface */ |
19
|
|
|
private $decoratedPropertyMetadataFactory; |
20
|
|
|
/** @var ContainerInterface */ |
21
|
|
|
private $container; |
22
|
|
|
/** @var array */ |
23
|
|
|
private $collectedParameters = []; |
24
|
|
|
|
25
|
|
|
public function __construct(PropertyMetadataFactoryInterface $decoratedPropertyMetadataFactory, ContainerInterface $container) |
26
|
|
|
{ |
27
|
|
|
$this->decoratedPropertyMetadataFactory = $decoratedPropertyMetadataFactory; |
28
|
|
|
$this->container = $container; |
29
|
|
|
} |
30
|
|
|
|
31
|
|
|
public function create(string $resourceClass, string $property, array $options = []): PropertyMetadata |
32
|
|
|
{ |
33
|
|
|
$propertyMetadata = $this->decoratedPropertyMetadataFactory->create($resourceClass, $property, $options); |
34
|
|
|
|
35
|
|
|
if (!$propertyMetadata->hasSubresource()) { |
36
|
|
|
return $propertyMetadata; |
37
|
|
|
} |
38
|
|
|
|
39
|
|
|
$subresourceMetadata = $propertyMetadata->getSubresource(); |
40
|
|
|
|
41
|
|
|
return $propertyMetadata->withSubresource(new SubresourceMetadata( |
42
|
|
|
$this->resolve($subresourceMetadata->getResourceClass()), |
43
|
|
|
$subresourceMetadata->isCollection(), |
44
|
|
|
$subresourceMetadata->getMaxDepth()) |
45
|
|
|
); |
46
|
|
|
} |
47
|
|
|
|
48
|
|
|
/** |
49
|
|
|
* Recursively replaces placeholders with the service container parameters. |
50
|
|
|
* |
51
|
|
|
* @see https://github.com/symfony/symfony/blob/6fec32c/src/Symfony/Bundle/FrameworkBundle/Routing/Router.php |
52
|
|
|
* |
53
|
|
|
* @copyright (c) Fabien Potencier <[email protected]> |
54
|
|
|
* |
55
|
|
|
* @param mixed $value The source which might contain "%placeholders%" |
56
|
|
|
* |
57
|
|
|
* @throws \RuntimeException When a container value is not a string or a numeric value |
58
|
|
|
* |
59
|
|
|
* @return mixed The source with the placeholders replaced by the container |
60
|
|
|
* parameters. Arrays are resolved recursively. |
61
|
|
|
* @psalm-suppress all |
62
|
|
|
*/ |
63
|
|
|
protected function resolve($value) |
64
|
|
|
{ |
65
|
|
|
if (null === $this->container) { |
66
|
|
|
return $value; |
67
|
|
|
} |
68
|
|
|
|
69
|
|
|
if (\is_array($value)) { |
70
|
|
|
foreach ($value as $key => $val) { |
71
|
|
|
$value[$key] = $this->resolve($val); |
72
|
|
|
} |
73
|
|
|
|
74
|
|
|
return $value; |
75
|
|
|
} |
76
|
|
|
|
77
|
|
|
if (!\is_string($value)) { |
78
|
|
|
return $value; |
79
|
|
|
} |
80
|
|
|
|
81
|
|
|
$escapedValue = preg_replace_callback('/%%|%([^%\s]++)%/', function ($match) use ($value) { |
82
|
|
|
$parameter = $match[1]; |
83
|
|
|
|
84
|
|
|
// skip %% |
85
|
|
|
if (!isset($parameter)) { |
86
|
|
|
return '%%'; |
87
|
|
|
} |
88
|
|
|
|
89
|
|
|
if (preg_match('/^env\(\w+\)$/', $parameter)) { |
90
|
|
|
throw new \RuntimeException(sprintf('Using "%%%s%%" is not allowed in routing configuration.', $parameter)); |
91
|
|
|
} |
92
|
|
|
|
93
|
|
|
if (\array_key_exists($parameter, $this->collectedParameters)) { |
94
|
|
|
return $this->collectedParameters[$parameter]; |
95
|
|
|
} |
96
|
|
|
|
97
|
|
|
if ($this->container instanceof SymfonyContainerInterface) { |
|
|
|
|
98
|
|
|
$resolved = $this->container->getParameter($parameter); |
99
|
|
|
} else { |
100
|
|
|
$resolved = $this->container->get($parameter); |
101
|
|
|
} |
102
|
|
|
|
103
|
|
|
if (\is_string($resolved) || is_numeric($resolved)) { |
104
|
|
|
$this->collectedParameters[$parameter] = $resolved; |
105
|
|
|
|
106
|
|
|
return (string) $resolved; |
107
|
|
|
} |
108
|
|
|
|
109
|
|
|
throw new\ RuntimeException(sprintf('The container parameter "%s", used in the resource configuration value "%s", must be a string or numeric, but it is of type %s.', $parameter, $value, \gettype($resolved))); |
110
|
|
|
}, $value); |
111
|
|
|
|
112
|
|
|
return str_replace('%%', '%', $escapedValue); |
113
|
|
|
} |
114
|
|
|
} |
115
|
|
|
|
This error could be the result of:
1. Missing dependencies
PHP Analyzer uses your
composer.json
file (if available) to determine the dependencies of your project and to determine all the available classes and functions. It expects thecomposer.json
to be in the root folder of your repository.Are you sure this class is defined by one of your dependencies, or did you maybe not list a dependency in either the
require
orrequire-dev
section?2. Missing use statement
PHP does not complain about undefined classes in
ìnstanceof
checks. For example, the following PHP code will work perfectly fine:If you have not tested against this specific condition, such errors might go unnoticed.