Autoloader PHP Universel – Jouons avec les Patterns

Nous avons vu quelques Modèles de Conception, je propose ici de les mettre en pratique sur un sujet concret, la réalisation d’un autoloader universel qui sera capable de trouver vos classes et interfaces PHP dans n’importe quelle arborescence de répertoires (que vous définirez vous même).

Nous allons mettre en oeuvre le singleton, le décorateur, l’itérateur et la chaine de responsabilité.

Vous êtes prêt ?

Qu’est-ce qu’un Autoloader ?

Une question facile pour commencer. L’autoload est un mécanisme arrivé depuis la version 5 de PHP. Il permet de coder la recherche de classes afin d’éviter au développeur de systématiquement inclure ses bibliothèques.

Concrètement, cela transforme le code suivant

require_once ('./chemin/biblio1.php');
require_once ('./chemin/biblio2.php');
require_once ('./chemin/biblio3.php');
... ... ... ...
$Biblio = new Biblio1 (Biblio2::Constante, Biblio3::Constante);

en

require_once ('chemin/Autoloader.php');
//a partir de la, on peut utiliser ce que l'on veut qui fait parti de l'autoload
$Biblio = new Biblio1 (Biblio2::Constante, Biblio3::Constante);

Exposé de l’exemple

Nous avons une arborescence de code source avec plusieurs répertoires, à savoir :

  • /biblio1/et des sous répertoires
  • /autre bibliothèque
  • ../../../biblitothèque commune

Et nous voulons que notre code ne se préoccupe pas de l’inclusion de ces bibliothèques, qu’il se contente de les utiliser.

Ce que nous allons réaliser

  • Nous allons réaliser une méthode capable de trouver depuis un fichier PHP des Classes et des Interfaces.
  • Nous allons créer un code capable de parcourir des fichiers depuis un ou plusieurs répertoires, de façon récursive ou non, en utilisant les Itérateurs
  • Nous allons créer un code capable de filtrer uniquement les fichiers « .php » de nos Itérateurs, en utilisant un Décorateur.
  • Nous allons enregistrer notre Autoloader dans la chaine de responsabilité
  • Nous allons faire en sorte que notre Autoloader soit unique, en utilisant un Singleton

Le Singleton

class DirectoriesAutoloaderException extends Exception {}
class DirectoriesAutoloader {
   //--- Singleton
   private function __construct (){}
   private static $_instance = false;
   public static function instance (){
      if(self::$_instance === false){
         self::$_instance = new DirectoriesAutoloader();
      }
      return self::$_instance;
   }
   //--- /Singleton

C’est une partie facile, nous avons rendu le constructeur privé et avons crée une méthode statique qui retournera toujours la même instance de notre objet.

Le code « classique »

Nous voulons pouvoir paramétrer notre Autoloader pour y définir les répertoires ou chercher nos classes. Nous allons ainsi développer les méthodes concernées.

   public function addDirectory ($pDirectory, $pRecursive = true){
      if (! is_readable ($pDirectory)){
         throw new DirectoriesAutoloaderException('Cannot read from ['.$pDirectory.']');
      }
      $this->_directories[$pDirectory] = $pRecursive ? true : false;
      return $this;
   }
   private $_directories = array ();

Rien d’extraordinaire ici. On enregistre la liste des répertoires dans lesquels on va vouloir chercher les classes, en indiquant si l’on souhaite parcourir ces derniers de façon récursive.

A noter que l’on a utilisé une classe d’Exception personnalisée (Donner un type à vos Exceptions est une bonne pratique car elle vous permet de segmenter la gestion de vos erreurs).

Un dernier point à noter : la méthode retourne $this. J’aime utiliser ce genre de retour pour les méthodes de configuration afin de chaîner les appels de configuration.

Avec un tel chaînage, on pourra configurer notre autoloader de la façon suivante :

$autoloader = DirectoriesAutoloader::instance ()->addDirectory ('../utils/')
                            ->addDirectory ('../framework/');
                            ->addDirectory ('../widgets/');

L’extraction des classes

