dynamic function call parameters - php

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.

Related

The sequence of calling functions in laravel route?

Example below:
Route::get('users/{id}', function ($id) {
//code
})->where('id', '[0-9]+');
The anonymous function will be called first or "where" function will be called first?
In my opinion, I think the anonymous function will be called first. But I think this is not reasonable, I think it is reasonable that only the id meet the regular expressions can call the anonymous function.
So what sequence is it exactly?
The where will be ran first.
Why?
the get function returns an object as well as the where. So if Laravel calls upon that Route, the Route object will be built first before firing the Route's actions.
$obj = Route::get() // returns an object, meaning that object returned has a function `where`.
$obj->where() // still returns the same object but validated.
So you have an object after the where call. That's what Laravel will use to perform an action on that route.
Like: performRoute($obj) // this is only for example
You can review the Route functions here: https://github.com/laravel/framework/blob/bd352a0d2ca93775fce8ef02365b03fc4fb8cbb0/src/Illuminate/Routing/Route.php

Closure callbacks in PHP (Laravel 5)

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.

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.

Laravel filter with parameter in construct

I searched on Stack and see this question How to add filter parameters to controllers in Laravel?.
I have a similar question but this time, I need to pass an flexible $myparam argument, the code looks like bellow:
In Route.php
Route::filter('diffauthor',function($myparam){
if(Authority::cannot('edit', 'postedit', $myparam))
return View::make('permdeny.index');
});
and in Controller:
public function __construct() {
parent::__construct();
$this->filter('before','diffauthor', $myparam);
}
How do I pass $myparam base on user request?
You can only pass parameters to filters as strings:
$this->filter('before', 'diffauthor:param1,param2');
Sometimes to get around this limitation I use the Session class as a sort of temporary store, or even inspect variables that are being passed to the method by looking at the object returned by Request::route() within my filter.

PHP - call_user_function_array or Reflection class pass by reference?

I'm trying to dispatch request in MVC framework.
I have a routing object that matches current URI against defined routes. If there is a match, it returns instance of Route object. Through that Route object I can access matched controller, method and method arguments.
I can use it like this:
$route->getClass(); name of controller class to instantiate
$route->getMethod(); name of method to call
$route->getArgs(); array holding arguments that should be passed to method
I can also add new arguments if I need to. For example, I can add Dependency Injection Container and current HTTP Request object instances to args like this:
$route->addArg('container', $container);
$route->addArg('request', $request);
Now $route->getArgs holds all arguments fetched from URI, but also a $container instance and $request instance. $container is dependency injection container i wrote. And $request is object that represents current HTTP Request.
So the idea is to instantiate Router, get current Route, then add objects I would like to use inside every method / action controller. And then pass that Route object to Dispatcher, and when request is dispatched, I would be able to use $container, $request and other arguments in every method / action controller.
The problem I have is that when I use lets say blog controller and post method. By default, and always, it shoud have instance of $container, and $request (since I pushed them into Route::$args, plus any other argument / variable defined in routes definitions or fetched from URI.
So when Im in my Blog Controller, post method I want it to act like this:
public function post($container, $request)
{
if($request->isAjax) {
// -- Its ajax request
}
$twig = $container->getService("Twig");
}
I dispatch request something like this:
Im using call_user_function_array to make it work:
$app = new $controller();
call_user_function_array(array($app, $method), $args);
Now this all works as it should but I have one problem.
When Im using that post controller I must be careful in what order are post method arguments ordered.
So i can do this:
public function post($id, $url, $container, $request) {
// -- SNIP --
}
but I cant use it this way:
public function post($container, $url, $request, $id) {
/-- SNIP --
}
So you get the problem, I cant mix arguments inside my method the way I want, and I need to keep an eye on order in which those arguments get defined in Route:$args array.
Every method / action controller should have $request, and $container instances plus arguments fetched through URI or defined in route definition.
Is there any way to make it work without having to think In what order is post method getting arguments? Does this have something to do with passing by reference? Since php passes variables by value?
Im using call_user_function_array and I want to find solution for call_user_function_array.
Is this possible?
If not, can this be done using Reflection class?
This is the solution to my problem:
So, I have everything needed to dispatch request:
$class = "\\Application\\Controller\\Blog";
$method = "post";
$args = array('id' => 1, 'url' => 'some-url', 'container' => DICOBJ, 'request' => RQSTOBJ);
Now I need to dispatch that request, so user can insert arguments as he wants in any order and if he wants.
This is what should be done:
$app = new $class();
$rm = new \ReflectionMethod($app, $method);
$params = $rm->getParameters();
$argsOrdered = array();
foreach($params as $param) {
$argsOrdered[$param->getName()] = $args[$param->getName()];
}
call_user_func_array(array($app, $method), $argsOrdered);
Ta daaa!!!
So, what this does is it gets defined arguments by user, and returns them as array in order user wrote them. And then we create another $argsOrdered array that holds only arguments user wants, ordered the way he wants :)
Im using ReflectionMethod to inspect method. And at the end call_user_func_array to dispatch request, but you can also use Reflection class to get controller instance.
Thats it. Do you find this solution elegant? Can this be done any faster?
With Reflection class you can get the arguments names of each parameter of the function you want to call and in that way check if they are in the correct order very easily.
You can't do that simply with call_user_function_array.
In particular you can use the getParameters() method in the ReflectionMethod class.
From there on I think you know what to do (check each key of the parameters array with the key of the argument passed and there you go).

Categories