This mini-article describes the methods of dependency injection, what each method implies in terms of both performance and simplicity.
- Constructor injection
This method is half manual and quite well known. Declare your classes' dependencies as constructor and pass the dependencies when you construct your instances. This approach completely skips the automation around detection of injections - but performs the best of all methods.
Can be combined with Singleton patterns, e.g. you can construct a Singleton using this method and this instance will be held in memory and returned when a fresh instance is requested.
Note that Extbase will create and pass constructor arguments when those constructor arguments are themselves Singletons (prototypes will cause an error), when you create the class without constructor arguments - however, this won't work if your "root" class is itself a Singleton which is why this article only mentions the manual passing of constructor arguments (as the only fully compatible, automation and reflection risk-free method, and coincidentally the best performing one too).
Addendum from @dfeyer: Constructor dependencies also have the benefit that you can actually access your dependencies in the constructor itself to initialize - and you will clearly see if you have architecture problems, e.g. when your constructor method grows too big - which are both great points!
- Injection methods
This method is the better of the two fully automatic dependency injections. It works by using native
PHP functions to read a list of methods on a class, parses the list of methods to detect ones with
inject prefix, reads the required type (without looking at PHPDoc!) and then attempts to build
the instance to inject (which then recurses down through the dependencies until all are injected).
The result of the analysis of method availability gets cached.
The benefit of this method is that the injection method can be immediately called after thawing the class reflection from cache - because all we need to know is that the method exists, is public and needs a particular instance.
- Injection annotations
This method is the worst performing of the three. It is the worst because of the following circumstances:
- The parsing requires PHPDoc parsing. Reflection is required to extract the PHPDoc and it must be parsed into a class name.
- The class name you provide must not be a short/imported class name because the reflection does not know about the namespace of your class.
- Every time a property needs to be set, Reflection must be used to force protected properties to become publicly settable (in other words: intentionally violates visibility).
Although the reflected information does get cached, the caching process is less efficient for all classes until cached and even when it is cached, violates visibility and uses Reflection which puts further pressure on the injection.
For your simple use cases and when this is appropriate, I recommend using constructor dependencies and handling those manually when you construct your instances. This method is the most explicit, least magic and more predictable of all of them. Your class can be, but is not required to be, a Singleton.
For use cases where your class is a Singleton and you need access to an instance but the context does not allow you to manually create instances, use injection methods - because they are the most efficient of the two automated dependency injection strategies and avoid the violation of visibility.
But please, don't use
@inject annotations. They are against PHP pragma, they perform worse than any of
the alternative methods, they have additional peculiarities like the demand for an FQN and they depend
on additional parsing of PHPDoc.
Thanks for reading!
Please also be aware: using dependency injection (constructor dependencies not included) implies that you must use this only with Singletons. Injecting any dependency that is not a Singleton is considered bad code smell.