Reputation: 616
I got a small Router Class that should return a user defined Callable or Closure. The Closure is saved inside a protected array with some additional information about the route.
This are (the important parts) of my structure:
index.php
<?php
// ... defining stuff
$router = new Router($_SERVER);
$router->add('/', 'GET', function() {
//... do sth
});
$router->run();
router.php
<?php
// defining....
class Router {
protected $container;
protected $requestUri;
protected $requedtMethod;
protected $routes = [];
/**
* Constructor will be responsible for parsing the
* request and create DiContainer.
*
* @param array $request
*/
public function __construct(array $request, DiContainer $container = null) {
// do stuff...
}
/**
* Add route with callback to $routes array.
*
* @param string $uri
* @param string $type Type of http request. Defaults to 'GET'
* @param Callable $fn
*/
public function add(string $uri, string $type = 'GET', \Closure $fn) {
$this->routes[$uri] = [
'type' => $type,
'cb' => $fn
];
}
/**
* Check if requested route exists in our routes array
*
* @param string $uri
* @return boolean
*/
protected function hasRoute(string $uri): bool {
return array_key_exists($uri, $this->routes);
}
/**
* Run the router.
*
*/
public function run() {
if (!$this->hasRoute($this->requestUri)) {
header("HTTP/1.0 404 Not Found");
return 'Route not found';
}
// Version 1. Doesn't work
return call_user_func($this->routes[$this->requestUri]['cb']->bindTo($this));
// Version 2. Works
return $this->routes[$this->requestUri]['cb']->call($this);
}
}
I just try to understand, whats the differences between call_user_func
with the closure bind to the object and the Closure::call($this).
Version 1 with call_user_func throws the following error:
Fatal error: Uncaught Error: Cannot access protected property \Router::$container
But why is this working with Version 2? I tried to read the docs through both and in my head this should work the same way, but Version 1 doesn't seem to have access to $this
. But why?
Thank you!
Upvotes: 0
Views: 779
Reputation: 3742
https://www.php.net/manual/en/closure.bindto.php#refsect1-closure.bindto-parameters
bindTo
accepts 2 parameters:
The class scope to which associate the closure is to be associated, or 'static' to keep the current one. If an object is given, the type of the object will be used instead. This determines the visibility of protected and private methods of the bound object. It is not allowed to pass (an object of) an internal class as this parameter.
<?php
...
// Pass an object through as 2nd argument.
return call_user_func($this->routes[$this->requestUri]['cb']->bindTo($this, $this));
// Pass the class name (string) through as 2nd argument.
return call_user_func($this->routes[$this->requestUri]['cb']->bindTo($this, 'Router'));
Upvotes: 1