public
Last active

DIC container 101 in PHP

  • Download Gist
BasicDicContainer.php
PHP
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42
<?php
 
class B {
protected $c;
public function __construct(C $c) {
$this->c = $c;
}
}
 
class C {}
 
class MyContainer
{
protected $instances = array();
 
public function __construct()
{
$this->instances = array(
'B' => function (MyContainer $container) {
return new B($container->get('C'));
},
 
'C' => function(MyContainer $container) {
return new C();
},
);
}
 
public function get($name)
{
$cb = $this->instances[$name];
 
if ($cb instanceof \Closure) {
$this->instances[$name] = $cb($this);
}
 
return $this->instances[$name];
}
}
 
$container = new MyContainer();
var_dump($container->get('B'));

That's essentially what mine does - except there is an adapter and handlers for each service. But, all services are requested of the container and returned by the container. The only entry point to the Container is the Front Controller. Here's a UML diagram of sorts https://github.com/Molajo/IoC/blob/master/.dev/IoC.png

The question is - when you have a Service that is dependent upon another Service (let's say the Model is dependent upon the Database service.) Then, the Model requests the Database service in order to inject the database into the Model during construction.

That's why I passed the closure with the call back to the single entry point into the Container - so that - in this case - the model can getService('Database') to inject it in when it constructs the class.

Otherwise, I can't see how you could do that - especially considering you want lazy loading and run time variables are often necessary.

(Thank you so much - your help is appreciated)

@AmyStephen I am not sure what you mean by "exactly the same thing" - looking at https://gist.github.com/AmyStephen/5607606#comment-832169, the code is too stripped down to really follow all the steps.

There some sentences that I believe to be wrong:

Then, the Model requests the Database service in order to inject the database into the Model during construction.

and

That's why I passed the closure with the call back to the single entry point into the Container - so that - in this case - the model can getService('Database') to inject it in when it constructs the class.

Maybe it's just a language problem, but if I understand it correctly, you are trying to achieve lazy-loading by passing closures around your models.

The problem is essentially that the model itself (service? Don't call it "Model" please, "Model" could literally be anything :( ) requests services as far as I understand, and that breaks the DIP by making your object "aware" that it has to pull something.

It also breaks the LSP, since you now cannot inject a mock now, but instead rely on some callback-based signature, trusting the fact that the callback will retrieve a valid object.

In my opinion, you should first focus on writing it correct and forgetting about the lazy-loading problem. The DIC will eventually be able to produce lazy wrappers of your services.

I wrote extensively on this subject in an article that you've probably already read:

http://ocramius.github.io/blog/zf2-and-symfony-service-proxies-with-doctrine-proxies/ (read it later eventually)

Please sign in to comment on this gist.

Something went wrong with that request. Please try again.