SafeAnalysis.php 4.4KB

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