From 04a0d156329153f7cf4024b3764a884395c2e024 Mon Sep 17 00:00:00 2001 From: Alies Lapatsin Date: Sun, 29 Jan 2023 12:58:03 +0100 Subject: [PATCH 1/2] Add a test for Legacy attribute accessor --- tests/Models/User.php | 5 +++++ tests/acceptance/EloquentModelPropertyTypes.feature | 11 +++++++++++ 2 files changed, 16 insertions(+) diff --git a/tests/Models/User.php b/tests/Models/User.php index 56511449..734ce235 100644 --- a/tests/Models/User.php +++ b/tests/Models/User.php @@ -60,4 +60,9 @@ public function scopeActive($query) { return $query->where('active', 1); } + + public function getFirstNameUsingLegacyAccessorAttribute(): string + { + return $this->name; + } } diff --git a/tests/acceptance/EloquentModelPropertyTypes.feature b/tests/acceptance/EloquentModelPropertyTypes.feature index de2f887a..96e63c4a 100644 --- a/tests/acceptance/EloquentModelPropertyTypes.feature +++ b/tests/acceptance/EloquentModelPropertyTypes.feature @@ -57,3 +57,14 @@ Feature: Eloquent Model property types """ When I run Psalm Then I see no errors + + Scenario: Legacy Attribute accessor + Given I have the following code + """ + function test(User $user): string + { + return $user->first_name_using_legacy_accessor; + } + """ + When I run Psalm + Then I see no errors From 096aca41244fc28244c321b3c57b60417043f03a Mon Sep 17 00:00:00 2001 From: Alies Lapatsin Date: Sun, 29 Jan 2023 22:47:39 +0100 Subject: [PATCH 2/2] Optimize Eloquent property checks --- src/Handlers/Eloquent/ModelMethodHandler.php | 8 ++----- .../Eloquent/ModelPropertyAccessorHandler.php | 24 +++++++++++++++++++ 2 files changed, 26 insertions(+), 6 deletions(-) diff --git a/src/Handlers/Eloquent/ModelMethodHandler.php b/src/Handlers/Eloquent/ModelMethodHandler.php index aa9629af..e739842c 100644 --- a/src/Handlers/Eloquent/ModelMethodHandler.php +++ b/src/Handlers/Eloquent/ModelMethodHandler.php @@ -68,12 +68,8 @@ public static function getMethodReturnType(MethodReturnTypeProviderEvent $event) return null; } - /** - * @param FileManipulation[] $file_replacements - * - * @return void - */ - public static function afterClassLikeVisit(AfterClassLikeVisitEvent $event) + /** @inheritDoc */ + public static function afterClassLikeVisit(AfterClassLikeVisitEvent $event): void { $storage = $event->getStorage(); if ( diff --git a/src/Handlers/Eloquent/ModelPropertyAccessorHandler.php b/src/Handlers/Eloquent/ModelPropertyAccessorHandler.php index 069d987c..bd6757d6 100644 --- a/src/Handlers/Eloquent/ModelPropertyAccessorHandler.php +++ b/src/Handlers/Eloquent/ModelPropertyAccessorHandler.php @@ -32,6 +32,10 @@ public static function doesPropertyExist(PropertyExistenceProviderEvent $event): return null; } + if (self::hasNativeProperty($event->getFqClasslikeName(), $event->getPropertyName())) { + return true; + } + $codebase = $source->getCodebase(); if (self::accessorExists($codebase, $event->getFqClasslikeName(), $event->getPropertyName())) { @@ -47,6 +51,10 @@ public static function isPropertyVisible(PropertyVisibilityProviderEvent $event) return null; } + if (self::hasNativeProperty($event->getFqClasslikeName(), $event->getPropertyName())) { + return null; + } + $codebase = $event->getSource()->getCodebase(); if (self::accessorExists($codebase, $event->getFqClasslikeName(), $event->getPropertyName())) { @@ -64,6 +72,11 @@ public static function getPropertyType(PropertyTypeProviderEvent $event): ?Type\ return null; } + // skip for real properties like $hidden, $casts + if (self::hasNativeProperty($event->getFqClasslikeName(), $event->getPropertyName())) { + return null; + } + $codebase = $source->getCodebase(); $fq_classlike_name = $event->getFqClasslikeName(); $property_name = $event->getPropertyName(); @@ -76,6 +89,17 @@ public static function getPropertyType(PropertyTypeProviderEvent $event): ?Type\ return null; } + private static function hasNativeProperty(string $fqcn, string $property_name): bool + { + try { + new \ReflectionProperty($fqcn, $property_name); + } catch (\ReflectionException $exception) { + return false; + } + + return true; + } + private static function accessorExists(Codebase $codebase, string $fq_classlike_name, string $property_name): bool { return $codebase->methodExists($fq_classlike_name . '::get' . str_replace('_', '', $property_name) . 'Attribute');