Container usage¶
Now that we have defined all of our dependencies its time to create a container that will use them.
Even though Container
can be instantiated as normal PHP objects do, it is
advisable to use the ContainerBuilder
factory to do so. It translates the
definitions file into DefinitionInterface
objects that dependency container
uses to create dependencies. It also chains all the created container so that if
you try to get an entry it will search over all containers in the chain. This
is a very handy feature if you want, for example, to create a package that you
will reuse in your applications and in that package you define a container with
default dependencies and later on your application also defines a dependency
container. Definitions can be overridden and the entries from your package
are also available in your application container.
Now lets create our container:
use Slick\Di\ContainerBuilder;
$container = (new ContainerBuilder(__DIR__ . '/services.php'))->getContainer();
That’s it! We now have a new created dependency container ready to create objects and inject their dependencies.
Constructor injection¶
Consider the following class:
class Car
{
/**
* @var Engine
*/
protected $engine;
public function __construct(Engine $engine)
{
$this->engine = $engine;
}
}
Now lets create a new Car
using our dependency container:
$myCar = $container->get(Car::class);
Tip
As a rule of thumb, always use type hint in your constructor. This will make your code more readable, bug free and easy to instantiate with the dependency container.
The dependency container will look to constructor arguments and search for type matches in the dependencies collection he holds and will inject those dependencies on the requested object instance.
Setter injection¶
When creating objects with containers, be aware that the container will also
look for methods like set<VarName>(VarType $var)
and if it has a matching
dependency it will use that method to inject that dependency:
class Car
{
/**
* @var Engine
*/
protected $engine;
public function setEngine(Engine $engine)
{
$this->engine = $engine;
return $this;
}
}
By calling the container to create your car object like:
$myCar = $container->get(Car::class);
Car::$engine
will be inject using the setter method.
Warning
When creating objects with a container if you have a setter but there is no known dependency in the container collection, it will not fail because the container only injects dependencies that he knows of.
In the other hand if you have dependencies on your constructor the object creation will fail due to missing arguments.
Property injection¶
In property injection we will use the annotation @inject
to tell the
container what to inject:
class Car
{
/**
* @inject
* @var Engine
*/
protected $engine;
}
In the example above the container will use the @var
annotation to determine
the dependency to inject:
class Car
{
/**
* @inject car.config
* @var array
*/
protected $config;
}
@inject
annotation accepts an argument with the entry name that the container
should use to determine the dependency. In the above example, config array was
stored with car.config key and will be injected in Car
creation.
Note
To skip dependency injection on methods or properties you need to set
@ignoreInject
annotation. This annotation tells dependency container to
ignore the automatic dependency injection on public properties or public
methods. This annotation does not work with constructor methods.
Attention
By using the @inject
annotation you are explicitly telling the container
to inject a dependency. If no dependency is found for the provided key or
type an exception will be thrown telling you that the container cannot
inject something you said that he has to.