فهرست منبع

[Form] Added FormTypeInterface::getAllowedOptionValues() to better validate passed options

Bernhard Schussek 14 سال پیش
والد
کامیت
da28f8e3b3

+ 12 - 0
src/Symfony/Component/Form/AbstractType.php

@@ -46,6 +46,18 @@ abstract class AbstractType implements FormTypeInterface
         return array();
     }
 
+    /**
+     * Returns the allowed option values for each option (if any).
+     *
+     * @param array $options
+     *
+     * @return array The allowed option values
+     */
+    public function getAllowedOptionValues(array $options)
+    {
+        return array();
+    }
+
     /**
      * Returns the name of the parent type.
      *

+ 12 - 0
src/Symfony/Component/Form/AbstractTypeExtension.php

@@ -29,4 +29,16 @@ abstract class AbstractTypeExtension implements FormTypeExtensionInterface
     {
         return array();
     }
+
+    /**
+     * Returns the allowed option values for each option (if any).
+     *
+     * @param array $options
+     *
+     * @return array The allowed option values
+     */
+    public function getAllowedOptionValues(array $options)
+    {
+        return array();
+    }
 }

+ 11 - 2
src/Symfony/Component/Form/FormFactory.php

@@ -114,6 +114,7 @@ class FormFactory implements FormFactoryInterface
         $types = array();
         $knownOptions = array();
         $passedOptions = array_keys($options);
+        $optionValues = array();
 
         if (!array_key_exists('data', $options)) {
             $options['data'] = $data;
@@ -127,12 +128,14 @@ class FormFactory implements FormFactoryInterface
             }
 
             $defaultOptions = $type->getDefaultOptions($options);
+            $optionValues = array_merge_recursive($optionValues, $type->getAllowedOptionValues($options));
 
             foreach ($type->getExtensions() as $typeExtension) {
-                $defaultOptions = array_merge($defaultOptions, $typeExtension->getDefaultOptions($options));
+                $defaultOptions = array_replace($defaultOptions, $typeExtension->getDefaultOptions($options));
+                $optionValues = array_merge_recursive($optionValues, $typeExtension->getAllowedOptionValues($options));
             }
 
-            $options = array_merge($defaultOptions, $options);
+            $options = array_replace($defaultOptions, $options);
             $knownOptions = array_merge($knownOptions, array_keys($defaultOptions));
             array_unshift($types, $type);
             $type = $type->getParent($options);
@@ -155,6 +158,12 @@ class FormFactory implements FormFactoryInterface
             throw new CreationException(sprintf('The option "%s" does not exist', $diff[0]));
         }
 
+        foreach ($optionValues as $option => $allowedValues) {
+            if (!in_array($options[$option], $allowedValues, true)) {
+                throw new CreationException(sprintf('The option "%s" has the value "%s", but is expected to be one of "%s"', $option, $options[$option], implode('", "', $allowedValues)));
+            }
+        }
+
         for ($i = 0, $l = count($types); $i < $l && !$builder; ++$i) {
             $builder = $types[$i]->createBuilder($name, $this, $options);
         }

+ 9 - 0
src/Symfony/Component/Form/FormTypeExtensionInterface.php

@@ -21,5 +21,14 @@ interface FormTypeExtensionInterface
 
     function getDefaultOptions(array $options);
 
+    /**
+     * Returns the allowed option values for each option (if any).
+     *
+     * @param array $options
+     *
+     * @return array The allowed option values
+     */
+    function getAllowedOptionValues(array $options);
+
     function getExtendedType();
 }

+ 9 - 0
src/Symfony/Component/Form/FormTypeInterface.php

@@ -30,6 +30,15 @@ interface FormTypeInterface
      */
     function getDefaultOptions(array $options);
 
