Suchen
Inside Wiki
Nützliche Links




 
phpforum.de bei Facebook
 
phpforum.de bei Twitter
 

Zurück   PHP Forum: phpforum.de > phpforum.de Wiki > phpforum.de Wiki

PHP Wiki Dieses Wiki sammelt Lösungen, zu Problemen, welche immer wieder im Forum auftauchen.

 
 
Artikel-Optionen Ansicht
  #1  

Standard Autoloading

 

Inhalte

Autoloading




Was ist das?



In PHP versteht man unter "Autoloading" den Vorgang, Klassen dynamisch nach zu laden, wenn sie in einer Datei benötigt werden. Der Autoloader übernimmt also somit die Aufgabe, die passende Datei zu finden, wo die aufgerufene Klasse definiert wurde, um diese ein zu binden.


Wozu soll das gut sein?



Stellt man sich einmal vor, man programmiere ein riesiges Framework oder ein anderes, größeres Projekt mit zig oder hunderten von Klassen. Irgendwann ist es recht nerventötend, jedes mal die zugehörige Datei für die Klassen manuell einzubinden, was dann in ewigen, listenartigen Codepassagen endet. Wenn es dann irgendwann nervt, baut man sich gar einen "General-Includer", der einfach alle Klassen-Dateien in sich inkludiert und alle anderen Dateien dann nur noch den General-Includer einbinden:
Code:
Klasse1 ----|                        |---- Datei1 (braucht nur Klasse1)
Klasse2 ----|\___General_Includer___/|---- Datei2 (braucht nur Klasse N)
...     ----|/                      \|---- ...
KlasseN ----|                        |---- DateiN (braucht nur Klasse 2 u. N)

Man erkennt sofort den Nachteil: längst nicht jede Datei benötigt alle Klassen, es werden aber nach Haudrauf-Prinzip alle eingebunden. Das bringt massigen Overhead und der PHP-Parser muss länger unnötig übersetzen ---> Größere Ladezeiten.
Jetzt kommt der Autoloader ins Spiel: Der Autoloader wird automatisch angepeilt, wenn eine Klasse aufgerufen wird (z.B. durch new, class_exists() usw.) Der Autoloader ist also somit eine ultimative Lösung für das Klassenproblem, der Programmierer muss sich jetzt nicht mehr darum kümmern, die Dateien einzubinden und es entsteht dabei kein Overhead, es wird nur das eingebunden, was benötigt wird.


Wie funktioniert nun das Autoloading



Zu allererst bietet PHP die magische Funktion __autoload() an, aber vorweg:

Warnung:
Man sollte diese Lösung besser nicht benutzen, warum, wird später erklärt.

Die Funktion __autoload() bekommt als Parameter den Klassennamen übergeben, die Funktion an sich wird, wie oben beschrieben, implizit beim Aufruf einer Klasse aufgerufen. Anhand des Klassennamens kann man nun die zugehörige Datei einbinden:

PHP Quellcode:
<?php

   function __autoload($class_name) {
  $file = $class_name . '.php'; // Binde Klassenname + Endung ein
  if(is_readable($file)) // Ist das eine Datei?
    require_once($file);
  // else keine Chance, Fatal Error
   }

   $object = new ObjectClass; // Aufurf vom Autoloader ---> __autoload('ObjectClass')
   // ---> require_once('ObjectClass.php');

?>


Man sieht, dass Klassen- und Dateiname der Klasse identisch sein müssen, was einen positiven Nebeneffekt hat, da man nun nicht lange nach einer Datei suchen muss und die Datei schön triftig je nach Klasse benannt ist. Genausogut kann man aber auch andere Möglichkeiten nutzen, wie die Datei gefunden werden soll, das hier ist aber der am meisten genutzte Weg.
Nun liegt die Klasse ja nicht immer im selben Verzeichnis, sondern auch mal anderswo und dann auch noch in ganz verschiedenen. Ein if-else-Gedöhns im Autoloader wäre eine nur unschöne Lösung. Stattdessen sollte man die Information des Pfades in den Klassennamen verankern:

