sfOutputEscaper.class.php 6.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244
  1. <?php
  2. /*
  3. * This file is part of the symfony package.
  4. * (c) 2004-2006 Fabien Potencier <fabien.potencier@symfony-project.com>
  5. *
  6. * For the full copyright and license information, please view the LICENSE
  7. * file that was distributed with this source code.
  8. */
  9. /**
  10. * Abstract class that provides an interface for escaping of output.
  11. *
  12. * @package symfony
  13. * @subpackage view
  14. * @author Mike Squire <mike@somosis.co.uk>
  15. * @version SVN: $Id: sfOutputEscaper.class.php 13463 2008-11-28 15:35:54Z fabien $
  16. */
  17. abstract class sfOutputEscaper
  18. {
  19. /**
  20. * The value that is to be escaped.
  21. *
  22. * @var mixed
  23. */
  24. protected $value;
  25. /**
  26. * The escaping method that is going to be applied to the value and its
  27. * children. This is actually the name of a PHP callable.
  28. *
  29. * @var string
  30. */
  31. protected $escapingMethod;
  32. static protected $safeClasses = array();
  33. /**
  34. * Constructor stores the escaping method and value.
  35. *
  36. * Since sfOutputEscaper is an abstract class, instances cannot be created
  37. * directly but the constructor will be inherited by sub-classes.
  38. *
  39. * @param string $escapingMethod Escaping method
  40. * @param string $value Escaping value
  41. */
  42. public function __construct($escapingMethod, $value)
  43. {
  44. $this->value = $value;
  45. $this->escapingMethod = $escapingMethod;
  46. }
  47. /**
  48. * Decorates a PHP variable with something that will escape any data obtained
  49. * from it.
  50. *
  51. * The following cases are dealt with:
  52. *
  53. * - The value is null or false: null or false is returned.
  54. * - The value is scalar: the result of applying the escaping method is
  55. * returned.
  56. * - The value is an array or an object that implements the ArrayAccess
  57. * interface: the array is decorated such that accesses to elements yield
  58. * an escaped value.
  59. * - The value implements the Traversable interface (either an Iterator, an
  60. * IteratorAggregate or an internal PHP class that implements
  61. * Traversable): decorated much like the array.
  62. * - The value is another type of object: decorated such that the result of
  63. * method calls is escaped.
  64. *
  65. * The escaping method is actually the name of a PHP callable. There are a set
  66. * of standard escaping methods listed in the escaping helper
  67. * (EscapingHelper.php).
  68. *
  69. * @param string $escapingMethod The escaping method (a PHP callable) to apply to the value
  70. * @param mixed $value The value to escape
  71. *
  72. * @return mixed Escaping value
  73. *
  74. * @throws InvalidArgumentException If the escaping fails
  75. */
  76. public static function escape($escapingMethod, $value)
  77. {
  78. if (is_null($value))
  79. {
  80. return $value;
  81. }
  82. // Scalars are anything other than arrays, objects and resources.
  83. if (is_scalar($value))
  84. {
  85. return call_user_func($escapingMethod, $value);
  86. }
  87. if (is_array($value))
  88. {
  89. return new sfOutputEscaperArrayDecorator($escapingMethod, $value);
  90. }
  91. if (is_object($value))
  92. {
  93. if ($value instanceof sfOutputEscaper)
  94. {
  95. // avoid double decoration
  96. $copy = clone $value;
  97. $copy->escapingMethod = $escapingMethod;
  98. return $copy;
  99. }
  100. else if (self::isClassMarkedAsSafe(get_class($value)))
  101. {
  102. // the class or one of its children is marked as safe
  103. // return the unescaped object
  104. return $value;
  105. }
  106. else if ($value instanceof sfOutputEscaperSafe)
  107. {
  108. // do not escape objects marked as safe
  109. // return the original object
  110. return $value->getValue();
  111. }
  112. else if ($value instanceof Traversable)
  113. {
  114. return new sfOutputEscaperIteratorDecorator($escapingMethod, $value);
  115. }
  116. else
  117. {
  118. return new sfOutputEscaperObjectDecorator($escapingMethod, $value);
  119. }
  120. }
  121. // it must be a resource; cannot escape that.
  122. throw new InvalidArgumentException(sprintf('Unable to escape value "%s".', var_export($value, true)));
  123. }
  124. /**
  125. * Unescapes a value that has been escaped previously with the escape() method.
  126. *
  127. * @param mixed $value The value to unescape
  128. *
  129. * @return mixed Unescaped value
  130. *
  131. * @throws InvalidArgumentException If the escaping fails
  132. */
  133. static public function unescape($value)
  134. {
  135. if (is_null($value) || is_bool($value))
  136. {
  137. return $value;
  138. }
  139. if (is_scalar($value))
  140. {
  141. return html_entity_decode($value, ENT_QUOTES, sfConfig::get('sf_charset'));
  142. }
  143. elseif (is_array($value))
  144. {
  145. foreach ($value as $name => $v)
  146. {
  147. $value[$name] = self::unescape($v);
  148. }
  149. return $value;
  150. }
  151. elseif (is_object($value))
  152. {
  153. return $value instanceof sfOutputEscaper ? $value->getRawValue() : $value;
  154. }
  155. return $value;
  156. }
  157. /**
  158. * Returns true if the class if marked as safe.
  159. *
  160. * @param string $class A class name
  161. *
  162. * @return bool true if the class if safe, false otherwise
  163. */
  164. static public function isClassMarkedAsSafe($class)
  165. {
  166. if (in_array($class, self::$safeClasses))
  167. {
  168. return true;
  169. }
  170. foreach (self::$safeClasses as $safeClass)
  171. {
  172. if (is_subclass_of($class, $safeClass))
  173. {
  174. return true;
  175. }
  176. }
  177. return false;
  178. }
  179. /**
  180. * Marks an array of classes (and all its children) as being safe for output.
  181. *
  182. * @param array $classes An array of class names
  183. */
  184. static public function markClassesAsSafe(array $classes)
  185. {
  186. self::$safeClasses = array_unique(array_merge(self::$safeClasses, $classes));
  187. }
  188. /**
  189. * Marks a class (and all its children) as being safe for output.
  190. *
  191. * @param string $class A class name
  192. */
  193. static public function markClassAsSafe($class)
  194. {
  195. self::markClassesAsSafe(array($class));
  196. }
  197. /**
  198. * Returns the raw value associated with this instance.
  199. *
  200. * Concrete instances of sfOutputEscaper classes decorate a value which is
  201. * stored by the constructor. This returns that original, unescaped, value.
  202. *
  203. * @return mixed The original value used to construct the decorator
  204. */
  205. public function getRawValue()
  206. {
  207. return $this->value;
  208. }
  209. /**
  210. * Gets a value from the escaper.
  211. *
  212. * @param string $var Value to get
  213. *
  214. * @return mixed Value
  215. */
  216. public function __get($var)
  217. {
  218. return $this->escape($this->escapingMethod, $this->value->$var);
  219. }
  220. }