Front controller

Le Front Controllerest un fichier unique qui constitue le point d’entrée principal d’une application web reposant sur une architecture MVC.

Toutes les requêtes HTTP y sont redirigées afin qu’il analyse l’URL demandée et délègue le traitement au contrôleur et à l’action correspondants.

Dans notre cas, ce rôle est assuré par le fichier public/index.php.

Dans un premier temps, nous allons montrer comment extraire, à partir de l’URL, les informations relatives au contrôleur et à l’action.

Par exemple : utilisateur/create.php correspond au contrôleur utilisateur et à l’action create.

Définitionpublic/index.php

Toutes les requêtes passent par le fichier public/index.php.

Ce fichier constituant un point d’entrée unique, il permet de centraliser les éléments communs à l’ensemble du site.

On y inclut notamment le modèle, ainsi que le calcul du chemin relatif $page_base en fonction du nombre de dossiers présents dans l’URL.

Ce fichier a également pour rôle de récupérer, à partir de l’URL, la partie située après le dossier public, d’en extraire le contrôleur et l’action correspondants, puis d’appeler la ressource appropriée.

1
<?php
2
3
/**
4
 * Routeur minimaliste
5
 * -------------------
6
 * Rôle :
7
 *  - Nettoyer l’URL
8
 *  - Extraire contrôleur + action
9
 *  - Inclure dynamiquement le bon fichier contrôleur
10
 */
11
12
// Chargement du modèle (fonctions d’accès à la base de données)
13
require '../app/models/model.php';
14
15
16
/* -------------------------------------------------------------
17
   Construction d’une URL propre à partir de REQUEST_URI
18
------------------------------------------------------------- */
19
20
$scriptDir = str_replace('\\', '/', dirname($_SERVER['SCRIPT_NAME']));
21
$requestUri = parse_url($_SERVER['REQUEST_URI'], PHP_URL_PATH);
22
23
if (strpos($requestUri, $scriptDir) === 0) {
24
    $requestUri = substr($requestUri, strlen($scriptDir));
25
}
26
27
$url = '/' . ltrim($requestUri, '/');
28
29
/* -------------------------------------------------------------
30
   Découpage de l’URL en segments
31
------------------------------------------------------------- */
32
33
$params = $url === '/' ? [] : explode('/', trim($url, '/'));
34
35
36
/* -------------------------------------------------------------
37
   Calcul du chemin relatif vers la racine
38
------------------------------------------------------------- */
39
40
$page_base = str_repeat('../', max(0, count($params) - 1));
41
42
43
/* -------------------------------------------------------------
44
   Détermination du contrôleur et de l’action
45
------------------------------------------------------------- */
46
47
$controller = $params[0] ?? 'home';
48
$action = $params[1] ?? 'index';
49
50
/* -------------------------------------------------------------
51
   Gestion des erreurs : contrôleur ou action manquants
52
------------------------------------------------------------- */
53
54
$controllerFile = "../app/controllers/$controller/$action.php";
55
56
if (!file_exists($controllerFile)) {
57
    http_response_code(404);
58
    $controllerFile = "../app/controllers/error/index.php";
59
}
60
61
/* -------------------------------------------------------------
62
   Inclusion dynamique du fichier contrôleur
63
------------------------------------------------------------- */
64
65
require $controllerFile;

Construction d'une URL propre :

1
$scriptDir = str_replace('\\', '/', dirname($_SERVER['SCRIPT_NAME']));

Récupère le dossier contenant le script courant et normalise les slashs pour être compatible Windows et Linux.

URL demandée : http://localhost/public/utilisateur/show?id=2

dirname($_SERVER['SCRIPT_NAME'])/public

str_replace('\\', '/', …)/public

$scriptDir = '/public'

1
$requestUri = parse_url($_SERVER['REQUEST_URI'], PHP_URL_PATH);

Récupère uniquement le chemin de l’URL, sans les paramètres GET (?id=2) ni les fragments (#section).

$_SERVER['REQUEST_URI']/public/utilisateur/show?id=2

parse_url(..., PHP_URL_PATH)/public/utilisateur/show

$requestUri = '/public/utilisateur/show'

1
if (strpos($requestUri, $scriptDir) === 0) {
2
    $requestUri = substr($requestUri, strlen($scriptDir));
3
}

Si le chemin commence par le dossier du script (/public), on le retire pour ne garder que l’URL relative au routage.

$requestUri = '/public/utilisateur/show'

$scriptDir = '/public'

$requestUri = substr('/public/utilisateur/show', strlen('/public'))/utilisateur/show

1
$url = '/' . ltrim($requestUri, '/');

Supprime les slashs superflus au début et ajoute un slash unique pour uniformiser le chemin.

$requestUri = '/utilisateur/show'

ltrim + ajout //utilisateur/show

$url = '/utilisateur/show'

Découpage de l’URL :

1
$params = $url === '/' ? [] : explode('/', trim($url, '/'));

Récupération du contrôler et de l'action :

Transforme l’URL en tableau de segments. Si c’est la racine /, on renvoie un tableau vide.

$url = '/utilisateur/show'

trim($url, '/')utilisateur/show

explode('/', 'utilisateur/show')['utilisateur', 'show']

$params = ['utilisateur', 'show']

Chemin relatif vers la racine :

1
$page_base = str_repeat('../', max(0, count($params) - 1));

Calcule le chemin relatif vers la racine du site (utile pour les liens dans les vues).

$params = ['utilisateur', 'show']count($params) = 2

count($params) - 1 = 1

$page_base = str_repeat('../', 1)'../'

Résumé :

Ce fichier permet de simplifier l’ensemble du reste du code de l’application.

Il n’est désormais plus nécessaire de conserver le dossier utilisateur dans le dossier public, celui-ci peut donc être supprimé.

L’inclusion du modèle dans les contrôleurs ainsi que la création de la variable $page_base deviennent inutiles, puisque ces éléments sont désormais centralisés.

En contrepartie, il est nécessaire de modifier toutes les références aux liens du site : les attributs href dans les vues ainsi que les redirections effectuées avec la fonction header dans les contrôleurs, en supprimant l’extension .php.

Il faut distinguer les URLs logiques, qui passent par le système de réécriture d’adresse et sont interprétées par l’application, des URLs physiques, qui pointent vers des fichiers réellement présents dans le dossier public (images, feuilles de style, scripts JavaScript) et ne doivent pas être réécrites.

Le chemin du dossier de stockage des photos, utilisé dans les contrôleurs, doit également être adapté afin d’être relatif au Front Controller, par exemple : $relDir =  '../' disparait il ne reste que $photoDir = 'images/photos/'.

Il est nécessaire de créer un dossier error et d’y ajouter un fichier index.php, tant dans le répertoire controllers que dans views.

Méthodeapp/controllers/error/index.php

1
<?php
2
3
// ==============================
4
// VARIABLE POUR LA VUE
5
// ==============================
6
7
// Titre dynamique de la page
8
$page_title = 'Error 404';
9
10
require '../app/views/error/index.php';
11

Méthodeapp/views/error/index.php

1
<!DOCTYPE html>
2
<html lang='fr'>
3
4
<?php require '../app/views/head.php'; ?>
5
6
<body>
7
    <?php require '../app/views/header.php'; ?>
8
9
    <main>
10
        <h1>Error 404</h1>
11
    </main>
12
13
    <?php require '../app/views/footer.php'; ?>
14
</body>
15
16
</html>

Méthode

Nous pouvons maintenant vérifier le bon fonctionnement du site et observer que les URLs sont réécrites, sans inclure l’extension des fichiers.

En saisissant une URL incorrecte, on peut contrôler l’apparition d’une erreur 404 via l’outil d’inspection du navigateur.