PHP Quellcode:
<?php

  function __autoload($class_name) {
    $path = str_replace('_', DIRECTORY_SEPARATOR, $class_name);
    $file = $path . '.php';
    if(is_readable($file))
      require_once($file);
    // else keine Chance, Fatal Error
  }
 
  $object = new Default_ClassObject;
  // ---> require_once('Default/ClassObject.php');

?>


Mit PHP 5.3 wurde eine sehr schöne Neuerung diesbzgl. eingeführt: die Namenräume ( = Namespaces ), was das ist, wird bei php.net genauer erklärt. Mit diesen Namespaces können wir Klassen genauer addressieren, ein Beispiel:
PHP Quellcode:
<?php

// SQL/SQLObject.php

  namespace SQL;
 
  class SQLObject { }

?>


PHP Quellcode:
<?php

  function __autoload($class_name) {
    $path = str_replace('\\', DIRECTORY_SEPARATOR, $class_name);
    $file = $path . '.php';
    if(is_readable($file))
      require_once($file);
    // else Fatal Error
  }
 
  $object = new SQL\SQLObject; // Lade Klasse SQLObject aus dem Namensraum SQL
  // ---> require_once('SQL/SQLObject.php');

?>


Dem Autoloader wird als Parameter auch die Namensraum-Addresse übergeben, die Backslashes formen wir einfach in einen Pfad um und können die Datei einbinden. Man beachte, dass man mit Namespaces nur absolut addressieren kann, etwas wie ../ gibt es nicht. Mit einem Backslah am Anfang, etwa
PHP Quellcode:
$object = new \SQL\SQLObject;

wird die Klasse dementsprechend aus dem Document-Root "/" addressiert, wenn man in der Datei des Aufrufs ebenfalls einen Namespace definiert hat, denn sowas geht nicht:
PHP Quellcode:
<?php

  namespace SQL\Security;

  $object = new ..\SQLObject; // zack
  // dann von oben addressieren, bei 0 anfangen
  $object = new \SQL\SQLObject;

?>


Wenn das Projekt nicht direkt im Root liegt, sondern in einem Verzeichnis, kann man das auch als Default ohne Namespace angeben, indem man den Autoloader leicht verändert:

PHP Quellcode:
<?php

  function __autoload($class_name) {
    $path = str_replace('\\', DIRECTORY_SEPARATOR, $class_name);
    // Wir hängen ein Verzeichnis vorne an den String
    $file = 'MeinProjekt' . DIRECTORY_SEPARATOR . $path . '.php';
    if(is_readable($file))
      require_once($file);
    // else Fatal Error
  }

  $object = new SQL\SQLObject; // Lade Klasse SQLObject aus dem Namensraum SQL
  // ---> require_once('MeinProjekt/SQL/SQLObject.php');

?>


Jetzt müssen also auch die Namensräume mit den Verzeichnisnamen übereinstimmen und die beiden Hierachie-Bäume müssen identisch sein, der selbe schöne Nebeneffekt wie mit den Dateinamen. Und hinzu kommt, dass das alles Zend- und Pear-kompatibel ist.


Bessere Lösung



Wieso war diese Lösung jetzt nicht der richtige Weg, wie es oben erläutert steht? Das Problem ist recht simpel: Sie haben ihren persönlichen Autoloader geschrieben, der meinetwegen Namespaces nutzt. Wenn sie jetzt noch ein anderes Framework benutzen, wird dies 100%tig eine andere Autoload-Struktur haben, logisch, der Autoloader soll sich ja nur um die eigenen Klassen kümmern. Dieses Verhalten ist erwünscht, so gibt es einen Autoloader für das benutzte Framework und einen für das eigene Projekt. Doch halt, heißen die beiden dann nicht __autoload()? Da haben wir den Salat! __autoload() wurde 2 mal defineirt (Namenskollision --> Fatal Error) Wir können auf diese Weise nur einen Autoloader benutzen. Wenn wir Glück haben, kann man sie zusammenfassen, aber wenn man Pech hat, sieht das bei einem selbst so aus:
PHP Quellcode:
$object = new \SQL\SQLObject;

