8000 [PropertyAccess] Throw an UnexpectedTypeException when the type do no… · symfony/symfony@10c8d5e · GitHub
[go: up one dir, main page]

Skip to content
8000

Commit 10c8d5e

Browse files
dunglasnicolas-grekas
authored andcommitted
[PropertyAccess] Throw an UnexpectedTypeException when the type do not match
1 parent 56624e6 commit 10c8d5e

File tree

4 files changed

+118
-3
lines changed

4 files changed

+118
-3
lines changed

src/Symfony/Component/PropertyAccess/PropertyAccessor.php

Lines changed: 69 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -400,6 +400,7 @@ private function writeIndex(&$array, $index, $value)
400400
*
401401
* @throws NoSuchPropertyException If the property does not exist or is not
402402
* public.
403+
* @throws UnexpectedTypeException
403404
*/
404405
private function writeProperty(&$object, $property, $singular, $value)
405406
{
@@ -410,7 +411,7 @@ private function writeProperty(&$object, $property, $singular, $value)
410411
$access = $this->getWriteAccessInfo($object, $property, $singular, $value);
411412

412413
if (self::ACCESS_TYPE_METHOD === $access[self::ACCESS_TYPE]) {
413-
$object->{$access[self::ACCESS_NAME]}($value);
414+
$this->callMethod($object, $access[self::ACCESS_NAME], $value);
414415
} elseif (self::ACCESS_TYPE_PROPERTY === $access[self::ACCESS_TYPE]) {
415416
$object->{$access[self::ACCESS_NAME]} = $value;
416417
} elseif (self::ACCESS_TYPE_ADDER_AND_REMOVER === $access[self::ACCESS_TYPE]) {
@@ -457,12 +458,78 @@ private function writeProperty(&$object, $property, $singular, $value)
457458

458459
$object->$property = $value;
459460
} elseif (self::ACCESS_TYP 10000 E_MAGIC === $access[self::ACCESS_TYPE]) {
460-
$object->{$access[self::ACCESS_NAME]}($value);
461+
$this->callMethod($object, $access[self::ACCESS_NAME], $value);
461462
} else {
462463
throw new NoSuchPropertyException($access[self::ACCESS_NAME]);
463464
}
464465
}
465466

467+
/**
468+
* Throws a {@see UnexpectedTypeException} as in PHP 7 when using PHP 5.
469+
*
470+
* @param object $object
471+
* @param string $method
472+
* @param mixed $value
473+
*
474+
* @throws UnexpectedTypeException
475+
* @throws \Exception
476+
*/
477+
private function callMethod($object, $method, $value) {
478+
if (PHP_MAJOR_VERSION >= 7) {
479+
try {
480+
$object->{$method}($value);
481+
} catch (\TypeError $e) {
482+
throw $this->createUnexpectedTypeException($object, $method, $value);
483+
}
484+
485+
return;
486+
}
487+
488+
$that = $this;
489+
set_error_handler(function ($errno, $errstr) use ($object, $method, $value, $that) {
490+
if (E_RECOVERABLE_ERROR === $errno && false !== strpos($errstr, sprintf('passed to %s::%s() must', get_class($object), $method))) {
491+
throw $that->createUnexpectedTypeException($object, $method, $value);
492+
}
493+
494+
return false;
495+
});
496+
497+
try {
498+
$object->{$method}($value);
499+
restore_error_handler();
500+
} catch (\Exception $e) {
501+
// Cannot use finally in 5.5 because of https://bugs.php.net/bug.php?id=67047
502+
restore_error_handler();
503+
504+
throw $e;
505+
}
506+
}
507+
508+
/**
509+
* Creates an UnexpectedTypeException.
510+
*
511+
* @param object $object
512+
* @param string $method
513+
* @param mixed $value
514+
*
515+
* @return UnexpectedTypeException
516+
*/
517+
private function createUnexpectedTypeException($object, $method, $value)
518+
{
519+
$reflectionMethod = new \ReflectionMethod($object, $method);
520+
$parameters = $reflectionMethod->getParameters();
521+
522+
$expectedType = 'unknown';
523+
if (isset($parameters[0])) {
524+
$class = $parameters[0]->getClass();
525+
if (null !== $class) {
526+
$expectedType = $class->getName();
527+
}
528+
}
529+
530+
return new UnexpectedTypeException($value, $expectedType);
531+
}
532+
466533
/**
467534
* Guesses how to write the property value.
468535
*

src/Symfony/Component/PropertyAccess/PropertyAccessorInterface.php

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,6 @@ interface PropertyAccessorInterface
4545
*
4646
* @throws Exception\NoSuchPropertyException If a property does not exist or is not public.
4747
* @throws Exception\UnexpectedTypeException If a value within the path is neither object
48-
* nor array
4948
*/
5049
public function setValue(&$objectOrArray, $propertyPath, $value);
5150

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
<?php
2+
3+
/*
4+
* This file is part of the Symfony package.
5+
*
6+
* (c) Fabien Potencier <fabien@symfony.com>
7+
*
8+
* For the full copyright and license information, please view the LICENSE
9+
* file that was distributed with this source code.
10+
*/
11+
12+
namespace Symfony\Component\PropertyAccess\Tests\Fixtures;
13+
14+
/**
15+
* @author Kévin Dunglas <dunglas@gmail.com>
16+
*/
17+
class TypeHinted
18+
{
19+
private $date;
20+
21+
public function setDate(\DateTime $date)
22+
{
23+
$this->date = $date;
24+
}
25+
26+
public function getDate()
27+
{
28+
return $this->date;
29+
}
30+
}

src/Symfony/Component/PropertyAccess/Tests/PropertyAccessorTest.php

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
use Symfony\Component\PropertyAccess\Tests\Fixtures\Magician;
1717
use Symfony\Component\PropertyAccess\Tests\Fixtures\MagicianCall;
1818
use Symfony\Component\PropertyAccess\Tests\Fixtures\Ticket5775Object;
19+
use Symfony\Component\PropertyAccess\Tests\Fixtures\TypeHinted;
1920

2021
class PropertyAccessorTest extends \PHPUnit_Framework_TestCase
2122
{
@@ -403,4 +404,22 @@ public function getValidPropertyPaths()
403404
array(array('root' => array('index' => array())), '[root][index][firstName]', null),
404405
);
405406
}
407+
408+
/**
409+
* @expectedException \Symfony\Component\PropertyAccess\Exception\UnexpectedTypeException
410+
* @expectedExceptionMessage Expected argument of type "DateTime", "string" given
411+
*/
412+
public function testThrowTypeError()
413+
{
414+
$this->propertyAccessor->setValue(new TypeHinted(), 'date', 'This is a string, \DateTime excepted.');
415+
}
416+
417+
public function testSetTypeHint()
418+
{
419+
$date = new \DateTime();
420+
$object = new TypeHinted();
421+
422+
$this->propertyAccessor->setValue($object, 'date', $date);
423+
$this->assertSame($date, $object->getDate());
424+
}
406425
}

0 commit comments

Comments
 (0)
0