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 file is part of the puli/discovery package. |
||
5 | * |
||
6 | * (c) Bernhard Schussek <[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 | namespace Puli\Discovery; |
||
13 | |||
14 | use Puli\Discovery\Api\Binding\Binding; |
||
15 | use Puli\Discovery\Api\Binding\Initializer\BindingInitializer; |
||
16 | use Puli\Discovery\Api\Type\BindingType; |
||
17 | use Puli\Discovery\Api\Type\DuplicateTypeException; |
||
18 | use Puli\Discovery\Api\Type\NoSuchTypeException; |
||
19 | use Webmozart\Assert\Assert; |
||
20 | use Webmozart\Expression\Expr; |
||
21 | use Webmozart\Expression\Expression; |
||
22 | use Webmozart\Json\JsonDecoder; |
||
23 | use Webmozart\Json\JsonEncoder; |
||
24 | |||
25 | /** |
||
26 | * A discovery backed by a JSON file. |
||
27 | * |
||
28 | * @since 1.0 |
||
29 | * |
||
30 | * @author Bernhard Schussek <[email protected]> |
||
31 | */ |
||
32 | class JsonDiscovery extends AbstractEditableDiscovery |
||
33 | { |
||
34 | /** |
||
35 | * @var string |
||
36 | */ |
||
37 | private $path; |
||
38 | |||
39 | /** |
||
40 | * @var array |
||
41 | */ |
||
42 | private $json; |
||
43 | |||
44 | /** |
||
45 | * @var JsonEncoder |
||
46 | */ |
||
47 | private $encoder; |
||
48 | |||
49 | /** |
||
50 | * Stores the binding type for each key. |
||
51 | * |
||
52 | * Synchronized with the entries "t:<key>" in the store. |
||
53 | * |
||
54 | * @var BindingType[] |
||
55 | */ |
||
56 | private $typesByKey = array(); |
||
57 | |||
58 | /** |
||
59 | * Stores the bindings for each key. |
||
60 | * |
||
61 | * Synchronized with the entries "b:<key>" in the store. |
||
62 | * |
||
63 | * @var Binding[][] |
||
64 | */ |
||
65 | private $bindingsByKey = array(); |
||
66 | |||
67 | /** |
||
68 | * Creates a new discovery. |
||
69 | * |
||
70 | * @param string $path The path to the JSON file. |
||
71 | * @param BindingInitializer[] $initializers The binding initializers to |
||
72 | * apply to newly created or |
||
73 | * unserialized bindings. |
||
74 | */ |
||
75 | 99 | public function __construct($path, array $initializers = array()) |
|
76 | { |
||
77 | 99 | Assert::stringNotEmpty($path, 'The path to the JSON file must be a non-empty string. Got: %s'); |
|
78 | |||
79 | 99 | parent::__construct($initializers); |
|
80 | |||
81 | 99 | $this->path = $path; |
|
82 | 99 | $this->encoder = new JsonEncoder(); |
|
83 | 99 | $this->encoder->setPrettyPrinting(true); |
|
84 | 99 | $this->encoder->setEscapeSlash(false); |
|
85 | 99 | } |
|
86 | |||
87 | /** |
||
88 | * {@inheritdoc} |
||
89 | */ |
||
90 | 67 | public function addBindingType(BindingType $type) |
|
91 | { |
||
92 | 67 | if (null === $this->json) { |
|
93 | 67 | $this->load(); |
|
94 | } |
||
95 | |||
96 | 67 | if (isset($this->json['keysByTypeName'][$type->getName()])) { |
|
97 | 2 | throw DuplicateTypeException::forTypeName($type->getName()); |
|
98 | } |
||
99 | |||
100 | 67 | $key = $this->json['nextKey']++; |
|
101 | |||
102 | 67 | $this->json['keysByTypeName'][$type->getName()] = $key; |
|
103 | |||
104 | 67 | $this->typesByKey[$key] = $type; |
|
105 | |||
106 | // Use integer keys to reduce storage space |
||
107 | // (compared to fully-qualified class names) |
||
108 | 67 | $this->json['typesByKey'][$key] = serialize($type); |
|
109 | |||
110 | 67 | $this->flush(); |
|
111 | 67 | } |
|
112 | |||
113 | /** |
||
114 | * {@inheritdoc} |
||
115 | */ |
||
116 | 10 | public function removeBindingType($typeName) |
|
117 | { |
||
118 | 10 | Assert::stringNotEmpty($typeName, 'The type class must be a non-empty string. Got: %s'); |
|
119 | |||
120 | 8 | if (null === $this->json) { |
|
121 | 1 | $this->load(); |
|
122 | } |
||
123 | |||
124 | 8 | if (!isset($this->json['keysByTypeName'][$typeName])) { |
|
125 | 2 | return; |
|
126 | } |
||
127 | |||
128 | 6 | $key = $this->json['keysByTypeName'][$typeName]; |
|
129 | |||
130 | 6 | if (!isset($this->bindingsByKey[$key])) { |
|
131 | // no initialize, since we're removing this anyway |
||
132 | 3 | $this->loadBindingsForKey($key, false); |
|
133 | } |
||
134 | |||
135 | 6 | unset($this->typesByKey[$key]); |
|
136 | 6 | unset($this->bindingsByKey[$key]); |
|
137 | |||
138 | 6 | unset($this->json['keysByTypeName'][$typeName]); |
|
139 | 6 | unset($this->json['typesByKey'][$key]); |
|
140 | 6 | unset($this->json['bindingsByKey'][$key]); |
|
141 | |||
142 | 6 | $this->flush(); |
|
143 | 6 | } |
|
144 | |||
145 | /** |
||
146 | * {@inheritdoc} |
||
147 | */ |
||
148 | 4 | public function removeBindingTypes() |
|
149 | { |
||
150 | 4 | if (null === $this->json) { |
|
151 | $this->load(); |
||
152 | } |
||
153 | |||
154 | 4 | $this->typesByKey = array(); |
|
155 | 4 | $this->bindingsByKey = array(); |
|
156 | |||
157 | 4 | $this->json['keysByTypeName'] = array(); |
|
158 | 4 | $this->json['typesByKey'] = array(); |
|
159 | 4 | $this->json['bindingsByKey'] = array(); |
|
160 | 4 | $this->json['nextKey'] = 0; |
|
161 | |||
162 | 4 | $this->flush(); |
|
163 | 4 | } |
|
164 | |||
165 | /** |
||
166 | * {@inheritdoc} |
||
167 | */ |
||
168 | 10 | public function hasBindingType($typeName) |
|
169 | { |
||
170 | 10 | Assert::stringNotEmpty($typeName, 'The type class must be a non-empty string. Got: %s'); |
|
171 | |||
172 | 8 | if (null === $this->json) { |
|
173 | 2 | $this->load(); |
|
174 | } |
||
175 | |||
176 | 8 | return isset($this->json['keysByTypeName'][$typeName]); |
|
177 | } |
||
178 | |||
179 | /** |
||
180 | * {@inheritdoc} |
||
181 | */ |
||
182 | 53 | public function getBindingType($typeName) |
|
183 | { |
||
184 | 53 | Assert::stringNotEmpty($typeName, 'The type class must be a non-empty string. Got: %s'); |
|
185 | |||
186 | 51 | if (null === $this->json) { |
|
187 | 4 | $this->load(); |
|
188 | } |
||
189 | |||
190 | 51 | if (!isset($this->json['keysByTypeName'][$typeName])) { |
|
191 | 2 | throw NoSuchTypeException::forTypeName($typeName); |
|
192 | } |
||
193 | |||
194 | 49 | $key = $this->json['keysByTypeName'][$typeName]; |
|
195 | |||
196 | 49 | View Code Duplication | if (!isset($this->typesByKey[$key])) { |
197 | 18 | $this->typesByKey[$key] = unserialize($this->json['typesByKey'][$key]); |
|
198 | } |
||
199 | |||
200 | 49 | return $this->typesByKey[$key]; |
|
201 | } |
||
202 | |||
203 | /** |
||
204 | * {@inheritdoc} |
||
205 | */ |
||
206 | public function hasBindingTypes() |
||
207 | { |
||
208 | if (null === $this->json) { |
||
209 | $this->load(); |
||
210 | } |
||
211 | |||
212 | return count($this->json['keysByTypeName']) > 0; |
||
213 | } |
||
214 | |||
215 | /** |
||
216 | * {@inheritdoc} |
||
217 | */ |
||
218 | 6 | public function getBindingTypes() |
|
219 | { |
||
220 | 6 | if (null === $this->json) { |
|
221 | 3 | $this->load(); |
|
222 | } |
||
223 | |||
224 | 6 | foreach ($this->json['keysByTypeName'] as $key) { |
|
225 | 4 | View Code Duplication | if (!isset($this->typesByKey[$key])) { |
226 | 4 | $this->typesByKey[$key] = unserialize($this->json['typesByKey'][$key]); |
|
227 | } |
||
228 | } |
||
229 | |||
230 | 6 | ksort($this->typesByKey); |
|
231 | |||
232 | 6 | return $this->typesByKey; |
|
233 | } |
||
234 | |||
235 | /** |
||
236 | * {@inheritdoc} |
||
237 | */ |
||
238 | 45 | public function addBinding(Binding $binding) |
|
239 | { |
||
240 | 45 | if (null === $this->json) { |
|
241 | 4 | $this->load(); |
|
242 | } |
||
243 | |||
244 | 45 | $typeName = $binding->getTypeName(); |
|
245 | |||
246 | 45 | if (!isset($this->json['keysByTypeName'][$typeName])) { |
|
247 | 2 | throw NoSuchTypeException::forTypeName($typeName); |
|
248 | } |
||
249 | |||
250 | 43 | $key = $this->json['keysByTypeName'][$typeName]; |
|
251 | |||
252 | 43 | if (!isset($this->bindingsByKey[$key])) { |
|
253 | 43 | $this->loadBindingsForKey($key); |
|
254 | } |
||
255 | |||
256 | 43 | $this->initializeBinding($binding); |
|
257 | |||
258 | 41 | $this->bindingsByKey[$key][] = $binding; |
|
259 | |||
260 | 41 | $this->json['bindingsByKey'][$key] = serialize($this->bindingsByKey[$key]); |
|
261 | |||
262 | 41 | $this->flush(); |
|
263 | 41 | } |
|
264 | |||
265 | /** |
||
266 | * {@inheritdoc} |
||
267 | */ |
||
268 | 23 | public function findBindings($typeName, Expression $expr = null) |
|
269 | { |
||
270 | 23 | Assert::stringNotEmpty($typeName, 'The type class must be a non-empty string. Got: %s'); |
|
271 | |||
272 | 21 | if (null === $this->json) { |
|
273 | 11 | $this->load(); |
|
274 | } |
||
275 | |||
276 | 21 | if (!isset($this->json['keysByTypeName'][$typeName])) { |
|
277 | 2 | return array(); |
|
278 | } |
||
279 | |||
280 | 19 | $key = $this->json['keysByTypeName'][$typeName]; |
|
281 | |||
282 | 19 | if (!isset($this->bindingsByKey[$key])) { |
|
283 | 11 | $this->loadBindingsForKey($key); |
|
284 | } |
||
285 | |||
286 | 19 | $bindings = $this->bindingsByKey[$key]; |
|
287 | |||
288 | 19 | if (null !== $expr) { |
|
289 | 2 | $bindings = Expr::filter($bindings, $expr); |
|
290 | } |
||
291 | |||
292 | 19 | return $bindings; |
|
293 | } |
||
294 | |||
295 | /** |
||
296 | * {@inheritdoc} |
||
297 | */ |
||
298 | 35 | public function getBindings() |
|
299 | { |
||
300 | 35 | if (null === $this->json) { |
|
301 | 11 | $this->load(); |
|
302 | } |
||
303 | |||
304 | 35 | $this->loadAllBindings(); |
|
305 | |||
306 | 35 | $bindings = array(); |
|
307 | |||
308 | 35 | foreach ($this->bindingsByKey as $bindingsForKey) { |
|
309 | 25 | $bindings = array_merge($bindings, $bindingsForKey); |
|
310 | } |
||
311 | |||
312 | 35 | return $bindings; |
|
313 | } |
||
314 | |||
315 | /** |
||
316 | * {@inheritdoc} |
||
317 | */ |
||
318 | 6 | protected function removeAllBindings() |
|
319 | { |
||
320 | 6 | if (null === $this->json) { |
|
321 | 3 | $this->load(); |
|
322 | } |
||
323 | |||
324 | 6 | $this->bindingsByKey = array(); |
|
325 | |||
326 | 6 | $this->json['bindingsByKey'] = array(); |
|
327 | |||
328 | 6 | $this->flush(); |
|
329 | 6 | } |
|
330 | |||
331 | /** |
||
332 | * {@inheritdoc} |
||
333 | */ |
||
334 | 2 | protected function removeBindingsThatMatch(Expression $expr) |
|
335 | { |
||
336 | 2 | if (null === $this->json) { |
|
337 | $this->load(); |
||
338 | } |
||
339 | |||
340 | 2 | $this->loadAllBindings(); |
|
341 | |||
342 | 2 | View Code Duplication | foreach ($this->bindingsByKey as $key => $bindingsForKey) { |
343 | 2 | foreach ($bindingsForKey as $i => $binding) { |
|
344 | 2 | if ($expr->evaluate($binding)) { |
|
345 | 2 | unset($this->bindingsByKey[$key][$i]); |
|
346 | } |
||
347 | } |
||
348 | |||
349 | 2 | $this->reindexBindingsForKey($key); |
|
350 | 2 | $this->syncBindingsForKey($key); |
|
351 | } |
||
352 | |||
353 | 2 | $this->flush(); |
|
354 | 2 | } |
|
355 | |||
356 | /** |
||
357 | * {@inheritdoc} |
||
358 | */ |
||
359 | 8 | protected function removeBindingsWithTypeName($typeName) |
|
360 | { |
||
361 | 8 | if (null === $this->json) { |
|
362 | 3 | $this->load(); |
|
363 | } |
||
364 | |||
365 | 8 | if (!isset($this->json['keysByTypeName'][$typeName])) { |
|
366 | 2 | return; |
|
367 | } |
||
368 | |||
369 | 6 | $key = $this->json['keysByTypeName'][$typeName]; |
|
370 | |||
371 | unset( |
||
372 | 6 | $this->bindingsByKey[$key], |
|
373 | 6 | $this->json['bindingsByKey'][$key] |
|
374 | ); |
||
375 | |||
376 | 6 | $this->flush(); |
|
377 | 6 | } |
|
378 | |||
379 | /** |
||
380 | * {@inheritdoc} |
||
381 | */ |
||
382 | 8 | protected function removeBindingsWithTypeNameThatMatch($typeName, Expression $expr) |
|
383 | { |
||
384 | 8 | if (null === $this->json) { |
|
385 | 3 | $this->load(); |
|
386 | } |
||
387 | |||
388 | 8 | if (!isset($this->json['keysByTypeName'][$typeName])) { |
|
389 | 2 | return; |
|
390 | } |
||
391 | |||
392 | 6 | $key = $this->json['keysByTypeName'][$typeName]; |
|
393 | |||
394 | 6 | if (!isset($this->bindingsByKey[$key])) { |
|
395 | 3 | $this->loadBindingsForKey($key); |
|
396 | } |
||
397 | |||
398 | 6 | View Code Duplication | foreach ($this->bindingsByKey[$key] as $i => $binding) { |
399 | 4 | if ($expr->evaluate($binding)) { |
|
400 | 4 | unset($this->bindingsByKey[$key][$i]); |
|
401 | } |
||
402 | } |
||
403 | |||
404 | 6 | $this->reindexBindingsForKey($key); |
|
405 | 6 | $this->syncBindingsForKey($key); |
|
406 | |||
407 | 6 | $this->flush(); |
|
408 | 6 | } |
|
409 | |||
410 | /** |
||
411 | * {@inheritdoc} |
||
412 | */ |
||
413 | 4 | protected function hasAnyBinding() |
|
414 | { |
||
415 | 4 | if (null === $this->json) { |
|
416 | 2 | $this->load(); |
|
417 | } |
||
418 | |||
419 | 4 | return count($this->json['bindingsByKey']) > 0; |
|
420 | } |
||
421 | |||
422 | /** |
||
423 | * {@inheritdoc} |
||
424 | */ |
||
425 | protected function hasBindingsThatMatch(Expression $expr) |
||
426 | { |
||
427 | if (null === $this->json) { |
||
428 | $this->load(); |
||
429 | } |
||
430 | |||
431 | $this->loadAllBindings(); |
||
432 | |||
433 | foreach ($this->bindingsByKey as $bindingsForKey) { |
||
434 | foreach ($bindingsForKey as $binding) { |
||
435 | if ($expr->evaluate($binding)) { |
||
436 | return true; |
||
437 | } |
||
438 | } |
||
439 | } |
||
440 | |||
441 | return false; |
||
442 | } |
||
443 | |||
444 | /** |
||
445 | * {@inheritdoc} |
||
446 | */ |
||
447 | 8 | protected function hasBindingsWithTypeName($typeName) |
|
448 | { |
||
449 | 8 | if (null === $this->json) { |
|
450 | 3 | $this->load(); |
|
451 | } |
||
452 | |||
453 | 8 | if (!isset($this->json['keysByTypeName'][$typeName])) { |
|
454 | 4 | return false; |
|
455 | } |
||
456 | |||
457 | 4 | $key = $this->json['keysByTypeName'][$typeName]; |
|
458 | |||
459 | 4 | return isset($this->json['bindingsByKey'][$key]); |
|
460 | } |
||
461 | |||
462 | 4 | protected function hasBindingsWithTypeNameThatMatch($typeName, Expression $expr) |
|
463 | { |
||
464 | 4 | if (null === $this->json) { |
|
465 | 3 | $this->load(); |
|
466 | } |
||
467 | |||
468 | 4 | if (!$this->hasBindingsWithTypeName($typeName)) { |
|
469 | 2 | return false; |
|
470 | } |
||
471 | |||
472 | 2 | $key = $this->json['keysByTypeName'][$typeName]; |
|
473 | |||
474 | 2 | if (!isset($this->bindingsByKey[$key])) { |
|
475 | 1 | $this->loadBindingsForKey($key); |
|
476 | } |
||
477 | |||
478 | 2 | foreach ($this->bindingsByKey[$key] as $binding) { |
|
479 | 2 | if ($expr->evaluate($binding)) { |
|
480 | 2 | return true; |
|
481 | } |
||
482 | } |
||
483 | |||
484 | 2 | return false; |
|
485 | } |
||
486 | |||
487 | 35 | private function loadAllBindings() |
|
488 | { |
||
489 | 35 | foreach ($this->json['keysByTypeName'] as $key) { |
|
490 | 25 | if (!isset($this->bindingsByKey[$key])) { |
|
491 | 7 | $this->bindingsByKey[$key] = isset($this->json['bindingsByKey'][$key]) |
|
492 | 3 | ? unserialize($this->json['bindingsByKey'][$key]) |
|
493 | 4 | : array(); |
|
494 | 25 | $this->initializeBindings($this->bindingsByKey[$key]); |
|
495 | } |
||
496 | } |
||
497 | 35 | } |
|
498 | |||
499 | 47 | private function loadBindingsForKey($key, $initialize = true) |
|
500 | { |
||
501 | 47 | $this->bindingsByKey[$key] = isset($this->json['bindingsByKey'][$key]) |
|
502 | 13 | ? unserialize($this->json['bindingsByKey'][$key]) |
|
503 | 47 | : array(); |
|
504 | |||
505 | 47 | if ($initialize) { |
|
506 | 45 | $this->initializeBindings($this->bindingsByKey[$key]); |
|
507 | } |
||
508 | 47 | } |
|
509 | |||
510 | 8 | private function reindexBindingsForKey($key) |
|
511 | { |
||
512 | 8 | $this->bindingsByKey[$key] = array_values($this->bindingsByKey[$key]); |
|
513 | |||
514 | 8 | $this->json['bindingsByKey'][$key] = serialize($this->bindingsByKey[$key]); |
|
515 | 8 | } |
|
516 | |||
517 | 8 | private function syncBindingsForKey($key) |
|
518 | { |
||
519 | 8 | if (count($this->bindingsByKey[$key]) > 0) { |
|
520 | 6 | $this->json['bindingsByKey'][$key] = serialize($this->bindingsByKey[$key]); |
|
521 | } else { |
||
522 | 2 | unset($this->bindingsByKey[$key], $this->json['bindingsByKey'][$key]); |
|
523 | } |
||
524 | 8 | } |
|
525 | |||
526 | /** |
||
527 | * Loads the JSON file. |
||
528 | */ |
||
529 | 85 | private function load() |
|
530 | { |
||
531 | 85 | $decoder = new JsonDecoder(); |
|
532 | 85 | $decoder->setObjectDecoding(JsonDecoder::ASSOC_ARRAY); |
|
533 | |||
534 | 85 | $this->json = file_exists($this->path) |
|
0 ignored issues
–
show
|
|||
535 | 34 | ? $decoder->decodeFile($this->path) |
|
536 | 85 | : array(); |
|
537 | |||
538 | 85 | if (!isset($this->json['keysByTypeName'])) { |
|
539 | 85 | $this->json['keysByTypeName'] = array(); |
|
540 | 85 | $this->json['typesByKey'] = array(); |
|
541 | 85 | $this->json['bindingsByKey'] = array(); |
|
542 | 85 | $this->json['nextKey'] = 0; |
|
543 | } |
||
544 | 85 | } |
|
545 | |||
546 | /** |
||
547 | * Writes the JSON file. |
||
548 | */ |
||
549 | 69 | private function flush() |
|
550 | { |
||
551 | 69 | $this->encoder->encodeFile($this->json, $this->path); |
|
552 | 69 | } |
|
553 | } |
||
554 |
Our type inference engine has found an assignment to a property that is incompatible with the declared type of that property.
Either this assignment is in error or the assigned type should be added to the documentation/type hint for that property..