und im Framework
PHP Quellcode:
$controller = new MVC_Controller;

Ohne viel Zwischenarbeit ---> Keine Chance.
Dafür wurde in der SPL (Standard PHP Library) etwas neues entwickelt, nämlich das Prinzip, Funktionen zu einer Autoload-Queue hinzuzufügen. In dieser Warteschlange wird dann Autoloader 1 aufgerufen, scheitert er beim Einbinden kommt, ist Autoloader 2 dran usw.
Eine Funktion fügt man zu dieser Liste durch die Funktion spl_autoload_register hinzu, die als Parameter den Funktionsnamen benötigt:

PHP Quellcode:
<?php

  function mein_autoload($class_name) {
    $path = str_replace('\\', DIRECTORY_SEPARATOR, $class_name);
    $file = $path . '.php';
    if(is_readable($file))
      require_once($file);
    // else nächsten Autoloader probieren, wenn vorhanden
  }
 
  spl_autoload_register('mein_autoload'); // Funktion zur Liste hinzufügen

?>


Jetzt sind auch mehrere Autoloader möglich. Zu guterletzt kann man sich eine kleine Autoloader-Klasse bauen, um das ganze zu strukturieren. Den Autoloader muss man allerdings schon einbinden, schließlich kann er sich wohl kaum selbst einbinden, oder?

PHP Quellcode:
<?php

  class Autoloader
  {

    public static function LoadClass($classname) {
      $path = str_replace('\\', DIRECTORY_SEPARATOR, $classname);
      $file = 'MeinProjekt' . DIRECTORY_SEPARATOR .$path . '.php';
      if(is_readable($file)) {
        include_once($file);
      }
    }

    public static function Register() {
      spl_autoload_register(array(__CLASS__, 'LoadClass'));
    }

  }

?>

LoadClass() ist der eigentliche Autoloader, Register registiert die Autoloader-Methode in die Warteschlange. Um der Warteschlange Methoden statt Funktionen zu überreichen, übergibt man spl_autoload_register einfach ein Array mit dem Klassennamen an Position 0 und den Namen der Methode in Position 1.
Aufrufen kann man das dann einfach so:
PHP Quellcode:
<?php

namespace main;

  include_once('autoloader.php');
  Autoloader::Register();

  $object = new \SQL\SQLObject;

?>


Zusammenfassung



Der Autoloader kann Klassen laden, ohne dass man sie selber durch include/require einbinden muss. Der Autoloader wird implizit aufgerufen, wenn eine Klasse z.B. durch new aufgerufen wurde. Um Namenskollisionen zu vermeinden, sollte man statt __autoload() lieber eigene Autoloader mit spl_autoload_register() in der Loader-Warteschlange registrieren, anschließend wird Autoloader für Autoloader aufgerufen, bis die Datei gefunden und eingebunden wurde.

Weiteres zum lesen




Mitwirkende: Ad aCTa
Erstellt von Ad aCTa, 13.08.2009 am 13:52
Zuletzt bearbeitet von Ad aCTa, 19.08.2009 am 15:49
28 Kommentare , 11827 Betrachtungen

Dieser Text steht unter der GNU-Lizenz für freie Dokumentation


 

Lesezeichen

Stichworte
autoload, spl

Artikel-Optionen
Ansicht

Forumregeln
Es ist Ihnen nicht erlaubt, neue Themen zu verfassen.
Es ist Ihnen nicht erlaubt, auf Beiträge zu antworten.
Es ist Ihnen nicht erlaubt, Anhänge hochzuladen.
Es ist Ihnen nicht erlaubt, Ihre Beiträge zu bearbeiten.

BB-Code ist an.
Smileys sind an.
[IMG] Code ist an.
HTML-Code ist aus.

Gehe zu

Alle Zeitangaben in WEZ +2. Es ist jetzt 05:40 Uhr.


Powered by vBulletin® Version 3.8.8 (Deutsch)
Copyright ©2000 - 2018, Jelsoft Enterprises Ltd.
Powered by NuWiki v1.3 RC1 Copyright ©2006-2007, NuHit, LLC