JeuWeb - Crée ton jeu par navigateur
Chargement générique des classes (__autoload) - Version imprimable

+- JeuWeb - Crée ton jeu par navigateur (https://jeuweb.org)
+-- Forum : Discussions, Aide, Ressources... (https://jeuweb.org/forumdisplay.php?fid=38)
+--- Forum : Programmation, infrastructure (https://jeuweb.org/forumdisplay.php?fid=51)
+--- Sujet : Chargement générique des classes (__autoload) (/showthread.php?tid=1183)

Pages : 1 2


Chargement générique des classes (__autoload) - Myrina - 12-10-2010

Voici ma première contribution Cool

Je développe en PHP 5.2.6 en POO; actuellement je n'ai que 117 classes mais c'est déjà beaucoup et cela nécessite de l'organisation et surtout de la réorganisation (refactoring).

Je m'étais fait une version simple de la fonction __autoload qui déterminait l'arborescence dans laquelle se trouvait le fichier à inclure selon le nommage de la classe. Mais très vite, j'ai trouvé des limites à ce système car je n'aime pas avoir trop de classes dans un répertoire.

J'ai donc commencé à voir pour un système d'exploration d'un répertoire racine (./inc) afin de localiser le fichier contenant la classe. Mais ce système coûte en ressources et en temps, j'ai alors rajouté une mise en cache simple:


<?php
/**
* Gestion du chargement des classes
*
* @author Myrina
* @version 1.0
*/
if (!defined('SITE_ROOTDIR')) {
define('SITE_ROOTDIR',realpath(dirname(__FILE__)));
}

class ClassAutoLoader {

/**
* Nombre de classes chargées
*
* @var Integer
*/
private static $nbClasse;

/**
* Durée totale du chargement des classes
*
* @var Integer
*/
private static $duree;

/**
* Tableau contenant les chemins d'accès aux classes
*
* @var Array
*/
private static $path;

/**
* Transforme le nom de la classe en nom de fichier
*
* @param String $className Nom de la classe
* @return String Nom du fichier à charger
*/
private static function __class2file($className) {
return strtolower($className).'.inc.php';
}

/**
* search for folders and subfolders with classes
*
* @param $className string
* @param $sub string[optional]
* @return string
*/
private static function __classFolder($className, $sub = "/") {
$classDir=SITE_ROOTDIR.'/inc';
$dir = dir($classDir.$sub);

if(file_exists($classDir.$sub.self::__class2file($className)))
return $classDir.$sub;

while(false !== ($folder = $dir->read())) {
if($folder != "." && $folder != "..") {
if(is_dir($classDir.$sub.$folder)) {
$subFolder = self::__classFolder($className, $sub.$folder."/");

if($subFolder)
return $subFolder;
}
}
}
$dir->close();
return false;
}


/**
* Fonction d'autochargement de classe
*
* @param String $class Nom de la classe
*/
public static function __autoload($class) {
list($msec,$sec)=explode(' ',microtime());
$timeDebut=$msec+$sec;
$miniClass=strtolower($class);

if (!is_array(self::$path)) {
self::__initloader();
}

//Recherche dans le cache du répertoire
$ok=false;
if (array_key_exists($miniClass,self::$path)) {
if (file_exists(self::$path[$miniClass].self::__class2file($class))) {
$folder = self::$path[$miniClass];
$ok=true;
}
}

//Si classe pas trouvée en cache, localisation récursive lancée
if (!$ok) {
$folder = self::__classFolder($class);
}

if($folder) {
require_once($folder.self::__class2file($class));
self::$path[$miniClass]=$folder;
} else {
throw new Exception("Class $class not found");
}

list($msec2,$sec2)=explode(' ',microtime());
$timeEnd=$msec2+$sec2;
$duree=$timeEnd-$timeDebut;
self::$duree+=$duree;
self::$nbClasse++;
}

/**
* Fonction d'initalisation des données cachées concernant l'emplacement des classes
*/
public static function __initloader() {
self::$duree=0;
self::$nbClasse=0;
//Chargement du cache d'accès aux classes
if (self::__createcachefolder()) {
if (file_exists(SITE_ROOTDIR.'/cache/classes.cache')) {
self::$path=unserialize(file_get_contents(SITE_ROOTDIR.'/cache/classes.cache'));
} else {
self::$path=array();
}
} else {
self::$path=array();
}
spl_autoload_register(array('ClassAutoLoader','__autoload'));
}

/**
* Fonction de mise en cache des données concernant l'emplacement des classes
*/
public static function __saveloader() {
//Mise en cache
if (self::__createcachefolder()) {
$ser=serialize(self::$path);
$f= fopen(SITE_ROOTDIR.'/cache/classes.cache', "w+");
fputs($f,$ser);
fclose($f);
}
}

/**
* Fonction de suppression du fichier de cache
*/
public static function __cleancacheloader() {
if (self::__createcachefolder()) {
unlink(SITE_ROOTDIR.'/cache/classes.cache');
}
}

/**
* Fonction de création du répertoire de cache si celui-ci n'est pas présent
*
* @return boolean <b>true</b> si le répertoire est OK
*/
private static function __createcachefolder() {
//Si le répertoire n'existe pas
if (!is_dir(SITE_ROOTDIR.'/cache')) {
//Si il peut être créé
if (mkdir(SITE_ROOTDIR.'/cache',0777)) {
return true;
}
} else {
return true;
}
return false;
}

/**
* Renvoie le nombre de classes chargées
*
* @return Integer
*/
public static function getNbClasses() {
return self::$nbClasse;
}

/**
* Renvoie la durée du chargement de toutes les classes chargées
*
* @return Float
*/
public static function getDureeChargement() {
return self::$duree;
}

}
?>

La méthode __class2file() est éventuellement à réécrire pour le lien entre le nom d'une classe et le nom du fichier qui la contient.
Le répertoire initial (./inc) est également modifiable au début de la méthode __classFolder().

Pour l'utiliser, il faut effectuer un traitement au début du script PHP:

//Chargement de la fonction autoload
include_once('class.loader.php');
ClassAutoLoader::__initloader();

//Démarrage de la session
session_start();

et un autre à la fin du script PHP:

ClassAutoLoader::__saveloader();

Après cet appel,pour des fins statistiques,
ClassAutoLoader::getNbClasses() contient le nombre de classes chargées pour l'exécution du script;
ClassAutoLoader::getDureeChargement() contient la durée de la recherche et du chargement de toutes les classes.

EDIT: Il manque le plus important: les restrictions:
* un nom de classe doit être unique pour toute l'application
* une publication sur un serveur suite à un refactoring doit être précédé par un nettoyage des anciens fichiers; le cache, quant à lui, sait gérer les références mortes.

EDIT2: Encapsulation du code dans une classe


RE: Chargement générique des classes (__autoload) - srm - 12-10-2010

Une question comme ça, pourquoi tu ne t'inspires pas des Framework qui eux placent leur class dans divers sous répertoire et le nom de la classe y fait référence, exemple :

PHPUnit/Test/Case.php
Le nom de classe : PHPUnit_Test_Case
Il est ensuite très simple de retrouver le fichier.


RE: Chargement générique des classes (__autoload) - Myrina - 12-10-2010

(12-10-2010, 06:33 PM)oxman a écrit : Une question comme ça, pourquoi tu ne t'inspires pas des Framework qui eux placent leur class dans divers sous répertoire et le nom de la classe y fait référence, exemple :

PHPUnit/Test/Case.php
Le nom de classe : PHPUnit_Test_Case
Il est ensuite très simple de retrouver le fichier.
C'est justement que je ne voulais pas cette adhérence entre le nom de la classe et la localisation de celle-ci


RE: Chargement générique des classes (__autoload) - Sephi-Chan - 12-10-2010

C'est le fait d'avoir des noms de classe à rallonge qui te gêne ?


RE: Chargement générique des classes (__autoload) - srm - 12-10-2010

Pourtant c'est structuré, bon je te l'accorde c'est un peu de l'économie de bout de chandelle pour pouvoir faire l'équivalent de Scala/Java Wink


RE: Chargement générique des classes (__autoload) - Myrina - 12-10-2010

(12-10-2010, 06:50 PM)Sephi-Chan a écrit : C'est le fait d'avoir des noms de classe à rallonge qui te gêne ?
oui, plus le besoin de refactorer les classes dans des sous-répertoires plus spécifiques quand la volumétrie devient trop importante à mes yeux.


RE: Chargement générique des classes (__autoload) - niahoo - 12-10-2010

hmm perso la "volumétrie" ne me gêne pas, tant que les fichiers restent clairs avec pas trop de lignes.


RE: Chargement générique des classes (__autoload) - Anthor - 13-10-2010

Une petite question comme cela, pourquoi ne pas avoir fait une classe, pour gérer le loading des classes ? Smile


RE: Chargement générique des classes (__autoload) - Myrina - 13-10-2010

(13-10-2010, 10:22 AM)Anthor a écrit : Une petite question comme cela, pourquoi ne pas avoir fait une classe, pour gérer le loading des classes ? Smile
Je ne l'ai pas encore fait mais c'est largement envisageable grâce à spl_autoload_register().
De plus, cela me "résoudrait" la problématique d'utilisation de variables globales.

Maintenant, faire une classe non destiner à être instancié et pour ne regrouper que des fonctions statiques...(quoique, en Java, je n'ai pas le choix)


RE: Chargement générique des classes (__autoload) - Anthor - 13-10-2010

(13-10-2010, 10:33 AM)Myrina a écrit : De plus, cela me "résoudrait" la problématique d'utilisation de variables globales.

Maintenant, faire une classe non destiner à être instancié et pour ne regrouper que des fonctions statiques...(quoique, en Java, je n'ai pas le choix)

Comme tu le dis, c'est un regroupement de fonctions, avec des variables à réutiliser ^^