In Slim Framework, there is a protect
function that wraps callables within a function (i.e. another callable). The description in the manual says:
What if you want to literally store a closure as the raw value and not have it invoked? You can do that like this:
$app->myClosure = $app->container->protect(function () {});
Looking into the source code, I see this:
/**
* Protect closure from being directly invoked
* @param Closure $callable A closure to keep from being invoked and evaluated
* @return Closure
*/
public function protect(\Closure $callable)
{
return function () use ($callable) {
return $callable;
};
}
I'm wondering what the point of that is. Here I did my own test:
$foo = function() {
echo "Hello";
};
$bar = function () use ($foo) {
return $foo;
};
var_dump($foo);
var_dump($bar);
This is what I got:
object(Closure)#1 (0) {
}
object(Closure)#2 (1) {
["static"]=>
array(1) {
["foo"]=>
object(Closure)#1 (0) {
}
}
}
I can call $bar()
to return the callable but why would I do that if I can just use $foo
? Can someone please explain the purpose of this?
It is all about closures being invoked on runtime. The point of it becomes apparent when you need to use a callback (pass a closure as callable \Closure
to another function) without immediately invoking it.
Let's look what happens inside our Slim run.
So if we simply assign a closure to a resource, like so
$app->foo = function () {
return 'invoked foo returns this string';
};
or as a Slims singleton resource
$app->container->singleton('foo', function () {
return 'invoked foo returns this string';
});
it will be invoked on each call or on our first call to it, respectively ... so
$app->foo;
will return the string invoked foo returns this string
.
Lets say that we want another function to use our callable (as some kind of middle layer) and wants to invoke it with call_user_function()
. So we don't want to pass in the invoked function (which will be passing the returned value), but rather an uninvoked closure, which we achieve by assigning the closure to a variable/resource using the protect()
method
$app->bar = $app->container->protect(function () {
return 'bar returns a callable closure';
});
And for a demonstration let's pass our $app->foo
and $app->bar
to call_user_function()
:
call_user_func($app->foo);
will throw an error
"call_user_func() expects parameter 1 to be a valid callback,
function 'invoked foo returns this string' not found or invalid function name"
because it tries to invoke the returned string, where
call_user_func($app->bar);
calls the closure saved in $app->bar
and returns its return string, like so:
"bar returns a callable closure"
I hope this example illustrates the usefulness of Slim's protect()
method.