I have got told to use A dependency injection container to initialize
my controller(s). I have been googling all night, last night, I
understood that Dependency injection Container (DiC) is used to create
an instance of an object, and inject the needed objects into it.
That is correct.
I have read this post How to build a PHP Dependency Injection
Container
Stop right there. That answer does not show how to build a container -- it shows how to build a service locator. There's a huge difference between the two:
- A service locator is like a key/value store: you put named things inside, and later on you ask for them by name and the locator returns them to you.
- An injection container is like a factory: you ask it to produce something and it produces it for you. The specifics of how the product is created can range from totally opaque to fully specifiable on your part.
In layman's terms, a service locator can do this:
class Perishable
{
public function __construct(DateTime $expirationDate) { ... }
}
$locator->set('milk', new Perishable(new DateTime());
$milk = $locator->get('milk');
but, in contrast to an injection container, it cannot do this:
$milk = $container->build(Perishable::class); // ::class is a PHP 5.5 feature
An injection container is able to determine on its own that Perishable
has a dependency on a DateTime
and resolve that dependency. This happens recursively, with the understanding that all the "leaf" dependencies can be instantiated. In this case DateTime
is easily instantiated because it's a concrete class with a constructor that can run parameterless; in other scenarios you would have to help by telling the container what to do if it comes upon a dependency that cannot be directly instantiated, e.g. an Iterator
.
Now, I assume $host
, $user
, $pass
are just string variables, now if I
want to pass objects to a controller, how can I dynamically know which
objects it needs to have injected?
We have now reached the implementation part: how do you know that a Perishable
needs a DateTime
, and how do you create that DateTime
?
You can do this by using reflection:
$class = new ReflectionClass('Perishable');
$constructor = $class->getConstructor();
$arguments = [];
foreach ($constructor->getParameters() as $parameter) {
$argumentClass = $parameter->getClass();
$arguments[] = $argumentClass->newInstance();
}
$result = $class->newInstanceArgs($arguments);
This is barebones code that can instantiate a Perishable
as given above, but of course it will fail badly in more general cases (there's a lot more you have to properly take care of, e.g. recursively resolving constructor arguments).
Is it right to add an array of types like this?
No, because you would have to violate DRY: the dependencies for SomeController
would be specified both as part of its constructor definition and when registering it into the container or locator.
Or is it right to initialize all controllers object in the container when the
application loads, then I can easily use any controller?
It's not right at all, and as explained above here you are using the word "container" when in fact you have a mental picture of a service locator. In general, service locator has many drawbacks that an injection container does not. In this situation you should use a proper container instead.