| 1 |
<?php |
| 2 |
|
| 3 |
/* |
| 4 |
* This file is part of Twig. |
| 5 |
* |
| 6 |
* (c) Fabien Potencier |
| 7 |
* |
| 8 |
* For the full copyright and license information, please view the LICENSE |
| 9 |
* file that was distributed with this source code. |
| 10 |
*/ |
| 11 |
namespace ElementorDeps\Twig\NodeVisitor; |
| 12 |
|
| 13 |
use ElementorDeps\Twig\Environment; |
| 14 |
use ElementorDeps\Twig\Node\Expression\BlockReferenceExpression; |
| 15 |
use ElementorDeps\Twig\Node\Expression\ConditionalExpression; |
| 16 |
use ElementorDeps\Twig\Node\Expression\ConstantExpression; |
| 17 |
use ElementorDeps\Twig\Node\Expression\FilterExpression; |
| 18 |
use ElementorDeps\Twig\Node\Expression\FunctionExpression; |
| 19 |
use ElementorDeps\Twig\Node\Expression\GetAttrExpression; |
| 20 |
use ElementorDeps\Twig\Node\Expression\MethodCallExpression; |
| 21 |
use ElementorDeps\Twig\Node\Expression\NameExpression; |
| 22 |
use ElementorDeps\Twig\Node\Expression\ParentExpression; |
| 23 |
use ElementorDeps\Twig\Node\Node; |
| 24 |
/** |
| 25 |
* @internal |
| 26 |
*/ |
| 27 |
final class SafeAnalysisNodeVisitor implements NodeVisitorInterface |
| 28 |
{ |
| 29 |
private $data = []; |
| 30 |
private $safeVars = []; |
| 31 |
public function setSafeVars(array $safeVars) : void |
| 32 |
{ |
| 33 |
$this->safeVars = $safeVars; |
| 34 |
} |
| 35 |
public function getSafe(Node $node) |
| 36 |
{ |
| 37 |
$hash = \spl_object_hash($node); |
| 38 |
if (!isset($this->data[$hash])) { |
| 39 |
return; |
| 40 |
} |
| 41 |
foreach ($this->data[$hash] as $bucket) { |
| 42 |
if ($bucket['key'] !== $node) { |
| 43 |
continue; |
| 44 |
} |
| 45 |
if (\in_array('html_attr', $bucket['value'])) { |
| 46 |
$bucket['value'][] = 'html'; |
| 47 |
} |
| 48 |
return $bucket['value']; |
| 49 |
} |
| 50 |
} |
| 51 |
private function setSafe(Node $node, array $safe) : void |
| 52 |
{ |
| 53 |
$hash = \spl_object_hash($node); |
| 54 |
if (isset($this->data[$hash])) { |
| 55 |
foreach ($this->data[$hash] as &$bucket) { |
| 56 |
if ($bucket['key'] === $node) { |
| 57 |
$bucket['value'] = $safe; |
| 58 |
return; |
| 59 |
} |
| 60 |
} |
| 61 |
} |
| 62 |
$this->data[$hash][] = ['key' => $node, 'value' => $safe]; |
| 63 |
} |
| 64 |
public function enterNode(Node $node, Environment $env) : Node |
| 65 |
{ |
| 66 |
return $node; |
| 67 |
} |
| 68 |
public function leaveNode(Node $node, Environment $env) : ?Node |
| 69 |
{ |
| 70 |
if ($node instanceof ConstantExpression) { |
| 71 |
// constants are marked safe for all |
| 72 |
$this->setSafe($node, ['all']); |
| 73 |
} elseif ($node instanceof BlockReferenceExpression) { |
| 74 |
// blocks are safe by definition |
| 75 |
$this->setSafe($node, ['all']); |
| 76 |
} elseif ($node instanceof ParentExpression) { |
| 77 |
// parent block is safe by definition |
| 78 |
$this->setSafe($node, ['all']); |
| 79 |
} elseif ($node instanceof ConditionalExpression) { |
| 80 |
// intersect safeness of both operands |
| 81 |
$safe = $this->intersectSafe($this->getSafe($node->getNode('expr2')), $this->getSafe($node->getNode('expr3'))); |
| 82 |
$this->setSafe($node, $safe); |
| 83 |
} elseif ($node instanceof FilterExpression) { |
| 84 |
// filter expression is safe when the filter is safe |
| 85 |
$name = $node->getNode('filter')->getAttribute('value'); |
| 86 |
$args = $node->getNode('arguments'); |
| 87 |
if ($filter = $env->getFilter($name)) { |
| 88 |
$safe = $filter->getSafe($args); |
| 89 |
if (null === $safe) { |
| 90 |
$safe = $this->intersectSafe($this->getSafe($node->getNode('node')), $filter->getPreservesSafety()); |
| 91 |
} |
| 92 |
$this->setSafe($node, $safe); |
| 93 |
} else { |
| 94 |
$this->setSafe($node, []); |
| 95 |
} |
| 96 |
} elseif ($node instanceof FunctionExpression) { |
| 97 |
// function expression is safe when the function is safe |
| 98 |
$name = $node->getAttribute('name'); |
| 99 |
$args = $node->getNode('arguments'); |
| 100 |
if ($function = $env->getFunction($name)) { |
| 101 |
$this->setSafe($node, $function->getSafe($args)); |
| 102 |
} else { |
| 103 |
$this->setSafe($node, []); |
| 104 |
} |
| 105 |
} elseif ($node instanceof MethodCallExpression) { |
| 106 |
if ($node->getAttribute('safe')) { |
| 107 |
$this->setSafe($node, ['all']); |
| 108 |
} else { |
| 109 |
$this->setSafe($node, []); |
| 110 |
} |
| 111 |
} elseif ($node instanceof GetAttrExpression && $node->getNode('node') instanceof NameExpression) { |
| 112 |
$name = $node->getNode('node')->getAttribute('name'); |
| 113 |
if (\in_array($name, $this->safeVars)) { |
| 114 |
$this->setSafe($node, ['all']); |
| 115 |
} else { |
| 116 |
$this->setSafe($node, []); |
| 117 |
} |
| 118 |
} else { |
| 119 |
$this->setSafe($node, []); |
| 120 |
} |
| 121 |
return $node; |
| 122 |
} |
| 123 |
private function intersectSafe(?array $a = null, ?array $b = null) : array |
| 124 |
{ |
| 125 |
if (null === $a || null === $b) { |
| 126 |
return []; |
| 127 |
} |
| 128 |
if (\in_array('all', $a)) { |
| 129 |
return $b; |
| 130 |
} |
| 131 |
if (\in_array('all', $b)) { |
| 132 |
return $a; |
| 133 |
} |
| 134 |
return \array_intersect($a, $b); |
| 135 |
} |
| 136 |
public function getPriority() : int |
| 137 |
{ |
| 138 |
return 0; |
| 139 |
} |
| 140 |
} |
| 141 |
|