+    /**
+     * Returns the allowed option values for each option (if any).
+     *
+     * @param array $options
+     *
+     * @return array The allowed option values
+     */
+    function getAllowedOptionValues(array $options);
+
     /**
      * Returns the name of the parent type.
      *

+ 8 - 0
tests/Symfony/Tests/Component/Form/Fixtures/FooType.php

@@ -31,6 +31,14 @@ class FooType extends AbstractType
             'data' => null,
             'required' => false,
             'max_length' => null,
+            'a_or_b' => 'a',
+        );
+    }
+
+    public function getAllowedOptionValues(array $options)
+    {
+        return array(
+            'a_or_b' => array('a', 'b'),
         );
     }
 

+ 7 - 0
tests/Symfony/Tests/Component/Form/Fixtures/FooTypeBarExtension.php

@@ -12,6 +12,13 @@ class FooTypeBarExtension extends AbstractTypeExtension
         $builder->setAttribute('bar', 'x');
     }
 
+    public function getAllowedOptionValues(array $options)
+    {
+        return array(
+            'a_or_b' => array('c'),
+        );
+    }
+
     public function getExtendedType()
     {
         return 'foo';

+ 37 - 0
tests/Symfony/Tests/Component/Form/FormFactoryTest.php

@@ -162,6 +162,9 @@ class FormFactoryTest extends \PHPUnit_Framework_TestCase
         $type->expects($this->any())
             ->method('getExtensions')
             ->will($this->returnValue(array()));
+        $type->expects($this->any())
+            ->method('getAllowedOptionValues')
+            ->will($this->returnValue(array()));
         $type->expects($this->any())
             ->method('getDefaultOptions')
             ->will($this->returnValue(array(
@@ -186,6 +189,9 @@ class FormFactoryTest extends \PHPUnit_Framework_TestCase
         $type->expects($this->any())
             ->method('getExtensions')
             ->will($this->returnValue(array()));
+        $type->expects($this->any())
+            ->method('getAllowedOptionValues')
+            ->will($this->returnValue(array()));
         $type->expects($this->any())
             ->method('getDefaultOptions')
             ->will($this->returnValue(array(
@@ -210,6 +216,9 @@ class FormFactoryTest extends \PHPUnit_Framework_TestCase
         $type->expects($this->any())
             ->method('getExtensions')
             ->will($this->returnValue(array()));
+        $type->expects($this->any())
+            ->method('getAllowedOptionValues')
+            ->will($this->returnValue(array()));
         $type->expects($this->any())
             ->method('getDefaultOptions')
             ->will($this->returnValue(array(
@@ -234,6 +243,9 @@ class FormFactoryTest extends \PHPUnit_Framework_TestCase
         $type->expects($this->any())
             ->method('getExtensions')
             ->will($this->returnValue(array()));
+        $type->expects($this->any())
+            ->method('getAllowedOptionValues')
+            ->will($this->returnValue(array()));
         $type->expects($this->any())
             ->method('getDefaultOptions')
             ->will($this->returnValue(array(
@@ -263,6 +275,31 @@ class FormFactoryTest extends \PHPUnit_Framework_TestCase
         ));
     }
 
+    /**
+     * @expectedException Symfony\Component\Form\Exception\CreationException
+     */
+    public function testCreateNamedBuilderExpectsOptionsToBeInValidRange()
+    {
+        $type = new FooType();
+        $this->extension1->addType($type);
+
+        $this->factory->createNamedBuilder('foo', 'bar', null, array(
+            'a_or_b' => 'c',
+        ));
+    }
+
+    public function testCreateNamedBuilderAllowsExtensionsToExtendAllowedOptionValues()
+    {
+        $type = new FooType();
+        $this->extension1->addType($type);
+        $this->extension1->addTypeExtension(new FooTypeBarExtension());
+
+        // no exception this time
+        $this->factory->createNamedBuilder('foo', 'bar', null, array(
+            'a_or_b' => 'c',
+        ));
+    }
+
     public function testCreateNamedBuilderAddsTypeInstances()
     {
         $type = new FooType();