Home > database >  php router passing parameters to call_user_func_array
php router passing parameters to call_user_func_array

Time:12-22

<?php 
    class Router {
        protected $conn;
        protected $currentController = "";
        protected $currentMethod = "";
        protected array $params = [];

        public function __construct(\PDO $conn){
            $this->conn = $conn;
        }

        public function dispatch(){
            $url = $this->getUrl();
            if(isset($url[4])){
                switch($url[4]){
                    case "events":
                        $this->currentController = new App\Controllers\EventController($this->conn);
                        $this->currentMethod = "getEvents";
                        break;
                    case "event":
                        $this->params = array($url[5]);
                        $this->currentController = new App\Controllers\EventController($this->conn);
                        $this->currentMethod = "getEvent";
                        break;

                }
            }
            call_user_func_array([$this->currentController, $this->currentMethod], $this->params);
            $this->currentController->display();
        }

        public function getUrl(){
            $uri = parse_url($_SERVER["REQUEST_URI"], PHP_URL_PATH);
            $url = explode("/", $uri);
            return $url;
        }
    }
?>

$url[5] is supposed to be a numeric id for selecting an event or a string namely "?id=1" how can I pass this value to call_user_func_array I've seen doing it with preg_match expressions unfortunately I didn't understand how it works

CodePudding user response:

Maybe you could try out this router. This is just a SIMPLIFICATION in order to understand how a router MIGHT look like. It is much better to install existing ones over packagist with composer. for example nikic/fast-route is a good option.

<?php

error_reporting(E_ALL);
ini_set('display_errors', 'On');


final class Router
{
    /**
     * This is a list of all routes with the regular expression in the key
     * the result loooks like following
     * (POST|GET)_/testMultiple/(\d )/(\S )
     * @var array<string, callable>
     */
    private array $routes = [];

    /**
     * Collect all routes
     * @param string $path
     * @param callable $action
     * @param string $methods
     * @return void
     */
    public function any(string $path, callable $action, string $methods = 'POST|GET'): void
    {
        //Skip router injections
        if (strpos($path, '..') !== false) {
            return;
        }
        $this->routes['(' . $methods . ')_' . $path] = $action;
    }

    /**
     * Create a regular expresion based on parameter and search for route
     * @param string $path
     * @return string
     * @throws Exception
     */
    public function callRoute(string $path): string
    {
        /**
         * $path might look like GET_/testMultiple/123/test
         */
        $path = $_SERVER['REQUEST_METHOD'] . '_' . parse_url($path,PHP_URL_PATH);

        /**
         * Iterate over all routes and cehck if  "GET_/testMultiple/123/test" can be found in an routes
         */
        foreach ($this->routes as $route => $action) {
            $regEx = "~^$route/?$~i";
            $matches = [];
            if (!preg_match($regEx, $path, $matches)) {
                continue;
            }
            //If we found the route we need to remove first 2 values from matched result so that only parameters from URL are inside $matched array
            array_shift($matches);
            array_shift($matches);
            $arguments = $matches;

            return call_user_func_array($action,$arguments);
        }
        throw new Exception(sprintf('Route %s not found', $path));
    }
}

final class TestController
{
    public function indexAction(): string
    {
        return 'Hello world';
    }
    public function testPost(): string
    {
        return 'Hello this is post only';
    }
    public function testAction(string $parameter1): string
    {
        return 'Hello ' . $parameter1;
    }

    public function testOptionalAction(?string $parameter1 = null): string
    {
        return 'Hello ' . $parameter1;
    }

    public function testMultiple(int $parameter1, string $parameter2): string
    {
        return 'The first value is ' . $parameter1 . ' and the second is ' . $parameter2;
    }
}


$services = [];
$services[TestController::class] = function () use ($services) {
    return new TestController();
};


$router = new Router();
$router->any('/', [$services[TestController::class](), 'indexAction']);

$router->any('/test/(\S )', [$services[TestController::class](), 'testAction']);
$router->any('/testOptional/?(\S )?', [$services[TestController::class](), 'testOptionalAction']);
$router->any('/testMultiple/(\d )/(\S )', [$services[TestController::class](), 'testMultiple']);
$router->any('/testJustPost', [$services[TestController::class](), 'testPost'],'POST');



echo $router->callRoute($_SERVER['REQUEST_URI']);

Hope that gives you some ideas for your router

  •  Tags:  
  • php
  • Related