   private function _extractClasses ($pFileName){
      $toReturn = array ();
      $tokens = token_get_all (file_get_contents ($pFileName, false));
      $tokens = array_filter ($tokens, 'is_array');

      $classHunt = false;
      foreach ($tokens as $token){
         if ($token[0] === T_INTERFACE || $token[0] === T_CLASS){
            $classHunt = true;
            continue;
         }
         if ($classHunt && $token[0] === T_STRING){
            $toReturn[$token[1]] = $pFileName;
            $classHunt = false;
         }
      }
      return $toReturn;
   }

Cette méthode attend en paramètre un fichier que l’on va ouvrir et passer à la méthode token_get_all. Cette méthode parse le code reçu et le décompose en primitives PHP : Cela nous évite de développer un parser approximatif de code PHP.

Nous parcourons ensuite tous les tokens à la recherche d’une déclaration de classe ou d’interface, et lorsque nous avons cette déclaration, la chasse commence, nous recherchons son nom…… et ainsi de suite.

Revenons à nos patterns, les Iterateurs

Nous avions parlé d’Itérateurs. Nous allons les utiliser pour parcourir les répertoires à la recherche de nos fichiers PHP.

Le code est ici simpliste…

   private function _includesAll (){
      foreach ($this->_directories as $directory=>$recursive){
         $directories = new AppendIterator ();

         //On ajoute tous les chemins à parcourir
         if ($recursive){
            $directories->append (new RecursiveIteratorIterator (new RecursiveDirectoryIterator ($directory)));
         }else{
            $directories->append (new DirectoryIterator ($directory));
         }
         //On va filtrer les fichiers php depuis les répertoires trouvés.
         $files = new ExtensionFilterIteratorDecorator ($directories);
         $files->setExtension ('.php');

         foreach ($files as $fileName){
            $classes = $this->_extractClasses ((string) $fileName);
            foreach ($classes as $className=>$fileName){
               $this->_classes[strtolower ($className)] = $fileName;
            }
         }
      }
   }

Ici, nous utilisons plusieurs Itérateurs :

  • AppendIterator, un Itérateur de la SPL capable de parcourir une collection d’Itérateurs
  • DirectoryIterator, un Itérateur de la SPL capable de parcourir un répertoire pour y lister ses fichiers
  • RecursiveDirectoryIterator, un Itérateur de la SPL capable de parcourir un répertoire pour y lister ses fichiers, et qui propose des méthode pour parcourir le contenu des sous répertoires
  • RecursiveIteratorIterator, un Itérateur de la SPL capable de parcourir un Itérateur récursif comme s’il était un Itérateur à plat.

Le problème, c’est que nous devons uniquement parcourir les fichiers PHP, pas de problème, utilisons un décorateur !

Le décorateur

Nous allons créer une classe pour décorer nos Itérateurs et permettre de définir une extension de fichier pour filtrer les entrées (on ne souhaite parcourir que les fichiers .php)

class ExtensionFilterIteratorDecorator extends FilterIterator {
   private $_ext;
   public function accept (){
      if (substr ($this->current (), -1 * strlen ($this->_ext)) === $this->_ext){
         return is_readable ($this->current ());
      }
      return false;
   }
   public function setExtension ($pExt){
      $this->_ext = $pExt;
   }
}

Nous avons profité pour réaliser notre décorateur d’une classe de base de la SPL, le FilterIterator qui définit la méthode « accept » indiquant si l’entrée courante est acceptée. Seule les entrées acceptées seront parcourues, les autres seront ignorées.

Méthode Autoload & Enregistrement

La méthode Autoload sera basique, elle demande le parcours des répertoires configurés, l’extraction des classes des fichiers trouvés, puis l’inclusion des classes si elles existent bien.

