Ver código fonte

[OutputEscaper] refactored the component

Fabien Potencier 14 anos atrás
pai
commit
c065be88b5

+ 10 - 11
src/Symfony/Component/OutputEscaper/ArrayDecorator.php

@@ -18,7 +18,7 @@ namespace Symfony\Component\OutputEscaper;
  * @author Fabien Potencier <fabien.potencier@symfony-project.com>
  * @author Mike Squire <mike@somosis.co.uk>
  */
-class ArrayDecorator extends GetterDecorator implements \Iterator, \ArrayAccess, \Countable
+class ArrayDecorator extends BaseEscaper implements \Iterator, \ArrayAccess, \Countable
 {
     /**
      * Used by the iterator to know if the current element is valid.
@@ -142,24 +142,23 @@ class ArrayDecorator extends GetterDecorator implements \Iterator, \ArrayAccess,
     }
 
     /**
-     * Returns the size of the array (are required by the Countable interface).
+     * Escapes a key from the array using the specified escaper.
      *
-     * @return int The size of the array
+     * @param string $key     The array key
+     * @param mixed  $escaper The escaping method (a PHP callable or a named escaper)
      */
-    public function count()
+    public function getEscapedKey($key, $escaper)
     {
-        return count($this->value);
+        return Escaper::escape($escaper, $this->value[$key]);
     }
 
     /**
-     * Returns the (unescaped) value from the array associated with the key supplied.
-     *
-     * @param  string $key  The key into the array to use
+     * Returns the size of the array (are required by the Countable interface).
      *
-     * @return mixed The value
+     * @return int The size of the array
      */
-    public function getRaw($key)
+    public function count()
     {
-        return $this->value[$key];
+        return count($this->value);
     }
 }

+ 69 - 0
src/Symfony/Component/OutputEscaper/BaseEscaper.php

