development

Dependency Injection Container - TYPO3 4.3

(updated on 3.6.2010)

Today I uploaded a initial version of the "container" extension to forge. This extension has a simple Dependency Injection Container with a Feature subset of the FLOW3 DI Framework - and some special TYPO3 4.x Features.

Dependency Injection

There are a lot of explanations on different websites (e.g. Wikipedia, FLOW3).
In general DI is a Pattern that simply means, that a class that requires other objects for its work, does not instanciate these objects itself: Instead the class asks for this dependencies.
The easiest way of doing this, is to require this objects in the constructor:
public function __construct(Tx_Blog_Blog $blog)
This is called "Constructor Injection" and should be used for mandatory dependencies.

Another way of asking for dependencies is in using setter methods:
public function setBlog(Tx_Blog_Blog $blog)
This is called "setter injection".

Other Objects are not the only dependencies a class could have, there are also configurations or other settings a class would need. Therefore a class using DI would also expect this settings to be injected from the outside.

Its a good practice that a class does not ask for a concrete class but for a Interface or an abstract class, because that gives the class a greater usage flexibility.

Using DI is essential for writing good Unit-Test, because than it is easy to test the class in isolation by Injecting Mocks, Stubs or Fakeobjects to the class.

DI Container

Using DI does not require a container, however it is a common pattern when it comes to Dependency Injection. A container knows how to build instances of a class. For example this is a container:

class myContainer {

public function getDomainBlog() {
return new Tx_Blog_Domain_Blog(new Tx_Blog_System_Persitence('user','password'));
}

public function getDomainWebsite() {
return new TX_Blog_Domain_Website($this->getDomainBlog());
}

}

Of course a typical DI Container embedded in a framework is not hardcoded, but detects and resolves dependencys of classes automatically.

Container for TYPO3 4.3

As said you can find a DI container for TYPO3 4.3 here:

https://svn.typo3.org/TYPO3v4/Extensions/container

It offers the following features:

  • Constructor injection detection
  • Setter injection detection via annotation or "inject*" Name
  • Settings injection for extension settings (ext_conf_template)
  • Singleton support via "t3lib_Singleton"
  • Support for manual registration of implementations
  • XClass awareness 
  • Able to resolve cyclic dependencys with setter Injection
  • First and Second Level reflection cache for the matter of speed

 

Introduction

Instead of calling new or "makeInstance" to get the object you need, you expect the object instance to be injected by the outside. Therefore you will "ask" for the Objects you need in the constructor of your class or you expect them to be setted by some setter methods.

For initial startup you can request a container with:

Tx_Container_Container::getContainer()

 

Constructor Injection

You just have to write your constructor and you will get the dependencys:

class tx_container_tests_amixed_string {
public function __construct(tx_container_tests_b $b, tx_container_tests_c $c, $myvalue='test') {

}
}

Use this to get Dependencys that are required for your class to do the job.


Setter Injection

Can be used by annotation "@inject" or by naming a method "inject*":

class tx_container_tests_injectmethods {
public $b;
public $bchild;

public function injectClassB(tx_container_tests_b $o) {
$this->b = $o;
}

/**
* @inject
* @param tx_container_tests_b $o
*/
public function setClassBChild(tx_container_tests_b_child $o) {
$this->bchild = $o;
}
}

Settings Injection

You can also inject the ext_conf_template Settings by simple using a method "injectExtensionSettings":

 public function injectExtensionSettings(array $settings) {
        $this->settings = $settings;
    }

Singletons

Per default all objects are prototypes, if you want them to be singletons, just implememt t3lib_Singleton

 class tx_container_tests_singleton implements t3lib_Singleton {
   
}

Interfaces and Abstract Classes

The container is not aware of the available implementations of and interface or abstract class.

Therefore you need to let him know in the ext_localconf.php:

Tx_Container_Container::getContainer()->registerImplementation('tx_container_tests_someinterface', 'tx_container_tests_someimplementation');



What if I need to call new?

You need a container if you need to create new objects (e.g. in a factory), normally very few classes needs to do this, so only very few classes actually need to know the container.

You should call a container to give you a new object if you need to. The container is aware of building the object with the dependencies.

If your class needs a container you should inject it (see above) like other dependencies.

Upcoming

There are some new features planned:

  • Extension context aware dependency resolving
  • TypoScript Configuration Injection

blog comments powered by Disqus
blogroll