Browse Source

[Form] Catch exceptions in DataTransformers

Victor Berchet 14 năm trước cách đây
mục cha
commit
dac798c791

+ 22 - 16
src/Symfony/Component/Form/Extension/Core/DataTransformer/ArrayToBooleanChoicesTransformer.php

@@ -25,17 +25,19 @@ class ArrayToBooleanChoicesTransformer implements DataTransformerInterface
     }
 
     /**
-     * Transforms a single choice or an array of choices to a format appropriate
-     * for the nested checkboxes/radio buttons.
+     * Transforms an array of choices to a format appropriate for the nested
+     * checkboxes/radio buttons.
      *
      * The result is an array with the options as keys and true/false as values,
      * depending on whether a given option is selected. If this field is rendered
      * as select tag, the value is not modified.
      *
-     * @param  mixed $array  An array if "multiple" is set to true, a scalar
-     *                       value otherwise.
-     * @return mixed         An array if "expanded" or "multiple" is set to true,
-     *                       a scalar value otherwise.
+     * @param  mixed $array  An array
+     *
+     * @return mixed         An array
+     *
+     * @throws UnexpectedTypeException if the given value is not an array
+     * @throws TransformationFailedException if the choices can not be retrieved
      */
     public function transform($array)
     {
@@ -47,27 +49,31 @@ class ArrayToBooleanChoicesTransformer implements DataTransformerInterface
             throw new UnexpectedTypeException($array, 'array');
         }
 
-        $choices = $this->choiceList->getChoices();
+        try {
+            $choices = $this->choiceList->getChoices();
+        } catch (\Exception $e) {
+            throw new TransformationFailedException('Can not get the choice list', $e->getCode(), $e);
+        }
 
