Router and solid principle I would like to know if my Routing system respects the solid principle ?
The system is simple, Router class contains the routes and returns the correct route and Route class represents a route, contains a path and an action to call. Router takes the Request class as a parameter in its constructor to try to respect the solid principle.

https://en.wikipedia.org/wiki/SOLID
https://guillaume-richard.fr/principe-solid-simplifies-avec-des-exemples-en-php/
ETC...........

I would like to know what's good and what isn't.
class Router
{
    private $request;
    private $routes = [];

    public function __construct(Request $request)
    {
        $this-&gt;request = $request;
    }
  
    public function get(string $path, string $action)
    {
        return $this-&gt;addRoute($path, $action, 'GET');
    }

    private function addRoute(string $path, string $action, string $method)
    {
        $route = new Route($path, $action);
        $this-&gt;routes[$method][] = $route;
        return $route;
    }

    public function run()
    {
        if(!isset($this-&gt;routes[$this-&gt;request-&gt;getMETHOD()])) {
            throw new RouterException('REQUEST_METHOD does not exist');
        }
        foreach ($this-&gt;routes[$this-&gt;request-&gt;getMETHOD()] as $route) {
            if ($route-&gt;match($this-&gt;request-&gt;getUri())) {
                return $route-&gt;call();
            }
        }
        throw new RouterException(&quot;La page demandée est introuvable.&quot;);
    }
}

class Route
{
    private $path;   
    private $action; 
    private $matches; 
    private $parameters = [];

    public function __construct(string $path, string $action)
    {
        $this-&gt;path = trim($path, '/');
        $this-&gt;action = $action;
    }

    public function where(string $param, string $regex)
    {
        $this-&gt;parameters[$param] = str_replace('(', '(?:', $regex);
        return $this; 
    }

    public function match(string $requestUri)
    {
        $requestUri = trim($requestUri, '/');
        $path = preg_replace_callback('#:([\w]+)#', [$this, 'paramMatch'], $this-&gt;path); 
        $regex = &quot;#^$path$#&quot;;
        if (preg_match($regex, $requestUri, $matches)) {
            array_shift($matches);
            $this-&gt;matches = $matches;
            return true;
        } else {
            return false;
        }
    }

    private function paramMatch(array $match)
    {
        if (isset($this-&gt;parameters[$match[1]])) {
            return '(' . $this-&gt;parameters[$match[1]] . ')';
        }
        return '([^/]+)';
    }

    public function call()
    {
        $parameters = explode('@', $this-&gt;action);
        $controller = &quot;Src\\Controller\\&quot; . $parameters[0];
        $controller = new $controller();
        $method = $parameters[1];
        return call_user_func_array([$controller, $method], $this-&gt;matches);
    }
}
Router should not accept request object in constructor. You will need a new router for each request.
I know PHP spawns a new process for each request so it gets created for each request anyway. But in tests it may be different...
And anyway it doesn't make much sense to hide the request inside the router from the logical point of view.
Hiding a constructor dependency means that the dependency is an implementation detail. And sure you agree that request is not just an implementation detail of a router. It is the input for routing.
Also the route should not be aware of how to determine controller class and surely it doesn't want to know or, and thats even worse, force no constructor arguments for all controllers. There should be a controller factory that knows the details.
And you don't want to pass controller factory to every route. So maybe instead, route should just define path mapping and controller factory should take a route as an input. Router would then pass the matched route to the controller factory.
Another thing is that the addRoute method doesn't really belong on a router, it belong on a router builder. You register routes on the builder and finalize by calling it's createRouter method.
What's entirely missing in your code is an &quot;application&quot; class - that's the one where run method belongs, not the router, router should really have a method that accepts request object and returns response object. Application might have a request factory, the router and some kind of response emitter which takes the response object returned by router and send it's representation to the client.