Closure callbacks in PHP (Laravel 5) - php

I am learning Laravel 5, and getting a little bit confused at these lines of code (creation of service provider):
public function register()
{
$this->app->singleton('Riak\Contracts\Connection', function($app)
{
return new Connection($app['config']['riak']);
});
}
I cant understand $app variable, and frome where it will be passed into function? Thanks!
P.S. And please correct if I wrong, I read some posts, and how I understand closures is a anonimous functions what uses vars from outside scope (by using use ()), and lambda functions is just closure what doesnt use these vars. Or I miss something?

It's $this->app->singleton that passes $app to the callback closure when it is called.

The anonymous function is a Callable which is passed to the function singleton in the object $app.
The function singleton then calls the Callable (anonymous function) that you passed to it, and passes $app as argument into the anonymous function.

Related

dynamic function call parameters

Today I have used a Klein Semi Framework and I wanted to understand something which is really important for me to know.
$klein->respond('/report/latest', function ($request, $response, $service) {
$response->file('/tmp/cached_report.zip');
});
for example in this code we pass into the function three variables request, response and service. How does it know to put into the request variable the request class, to put into the response variable the response class, etc. no matter the order of the varaibles?
Is there any example of code that will help me to understand this?
Thanks!
Not familiar with the framework, but based on your explanation, I'd assume they are using reflection to get the name and order of the parameters you provided to the closure. You can see an example of this here: (https://3v4l.org/jjWa1)
$closure = function ($request, $response, $service) {
$response->file('/tmp/cached_report.zip');
};
$reflected = new ReflectionFunction($closure);
var_dump($reflected->getParameters());
ReflectionFunction allows you to get details about a function's definition.
$request, $response, and $service aren't variables in your current scope that you're passing into that function.
function ($request, $response, $service) { $response->file('/tmp/cached_report.zip'); }
Is an anonymous function, a callback that you're passing as the second argument to $respond. So the parameters for that function aren't defined at the time you call $klein->respond(). What you're doing there is creating a route by assigning that callback to the $klein object to handle the '/report/latest' route.
The idea of a router class like that is that you define functions to handle requests matching various routes, and when a route is matched, the router object will call the function you've defined for it and supply the necessary arguments to it at that time.
I'm not sure what you mean by "no matter the order of the variables". I think the callback needs to have those variables defined in the correct order.

How to code/reference to a PHP callable functions easy to manage for my IDE

When I have to write a reference to a callable function I use the standard syntax of PHP defined as:
A PHP function is passed by its name as a string. Any built-in or user-defined function can be used [... omitted...].
A method of an instantiated object is passed as an array containing an object at index 0 and the method name (aka string)
at index 1.
Static class methods can also be passed without instantiating an object of that class by passing the class name (still a string)
instead of an object at index 0.
As of PHP 5.2.3, it is also possible to pass (the string) 'ClassName::methodName'.
Apart from common user-defined function, anonymous functions can also be passed to a callback parameter.
All of these ways are not "IDE friendly" for operations like function name refactor or find usage of.
In my answer I propose a solution, but there are other approaches that can be applied, even totally different, that allow to IDE to "find" the invocation of the methods?
You already are next to the shortest thing you can do
You can perfectly call your anonymous function directly in your function call without using a variable
For instance, you can replace:
$callable=function($param) use ($object){
return $object->myMethod($param);
}
call_user_func($callable, $param);
by:
call_user_func(function($param) use ($object){
return $object->myMethod($param);
}, $param);
You will have to wait for arrow functions in future PHP versions, and you should be able to use something like:
call_user_func(fn($a) => $object->myMethod($a), $param);
I became to a solution, always based on the anonymous-functions that solve the problem but leave me not full satisfied:
Static method of a class
$callable = function($param){
return \my\namespace\myClass::myMethod($param);
}
method of a object
$callable = function($param) use ($object){
return $object->myMethod($param);
}
method of $this object
$callable = function($param){
return self::myMethod($param);
}
Alternatives for oldest php versions
Inside the all classess you gonna call (or in their parents) define the function classname() as follow:
public static function className()
{
return get_called_class();
}
or for very old PHP:
public static function className()
{
return "MyClass";
}
Then
call_user_func(array(MyClass::className(), 'myCallbackMethod'));

Passing a variable to an anonymous function without using the use keyword

