Gestion des routes dans Phyxo en utilisant le composant Symfony Routing

Pour continuer dans la modernisation du code de Phyxo, passons à la gestion des URLS en utilisant le composant Routing de Symfony. Il faut bien évidemment avoir installer Symfony avant.

Pour ne casser aucune URL existantes, dans un premier temps je déplace le fichier index.php qui se trouve dans public à la racine en adaptant les chemins vers le répertoire vendor et le fichier .env. Evidemment je garde le précédent index.php que je mets dans un répertoire src/LegacyPages. Je désactive LegacyPages comme pour le namespace Phyxo dans config/services.yaml.

Il va maintenant falloir recréer les anciennes routes. Je commence par une route simple "about". Ce sera la même chose pour la plupart des routes :

  • déplacer le contrôleur dans src/LegacyPages
  • renseigner le fichier config/routes.yaml avec la route
  • créer le contrôleur dans src/Controller

Le déplacement du fichier ne pose pas de problème à l'adaptation du chargement de common.inc.php près. La route ressemble à ça :

about:
  path:  /about.{_format}
  controller: App\Controller\LegacyController::about
  defaults:
    _format: php

La création du contrôleur est un peu plus complexe. En fait, on va renvoyer une réponse en chargeant le contenu générer par l'ancien contrôleur. On encapsule le chargement du contenu dans un bloc ob_start/ob_get_clean et le tour est joué. Cela peut ressembler à ça :

public function about()
{
   ob_start();
   chdir('/var/projets/git/phyxo/src/LegacyPages');
   require '/var/projets/git/phyxo/src/LegacyPages/about.php';
   return new Response(ob_get_clean());
}

En allant à la page http://localhost/phyxo/index.php/about on se retrouve avec une exception symfony. Cela ne fonctionne pas mais cela veut tout de même dire que notre contrôleur est bien géré par symfony. Le problème provient du fait que l'ancienne page s'attend à trouver tout un tas de variable globale : $conf, $conn, $template, $lang, ... En ajoutant ces variables avec un "global $conf, $conn,..." juste avant la fonction ob_start ça va mieux. J'ai bien la page sans erreur apparente. Mais il manque le thème (en fait les feuilles de style css et les fichiers javascript). Le problème est lié au fait que Phyxo (comme Piwigo d'ailleurs) définit la racine des URLS à partir de la variable PHPWG_ROOT_PATH. Pour les décorréler je passe une variable globale PUBLIC_BASE_PATH dans le tableau $_SERVER que je vais exploiter dans le code pour positionner la racine du site. Il faut modifier la fonction get_root_url et utiliser la variable $_SERVER['PUBLIC_BASE_PATH'] si elle existe avant le reste du traitement :

function get_root_url()
{
    global $page;

    if (!empty($_SERVER['PUBLIC_BASE_PATH'])) {
        return $_SERVER['PUBLIC_BASE_PATH'] . '/';
    } elseif (!empty($page['root_path'])) {
      // reste du code identique
   }
}

Pour positionner correctement cette variable, on peut par exemple utiliser la méthode getBasePath() de l'object Request. Le contrôleur ressemble alors à ça :

public function about(Request $request)
{
	$_SERVER['PUBLIC_BASE_PATH'] = $request->getBasePath();

        global $conf, $conn, $services, $template, $user, $page, $persistent_cache, $lang, $lang_info;

        ob_start();
        chdir('/var/projets/git/phyxo/src/LegacyPages');
        require '/var/projets/git/phyxo/src/LegacyPages/about.php';
        return new Response(ob_get_clean());
}

Et cette fois la page ressemble en tout point à celle sans passer par Symfony ! Il faut maintenant généraliser cela et l'appliquer à toutes les routes. Pour les routes sans arguments telle que "/register", "/password", "/profile",... il est possible (au moins dans un premier temps) d'utiliser le même contrôleur avec un paramètre qui change. La route générique pourrait être :

fallback:
  path:  /{path_info}.{_format}
  controller: App\Controller\LegacyController::fallback
  defaults:
    _format: php
  requirements:
    path_info: about|register|password|profile|feed|notification

Et le contrôleur correspondant pourrait être :

public function fallback(Request $request, $path_info)
{
    $_SERVER['PUBLIC_BASE_PATH'] = $request->getBasePath();
    $_SERVER['PATH_INFO'] = $path_info;

    $legacy_file = sprintf('%s/%s.php', $this->container->getParameter('legacy_base_dir'), $path_info);

    global $conf, $conn, $services, $template, $user, $page, $persistent_cache, $lang, $lang_info;

    ob_start();
    chdir(dirname($legacy_file));
    require $legacy_file;
    return new Response(ob_get_clean());
}

Il faut bien évidemment déplacer les fichiers dans le répertoire src/LegacyPages et adapter la constante PHPWG_ROOT_PATH. Pour éviter de répéter le chemin vers le répertoire où sont stockés les anciens contrôleurs, j'ai ajouté une clé de configuration "legacy_base_dir" dans config/services.yaml que l'on récupère via le container.

Pour récupérer d'éventuels problèmes, une amélioration du contrôleur fallback est de gérer la création de la réponse dans un bloc try/catch en envoyant différentes exceptions en cas de problème. Par exemple :

public function fallback(Request $request, $path_info)
{
    $legacy_file = sprintf('%s/%s.php', $this->container->getParameter('legacy_base_dir'), $path_info);

    $_SERVER['PUBLIC_BASE_PATH'] = $request->getBasePath();
    $_SERVER['PATH_INFO'] = $path_info;

    return $this->doResponse($legacy_file);
}

private function doResponse($legacy_file)
{
    $_SERVER['PHP_SELF'] = $legacy_file;
    $_SERVER['SCRIPT_NAME'] = $legacy_file;
    $_SERVER['SCRIPT_FILENAME'] = $legacy_file;
    try {
        global $conf, $conn, $services, $template, $user, $page, $persistent_cache, $lang, $lang_info;

        ob_start();
        chdir(dirname($legacy_file));
        require $legacy_file;
        return new Response(ob_get_clean());
    } catch (Routing\Exception\ResourceNotFoundException $e) {
        return new Response('Not Found', 404);
    } catch (Exception $e) {
        return new Response('An error occurred', 500);
    }
}

Ce contrôleur peut s'appliquer pour les anciens contrôleur suivants : about, register, password, identification, profile, feed, notification, install, upgrade, upgrade_feed, random, comments, ws, action, tags Le traitement de i.php sera un peu à part et je le traiterais dans un second temps mais en restant à la racine il remplit son rôle.

Les contrôleurs suivants ont besoin d'un paramètre : search, tags, ... Certains contrôleurs ont besoin de paramètres variables. Mais l'idée est toujours la même. Pour les pages n'ayant pas d'erreurs, la barre de debug s'affiche en bas de page :

Tags via Symfony

On voit la route @images_by_tags, le code http 200, ... On est en terrain connu !

L'ensemble de ces modifications se trouve sur mon dépôt github via le commit déplacement des contrôleurs.

Haut de page