Icybee /
Patron
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=[]) |
||
|
0 ignored issues
–
show
|
|||
| 129 | { |
||
| 130 | if ($alert instanceof \ICanBoogie\Exception\Config) |
||
| 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) |
||
| 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) |
||
| 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) |
||
| 188 | { |
||
| 189 | throw $e; |
||
| 190 | } |
||
| 191 | else if ($e instanceof \ICanBoogie\ActiveRecord\Exception) |
||
| 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) |
||
| 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': |
||
|
0 ignored issues
–
show
CASE statements must be defined using a colon
As per the PSR-2 coding standard, case statements should not be wrapped in curly braces.
There is no need for braces, since each case is terminated by the next switch ($expr) {
case "A": { //wrong
doSomething();
break;
}
case "B": //right
doSomething();
break;
}
To learn more about the PSR-2 coding standard, please refer to the PHP-Fig. Loading history...
|
|||
| 410 | { |
||
| 411 | foreach ($value as $k => $v) |
||
| 412 | { |
||
| 413 | $this->context[$k] = $v; |
||
| 414 | } |
||
| 415 | } |
||
| 416 | break; |
||
| 417 | |||
| 418 | case 'file': |
||
|
0 ignored issues
–
show
CASE statements must be defined using a colon
As per the PSR-2 coding standard, case statements should not be wrapped in curly braces.
There is no need for braces, since each case is terminated by the next switch ($expr) {
case "A": { //wrong
doSomething();
break;
}
case "B": //right
doSomething();
break;
}
To learn more about the PSR-2 coding standard, please refer to the PHP-Fig. Loading history...
|
|||
| 419 | { |
||
| 420 | $file = $value; |
||
| 421 | } |
||
| 422 | break; |
||
| 423 | |||
| 424 | default: |
||
|
0 ignored issues
–
show
DEFAULT statements must be defined using a colon
As per the PSR-2 coding standard, default statements should not be wrapped in curly braces. switch ($expr) {
default: { //wrong
doSomething();
break;
}
}
switch ($expr) {
default: //right
doSomething();
break;
}
To learn more about the PSR-2 coding standard, please refer to the PHP-Fig. Loading history...
|
|||
| 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) |
||
| 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 |
Instead of super-globals, we recommend to explicitly inject the dependencies of your class. This makes your code less dependent on global state and it becomes generally more testable: