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

  • [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


  • #2
    Auf Dateisystemfunktionen würde ich tunlichst verzichten! Autoloader sollten IMHO möglichst performant sein. Das ist bei Dateizugriffen nicht gegeben.

    Für kleine Projekte (für große gälte sicher die gleiche Argumentation bzgl. Speicherbelegung), benutze ich einfach eine statische Registry.

    PHP-Code:
    Application::registerClass ('BA_PropertyFactory_VerzeinhExtId'    $sSubPath 'specific/ext_id/ext_id_verzeinh.php');
    Application::registerClass ('BA_PropertyVerzeinhExtId'            $sSubPath 'specific/ext_id/ext_id_verzeinh.php'); 
    Das gibt mir alle Freiheiten für Datei/Klassennamen (ich mag gerne sprechende Namen) und hat einen positiven Nebeneffekt: Ich kann diese Angaben Modulweise einbinden (per require, $sSubPath zeigt hier bspw. auf den Modulpfad, wie man sieht gehen auch zwei zusammengehörige Klassen in einer Datei) UND
    Ich kann Klassendefinitionen überschreiben, indem ich einfach später eine erneute Zuweisung mit dem selben Schlüssel benutze.
    --

    „Emoticons machen einen Beitrag etwas freundlicher. Deine wirken zwar fachlich richtig sein, aber meist ziemlich uninteressant.
    Wenn man nur Text sieht, haben viele junge Entwickler keine interesse, diese stumpfen Texte zu lesen.“


    --

    Kommentar


    • #3
      Es gibt zwei Gründe dir mir spontan einfallen:

      1. Die Performance. Jedes mal das komplette Dateisystem zu scannen würde wohl relativ lange dauern. Gerade wenn du mehrere Dateien einbinden musst und jedes mal das komplette System gescannt wird, dann sprengt das wohl irgendwann den Rahmen.

      2. Namenskonflikte: Wie schnell passiert es dass du dann zwei Klassen hast die den selben Namen haben? Du darfst auf keinen Fall zwei gleiche Dateien haben. Zend/Db/Adapter/Abstract.php oder Zend/Form/Decorator/Abstract.php beide würden bei deinem autoloader auf die selbe Klasse "zeigen".

      Ich find eigentlich die Namenkonventionen des Zend Frameworks sehr angenehm. Grade weil man weiß das _ einen Ordner bezeichnet und die Großbuchstaben dann beliebig im Klassennamen verwendet werden können wie eben MySQL oder HtmlTag.

      Ansonsten kannst du dich ja mal mit den Namespaces in PHP 5.3.0 auseinander setzen. Vlt. kannst du dir damit was schönes basteln. Ich muss ehrlich sagen ich hab damit noch nicht gearbeitet und dementsprechend kenn ich auch keine "Musterlösungen". Müsste man mal sehen was man damit anstellen könnte. Falls du ne Lösung findest kannst du dich ja nochmal melden

      Kommentar


      • #4
        Also ersteinmal vielen Dank für die Schnellen Antworten.

        @Florian:

        - Der Punkt mit der Performance ist gut. Jedesmal das Dateisystem nach nur einer Klasse abzusuchen ist auf Dauer ein langer Prozess, sobald das Dateisystem eine entsprechende Größe hat.

        - Die Namenskonflikte würden in meinem Lösungsansatz ja eigentlich wegfallen, da ich nach der Datei anhand des Klassennamens suchen würde. Also wird es nicht dazu kommen, das ich zwei oder mehrere Dateien in meinem System habe, die beispielsweise Abstract.php heißen.

        Bezüglich der Namespaces.. Ich habe heute ehrlich gesagt, den ganzen Tag damit verbracht eine saubere Lösung mit Namespaces zu konzipieren, nur scheiterte jeder Versuch. Da auch dort die Klassenbezeichnungen nicht mehr nur die einzelnen Namen der Klassen sind, sondern die dazu gehörigen Namespaces vor den Klassenbezeichner geschrieben werden. Womit eine instanziierung einer Klasse auch nicht schön aussehen würde.

        Aber vielen Danke das Argument mit der Performance überzeugt mich schon an ganzes Stück .

        @nikosch:

        - Also deine Lösung mit der Registry-class finde ich sehr gut. Sie bringt wirklich eine Menge Vorteile mit sich. Nur habe ich wohl vergessen zu erwähnen, das ich vor habe die nutzung von require_once oder include möglichst gering zu halten, wenn nicht sogar auf eine einmalige Verwendung zu reduzieren. Deswegen auch die Idee die __autoload() Funktion so zu Programmieren, das Sie mir alles an Klassen zurück geben kann, was ich grade so brauche.

        Aber da wir ja eben schon den Punkt Dateizugriff und Performance hatten, muss ich sagen, das mich das schon sehr Überzeugt hat, von dem Lösungsansatz wegzulenken.


        Nochmal vielen Dank für die Ratschläge

        Gruß Tidus

        Kommentar


        • #5
          Zitat von Tidus Beitrag anzeigen
          Aber da wir ja eben schon den Punkt Dateizugriff und Performance hatten, muss ich sagen, das mich das schon sehr Überzeugt hat, von dem Lösungsansatz wegzulenken.
          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.

          Kommentar


          • #6
            Sorry dass ich hier noch mal anhänge...

            @nikosch:

            Ich hatte ebend eine statische Registry nur kurz überflogen. Wie sie letztendlich zu nutzen ist und was für Vorteile sie mit sich bringt ist ganz klar einleuchtend. Jetzt hätte ich allerdings noch eine Frage.

            Du registrierst ja mit der statischen Methode registerClass($name, $value) die Klasse die du als Parameter $value (bzw. hier ist es ja der Pfad zur Datei) übergibst.

            Aber das bedeutet ja, das jedesmal wenn du eine Klasse an einer bestimmten stelle brauchst, das du diese mit deiner registerClass() Methode erst einmal registrieren muss, damit du sie von da an anhand des Schlüssels als Klassenbezeichner instanziieren kannst. Das Bedeuted aber doch wiederum, das man dazu gezwungen ist, sämtliche Klassen, die grade gebraucht werden, händisch nachzuladen.

            In deinem Fall, dass man sie registriert. Es sei denn, man registriert an einer Zentralen Stelle mit deiner Methode alle Klassen, was aber ja auch nicht unbedingt sinnvoll ist, da nicht immer alle Klassen gebraucht werden.

            Ich hoffe ich habe die Funktionalität soweit richtig nachvoll ziehen können. Ich finde diese Lösung auch sehr gut. Nur der Punkt, das ich weiterhin händisch Klassen im Code nachladen muss, und dies nicht automatisch über die __autoload() Funktion passiert, ist mir weiterhin ein kleiner Dorn im Auge =/

            Bei Missverständnissen meinerseits, bitte drauf aufmerksam machen.

            Kommentar


            • #7
              Es bedeutet, dass ich sämtlich Klassen, die ich benötige „bekannt machen“ muss (eben registrieren), nicht aber, dass ich alle entspr. Files einbinden (require) muss. Ich halte das so, dass ich modulweise eine Datei erstelle (meinetwegen global.php im obersten Modulpfad) und dort dann alle Registrierungen notiere. Den Overhead für nicht benötigte Klassen nehem ich dabei in Kauf - wie gesagt, für Projekte mit tausenden Klassen ist das wahrscheinlich nicht mehr sinnvoll. Mir ist auch bewusst, dass dieses Prinzip nicht die Freiheit des „Modul reinkopieren und verwenden“ bieten kann.
              Für den Core benutze ich übrigens ein zentrales File, das dann mehrere Core-Module umfasst.
              --

              „Emoticons machen einen Beitrag etwas freundlicher. Deine wirken zwar fachlich richtig sein, aber meist ziemlich uninteressant.
              Wenn man nur Text sieht, haben viele junge Entwickler keine interesse, diese stumpfen Texte zu lesen.“


              --

              Kommentar


              • #8
                @hpf:

                Vielen dank für deine Antwort.

                Okay, das stimmt. Nur ab wann weiß mein System denn, das eine neue Klasse dazu gekommen ist, und ein neues Array mit den Pfaden erzeugt werden muss?

                Das bedeutet, ich müsste bei jeder neuen Klasse mit das Array erweitern.

                Diesen Ansatz habe ich bereits durchgekaut. Und halte ihn in der Hinsicht für ein wenig unflexibel, was die Automatisierung meines Problems angeht.

                Für den Fall das ich dich falsch verstanden habe, korrigiere mich bitte.

                Gruß Tidus

                Kommentar


                • #9
                  Betreffs einer kompletten Umsetzung mit Namespaces kannst du dir ja mal das hier anschauen:
                  PSR-0 Final Proposal - PHP Standards Working Group | Google Groups
                  implementierung in php 5.3:
                  gist: 221634 - GitHub

                  Das soll ein Standard werden auf den sich einige Frameworks und Projekte (Zend, Symfony, Drupal, Sonar, Lithium, Agavi, Cake, PEAR, Typo3/Flow3, phpBB ) einigen wollen um Code auch besser untereinander wiederverwendbar zu machen und bei der Nutzung von Code anderer Systeme nicht immer so einen Aufwand mit dem Autoloading zu haben.

                  Eventuell gibt es das ganze dann auch als PECL-Modul oder integration in die SPL:
                  -> SplClassLoader as a PECL extension - PHP Standards Working Group | Google Groups

                  Wenn du eh mit php 5.3+ arbeitest ist es ja auch kein problem deine Klasse einfach "Admin" zu nenen in den Namespace User zu packen und dann oben ein use User\Admin; oder use User\Admin as UserAdmin; zu machen, damit bist du im Quellcode selbst absolut frei die klasse zu nennen wie du willst und das autoloading passiert transparent über den ursprünglichen namen. Der aktuelle Autoloader des Zend Frameworks kann auch schon mit namespaces und php 5.3 umgehen.
                  robo47.net - Blog, Codeschnipsel und mehr
                  | Caching-Klassen und Opcode Caches in php | Robo47 Components - PHP Library extending Zend Framework

                  Kommentar


                  • #10
                    @nikosch

                    Okay verstehe. Damit hat sich diese Frage auch geklärt .

                    Vielen Dank

                    Gruß Tidus

                    Kommentar


                    • #11
                      @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

                      Kommentar


                      • #12
                        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.
                        --

                        „Emoticons machen einen Beitrag etwas freundlicher. Deine wirken zwar fachlich richtig sein, aber meist ziemlich uninteressant.
                        Wenn man nur Text sieht, haben viele junge Entwickler keine interesse, diese stumpfen Texte zu lesen.“


                        --

                        Kommentar


                        • #13
                          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.

                          Kommentar


                          • #14
                            Ich mach ja auch niemanden Vorwürfe
                            War mehr als Erkenntnis gedacht. Nicht als Kritik.
                            --

                            „Emoticons machen einen Beitrag etwas freundlicher. Deine wirken zwar fachlich richtig sein, aber meist ziemlich uninteressant.
                            Wenn man nur Text sieht, haben viele junge Entwickler keine interesse, diese stumpfen Texte zu lesen.“


                            --

                            Kommentar


                            • #15
                              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.
                              robo47.net - Blog, Codeschnipsel und mehr
                              | Caching-Klassen und Opcode Caches in php | Robo47 Components - PHP Library extending Zend Framework

                              Kommentar

                              Lädt...
                              X