Reputation: 755
General information
What i would like to achieve
The magic __call() method if the instance :
public function __call($method, $request) {
$this->_links[strtoupper($method)] [$this->_baseUrl . $request[0]] = $request[1];
return $this;
}
The prefix method as it is now ;
public function prefix($prefix, $callbackmethods = array()) {
foreach ($callbackmethods as $closure) {
$reflection = new ReflectionFunction($closure);
}
return $this;
}
Example of code structure i am trying to achieve.
# Normal method call to __call
$link->get('/', function(){
});
# Prefic the regex of multiple method calls to __call
$link->prefix('/admin', [
$link->get('user/(\d+)', function(){
}),
$link->post('user/(\d+)', function(){
}),
]);
What i have tried
What i want to achieve is to define a public function prefix($prefix, $callbackmethods = array()). This way the user can easlily prepend a string to the regex to be matched.
Is the reflection class the right way? Am i looking for the wrong thing? Can this be achieved with Php. Are there other options? Can anyone show me how to use this correctly, if the reflection class is the right way?
This is a learning project for me so please don't advice to use an existing routing class.
Update
I have resolved the problem differently with the following code.After that code the filtering is done inside the __call() method. See the answer provided below for a more elegant solution.
/**
*
* @param type $prefix
* @param type $requestMethod
* @return \Link
*/
public function prefix($prefix, $requestMethod = false) {
$this->_applyPrefix = true;
if ($requestMethod != false) {
$this->_prefixMethod = strtoupper($requestMethod);
}
$this->_prefix = $prefix;
return $this;
}
/**
*
* @return \Link
*/
public function end() {
$this->_applyPrefix = false;
$this->_prefixMethod = false;
$this->_prefix = false;
return $this;
}
Upvotes: 1
Views: 242
Reputation: 21817
The problem with this setup is that the __call()
method itself will add a route to the list of routes. So wrapping those calls within a call to prefix()
doesn't change that, the __call()
will still be executed first.
So it has nothing to do with how anonymous functions / closures work, the problem is the order of execution.
I suggest you choose a slightly different approach:
Route
). You can manage it more easily this way.__call()
method create and return a Route object, but don't have it add the route to the list.prefix()
method create new Route
objects based on the routes you pass to it.add()
that will add the route to the list.This could look something like this:
class Router
{
/**
* @type array
*/
private $routes = array();
/**
* @param Route|Route[] $routes
*/
public function add($routes)
{
foreach ((array)$routes as $route) {
$this->routes[] = $route;
}
}
/**
* @param string $prefix
* @param Route[] $routes
* @return Route[]
*/
public function prefix($prefix, array $routes)
{
$prefixedRoutes = array();
foreach ($routes as $route) {
$prefixedRoutes[] = new Route(
$route->getMethod(),
$prefix . $route->getUri(),
$route->getController()
);
}
return $prefixedRoutes;
}
/**
* @param string $name
* @param array $arguments
* @return Route
*/
public function __call($name, array $arguments)
{
$method = $name;
$uri = $arguments[0];
$controller = $arguments[1];
return new Route($method, $uri, $controller);
}
}
class Route
{
/**
* $var string
*/
private $method;
/**
* @var string
*/
private $uri;
/**
* @var callable
*/
private $controller;
/**
* @param string $method
* @param string $uri
* @param callable $controller
*/
public function __construct($method, $uri, $controller)
{
$this->method = $method;
$this->uri = $uri;
$this->controller = $controller;
}
/**
* @param string $prefix
*/
public function prefix($prefix)
{
$this->uri = $prefix . $this->uri;
}
/**
* @return string
*/
public function getMethod()
{
return $this->method;
}
/**
* @return string
*/
public function getUri()
{
return $this->uri;
}
/**
* @return callable
*/
public function getController()
{
return $this->controller;
}
}
You can use it like this:
$router->add(
$router->get('/', function() {
// ...
});
);
$router->add(
$router->prefix('/blog', array(
$router->get('/(\d+)', function() {
// ...
}),
$router->post('/(\d+)', function() {
// ...
})
))
);
Now this is a very, very simple setup. And it could be solved a lot more elegantly.
I strongly suggest you take a look at Silex (which is a micro-framework based on Symfony2 components). It has a router that looks a lot like what you're trying to achieve. You could take inspiration from that code.
Upvotes: 1