-        foreach ($choices as $choice => $_) {
-            $choices[$choice] = in_array($choice, $array, true);
+        foreach (array_keys($choices) as $key) {
+            $choices[$key] = in_array($key, $array, true);
         }
 
         return $choices;
     }
 
     /**
-     * Transforms a checkbox/radio button array to a single choice or an array
-     * of choices.
+     * Transforms a checkbox/radio button array to an array of choices.
      *
      * The input value is an array with the choices as keys and true/false as
      * values, depending on whether a given choice is selected. The output
-     * is an array with the selected choices or a single selected choice.
+     * is an array with the selected choices.
+     *
+     * @param  mixed $value  An array
+     *
+     * @return mixed $value  An array
      *
-     * @param  mixed $value  An array if "expanded" or "multiple" is set to true,
-     *                       a scalar value otherwise.
-     * @return mixed $value  An array if "multiple" is set to true, a scalar
-     *                       value otherwise.
+     * @throws UnexpectedTypeException if the given value is not an array
      */
     public function reverseTransform($value)
     {

+ 8 - 0
src/Symfony/Component/Form/Extension/Core/DataTransformer/DataTransformerChain.php

@@ -45,7 +45,11 @@ class DataTransformerChain implements DataTransformerInterface
      * by this method.
      *
      * @param  mixed $value  The original value
+     *
      * @return mixed         The transformed value
+     *
+     * @throws Symfony\Component\Form\Exception\TransformationFailedException
+     * @throws Symfony\Component\Form\Exception\UnexpectedTypeException
      */
     public function transform($value)
     {
@@ -66,7 +70,11 @@ class DataTransformerChain implements DataTransformerInterface
      * by this method.
      *
      * @param  mixed $value  The transformed value
+     *
      * @return mixed         The reverse-transformed value
+     *
+     * @throws Symfony\Component\Form\Exception\TransformationFailedException
+     * @throws Symfony\Component\Form\Exception\UnexpectedTypeException
      */
     public function reverseTransform($value)
     {

+ 12 - 5
src/Symfony/Component/Form/Extension/Core/DataTransformer/DateTimeToArrayTransformer.php

@@ -56,6 +56,7 @@ class DateTimeToArrayTransformer extends BaseDateTimeTransformer
      * @return array               Localized date.
      *
      * @throws UnexpectedTypeException if the given value is not an instance of \DateTime
+     * @throws TransformationFailedException if the output timezone is not supported
      */
     public function transform($dateTime)
     {
@@ -74,8 +75,13 @@ class DateTimeToArrayTransformer extends BaseDateTimeTransformer
             throw new UnexpectedTypeException($dateTime, '\DateTime');
         }
 
+
         if ($this->inputTimezone !== $this->outputTimezone) {
-            $dateTime->setTimezone(new \DateTimeZone($this->outputTimezone));
+            try {
+                $dateTime->setTimezone(new \DateTimeZone($this->outputTimezone));
+            } catch (\Exception $e) {
+                throw new TransformationFailedException($e->getMessage(), $e->getCode(), $e);
+            }
         }
 
         $result = array_intersect_key(array(
@@ -106,6 +112,7 @@ class DateTimeToArrayTransformer extends BaseDateTimeTransformer
      *
      * @throws UnexpectedTypeException if the given value is not an array
      * @throws TransformationFailedException if the value could not bet transformed
+     * @throws TransformationFailedException if the input timezone is not supported
      */
     public function reverseTransform($value)
     {
@@ -146,14 +153,14 @@ class DateTimeToArrayTransformer extends BaseDateTimeTransformer
                 empty($value['second']) ? '0' : $value['second'],
                 $this->outputTimezone
             ));
+
+            if ($this->inputTimezone !== $this->outputTimezone) {
+                $dateTime->setTimezone(new \DateTimeZone($this->inputTimezone));
+            }
         } catch (\Exception $e) {
             throw new TransformationFailedException($e->getMessage(), $e->getCode(), $e);
         }
 
-        if ($this->inputTimezone !== $this->outputTimezone) {
-            $dateTime->setTimezone(new \DateTimeZone($this->inputTimezone));
-        }
-
         return $dateTime;
     }
 }

+ 8 - 3
src/Symfony/Component/Form/Extension/Core/DataTransformer/DateTimeToLocalizedStringTransformer.php

@@ -52,11 +52,11 @@ class DateTimeToLocalizedStringTransformer extends BaseDateTimeTransformer
         }
 
         if (!in_array($dateFormat, self::$formats, true)) {
-            throw new \InvalidArgumentException(sprintf('The value $dateFormat is expected to be one of "%s". Is "%s"', implode('", "', self::$formats), $dateFormat));
+            throw new UnexpectedTypeException($dateFormat, implode('", "', self::$formats));
         }
 
         if (!in_array($timeFormat, self::$formats, true)) {
-            throw new \InvalidArgumentException(sprintf('The value $timeFormat is expected to be one of "%s". Is "%s"', implode('", "', self::$formats), $timeFormat));
+            throw new UnexpectedTypeException($timeFormat, implode('", "', self::$formats));
         }
 
         $this->dateFormat = $dateFormat;
@@ -106,6 +106,7 @@ class DateTimeToLocalizedStringTransformer extends BaseDateTimeTransformer
      *
      * @throws UnexpectedTypeException if the given value is not a string
      * @throws TransformationFailedException if the date could not be parsed
+     * @throws TransformationFailedException if the input timezone is not supported
      */
     public function reverseTransform($value)
     {
@@ -127,7 +128,11 @@ class DateTimeToLocalizedStringTransformer extends BaseDateTimeTransformer
         $dateTime = new \DateTime(sprintf('@%s UTC', $timestamp));
 
         if ('UTC' !== $this->inputTimezone) {
-            $dateTime->setTimezone(new \DateTimeZone($this->inputTimezone));
+            try {
+                $dateTime->setTimezone(new \DateTimeZone($this->inputTimezone));
+            } catch (\Exception $e) {
+                throw new TransformationFailedException($e->getMessage(), $e->getCode(), $e);
+            }
         }
 
         return $dateTime;

+ 10 - 4
src/Symfony/Component/Form/Extension/Core/DataTransformer/DateTimeToStringTransformer.php

@@ -51,6 +51,7 @@ class DateTimeToStringTransformer extends BaseDateTimeTransformer
      * @return string           A value as produced by PHP's date() function
      *
      * @throws UnexpectedTypeException if the given value is not a \DateTime instance
+     * @throws TransformationFailedException if the output timezone is not supported
      */
     public function transform($value)
     {
@@ -62,7 +63,11 @@ class DateTimeToStringTransformer extends BaseDateTimeTransformer
             throw new UnexpectedTypeException($value, '\DateTime');
         }
 
-        $value->setTimezone(new \DateTimeZone($this->outputTimezone));
+        try {
+            $value->setTimezone(new \DateTimeZone($this->outputTimezone));
+        } catch (\Exception $e) {
+            throw new TransformationFailedException($e->getMessage(), $e->getCode(), $e);
+        }
 
         return $value->format($this->format);
     }
