Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why use a closure for assignment instead of directly assigning a value to a key?

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?

like image 227
tareq Avatar asked Apr 26 '17 07:04

tareq


People also ask

Why are closures useful?

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.

What is the difference between closure and function in Swift?

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.

What is a closure in programming?

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.

How is closure implemented?

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.


1 Answers

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.

like image 119
Nima Avatar answered Sep 28 '22 02:09

Nima