Autoload von Klassen - 3 Varianten - Meinungen?

Raisch

extraordinary bit
Hallo Community,

ich möchte euch hier meine 3 Varianten für den Autoload von Klassen vorstellen und suche Meinungen/Erfahrungen.
Was ist besser? Warum ist es besser? Gibt es noch andere Möglichkeiten?

Fragen über Fragen, aber hier mal zu meinen Varianten:

Variante 1 __autoload()
PHP:
function __autoload( $class ) {
    if ( !class_exists( $class ) && !interface_exists( $class ) ) {
        $class = strtolower( $class );
        $paths = array(
            './.include/class/'.$class.'.class.php',
            './.include/class/abstract/'.$class.'.abstract.php',
            './.include/class/interface/'.$class.'.interface.php'
        );

        foreach ( $paths as $file ) {
            if ( file_exists( $file ) && is_file( $file ) ) {
                require_once $file;
                return true;
            }
        }

        die( "<h1>Error: Class '<em>$class</em>' not found!</h1>" );
    }

    return true;
}

Variante 2 Autoloadklasse mit manueller Einbindung
PHP:
abstract class RsAutoLoad
{
    private static $path = null;
    private static $ext = null;

    public static function init()
    {
        $root = $_SERVER['DOCUMENT_ROOT'].'/foo/bar/';

        self::$path = array (
            'cls' => $root.'.include/class/',
            'abs' => $root.'.include/class/abstract/',
            'itf' => $root.'.include/class/interface/'
        );
        self::$ext = array (
            'cls' => '.class.php',
            'abs' => '.abstract.php',
            'itf' => '.interface.php'
        );

        spl_autoload_register( 'RsAutoLoad::loadClass'     );
        spl_autoload_register( 'RsAutoLoad::loadAbstact'   );
        spl_autoload_register( 'RsAutoLoad::loadInterface' );
    }

    public static function loadClass( $class )
    {
        return self::registerClass(
            $class, self::$path['cls'].strtolower( $class ).self::$ext['cls']
        );
    }

    public static function loadAbstact( $class )
    {
        return self::registerClass(
            $class, self::$path['abs'].strtolower( $class ).self::$ext['abs']
        );
    }

    public static function loadInterface( $class )
    {
        return self::registerClass(
            $class, self::$path['itf'].strtolower( $class ).self::$ext['itf']
        );
    }

    private static function registerClass( $class, $path )
    {
        if ( file_exists( $path ) && is_file( $path ) ) {
            require_once $path;
            return true;
        }

        return false;
    }
}

RsAutoLoad::init();

Variante 3 Autoloadklasse mit Einbindung über spl_autoload()
PHP:
abstract class RsAutoLoad
{
    private static $path = null;
    private static $ext = null;

    public static function init()
    {
        $root = $_SERVER['DOCUMENT_ROOT'].'/foo/bar/';

        self::$path = array (
            'cls' => $root.'.include/class/',
            'abs' => $root.'.include/class/abstract/',
            'itf' => $root.'.include/class/interface/'
        );
        self::$ext = array (
            'cls' => '.class.php',
            'abs' => '.abstract.php',
            'itf' => '.interface.php'
        );

        spl_autoload_register( 'RsAutoLoad::loadClass'     );
        spl_autoload_register( 'RsAutoLoad::loadAbstact'   );
        spl_autoload_register( 'RsAutoLoad::loadInterface' );
    }

    public static function loadClass( $class )
    {
        self::feedSplAutoLoad( self::$path['cls'], self::$ext['cls'], $class );
    }

    public static function loadAbstact( $class )
    {
        self::feedSplAutoLoad( self::$path['abs'], self::$ext['abs'], $class );
    }

    public static function loadInterface( $class )
    {
        self::feedSplAutoLoad( self::$path['itf'], self::$ext['itf'], $class );
    }

    private static function feedSplAutoLoad( $path, $ext, $class )
    {
        //set_include_path( $path.PATH_SEPARATOR.get_include_path() );
        spl_autoload_extensions( $ext );
        spl_autoload( $path.$class );
    }
}

RsAutoLoad::init();

Viel Code wenig Text, wenn noch Fragen sind... ich höre.

Gruß
 
Hi,

bei der ersten Variante ist doch diese Zeile sinnlos:
PHP:
if ( !class_exists( $class ) && !interface_exists( $class ) )
Denn sonst würde __autoload() ja gar nicht aufgerufen werden, oder?

Zwischen Variante 2 und 3 sehe ich nicht so einen großen Unterschied.
Allerdings verursachen die Klassen bestimmt einen gewissen Geschwindigkeitsverlust gegenüber nur einer Funktion.

In meinem letztem Projekt habe ich einfach eine Funktion + [phpf]spl_autoload_register[/phpf] genutzt.
Somit konnte man mehrere Autoload-Funktionen definieren.
PHP:
<?php

function myproject_autoload($class)
{
  /* ... */
}
spl_autoload_register('myproject_autoload');

?>

Weil ich da großen Wert auf Performance gelegt habe, habe ich auch noch eine Cache-Funktion eingebaut gehabt.
Also wenn diese Autoload-Funktion aufgerufen wurde und die Klasse gefunden wurde, wird der Pfad zur Datei gecacht.