@@ -76,6 +81,7 @@ class DateTimeToStringTransformer extends BaseDateTimeTransformer
      *
      * @throws UnexpectedTypeException if the given value is not a string
      * @throws TransformationFailedException if the date could not be parsed
+     * @throws TransformationFailedException if the input timezone is not supported
      */
     public function reverseTransform($value)
     {
@@ -93,10 +99,10 @@ class DateTimeToStringTransformer extends BaseDateTimeTransformer
             if ($this->inputTimezone !== $this->outputTimezone) {
                 $dateTime->setTimeZone(new \DateTimeZone($this->inputTimezone));
             }
-
-            return $dateTime;
         } catch (\Exception $e) {
-            throw new TransformationFailedException('Invalid date format.', $e->getCode(), $e);
+            throw new TransformationFailedException($e->getMessage(), $e->getCode(), $e);
         }
+
+        return $dateTime;
     }
 }

+ 9 - 4
src/Symfony/Component/Form/Extension/Core/DataTransformer/DateTimeToTimestampTransformer.php

@@ -30,6 +30,7 @@ class DateTimeToTimestampTransformer extends BaseDateTimeTransformer
      * @return integer          A timestamp
      *
      * @throws UnexpectedTypeException if the given value is not an instance of \DateTime
+     * @throws TransformationFailedException if the output timezone is not supported
      */
     public function transform($value)
     {
@@ -41,7 +42,11 @@ class DateTimeToTimestampTransformer extends BaseDateTimeTransformer
             throw new UnexpectedTypeException($value, '\DateTime');
         }
 
-        $value->setTimezone(new \DateTimeZone($this->outputTimezone));
+        try {
+            $value->setTimezone(new \DateTimeZone($this->outputTimezone));
+        } catch (\Exception $e) {
+            throw new TransformationFailedException($e->getMessage(), $e->getCode(), $e);
+        }
 
         return (int) $value->format('U');
     }
@@ -72,10 +77,10 @@ class DateTimeToTimestampTransformer extends BaseDateTimeTransformer
             if ($this->inputTimezone !== $this->outputTimezone) {
                 $dateTime->setTimezone(new \DateTimeZone($this->inputTimezone));
             }
-
-            return $dateTime;
         } catch (\Exception $e) {
-            throw new TransformationFailedException('Invalid timestamp format.', $e->getCode(), $e);
+            throw new TransformationFailedException($e->getMessage(), $e->getCode(), $e);
         }
+
+        return $dateTime;
     }
 }

+ 33 - 1
src/Symfony/Component/Form/Extension/Core/DataTransformer/FileToStringTransformer.php

@@ -13,6 +13,8 @@ namespace Symfony\Component\Form\Extension\Core\DataTransformer;
 
 use Symfony\Component\Form\DataTransformerInterface;
 use Symfony\Component\Form\Exception\UnexpectedTypeException;
