Maintenant que nous avons le moyen d’identifier des classes par leur espace de noms, et que cet espace peut représenter une arborescence de répertoires, nous allons structurer et distribuer nos fichiers en suivant cette règle.
On va se fixer une règle supplémentaire : un fichier pour une classe. À chaque appel de classe on va charger le fichier associé. Non seulement cela diminue la taille de vos fichiers, mais en prime PHP n’ira charger et analyser que le strict nécessaire au moment de la requête. :D
Voyons à quoi cette distribution peut ressembler avec l'exemple des messages du chapitre précédent. Le fichier index.php
:
<?php
require_once('Domain/Forum/Message.php');
$forumMessage = new Domain\Forum\Message;
var_dump(get_class($forumMessage));
Notre classe Message
a son propre fichier Message.php
dans le dossierForum
; lui-même à un l'intérieur du dossierDomain
. Volontairement, je reste dans la simplicité.
Voici le code de notre fichier Message.php
:
<?php
declare(strict_types=1);
namespace Domain\Forum;
class Message
{
}
Mais à présent, ajoutons une classe utilisateur
dédiée pour définir l'auteur du message.
Voyons ce que ça donne, pour le fichierindex.php
:
<?php
require_once('Domain/Forum/Message.php');
require_once('Domain/User/User.php');
use Domain\Forum\Message;
use Domain\User\User;
$user = new User;
$user->name = 'Greg';
$forumMessage = new Message($user, 'J\'aime les pates.');
var_dump($forumMessage);
Notre classe User
a son propre fichier User.php
dans le dossier User
; lui-même rangé à l'intérieur du dossier Domain
:
<?php
declare(strict_types=1);
namespace Domain\User;
class User
{
public $name;
}
Regardons ça de plus près dans le screencast ci-dessous :
Nous sommes obligés d’utiliser require_once
pour chaque fichier de chaque classe utilisée... Ça veut dire que pour toutes les classes que j'utilise potentiellement, je suis obligé de faire cet import de fichier. Même si l'utilisation de la classe est dans une condition IF et que la classe n'est pas utilisée systématiquement.
C'est pourquoi nous allons utiliser une technique grâce à une bibliothèque fournie avec PHP : le chargement automatisé de la bibliothèque standard PHP, SPL.
Tirez profit du chargement automatisé
Dans la bibliothèque SPL se trouve une fonction nommée spl_autoload_register
. Suivez-moi dans le screencast juste en dessous pour un premier aperçu de cette mécanique :
Lorsque vous tentez de charger une classe (instanciation, usage de constante, etc.) alors que PHP ne la trouve pas, PHP va appeler automatiquement les fonctions enregistrées précédemment à l’aide de spl_autoload_register
. À la suite de l’exécution des fonctions enregistrées, le script reprend son cours et tente à nouveau de charger la classe. Si le script échoue à nouveau, il s'arrête.
Puisque nous écrivons nos espaces de noms comme des chemins de répertoire, si notre arborescence de fichier correspond, alors on peut automatiser le chargement des fichiers :
<?php
spl_autoload_register(static function(string $fqcn) {
// $fqcn contient Domain\Forum\Message
// remplaçons les \ par des / et ajoutons .php à la fin.
// on obtient Domain/Forum/Message.php
$path = str_replace('\\', '/', $fqcn).'.php';
// puis chargeons le fichier
require_once($path);
});
use Domain\Forum\Message;
$forumMessage = new Message;
Ici, nous disons à PHP : “Si tu n’arrives pas à charger une classe, voici la fonction que tu peux exécuter pour tenter de la trouver”. La fonction en question débute à la fin de la ligne 3 et fait un require_once
de la classe à partir de son nom complet. Il se trouve que cette façon de faire est la méthode par défaut dans PHP. On aurait pu se contenter d'écrire :
spl_autoload_register();
Cette façon de répartir son code en fichiers et répertoires, et d'accorder les espaces de noms, est d'ailleurs très bien détaillée la recommandation standard de PHP PSR-4 (en anglais).
Eh bien, grâce à cette technique, les classes et donc les fichiers sont chargés uniquement lorsque c'est nécessaire ! Moins à parser, et moins à interpréter, donc de meilleures performances et moins de consommation de mémoire ! Des fichiers plus petits donc plus simples et moins susceptibles de contenir des erreurs ou des bugs ! Que demande le peuple ? Du code maintenable et évolutif ? OK OK on y va !
Exercez-vous
Il est temps de pratiquer. Utilisez la fonction spl_autoload_register
pour charger automatiquement vos classes, et répartissez-les dans une arborescence correspondant à vos espaces de noms afin de respecter PSR-4.
Vous trouverez le code sur la branche P3C2, et la correction sur la branche P3C2-correction.
En résumé
Les espaces de noms peuvent être utilisés pour correspondre à l'arborescence des fichiers, dans le but d'importer ces fichiers dynamiquement (PSR-4).
Utiliser SPL combiné aux espaces de noms permet de charger les classes à la volée.
Séparer les classes par fichiers réduit le nombre de fichiers à charger et à interpréter pour le langage.
Maintenant que nous avons un moyen de facilement gérer et distribuer nos classes dans notre système de fichiers, ce sera plus facile pour la suite. :) Regardons ensemble des concepts un peu plus avancés de l'héritage dans le prochain chapitre !