@@ -0,0 +1,69 @@
+<?php
+
+namespace Symfony\Component\OutputEscaper;
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien.potencier@symfony-project.com>
+ *
+ * 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 output escaping.
+ *
+ * @author Fabien Potencier <fabien.potencier@symfony-project.com>
+ * @author Mike Squire <mike@somosis.co.uk>
+ */
+abstract class BaseEscaper
+{
+    /**
+     * The value that is to be escaped.
+     */
+    protected $value;
+
+    /**
+     * The escaper (a PHP callable or a named escaper) that is going to be applied to the value and its children.
+     */
+    protected $escaper;
+
+    /**
+     * Constructor.
+     *
+     * Since BaseEscaper is an abstract class, instances cannot be created
+     * directly but the constructor will be inherited by sub-classes.
+     *
+     * @param mixed  $escaper The escaping method (a PHP callable or a named escaper)
+     * @param string $value   Escaping value
+     */
+    public function __construct($escaper, $value)
+    {
+        $this->escaper = $escaper;
+        $this->value = $value;
+    }
+
+    /**
+     * Sets the default escaper to use.
+     *
+     * @param mixed  $escaper The escaping method (a PHP callable or a named escaper)
+     */
+    public function setEscaper($escaper)
+    {
+        $this->escaper = $escaper;
+    }
+
+    /**
+     * Returns the raw value associated with this instance.
+     *
+     * Concrete instances of BaseEscaper 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;
+    }
+}

+ 23 - 55
src/Symfony/Component/OutputEscaper/Escaper.php

@@ -12,51 +12,17 @@ namespace Symfony\Component\OutputEscaper;
  */
 
 /**
- * Abstract class that provides an interface for escaping of output.
+ * Escaper provides output escaping features.
  *
  * @author Fabien Potencier <fabien.potencier@symfony-project.com>
  * @author Mike Squire <mike@somosis.co.uk>
  */
-abstract class Escaper
+class Escaper
 {
-    /**
-     * The value that is to be escaped.
-     *
-     * @var mixed
-     */
-    protected $value;
-
-    /**
-     * The escaper (a PHP callable) that is going to be applied to the value and its
-     * children.
-     *
-     * @var string
-     */
-    protected $escaper;
-
     static protected $charset = 'UTF-8';
     static protected $safeClasses = array();
     static protected $escapers;
 
-    /**
-     * Constructor.
-     *
-     * Since Escaper is an abstract class, instances cannot be created
-     * directly but the constructor will be inherited by sub-classes.
-     *
-     * @param string $callable A PHP callable
-     * @param string $value    Escaping value
-     */
-    public function __construct($escaper, $value)
-    {
-        if (null === self::$escapers) {
-            self::initializeEscapers();
-        }
-
-        $this->escaper = is_string($escaper) && isset(self::$escapers[$escaper]) ? self::$escapers[$escaper] : $escaper;
-        $this->value = $value;
-    }
-
     /**
      * Decorates a PHP variable with something that will escape any data obtained
      * from it.
@@ -78,7 +44,7 @@ abstract class Escaper
      * The escaping method is actually a PHP callable. This class hosts a set
      * of standard escaping strategies.
      *
-     * @param  string $escaper The escaping method (a PHP callable) to apply to the value
+     * @param  mixed  $escaper The escaping method (a PHP callable or a named escaper) to apply to the value
      * @param  mixed  $value   The value to escape
      *
      * @return mixed Escaped value
@@ -109,11 +75,10 @@ abstract class Escaper
         }
 
         if (is_object($value)) {
-            if ($value instanceof Escaper) {
+            if ($value instanceof BaseEscaper) {
                 // avoid double decoration
                 $copy = clone $value;
-
-                $copy->escaper = $escaper;
+                $copy->setEscaper($escaper);
 
                 return $copy;
             }
@@ -121,7 +86,7 @@ abstract class Escaper
             if ($value instanceof SafeDecorator) {
                 // do not escape objects marked as safe
                 // return the original object
-                return $value->getValue();
+                return $value->getRawValue();
             }
 
             if (self::isClassMarkedAsSafe(get_class($value)) || $value instanceof SafeDecoratorInterface) {
@@ -170,7 +135,7 @@ abstract class Escaper
         }
 
         if (is_object($value)) {
-            return $value instanceof Escaper ? $value->getRawValue() : $value;
+            return $value instanceof BaseEscaper ? $value->getRawValue() : $value;
         }
 
         return $value;
@@ -218,19 +183,6 @@ abstract class Escaper
         self::markClassesAsSafe(array($class));
     }
 
-    /**
-     * Returns the raw value associated with this instance.
-     *
-     * Concrete instances of Escaper 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;
-    }
-
     /**
      * Sets the current charset.
      *
@@ -262,6 +214,22 @@ abstract class Escaper
         self::$escapers[$name] = $escaper;
     }
 
+    /**
+     * Gets a named escaper.
+     *
+     * @param  string $name    The escaper name
+     *
+     * @return mixed  $escaper A PHP callable
+     */
+    static public function getEscaper($escaper)
+    {
+        if (null === self::$escapers) {
+            self::initializeEscapers();
+        }
+
+        return is_string($escaper) && isset(self::$escapers[$escaper]) ? self::$escapers[$escaper] : $escaper;
+    }
+
     /**
      * Initializes the built-in escapers.
      *

+ 0 - 54
src/Symfony/Component/OutputEscaper/GetterDecorator.php

@@ -1,54 +0,0 @@
-<?php
-
-namespace Symfony\Component\OutputEscaper;
-
-/*
- * This file is part of the Symfony package.
- *
- * (c) Fabien Potencier <fabien.potencier@symfony-project.com>
- *
- * For the full copyright and license information, please view the LICENSE
- * file that was distributed with this source code.
- */
-
-/**
- * Abstract output escaping decorator class for "getter" objects.
- *
- * @see    Escaper
- * @author Fabien Potencier <fabien.potencier@symfony-project.com>
- * @author Mike Squire <mike@somosis.co.uk>
- */
-abstract class GetterDecorator extends Escaper
-{
-    /**
-     * Returns the raw, unescaped value associated with the key supplied.
-     *
-     * The key might be an index into an array or a value to be passed to the
-     * decorated object's get() method.
-     *
-     * @param  string $key  The key to retrieve
-     *
-     * @return mixed The value
-     */
-    public abstract function getRaw($key);
-
-    /**
-     * Returns the escaped value associated with the key supplied.
-     *
-     * Typically (using this implementation) the raw value is obtained using the
-     * {@link getRaw()} method, escaped and the result returned.
-     *
-     * @param  string $key     The key to retrieve
-     * @param  string $escaper The escaping method (a PHP function) to use
-     *
-     * @return mixed The escaped value
-     */
-    public function get($key, $escaper = null)
-    {
-        if (!$escaper) {
-            $escaper = $this->escaper;
-        }
-
-        return Escaper::escape($escaper, $this->getRaw($key));
-    }
-}

+ 1 - 68
src/Symfony/Component/OutputEscaper/IteratorDecorator.php

@@ -21,7 +21,7 @@ namespace Symfony\Component\OutputEscaper;
  * @author Fabien Potencier <fabien.potencier@symfony-project.com>
  * @author Mike Squire <mike@somosis.co.uk>
  */
-class IteratorDecorator extends ObjectDecorator implements \Iterator, \Countable, \ArrayAccess
+class IteratorDecorator extends ObjectDecorator implements \Iterator
 {
     /**
      * The iterator to be used.
@@ -94,71 +94,4 @@ class IteratorDecorator extends ObjectDecorator implements \Iterator, \Countable
     {
         return $this->iterator->valid();
     }
-
-    /**
-     * Returns true if the supplied offset isset in the array (as required by the ArrayAccess interface).
-     *
-     * @param  string $offset  The offset of the value to check existence of
-     *
-     * @return bool true if the offset isset; false otherwise
-     */
-    public function offsetExists($offset)
-    {
-        return isset($this->value[$offset]);
-    }
-
-    /**
-     * Returns the element associated with the offset supplied (as required by the ArrayAccess interface).
-     *
-     * @param  string $offset  The offset of the value to get
-     *
-     * @return mixed The escaped value
-     */
-    public function offsetGet($offset)
-    {
-        return Escaper::escape($this->escaper, $this->value[$offset]);
-    }
-
-    /**
-     * Throws an exception saying that values cannot be set (this method is
-     * required for the ArrayAccess interface).
-     *
-     * This (and the other Escaper classes) are designed to be read only
-     * so this is an illegal operation.
-     *
-     * @param  string $offset  (ignored)
-     * @param  string $value   (ignored)
-     *
-     * @throws \LogicException When trying to set values
-     */
-    public function offsetSet($offset, $value)
-    {
-        throw new \LogicException('Cannot set values.');
-    }
-
-    /**
-     * Throws an exception saying that values cannot be unset (this method is
-     * required for the ArrayAccess interface).
-     *
-     * This (and the other Escaper classes) are designed to be read only
-     * so this is an illegal operation.
-     *
-     * @param  string $offset  (ignored)
-     *
-     * @throws \LogicException When trying to unset values
-     */
-    public function offsetUnset($offset)
-    {
-        throw new \LogicException('Cannot unset values.');
-    }
-
-    /**
-     * Returns the size of the array (are required by the Countable interface).
-     *
-     * @return int The size of the array
-     */
-    public function count()
-    {
-        return count($this->value);
-    }
 }

+ 96 - 24
src/Symfony/Component/OutputEscaper/ObjectDecorator.php

@@ -19,7 +19,7 @@ namespace Symfony\Component\OutputEscaper;
  * @author Fabien Potencier <fabien.potencier@symfony-project.com>
  * @author Mike Squire <mike@somosis.co.uk>
  */
-class ObjectDecorator extends GetterDecorator
+class ObjectDecorator extends BaseEscaper implements \ArrayAccess, \Countable
 {
     /**
      * Magic PHP method that intercepts method calls, calls them on the objects
@@ -62,27 +62,6 @@ class ObjectDecorator extends GetterDecorator
         return Escaper::escape($escaper, $value);
     }
 
-    /**
-     * Returns the result of calling the get() method on the object, bypassing
-     * any escaping, if that method exists.
-     *
-     * If there is not a callable get() method this will throw an exception.
-     *
-     * @param  string $key  The parameter to be passed to the get() get method
-     *
-     * @return mixed The unescaped value returned
-     *
-     * @throws \LogicException if the object does not have a callable get() method
-     */
-    public function getRaw($key)
-    {
-        if (!is_callable(array($this->value, 'get'))) {
-            throw new \LogicException('Object does not have a callable get() method.');
-        }
-
-        return $this->value->get($key);
-    }
-
     /**
      * Try to call decorated object __toString() method if exists.
      *
@@ -90,7 +69,7 @@ class ObjectDecorator extends GetterDecorator
      */
     public function __toString()
     {
-        return $this->escape($this->escaper, (string) $this->value);
+        return Escaper::escape($this->escaper, (string) $this->value);
     }
 
     /**
@@ -102,7 +81,7 @@ class ObjectDecorator extends GetterDecorator
      */
     public function __get($key)
     {
-        return $this->escape($this->escaper, $this->value->$key);
+        return Escaper::escape($this->escaper, $this->value->$key);
     }
 
     /**
@@ -116,4 +95,97 @@ class ObjectDecorator extends GetterDecorator
     {
         return isset($this->value->$key);
     }
+
+    /**
+     * Escapes an object property using the specified escaper.
+     *
+     * @param string $key     The object property name
+     * @param mixed  $escaper The escaping method (a PHP callable or a named escaper)
+     */
+    public function getEscapedProperty($key, $escaper)
+    {
+        return Escaper::escape($escaper, $this->value->$key);
+    }
+
+    /**
+     * Returns true if the supplied offset isset in the array (as required by the ArrayAccess interface).
+     *
+     * @param  string $offset  The offset of the value to check existence of
+     *
+     * @return bool true if the offset isset; false otherwise
+     */
+    public function offsetExists($offset)
+    {
+        return isset($this->value[$offset]);
+    }
+
+    /**
+     * Returns the element associated with the offset supplied (as required by the ArrayAccess interface).
+     *
+     * @param  string $offset  The offset of the value to get
+     *
+     * @return mixed The escaped value
+     */
+    public function offsetGet($offset)
+    {
+        return Escaper::escape($this->escaper, $this->value[$offset]);
+    }
+
+    /**
+     * Throws an exception saying that values cannot be set (this method is
+     * required for the ArrayAccess interface).
+     *
+     * This (and the other Escaper classes) are designed to be read only
+     * so this is an illegal operation.
+     *
+     * @param  string $offset  (ignored)
+     * @param  string $value   (ignored)
+     *
+     * @throws \LogicException When trying to set values
+     */
+    public function offsetSet($offset, $value)
+    {
+        throw new \LogicException('Cannot set values.');
+    }
+
+    /**
+     * Throws an exception saying that values cannot be unset (this method is
+     * required for the ArrayAccess interface).
+     *
+     * This (and the other Escaper classes) are designed to be read only
+     * so this is an illegal operation.
+     *
+     * @param  string $offset  (ignored)
+     *
+     * @throws \LogicException When trying to unset values
+     */
+    public function offsetUnset($offset)
+    {
+        throw new \LogicException('Cannot unset values.');
+    }
+
+    /**
+     * Escapes a key from the array using the specified escaper.
+     *
+     * @param string $key     The array key
+     * @param mixed  $escaper The escaping method (a PHP callable or a named escaper)
+     */
+    public function getEscapedKey($key, $escaper)
+    {
+        return Escaper::escape($escaper, $this->value[$key]);
+    }
+
+    /**
+     * Returns the size of the array (are required by the Countable interface).
+     *
+     * @return int The size of the array
+     */
+    public function count()
+    {
+        if ($this->value instanceof \Countable) {
+            return count($this->value);
+        }
+
+        return call_user_func_array(array($this->value, 'count'), func_get_args());
+    }
 }

+ 4 - 38
src/Symfony/Component/OutputEscaper/SafeDecorator.php

@@ -16,7 +16,7 @@ namespace Symfony\Component\OutputEscaper;
  *
  * @author Fabien Potencier <fabien.potencier@symfony-project.com>
  */
-class SafeDecorator extends \ArrayIterator implements SafeDecoratorInterface
+class SafeDecorator implements SafeDecoratorInterface
 {
     protected $value;
 
@@ -28,48 +28,14 @@ class SafeDecorator extends \ArrayIterator implements SafeDecoratorInterface
     public function __construct($value)
     {
         $this->value = $value;
-
-        if (is_array($value) || is_object($value)) {
-            parent::__construct($value);
-        }
-    }
-
-    public function __toString()
-    {
-        return (string) $this->value;
-    }
-
-    public function __get($key)
-    {
-        return $this->value->$key;
-    }
-
-    public function __set($key, $value)
-    {
-        $this->value->$key = $value;
-    }
-
-    public function __call($method, $arguments)
-    {
-        return call_user_func_array(array($this->value, $method), $arguments);
-    }
-
-    public function __isset($key)
-    {
-        return isset($this->value->$key);
-    }
-
-    public function __unset($key)
-    {
-        unset($this->value->$key);
     }
 
     /**
-     * Returns the embedded value.
+     * Returns the raw value.
      *
-     * @return mixed The embedded value
+     * @return mixed The raw value
      */
-    public function getValue()
+    public function getRawValue()
     {
         return $this->value;
     }

+ 2 - 2
tests/Symfony/Tests/Component/OutputEscaper/ArrayDecoratorTest.php

@@ -24,9 +24,9 @@ class ArrayDecoratorTest extends \PHPUnit_Framework_TestCase
         self::$escaped = Escaper::escape('entities', $a);
     }
 
-    public function testGetRaw()
+    public function testGetEscapedKey()
     {
-        $this->assertEquals('<strong>escaped!</strong>', self::$escaped->getRaw(0), '->getRaw() returns the raw value');
+        $this->assertEquals('<strong>escaped!</strong>', self::$escaped->getEscapedKey(0, 'raw'), '->getEscapedKey() returns the value with an other escaper');
     }
 
     public function testArrayAccessInterface()

+ 1 - 1
tests/Symfony/Tests/Component/OutputEscaper/EscaperTest.php

@@ -120,7 +120,7 @@ class EscaperTest extends \PHPUnit_Framework_TestCase
         $this->assertEquals('<strong>escaped!</strong>', $output->title, '::unescape() unescapes all properties of the original object');
         $this->assertEquals('<strong>escaped!</strong>', $output->getTitleTitle(), '::unescape() is recursive');
 
-        $this->assertInstanceOf('\DirectoryIterator', IteratorDecorator::unescape(Escaper::escape('entities', new \DirectoryIterator('.'))), '::unescape() unescapes IteratorDecorator objects');
+        $this->assertInstanceOf('\DirectoryIterator', Escaper::unescape(Escaper::escape('entities', new \DirectoryIterator('.'))), '::unescape() unescapes IteratorDecorator objects');
     }
 
     public function testUnescapeDoesNotUnescapeObjectMarkedAsBeingSafe()

+ 0 - 15
tests/Symfony/Tests/Component/OutputEscaper/ObjectDecoratorTest.php

@@ -50,21 +50,6 @@ class ObjectDecoratorTest extends \PHPUnit_Framework_TestCase
         $this->assertTrue(isset(self::$escaped->someMember), 'The escaped object behaves like the real object');
         $this->assertFalse(isset(self::$escaped->invalidMember), 'The escaped object behaves like the real object');
     }
