* * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ /** * Abstract class that provides an interface for escaping of output. * * @package symfony * @subpackage view * @author Mike Squire * @version SVN: $Id: sfOutputEscaper.class.php 13463 2008-11-28 15:35:54Z fabien $ */ abstract class sfOutputEscaper { /** * The value that is to be escaped. * * @var mixed */ protected $value; /** * The escaping method that is going to be applied to the value and its * children. This is actually the name of a PHP callable. * * @var string */ protected $escapingMethod; static protected $safeClasses = array(); /** * Constructor stores the escaping method and value. * * Since sfOutputEscaper is an abstract class, instances cannot be created * directly but the constructor will be inherited by sub-classes. * * @param string $escapingMethod Escaping method * @param string $value Escaping value */ public function __construct($escapingMethod, $value) { $this->value = $value; $this->escapingMethod = $escapingMethod; } /** * Decorates a PHP variable with something that will escape any data obtained * from it. * * The following cases are dealt with: * * - The value is null or false: null or false is returned. * - The value is scalar: the result of applying the escaping method is * returned. * - The value is an array or an object that implements the ArrayAccess * interface: the array is decorated such that accesses to elements yield * an escaped value. * - The value implements the Traversable interface (either an Iterator, an * IteratorAggregate or an internal PHP class that implements * Traversable): decorated much like the array. * - The value is another type of object: decorated such that the result of * method calls is escaped. * * The escaping method is actually the name of a PHP callable. There are a set * of standard escaping methods listed in the escaping helper * (EscapingHelper.php). * * @param string $escapingMethod The escaping method (a PHP callable) to apply to the value * @param mixed $value The value to escape * * @return mixed Escaping value * * @throws InvalidArgumentException If the escaping fails */ public static function escape($escapingMethod, $value) { if (is_null($value)) { return $value; } // Scalars are anything other than arrays, objects and resources. if (is_scalar($value)) { return call_user_func($escapingMethod, $value); } if (is_array($value)) { return new sfOutputEscaperArrayDecorator($escapingMethod, $value); } if (is_object($value)) { if ($value instanceof sfOutputEscaper) { // avoid double decoration $copy = clone $value; $copy->escapingMethod = $escapingMethod; return $copy; } else if (self::isClassMarkedAsSafe(get_class($value))) { // the class or one of its children is marked as safe // return the unescaped object return $value; } else if ($value instanceof sfOutputEscaperSafe) { // do not escape objects marked as safe // return the original object return $value->getValue(); } else if ($value instanceof Traversable) { return new sfOutputEscaperIteratorDecorator($escapingMethod, $value); } else { return new sfOutputEscaperObjectDecorator($escapingMethod, $value); } } // it must be a resource; cannot escape that. throw new InvalidArgumentException(sprintf('Unable to escape value "%s".', var_export($value, true))); } /** * Unescapes a value that has been escaped previously with the escape() method. * * @param mixed $value The value to unescape * * @return mixed Unescaped value * * @throws InvalidArgumentException If the escaping fails */ static public function unescape($value) { if (is_null($value) || is_bool($value)) { return $value; } if (is_scalar($value)) { return html_entity_decode($value, ENT_QUOTES, sfConfig::get('sf_charset')); } elseif (is_array($value)) { foreach ($value as $name => $v) { $value[$name] = self::unescape($v); } return $value; } elseif (is_object($value)) { return $value instanceof sfOutputEscaper ? $value->getRawValue() : $value; } return $value; } /** * Returns true if the class if marked as safe. * * @param string $class A class name * * @return bool true if the class if safe, false otherwise */ static public function isClassMarkedAsSafe($class) { if (in_array($class, self::$safeClasses)) { return true; } foreach (self::$safeClasses as $safeClass) { if (is_subclass_of($class, $safeClass)) { return true; } } return false; } /** * Marks an array of classes (and all its children) as being safe for output. * * @param array $classes An array of class names */ static public function markClassesAsSafe(array $classes) { self::$safeClasses = array_unique(array_merge(self::$safeClasses, $classes)); } /** * Marks a class (and all its children) as being safe for output. * * @param string $class A class name */ static public function markClassAsSafe($class) { self::markClassesAsSafe(array($class)); } /** * Returns the raw value associated with this instance. * * Concrete instances of sfOutputEscaper classes decorate a value which is * stored by the constructor. This returns that original, unescaped, value. * * @return mixed The original value used to construct the decorator */ public function getRawValue() { return $this->value; } /** * Gets a value from the escaper. * * @param string $var Value to get * * @return mixed Value */ public function __get($var) { return $this->escape($this->escapingMethod, $this->value->$var); } }