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 Patron package. |
||
5 | * |
||
6 | * (c) Olivier Laviale <[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 Patron; |
||
13 | |||
14 | use ICanBoogie\Accessor\AccessorTrait; |
||
15 | use ICanBoogie\Debug; |
||
16 | use ICanBoogie\Render; |
||
17 | use ICanBoogie\Render\TemplateName; |
||
18 | |||
19 | define('WDPATRON_DELIMIT_MACROS', false); |
||
20 | |||
21 | /** |
||
22 | * Patron engine. |
||
23 | * |
||
24 | * @property-read MarkupCollection $markups |
||
25 | * @property-read FunctionCollection $functions |
||
26 | */ |
||
27 | class Engine |
||
28 | { |
||
29 | use AccessorTrait; |
||
30 | |||
31 | const PREFIX = 'p:'; |
||
32 | |||
33 | protected $trace_templates = false; |
||
34 | |||
35 | /** |
||
36 | * @var MarkupCollection |
||
37 | */ |
||
38 | private $markups; |
||
39 | |||
40 | protected function get_markups() |
||
41 | { |
||
42 | return $this->markups; |
||
43 | } |
||
44 | |||
45 | /** |
||
46 | * @var FunctionCollection |
||
47 | */ |
||
48 | private $functions; |
||
49 | |||
50 | protected function get_functions() |
||
51 | { |
||
52 | return $this->functions; |
||
53 | } |
||
54 | |||
55 | /** |
||
56 | * Expression evaluator. |
||
57 | * |
||
58 | * @var Evaluator |
||
59 | */ |
||
60 | private $evaluator; |
||
61 | |||
62 | /** |
||
63 | * Initializes the {@link $evaluator} property, and a bunch of functions. |
||
64 | * |
||
65 | * @param MarkupCollection $markups |
||
66 | * @param FunctionCollection $functions |
||
67 | */ |
||
68 | public function __construct(MarkupCollection $markups, FunctionCollection $functions) |
||
69 | { |
||
70 | $this->markups = $markups; |
||
71 | $this->functions = $functions; |
||
72 | $this->evaluator = new Evaluator($this); |
||
73 | $this->template_resolver = Render\get_template_resolver(); |
||
74 | |||
75 | $this->init_context(); |
||
76 | } |
||
77 | |||
78 | public function __clone() |
||
79 | { |
||
80 | $this->init_context(); |
||
81 | } |
||
82 | |||
83 | /** |
||
84 | * Evaluate an expression relative to a context. |
||
85 | * |
||
86 | * @param mixed $context |
||
87 | * @param string $expression |
||
88 | * @param bool $silent |
||
89 | */ |
||
90 | public function evaluate($expression, $silent=false, $context=null) |
||
91 | { |
||
92 | $evaluator = $this->evaluator; |
||
93 | |||
94 | return $evaluator($context ?: $this->context, $expression, $silent); |
||
95 | } |
||
96 | |||
97 | /** |
||
98 | * @return Engine |
||
99 | * |
||
100 | * @deprecated |
||
101 | */ |
||
102 | static public function get_singleton() |
||
103 | { |
||
104 | return get_patron(); |
||
105 | } |
||
106 | |||
107 | /* |
||
108 | ** |
||
109 | |||
110 | SYSTEM |
||
111 | |||
112 | ** |
||
113 | */ |
||
114 | |||
115 | protected $trace = []; |
||
116 | protected $errors = []; |
||
117 | |||
118 | public function trace_enter($a) |
||
119 | { |
||
120 | array_unshift($this->trace, $a); |
||
121 | } |
||
122 | |||
123 | public function trace_exit() |
||
124 | { |
||
125 | array_shift($this->trace); |
||
126 | } |
||
127 | |||
128 | public function error($alert, array $args=[]) |
||
129 | { |
||
130 | if ($alert instanceof \ICanBoogie\Exception\Config) |
||
0 ignored issues
–
show
|
|||
131 | { |
||
132 | $this->errors[] = '<div class="alert alert-danger">' . $alert->getMessage() . '</div>'; |
||
133 | |||
134 | return; |
||
135 | } |
||
136 | else if ($alert instanceof \Exception) |
||
137 | { |
||
138 | $alert = class_exists('ICanBoogie\Debug') ? Debug::format_alert($alert) : (string) $alert; |
||
139 | } |
||
140 | else |
||
141 | { |
||
142 | $alert = \ICanBoogie\format($alert, $args); |
||
143 | } |
||
144 | |||
145 | # |
||
146 | # |
||
147 | # |
||
148 | |||
149 | $trace_html = null; |
||
150 | |||
151 | if ($this->trace) |
||
0 ignored issues
–
show
The expression
$this->trace of type array 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 ![]() |
|||
152 | { |
||
153 | $i = count($this->trace); |
||
154 | $root = $_SERVER['DOCUMENT_ROOT']; |
||
155 | $root_length = strlen($root); |
||
156 | |||
157 | foreach ($this->trace as $trace) |
||
158 | { |
||
159 | list($which, $message) = $trace; |
||
160 | |||
161 | if ($which == 'file') |
||
162 | { |
||
163 | if (strpos($message, $root_length) === 0) |
||
164 | { |
||
165 | $message = substr($message, $root_length); |
||
166 | } |
||
167 | } |
||
168 | |||
169 | $trace_html .= sprintf('#%02d: in %s "%s"', $i--, $which, $message) . '<br />'; |
||
170 | } |
||
171 | |||
172 | if ($trace_html) |
||
0 ignored issues
–
show
The expression
$trace_html of type null|string is loosely compared to true ; this is ambiguous if the string can be empty. You might want to explicitly use !== null instead.
In PHP, under loose comparison (like For '' == false // true
'' == null // true
'ab' == false // false
'ab' == null // false
// It is often better to use strict comparison
'' === false // false
'' === null // false
![]() |
|||
173 | { |
||
174 | $trace_html = '<pre>' . $trace_html . '</pre>'; |
||
175 | } |
||
176 | } |
||
177 | |||
178 | # |
||
179 | # |
||
180 | # |
||
181 | |||
182 | $this->errors[] = '<div class="alert alert-danger">' . $alert . $trace_html . '</div>'; |
||
183 | } |
||
184 | |||
185 | public function handle_exception(\Exception $e) |
||
186 | { |
||
187 | if ($e instanceof \ICanBoogie\HTTP\Exception) |
||
0 ignored issues
–
show
The class
ICanBoogie\HTTP\Exception does not exist. Did you forget a USE statement, or did you not list all dependencies?
This error could be the result of: 1. Missing dependenciesPHP Analyzer uses your Are you sure this class is defined by one of your dependencies, or did you maybe
not list a dependency in either the 2. Missing use statementPHP does not complain about undefined classes in if ($x instanceof DoesNotExist) {
// Do something.
}
If you have not tested against this specific condition, such errors might go unnoticed. ![]() |
|||
188 | { |
||
189 | throw $e; |
||
190 | } |
||
191 | else if ($e instanceof \ICanBoogie\ActiveRecord\Exception) |
||
0 ignored issues
–
show
The class
ICanBoogie\ActiveRecord\Exception does not exist. Did you forget a USE statement, or did you not list all dependencies?
This error could be the result of: 1. Missing dependenciesPHP Analyzer uses your Are you sure this class is defined by one of your dependencies, or did you maybe
not list a dependency in either the 2. Missing use statementPHP does not complain about undefined classes in if ($x instanceof DoesNotExist) {
// Do something.
}
If you have not tested against this specific condition, such errors might go unnoticed. ![]() |
|||
192 | { |
||
193 | throw $e; |
||
194 | } |
||
195 | |||
196 | $this->error($e); |
||
197 | } |
||
198 | |||
199 | public function fetchErrors() |
||
200 | { |
||
201 | $rc = implode(PHP_EOL, $this->errors); |
||
202 | |||
203 | $this->errors = []; |
||
204 | |||
205 | return $rc; |
||
206 | } |
||
207 | |||
208 | public function get_file() |
||
209 | { |
||
210 | foreach ($this->trace as $trace) |
||
211 | { |
||
212 | list($which, $data) = $trace; |
||
213 | |||
214 | if ($which == 'file') |
||
215 | { |
||
216 | return $data; |
||
217 | } |
||
218 | } |
||
219 | } |
||
220 | |||
221 | public function get_template_dir() |
||
222 | { |
||
223 | return dirname($this->get_file()); |
||
224 | } |
||
225 | |||
226 | /* |
||
227 | ** |
||
228 | |||
229 | TEMPLATES |
||
230 | |||
231 | ** |
||
232 | */ |
||
233 | |||
234 | protected $templates = []; |
||
235 | |||
236 | public function addTemplate($name, $template) |
||
237 | { |
||
238 | if (isset($this->templates[$name])) |
||
239 | { |
||
240 | $this->error('The template %name is already defined ! !template', [ |
||
241 | |||
242 | '%name' => $name, '!template' => $template |
||
243 | |||
244 | ]); |
||
245 | |||
246 | return; |
||
247 | } |
||
248 | |||
249 | $this->templates[$name] = $template; |
||
250 | } |
||
251 | |||
252 | protected function resolve_template($name) |
||
253 | { |
||
254 | $template_resolver = $this->template_resolver; |
||
255 | $file = $this->get_file(); |
||
256 | |||
257 | if ($file) |
||
258 | { |
||
259 | // FIXME-20150721: use a decorator |
||
260 | |||
261 | $template_resolver = clone $this->template_resolver; |
||
262 | |||
263 | $basic_template_resolver = null; |
||
264 | |||
265 | if ($template_resolver instanceof Render\BasicTemplateResolver) |
||
266 | { |
||
267 | $basic_template_resolver = $template_resolver; |
||
268 | } |
||
269 | else if ($template_resolver instanceof Render\TemplateResolverDecorator) |
||
270 | { |
||
271 | $basic_template_resolver = $template_resolver->find_renderer(Render\BasicTemplateResolver::class); |
||
272 | } |
||
273 | |||
274 | if ($basic_template_resolver) |
||
275 | { |
||
276 | $basic_template_resolver->add_path(dirname($file)); |
||
277 | } |
||
278 | } |
||
279 | |||
280 | $tries = []; |
||
281 | $template_pathname = $template_resolver->resolve($name, [ '.patron', '.html' ], $tries); |
||
282 | |||
283 | if ($template_pathname) |
||
284 | { |
||
285 | return $this->create_template_from_file($template_pathname); |
||
286 | } |
||
287 | |||
288 | $template_name = TemplateName::from($name); |
||
289 | $template_pathname = $template_resolver->resolve($template_name->as_partial, [ '.patron', '.html' ], $tries); |
||
290 | |||
291 | if ($template_pathname) |
||
292 | { |
||
293 | return $this->create_template_from_file($template_pathname); |
||
294 | } |
||
295 | |||
296 | throw new TemplateNotFound("Template not found: $name.", $tries); |
||
297 | } |
||
298 | |||
299 | protected function create_template_from_file($pathname) |
||
300 | { |
||
301 | $content = file_get_contents($pathname); |
||
302 | $nodes = $this->get_compiled($content); |
||
303 | |||
304 | return new Template($nodes, [ 'file' => $pathname ]); |
||
305 | } |
||
306 | |||
307 | protected function get_template($name) |
||
308 | { |
||
309 | if (isset($this->templates[$name])) |
||
310 | { |
||
311 | return $this->templates[$name]; |
||
312 | } |
||
313 | |||
314 | return $this->resolve_template($name); |
||
315 | } |
||
316 | |||
317 | /** |
||
318 | * Calls a template. |
||
319 | * |
||
320 | * @param $name |
||
321 | * @param array $args |
||
322 | * |
||
323 | * @return string |
||
324 | */ |
||
325 | public function callTemplate($name, array $args=[]) |
||
326 | { |
||
327 | $template = $this->get_template($name); |
||
328 | |||
329 | if (!$template) |
||
330 | { |
||
331 | $er = 'Unknown template %name'; |
||
332 | $params = [ '%name' => $name ]; |
||
333 | |||
334 | if ($this->templates) |
||
0 ignored issues
–
show
The expression
$this->templates of type array 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 ![]() |
|||
335 | { |
||
336 | $er .= ', available templates: :list'; |
||
337 | $params[':list'] = implode(', ', array_keys($this->templates)); |
||
338 | } |
||
339 | |||
340 | $this->error($er, $params); |
||
341 | |||
342 | return null; |
||
343 | } |
||
344 | |||
345 | $this->trace_enter([ 'template', $name, $template ]); |
||
346 | |||
347 | $this->context['self']['arguments'] = $args; |
||
348 | |||
349 | $rc = $this($template); |
||
350 | |||
351 | array_shift($this->trace); |
||
352 | |||
353 | return $rc; |
||
354 | } |
||
355 | |||
356 | /* |
||
357 | ** |
||
358 | |||
359 | CONTEXT |
||
360 | |||
361 | ** |
||
362 | */ |
||
363 | |||
364 | public $context; |
||
365 | |||
366 | protected function init_context() |
||
367 | { |
||
368 | $this->context = new \BlueTihi\Context([ 'self' => null, 'this' => null ]); |
||
369 | } |
||
370 | |||
371 | /* |
||
372 | ** |
||
373 | |||
374 | PUBLISH |
||
375 | |||
376 | ** |
||
377 | */ |
||
378 | |||
379 | protected function get_compiled($template) |
||
380 | { |
||
381 | static $compiler; |
||
382 | |||
383 | if ($compiler === null) |
||
384 | { |
||
385 | $compiler = new Compiler(); |
||
386 | } |
||
387 | |||
388 | return $compiler($template); |
||
389 | } |
||
390 | |||
391 | public function __invoke($template, $bind=null, array $options=[]) |
||
392 | { |
||
393 | if (!$template) |
||
394 | { |
||
395 | return null; |
||
396 | } |
||
397 | |||
398 | if ($bind !== null) |
||
399 | { |
||
400 | $this->context['this'] = $bind; |
||
401 | } |
||
402 | |||
403 | $file = null; |
||
404 | |||
405 | foreach ($options as $option => $value) |
||
406 | { |
||
407 | switch ((string) $option) |
||
408 | { |
||
409 | case 'variables': |
||
410 | { |
||
411 | foreach ($value as $k => $v) |
||
412 | { |
||
413 | $this->context[$k] = $v; |
||
414 | } |
||
415 | } |
||
416 | break; |
||
417 | |||
418 | case 'file': |
||
419 | { |
||
420 | $file = $value; |
||
421 | } |
||
422 | break; |
||
423 | |||
424 | default: |
||
425 | { |
||
426 | trigger_error(\ICanBoogie\format('Suspicious option: %option :value', [ |
||
427 | |||
428 | '%option' => $option, |
||
429 | ':value' => $value |
||
430 | |||
431 | ])); |
||
432 | } |
||
433 | break; |
||
434 | } |
||
435 | } |
||
436 | |||
437 | if (!($template instanceof Template)) |
||
438 | { |
||
439 | if (is_array($template) && isset($template['file'])) |
||
440 | { |
||
441 | $file = $template['file']; |
||
442 | |||
443 | unset($template['file']); |
||
444 | } |
||
445 | |||
446 | if (!is_array($template)) |
||
447 | { |
||
448 | $template = $this->get_compiled($template); |
||
449 | } |
||
450 | |||
451 | $template = new Template($template, [ 'file' => $file ]); |
||
452 | } |
||
453 | |||
454 | if ($template->file) |
||
0 ignored issues
–
show
The expression
$template->file of type string|null is loosely compared to true ; this is ambiguous if the string can be empty. You might want to explicitly use !== null instead.
In PHP, under loose comparison (like For '' == false // true
'' == null // true
'ab' == false // false
'ab' == null // false
// It is often better to use strict comparison
'' === false // false
'' === null // false
![]() |
|||
455 | { |
||
456 | $this->trace_enter([ 'file', $template->file ]); |
||
457 | } |
||
458 | |||
459 | $rc = ''; |
||
460 | |||
461 | foreach ($template as $node) |
||
462 | { |
||
463 | if (!($node instanceof Node)) |
||
464 | { |
||
465 | continue; |
||
466 | } |
||
467 | |||
468 | try |
||
469 | { |
||
470 | $rc .= $node($this, $this->context); |
||
471 | } |
||
472 | catch (\Exception $e) |
||
473 | { |
||
474 | if (class_exists('ICanBoogie\Debug')) |
||
475 | { |
||
476 | $rc .= Debug::format_alert($e); |
||
477 | } |
||
478 | else |
||
479 | { |
||
480 | $rc .= $e; |
||
481 | } |
||
482 | } |
||
483 | |||
484 | $rc .= $this->fetchErrors(); |
||
485 | } |
||
486 | |||
487 | $rc .= $this->fetchErrors(); |
||
488 | |||
489 | # |
||
490 | # |
||
491 | # |
||
492 | |||
493 | if ($file) |
||
494 | { |
||
495 | array_shift($this->trace); |
||
496 | } |
||
497 | |||
498 | return $rc; |
||
499 | } |
||
500 | |||
501 | /* |
||
502 | |||
503 | # |
||
504 | # $context_markup is used to keep track of two variables associated with each markup : |
||
505 | # self and this. |
||
506 | # |
||
507 | # 'self' is a reference to the markup itself, holding its name and the arguments with which |
||
508 | # it was called, it is also used to store special markup data as for the foreach markup |
||
509 | # |
||
510 | # 'this' is a reference to the object of the markup, that being an array, an object or a value |
||
511 | # |
||
512 | # |
||
513 | |||
514 | <p:articles> |
||
515 | |||
516 | self.range.start |
||
517 | self.range.limit |
||
518 | self.range.count |
||
519 | |||
520 | this = array of Articles |
||
521 | |||
522 | <p:foreach> |
||
523 | |||
524 | self.name = foreach |
||
525 | self.arguments = [] |
||
526 | self.position |
||
527 | self.key |
||
528 | self.left |
||
529 | |||
530 | this = an Article object |
||
531 | |||
532 | </p:foreach> |
||
533 | </p:articles> |
||
534 | |||
535 | */ |
||
536 | |||
537 | public $context_markup = []; // should be protected |
||
538 | } |
||
539 |
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.