Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit a4f99d8

Browse files
[Serializer] fix support for lazy/unset properties
1 parent 0796087 commit a4f99d8

File tree

5 files changed

+55
-19
lines changed

5 files changed

+55
-19
lines changed

src/Symfony/Component/PropertyAccess/PropertyAccessor.php

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -385,6 +385,7 @@ private function readProperty(array $zval, string $property, bool $ignoreInvalid
385385

386386
$result = self::RESULT_PROTO;
387387
$object = $zval[self::VALUE];
388+
$class = \get_class($object);
388389
$access = $this->getReadAccessInfo(\get_class($object), $property);
389390

390391
try {
@@ -406,6 +407,11 @@ private function readProperty(array $zval, string $property, bool $ignoreInvalid
406407
throw $e;
407408
}
408409
} elseif (self::ACCESS_TYPE_PROPERTY === $access[self::ACCESS_TYPE]) {
410+
$name = $access[self::ACCESS_NAME];
411+
if (!method_exists($object, '__get') && !\array_key_exists($name, (array) $object) && (\PHP_VERSION_ID < 70400 || !(new \ReflectionProperty($class, $name))->hasType())) {
412+
throw new AccessException(sprintf('The property "%s::$%s" is not initialized.', $class, $name));
413+
}
414+
409415
$result[self::VALUE] = $object->{$access[self::ACCESS_NAME]};
410416

411417
if ($access[self::ACCESS_REF] && isset($zval[self::REF])) {

src/Symfony/Component/Serializer/Normalizer/AbstractObjectNormalizer.php

Lines changed: 29 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111

1212
namespace Symfony\Component\Serializer\Normalizer;
1313

14+
use Symfony\Component\PropertyAccess\Exception\AccessException;
1415
use Symfony\Component\PropertyAccess\Exception\InvalidArgumentException;
1516
use Symfony\Component\PropertyAccess\Exception\NoSuchPropertyException;
1617
use Symfony\Component\PropertyInfo\PropertyTypeExtractorInterface;
@@ -181,7 +182,23 @@ public function normalize($object, $format = null, array $context = [])
181182
continue;
182183
}
183184

184-
$attributeValue = $this->getAttributeValue($object, $attribute, $format, $context);
185+
try {
186+
$attributeValue = $this->getAttributeValue($object, $attribute, $format, $context);
187+
} catch (AccessException $e) {
188+
if (sprintf('The property "%s::$%s" is not initialized.', \get_class($object), $attribute) === $e->getMessage()) {
189+
continue;
190+
}
191+
if (($p = $e->getPrevious()) && 'Error' === \get_class($p) && $this->isUninitializedValueError($p)) {
192+
continue;
193+
}
194+
throw $e;
195+
} catch (\Error $e) {
196+
if ($this->isUninitializedValueError($e)) {
197+
continue;
198+
}
199+
throw $e;
200+
}
201+
185202
if ($maxDepthReached) {
186203
$attributeValue = $maxDepthHandler($attributeValue, $object, $attribute, $format, $context);
187204
}
@@ -637,4 +654,15 @@ private function getCacheKey(?string $format, array $context)
637654
return false;
638655
}
639656
}
657+
658+
/**
659+
* This error may occur when specific object normalizer implementation gets attribute value
660+
* by accessing a public uninitialized property or by calling a method accessing such property.
661+
*/
662+
private function isUninitializedValueError(\Error $e): bool
663+
{
664+
return \PHP_VERSION_ID >= 70400
665+
&& str_starts_with($e->getMessage(), 'Typed property')
666+
&& str_ends_with($e->getMessage(), 'must not be accessed before initialization');
667+
}
640668
}

src/Symfony/Component/Serializer/Normalizer/ObjectNormalizer.php