   public function autoload ($pClassName){
      //On regarde si on connais la classe
      if (isset ($this->_classes[$className])){
         require_once ($this->_classes[$className]);
         return true;
      }

      //On va recharger la liste des classes connues
      if ($this->_canRegenerate){
         //pour ne charger les classes qu'une seule fois par page
         $this->_canRegenerate = false;
         $this->_includesAll ();
         return $this->autoload ($pClassName);
      }
      //on a vraiment rien trouvé.
      return false;
   }
   private $_canRegenerate = true;

Il n’y a plus qu’à enregistrer notre autoloader dans la chaîne de responsabilité grâce à la SPL (encore une fois).

$autoloader = DirectoriesAutoloader::instance ()->addDirectory ('repertoire');
spl_autoload_register (array ($autoloader, 'autoload'));

C’est fini !

Et voilà, nous avons un autoloader universel capable de trouver toutes les classes d’une arborescence.

EDIT 21/01/2011: Pour aller plus loin, vous pouvez consulter la suite de cet article ou l’autoloader est en mesure de gérer les espaces de nom pour PHP 5.3

L’ajout d’un Cache pour aller plus vite

Pour éviter de parcourir et analyser tous les fichiers à chaque demande de chargement, voici le code de l’autoloader complet, avec un système de cache pour sauvegarder d’un appel sur l’autre la liste des classes connues.

class ExtensionFilterIteratorDecorator extends FilterIterator {
	private $_ext;
	public function accept (){
		if (substr ($this->current (), -1 * strlen ($this->_ext)) === $this->_ext){
			return is_readable ($this->current ());
		}
		return false;
	}
	public function setExtension ($pExt){
		$this->_ext = $pExt;
	}
}

class DirectoriesAutoloaderException extends Exception {}
class DirectoriesAutoloader {
	//--- Singleton
	private function __construct (){}
	private static $_instance = false;
	public static function instance ($pTmpPath){
		if(self::$_instance === false){
			self::$_instance = new DirectoriesAutoloader();
			self::$_instance->setCachePath ($pTmpPath);
		}
		return self::$_instance; 
	}
	//--- /Singleton
	
	//--- Cache
	private $_cachePath;
	public function setCachePath ($pTmp){
		if (!is_writable ($pTmp)){
			throw new DirectoriesAutoloaderException('Cannot write in given CachePath ['.$pTmp.']');
		}
		$this->_cachePath = $pTmp;
	}
	//--- /Cache

	//--- Autoload
	public function autoload ($pClassName){
		//On regarde si on connais la classe
		if ($this->_loadClass ($pClassName)){
			return true;
		}

		//Si on a le droit de tenter la regénération du fichier d'autoload, on retente l'histoire
		if ($this->_canRegenerate){
			$this->_canRegenerate = false;//pour éviter que l'on 
			$this->_includesAll ();
			$this->_saveInCache ();
			return $this->autoload ($pClassName);
		}
		//on a vraiment rien trouvé.
		return false;
	}
	private $_canRegenerate = true;
	//--- /Autoload

	/**
	 * Recherche de toutes les classes dans les répertoires donnés
	 */
	private function _includesAll (){
		//Inclusion de toute les classes connues
		foreach ($this->_directories as $directory=>$recursive){
			$directories = new AppendIterator ();

			//On ajoute tous les chemins à parcourir
			if ($recursive){
				$directories->append (new RecursiveIteratorIterator (new RecursiveDirectoryIterator ($directory)));	
			}else{
				$directories->append (new DirectoryIterator ($directory));
			}

			//On va filtrer les fichiers php depuis les répertoires trouvés.
			$files = new ExtensionFilterIteratorDecorator ($directories);
			$files->setExtension ('.php');

			foreach ($files as $fileName){
				$classes = $this->_extractClasses ((string) $fileName);
				foreach ($classes as $className=>$fileName){
					$this->_classes[strtolower ($className)] = $fileName;
				}
			}
		}
	}

	/**
	 * Extraction des classes & interfaces d'un fichier
	 */
	private function _extractClasses ($pFileName){
		$toReturn = array ();
		$tokens = token_get_all (file_get_contents ($pFileName, false));
		$tokens = array_filter ($tokens, 'is_array');

		$classHunt = false;
		foreach ($tokens as $token){
			if ($token[0] === T_INTERFACE || $token[0] === T_CLASS){
				$classHunt = true;
				continue;
			}

			if ($classHunt && $token[0] === T_STRING){
				$toReturn[$token[1]] = $pFileName;
				$classHunt = false;
			}
		}

		return $toReturn;
	}
	private $_classes = array ();
	private function _saveIncache (){
		$toSave = '<?php $classes = '.var_export ($this->_classes, true).'; ?>';
		file_put_contents ($this->_cachePath.'directoriesautoloader.cache.php', $toSave);
	}

