Search code examples
phpurl-rewritingurl-routing

How can I make a router in PHP?


I'm currently developing a router for one of my projects and I need to do the following:

For example, imagine we have this array of set routes:

$routes = [
    'blog/posts' => 'Path/To/Module/Blog@posts',
    'blog/view/{params} => 'Path/To/Module/Blog@view',
    'api/blog/create/{params}' => 'Path/To/Module/API/Blog@create'
];

and then if we pass this URL through: http://localhost/blog/posts it will dispatch the blog/posts route - that's fine.

Now, when it comes to the routes that require parameters, all I need is a method of implementing the ability to pass the parameters through, (i.e. http://localhost/blog/posts/param1/param2/param3 and the ability to prepend api to create http://localhost/api/blog/create/ to target API calls but I'm stumped.


Solution

  • Here's something basic, currently routes can have a pattern, and if the application paths start with that pattern then it's a match. The rest of the path is turned into params.

    <?php
    class Route
    {
        public $name;
        public $pattern;
        public $class;
        public $method;
        public $params;
    }
    
    class Router
    {
        public $routes;
    
        public function __construct(array $routes)
        {
            $this->routes = $routes;
        }
    
        public function resolve($app_path)
        {
            $matched = false;
            foreach($this->routes as $route) {
                if(strpos($app_path, $route->pattern) === 0) {
                    $matched = true;
                    break;
                }
            }
    
            if(! $matched) throw new Exception('Could not match route.');
    
            $param_str = str_replace($route->pattern, '', $app_path);
            $params = explode('/', trim($param_str, '/'));
            $params = array_filter($params);
    
            $match = clone($route);
            $match->params = $params;
    
            return $match;
        }
    }
    
    class Controller
    {
        public function action()
        {
            var_dump(func_get_args());
        }
    }
    
    $route = new Route;
    $route->name    = 'blog-posts';
    $route->pattern = '/blog/posts/';
    $route->class   = 'Controller';
    $route->method  = 'action';
    
    $router = new Router(array($route));
    $match  = $router->resolve('/blog/posts/foo/bar');
    
    // Dispatch
    if($match) {
        call_user_func_array(array(new $match->class, $match->method), $match->params);
    }
    

    Output:

    array (size=2)
      0 => string 'foo' (length=3)
      1 => string 'bar' (length=3)