Man kann nicht nur über die composer.json den Autoloader befüllen. Wenn man z.B. Module hat die nicht über den Composer installiert werden sondern aus einem Verzeichnis geladen werden, müsste man die Einträge per Hand vornehmen, was aber nicht wirklich ideal ist.
$autoloader = require_once("../vendor/autoload.php");
foreach(ModuleFactory->getModuleList("modules/") as $module){
if(is_dir($module->getPath() . "/deploy/classes")){
$autoloader->addPsr4($module->getName()."\", $module->getPath() . "/deploy/classes");
}
}
Damit kann schnell und einfach alle Klassen des Modules hinzufügen und muss keinen eigenen ClassLoader schreiben.
Es ist natürlich langsamer als die über "dump-autoload" angelegten Mappings, aber dafür auch sehr viel flexibler.
Das Geheimnis beim Class-Loading in PHP ist die Funktion __autoload, diese Funktion wird immer aufgerufen bevor eine Instanz einer Klasse erzeugt wird.
function __autoload($classname){
}
Hier kann man also anhand des Klassennamens den Code der Klasse nachladen. Man kann natürlich auch per Hand in jeder Datei über include() die Klassen laden, die man braucht. Meistens läuft es dann darauf hinaus, dass man zu viel lädt und Zugriffe auf das Dateisystem ist eben sehr teuer. Mit der autoload-Funktion ist sicher gestellt nur das zu laden, was auch wirklich benötigt wird und sort dafür, dass möglichst wenige Dateizugriffe durch geführt werden. Wenn also eine Klasse in einem Code-Block verwendet wird der nie ausgeführt wird, wird der Code der Klasse nicht aus dem Dateisystem nachgeladen.
Ein sehr simpler Class-Loader kann also so aussehen:
function __autoload($classname){
if(!class_exists($classname)){
include_once("classes/".$classname.".php");
}
}
Alle Klassen-Dateien müssen an einem bestimmten Ort legen, hier im Order "classes/". Wenn man nun mit Modulen arbeitet, wo ein Modul die Klassen eines übergeordneten Modules nutzen kann, hat man das Problem, dass man nicht immer genau weiß wo eine Klasse liegt und man muss diese Suchen. Bekannt ist das Problem beim Class-Loading in Java, wo ein Package über mehrere JAR-Files verteilt sein kann und man also in allen JAR-Files nach einer Klasse muss, weil man durch Package und Klassennamen nicht auf die JAR schliessen kann in welcher die Klassen-Datei liegt.
Suchen ist natürlich auch sehr teuer, gerade wenn man rekursiv durch Verzeichnisstrukturen laufen will. Wenn man nun bei jeder Klasse alle Verzeichnisse durchläuft bis man eine Klasse gefunden hat, würde alles sehr lange dauern.
Eine Lösung ist, bekannte Pfade sich zu merken, damit man nicht immer wieder das selbe Suchen muss. Wenn man in einem Modul unterwegs ist, hat man es mit maximal zwei Händen voll von Klassen zu tun, die immer und immer wieder verwendet werden. Wenn ich aber durch eine Liste von Dateien laufe und bei jeden Item in der Liste prüfe, ob es das passende ist (ok.. man könnte auch file_exists() innerhalb des Verzeichnisses verwenden...) kann man sich die gefunden Klassen und deren Pfade bis zum Pfund der gesuchten Klasse merken.
function __autoload($classname){
if(!class_exists($classname)){
if(!isset($_SESSION["XWCLASSCACHE"])){
$_SESSION["XWCLASSCACHE"]=array();
}
$cache=$_SESSION["XWCLASSCACHE"];
$seeker=null;
if(!isset($cache[$classname])){
$seeker=new XWRecursiveClassSeeker();
$cache=$seeker->createCache("system/classes/",$classname,$cache);
}
if(isset($cache[$classname])){
try{
$classPath=$cache[$classname];
if(is_file($classPath)){
include_once($classPath);
$_SESSION["XWCLASSCACHE"]=$cache;
}
else{
unset($cache[$classname]);
$_SESSION["XWCLASSCACHE"]=$cache;
}
}
catch(Exception $e){
if(isset($cache[$classname])){
unset($cache[$classname]);
}
echo "<!-- ".$e->getMessage()." -->";
}
}
}
}
Hier wird alles in einen Cache geladen. XWRecursiveClassSeeker durchläuft alle Verzeichnisse rekursiv und vermerkt jeden Pfad zu jeder Klasse auf die er trifft. Wenn die gesuchte Klasse gefunden wurde bricht er ab. Dann wird der Code der Klasse nachgeladen. Alles wird auch in de Session gespeichert so dass man pro Request kaum noch Dateisystemzugriffe zum Suchen hat und nur noch für die includes der Klassen-Datein.
Das Problem ist, wenn man Klassen-Dateien umkopiert, weil dann die Pfade im Cache nicht mehr stimmen. Hier werden diese in dem Fall aus dem Cache entfernt, so dass beim nächsten Versuch die Klasse zuladen wieder neu gesucht wird und es dann auch klappen sollte.
Über dieses Vorgehen, kann man auch in PHP einen relativ peformanten und flexiblen Class-Loader schreiben und muss sich nie wieder selbst um includes von Klassen kümmern!