	/**
	 * Tente de charger une classe
	 */
	private function _loadClass ($pClassName){
		$className = strtolower ($pClassName);
		if (count ($this->_classes) === 0){
			if (is_readable ($this->_cachePath.'directoriesautoloader.cache.php')){
				require ($this->_cachePath.'directoriesautoloader.cache.php');	
                                $this->_classes = $classes;
			}
		}
		if (isset ($this->_classes[$className])){
			require_once ($this->_classes[$className]);
			return true;
		}
		return false;
	}
	
	/**
	 * Ajoute un répertoire a la liste de ceux à autoloader 
	 */
	public function addDirectory ($pDirectory, $pRecursive = true){
		if (! is_readable ($pDirectory)){
			throw new DirectoriesAutoloaderException('Cannot read from ['.$pDirectory.']');
		}
		$this->_directories[$pDirectory] = $pRecursive ? true : false;
		return $this;		
	}
	private $_directories = array ();
}
  1. Bonsoir,

    Très bon article une fois de plus, chapeau bas !
    Qu’en est-il côté rapidité/performance ?

    Merci par avance 🙂

  2. Bonjour et merci.

    Niveau performances, avec la mise en place du cache, les impacts sont de mon point de vue négligeables comparé au confort récupéré.

    Je n’ai pas fait de bench précis avec l’exemple présenté ici (qui est une version simplifiée d’un des autoloader que j’utilise sur plusieurs applications).

    Toutefois, l’autoloading est un processus coûteux pour qui est à la milliseconde prêt, pour les autres, inutile de s’en passer.

  3. Bonjour,

    J’ai juste une question :

    Est-ce que tu utilises cet autoloader sur un vrai projet, ou bien est-ce que c’est juste l’implémentation d’un cas d’école ?

  4. La veille du week-end (neuvième) | LoïcG - pingback on 10 décembre 2010 at 6 h 07 min
  5. Bonjour,
    comme je le dis dans le commentaire juste au dessus, c’est une version simplifiée d’un autoloader que j’utilise en production sur plusieurs applications.

    Les différences notables sont :
    – Celui de l’exemple gère un cache global la ou celui que j’utilise en production gère un cache par module
    – Celui de l’exemple prend la première classe venue la ou celui que j’utilise cherche d’abord par contexte (le module courant)

    La version utilisée en production, pour les plus curieux :
    http://svn.copix.org/browser/trunk/utils/copix/autoload/CopixModuleClassAutoloader.class.php?rev=8356

  6. Bonjour, je viens de tester votre exemple mais il me retourne une page blanche sans erreur.
    En faites je cherche un moyen plus rapide et plus souple pour mon autoload qui se fait vieux 🙂

    J’ai tester l’exemple avec mise en cache.
    J’ai simplement créer un dossier, placer une classe simple avec un print et fais l’appel dans un index mais rien ne se passe ?
    L’appel se fait bien comme sa :
    abstractdb = dossier contenant une classe pour l’exemple
    $autoloader = DirectoriesAutoloader::instance ()->addDirectory (‘abstractdb’);
    spl_autoload_register (array ($autoloader, ‘autoload’));

  7. Avec ini_set (‘display_errors’, 1); en première ligne, la page est toujours blanche ? ça nous permettra de voir plus clair (par exemple si c’est la mise en cache qui échoue)

  8. Instance attend un paramètre (le chemin du cache), c’est tout simplement surement ça.

  9. J’ai créer le dossier cache et placer ‘cache’ comme paramètre sa fonctionne mais je ne vois aucun fichier dans ce dossier ? Ne doit il pas créer des fichiers pour la mise en cache ?
    Je souhaite améliorer votre autoload car le mien est très vieux et pas super flexible.

    Voir:

    class frontend_Autoloader{
    /**
    * @static
    * @var path
    * string
    */
    private static $path;
    /**
    * @static
    * @var prefix
    * string
    */
    private static $prefix;
    /**
    * Registration
    * @access public
    * @static
    * @name register
    */
    public static function register(){
    self::$prefix = substr(__CLASS__, 0, strpos(__CLASS__, ‘_’)+1);
    self::$path = dirname(dirname(realpath(__FILE__))).DIRECTORY_SEPARATOR;
    // ici est opéré la registration
    spl_autoload_register(array(__CLASS__, ‘autoload’));
    }
    /**
    * Autoload
    * @param void $class
    * @access public
    * @static
    */
    public static function autoload($class)
    {
    // vérifie que ‘backend_’ est bien le prefix demandé
    if (strpos($class, self::$prefix) === 0) {
    if(file_exists(self::$path.str_replace(‘_’, DIRECTORY_SEPARATOR, $class).’.php’)){
    include self::$path
    .str_replace(‘_’, DIRECTORY_SEPARATOR, $class)
    .’.php’;
    }
    }
    }
    /**
    * Supprime un fichier de l’autoload (contraire de register)
    * @param void $class
    * @access public
    * @static
    */
    public static function unregister($class){
    spl_autoload_unregister(array($class, ‘autoload’));
    }
    }
    Je souhaite garder cette esprit de ne charger que les classes avec un prefixe particulier comment dois je m’y prendre ?
    Comme sa je n’aurai que les classes avec comme prefixe : mage_maclassphp{}
    Merci

  10. Il y a plusieurs solutions, l’une qui consiste à ajouter des objets en mesure de filter les noms de classes à conserver, l’autre qui se contente d’ajouter « en dur » un filtre sur les noms de classe (ligne 80).

    Un bête if strpos === 0 sur le préfixe et le tour est joué.

  11. Pour la problématique du fichier non crée, il est probable que l’application ne dispose pas des droits d’écriture sur ce dernier. Pour vérifier cela, vous pouvez modifier l’autoloader pour ajouter une exception lorsque file_put_contents retourne false (ligne 112).

  12. Bonjour,

    Ce chargeur ne fonctionnera pas « à priori » avec les espaces de noms. autoload recevra le nom de la classe complet de la classe par exemple, my\name\space\class ce qui ne pose pas de problème outre mesure dans ce code. Toutefois, la méthode _loadClasses ne s’occupe que des déclarations brutes de classes quelque soit l’espace de nom dans lequel elles sont faites, autrement dit elle ne recherche que le T_STRING qui suit T_CLASS. Pour deux classes de même nom mais de deux espaces de nom différents, le chargeur aura un comportement aléatoire qui aboutira à une erreur : La classe n’existe pas. Toutefois, plusieurs fonctions peuvent être déclarées comme autoload, si l’un échoue, la suivante est appelée. Si toutes échouent, cela provoque une erreur, si une réussi mais que la classe demandée n’existe pas, cela provoque aussi une erreur. Dans la mesure du possible, l’autoloader qui est ajouté ne devrait se soucier que de ce qui le concerne. Ainsi, ce chargeur ne se préoccupant que des dossiers qui lui ont été indiqués, le problème sera contourné tant que les classes attendus de ces dossiers sont définies dans l’espace de nom global. C’est pour cela que je débutais par ‘Ce chargeur ne fonctionnera pas « à priori » ‘

  13. Tout à fait d’accord Gabriel, comment améliorer l’autoload pour qu’il fonctionne convenablement avec les espaces de nom sachant que rester compatible PHP5.2 n’est pas un luxe.
    Remplacer la méthode _loadClasses brute par une version plus flexible.Sachant que dans mon cas comme cité plus haut je ne souhaite pas charger toutes les classes mais seulement celle avec un préfixe défini pour plus de faciliter et ne pas ajouter mes classes « outils » dans l’autoload (le strpos dans le filtre est une bonne idée).

  14. Excellente remarque Gabriel !

    L’autoloader présenté ne propose pas la gestion des espaces de nom (namespace) apparus en PHP 5.3+

    Voilà qui va me permettre d’enchaîner sur la présentation d’autres patterns pour étoffer l’exemple 🙂

    Stay tuned !

  15. Il ne faut pas grand chose pour ajouter le support des namespaces dans ce chargeur. Il « faut juste » savoir quel est le dernier espace de nom déclaré dans un fichier lorsque je rencontre un T_CLASS. Si aucun espace de non n’est mentionné, la classe est dans l’espace globale, sinon dans l’espace de nom déclaré. Un espace de nom sera toujours déclaré avant une classe, ce qui nous facilite la tâche.
    D’ailleurs, j’ai peut être ton prochain. Le design pattern Template.
    Pour améliorer ce chargeur, l’idée est de créer un TemplateTokenizer qui se charge de fournir une API pour parcourir une collection de tokens. Les classes filles se chargent elles de fournir la collection de tokens à parcourir. De là, on peut réaliser quelque chose qui ressemble à une ReflectionFile qui aura la charge, à l’aide d’un PHPTokenizer (qui hérite de TemplateTokenizer) de trouver toutes les classes déclarées dans un fichier (sans l’inclure évidement). Le chargeur utilise ReflectionFile pour chaque fichier qu’il rencontre.

  16. La Stratégie (Strategy) en PHP | Gerald's Blog - pingback on 29 décembre 2010 at 14 h 35 min
  17. J’ai choisi d’utiliser le modèle Stratégie pour illustrer l’exemple, même si dans le billet en question je donne l’option Template.

    Si je suis courageux d’ici ce soir, je ferais l’adaptation de l’exemple avec le Template.

  18. Voila pour le prefix mais c’est en dur, pas très flexible à moins de le déclarer dynamiquement via une autre fonction.
    foreach ($files as $fileName){
    $classes = $this->_extractClasses ((string) $fileName);
    foreach ($classes as $className=>$fileName){
    if(strpos($className,'factory') === 0){
    $this->_classes[strtolower ($className)] = $fileName;
    }
    }
    }

  19. Pour les gens pressés, il existe un Zend_Autoloader qui marche très bien en standalone (comme toute les modules zend).

  20. @Vincent : Je rajoute le lien : http://framework.zend.com/manual/fr/zend.loader.autoloader.html (je suppose que c’est de celui ci dont tu parlais)

  21. Effectivement l’autoloader de zend marche très bien mais quand est il des personnes qui n’utilise pas le zend framework ?
    C’est mon cas et je cherche un moyen de remplacer mon autoload actuel par une solution plus flexible.
    Qui est utilisable avec ou sans espace de nom suivant sa version de PHP.
    Pas évident je l’accorde mais celui-ci me semble très proche de ce que je cherche même si je n’ai pas encore décortiquer l’entièreté.
    Mon histoire de préfixe est simplement pour standardiser un peu tout sa tout en restant flexible et surtout rapide.
    Le cache est régénérer si l’autoload rencontre une nouvelle classes ?
    Merci à vous tous

  22. salut, j’aimerais avoir un exemple d’utilisation de votre code.

  23. Bonjour ou plutôt bonsoir
    voilà j’ai aimé le tutoriel
    Bon sauf moi voici mon problème:
    j’ai cette arborescence de fichier:
    -images
    -config
    -autoload.php
    -bg_class
    -classes(toutes les classes du projet)
    -manager(tous les managers)

    Ce que j’aimerais savoir c’est comment adapter mon fichier autoload.php pour qu’il scanne le contenu de du repertoire bg_class et cs sous repertoires
    et qu’il inclut les classes qui sy trouvent
    Merci d’avance de vos réponses!

  24. Hello. think jobwe’d I do not not anticipate thiswe would Thia password is a fantastic argument. grasping!

  25. Ca fait plusieurs fois que je vois des exemples d’autoloader sur le même principe, mais je ne saisi pas l’intérêt.

