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 Ynlo\GraphQLBundle\Definition\Loader\Annotation\AnnotationParserInterface; |
17
|
|
|
use Ynlo\GraphQLBundle\Definition\Registry\Endpoint; |
18
|
|
|
|
19
|
|
|
/** |
20
|
|
|
* Resolve and load definitions based on common annotations |
21
|
|
|
*/ |
22
|
|
|
class AnnotationLoader implements DefinitionLoaderInterface |
23
|
|
|
{ |
24
|
|
|
/** |
25
|
|
|
* Folders inside bundles to locate definitions |
26
|
|
|
* TODO: allow add additional mapping on bundle config |
27
|
|
|
*/ |
28
|
|
|
private const DEFINITIONS_LOCATIONS = [ |
29
|
|
|
'Model', //non persistent models like interfaces, or abstract classes |
30
|
|
|
'Entity', //doctrine entities |
31
|
|
|
'Mutation', //custom actions |
32
|
|
|
'Query', //custom actions |
33
|
|
|
]; |
34
|
|
|
|
35
|
|
|
/** |
36
|
|
|
* @var Kernel |
37
|
|
|
*/ |
38
|
|
|
protected $kernel; |
39
|
|
|
|
40
|
|
|
/** |
41
|
|
|
* @var Reader |
42
|
|
|
*/ |
43
|
|
|
protected $reader; |
44
|
|
|
|
45
|
|
|
/** |
46
|
|
|
* @var iterable|array|AnnotationParserInterface[] |
47
|
|
|
*/ |
48
|
|
|
protected $annotationParsers = []; |
49
|
|
|
|
50
|
|
|
/** |
51
|
|
|
* @param Kernel $kernel |
52
|
|
|
* @param Reader $reader |
53
|
|
|
* @param iterable|AnnotationParserInterface[] $annotationParsers |
54
|
|
|
*/ |
55
|
1 |
|
public function __construct(Kernel $kernel, Reader $reader, $annotationParsers = []) |
56
|
|
|
{ |
57
|
1 |
|
$this->kernel = $kernel; |
58
|
1 |
|
$this->reader = $reader; |
59
|
1 |
|
$this->annotationParsers = $annotationParsers; |
60
|
1 |
|
} |
61
|
|
|
|
62
|
|
|
/** |
63
|
|
|
* {@inheritdoc} |
64
|
|
|
*/ |
65
|
1 |
|
public function loadDefinitions(Endpoint $endpoint): void |
66
|
|
|
{ |
67
|
1 |
|
$classesToLoad = $this->resolveClasses(); |
68
|
1 |
|
$annotationsMapping = []; |
69
|
1 |
|
foreach ($classesToLoad as $class) { |
70
|
1 |
|
$refClass = new \ReflectionClass($class); |
71
|
1 |
|
$annotations = $this->reader->getClassAnnotations($refClass); |
72
|
1 |
|
if ($annotations) { |
|
|
|
|
73
|
1 |
|
$annotationsMapping[$refClass->getName()] = [$refClass, $annotations]; |
74
|
|
|
} |
75
|
|
|
} |
76
|
|
|
|
77
|
1 |
|
foreach ($this->annotationParsers as $parser) { |
78
|
1 |
|
if ($parser instanceof AnnotationParserInterface) { |
79
|
1 |
|
foreach ($annotationsMapping as $className => $map) { |
80
|
1 |
|
list($refClass, $annotations) = $map; |
81
|
1 |
|
foreach ($annotations as $annotation) { |
82
|
1 |
|
if ($parser->supports($annotation)) { |
83
|
1 |
|
$parser->parse($annotation, $refClass, $endpoint); |
84
|
|
|
} |
85
|
|
|
} |
86
|
|
|
} |
87
|
|
|
} |
88
|
|
|
} |
89
|
1 |
|
} |
90
|
|
|
|
91
|
|
|
/** |
92
|
|
|
* @return array |
93
|
|
|
*/ |
94
|
1 |
|
protected function resolveClasses(): array |
95
|
|
|
{ |
96
|
1 |
|
$bundles = $this->kernel->getBundles(); |
97
|
1 |
|
$classes = [[]]; |
98
|
1 |
|
foreach (self::DEFINITIONS_LOCATIONS as $definitionLocation) { |
99
|
1 |
|
foreach ($bundles as $bundle) { |
100
|
1 |
|
$path = $bundle->getPath().'/'.$definitionLocation; |
101
|
1 |
|
if (file_exists($path)) { |
102
|
1 |
|
$classes[] = $this->extractNamespaceClasses($path, $bundle->getNamespace(), $definitionLocation); |
103
|
|
|
} |
104
|
|
|
} |
105
|
|
|
|
106
|
1 |
|
if (Kernel::VERSION_ID >= 40000) { |
107
|
|
|
$path = $this->kernel->getRootDir().'/'.$definitionLocation; |
108
|
|
|
if (file_exists($path)) { |
109
|
1 |
|
$classes[] = $this->extractNamespaceClasses($path, 'App', $definitionLocation); |
110
|
|
|
} |
111
|
|
|
} |
112
|
|
|
} |
113
|
|
|
|
114
|
1 |
|
$classes = array_merge(...$classes); |
115
|
|
|
|
116
|
1 |
|
return array_unique($classes); |
117
|
|
|
} |
118
|
|
|
|
119
|
|
|
/** |
120
|
|
|
* @param string $path |
121
|
|
|
* @param string $baseNamespace |
122
|
|
|
* @param string $baseLocation |
123
|
|
|
* |
124
|
|
|
* @return array |
125
|
|
|
*/ |
126
|
1 |
|
protected function extractNamespaceClasses($path, $baseNamespace, $baseLocation) |
127
|
|
|
{ |
128
|
1 |
|
$classes = []; |
129
|
1 |
|
$finder = new Finder(); |
130
|
1 |
|
foreach ($finder->in($path)->name('/.php$/')->getIterator() as $file) { |
131
|
1 |
|
$className = preg_replace('/.php$/', null, $file->getFilename()); |
132
|
1 |
|
if ($file->getRelativePath()) { |
133
|
1 |
|
$subNamespace = str_replace('/', '\\', $file->getRelativePath()); |
134
|
1 |
|
$fullyClassName = $baseNamespace.'\\'.$baseLocation.'\\'.$subNamespace.'\\'.$className; |
135
|
|
|
} else { |
136
|
1 |
|
$fullyClassName = $baseNamespace.'\\'.$baseLocation.'\\'.$className; |
137
|
|
|
} |
138
|
1 |
|
if (class_exists($fullyClassName) || interface_exists($fullyClassName)) { |
139
|
1 |
|
$classes[] = $fullyClassName; |
140
|
|
|
} |
141
|
|
|
} |
142
|
|
|
|
143
|
1 |
|
return $classes; |
144
|
|
|
} |
145
|
|
|
} |
146
|
|
|
|
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.