diff --git a/extension.neon b/extension.neon index 7de74f0..174f4cf 100644 --- a/extension.neon +++ b/extension.neon @@ -19,6 +19,10 @@ services: class: Nextras\OrmPhpStan\Reflection\EntityRelationshipPropertyReflectionExtension tags: - phpstan.broker.propertiesClassReflectionExtension + - + class: Nextras\OrmPhpStan\Reflection\EntityDateTimePropertyReflectionExtension + tags: + - phpstan.broker.propertiesClassReflectionExtension - class: Nextras\OrmPhpStan\Rules\SetValueMethodRule tags: diff --git a/src/Reflection/EntityDateTimePropertyReflectionExtension.php b/src/Reflection/EntityDateTimePropertyReflectionExtension.php new file mode 100644 index 0000000..03428ef --- /dev/null +++ b/src/Reflection/EntityDateTimePropertyReflectionExtension.php @@ -0,0 +1,62 @@ +annotationsExtension = $annotationsExtension; + } + + + public function hasProperty(ClassReflection $classReflection, string $propertyName): bool + { + $hasProperty = $this->annotationsExtension->hasProperty($classReflection, $propertyName); + if (!$hasProperty) { + return false; + } + + $interfaces = array_map(function (ClassReflection $interface) { + return $interface->getName(); + }, $classReflection->getInterfaces()); + if (!in_array(IEntity::class, $interfaces, true)) { + return false; + } + + $property = $this->annotationsExtension->getProperty($classReflection, $propertyName); + $propertyType = TypeCombinator::removeNull($property->getReadableType()); // remove null to be properly match subtype + $dateTimeType = new ObjectType(\DateTimeImmutable::class); + $hasDateTime = $dateTimeType->isSuperTypeOf($propertyType)->yes(); + + return $hasDateTime; + } + + + public function getProperty(ClassReflection $classReflection, string $propertyName): PropertyReflection + { + $property = $this->annotationsExtension->getProperty($classReflection, $propertyName); + return new AnnotationPropertyReflection( + $property->getDeclaringClass(), + $property->getReadableType(), + TypeCombinator::union($property->getWritableType(), new StringType()), + $property->isReadable(), + $property->isWritable() + ); + } +} diff --git a/tests/expected.txt b/tests/expected.txt index ca754bc..abd463d 100644 --- a/tests/expected.txt +++ b/tests/expected.txt @@ -1,11 +1,12 @@ /testbox/Rules/Test.php:21:Entity NextrasTests\OrmPhpStan\Rules\Entity: property $age (int) does not accept string. /testbox/Rules/Test.php:22:Entity NextrasTests\OrmPhpStan\Rules\Entity: property $description (string) does not accept int. -/testbox/Rules/Test.php:23:Entity NextrasTests\OrmPhpStan\Rules\Entity: property $createdAt (DateTimeImmutable) does not accept DateTime. +/testbox/Rules/Test.php:23:Entity NextrasTests\OrmPhpStan\Rules\Entity: property $createdAt (DateTimeImmutable|string) does not accept DateTime. /testbox/Rules/Test.php:24:Entity NextrasTests\OrmPhpStan\Rules\Entity has no $foo property. /testbox/Rules/Test.php:25:Entity NextrasTests\OrmPhpStan\Rules\Entity: property $read is read-only. /testbox/Rules/Test.php:26:Entity NextrasTests\OrmPhpStan\Rules\Entity: property $age (int) does not accept string. /testbox/Types/CollectionTypesTest.php:15:Parameter #1 $author of method NextrasTests\OrmPhpStan\Types\CollectionTypesTest::takeAuthor() expects NextrasTests\OrmPhpStan\Types\Author, NextrasTests\OrmPhpStan\Types\Author|null given. /testbox/Types/CollectionTypesTest.php:16:Parameter #1 $author of method NextrasTests\OrmPhpStan\Types\CollectionTypesTest::takeAuthor() expects NextrasTests\OrmPhpStan\Types\Author, NextrasTests\OrmPhpStan\Types\Author|null given. +/testbox/Types/DateTimePropertyTypesTest.php:9:Property NextrasTests\OrmPhpStan\Types\Book::$date (DateTimeImmutable|string|null) does not accept int. /testbox/Types/RelationshipPropertyTypesTest.php:11:Parameter #1 $int of method NextrasTests\OrmPhpStan\Types\RelationshipPropertyTypesTest::takeInt() expects int, NextrasTests\OrmPhpStan\Types\Author given. /testbox/Types/RelationshipPropertyTypesTest.php:13:Parameter #1 $int of method NextrasTests\OrmPhpStan\Types\RelationshipPropertyTypesTest::takeInt() expects int, NextrasTests\OrmPhpStan\Types\Author given. /testbox/Types/RelationshipPropertyTypesTest.php:14:Property NextrasTests\OrmPhpStan\Types\Book::$author (int|NextrasTests\OrmPhpStan\Types\Author) does not accept NextrasTests\OrmPhpStan\Types\Book. diff --git a/tests/testbox/Types/DateTimePropertyTypesTest.php b/tests/testbox/Types/DateTimePropertyTypesTest.php new file mode 100644 index 0000000..c5cc11e --- /dev/null +++ b/tests/testbox/Types/DateTimePropertyTypesTest.php @@ -0,0 +1,24 @@ +date = 1; + } + + + public function testOk(Book $book): void + { + $book->date = 'now'; + $this->takeNullableDateTime($book->date); + $book->date = null; + } + + + private function takeNullableDateTime(?\DateTimeImmutable $data): void + { + } +} diff --git a/tests/testbox/Types/fixtures/Book.php b/tests/testbox/Types/fixtures/Book.php index cfde1d9..fcf9c5f 100644 --- a/tests/testbox/Types/fixtures/Book.php +++ b/tests/testbox/Types/fixtures/Book.php @@ -4,6 +4,7 @@ /** * @property Author $author {m:1 Author::$books} + * @property \DateTimeImmutable|null $date */ class Book extends \Nextras\Orm\Entity\Entity {