diff --git a/README.md b/README.md index 6efb544..2f5b326 100644 --- a/README.md +++ b/README.md @@ -18,6 +18,7 @@ This extension provides following features: * Magic [Nette\Object and Nette\SmartObject](https://doc.nette.org/en/2.4/php-language-enhancements) properties * Event listeners through the `on*` properties * Defines early terminating method calls for Presenter methods to prevent `Undefined variable` errors +* `@inject` annotation and `#[Nette\DI\Attributes\Inject]` attribute initialize properties It also contains these framework-specific rules (can be enabled separately): diff --git a/composer.json b/composer.json index 78a1277..3437225 100644 --- a/composer.json +++ b/composer.json @@ -19,6 +19,7 @@ }, "require-dev": { "nette/application": "^3.0", + "nette/di": "^2.3.0 || ^3.0.0", "nette/forms": "^3.0", "nette/utils": "^2.3.0 || ^3.0.0", "nikic/php-parser": "^4.13.2", diff --git a/src/Rule/Nette/PresenterInjectedPropertiesExtension.php b/src/Rule/Nette/PresenterInjectedPropertiesExtension.php index c724066..16119fb 100644 --- a/src/Rule/Nette/PresenterInjectedPropertiesExtension.php +++ b/src/Rule/Nette/PresenterInjectedPropertiesExtension.php @@ -2,6 +2,8 @@ namespace PHPStan\Rule\Nette; +use Nette\DI\Attributes\Inject; +use PHPStan\Php\PhpVersion; use PHPStan\Reflection\PropertyReflection; use PHPStan\Rules\Properties\ReadWritePropertiesExtension; use function strpos; @@ -9,6 +11,14 @@ class PresenterInjectedPropertiesExtension implements ReadWritePropertiesExtension { + /** @var PhpVersion */ + private $phpVersion; + + public function __construct(PhpVersion $phpVersion) + { + $this->phpVersion = $phpVersion; + } + public function isAlwaysRead(PropertyReflection $property, string $propertyName): bool { return false; @@ -21,8 +31,20 @@ public function isAlwaysWritten(PropertyReflection $property, string $propertyNa public function isInitialized(PropertyReflection $property, string $propertyName): bool { - return $property->isPublic() && - strpos($property->getDocComment() ?? '', '@inject') !== false; + if (!$property->isPublic()) { + return false; + } + + if (strpos($property->getDocComment() ?? '', '@inject') !== false) { + return true; + } + + $nativeProperty = $property->getDeclaringClass()->getNativeProperty($propertyName)->getNativeReflection(); + if ($this->phpVersion->getVersionId() >= 80000 && $nativeProperty->getAttributes(Inject::class) !== []) { + return true; + } + + return false; } } diff --git a/tests/Rule/Nette/PresenterInjectedPropertiesExtensionTest.php b/tests/Rule/Nette/PresenterInjectedPropertiesExtensionTest.php index e05b89e..6241a54 100644 --- a/tests/Rule/Nette/PresenterInjectedPropertiesExtensionTest.php +++ b/tests/Rule/Nette/PresenterInjectedPropertiesExtensionTest.php @@ -14,9 +14,15 @@ public function dataTopics(): array self::markTestSkipped('Only for PHP 7.4+'); } - return [ + $topics = [ ['presenterInject'], ]; + + if (PHP_VERSION_ID >= 80000) { + $topics[] = ['presenterInjectAttribute']; + } + + return $topics; } public function getDataPath(): string diff --git a/tests/Rule/Nette/data/presenterInjectAttribute.php b/tests/Rule/Nette/data/presenterInjectAttribute.php new file mode 100644 index 0000000..e4a31f6 --- /dev/null +++ b/tests/Rule/Nette/data/presenterInjectAttribute.php @@ -0,0 +1,14 @@ +