+use Symfony\Component\HttpFoundation\File\Exception\FileNotFoundException;
+use Symfony\Component\Form\Exception\TransformationFailedException;
 use Symfony\Component\HttpFoundation\File\File;
 
 /**
@@ -20,6 +22,15 @@ use Symfony\Component\HttpFoundation\File\File;
  */
 class FileToStringTransformer implements DataTransformerInterface
 {
+    /**
+     * Transforms a File instance to a path
+     *
+     * @param File $file The file
+     *
+     * @return string The path to the file
+     *
+     * @throws UnexpectedTypeException if the given file is not an instance of File
+     */
     public function transform($file)
     {
         if (null === $file || '' === $file) {
@@ -33,6 +44,17 @@ class FileToStringTransformer implements DataTransformerInterface
         return $file->getPath();
     }
 
+
+    /**
+     * Transforms a path to a File instance
+     *
+     * @param string $path The path to the file
+     *
+     * @return File The File
+     *
+     * @throws UnexpectedTypeException if the given path is not a string
+     * @throws TransformationFailedException if the File instance could not be created
+     */
     public function reverseTransform($path)
     {
         if (null === $path || '' === $path) {
@@ -43,6 +65,16 @@ class FileToStringTransformer implements DataTransformerInterface
             throw new UnexpectedTypeException($path, 'string');
         }
 
-        return new File($path);
+        try {
+            $file = new File($path);
+        } catch (FileNotFoundException $e) {
+            throw new TransformationFailedException(
+                sprintf('The file "%s" does not exist', $path),
+                $e->getCode(),
+                $e
+            );
+        }
+
+        return $file;
     }
 }

+ 30 - 14
src/Symfony/Component/Form/Extension/Core/DataTransformer/ScalarToBooleanChoicesTransformer.php

@@ -19,14 +19,19 @@ class ScalarToBooleanChoicesTransformer implements DataTransformerInterface
 {
     private $choiceList;
 
+    /**
+     * Constructor.
+     *
+     * @param ChoiceListInterface $choiceList
+     */
     public function __construct(ChoiceListInterface $choiceList)
     {
         $this->choiceList = $choiceList;
     }
 
     /**
-     * Transforms a single choice or an array of choices to a format appropriate
-     * for the nested checkboxes/radio buttons.
+     * Transforms a single choice to a format appropriate for the nested
+     * checkboxes/radio buttons.
      *
      * The result is an array with the options as keys and true/false as values,
      * depending on whether a given option is selected. If this field is rendered
@@ -34,32 +39,43 @@ class ScalarToBooleanChoicesTransformer implements DataTransformerInterface
      *
      * @param  mixed $value  An array if "multiple" is set to true, a scalar
      *                       value otherwise.
-     * @return mixed         An array if "expanded" or "multiple" is set to true,
-     *                       a scalar value otherwise.
+     *
+     * @return mixed         An array
+     *
+     * @throws UnexpectedTypeException if the given value is not scalar
+     * @throws TransformationFailedException if the choices can not be retrieved
      */
     public function transform($value)
     {
-        $choices = $this->choiceList->getChoices();
+        if (!is_scalar($value) && !is_null($value)) {
+            throw new UnexpectedTypeException($value, 'scalar');
+        }
 
-        foreach ($choices as $choice => $_) {
-            $choices[$choice] = $choice === $value;
+        try {
+            $choices = $this->choiceList->getChoices();
+        } catch (\Exception $e) {
+            throw new TransformationFailedException('Can not get the choice list', $e->getCode(), $e);
+        }
+
+        foreach (array_keys($choices) as $key) {
+            $choices[$key] = $key === $value;
         }
 
         return $choices;
     }
 
     /**
-     * Transforms a checkbox/radio button array to a single choice or an array
-     * of choices.
+     * Transforms a checkbox/radio button array to a single choice.
      *
      * The input value is an array with the choices as keys and true/false as
      * values, depending on whether a given choice is selected. The output
-     * is an array with the selected choices or a single selected choice.
+     * is the selected choice.
      *
-     * @param  mixed $value  An array if "expanded" or "multiple" is set to true,
-     *                       a scalar value otherwise.
-     * @return mixed $value  An array if "multiple" is set to true, a scalar
-     *                       value otherwise.
+     * @param  array $value  An array of values
+     *
+     * @return mixed $value  A scalar value
+     *
+     * @throws new UnexpectedTypeException if the given value is not an array
      */
     public function reverseTransform($value)
     {

+ 15 - 13
tests/Symfony/Tests/Component/Form/Extension/Core/DataTransformer/DateTimeToLocalizedStringTransformerTest.php

@@ -123,12 +123,12 @@ class DateTimeToLocalizedStringTransformerTest extends DateTimeTestCase
         $this->assertEquals($dateTime->format('d.m.Y H:i'), $transformer->transform($input));
     }
 
+    /**
+     * @expectedException Symfony\Component\Form\Exception\UnexpectedTypeException
+     */
     public function testTransformRequiresValidDateTime()
     {
         $transformer = new DateTimeToLocalizedStringTransformer();
-
-        $this->setExpectedException('Symfony\Component\Form\Exception\UnexpectedTypeException');
-
         $transformer->transform('2010-01-01');
     }
 
@@ -225,35 +225,37 @@ class DateTimeToLocalizedStringTransformerTest extends DateTimeTestCase
         $this->assertSame(null, $transformer->reverseTransform('', null));
     }
 
+    /**
+     * @expectedException Symfony\Component\Form\Exception\UnexpectedTypeException
+     */
     public function testReverseTransformRequiresString()
     {
         $transformer = new DateTimeToLocalizedStringTransformer();
-
-        $this->setExpectedException('Symfony\Component\Form\Exception\UnexpectedTypeException');
-
         $transformer->reverseTransform(12345, null);
     }
 
+    /**
+     * @expectedException Symfony\Component\Form\Exception\TransformationFailedException
+     */
     public function testReverseTransformWrapsIntlErrors()
     {
         $transformer = new DateTimeToLocalizedStringTransformer();
-
-        $this->setExpectedException('Symfony\Component\Form\Exception\TransformationFailedException');
-
         $transformer->reverseTransform('12345', null);
     }
 
+    /**
+     * @expectedException Symfony\Component\Form\Exception\UnexpectedTypeException
+     */
     public function testValidateDateFormatOption()
     {
-        $this->setExpectedException('\InvalidArgumentException');
-
         new DateTimeToLocalizedStringTransformer(null, null, 'foobar');
     }
 
+    /**
+     * @expectedException Symfony\Component\Form\Exception\UnexpectedTypeException
+     */
     public function testValidateTimeFormatOption()
     {
-        $this->setExpectedException('\InvalidArgumentException');
-
         new DateTimeToLocalizedStringTransformer(null, null, null, 'foobar');
     }
 }

+ 65 - 0
tests/Symfony/Tests/Component/Form/Extension/Core/DataTransformer/FileToStringTransformerTest.php

@@ -0,0 +1,65 @@
+<?php
+
+/*
+ * 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.
+ */
+
+namespace Symfony\Tests\Component\Form\Extension\Core\DataTransformer;
+
+use Symfony\Component\Form\Extension\Core\DataTransformer\FileToStringTransformer;
+use Symfony\Component\HttpFoundation\File\File;
+
+class FileToStringTransformerTest extends \PHPUnit_Framework_TestCase
+{
+    private $transformer;
+
+    protected function setUp()
+    {
+        $this->transformer = new FileToStringTransformer();
+    }
+
+    public function testTransform()
+    {
+        $path = realpath(__DIR__.'/../../../Fixtures/foo');
+
+        $file = new File($path);
+        $t = $this->transformer->transform($file);
+
+        $this->assertTrue(file_exists($path));
+        $this->assertInternalType('string', $t);
+        $this->assertEquals($path, realpath($t));
+    }
+
+    /**
+     * @expectedException Symfony\Component\Form\Exception\UnexpectedTypeException
+     */
+    public function testTransformRequiresAFile()
+    {
+        $this->transformer->transform(array());
+    }
+
+    public function testReverseTransform()
+    {
+        $path = realpath(__DIR__.'/../../../Fixtures/foo');
+
+        $file = new File($path);
+        $r = $this->transformer->reverseTransform($path);
+
+        $this->assertInstanceOf('Symfony\Component\HttpFoundation\File\File', $file);
+        $this->assertEquals($path, realpath($r->getPath()));
+
+    }
+
+    /**
+     * @expectedException Symfony\Component\Form\Exception\TransformationFailedException
+     */
+    public function testReverseTransformRequiresArray()
+    {
+        $t = $this->transformer->reverseTransform(__DIR__.'/../../../Fixtures/no-foo');
+    }
+}

+ 0 - 0
tests/Symfony/Tests/Component/Form/Fixtures/foo