Bonjour,

Je suis le tutoriel pour créer un router. Avant la 32ème minute, tout fonctionne, j'obtiens la même chose que Grafikart lors des var_dump() et autres. Par contre, une fois qu'on a mis en place la méthode privée paramMatch(), je n'obtiens plus le même résultat et ai un joli warning sur un preg_match().

Ce que je fais

La page routes.php sert à écrire mes routes :

<?php

$router = new \Core\Router\Router($_GET['url']);

$router->get('/posts/:id-:slug', function($id, $slug){
    echo "Article : $slug et $id";
})->with('id', '[O-9]+')->with('slug', '[a-z\-0-9]+');

$router->run();
?>

La classe Router.php :

<?php
namespace Core\Router;

/*
|--------------------------------------------------------------------------
| Router
|--------------------------------------------------------------------------
|
| Créer les routes pour le site
|
*/
class Router {

    /**
    * Le point d'entrée du router, l'URL : $_GET['url']
    * @var string
    */
    private $url;

    /**
    * On stock les routes dans un tableau
    * @var array
    */
    private $routes = [];

    /**
    * Les routes sont nommées pour les retrouver plus facilement
    * @var array
    */
    private $namedRoutes = [];

    // private $GroupPattern = '';

    public function __construct($url){
        $this->url = $url;
    }

    /**
    * Créer une route avec la method GET
    * @param  string $path      route a inclure dans le tableau des routes
    * @param  string $callable  controller a appeller
    * @return object            retourne l'objet en cours
    */
    public function get($path, $callable){
        $route = new Route($path, $callable);
        $this->routes["GET"][] = $route;

        return $route; // On retourne l'instance de la classe route pour "enchainer" les méthodes
    }

    public function post($path, $callable){
        $route = new Route($path, $callable);
        $this->routes["POST"][] = $route;

        return $route;
    }

    /**
    * Vérifie si une URL correspond à une "route"
    * @param  string $param nom du parametre a verifier
    * @param  string $regex regex a vérifier
    * @return object        retiourne l'objet en cours
    */
    public function run(){
        if(!isset($this->routes[$_SERVER['REQUEST_METHOD']])){
            throw new RouterException('REQUEST_METHOD does not exist');
        }

        foreach($this->routes[$_SERVER['REQUEST_METHOD']] as $route){
            if($route->match($this->url)){
                return $route->call();
            }
        }

        throw new RouterException('No matching routes');
    }

}
?>

La classe Route.php :

<?php
namespace Core\Router;

/*
|--------------------------------------------------------------------------
| Route
|--------------------------------------------------------------------------
|
| Décortique l'url pour avoir :
| - le controlleur
| - la méthode
| - les paramètres (id, slug etc)
|
*/
class Route {

    /**
    * La route à décortiquer
    * @var string
    */
    private $path;

    /**
    * L'appel au controlleur et la méthode
    * @var string
    */
    private $callable;

    /**
    * Les correspondances entre la route et l'URL
    * @var array
    */
    private $matches = [];

    /**
    * Les paramètres trouvés : id, slug, etc
    * @var array
    */
    private $params = [];

    public function __construct($path, $callable){
        $this->path = trim($path, '/');
        $this->callable = $callable;
    }

    /**
    * Trouve la correspondance entre les routes et l'URL
    * @param  string $url   URL à comparer
    * @param  string $route Route avec laquelle le comparer
    * @return bool          Retourne un bouléen si la correspondance est trouvée
    */
    public function match($url){
        $url = trim($url, '/');
        $path = preg_replace_callback('#:([\w]+)#', [$this, 'paramMatch'], $this->path);
        $regex = "#^$path$#i";

        if(!preg_match($regex, $url, $matches)){
            return false;
        }

        array_shift($matches);

        // On sauvegarde les paramètres dans l'instance pour la méthode call()
        $this->matches = $matches;

        return true;
    }

    /**
    * Récupère et stocke les paramètres
    * @param  string $match Correspondance
    * @return string        Retourne le pattern
    */
    private function paramMatch($match)
    {
        if (isset($this->params[$match[1]])) {
            return '(' . $this->params[$match[1]] . ')';
        }

        return '([^/]+)';
    }

    /**
    * Vérifie la validité des paramètres
    * @param  string $param Nom du parametre à vérifier
    * @param  string $regex Regex à vérifier
    * @return object        Retourne l'objet en cours
    */
    public function with($param, $regex){
        // $this->params[$param] = str_replace('(', '(?:', $regex);
        $this->params[$param] = $regex;

        // Permet de faire du fluence pour enchainer les arguments
        // $router->get('/blog/:id', Blog#article)->with('id', '[O-9]+');
        return $this;
    }

    /**
    * Appel le controlleur et la méthode
    * @param  string callable Controlleur et méthode à appeler
    * @return string matches  Paramètres à envoyer
    */
    public function call(){
        return call_user_func_array($this->callable, $this->matches);
    }

}
?>

Ce que je veux

Je veux obtenir comme Grafikart à la 32ème minute et 10s pour être précis. C'est-à-dire le tableau récupérant les paramètres id et slug correctement.

Ce que j'obtiens

Lorsque je place un var_dump($this->matches) dans la méthode call() de Route.php pour savoir si il a bien matcher les paramètres, je récupère un warning disant :

Warning: preg_match(): Compilation failed: range out of order in character class at offset 11 in /test/core/Router/Route.php on line 57

Soit le preg_match() dans la méthode match() de Route.php. J'ai beau chercher, même sur google, je ne trouve pas.

Et ci-dessous ne résout non plus le problème..

if(preg_match($regex, $url, $matches) === false){
      return false;
}

Merci d'avance !

1 réponse


JeremyB
Auteur

J'ai résolu mon problème mais je ne comprend pas pourquoi.. C'était un problème de regex, sur l'id. Sur la page routes.php où je crée les routes, il y avait un soucis avec : with('id', '[0-9]+').

[0-9] ne fonctionne pas, seul [\d] fonctionne, ce qui donne : with('id', '[\d]+').

Une explication ?