SafeAnalysis.php 3.8KB

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