notice - Newer documentation available
Dependency Injection is a specialization of the „Inversion of control“ software design pattern. It helps to manage dependencies between objects. The goal is to decouple the objects and make them more flexible and extendable.
There are numerous ressources that explain dependency injection and inversion of control:
- https://msdn.microsoft.com/en-us/library/ff921087.aspx [not available anymore]
- https://en.wikipedia.org/wiki/Dependency_injection
In Extbase this mechanism is used as well.
Let's give an example:
If my class MyController needs another class LoggingService, it can get an instance of the logging service by Dependency Injection, by specifying the following code:
namespace Vendor\Extname\Controller; use Vendor\Extname\Service\LoggingService; class MyController { /** * @var LoggingService */ protected $loggingService; /** * @param LoggingService $loggingService */ public function injectLoggingService(LoggingService $loggingService) { $this->loggingService = $loggingService; } }
The DI system finds that the class MyController has an method whose name starts with inject, and thus passes the logging service to MyController. This is automatically done by the Extbase framework.
Since version 4.7, Dependency Injection works with an @inject annotation and no inject-method is needed anymore.
/** * @var \Vendor\Foo\Service\LoggingService * @inject */ protected $loggingService;
However, be aware of possible performance implications, see for example "Why you should never use @inject in TYPO3 Extbase".
Extbase also supports Constructor Injection.
There, the dependencies are set in the constructor arguments, like in the following example:
use Vendor\Extname\Service\LoggingService; class MyController { /** * @var LoggingService */ protected $loggingService; /** * @param LoggingService $loggingService */ public function __construct(LoggingService $loggingService) { $this->loggingService = $loggingService; } }
If a method with the name initializeObject() exists, it is called after all dependencies have been injected and configured; so you can use this method for further initialization work.
To create prototype objects, use the get() method on the ObjectManager [not available anymore], as in the following example.
Do not use GeneralUtility::makeInstance anymore!
use Vendor\Extension\Log\LogFile class MyController { public function foo() { $logFile = $this->objectManager->get(LogFile::class); } }
You can also instantiate classes with constructor arguments:
public function foo() { $logFile = $this->objectManager->get(LogFile::class, 'arg1', 'arg2'); }
You can also inject prototypes into your classes.
If a name ends with "...Interface", Extbase DI automatically strips away the "Interface" from the name, and expects to find a concrete implementation of that interface.
Programming against interfaces is greatly eased by that: For your core classes, you should always reference an interface, and let the DI container instanciate the concrete class.
Additionally, Extbase DI allows to replace certain implementation classes by other classes through configuration in TypoScript. Let's give an example, and then you can see the concept:
config.tx_extbase.objects { TYPO3\CMS\Extbase\Persistence\Generic\Storage\BackendInterface { className = TYPO3\CMS\Extbase\Persistence\Generic\Storage\Typo3DbBackend } }
This essentially means to the DI container: At all places where the code refers to BackendInterface, an object of Typo3DbBackend should be instantiated.
This even works with concrete classes - you can configure the DI to replace them.
This setting could only be configured *globally* until version 6.0. Since version 6.1 it is possible to override that on a per-plugin basis:
plugin.tx_myextension.objects { TYPO3\CMS\Extbase\Persistence\Generic\Storage\BackendInterface { className = TYPO3\CMS\Extbase\Persistence\Generic\Storage\Typo3DbBackend } }