SafeAnalysis.php 4.7 KB

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