-
-    public function testGetRaw()
-    {
-        $this->assertEquals('<em>escape me</em>', self::$escaped->getRaw('someMember'), '->getRaw() returns result with any escaping');
-    }
-
-    /**
-     * @expectedException LogicException
-     */
-    public function testGetRawException()
-    {
-        $object = new \stdClass();
-        $escaped = Escaper::escape('entities', $object);
-        $escaped->getRaw('something');
-    }
 }
 
 class OutputEscaperTest

+ 2 - 89
tests/Symfony/Tests/Component/OutputEscaper/SafeDecoratorTest.php

@@ -15,96 +15,9 @@ use Symfony\Component\OutputEscaper\SafeDecorator;
 
 class SafeDecoratorTest extends \PHPUnit_Framework_TestCase
 {
-    public function testGetValue()
+    public function testGetRawValue()
     {
         $safe = new SafeDecorator('foo');
-        $this->assertEquals('foo', $safe->getValue(), '->getValue() returns the embedded value');
+        $this->assertEquals('foo', $safe->getRawValue(), '->getValue() returns the embedded value');
     }
-
-    public function testMagicGetAndSet()
-    {
-        $safe = new SafeDecorator(new TestClass1());
-
-        $this->assertEquals('bar', $safe->foo, '->__get() returns the object parameter');
-        $safe->foo = 'baz';
-        $this->assertEquals('baz', $safe->foo, '->__set() sets the object parameter');
-    }
-
-    public function testMagicCall()
-    {
-        $safe = new SafeDecorator(new TestClass2());
-
-        $this->assertEquals('ok', $safe->doSomething(), '->__call() invokes the embedded method');
-    }
-
-    public function testMagicToString()
-    {
-        $safe = new SafeDecorator(new TestClass4());
-
-        $this->assertEquals('TestClass4', (string)$safe, '->__toString() invokes the embedded __toString method');
-    }
-
-    public function testMagicIssetAndUnset()
-    {
-        $safe = new SafeDecorator(new TestClass3());
-
-        $this->assertTrue(isset($safe->boolValue), '->__isset() returns true if the property is not null');
-        $this->assertFalse(isset($safe->nullValue), '->__isset() returns false if the property is null');
-        $this->assertFalse(isset($safe->undefinedValue), '->__isset() returns false if the property does not exist');
-
-        unset($safe->boolValue);
-        $this->assertFalse(isset($safe->boolValue), '->__unset() unsets the embedded property');
-    }
-
-    public function testIteratorInterface()
-    {
-        $input = array('one' => 1, 'two' => 2, 'three' => 3, 'children' => array(1, 2, 3));
-        $output = array();
-
-        $safe = new SafeDecorator($input);
-        foreach ($safe as $key => $value) {
-            $output[$key] = $value;
-        }
-        $this->assertSame($output, $input, '"Iterator" implementation imitates an array');
-    }
-
-    public function testArrayAccessIterator()
-    {
-        $safe = new SafeDecorator(array('foo' => 'bar'));
-
-        $this->assertEquals('bar', $safe['foo'], '"ArrayAccess" implementation returns a value from the embedded array');
-        $safe['foo'] = 'baz';
-        $this->assertEquals('baz', $safe['foo'], '"ArrayAccess" implementation sets a value on the embedded array');
-        $this->assertTrue(isset($safe['foo']), '"ArrayAccess" checks if a value is set on the embedded array');
-        unset($safe['foo']);
-        $this->assertFalse(isset($safe['foo']), '"ArrayAccess" unsets a value on the embedded array');
-    }
-}
-
-class TestClass1
-{
-    public $foo = 'bar';
-}
-
-class TestClass2
-{
-    public function doSomething()
-    {
-        return 'ok';
-    }
-}
-
-class TestClass3
-{
-    public
-        $boolValue = true,
-        $nullValue = null;
 }
-
-class TestClass4
-{
-    public function __toString()
-    {
-        return 'TestClass4';
-    }
-}