I have a Router class:
$router->
get($pattern, $callback); // creates a new route
[...]
And an Application class:
$app->
get($pattern, $callback); // calls the router `get()` method
[...]
The problem is, when I'm setting the callback, inside each function I need the $app instance. I know I can use the use keyword, but using it for every route, every callback will by annoying and pointless.
Example:
Change:
$app->get('here/is/my/pattern', function () use ($app) {
$app->controller('just_an_example');
});
To:
$app->get('here/is/my/pattern', function () {
$app->controller('just_an_example');
});
How can I pass a variable to an anonymous function without the use keyword?
Short Answer
Just use in the variable.
Long Answer
Seriously, just use the variable.
Alternate Answer
You could rebind the callback, if it's a closure:
function rebind(App $app, Closure $closure) {
return $closure->bindTo($app);
}
$app->get('here/is/my/pattern', rebind($app, function () {
$this->controller('just_an_example');
}));
$this is now bound to $app, and $this is always available in the scope of a non-static closure.
But that isn't gaining you much aside from unnecessary indirection; please refer to the Short Answer and/or Long Answer.
Your design is wrong.
It is not the Router's job to call the Controller. Instead, your Router should return something (usually a Route object) the corresponds to the match route. Generally like so:
$router = new Router($reuqest->getURI()); //The request URI
$router->addRoute("some/pattern", "SomeController");
$router->addRoute("some/other/pattern", "SomeOtherController");
$route = $router->route(); //$route now has a Route object.
$controllerClass = $route->getResource();
$app->$controllerClass($request); //Pass the Request object with GET and POST and whatever into the controller
This way, the router doesn't know it goes to some controller, and the controller doesn't know it came from a route. The two components are now completely reusable.

PHP, The Closure class

I am trying to understand about the closure class, at the manual there is,
All at the manual Link,
Closure::__construct - Constructor that disallows instantiation. If i understand it right the only instance of this class is for anonymous function variable assignment.
But i did not understand few lines:
Closure::bind — Duplicates a closure with a specific bound object and class scope.
Closure::bindTo — Duplicates the closure with a new bound object and class scope.
And the last at the manual i did not understand this sentence:
Besides the methods listed here, this class also has an __invoke
method. This is for consistency with other classes that implement
calling magic, as this method is not used for calling the function.
If can some one please try to explain thoes lines to me i will be very thankful, Have a nice day.
It is referring to the calling magic.
As my understanding, for any class that contains the method __invoke which its instances can be called as if it is a function. The Closure::__invoke acts like that.
i.e. when $foo is of class Closure (anonymous function), calling $foo($bar) will call $foo->__invoke(bar) (although the __invoke member is not meant to be called directly, this is just to show how it works).
When you define anonymous functions, you do this:
$greet = function($name)
{
printf("Hello %s\r\n", $name);
};
Now, $greet is of class Closure. and $greet->__invoke is sort of equal to function($name){ printf("Hello %s\r\n", $name); }
And remember, Closure::__invoke is a Magic Method.
I think the difference between bind and bindTo is just in the way they're called:
$cl->bindTo($obj)
is equivalent to
Closure::bind($cl, $obj)
Regarding the __invoke meethod, it's saying that the method exists, but it's not actually used. When you use the closure as a function, an internal (probably more efficient) mechanism is used that bypasses the method. But the method is there for compatibility with other classes that are callable, and you could call it manually if you wanted to.

Determining, if a variable is a valid closure in PHP

Using the following function:
function is_closure($t) { return ( !is_string($t) && is_callable($t)); }
Can this return true for anything else, than an anonymous closure function? If so, what would be the correct way to determine, if a variable is a closure?
Many thanks
The most deterministic way to check if a callback is an actual closure is:
function is_closure($t) {
return $t instanceof \Closure;
}
All anonymous functions are represented as objects of the type Closure in PHP. (Which, coming back to above comment, happen to implement the __invoke() method.)
I think you can use instanceof Closure though the manual states this should not be relied upon. I guess it works for now.
Anonymous functions are currently implemented using the Closure class. This is an implementation detail and should not be relied upon.
Update
The Closure manual page has updated its guidance on this. It appears that this behaviour can now be relied upon.
Anonymous functions, implemented in PHP 5.3, yield objects of this type. This fact used to be considered an implementation detail, but it can now be relied upon.
php.net suggests using reflections to figure out if the variable contains a valid closure or not
I use this little helper
function isClosure($suspected_closure) {
$reflection = new ReflectionFunction($suspected_closure);
return (bool) $reflection->isClosure();
}
This is supported with Reflection http://www.php.net/manual/en/reflectionfunctionabstract.isclosure.php
If you get an error about that does not exist ReflectionFunction, use backslash before class:
// Closure
$closure = function () {};
$reflection = new \ReflectionFunction($closure);
// checkout if it is a closure
$test->isTrue($reflection->isClosure());

Categories