Lines changed: 1 addition & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -104,16 +104,8 @@ protected function extractAttributes($object, $format = null, array $context = [
104104
}
105105

106106
// properties
107-
$propertyValues = !method_exists($object, '__get') ? (array) $object : null;
108107
foreach ($reflClass->getProperties() as $reflProperty) {
109-
if (null !== $propertyValues && !\array_key_exists($reflProperty->name, $propertyValues)) {
110-
if ($reflProperty->isPublic()
111-
|| ($reflProperty->isProtected() && !\array_key_exists("\0*\0{$reflProperty->name}", $propertyValues))
112-
|| ($reflProperty->isPrivate() && !\array_key_exists("\0{$reflProperty->class}\0{$reflProperty->name}", $propertyValues))
113-
) {
114-
unset($attributes[$reflProperty->name]);
115-
}
116-
108+
if (!$reflProperty->isPublic()) {
117109
continue;
118110
}
119111

src/Symfony/Component/Serializer/Normalizer/PropertyNormalizer.php

Lines changed: 18 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,8 @@
1111

1212
namespace Symfony\Component\Serializer\Normalizer;
1313

14+
use Symfony\Component\PropertyAccess\Exception\AccessException;
15+
1416
/**
1517
* Converts between objects and arrays by mapping properties.
1618
*
@@ -101,17 +103,10 @@ protected function extractAttributes($object, $format = null, array $context = [
101103
{
102104
$reflectionObject = new \ReflectionObject($object);
103105
$attributes = [];
104-
$propertyValues = !method_exists($object, '__get') ? (array) $object : null;
105106

106107
do {
107108
foreach ($reflectionObject->getProperties() as $property) {
108-
if ((null !== $propertyValues && (
109-
($property->isPublic() && !\array_key_exists($property->name, $propertyValues))
110-
|| ($property->isProtected() && !\array_key_exists("\0*\0{$property->name}", $propertyValues))
111-
|| ($property->isPrivate() && !\array_key_exists("\0{$property->class}\0{$property->name}", $propertyValues))
112-
))
113-
|| !$this->isAllowedAttribute($reflectionObject->getName(), $property->name, $format, $context)
114-
) {
109+
if (!$this->isAllowedAttribute($reflectionObject->getName(), $property->name, $format, $context)) {
115110
continue;
116111
}
117112

@@ -138,6 +133,21 @@ protected function getAttributeValue($object, $attribute, $format = null, array
138133
$reflectionProperty->setAccessible(true);
139134
}
140135

136+
if (\PHP_VERSION_ID >= 70400 && $reflectionProperty->hasType()) {
137+
return $reflectionProperty->getValue($object);
138+
}
139+
140+
if (!method_exists($object, '__get')) {
141+
$propertyValues = (array) $object;
142+
143+
if (($reflectionProperty->isPublic() && !\array_key_exists($reflectionProperty->name, $propertyValues))
144+
|| ($reflectionProperty->isProtected() && !\array_key_exists("\0*\0{$reflectionProperty->name}", $propertyValues))
145+
|| ($reflectionProperty->isPrivate() && !\array_key_exists("\0{$reflectionProperty->class}\0{$reflectionProperty->name}", $propertyValues))
146+
) {
147+
throw new AccessException(sprintf('The property "%s::$%s" is not initialized.', \get_class($object), $reflectionProperty->name));
148+
}
149+
}
150+
141151
return $reflectionProperty->getValue($object);
142152
}
143153

src/Symfony/Component/Serializer/composer.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@
2929
"symfony/error-handler": "^4.4|^5.0",
3030
"symfony/http-foundation": "^3.4|^4.0|^5.0",
3131
"symfony/mime": "^4.4|^5.0",
32-
"symfony/property-access": "^3.4.41|^4.4.9|^5.0.9",
32+
"symfony/property-access": "^4.4.36|^5.3.13",
3333
"symfony/property-info": "^3.4.13|~4.0|^5.0",
3434
"symfony/validator": "^3.4|^4.0|^5.0",
3535
"symfony/yaml": "^3.4|^4.0|^5.0"

0 commit comments

Comments
 (0)