[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
|
* @throws NoSuchPropertyException If the property does not exist or is not
|
||||||
* public.
|
* public.
|
||||||
|
* @throws UnexpectedTypeException
|
||||||
*/
|
*/
|
||||||
private function writeProperty(&$object, $property, $singular, $value)
|
private function writeProperty(&$object, $property, $singular, $value)
|
||||||
{
|
{
|
||||||
@ -410,7 +411,7 @@ class PropertyAccessor implements PropertyAccessorInterface
|
|||||||
$access = $this->getWriteAccessInfo($object, $property, $singular, $value);
|
$access = $this->getWriteAccessInfo($object, $property, $singular, $value);
|
||||||
|
|
||||||
if (self::ACCESS_TYPE_METHOD === $access[self::ACCESS_TYPE]) {
|
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]) {
|
} elseif (self::ACCESS_TYPE_PROPERTY === $access[self::ACCESS_TYPE]) {
|
||||||
$object->{$access[self::ACCESS_NAME]} = $value;
|
$object->{$access[self::ACCESS_NAME]} = $value;
|
||||||
} elseif (self::ACCESS_TYPE_ADDER_AND_REMOVER === $access[self::ACCESS_TYPE]) {
|
} elseif (self::ACCESS_TYPE_ADDER_AND_REMOVER === $access[self::ACCESS_TYPE]) {
|
||||||
@ -457,12 +458,78 @@ class PropertyAccessor implements PropertyAccessorInterface
|
|||||||
|
|
||||||
$object->$property = $value;
|
$object->$property = $value;
|
||||||
} elseif (self::ACCESS_TYPE_MAGIC === $access[self::ACCESS_TYPE]) {
|
} elseif (self::ACCESS_TYPE_MAGIC === $access[self::ACCESS_TYPE]) {
|
||||||
$object->{$access[self::ACCESS_NAME]}($value);
|
$this->callMethod($object, $access[self::ACCESS_NAME], $value);
|
||||||
} else {
|
} else {
|
||||||
throw new NoSuchPropertyException($access[self::ACCESS_NAME]);
|
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.
|
* 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\NoSuchPropertyException If a property does not exist or is not public.
|
||||||
* @throws Exception\UnexpectedTypeException If a value within the path is neither object
|
* @throws Exception\UnexpectedTypeException If a value within the path is neither object
|
||||||
* nor array
|
|
||||||
*/
|
*/
|
||||||
public function setValue(&$objectOrArray, $propertyPath, $value);
|
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\Magician;
|
||||||
use Symfony\Component\PropertyAccess\Tests\Fixtures\MagicianCall;
|
use Symfony\Component\PropertyAccess\Tests\Fixtures\MagicianCall;
|
||||||
use Symfony\Component\PropertyAccess\Tests\Fixtures\Ticket5775Object;
|
use Symfony\Component\PropertyAccess\Tests\Fixtures\Ticket5775Object;
|
||||||
|
use Symfony\Component\PropertyAccess\Tests\Fixtures\TypeHinted;
|
||||||
|
|
||||||
class PropertyAccessorTest extends \PHPUnit_Framework_TestCase
|
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),
|
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
Block a user