Créer son Framework PHP › L'Autoload
Essentiel dans un projet PHP conséquent, l'autoload va automatiquement importer les classes utilisées. Focus sur son implémentation dans notre framework PHP.
Rôle de l’autoload
Qui dit framework orienté objet dit classes, et qui dit classes dit import. Mais quoi de plus pénible que d’importer une classe PHP manuellement avec un require à chaque fois que l’on veut l’utiliser depuis un autre fichier ?!
Heureusement, il existe un procédé nous facilitant cette lourde tâche : l’autoload.
Le principe de l’autoload est simple, nous allons pouvoir dire à notre application :
Dès qu’une classe est utilisée, occupe-toi de l’importer pour moi.
Nous allons apporter notre propre logique framework à cette mécanique d’import afin que celle-ci fonctionne avec les namespaces définis dans notre application.
Création de l’autoload
1. Fonction d’autoload
C’est avec la fonction PHP spl_autoload_register() qu’il sera possible d’implémenter cette mécanique d’inclusion automatique de classes.
spl_autoload_register(function (string $class): void {
});La fonction spl_autoload_register() sera exécutée à chaque utilisation d’une classe.
Cette fonction va exécuter une fonction de callback anonyme qui récupérera dans la variable $class le nom de la classe en question. Si cette classe se situe dans un certain namespace, alors $class récupérera aussi ce namespace (ce qui sera systématiquement notre cas).
2. Éclatement du namespace
On cherche maintenant à éclater la variable $class sur le caractère \ de sorte à en isoler les différentes parties de son namespace. Cela est possible avec la fonction PHP explode().
spl_autoload_register(function (string $class): void {
$namespaceParts = explode('\\', $class);
});Si j’instancie la classe Foo\Bar\Demo, alors $namespaceParts contiendra ['Foo', 'Bar', 'Demo'].
3. Alias pour les namespaces
Ajoutons désormais dans notre code un tableau constant ALIASES dans lequel on associe aux clés :
Plugola valeurlibAppla valeursrc
const ALIASES = [
'Plugo' => 'lib',
'App' => 'src'
];
spl_autoload_register(function (string $class): void {
$namespaceParts = explode('\\', $class);
});L’objectif de ce tableau est de travailler en conservant de bonnes conventions de nommage au niveau de nos dossiers (📂 lib et 📂 src étant des noms de dossier assez génériques) tout en bénéficiant de namespaces plus esthétiques et « contextualisés ».
Plugofait référence aux classes du framework (situées dans le dossierlib).Appfait référence aux classes de l’application de l’utilisateur (situées dans le dossiersrc).
Logiquement les classes utilisées dans notre framework devront donc se situer dans un namespace commençant par App ou Plugo.
Adaptons le code de notre autoload en fonction :
const ALIASES = [
'Plugo' => 'lib',
'App' => 'src'
];
spl_autoload_register(function (string $class): void {
$namespaceParts = explode('\\', $class);
if (in_array($namespaceParts[0], array_keys(ALIASES))) {
$namespaceParts[0] = ALIASES[$namespaceParts[0]];
} else {
throw new Exception('Namespace « ' . $namespaceParts[0] . ' » invalide. Un namespace doit commencer par : « Plugo » ou « App »');
}
});Ici, on vérifie si la première portion du namespace de la classe correspond à une clé du tableau ALIASES :
- Si c’est le cas, on redéfinit la première portion du namespace par le dossier équivalent (
libousrcen fonction). - Sinon, on génère une erreur PHP.
4. Inclusion dynamique de la classe
Pour inclure automatiquement une classe il nous faut connaître son emplacement. Et pour cela, nous allons procéder en respectant une règle simple : namespace = chemin physique.
Par exemple, si une classe se situe dans le namespace App\Entity, on s’attend à ce qu’elle se situe dans les dossiers 📁 src/Entity.
const ALIASES = [
'Plugo' => 'lib',
'App' => 'src'
];
spl_autoload_register(function (string $class): void {
$namespaceParts = explode('\\', $class);
if (in_array($namespaceParts[0], array_keys(ALIASES))) {
$namespaceParts[0] = ALIASES[$namespaceParts[0]];
} else {
throw new Exception('Namespace « ' . $namespaceParts[0] . ' » invalide. Un namespace doit commencer par : « Plugo » ou « App »');
}
$filepath = dirname(__DIR__) . '/' . implode('/', $namespaceParts) . '.php';
if (!file_exists($filepath)) {
throw new Exception("Fichier « " . $filepath . " » introuvable pour la classe « " . $class . " ». Vérifier le chemin, le nom de la classe ou le namespace");
}
require $filepath;
});$filepath va contenir une chaîne de caractères résultant de la concaténation de :
dirname(__DIR__) . '/': est une expression qui est utilisée pour obtenir le chemin absolu vers le répertoire parent du répertoire courant (__DIR__est une constante qui représente le chemin absolu du répertoire courant etdirname()retourne le chemin du répertoire parent)implode('/', $namespaceParts): la reconstitution du namespace, réécrite par notre script (en remplaçant respectivementPlugoetAppparlibetsrc).php: l’extension d’un fichierPHP
Ainsi, la variable $filepath va contenir le chemin vers la classe en question.
- Si aucun fichier n’existe, on lève une erreur
- Sinon, on inclut la classe
Mise à jour de index.php
Notre autoload est fonctionnel ! Il ne nous reste plus qu’à l’inclure dans notre contrôleur frontal 📄 index.php :
require dirname(__DIR__) . '/lib/autoload.php';Tester l'autoloader
Pour vérifier et comprendre le fonctionnement de l’autoload, il peut être intéressant de créer une classe temporaire dans votre projet :
namespace Plugo\Temp;
class Demo {
// ...
}Ensuite, l’importer avec un use :
use Plugo\Temp;
new Demo();Une fois cela, vous pourrez l’instancier car l’autoload va automatiquement l’inclure.