DI or Service Locator? Lazy loading works better with Service Locator

Notice: This thread is very old.
David Grudl
Nette Core | 8227
+
0
-

The current implementation od DI allows very easily configure services and they can be, for example, passed into the presenter via setContext() (maybe inject() should be better name).

class MyPresenter extends Presenter
{
	private $userModel;

	function setContext(UserModel $model)
	{
		$this->userModel = $model;
	}

	function formSubmitted()
	{
		...
		$this->userModel->add(...);
	}
}

However, there is one major problem: lack of lazy loading. UserModel is required only if the form is submitted, but is created and passed all the time. Lazy loading is not problem in Java or C#, but serious problem in PHP.

There is very easy solution: pass DI container instead of individual services. Yes, it's a step back from the DI container to the Service Locator, but do you know another solution?

HosipLan
Moderator | 4668
+
0
-

Even symfony agreeds with you on this. The Controller gets the global container by default in constructor.

duke
Member | 650
+
0
-

We can always pass a custom container with lazily copied services, although there could be some kind of support for lazy copying of services between containers so we don't have to copy them manually like this:

$customContainer = new DI\Container;
$customContainer->addService('service1', function() use ($container) {
    return $container->getService('service1');
});
$customContainer->addService('service2', function() use ($container) {
    return $container->getService('service2');
});

Maybe something like:

$customContainer = new DI\Container;
$container->copyServices($customContainer, array('service1', 'service2')); // this would automatically copy lazily in case service is not instantiated yet

And then maybe we could have new syntax for such copying in config. Maybe it would suffice to have new section “containers”:

containers:
        customContainer:
                - service1
                - service2
David Grudl
Nette Core | 8227
+
0
-

From the perspective of DI there is no difference between passing the customer container and passing full context. The first one is just more complicated.

duke
Member | 650
+
0
-

Perhaps you do not count following view into “the perspective of DI”, but the difference is that if you pass full context, you give the service complete freedom about what services and parameters it can use (and therefore also about what services and parameters it can pass further to other sub-services, etc.). Limiting this freedom works as a security measure in first place and may help to diagnose bugs eventually. What I am talking about is the principle that service shouldn't know more than it needs to.

We were talking about lazy-loading, and unfortunately it is as you say and there is currently no good alternative to using containers in PHP for that (as far as I know). But if we are forced to inject whole containers when we want to retain lazy-loading, we should at least have a comfortable way to do so cleanly, i.e. by passing custom limited containers that contain only that, what is necessary.