Ankündigung

Einklappen
Keine Ankündigung bisher.

[Erledigt] __autoload() effizient verwenden

Einklappen

Neue Werbung 2019

Einklappen
X
  • Filter
  • Zeit
  • Anzeigen
Alles löschen
neue Beiträge

  • Tidus
    hat ein Thema erstellt [Erledigt] __autoload() effizient verwenden.

    [Erledigt] __autoload() effizient verwenden

    Hallo Zusammen,

    lasst euch bitte nicht von vorne rein von dem Titel des Themas abschrecken.
    Das Thema __autoload() ist hier schon ziemlich oft besprochen worden, wobei es meist um das Verständnis der Funktion an sich ging.

    Bei meinem aktuellen Problem geht es aber nicht um das Verständnis der Funktion, sondern viel mehr, einen Weg zu finden, diese optimal zu verwenden.

    Mir ist bewusst, dass es in dieser Richtung schon einige Lösungsansätze gibt. Aber vorerst möchte ich euch mal schildern wie es zu meiner gleich gestellen Fragen kommt.

    Also wie viele von euch wissen, ist die __autoload() - Funktion, eine Interzeptor - Funktion, die automatisch aufgerufen wird, sobald ein Objekt einer Klasse mit dem "new"-Operator instanziiert werden soll, diese allerdings noch nicht deklariert ist. __autoload() bekommt also den Namen der aufgerufenen ( noch nicht deklarierten ) Klasse als Parameter und kann nun die entsprechende Klasse nachladen.

    Dieses Muster findet man bereits in einigen Frameworks wieder. Viele dieser Lösungen sind sehr ähnlich. So löst das Zend - Framework das Problem so, das die Klassennamen Unterstriche enthalten, diese später durch slashes ersetzt werden und somit einen gültigen Pfad zur entsprechenden Klasse darstellen.

    So, oder so ähnlich würde es aussehen:

    PHP-Code:

    $frontcontroller 
    = new Zend_Controller_Front(); 
    Aus dieser Klassenbezeichnung wird also:

    Zend/Controller/Front.php
    Dieser Lösungsansatz, ist mein persönlicher Favorit. Nur gibt es hier eine Sache die mich gewaltig stört. Und zwar ist das die Bezeichnung der Klassen im Code.

    Habe ich beispielsweise eine Ordnerstruktur die so aussieht:

    /user/admin.php
    und:

    /user/teacher.php
    Könnte ich diese beiden Dateien, oder eher die Klassen, die diese Dateien enthalten, nach Zend - Konventionen in dieser Form nachladen:

    PHP-Code:

    $admin 
    = new User_Admin();

    $teacher = new User_Teacher(); 
    Diese Schreibweise macht den Code sicherlich nicht unleserlich, aber dennoch nicht sehr schön.

    Nach weiteren Überlegungen dachte ich man könnte es folgendermaßen schöner machen:

    Eine Instanziierung der Klasse Admin (für dieses Beispiel) würde so aussehen:

    PHP-Code:

    $user 
    = new UserAdmin(); 
    Die Unterstriche werden also einfach rausgelassen und der Klassenbezeichner im Code, entspricht dem Pfad der nach zuladenen Klasse im Camel Case format. Hier könnte man die __autoload() Funktion also mit regulären Ausdrücken ausstatten, die die Positionen der einzelnen Großbuchstaben ermittelt und zwischen diesen Vorkommnissen ( also User und Admin in diesem Beispiel) ein Slash setzen. Daraus würde dann wieder ein entsprechender Pfad erzeugt werden, der zum Nachladen der Klasse
    genutzt werden kann.

    Aber auch hier finde ich einige Punkte die mich Stören. Zum einen,
    werden bei diesem Lösungsansatz die Klassenbezeichner im Code schwer leserlich, sobald die Ordnerstruktur zur entsprechenden Klasse mehr als 2 oder 3 Ordner betrifft.

    So wird aus der Ordnerstruktur:

    Db/Connection/MySql.php
    Ein Klassenbezeichner der so aussehen würde:

    PHP-Code:

    $connection 
    = new DbConnectionMysql(); 
    Also in diesem Beispiel sind es nicht einmal 2-3 Ordner und trotzdem sieht die Klassenbezeichnung im Code sehr unschön aus. Hier würde ich zum Beispiel
    die Zend - Konvention vorziehen, da aus dem oben genannten Pfad folgende Klassenbezeichnung entstehen würde:

    PHP-Code:

    $connection 
    = new Db_Connection_MySql(); 
    Ist meiner Meinung nach, die definitiv schönere Variante. Aber nun zurück zum eigentlichen Problem.

    Ein weiterer Punkt der viele Probleme mit sich bringt, bei der Verwendung der Camel Case - Konvention ( ich nenne sie jetzt mal so ),
    ist, dass die eigentlichen Dateinamen der Klassen auf nur einen ersten Großbuchstaben beschränkt sind.

    Gut sichtbar ist dies in dem Beispiel von oben:

    PHP-Code:

    $connection 
    = new DbConnectionMySql(); 
    Das "Sql" beginnt ebenfalls mit einem Großbuchstaben, was bedeutet das also im Ordner "My" nach der Datei "Sql" gesucht werden würde.

    So nun hab ich viel erzählt.

    Meine Fragestellung ist folgende:

    Ich würde mir wünschen Klassen ganz normal nach ihrem wirklichen Bezeichner zu instanziieren.

    Das bedeutet also sowas wie:

    PHP-Code:

    $user 
    = new Admin(); 
    Die __autoload() - Funktion soll mir nun also diese Klasse im System suchen.
    Problem ist hier natürlich, das __autoload() nicht weiß, wo sie suchen muss. Dieses Problem gibt es in dem obigen Beispielen nicht.

    Ich suche also nach einer Lösung, eine __autoload() Funktion zu implementieren, die mir allein anhand des Klassenbezeichners, den ich auch aufrufe, die Entsprechende Klasse nachäd.

    Dies ist ja so ohne weiteres nicht möglich, wenn __autoload() nicht weiß, wo sie nach der entsprechenden Klasse suchen soll.

    Nun habe ich folgende Überlegungen zusammen getragen:

    Es gibt eine Funktion scan_dir(). Diese listet Dateien und Ordner innerhalb eines übergebenen Pfades aus.

    Angenommen, ich würde die __autoload() Funktion so implementieren, das sie mit scan_dir() mein Root verzeichnis nach einer Datei absucht, die genauso heißt wie der übergebene Parameter für __autoload(), und mir diese dann entsprechend nachläd.

    Also:

    PHP-Code:

    $user 
    = new Admin();

    // Funktion __autoload() würde im gesamten Ordnersystem (oder auch nur einige Bereiche), nach Admin.php suchen und diese nachladen. 

    Ist dieser Lösungsansatz eher ungewöhnlich ?

    Gibt es dort nicht vielleicht doch den einen oder anderen Harken ?

    Ich frage mich nämlich gleichzeitig, wenn das alles so ohne weiteres funktioniert, warum hab ich eine solche Art von Lösung noch nirgendwo anders gesehen ?


    So ich glaub das wars soweit auch. Vielen Dank für jeden Rat im Voraus

  • nikosch
    antwortet
    generell liegt dies ja daran, wie schlau das System die Controller packt. zudem ist es ja so, dass innerhalb eines Controllers ähnliche aufgaben abgearbeitet werden, diese benötigen idr. einen ähnlichen Zugriff auf die Datenschicht.
    Kann mit dieser Antwort irgendwie nichts anfangen. Bezug?

    Einen Kommentar schreiben:


  • notyyy
    antwortet
    generell liegt dies ja daran, wie schlau das System die Controller packt. zudem ist es ja so, dass innerhalb eines Controllers ähnliche aufgaben abgearbeitet werden, diese benötigen idr. einen ähnlichen Zugriff auf die Datenschicht.

    die Problematik mit den Klassendefinitionen seh ich irgendwie nicht

    Einen Kommentar schreiben:


  • nikosch
    antwortet
    Generell ist die Idee nicht schlecht, wobei natürlich auch Vorteile wegfallen:

    - unbenutzte Klassen werden nicht geladen
    - (damit kleine Dateigrößen)
    - Klassendefinitionen können überschrieben werden (siehe oben)

    Einen Kommentar schreiben:


  • notyyy
    antwortet
    ich habe mal darüber nachgedacht die klassen die ein Controller benötigt in Pakete dynamisch zusammen zu führen und zu Cachen, somit könnte die io Arbeit auf ein Dateisystem auf ein minimum reduziert werden.

    Einen Kommentar schreiben:


  • Paul.Schramenko
    antwortet
    Zitat von hpf Beitrag anzeigen
    Du musst ja nicht bei jedem Scriptaufruf alle Verzeichniss scannen, sondern nur einmalig oder wenn neue Dateien hinzugekommen sind. Beim Scannen kannst du dir ein Array mit der Zuordnung Klassenname => Include-Datei erstellen und dieses dann serialisieren. Die Autoload-Funktion kann dieses Array dann wieder deserialisieren und hat so die Zuordnung.
    So mache ich es bei meinen Applikationen auch. du erstellst eine Datei, in der alle Klassen bereits mit ihren Name und Pfaden "registriert" sind, dann kann kannst mit einfachen Array-Funktionen schauen wo diese datei liegt

    in php5.3 könnte man sowas in der Art realisieren
    PHP-Code:
    function __autoload($class) {
        static 
    $loadedClasses = include('registeredClasses.php');
        ....

    Dabei wird die "registeredClasses.php" auch nur beim ersten Mal eingebunden.


    OT:
    ich habe das gefühl, dass es immo kaum Provider gibt, die PHP5.3 anbieten, weils wohl ncoh net sauber läuft.
    Weiß jemand was genaueres dazu?

    Einen Kommentar schreiben:


  • Andreas
    antwortet
    Hallo zusammen,

    schöne Debatte. Rein performancetechnisch und designtechnisch ist diese Möglichkeit sicher für den Praxiseinsastz zu vernachlässigen, aber ist das Setzen des Includepath nicht eine Möglichkeit, Dateizugriffe von PHP selbst verwalten zu lassen, statt unnötigerweise Code dafür zu schreiben?

    Also quasi einmal das Hauptverzeichnis mit den Klassen rekursiv in den include_path überführen, statt alle Klassen in einem Array zu halten.

    PHP-Code:
    // Beispielhaft, nicht elegant
    set_include_path(ini_get('include_path').':'.implode(':'$aLibPaths)); 

    Einen Kommentar schreiben:


  • Paul.Schramenko
    antwortet
    Ich habe mir einen, wie ich finde ziemlich coolen Autoloader gebastelt.
    Ich erstelle mir eine Datei, die mir einfach ein Array zurückgibt. In diesem Array stehen als
    Key-Value Werte Klassenname und Dateipfad (bps.: array('Bootstrap' => 'D:/xampp/htdocs/mvc/library/psfw/Bootstrap.php',...))
    So dann binde diese Datei einmal ein und überprüfe dann im __autoload() über isset() ob diese Klasse gesetzt ist.

    Das hat den Vorteil, dass der autoloader weniger Dateizugriffe auf die Platte macht, denn diese Funktion, läuft über den include_path und schaut der Reihe nach ob so eine Klasse mit diesem Pfad eingebunden werden kann. Dies bedeutet, dass wenn erst an 5ter Stelle im include_path deine datei gefunden werden würde, macht das 5 Dateizugriffe statt einem.

    Man mus quasi einmal ein rekursives Skript ausführen, dass alle Klassen in eine array schreibt und dieses dann in einer Datei auf der Platte speichert. Und sich dann natürlich freuen

    bei Bedarf kann ich das ganze auch mit Code näher erläutern.

    Einen Kommentar schreiben:


  • robo47
    antwortet
    Zitat von xm22 Beitrag anzeigen
    Hat zwar nichts mit demAutloading-Thema zu tun, aber zum Compiling von Doctrine: Das habe ich mal testweise gemacht (Allerdings ohne PAC o. ä.). Danach lag der Speicherverbrauch des Skripts bei 13 MB statt 4 MB - allein für Doctrine und 2 Active-Records..
    // edit:
    // mit PAC meintest du wohl APC bzw. allgemein Bytecode-Caches oder ? Konnte damit nämlich nichts anfangen

    Das klingt für mich auch nach Werten ohne Bytecode-Cache oder dem ersten aufruf wo die Datei noch nicht gecacht war.

    Ohne Bytecode-Cache hat man bei PHP logischerweise immer einen viel höheren Speicherverbrauch, weil ja jedes mal die komplette datei gelesen werden muss, geparst werden muss und dann in Bytecode umgesetzt werden muss bevor er schlussendlich ausgeführt wird. Allein die kompilierte Version von Doctrine hat eine Dateigröße von ~ 850kb.

    Bei ner applikation mit ZF, Doctrine und weiteren Bibliotheken, kommen da ohne Bytecode-cache schnell alleine für den compilieraufwand und soweiter overhead von 10 - 20mb hinzu, was mit einer der gründe ist warum php ohne bytecode-cache ein gutes stück resourcenhungriger ist.

    Der Compiler von Doctrine bzw. allgemein das packen von vielen Klassen in eine Datei macht also nur dann Sinn wenn man einen Bytecode-Cache nutzt und eh einen großen Teil der Klassen die in dem Paket sind dann auch nutzt und sie daher auch hätten geladen werden müssen.

    Doctrine 1.X besteht aus mehr als 300 Klassen und Interfaces, wenn man das komplettpaket nimmt (beim compiler kann man das noch etwas reduzieren, wenn man nur den db-adapter mitreinpackt den man auch nutzen will).

    Hab gerade mal hier lokal mit php 5.3.2 mit und ohne APC das mit dem Doctrine-Compiler ausprobiert.

    Wenn ich nur die kompilierte Doctrine-Datei include erhöht das den speicherverbrauch beim ersten mal um 10,3MB, sobald die Datei im Bytecode-Cache von APC ist sind es nur noch 3,4MB.

    Ohne APC sind es dann immer ca 10,2 MB mehr.

    Die dauer für den include:
    apc beim ersten include: 0.21s (10,3MB)
    apc bei weiteren includes: 0.03s (3,4MB)
    ohne apc: 0.16s (10,2MB)

    Hier sieht man recht schön was Bytecode-Cachesbringen können, sowohl für die performance bei vielen Dateien, also auch für den Speicherverbrauch im gesamten und warum man sie wenn man die möglichkeit hat (v/root/managed server) definitiv nutzen sollte.

    Einen Kommentar schreiben:


  • xm22
    antwortet
    Oder am Beispiel von Doctrine's Compiler kann man auch eine komplette Bibliothek oder Teile davon in einer Datei zusammenfassen, geht dann etwas zu lassten des speicherverbrauchs
    Hat zwar nichts mit demAutloading-Thema zu tun, aber zum Compiling von Doctrine: Das habe ich mal testweise gemacht (Allerdings ohne PAC o. ä.). Danach lag der Speicherverbrauch des Skripts bei 13 MB statt 4 MB - allein für Doctrine und 2 Active-Records..

    Einen Kommentar schreiben:


  • robo47
    antwortet
    Zitat von Tidus Beitrag anzeigen
    Bin ich in dem Beispiel mit "namespace User;" in der Klasse Admin und "use User\Admin as UserAdmin" eigentlich daran gebunden, "UserAdmin" als 'as' zu definieren oder könnte ich in dem Falle auch einfach "... as Admin" schreiben?

    gruß Tidus
    use User\Admin;
    ist das gleiche wie
    use User\Admin as Admin;
    nur in kürzer.


    Betreffs performance sei gesagt, schau doch erstmal wann du wirklich probleme hast die anhand des autoloadings sich manifestieren.
    Wenn du das ZF eh mit Autoloading nutzt kannst du z.b. die require_once vor den klassendefinitionen rauschmeißen oder auch den Plugin-Loader nutzen ...

    -> Zend Framework: Documentation: Class Loading - Zend Framework Manual

    Einiges davon ist auch nicht ZF-only sondern lässt sich auch bei anderem Code anwenden.

    Wenn irgendwann wirklich die zu vielen klassen ein problem werden, lässt sich z.b. auch mit absoluten pfaden und APC's .stat-option noch etwas rausholen, dann gibt es nämlich sobald der apc-cache mal gefüllt ist garkeine zugriffe mehr aufs dateisystem, erfordert dann halt, dass man nen passenden update-prozess hat, der den cache leert, weil Änderungen an Dateien solange ignoriert werden wie sie im Cache von APC sind.


    Oder am Beispiel von Doctrine's Compiler kann man auch eine komplette Bibliothek oder Teile davon in einer Datei zusammenfassen, geht dann etwas zu lassten des speicherverbrauchs wenn immer alle klassen geladen werden, dafür steigt die performance weil nicht zig Dateien behandelt werden müssen.

    Einen Kommentar schreiben:


  • nikosch
    antwortet
    Ich mach ja auch niemanden Vorwürfe
    War mehr als Erkenntnis gedacht. Nicht als Kritik.

    Einen Kommentar schreiben:


  • Tidus
    antwortet
    Selbstverständlich nicht. Ich mach ja auch niemanden Vorwürfe .

    Bin schon recht erstaunt über die schnelle und auch sehr intuitive Hilfe. Ich denke ich kann diesen Thread dann auch als erledigt makieren.

    Ich habe von euch nun genug Anregungen, Tipps und Tricks bekommen. Ich bedanke mich nochmal recht herzlich für die Hilfe!

    Gruß Tidus.

    Einen Kommentar schreiben:


  • nikosch
    antwortet
    Ich finde das Handbuch eigentlich recht ausführlich dazu: PHP: Namespaces - Manual

    Habe für ein aktuelles Projekt aber dieses Thema auch noch zurückgestellt, denn eben mal schnell hat man die nicht im Blut.

    Einen Kommentar schreiben:


  • Tidus
    antwortet
    @robo47:

    Danke für deinen Beitrag.

    Ja die Namespaces haben mir heute wirklich den Kopf zerbrochen. Das liegt aber wahrscheinlich daran, das ich keine ausführliche Dokumentation dafür hatte. Lediglich die Lektüre in der neusten Auflage von PHP- Design Patterns gab mir eine kleine Unterstützung, mich in diesem Bereich schlau zu machen.

    Die Syntax mit der Verwendung von "use" ist mir dort auch bekannt geworden. Allerdings hat das bei mir alles nicht so einwandfrei funktionniert.

    Bin ich in dem Beispiel mit "namespace User;" in der Klasse Admin und "use User\Admin as UserAdmin" eigentlich daran gebunden, "UserAdmin" als 'as' zu definieren oder könnte ich in dem Falle auch einfach "... as Admin" schreiben?

    Um so weiterhin um Code die Klasse mit:

    PHP-Code:
    $user = new Admin(); 
    verwenden zu können ?


    gruß Tidus

    Einen Kommentar schreiben:

Lädt...
X