Außerdem habe ich bei "eigenen" Skripten die Klassen immer selber per [phpf]require_once[/phpf] eingebunden (schneller). Also war diese Autoload-Funktion größtenteils für User meines Projektes relevant.
 
Hi,

bei der ersten Variante ist doch diese Zeile sinnlos:
PHP:
if ( !class_exists( $class ) && !interface_exists( $class ) )
Denn sonst würde __autoload() ja gar nicht aufgerufen werden, oder?
Da hast Du vollkommen Recht.

Zwischen Variante 2 und 3 sehe ich nicht so einen großen Unterschied.
Nicht groß, haben aber beide Vor- und Nachteile.

Allerdings verursachen die Klassen bestimmt einen gewissen Geschwindigkeitsverlust gegenüber nur einer Funktion.
Kann ich nicht bestätigen. Nach Performancetest mit mehreren Klassenaufrufen bewegen sich alle 3 Varianten zwischen 0.012 und 0.018 Millisekunden.

Da meine Projekte komplett Objektorientiert sind bevorzuge ich definitiv eine Klasse.

In meinem letztem Projekt habe ich einfach eine Funktion + [phpf]spl_autoload_register[/phpf] genutzt.
Somit konnte man mehrere Autoload-Funktionen definieren.
PHP:
<?php

function myproject_autoload($class)
{
  /* ... */
}
spl_autoload_register('myproject_autoload');

?>
Mache ich in den Klassen nicht wirklich anders.

Weil ich da großen Wert auf Performance gelegt habe, habe ich auch noch eine Cache-Funktion eingebaut gehabt.
Also wenn diese Autoload-Funktion aufgerufen wurde und die Klasse gefunden wurde, wird der Pfad zur Datei gecacht.
Würde mich sehr interessieren wie Du das umgesetzt hast. Kann auch per PM sein, wenn Du das nicht öffentlich posten möchtest.

Außerdem habe ich bei "eigenen" Skripten die Klassen immer selber per [phpf]require_once[/phpf] eingebunden (schneller). Also war diese Autoload-Funktion größtenteils für User meines Projektes relevant.
Das mag schon sein aber ich möchte nicht dutzende Klassen jedesmal einbinden, da ist so ein autoload schon bequemer für so einen faulen Menschen wie mich. :)

Gruß
 
Ich habe die __autoload()-Funktion mal wie folgt geändert:
PHP:
function __autoload( $class ) {
    $class = strtolower( $class );
    $root = $_SERVER['DOCUMENT_ROOT'].'/foo/bar/';

    $paths = array(
        $root.'.include/class/'.$class.'.class.php',
        $root.'.include/class/abstract/'.$class.'.abstract.php',
        $root.'.include/class/interface/'.$class.'.interface.php'
    );

    foreach ( $paths as $file ) {
        if ( file_exists( $file ) && is_file( $file ) ) {
            require_once $file;
            break;
        }
    }

    if ( !class_exists( $class, false ) &&
         !interface_exists( $class, false ) ) {
        die( "<h1>Error: Class '<em>$class</em>' not found!</h1>" );
    }
}

Über weitere Meinungen/Erfahrungen würde ich mich freuen.

Gruß
 
Wieso verwendest du [phpf]file_exists[/phpf] und [phpf]is_file[/phpf]?
Ersteres würde meiner Meinung in deinem Fall reichen.

PHP:
if ( !class_exists( $class, false ) && 
         !interface_exists( $class, false ) ) { 
        die( "<h1>Error: Class '<em>$class</em>' not found!</h1>" ); 
    }
Eigentlich bräuchtest du dich darum nicht kümmern, denn PHP würde auch selbst einen Fehler ausgeben.

Übrigens würde diese Funktion für Namespaces (falls du die mal verwendest) nicht funktionieren, denn diese würden im Pfad dann einen Backslash hinzufügen.

Aber das sind im Prinzip nur Kleinigkeiten ;)
 
Wieso verwendest du [phpf]file_exists[/phpf] und [phpf]is_file[/phpf]?
Ersteres würde meiner Meinung in deinem Fall reichen.
Prinzipiell ja. Ich gehe aber immer davon aus, dass ich meine Scripte/Projekte mal zum Download bereit stelle und wenn dann einer in dem Klassenordner einen Ordner erstellt der wie eine der Klassen heißt... (kann ja passieren^^)

PHP:
if ( !class_exists( $class, false ) && 
         !interface_exists( $class, false ) ) { 
        die( "<h1>Error: Class '<em>$class</em>' not found!</h1>" ); 
    }
Eigentlich bräuchtest du dich darum nicht kümmern, denn PHP würde auch selbst einen Fehler ausgeben.
Naja, ich hab lieber so eine Meldung als einen Fatal Error wo der Pfad etc. drin steht.

Übrigens würde diese Funktion für Namespaces (falls du die mal verwendest) nicht funktionieren, denn diese würden im Pfad dann einen Backslash hinzufügen.

Aber das sind im Prinzip nur Kleinigkeiten ;)
Das ist mir schon klar. Da auf meinem Server aber PHP < 5.3 läuft, kümmere ich mich um Namespaces noch nicht wirklich.

Gruß
 
Zurück