From 192091dd3449f04595561632240c838f4688a15e Mon Sep 17 00:00:00 2001 From: Vincent Langlet Date: Fri, 20 Jun 2025 23:16:40 +0200 Subject: [PATCH 1/4] Move property accessor phpdoc to interface --- PropertyAccessor.php | 5 ----- PropertyAccessorInterface.php | 6 ++++++ 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/PropertyAccessor.php b/PropertyAccessor.php index 8685407..9a2c82d 100644 --- a/PropertyAccessor.php +++ b/PropertyAccessor.php @@ -109,11 +109,6 @@ public function getValue(object|array $objectOrArray, string|PropertyPathInterfa return $propertyValues[\count($propertyValues) - 1][self::VALUE]; } - /** - * @template T of object|array - * @param T $objectOrArray - * @param-out ($objectOrArray is array ? array : T) $objectOrArray - */ public function setValue(object|array &$objectOrArray, string|PropertyPathInterface $propertyPath, mixed $value): void { if (\is_object($objectOrArray) && (false === strpbrk((string) $propertyPath, '.[') || $objectOrArray instanceof \stdClass && property_exists($objectOrArray, $propertyPath))) { diff --git a/PropertyAccessorInterface.php b/PropertyAccessorInterface.php index 2e25e9e..ccbaf8b 100644 --- a/PropertyAccessorInterface.php +++ b/PropertyAccessorInterface.php @@ -39,6 +39,12 @@ interface PropertyAccessorInterface * * If neither is found, an exception is thrown. * + * @template T of object|array + * + * @param T $objectOrArray + * + * @param-out ($objectOrArray is array ? array : T) $objectOrArray + * * @throws Exception\InvalidArgumentException If the property path is invalid * @throws Exception\AccessException If a property/index does not exist or is not public * @throws Exception\UnexpectedTypeException If a value within the path is neither object nor array From beb97856e437c3cacb6b37ce7bebc756c5851988 Mon Sep 17 00:00:00 2001 From: Nicolas Grekas Date: Thu, 10 Jul 2025 09:12:18 +0200 Subject: [PATCH 2/4] CS fixes --- Exception/UnexpectedTypeException.php | 2 +- PropertyAccessor.php | 34 +++++++++++++-------------- PropertyPath.php | 10 ++++---- PropertyPathBuilder.php | 6 ++--- Tests/PropertyAccessorTest.php | 14 +++++------ 5 files changed, 33 insertions(+), 33 deletions(-) diff --git a/Exception/UnexpectedTypeException.php b/Exception/UnexpectedTypeException.php index aa59937..ed1308e 100644 --- a/Exception/UnexpectedTypeException.php +++ b/Exception/UnexpectedTypeException.php @@ -26,7 +26,7 @@ class UnexpectedTypeException extends RuntimeException */ public function __construct(mixed $value, PropertyPathInterface $path, int $pathIndex) { - $message = sprintf( + $message = \sprintf( 'PropertyAccessor requires a graph of objects or arrays to operate on, '. 'but it found type "%s" while trying to traverse path "%s" at property "%s".', \gettype($value), diff --git a/PropertyAccessor.php b/PropertyAccessor.php index d4dbaa8..1d96f1d 100644 --- a/PropertyAccessor.php +++ b/PropertyAccessor.php @@ -195,12 +195,12 @@ private static function throwInvalidArgumentException(string $message, array $tr if (preg_match('/^\S+::\S+\(\): Argument #\d+ \(\$\S+\) must be of type (\S+), (\S+) given/', $message, $matches)) { [, $expectedType, $actualType] = $matches; - throw new InvalidArgumentException(sprintf('Expected argument of type "%s", "%s" given at property path "%s".', $expectedType, 'NULL' === $actualType ? 'null' : $actualType, $propertyPath), 0, $previous); + throw new InvalidArgumentException(\sprintf('Expected argument of type "%s", "%s" given at property path "%s".', $expectedType, 'NULL' === $actualType ? 'null' : $actualType, $propertyPath), 0, $previous); } if (preg_match('/^Cannot assign (\S+) to property \S+::\$\S+ of type (\S+)$/', $message, $matches)) { [, $actualType, $expectedType] = $matches; - throw new InvalidArgumentException(sprintf('Expected argument of type "%s", "%s" given at property path "%s".', $expectedType, 'NULL' === $actualType ? 'null' : $actualType, $propertyPath), 0, $previous); + throw new InvalidArgumentException(\sprintf('Expected argument of type "%s", "%s" given at property path "%s".', $expectedType, 'NULL' === $actualType ? 'null' : $actualType, $propertyPath), 0, $previous); } } @@ -216,7 +216,7 @@ public function isReadable(object|array $objectOrArray, string|PropertyPathInter ]; // handle stdClass with properties with a dot in the name - if ($objectOrArray instanceof \stdClass && str_contains($propertyPath, '.') && property_exists($objectOrArray, $propertyPath)) { + if ($objectOrArray instanceof \stdClass && str_contains($propertyPath, '.') && property_exists($objectOrArray, $propertyPath)) { $this->readProperty($zval, $propertyPath, $this->ignoreInvalidProperty); } else { $this->readPropertiesUntil($zval, $propertyPath, $propertyPath->getLength(), $this->ignoreInvalidIndices); @@ -308,13 +308,13 @@ private function readPropertiesUntil(array $zval, PropertyPathInterface $propert if (!$ignoreInvalidIndices && !$isNullSafe) { if (!\is_array($zval[self::VALUE])) { if (!$zval[self::VALUE] instanceof \Traversable) { - throw new NoSuchIndexException(sprintf('Cannot read index "%s" while trying to traverse path "%s".', $property, (string) $propertyPath)); + throw new NoSuchIndexException(\sprintf('Cannot read index "%s" while trying to traverse path "%s".', $property, (string) $propertyPath)); } $zval[self::VALUE] = iterator_to_array($zval[self::VALUE]); } - throw new NoSuchIndexException(sprintf('Cannot read index "%s" while trying to traverse path "%s". Available indices are "%s".', $property, (string) $propertyPath, print_r(array_keys($zval[self::VALUE]), true))); + throw new NoSuchIndexException(\sprintf('Cannot read index "%s" while trying to traverse path "%s". Available indices are "%s".', $property, (string) $propertyPath, print_r(array_keys($zval[self::VALUE]), true))); } if ($i + 1 < $propertyPath->getLength()) { @@ -366,7 +366,7 @@ private function readPropertiesUntil(array $zval, PropertyPathInterface $propert private function readIndex(array $zval, string|int $index): array { if (!$zval[self::VALUE] instanceof \ArrayAccess && !\is_array($zval[self::VALUE])) { - throw new NoSuchIndexException(sprintf('Cannot read index "%s" from object of type "%s" because it doesn\'t implement \ArrayAccess.', $index, get_debug_type($zval[self::VALUE]))); + throw new NoSuchIndexException(\sprintf('Cannot read index "%s" from object of type "%s" because it doesn\'t implement \ArrayAccess.', $index, get_debug_type($zval[self::VALUE]))); } $result = self::RESULT_PROTO; @@ -394,7 +394,7 @@ private function readIndex(array $zval, string|int $index): array private function readProperty(array $zval, string $property, bool $ignoreInvalidProperty = false, bool $isNullSafe = false): array { if (!\is_object($zval[self::VALUE])) { - throw new NoSuchPropertyException(sprintf('Cannot read property "%s" from an array. Maybe you intended to write the property path as "[%1$s]" instead.', $property)); + throw new NoSuchPropertyException(\sprintf('Cannot read property "%s" from an array. Maybe you intended to write the property path as "[%1$s]" instead.', $property)); } $result = self::RESULT_PROTO; @@ -419,7 +419,7 @@ private function readProperty(array $zval, string $property, bool $ignoreInvalid && $object instanceof $trace['class'] && preg_match('/Return value (?:of .*::\w+\(\) )?must be of (?:the )?type (\w+), null returned$/', $e->getMessage(), $matches) ) { - throw new UninitializedPropertyException(sprintf('The method "%s::%s()" returned "null", but expected type "%3$s". Did you forget to initialize a property or to make the return type nullable using "?%3$s"?', get_debug_type($object), $name, $matches[1]), 0, $e); + throw new UninitializedPropertyException(\sprintf('The method "%s::%s()" returned "null", but expected type "%3$s". Did you forget to initialize a property or to make the return type nullable using "?%3$s"?', get_debug_type($object), $name, $matches[1]), 0, $e); } throw $e; @@ -430,11 +430,11 @@ private function readProperty(array $zval, string $property, bool $ignoreInvalid $r = new \ReflectionProperty($class, $name); if ($r->isPublic() && !$r->hasType()) { - throw new UninitializedPropertyException(sprintf('The property "%s::$%s" is not initialized.', $class, $name)); + throw new UninitializedPropertyException(\sprintf('The property "%s::$%s" is not initialized.', $class, $name)); } } catch (\ReflectionException $e) { if (!$ignoreInvalidProperty) { - throw new NoSuchPropertyException(sprintf('Can\'t get a way to read the property "%s" in class "%s".', $property, $class)); + throw new NoSuchPropertyException(\sprintf('Can\'t get a way to read the property "%s" in class "%s".', $property, $class)); } } } @@ -451,7 +451,7 @@ private function readProperty(array $zval, string $property, bool $ignoreInvalid $r = new \ReflectionProperty(str_contains($matches[1], '@anonymous') ? $class : $matches[1], $matches[2]); $type = ($type = $r->getType()) instanceof \ReflectionNamedType ? $type->getName() : (string) $type; - throw new UninitializedPropertyException(sprintf('The property "%s::$%s" is not readable because it is typed "%s". You should initialize it or declare a default value instead.', $matches[1], $r->getName(), $type), 0, $e); + throw new UninitializedPropertyException(\sprintf('The property "%s::$%s" is not readable because it is typed "%s". You should initialize it or declare a default value instead.', $matches[1], $r->getName(), $type), 0, $e); } throw $e; @@ -464,7 +464,7 @@ private function readProperty(array $zval, string $property, bool $ignoreInvalid } elseif ($isNullSafe) { $result[self::VALUE] = null; } elseif (!$ignoreInvalidProperty) { - throw new NoSuchPropertyException(sprintf('Can\'t get a way to read the property "%s" in class "%s".', $property, $class)); + throw new NoSuchPropertyException(\sprintf('Can\'t get a way to read the property "%s" in class "%s".', $property, $class)); } // Objects are always passed around by reference @@ -514,7 +514,7 @@ private function getReadInfo(string $class, string $property): ?PropertyReadInfo private function writeIndex(array $zval, string|int $index, mixed $value): void { if (!$zval[self::VALUE] instanceof \ArrayAccess && !\is_array($zval[self::VALUE])) { - throw new NoSuchIndexException(sprintf('Cannot modify index "%s" in object of type "%s" because it doesn\'t implement \ArrayAccess.', $index, get_debug_type($zval[self::VALUE]))); + throw new NoSuchIndexException(\sprintf('Cannot modify index "%s" in object of type "%s" because it doesn\'t implement \ArrayAccess.', $index, get_debug_type($zval[self::VALUE]))); } $zval[self::REF][$index] = $value; @@ -528,7 +528,7 @@ private function writeIndex(array $zval, string|int $index, mixed $value): void private function writeProperty(array $zval, string $property, mixed $value, bool $recursive = false): void { if (!\is_object($zval[self::VALUE])) { - throw new NoSuchPropertyException(sprintf('Cannot write property "%s" to an array. Maybe you should write the property path as "[%1$s]" instead?', $property)); + throw new NoSuchPropertyException(\sprintf('Cannot write property "%s" to an array. Maybe you should write the property path as "[%1$s]" instead?', $property)); } $object = $zval[self::VALUE]; @@ -553,7 +553,7 @@ private function writeProperty(array $zval, string $property, mixed $value, bool throw new NoSuchPropertyException(implode('. ', $mutator->getErrors()).'.'); } - throw new NoSuchPropertyException(sprintf('Could not determine access type for property "%s" in class "%s".', $property, get_debug_type($object))); + throw new NoSuchPropertyException(\sprintf('Could not determine access type for property "%s" in class "%s".', $property, get_debug_type($object))); } } catch (\TypeError $e) { if ($recursive || !$value instanceof \DateTimeInterface || !\in_array($value::class, ['DateTime', 'DateTimeImmutable'], true) || __FILE__ !== ($e->getTrace()[0]['file'] ?? null)) { @@ -646,7 +646,7 @@ private function isPropertyWritable(object $object, string $property): bool $mutatorForArray = $this->getWriteInfo($object::class, $property, []); if (PropertyWriteInfo::TYPE_PROPERTY === $mutatorForArray->getType()) { - return $mutatorForArray->getVisibility() === 'public'; + return 'public' === $mutatorForArray->getVisibility(); } if (PropertyWriteInfo::TYPE_NONE !== $mutatorForArray->getType()) { @@ -696,7 +696,7 @@ private function getPropertyPath(string|PropertyPath $propertyPath): PropertyPat public static function createCache(string $namespace, int $defaultLifetime, string $version, ?LoggerInterface $logger = null): AdapterInterface { if (!class_exists(ApcuAdapter::class)) { - throw new \LogicException(sprintf('The Symfony Cache component must be installed to use "%s()".', __METHOD__)); + throw new \LogicException(\sprintf('The Symfony Cache component must be installed to use "%s()".', __METHOD__)); } if (!ApcuAdapter::isSupported()) { diff --git a/PropertyPath.php b/PropertyPath.php index a94e960..e797ab7 100644 --- a/PropertyPath.php +++ b/PropertyPath.php @@ -122,7 +122,7 @@ public function __construct(self|string $propertyPath) } if ('' !== $remaining) { - throw new InvalidPropertyPathException(sprintf('Could not parse property path "%s". Unexpected token "%s" at position %d.', $propertyPath, $remaining[0], $position)); + throw new InvalidPropertyPathException(\sprintf('Could not parse property path "%s". Unexpected token "%s" at position %d.', $propertyPath, $remaining[0], $position)); } $this->length = \count($this->elements); @@ -171,7 +171,7 @@ public function getElements(): array public function getElement(int $index): string { if (!isset($this->elements[$index])) { - throw new OutOfBoundsException(sprintf('The index "%s" is not within the property path.', $index)); + throw new OutOfBoundsException(\sprintf('The index "%s" is not within the property path.', $index)); } return $this->elements[$index]; @@ -180,7 +180,7 @@ public function getElement(int $index): string public function isProperty(int $index): bool { if (!isset($this->isIndex[$index])) { - throw new OutOfBoundsException(sprintf('The index "%s" is not within the property path.', $index)); + throw new OutOfBoundsException(\sprintf('The index "%s" is not within the property path.', $index)); } return !$this->isIndex[$index]; @@ -189,7 +189,7 @@ public function isProperty(int $index): bool public function isIndex(int $index): bool { if (!isset($this->isIndex[$index])) { - throw new OutOfBoundsException(sprintf('The index "%s" is not within the property path.', $index)); + throw new OutOfBoundsException(\sprintf('The index "%s" is not within the property path.', $index)); } return $this->isIndex[$index]; @@ -198,7 +198,7 @@ public function isIndex(int $index): bool public function isNullSafe(int $index): bool { if (!isset($this->isNullSafe[$index])) { - throw new OutOfBoundsException(sprintf('The index "%s" is not within the property path.', $index)); + throw new OutOfBoundsException(\sprintf('The index "%s" is not within the property path.', $index)); } return $this->isNullSafe[$index]; diff --git a/PropertyPathBuilder.php b/PropertyPathBuilder.php index e2ac3fe..8dd6106 100644 --- a/PropertyPathBuilder.php +++ b/PropertyPathBuilder.php @@ -86,7 +86,7 @@ public function appendProperty(string $name) public function remove(int $offset, int $length = 1) { if (!isset($this->elements[$offset])) { - throw new OutOfBoundsException(sprintf('The offset "%s" is not within the property path.', $offset)); + throw new OutOfBoundsException(\sprintf('The offset "%s" is not within the property path.', $offset)); } $this->resize($offset, $length, 0); @@ -137,7 +137,7 @@ public function replace(int $offset, int $length, PropertyPathInterface|string $ public function replaceByIndex(int $offset, ?string $name = null) { if (!isset($this->elements[$offset])) { - throw new OutOfBoundsException(sprintf('The offset "%s" is not within the property path.', $offset)); + throw new OutOfBoundsException(\sprintf('The offset "%s" is not within the property path.', $offset)); } if (null !== $name) { @@ -157,7 +157,7 @@ public function replaceByIndex(int $offset, ?string $name = null) public function replaceByProperty(int $offset, ?string $name = null) { if (!isset($this->elements[$offset])) { - throw new OutOfBoundsException(sprintf('The offset "%s" is not within the property path.', $offset)); + throw new OutOfBoundsException(\sprintf('The offset "%s" is not within the property path.', $offset)); } if (null !== $name) { diff --git a/Tests/PropertyAccessorTest.php b/Tests/PropertyAccessorTest.php index d00c4e7..651b119 100644 --- a/Tests/PropertyAccessorTest.php +++ b/Tests/PropertyAccessorTest.php @@ -149,7 +149,7 @@ public function testGetValueThrowsExceptionIfUninitializedPropertyWithGetterOfAn $this->expectException(UninitializedPropertyException::class); $this->expectExceptionMessage('The method "class@anonymous::getUninitialized()" returned "null", but expected type "array". Did you forget to initialize a property or to make the return type nullable using "?array"?'); - $object = new class() { + $object = new class { private $uninitialized; public function getUninitialized(): array @@ -166,7 +166,7 @@ public function testGetValueThrowsExceptionIfUninitializedNotNullablePropertyWit $this->expectException(UninitializedPropertyException::class); $this->expectExceptionMessage('The property "class@anonymous::$uninitialized" is not readable because it is typed "string". You should initialize it or declare a default value instead.'); - $object = new class() { + $object = new class { private string $uninitialized; public function getUninitialized(): string @@ -183,7 +183,7 @@ public function testGetValueThrowsExceptionIfUninitializedPropertyOfAnonymousCla $this->expectException(UninitializedPropertyException::class); $this->expectExceptionMessage('The property "class@anonymous::$uninitialized" is not readable because it is typed "string". You should initialize it or declare a default value instead.'); - $object = new class() { + $object = new class { public string $uninitialized; }; @@ -211,7 +211,7 @@ public function testGetValueThrowsExceptionIfUninitializedPropertyWithGetterOfAn $this->expectException(UninitializedPropertyException::class); $this->expectExceptionMessage('The method "stdClass@anonymous::getUninitialized()" returned "null", but expected type "array". Did you forget to initialize a property or to make the return type nullable using "?array"?'); - $object = new class() extends \stdClass { + $object = new class extends \stdClass { private $uninitialized; public function getUninitialized(): array @@ -228,7 +228,7 @@ public function testGetValueThrowsExceptionIfUninitializedPropertyWithGetterOfAn $this->expectException(UninitializedPropertyException::class); $this->expectExceptionMessage('The method "Symfony\Component\PropertyAccess\Tests\Fixtures\UninitializedPrivateProperty@anonymous::getUninitialized()" returned "null", but expected type "array". Did you forget to initialize a property or to make the return type nullable using "?array"?'); - $object = new class() extends UninitializedPrivateProperty { + $object = new class extends UninitializedPrivateProperty { }; $this->propertyAccessor->getValue($object, 'uninitialized'); @@ -1010,7 +1010,7 @@ public function testIsReadableWithMissingPropertyAndLazyGhost() private function createUninitializedObjectPropertyGhost(): UninitializedObjectProperty { if (!class_exists(ProxyHelper::class)) { - $this->markTestSkipped(sprintf('Class "%s" is required to run this test.', ProxyHelper::class)); + $this->markTestSkipped(\sprintf('Class "%s" is required to run this test.', ProxyHelper::class)); } $class = 'UninitializedObjectPropertyGhost'; @@ -1072,7 +1072,7 @@ public function testSetValueWithAsymmetricVisibility(string $propertyPath, ?stri } /** - * @return iterable + * @return iterable */ public static function setValueWithAsymmetricVisibilityDataProvider(): iterable { From 181a37873d1d4582e20f5ba7d9bb04f651642d33 Mon Sep 17 00:00:00 2001 From: Fabien Potencier Date: Sat, 12 Jul 2025 11:35:41 +0200 Subject: [PATCH 3/4] Fix @var phpdoc --- PropertyPath.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/PropertyPath.php b/PropertyPath.php index e797ab7..71b90fc 100644 --- a/PropertyPath.php +++ b/PropertyPath.php @@ -72,7 +72,7 @@ public function __construct(self|string $propertyPath) { // Can be used as copy constructor if ($propertyPath instanceof self) { - /* @var PropertyPath $propertyPath */ + /** @var PropertyPath $propertyPath */ $this->elements = $propertyPath->elements; $this->length = $propertyPath->length; $this->isIndex = $propertyPath->isIndex; From 074de0e8b6dc8a305a464add9a832608178f20bb Mon Sep 17 00:00:00 2001 From: Gina Peter Banyard Date: Tue, 24 Jun 2025 14:17:47 +0100 Subject: [PATCH 4/4] Fix various bool-type coercions --- PropertyAccessorBuilder.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/PropertyAccessorBuilder.php b/PropertyAccessorBuilder.php index 5dc6e4f..431a7f4 100644 --- a/PropertyAccessorBuilder.php +++ b/PropertyAccessorBuilder.php @@ -128,7 +128,7 @@ public function disableMagicSet(): static */ public function isMagicCallEnabled(): bool { - return (bool) ($this->magicMethods & PropertyAccessor::MAGIC_CALL); + return $this->magicMethods & PropertyAccessor::MAGIC_CALL; } /**