This project does not seem to handle request data directly as such no vulnerable execution paths were found.
include
, or for example
via PHP's auto-loading mechanism.
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 software package is licensed under AGPL, Commercial license. |
||
5 | * |
||
6 | * @package maslosoft/addendum |
||
7 | * @licence AGPL, Commercial |
||
8 | * @copyright Copyright (c) Piotr Masełkowski <[email protected]> (Meta container, further improvements, bugfixes) |
||
9 | * @copyright Copyright (c) Maslosoft (Meta container, further improvements, bugfixes) |
||
10 | * @copyright Copyright (c) Jan Suchal (Original version, builder, parser) |
||
11 | * @link https://maslosoft.com/addendum/ - maslosoft addendum |
||
12 | * @link https://code.google.com/p/addendum/ - original addendum project |
||
13 | */ |
||
14 | |||
15 | namespace Maslosoft\Addendum\Builder; |
||
16 | |||
17 | use Exception; |
||
18 | use Maslosoft\Addendum\Addendum; |
||
19 | use Maslosoft\Addendum\Annotations\TargetAnnotation; |
||
20 | use Maslosoft\Addendum\Cache\BuildOneCache; |
||
21 | use Maslosoft\Addendum\Collections\AnnotationsCollection; |
||
22 | use Maslosoft\Addendum\Collections\MatcherConfig; |
||
23 | use Maslosoft\Addendum\Helpers\CoarseChecker; |
||
24 | use Maslosoft\Addendum\Interfaces\AnnotationInterface; |
||
25 | use Maslosoft\Addendum\Matcher\AnnotationsMatcher; |
||
26 | use Maslosoft\Addendum\Reflection\ReflectionAnnotatedClass; |
||
27 | use Maslosoft\Addendum\Reflection\ReflectionAnnotatedMethod; |
||
28 | use Maslosoft\Addendum\Reflection\ReflectionAnnotatedProperty; |
||
29 | use Maslosoft\Addendum\Utilities\Blacklister; |
||
30 | use Maslosoft\Addendum\Utilities\ClassChecker; |
||
31 | use Maslosoft\Addendum\Utilities\NameNormalizer; |
||
32 | use Maslosoft\Addendum\Utilities\ReflectionName; |
||
33 | use ReflectionClass; |
||
34 | use ReflectionMethod; |
||
35 | use ReflectionProperty; |
||
36 | use Reflector; |
||
37 | |||
38 | /** |
||
39 | * @Label("Annotations builder") |
||
40 | */ |
||
41 | class Builder |
||
42 | { |
||
43 | |||
44 | /** |
||
45 | * Cached values of parsing |
||
46 | * @var string[][][] |
||
47 | */ |
||
48 | private static $cache = []; |
||
49 | |||
50 | /** |
||
51 | * Addendum instance |
||
52 | * @var Addendum |
||
53 | */ |
||
54 | private $addendum = null; |
||
55 | |||
56 | /** |
||
57 | * One entity build cache |
||
58 | * @var BuildOneCache |
||
59 | */ |
||
60 | private $buildCache = null; |
||
61 | |||
62 | 58 | public function __construct(Addendum $addendum = null) |
|
63 | { |
||
64 | 58 | $this->addendum = $addendum ?: new Addendum(); |
|
65 | 58 | $this->buildCache = new BuildOneCache(static::class, null, $this->addendum); |
|
66 | 58 | } |
|
67 | |||
68 | /** |
||
69 | * Build annotations collection |
||
70 | * @param ReflectionAnnotatedClass|ReflectionAnnotatedMethod|ReflectionAnnotatedProperty $targetReflection |
||
71 | * @return AnnotationsCollection |
||
72 | */ |
||
73 | 58 | public function build($targetReflection) |
|
74 | { |
||
75 | 58 | $annotations = []; |
|
76 | |||
77 | 58 | $data = $this->buildOne($targetReflection); |
|
78 | |||
79 | // Get annotations from current entity |
||
80 | 58 | foreach ($data as $class => $parameters) |
|
81 | { |
||
82 | 55 | foreach ($parameters as $params) |
|
83 | { |
||
84 | 55 | $annotation = $this->instantiateAnnotation($class, $params, $targetReflection); |
|
85 | 55 | if ($annotation !== false) |
|
86 | { |
||
87 | 55 | $annotations[$class][] = $annotation; |
|
88 | } |
||
89 | } |
||
90 | } |
||
91 | |||
92 | 58 | return new AnnotationsCollection($annotations); |
|
93 | } |
||
94 | |||
95 | 58 | private function buildOne($targetReflection) |
|
96 | { |
||
97 | 58 | if (empty($targetReflection)) |
|
98 | { |
||
99 | 23 | return []; |
|
100 | } |
||
101 | // Decide where from take traits and base classes. |
||
102 | // Either from class if it's being processed reflection class |
||
103 | // or from declaring class if it's being processed for properties and |
||
104 | // methods |
||
105 | 58 | if ($targetReflection instanceof ReflectionClass) |
|
106 | { |
||
107 | 58 | $targetClass = $targetReflection; |
|
108 | } |
||
109 | else |
||
110 | { |
||
111 | 30 | assert($targetReflection instanceof ReflectionMethod || $targetReflection instanceof ReflectionProperty); |
|
112 | 30 | $targetClass = $targetReflection->getDeclaringClass(); |
|
113 | } |
||
114 | |||
115 | // Check if currently reflected component is cached |
||
116 | 58 | $this->buildCache->setComponent(ReflectionName::createName($targetReflection)); |
|
117 | 58 | $cached = $this->buildCache->get(); |
|
118 | |||
119 | // Check if currently reflected component *class* is cached |
||
120 | 58 | $this->buildCache->setComponent(ReflectionName::createName($targetClass)); |
|
121 | 58 | $cachedClass = $this->buildCache->get(); |
|
122 | |||
123 | // Cache is valid only if class cache is valid, |
||
124 | // this is required for Conflicts and Target checks |
||
125 | 58 | if (false !== $cached && false !== $cachedClass) |
|
126 | { |
||
127 | 58 | return $cached; |
|
128 | } |
||
129 | |||
130 | // Partials annotation data |
||
131 | 53 | $partialsData = []; |
|
132 | |||
133 | // Extract annotations from all partials |
||
134 | 53 | foreach ($this->getPartials($targetClass) as $partial) |
|
135 | { |
||
136 | // Need to recurse here, as: |
||
137 | // |
||
138 | // * Interface might extend from other interfaces |
||
139 | // * Parent class, might have traits or interfaces |
||
140 | // * Trait might have traits |
||
141 | 51 | $partData = $this->buildOne($this->getRelated($targetReflection, $partial)); |
|
142 | |||
143 | // Check if data is present and proper |
||
144 | 51 | if (false === $partData || empty($partData) || !is_array($partData)) |
|
145 | { |
||
146 | 46 | continue; |
|
147 | } |
||
148 | |||
149 | 17 | $partialsData = array_merge_recursive($partialsData, $partData); |
|
150 | } |
||
151 | |||
152 | // Merge data from traits etc. |
||
153 | // with data from class |
||
154 | 53 | $data = array_merge_recursive($partialsData, $this->parse($targetReflection)); |
|
0 ignored issues
–
show
|
|||
155 | |||
156 | 53 | $this->buildCache->setComponent(ReflectionName::createName($targetReflection)); |
|
157 | 53 | $this->buildCache->set($data); |
|
158 | 53 | return $data; |
|
159 | } |
||
160 | |||
161 | /** |
||
162 | * Get reflection for related target reflection. |
||
163 | * Will return class, property or method reflection from related reflection, |
||
164 | * based on type of target reflection. |
||
165 | * |
||
166 | * Will return false target reflection id for method or property, and |
||
167 | * method or property does not exists on related reflection. |
||
168 | * |
||
169 | * @param Reflector $targetReflection |
||
170 | * @param ReflectionClass $relatedReflection |
||
171 | * @return ReflectionClass|ReflectionProperty|ReflectionMethod|bool |
||
172 | */ |
||
173 | 51 | private function getRelated(Reflector $targetReflection, ReflectionClass $relatedReflection) |
|
174 | { |
||
175 | switch (true) |
||
176 | { |
||
177 | 51 | case $targetReflection instanceof ReflectionClass: |
|
178 | 43 | return $relatedReflection; |
|
179 | 26 | case $targetReflection instanceof ReflectionProperty && $relatedReflection->hasProperty($targetReflection->name): |
|
180 | 9 | return $relatedReflection->getProperty($targetReflection->name); |
|
181 | 25 | case $targetReflection instanceof ReflectionMethod && $relatedReflection->hasMethod($targetReflection->name): |
|
182 | 6 | return $relatedReflection->getMethod($targetReflection->name); |
|
183 | } |
||
184 | 23 | return false; |
|
185 | } |
||
186 | |||
187 | /** |
||
188 | * Get partials of class, this includes: |
||
189 | * |
||
190 | * * Parent class |
||
191 | * * Interfaces |
||
192 | * * Traits |
||
193 | * |
||
194 | * @param ReflectionClass $targetClass |
||
195 | * @return ReflectionAnnotatedClass[] |
||
196 | */ |
||
197 | 53 | private function getPartials(ReflectionClass $targetClass) |
|
198 | { |
||
199 | // Partial reflections |
||
200 | /* @var ReflectionAnnotatedClass[] */ |
||
201 | 53 | $partials = []; |
|
202 | |||
203 | // Collect current class interfaces |
||
204 | 53 | foreach ($targetClass->getInterfaces() as $targetInterface) |
|
205 | { |
||
206 | /* @var $targetInterface ReflectionAnnotatedClass */ |
||
207 | 48 | $partials[] = $targetInterface; |
|
208 | } |
||
209 | |||
210 | // Collect current class parent class |
||
211 | 53 | $targetParent = $targetClass->getParentClass(); |
|
212 | 53 | if (!empty($targetParent)) |
|
213 | { |
||
214 | 27 | $partials[] = $targetParent; |
|
215 | } |
||
216 | |||
217 | // Collect current class traits |
||
218 | 53 | foreach ($targetClass->getTraits() as $trait) |
|
219 | { |
||
220 | /* @var $trait ReflectionAnnotatedClass */ |
||
221 | 10 | $partials[] = $trait; |
|
222 | } |
||
223 | 53 | return $partials; |
|
224 | } |
||
225 | |||
226 | /** |
||
227 | * Create new instance of annotation |
||
228 | * @param string $class |
||
229 | * @param mixed[] $parameters |
||
230 | * @param ReflectionAnnotatedClass|ReflectionAnnotatedMethod|ReflectionAnnotatedProperty|bool $targetReflection |
||
231 | * @return boolean|object |
||
232 | */ |
||
233 | 55 | public function instantiateAnnotation($class, $parameters, $targetReflection = false) |
|
234 | { |
||
235 | 55 | $class = ucfirst($class) . "Annotation"; |
|
236 | |||
237 | // If namespaces are empty assume global namespace |
||
238 | 55 | $fqn = $this->normalizeFqn('\\', $class); |
|
239 | 55 | $mandatoryNs = TargetAnnotation::Ns; |
|
240 | 55 | $namespaces = $this->addendum->namespaces; |
|
241 | 55 | if (!in_array($mandatoryNs, $namespaces)) |
|
242 | { |
||
243 | $namespaces[] = $mandatoryNs; |
||
244 | } |
||
245 | 55 | foreach ($namespaces as $ns) |
|
246 | { |
||
247 | 55 | $fqn = $this->normalizeFqn($ns, $class); |
|
248 | 55 | if (Blacklister::ignores($fqn)) |
|
249 | { |
||
250 | 38 | continue; |
|
251 | } |
||
252 | try |
||
253 | { |
||
254 | 55 | if (!ClassChecker::exists($fqn)) |
|
255 | { |
||
256 | 52 | $this->addendum->getLogger()->debug('Annotation class `{fqn}` not found, ignoring', ['fqn' => $fqn]); |
|
257 | 52 | Blacklister::ignore($fqn); |
|
258 | } |
||
259 | else |
||
260 | { |
||
261 | // Class exists, exit loop |
||
262 | 55 | break; |
|
263 | } |
||
264 | } |
||
265 | catch (Exception $e) |
||
266 | 52 | { |
|
267 | // Ignore class autoloading errors |
||
268 | } |
||
269 | } |
||
270 | 55 | if (Blacklister::ignores($fqn)) |
|
271 | { |
||
272 | 1 | return false; |
|
273 | } |
||
274 | try |
||
275 | { |
||
276 | // NOTE: was class_exists here, however should be safe to use ClassChecker::exists here |
||
277 | 54 | if (!ClassChecker::exists($fqn)) |
|
278 | { |
||
279 | $this->addendum->getLogger()->debug('Annotation class `{fqn}` not found, ignoring', ['fqn' => $fqn]); |
||
280 | Blacklister::ignore($fqn); |
||
281 | 54 | return false; |
|
282 | } |
||
283 | } |
||
284 | catch (Exception $e) |
||
285 | { |
||
286 | // Ignore autoload errors and return false |
||
287 | Blacklister::ignore($fqn); |
||
288 | return false; |
||
289 | } |
||
290 | 54 | $resolvedClass = Addendum::resolveClassName($fqn); |
|
291 | 54 | if ((new ReflectionClass($resolvedClass))->implementsInterface(AnnotationInterface::class) || $resolvedClass == AnnotationInterface::class) |
|
292 | { |
||
293 | 54 | return new $resolvedClass($parameters, $targetReflection); |
|
294 | } |
||
295 | return false; |
||
296 | } |
||
297 | |||
298 | /** |
||
299 | * Normalize class name and namespace to proper fully qualified name |
||
300 | * @param string $ns |
||
301 | * @param string $class |
||
302 | * @return string |
||
303 | */ |
||
304 | 55 | private function normalizeFqn($ns, $class) |
|
305 | { |
||
306 | 55 | $fqn = "\\$ns\\$class"; |
|
307 | 55 | NameNormalizer::normalize($fqn); |
|
308 | 55 | return $fqn; |
|
309 | } |
||
310 | |||
311 | /** |
||
312 | * Get doc comment |
||
313 | * @param ReflectionAnnotatedClass|ReflectionAnnotatedMethod|ReflectionAnnotatedProperty $reflection |
||
314 | * @return array |
||
315 | */ |
||
316 | 53 | private function parse($reflection) |
|
317 | { |
||
318 | 53 | $key = sprintf('%s@%s', $this->addendum->getInstanceId(), ReflectionName::createName($reflection)); |
|
319 | 53 | if (!isset(self::$cache[$key])) |
|
320 | { |
||
321 | // |
||
322 | 53 | if (!CoarseChecker::mightHaveAnnotations($reflection)) |
|
323 | { |
||
324 | 27 | self::$cache[$key] = []; |
|
325 | 27 | return self::$cache[$key]; |
|
326 | } |
||
327 | 50 | $parser = new AnnotationsMatcher; |
|
328 | 50 | $data = []; |
|
329 | 50 | $parser->setPlugins(new MatcherConfig([ |
|
330 | 50 | 'addendum' => $this->addendum, |
|
331 | 50 | 'reflection' => $reflection |
|
332 | ])); |
||
333 | 50 | $parser->matches($this->getDocComment($reflection), $data); |
|
334 | 50 | self::$cache[$key] = $data; |
|
335 | } |
||
336 | 50 | return self::$cache[$key]; |
|
337 | } |
||
338 | |||
339 | /** |
||
340 | * Get doc comment |
||
341 | * @param ReflectionAnnotatedClass|ReflectionAnnotatedMethod|ReflectionAnnotatedProperty $reflection |
||
342 | * @return mixed[] |
||
343 | */ |
||
344 | 50 | protected function getDocComment($reflection) |
|
345 | { |
||
346 | 50 | return Addendum::getDocComment($reflection); |
|
347 | } |
||
348 | |||
349 | /** |
||
350 | * Clear local parsing cache |
||
351 | */ |
||
352 | 3 | public static function clearCache() |
|
353 | { |
||
354 | 3 | self::$cache = []; |
|
355 | 3 | } |
|
356 | |||
357 | } |
||
358 |
It seems like the type of the argument is not accepted by the function/method which you are calling.
In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.
We suggest to add an explicit type cast like in the following example: