From e8a7bb9d31fc19c5ec23d8331bd7c95affd13a83 Mon Sep 17 00:00:00 2001 From: Dave Liddament Date: Mon, 30 May 2022 15:01:18 +0100 Subject: [PATCH] ADD InjectableVersion Attribute --- README.md | 73 ++++++++++++++++++- .../InjectableVersionCheckOnMethod.php | 39 ++++++++++ .../InjectableVersionOnClass.php | 35 +++++++++ ...jectableVersionOnExtendsThenImplements.php | 31 ++++++++ .../InjectableVersionOnInterface.php | 27 +++++++ ...bleVersionRulesIgnoredForTestNamespace.php | 31 ++++++++ .../IterableInjectableVersion.php | 35 +++++++++ ...nheritanceInjectableVersionOnInterface.php | 37 ++++++++++ ...fInheritanceNoInjectableVersionOnClass.php | 35 +++++++++ ...eritanceNoInjectableVersionOnInterface.php | 35 +++++++++ src/CheckInjectableVersion.php | 12 +++ src/InjectableVersion.php | 12 +++ 12 files changed, 401 insertions(+), 1 deletion(-) create mode 100644 examples/injectableVersion/InjectableVersionCheckOnMethod.php create mode 100644 examples/injectableVersion/InjectableVersionOnClass.php create mode 100644 examples/injectableVersion/InjectableVersionOnExtendsThenImplements.php create mode 100644 examples/injectableVersion/InjectableVersionOnInterface.php create mode 100644 examples/injectableVersion/InjectableVersionRulesIgnoredForTestNamespace.php create mode 100644 examples/injectableVersion/IterableInjectableVersion.php create mode 100644 examples/injectableVersion/MultipleLevelsOfInheritanceInjectableVersionOnInterface.php create mode 100644 examples/injectableVersion/MultipleLevelsOfInheritanceNoInjectableVersionOnClass.php create mode 100644 examples/injectableVersion/MultipleLevelsOfInheritanceNoInjectableVersionOnInterface.php create mode 100644 src/CheckInjectableVersion.php create mode 100644 src/InjectableVersion.php diff --git a/README.md b/README.md index a01d623..31432e9 100644 --- a/README.md +++ b/README.md @@ -5,6 +5,7 @@ The intention, at least initially, is that these extra language features are enf **Language feature added:** - [Friend](#friend) +- [InjectableVersion](#injectableVersion) - [Package](#package) - [Sealed](#sealed) - [TestTag](#testtag) @@ -16,8 +17,9 @@ The intention, at least initially, is that these extra language features are enf - [PHPStan](#phpstan) - [Psalm](#psalm) - [New Language Features](#new-language-features) - - [Package](#package) - [Friend](#friend) + - [InjectableVersion](#injectableVersion) + - [Package](#package) - [Sealed](#sealed) - [TestTag](#testtag) - [Further examples](#further-examples) @@ -271,6 +273,75 @@ NOTES: - Assume all classes within a namespace is test code. See [namespace config option](https://github.com/DaveLiddament/phpstan-php-language-extensions#exclude-checks-based-on-test-namespace). + +## InjectableVersion + +The `#[InjectableVersion]` is used in conjunction with dependency injection. +`#[InjectableVersion]` is applied to a class or interface. +It denotes that it is this version and not any classes that implement/extend that should be used in the codebase. + +E.g. + +```php + +#[InjectableVersion] +class PersonRepository {...} // This is the version that should be type hinted in constructors. + +class DoctrinePersonRepository extends PersonRepository {...} + +class PersonCreator { + public function __construct( + private PersonRepository $personRepository, // OK - using the injectable version + ) +} +class PersonUpdater { + public function __construct( + private DoctrinePersonRepository $personRepository, // ERROR - not using the InjectableVersion + ) +} +``` + +This also works for collections: + +```php + +#[InjectableVersion] +interface Validator {...} // This is the version that should be type hinted in constructors. + +class NameValidator implements Validator {...} +class AddressValidator implements Validator {...} + +class PersonValidator { + /** @param Validator[] $validators */ + public function __construct( + private array $validators, // OK - using the injectable version + ) +} +``` + +By default, only constructor arguments are checked. Most DI should be done via constructor injection. + +In cases where dependencies are injected by methods that aren't constructors, the method must be marked with a `#[CheckInjectableVersion]`: + +```php + +#[InjectableVersion] +interface Logger {...} + +class FileLogger implements Logger {...} + +class MyService +{ + #[CheckInjectableVersion] + public function setLogger(Logger $logger): void {} // OK - Injectable Version injected + + public function addLogger(FileLogger $logger): void {} // No issue raised because addLogger doesn't have the #[CheckInjectableVersion] attribute. +} + +``` + + + ## Further examples More detailed examples of how to use attributes is found in [examples](examples/). diff --git a/examples/injectableVersion/InjectableVersionCheckOnMethod.php b/examples/injectableVersion/InjectableVersionCheckOnMethod.php new file mode 100644 index 0000000..915682d --- /dev/null +++ b/examples/injectableVersion/InjectableVersionCheckOnMethod.php @@ -0,0 +1,39 @@ +repository = $repository; + } +} + +class InjectingWrongVersion +{ + public Repository $repository; + + #[CheckInjectableVersion] + public function setRepository(DoctrineRepository $repository): void // ERROR + { + $this->repository = $repository; + } +} diff --git a/examples/injectableVersion/InjectableVersionOnClass.php b/examples/injectableVersion/InjectableVersionOnClass.php new file mode 100644 index 0000000..e40d8da --- /dev/null +++ b/examples/injectableVersion/InjectableVersionOnClass.php @@ -0,0 +1,35 @@ +