I was watching this video and at 7:10 he's adding a db dependency and is using a closure to assign the value.
My question is why not just use direct assignment instead, I mean isn't doing this:
$container['db'] = $capsule;
equivalent to doing this:
$container['db'] = function ($container) use ($capsule) {
return $capsule;
}
If not, what is the difference and which way is better?
Closures are useful because they let you associate data (the lexical environment) with a function that operates on that data. This has obvious parallels to object-oriented programming, where objects allow you to associate data (the object's properties) with one or more methods.
Difference between Function and ClosureFunction is declared using func keyword whereas Closure doesn't have func keyword. Function has always name but Closure doesn't have. Function doesn't have in keyword but closure has in the keyword.
A closure is the combination of a function bundled together (enclosed) with references to its surrounding state (the lexical environment). In other words, a closure gives you access to an outer function's scope from an inner function.
Implementation and theory. Closures are typically implemented with a special data structure that contains a pointer to the function code, plus a representation of the function's lexical environment (i.e., the set of available variables) at the time when the closure was created.
TLDR it's because defining dependencies as closures makes it possible for dependency injection container to build them on demand, hence you don't need to worry about their order of definition and managing their dependencies manually.
Pimple is Dependency Injection Container, it is supposed to help you set up your objects by managing their dependencies in an easy and convenient way.
If you directly assign a value to a key, Pimple calls that value a parameter, and when you need to access that key, it simply returns that exact value:
$container['sample-param'] = 'foo';
echo $container['sample-param'];
//output: foo
But the point is, this sample-param
does not require any set up, it is simply a value and we're fine with that. But assume the following service setup:
$container['myService'] = function($c) {
$service = new \Some\Complicated\Service();
//this service depends on cache service
$service->setCache($c['cache']);
return $service;
}
$container['cache'] = function($c) {
$cache = new \Cache\Driver();
//our cache driver needs a db connection
$cache->setDbConnection($c['db']);
return $cache;
}
$container['db'] = function($c) {
//our database connection requires some parameters
$dbConnection = new \Database\Connection($c['db-config']);
return $dbConnection;
}
$container['db-config'] = [
'username' => 'foo',
'password' => 'bar',
'host' => 'localhost'
];
//Now we want to user our service:
$container['myService']->doSomething();
Please pay attention to order which I used to define different keys in the container.
myService
needs cache
but cache definition comes after myService definition. And this is where Pimple is helping, and this is why we pass container to every closure, because Pimple is going to build our dependencies on demand. When we need to access myService
Pimple looks at its internal data storage, if it has previously built and stored myService
successfully, it will return that same instance, else it will call our closure to build it. And when our closure is called, it will ask Pimple ($c is the Pimple container) to give it its dependecies (which in this case is the cache
service). Pimple applies the same thing on cache, if it is not built yet, it will build it and so on...till it gets to the part that requires simple params like db-config
which will immediately return. And in this chain of closure calls, our object and all its dependencies are built.
Now imagine what would happen if we used simple values instead of closures? In that case, when we want to build myService
we have to take care of its dependencies. We have to make sure its dependencies are defined before we define the service itself and we have to deal with other problems related to managing dependencies. In that case we couldn't just define our myService
and assume there is a cache
service available, which will be defined later.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With