    Le but d’un autoloader, avant d’éviter au codeur de devoir taper tous ses require(), est surtout de ne charger que les classes strictement nécessaires à la requête reçue.

    Hors ici, on charge la quasi totalité des classes via file_get_contents(), sauf si on a beaucoup de chance et que les classes dont on a besoin se trouvent toutes au début de l’arborescence.

    Je ne sais pas si on peut comparer un file_get_contents() et un require() en terme de consommation mémoire, en tout cas tout cela me semble être assez lourd en terme de taille et complexité de cet autoloader et en terme de fonctionnement.

    On peut faire beaucoup plus simple et plus efficace en se basant sur des contraintes de nommage des répertoires/fichiers qui font écho au nommage des namespaces/classes.

    Exemple : http://www.php-fig.org/psr/psr-0/
    (Note : ce n’est pas de moi, et je n’ai pas encore testé, mais je trouve ça beaucoup plus efficace 😀 !!)

    • Bonjour,

      disclaimer en préambule : ce n’est pas un autoloader destiné à la production, mais surtout un autoloader pour manipuler des patterns, s’amuser un peu à réaliser du code.

      Toutefois, attention, j’ai l’impression qu’il y a quelques mauvaises compréhensions à la lecture de l’article. En aucun cas un file_get_contents ne « charge » le code en mémoire, il lit simplement le contenu d’un fichier sans le considérer comme du code. Dans l’exemple on l’utilise pour analyser les fichiers et trouver les classes qu’il contient avec un token_get_all. C’est ensuite qu’on sait ce qu’un fichier contient… et qu’on le met en cache.

      Dans l’exemple, on construit un de tableau clef valeur avec « cette classe se situe ici », ainsi, on ne charge effectivement que les classes nécessaires à l’exécution… la critique ainsi exprimée n’est plus justifiée.

      Oui, PSR-0 c’est très bien si vous voulez l’utiliser (et je sais bien que ce n’est pas de vous :-)), mais tout n’est pas psr0 et parfois on peut vouloir faire différemment (à tord ou a raison).

      • Oui je connais bien la différence entre file_get_contents et require depuis 15 ans que je fais du php lol 😉 Je me suis peut être mal exprimé mais je vous rassure il n’y a absolument aucune confusion entre les 2 😉

        La question n’était pas la … c’est juste que cela consomme de la mémoire en chargeant le contenu du fichier (et pas le code en tant que tel on est bien d’accord) et je trouve ça dommage de monter un autoload pour reproduire les inconvénients d’un système sans autoload, malgré le système de cache qui limite la casse.

        Concernant l’exemple PSR-0, évidemment ce n’est pas applicable partout, mais d’un autre côté c’est tellement facile de l’adapter à une autre structure (c’est d’ailleurs ce que j’ai fait personnellement) qu’il me semblait utile de partager cet autre approche ! 😀

        • Toutes mes excuses d’avoir mal interprété le commentaire.

          Pour en revenir au sujet, on est du coup d’accord sur le fait qu’à la création de l’autoload, la charge globale (mémoire ou temps d’exécution) est dramatiquement consommatrice et dépasse largement celle qu’un « include/require » de toutes les classes pourrait engendrer (ou un système de recherche de classe basée sur des noms de fichiers / conventions).

          Par contre, au deuxième chargement (une fois que le fichier de cache est présent), l’overhead est quasi nul (accès direct dans un tableau associatif) ainsi que l’impact mémoire.

          Encore une fois, ne perdons pas de vue le sujet de l’article (qui date de 2010) qui est principalement de mettre à l’oeuvre des modèles de conception, pas de proposer un autoloader (qui est devenu à ce jour quasi inclus avec n’importe quel framework / librairie « composerisée »).

          Pour être exhaustif, il faut noter que PSR-0 est à ce jour obsolète et est remplacé par PSR-4, mais cela ne change rien à l’échange.

          Merci d’avoir partagé votre retour.

Laisser un commentaire


NOTE - Vous pouvez utiliser les éléments et attributs HTML tags and attributes:
<a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <s> <strike> <strong>

Trackbacks and Pingbacks: