[PropertyAccess] Throw an UnexpectedTypeException when the type do not match
This commit is contained in:
parent
56624e6a9d
commit
10c8d5eadb
|
@ -400,6 +400,7 @@ class PropertyAccessor implements PropertyAccessorInterface
|
|||
*
|
||||
* @throws NoSuchPropertyException If the property does not exist or is not
|
||||
* public.
|
||||
* @throws UnexpectedTypeException
|
||||
*/
|
||||
private function writeProperty(&$object, $property, $singular, $value)
|
||||
{
|
||||
|
@ -410,7 +411,7 @@ class PropertyAccessor implements PropertyAccessorInterface
|
|||
$access = $this->getWriteAccessInfo($object, $property, $singular, $value);
|
||||
|
||||
if (self::ACCESS_TYPE_METHOD === $access[self::ACCESS_TYPE]) {
|
||||
$object->{$access[self::ACCESS_NAME]}($value);
|
||||
$this->callMethod($object, $access[self::ACCESS_NAME], $value);
|
||||
} elseif (self::ACCESS_TYPE_PROPERTY === $access[self::ACCESS_TYPE]) {
|
||||
$object->{$access[self::ACCESS_NAME]} = $value;
|
||||
} elseif (self::ACCESS_TYPE_ADDER_AND_REMOVER === $access[self::ACCESS_TYPE]) {
|
||||
|
@ -457,12 +458,78 @@ class PropertyAccessor implements PropertyAccessorInterface
|
|||
|
||||
$object->$property = $value;
|
||||
} elseif (self::ACCESS_TYPE_MAGIC === $access[self::ACCESS_TYPE]) {
|
||||
$object->{$access[self::ACCESS_NAME]}($value);
|
||||
$this->callMethod($object, $access[self::ACCESS_NAME], $value);
|
||||
} else {
|
||||
throw new NoSuchPropertyException($access[self::ACCESS_NAME]);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Throws a {@see UnexpectedTypeException} as in PHP 7 when using PHP 5.
|
||||
*
|
||||
* @param object $object
|
||||
* @param string $method
|
||||
* @param mixed $value
|
||||
*
|
||||
* @throws UnexpectedTypeException
|
||||
* @throws \Exception
|
||||
*/
|
||||
private function callMethod($object, $method, $value) {
|
||||
if (PHP_MAJOR_VERSION >= 7) {
|
||||
try {
|
||||
$object->{$method}($value);
|
||||
} catch (\TypeError $e) {
|
||||
throw $this->createUnexpectedTypeException($object, $method, $value);
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
$that = $this;
|
||||
set_error_handler(function ($errno, $errstr) use ($object, $method, $value, $that) {
|
||||
if (E_RECOVERABLE_ERROR === $errno && false !== strpos($errstr, sprintf('passed to %s::%s() must', get_class($object), $method))) {
|
||||
throw $that->createUnexpectedTypeException($object, $method, $value);
|
||||
}
|
||||
|
||||
return false;
|
||||
});
|
||||
|
||||
try {
|
||||
$object->{$method}($value);
|
||||
restore_error_handler();
|
||||
} catch (\Exception $e) {
|
||||
// Cannot use finally in 5.5 because of https://bugs.php.net/bug.php?id=67047
|
||||
restore_error_handler();
|
||||
|
||||
throw $e;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates an UnexpectedTypeException.
|
||||
*
|
||||
* @param object $object
|
||||
* @param string $method
|
||||
* @param mixed $value
|
||||
*
|
||||
* @return UnexpectedTypeException
|
||||
*/
|
||||
private function createUnexpectedTypeException($object, $method, $value)
|
||||
{
|
||||
$reflectionMethod = new \ReflectionMethod($object, $method);
|
||||
$parameters = $reflectionMethod->getParameters();
|
||||
|
||||
$expectedType = 'unknown';
|
||||
if (isset($parameters[0])) {
|
||||
$class = $parameters[0]->getClass();
|
||||
if (null !== $class) {
|
||||
$expectedType = $class->getName();
|
||||
}
|
||||
}
|
||||
|
||||
return new UnexpectedTypeException($value, $expectedType);
|
||||
}
|
||||
|
||||
/**
|
||||
* Guesses how to write the property value.
|
||||
*
|
||||
|
|
|
@ -45,7 +45,6 @@ interface PropertyAccessorInterface
|
|||
*
|
||||
* @throws Exception\NoSuchPropertyException If a property does not exist or is not public.
|
||||
* @throws Exception\UnexpectedTypeException If a value within the path is neither object
|
||||
* nor array
|
||||
*/
|
||||
public function setValue(&$objectOrArray, $propertyPath, $value);
|
||||
|
||||
|
|
|
@ -0,0 +1,30 @@
|
|||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Symfony\Component\PropertyAccess\Tests\Fixtures;
|
||||
|
||||
/**
|
||||
* @author Kévin Dunglas <dunglas@gmail.com>
|
||||
*/
|
||||
class TypeHinted
|
||||
{
|
||||
private $date;
|
||||
|
||||
public function setDate(\DateTime $date)
|
||||
{
|
||||
$this->date = $date;
|
||||
}
|
||||
|
||||
public function getDate()
|
||||
{
|
||||
return $this->date;
|
||||
}
|
||||
}
|
|
@ -16,6 +16,7 @@ use Symfony\Component\PropertyAccess\Tests\Fixtures\Author;
|
|||
use Symfony\Component\PropertyAccess\Tests\Fixtures\Magician;
|
||||
use Symfony\Component\PropertyAccess\Tests\Fixtures\MagicianCall;
|
||||
use Symfony\Component\PropertyAccess\Tests\Fixtures\Ticket5775Object;
|
||||
use Symfony\Component\PropertyAccess\Tests\Fixtures\TypeHinted;
|
||||
|
||||
class PropertyAccessorTest extends \PHPUnit_Framework_TestCase
|
||||
{
|
||||
|
@ -403,4 +404,22 @@ class PropertyAccessorTest extends \PHPUnit_Framework_TestCase
|
|||
array(array('root' => array('index' => array())), '[root][index][firstName]', null),
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @expectedException \Symfony\Component\PropertyAccess\Exception\UnexpectedTypeException
|
||||
* @expectedExceptionMessage Expected argument of type "DateTime", "string" given
|
||||
*/
|
||||
public function testThrowTypeError()
|
||||
{
|
||||
$this->propertyAccessor->setValue(new TypeHinted(), 'date', 'This is a string, \DateTime excepted.');
|
||||
}
|
||||
|
||||
public function testSetTypeHint()
|
||||
{
|
||||
$date = new \DateTime();
|
||||
$object = new TypeHinted();
|
||||
|
||||
$this->propertyAccessor->setValue($object, 'date', $date);
|
||||
$this->assertSame($date, $object->getDate());
|
||||
}
|
||||
}
|
||||
|
|
Reference in New Issue