diff --git a/Exception/InvalidTypeException.php b/Exception/InvalidTypeException.php index 07f6070..c22ad5a 100644 --- a/Exception/InvalidTypeException.php +++ b/Exception/InvalidTypeException.php @@ -25,7 +25,7 @@ public function __construct( ?\Throwable $previous = null, ) { parent::__construct( - sprintf('Expected argument of type "%s", "%s" given at property path "%s".', $expectedType, 'NULL' === $actualType ? 'null' : $actualType, $propertyPath), + \sprintf('Expected argument of type "%s", "%s" given at property path "%s".', $expectedType, 'NULL' === $actualType ? 'null' : $actualType, $propertyPath), previous: $previous, ); } 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 045d7ed..d34e19f 100644 --- a/PropertyAccessor.php +++ b/PropertyAccessor.php @@ -109,6 +109,11 @@ 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))) { @@ -216,7 +221,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); @@ -297,13 +302,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()) { @@ -355,7 +360,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; @@ -383,7 +388,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; @@ -408,7 +413,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; @@ -419,11 +424,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)); } } } @@ -440,7 +445,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; @@ -453,7 +458,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 @@ -503,7 +508,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; @@ -517,7 +522,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]; @@ -542,7 +547,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)) { @@ -635,7 +640,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()) { @@ -685,7 +690,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/PropertyAccessorInterface.php b/PropertyAccessorInterface.php index 59f204f..2e25e9e 100644 --- a/PropertyAccessorInterface.php +++ b/PropertyAccessorInterface.php @@ -39,8 +39,6 @@ interface PropertyAccessorInterface * * If neither is found, an exception is thrown. * - * @return void - * * @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 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 2c439f0..4c40c68 100644 --- a/PropertyPathBuilder.php +++ b/PropertyPathBuilder.php @@ -78,7 +78,7 @@ public function appendProperty(string $name): void public function remove(int $offset, int $length = 1): void { 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); @@ -125,7 +125,7 @@ public function replace(int $offset, int $length, PropertyPathInterface|string $ public function replaceByIndex(int $offset, ?string $name = null): void { 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) { @@ -143,7 +143,7 @@ public function replaceByIndex(int $offset, ?string $name = null): void public function replaceByProperty(int $offset, ?string $name = null): void { 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/PropertyAccessorCollectionTestCase.php b/Tests/PropertyAccessorCollectionTestCase.php index f972603..43f11a4 100644 --- a/Tests/PropertyAccessorCollectionTestCase.php +++ b/Tests/PropertyAccessorCollectionTestCase.php @@ -165,7 +165,7 @@ public function testSetValueFailsIfNoAdderNorRemoverFound() ->willReturn($axesBefore); $this->expectException(NoSuchPropertyException::class); - $this->expectExceptionMessageMatches('/Could not determine access type for property "axes" in class "Mock_PropertyAccessorCollectionTestCase_CarNoAdderAndRemover_[^"]*"./'); + $this->expectExceptionMessage(\sprintf('Could not determine access type for property "axes" in class "%s".', $car::class)); $this->propertyAccessor->setValue($car, 'axes', $axesAfter); } diff --git a/Tests/PropertyAccessorTest.php b/Tests/PropertyAccessorTest.php index 6a67563..bb8043d 100644 --- a/Tests/PropertyAccessorTest.php +++ b/Tests/PropertyAccessorTest.php @@ -148,7 +148,7 @@ public function testGetValueThrowsExceptionIfUninitializedPropertyWithGetter() public function testGetValueThrowsExceptionIfUninitializedPropertyWithGetterOfAnonymousClass() { - $object = new class() { + $object = new class { private $uninitialized; public function getUninitialized(): array @@ -165,7 +165,7 @@ public function getUninitialized(): array public function testGetValueThrowsExceptionIfUninitializedNotNullablePropertyWithGetterOfAnonymousClass() { - $object = new class() { + $object = new class { private string $uninitialized; public function getUninitialized(): string @@ -182,7 +182,7 @@ public function getUninitialized(): string public function testGetValueThrowsExceptionIfUninitializedPropertyOfAnonymousClass() { - $object = new class() { + $object = new class { public string $uninitialized; }; @@ -210,7 +210,7 @@ public function testGetValueThrowsExceptionIfUninitializedNotNullablePropertyWit public function testGetValueThrowsExceptionIfUninitializedPropertyWithGetterOfAnonymousStdClass() { - $object = new class() extends \stdClass { + $object = new class extends \stdClass { private $uninitialized; public function getUninitialized(): array @@ -227,7 +227,7 @@ public function getUninitialized(): array public function testGetValueThrowsExceptionIfUninitializedPropertyWithGetterOfAnonymousChildClass() { - $object = new class() extends UninitializedPrivateProperty { + $object = new class extends UninitializedPrivateProperty { }; $this->expectException(UninitializedPropertyException::class); @@ -832,7 +832,7 @@ public function testWriteToSingularPropertyWhilePluralOneExists() $this->propertyAccessor->setValue($object, 'email', 'test@email.com'); self::assertEquals('test@email.com', $object->getEmail()); - self::assertEmpty($object->getEmails()); + self::assertSame([], $object->getEmails()); } public function testWriteToPluralPropertyWhileSingularOneExists() @@ -1032,20 +1032,24 @@ 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)); - } + if (\PHP_VERSION_ID < 80400) { + if (!class_exists(ProxyHelper::class)) { + $this->markTestSkipped(\sprintf('Class "%s" is required to run this test.', ProxyHelper::class)); + } - $class = 'UninitializedObjectPropertyGhost'; + $class = 'UninitializedObjectPropertyGhost'; - if (!class_exists($class)) { - eval('class '.$class.ProxyHelper::generateLazyGhost(new \ReflectionClass(UninitializedObjectProperty::class))); - } + if (!class_exists($class)) { + eval('class '.$class.ProxyHelper::generateLazyGhost(new \ReflectionClass(UninitializedObjectProperty::class))); + } + + $this->assertTrue(class_exists($class)); - $this->assertTrue(class_exists($class)); + return $class::createLazyGhost(initializer: function ($instance) { + }); + } - return $class::createLazyGhost(initializer: function ($instance) { - }); + return (new \ReflectionClass(UninitializedObjectProperty::class))->newLazyGhost(fn () => null); } /** @@ -1095,7 +1099,7 @@ public function testSetValueWithAsymmetricVisibility(string $propertyPath, ?stri } /** - * @return iterable + * @return iterable */ public static function setValueWithAsymmetricVisibilityDataProvider(): iterable {