1 | <?php |
||||
2 | /******************************************************************************* |
||||
3 | * This file is part of the GraphQL Bundle package. |
||||
4 | * |
||||
5 | * (c) YnloUltratech <[email protected]> |
||||
6 | * |
||||
7 | * For the full copyright and license information, please view the LICENSE |
||||
8 | * file that was distributed with this source code. |
||||
9 | ******************************************************************************/ |
||||
10 | |||||
11 | namespace Ynlo\GraphQLBundle\Definition\Loader; |
||||
12 | |||||
13 | use Doctrine\Common\Annotations\Reader; |
||||
14 | use Symfony\Component\Finder\Finder; |
||||
15 | use Symfony\Component\HttpKernel\Kernel; |
||||
16 | use Symfony\Component\HttpKernel\KernelInterface; |
||||
17 | use Ynlo\GraphQLBundle\Definition\Loader\Annotation\AnnotationParserInterface; |
||||
18 | use Ynlo\GraphQLBundle\Definition\Registry\Endpoint; |
||||
19 | |||||
20 | /** |
||||
21 | * Resolve and load definitions based on common annotations |
||||
22 | */ |
||||
23 | class AnnotationLoader implements DefinitionLoaderInterface |
||||
24 | { |
||||
25 | /** |
||||
26 | * Folders inside bundles to locate definitions |
||||
27 | * TODO: allow add additional mapping on bundle config |
||||
28 | */ |
||||
29 | private const DEFINITIONS_LOCATIONS = [ |
||||
30 | 'Model', //non persistent models like interfaces, or abstract classes |
||||
31 | 'Entity', //doctrine entities |
||||
32 | 'Mutation', //custom actions |
||||
33 | 'Subscription', //custom subscriptions |
||||
34 | 'Query', //custom actions |
||||
35 | ]; |
||||
36 | |||||
37 | /** |
||||
38 | * @var Kernel |
||||
39 | */ |
||||
40 | protected $kernel; |
||||
41 | |||||
42 | /** |
||||
43 | * @var Reader |
||||
44 | */ |
||||
45 | protected $reader; |
||||
46 | |||||
47 | /** |
||||
48 | * @var iterable|array|AnnotationParserInterface[] |
||||
49 | */ |
||||
50 | protected $annotationParsers = []; |
||||
51 | |||||
52 | /** |
||||
53 | * @param KernelInterface $kernel |
||||
54 | * @param Reader $reader |
||||
55 | * @param iterable|AnnotationParserInterface[] $annotationParsers |
||||
56 | */ |
||||
57 | 1 | public function __construct(KernelInterface $kernel, Reader $reader, $annotationParsers = []) |
|||
58 | { |
||||
59 | 1 | $this->kernel = $kernel; |
|||
0 ignored issues
–
show
|
|||||
60 | 1 | $this->reader = $reader; |
|||
61 | 1 | $this->annotationParsers = $annotationParsers; |
|||
62 | 1 | } |
|||
63 | |||||
64 | /** |
||||
65 | * {@inheritdoc} |
||||
66 | */ |
||||
67 | 1 | public function loadDefinitions(Endpoint $endpoint): void |
|||
68 | { |
||||
69 | 1 | $classesToLoad = $this->resolveClasses(); |
|||
70 | 1 | $annotationsMapping = []; |
|||
71 | 1 | foreach ($classesToLoad as $class) { |
|||
72 | $refClass = new \ReflectionClass($class); |
||||
73 | $annotations = $this->reader->getClassAnnotations($refClass); |
||||
74 | if ($annotations) { |
||||
0 ignored issues
–
show
The expression
$annotations of type array<mixed,object> 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 ![]() |
|||||
75 | $annotationsMapping[$refClass->getName()] = [$refClass, $annotations]; |
||||
76 | } |
||||
77 | } |
||||
78 | |||||
79 | 1 | foreach ($this->annotationParsers as $parser) { |
|||
80 | 1 | if ($parser instanceof AnnotationParserInterface) { |
|||
81 | 1 | foreach ($annotationsMapping as $className => $map) { |
|||
82 | list($refClass, $annotations) = $map; |
||||
83 | foreach ($annotations as $annotation) { |
||||
84 | if ($parser->supports($annotation)) { |
||||
85 | $parser->parse($annotation, $refClass, $endpoint); |
||||
86 | } |
||||
87 | } |
||||
88 | } |
||||
89 | } |
||||
90 | } |
||||
91 | 1 | } |
|||
92 | |||||
93 | /** |
||||
94 | * @return array |
||||
95 | */ |
||||
96 | 1 | protected function resolveClasses(): array |
|||
97 | { |
||||
98 | 1 | $bundles = $this->kernel->getBundles(); |
|||
99 | 1 | $classes = [[]]; |
|||
100 | 1 | foreach (self::DEFINITIONS_LOCATIONS as $definitionLocation) { |
|||
101 | 1 | foreach ($bundles as $bundle) { |
|||
102 | 1 | $path = $bundle->getPath().'/'.$definitionLocation; |
|||
103 | 1 | if (file_exists($path)) { |
|||
104 | $classes[] = $this->extractNamespaceClasses($path, $bundle->getNamespace(), $definitionLocation); |
||||
105 | } |
||||
106 | } |
||||
107 | |||||
108 | 1 | if (Kernel::VERSION_ID >= 40000) { |
|||
109 | 1 | $path = $this->kernel->getRootDir().'/'.$definitionLocation; |
|||
0 ignored issues
–
show
The function
Symfony\Component\HttpKernel\Kernel::getRootDir() has been deprecated: since Symfony 4.2, use getProjectDir() instead
(
Ignorable by Annotation
)
If this is a false-positive, you can also ignore this issue in your code via the
This function has been deprecated. The supplier of the function has supplied an explanatory message. The explanatory message should give you some clue as to whether and when the function will be removed and what other function to use instead. ![]() |
|||||
110 | 1 | if (file_exists($path)) { |
|||
111 | $classes[] = $this->extractNamespaceClasses($path, (new \ReflectionClass($this->kernel))->getNamespaceName(), $definitionLocation); |
||||
112 | } |
||||
113 | } |
||||
114 | } |
||||
115 | |||||
116 | 1 | $classes = array_merge(...$classes); |
|||
117 | |||||
118 | 1 | return array_unique($classes); |
|||
119 | } |
||||
120 | |||||
121 | /** |
||||
122 | * @param string $path |
||||
123 | * @param string $baseNamespace |
||||
124 | * @param string $baseLocation |
||||
125 | * |
||||
126 | * @return array |
||||
127 | */ |
||||
128 | protected function extractNamespaceClasses($path, $baseNamespace, $baseLocation) |
||||
129 | { |
||||
130 | $classes = []; |
||||
131 | $finder = new Finder(); |
||||
132 | foreach ($finder->in($path)->name('/.php$/')->getIterator() as $file) { |
||||
133 | $className = preg_replace('/.php$/', null, $file->getFilename()); |
||||
134 | if ($file->getRelativePath()) { |
||||
135 | $subNamespace = str_replace('/', '\\', $file->getRelativePath()); |
||||
136 | $fullyClassName = $baseNamespace.'\\'.$baseLocation.'\\'.$subNamespace.'\\'.$className; |
||||
137 | } else { |
||||
138 | $fullyClassName = $baseNamespace.'\\'.$baseLocation.'\\'.$className; |
||||
139 | } |
||||
140 | if (class_exists($fullyClassName) || interface_exists($fullyClassName)) { |
||||
141 | $classes[] = $fullyClassName; |
||||
142 | } |
||||
143 | } |
||||
144 | |||||
145 | return $classes; |
||||
146 | } |
||||
147 | } |
||||
148 |
Our type inference engine has found a suspicous assignment of a value to a property. This check raises an issue when a value that can be of a given class or a super-class is assigned to a property that is type hinted more strictly.
Either this assignment is in error or an instanceof check should be added for that assignment.