diff --git a/lib/Doctrine/ORM/Mapping/ReflectionEmbeddedProperty.php b/lib/Doctrine/ORM/Mapping/ReflectionEmbeddedProperty.php index 662c00690..0163ebb89 100644 --- a/lib/Doctrine/ORM/Mapping/ReflectionEmbeddedProperty.php +++ b/lib/Doctrine/ORM/Mapping/ReflectionEmbeddedProperty.php @@ -19,6 +19,9 @@ namespace Doctrine\ORM\Mapping; +use Doctrine\Instantiator\Instantiator; +use ReflectionProperty; + /** * Acts as a proxy to a nested Property structure, making it look like * just a single scalar property. @@ -28,36 +31,62 @@ namespace Doctrine\ORM\Mapping; * * TODO: Move this class into Common\Reflection */ -class ReflectionEmbeddedProperty +class ReflectionEmbeddedProperty extends ReflectionProperty { + /** + * @var ReflectionProperty + */ private $parentProperty; - private $childProperty; - private $class; - public function __construct($parentProperty, $childProperty, $class) + /** + * @var ReflectionProperty + */ + private $childProperty; + + /** + * @var Instantiator|null + */ + private $instantiator; + + /** + * @param ReflectionProperty $parentProperty + * @param ReflectionProperty $childProperty + * @param string $class + */ + public function __construct(ReflectionProperty $parentProperty, ReflectionProperty $childProperty, $class) { $this->parentProperty = $parentProperty; - $this->childProperty = $childProperty; - $this->class = $class; + $this->childProperty = $childProperty; + + parent::__construct($childProperty->getDeclaringClass()->getName(), $childProperty->getName()); } - public function getValue($object) + /** + * {@inheritDoc} + */ + public function getValue($object = null) { $embeddedObject = $this->parentProperty->getValue($object); - if ($embeddedObject === null) { + if (null === $embeddedObject) { return null; } return $this->childProperty->getValue($embeddedObject); } - public function setValue($object, $value) + /** + * {@inheritDoc} + */ + public function setValue($object, $value = null) { $embeddedObject = $this->parentProperty->getValue($object); - if ($embeddedObject === null) { - $embeddedObject = unserialize(sprintf('O:%d:"%s":0:{}', strlen($this->class), $this->class)); + if (null === $embeddedObject) { + $this->instantiator = $this->instantiator ?: new Instantiator(); + + $embeddedObject = $this->instantiator->instantiate($this->class); + $this->parentProperty->setValue($object, $embeddedObject); } diff --git a/tests/Doctrine/Tests/Models/Reflection/ArrayObjectExtendingClass.php b/tests/Doctrine/Tests/Models/Reflection/ArrayObjectExtendingClass.php new file mode 100644 index 000000000..b894a81d3 --- /dev/null +++ b/tests/Doctrine/Tests/Models/Reflection/ArrayObjectExtendingClass.php @@ -0,0 +1,15 @@ +getDeclaringClass()->getName() + ); + + $instantiator = new Instantiator(); + + $object = $instantiator->instantiate($parentProperty->getDeclaringClass()->getName()); + + $embeddedPropertyReflection->setValue($object, 'newValue'); + + $this->assertSame('newValue', $embeddedPropertyReflection->getValue($object)); + + $embeddedPropertyReflection->setValue($object, 'changedValue'); + + $this->assertSame('changedValue', $embeddedPropertyReflection->getValue($object)); + } + + /** + * @param ReflectionProperty $parentProperty + * @param ReflectionProperty $childProperty + * + * @dataProvider getTestedReflectionProperties + */ + public function testWillSkipReadingPropertiesFromNullEmbeddable( + ReflectionProperty $parentProperty, + ReflectionProperty $childProperty + ) + { + $embeddedPropertyReflection = new ReflectionEmbeddedProperty( + $parentProperty, + $childProperty, + $childProperty->getDeclaringClass()->getName() + ); + + $instantiator = new Instantiator(); + + $this->assertNull($embeddedPropertyReflection->getValue( + $instantiator->instantiate($parentProperty->getDeclaringClass()->getName()) + )); + } + + /** + * Data provider + * + * @return ReflectionProperty[][] + */ + public function getTestedReflectionProperties() + { + return array( + array( + $this->getReflectionProperty( + 'Doctrine\\Tests\\Models\\Generic\\BooleanModel', + 'id' + ), + $this->getReflectionProperty( + 'Doctrine\\Tests\\Models\\Generic\\BooleanModel', + 'id' + ), + ), + // reflection on classes extending internal PHP classes: + array( + $this->getReflectionProperty( + 'Doctrine\\Tests\\Models\\Reflection\\ArrayObjectExtendingClass', + 'publicProperty' + ), + $this->getReflectionProperty( + 'Doctrine\\Tests\\Models\\Reflection\\ArrayObjectExtendingClass', + 'privateProperty' + ), + ), + array( + $this->getReflectionProperty( + 'Doctrine\\Tests\\Models\\Reflection\\ArrayObjectExtendingClass', + 'publicProperty' + ), + $this->getReflectionProperty( + 'Doctrine\\Tests\\Models\\Reflection\\ArrayObjectExtendingClass', + 'protectedProperty' + ), + ), + array( + $this->getReflectionProperty( + 'Doctrine\\Tests\\Models\\Reflection\\ArrayObjectExtendingClass', + 'publicProperty' + ), + $this->getReflectionProperty( + 'Doctrine\\Tests\\Models\\Reflection\\ArrayObjectExtendingClass', + 'publicProperty' + ), + ), + ); + } + + /** + * @param string $className + * @param string $propertyName + * + * @return ReflectionProperty + */ + private function getReflectionProperty($className, $propertyName) + { + $reflectionProperty = new ReflectionProperty($className, $propertyName); + + $reflectionProperty->setAccessible(true); + + return $reflectionProperty; + } +}