Namespaces und Autoloading

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

  • Namespaces und Autoloading

    Ich experimentiere gerade mit PHP6 und bin an den Namespaces hängen gelieben, jedenfalls im Zusammenhang mit __autoload. Bisher habe ich das Laden (am Beispiel einer Bibliothek) gelöst, in dem ich alle Dateien mit den zugehörigen Pfaden aus dem Bibliotheksverzeichnis ausgelesen und gespeichert habe, da meine Klassenname - anders als Zend Framework und Co. - kein Abbild ihres Dateipfads darstellen.
    PHP-Code:
    public static function load_library()
    {
        
    $loaded_library = array();

        foreach (new 
    RecursiveIteratorIterator(
                 new 
    RecursiveDirectoryIterator(dirname(__FILE__))) as $file)                           
        {
            if (
    substr(strrchr($file->getFilename(), '.'), 1) == 'php')
            {
                
    $filepath $file->getPathname();
                    
                require_once 
    $filepath;
                    
                
    $loaded_library[end(get_declared_classes())] = $filepath;
            }
        }

        return 
    $loaded_library;

    PHP-Code:
    public static function __autoload($class_name)
    {
        
    $library self::load_library();

        if (isset(
    $library[$class_name]))
        {
            require_once 
    $library[$class_name];
            
            return 
    true;
        }

        return 
    false;

    Namespaces scheinen mir auf den ersten Blick allerdings eine nette Lösung zu bieten, die die ganze Sache simplifiziert. Eine entsprechende Verzeichnisstruktur vorausgesetzt, ließe sich von den Namespaces auf die Dateipfade der Klassen schließen.
    Aus Http::Request würde nach einem simplen str_replace und strtolower http/request.php. Allerdings habe ich in dieser Richtung schon von Problematiken gelesen. Hat jemand von euch diesbezüglich schon Erfahrungen gemacht oder handhabt das auf eine ähnliche Weise?

    Grüße
    Zuletzt geändert von Griecherus; 09.05.2008, 00:24.
    Nieder mit der Camel Case-Konvention

  • #2
    Ich habe gerade auch mal ein wenig rumgespielt. Verwendest du __autoload mit einer Klasse aus einem Namensraum, so wird der vollqualifizierte Name der Funktion übergeben. Du könntest dies also auf Verzeichnisse abbilden. Selbst nach einer use-Direktive wird der Name korrekt aufgelöst, scheint also eine sichere Möglichkeit zu sein.

    Kommentar


    • #3
      Ja, das ist das Gute daran. __autoload kann mit einem simplen
      PHP-Code:
      str_replace('::'DIRECTORY_SEPARATOR$class_name); 
      den Namensraum in eine Verzeichnissstruktur abbilden. Allerdings hat sich in meinem Fall ein kleines "Hindernis" im Zusammenhang mit der Namenskonvention meiner Dateien und Klassen ergeben.
      Aus class ClassName wird class_name.php. Das heisst ich müsste eine Funktion einbeziehen, die den Pascal Case Namen der Klassen in das Snake Case Format der Dateien umwandelt. Oder ich bleibe eben dabei, die Verzeichnisse auf "die alte Tour" zu durchsuchen und ein Array aus Klassen und Verzeichnissen zu bilden. Da bin ich mir noch nicht einig.

      Grüße
      Nieder mit der Camel Case-Konvention

      Kommentar


      • #4
        Aus class ClassName wird class_name.php. Das heisst ich müsste eine Funktion einbeziehen, die den Pascal Case Namen der Klassen in das Snake Case Format der Dateien umwandelt.
        Das ist ja in Ordnung. Wenn du keinen Nerv hast, das selbst zu malen, portiere den Sourcecode von prototypes String.underscore auf PHP. Allerdings wird aus dem str_replace dann wohl eher ein explode und implode...

        Kommentar


        • #5
          Original geschrieben von PHP-Desaster
          Das ist ja in Ordnung. Wenn du keinen Nerv hast, das selbst zu malen, portiere den Sourcecode von prototypes String.underscore auf PHP. Allerdings wird aus dem str_replace dann wohl eher ein explode und implode...
          Danke für den Tipp. Ich werde mir das mal genauer ansehen. Bisher habe ich das in einer Schleife mit den String-Funktionen, also ohne Regex, gelöst:
          PHP-Code:
          $class_file '';

          for (
          $i 0$len strlen($class_name); $i $len$i++)
          {
              
          $char substr($class_name$i1);
                  
              if (
          strtoupper($char) === $char)
              {
                  
          $class_file .= (($class_file != '') ? '_' '') . strtolower($char);
              }
              else
              {
                  
          $class_file .= $char;
              }

          Grüße
          Nieder mit der Camel Case-Konvention

          Kommentar


          • #6
            Original geschrieben von PHP-Desaster
            Das ist ja in Ordnung. Wenn du keinen Nerv hast, das selbst zu malen, portiere den Sourcecode von prototypes String.underscore auf PHP. Allerdings wird aus dem str_replace dann wohl eher ein explode und implode...
            Danke für den Tipp. Ich werde mir das mal genauer ansehen. Bisher habe ich das in einer Schleife mit den String-Funktionen, also ohne Regex, gelöst:
            PHP-Code:
            $class_file '';

            for (
            $i 0$len strlen($class_name); $i $len$i++)
            {
                
            $char substr($class_name$i1);
                    
                if (
            strtoupper($char) === $char)
                {
                    
            $class_file .= (($class_file != '') ? '_' '') . strtolower($char);
                }
                else
                {
                    
            $class_file .= $char;
                }

            Grüße
            Nieder mit der Camel Case-Konvention

            Kommentar


            • #7
              find' ich eine gute idee, fürs autoloading namespaces (gibt es ja schon ab php 5.3.0) zu benutzen. scheint auch keine probleme zu geben:
              http://php.net/manual/de/language.oo...load.php#82087

              Griecherus, wenn du deine load_library() benutzt, brauchst du allerdings keine autoloading mehr, die includiert ja einfach alle php-dateien.

              Kommentar


              • #8
                find' ich eine gute idee, fürs autoloading namespaces (gibt es ja schon ab php 5.3.0) zu benutzen. scheint auch keine probleme zu geben:
                http://php.net/manual/de/language.oo...load.php#82087
                Das gleiche steht im zweiten Post

                Kommentar


                • #9
                  Original geschrieben von 3DMax
                  find' ich eine gute idee, fürs autoloading namespaces (gibt es ja schon ab php 5.3.0) zu benutzen. scheint auch keine probleme zu geben:
                  http://php.net/manual/de/language.oo...load.php#82087

                  Griecherus, wenn du deine load_library() benutzt, brauchst du allerdings keine autoloading mehr, die includiert ja einfach alle php-dateien.
                  Die wird aber nicht automatisch aufgerufen, sobald ich eine Instanz einer Klasse erstelle.

                  Im Übrigen ergibt sich ein unschönes Problem bei meiner load_library() Methode: Sie berücksichtigt keine Interfaces. Ich müsste sie also dahingehend erweitern, heraus zu finden, ob die jeweilige Datei eine Klasse oder ein Interface enthält und dementsprechend das library Array erstellen.

                  Grüße
                  Nieder mit der Camel Case-Konvention

                  Kommentar


                  • #10
                    OffTopic:
                    Meine Interfaces beginnen immer mit einem I, zum Beispiel ISortable. Vielleicht kannst du ja sowas in die Richtung verwenden

                    Kommentar


                    • #11
                      Original geschrieben von PHP-Desaster
                      OffTopic:
                      Meine Interfaces beginnen immer mit einem I, zum Beispiel ISortable. Vielleicht kannst du ja sowas in die Richtung verwenden
                      Das ist bei mir auch der Fall. Interfaces bilden die einzige Ausnahme, die (das von mir verhasste ) Camel Case zur ihrer Benennung verwenden.
                      PHP-Code:
                      interface iInterfaceName
                      {
                          
                      // ...

                      Allerdings dachte ich bisher eher daran, Reflections zu verwenden (resp. ReclectionClass::isInterface()).

                      Grüße
                      Zuletzt geändert von Griecherus; 11.05.2008, 01:57.
                      Nieder mit der Camel Case-Konvention

                      Kommentar


                      • #12
                        Danke für den Tipp. Ich werde mir das mal genauer ansehen. Bisher habe ich das in einer Schleife mit den String-Funktionen, also ohne Regex, gelöst:
                        Eine noch etwas schnellere Implementation:
                        PHP-Code:
                        /**
                         * Reverse camelizing a string
                         * 
                         * @param string $string string to uncamlize
                         * @return string
                         **/
                         
                        function uncamelize $string ) {
                            
                            
                        $string trim $string );
                            
                        $string strtr mb_strtolower mb_substr $string0) ) . mb_substr $string),
                        array ( 
                        'A' => '_a''B' => '_b''C' => '_c''D' => '_d''E' => '_e''F' => '_f''G' => '_g',
                        'H' => '_h''I' => '_l''J' => '_l''K' => '_k''L' => '_l''M' => '_m''N' => '_n',
                        'O' => '_o''P' => '_p''Q' => '_q''R' => '_r''S' => '_s''T' => '_t''U' => '_u',
                        'V' => '_v''W' => '_w''X' => '_x''Y' => '_y''Z' => '_z' ) );
                            
                            return 
                        $string;
                            

                        Allerdings dachte ich bisher eher daran, Reflections zu verwenden (resp. ReclectionClass::isInterface()).
                        Ist in der Praxis imho etwas viel Overhead fürs laden von Dateien.
                        Zuletzt geändert von tontechniker; 17.05.2008, 20:59.
                        Die Regeln | rtfm | register_globals | strings | SQL-Injections | [COLOR=silver][[/COLOR][COLOR=royalblue]–[/COLOR][COLOR=silver]][/COLOR]

                        Kommentar


                        • #13
                          Danke für deinen Vorschlag. So simpel habe ich gar nicht gedacht
                          Original geschrieben von tontechniker
                          Ist in der Praxis imho etwas viel Overhead fürs laden von Dateien. [/B]
                          Was würdest du alternativ vorschlagen?

                          Grüße
                          Nieder mit der Camel Case-Konvention

                          Kommentar


                          • #14
                            Was würdest du alternativ vorschlagen?
                            Wenn du tatsächlich konsequent das "i" und CamelCase benutzt würde ich darauf prüfen. Ich lege Interfaces entweder in der normalen Ordnerstruktur ab (aus _ wird /, Beispiel Zend Framework) oder lege sie zusammen mit Klassen ab soweit sinnvoll (Beispiel: in der Datei Controller.php befindet sich mit der Klasse Controller auch noch das Interface ControllerCallInterface, das Controller implementieren die nicht auf Methoden reagieren, sondern über __call auf beliebige Werte. Wenn ein Controller aufgerufen wird und Controller.php noch nicht inkludiert, wird auf Grund der Abstammung des Controllers von der der Klasse Controller die Datei inkludiert und damit auch das Interface). Natürlich ist das beliebig handhabbar, es gibt Leute die meinen, dass man eine Datei pro Klasse / Interface bräuchte, ich finde allerdings, dass es mit der Größe des Projekts irgendwann sehr komplex und unübersichtlich wird wenn man alles aufspaltet, außerdem braucht jeder include zusätzliche Zeit - wenn man für den Controller jedesmal fünf Dateien inkludieren muss (und das auf jeder einzelnen Seite) ist das imho etwas viel.
                            Die Regeln | rtfm | register_globals | strings | SQL-Injections | [COLOR=silver][[/COLOR][COLOR=royalblue]–[/COLOR][COLOR=silver]][/COLOR]

                            Kommentar


                            • #15
                              Original geschrieben von tontechniker
                              Eine noch etwas schnellere Implementation:
                              PHP-Code:
                              /**
                               * Reverse camelizing a string
                               * 
                               * @param string $string string to uncamlize
                               * @return string
                               **/
                               
                              function uncamelize $string ) {
                                  
                                  
                              $string trim $string );
                                  
                              $string strtr mb_strtolower mb_substr $string0) ) . mb_substr $string),
                              array ( 
                              'A' => '_a''B' => '_b''C' => '_c''D' => '_d''E' => '_e''F' => '_f''G' => '_g',
                              'H' => '_h''I' => '_l''J' => '_l''K' => '_k''L' => '_l''M' => '_m''N' => '_n',
                              'O' => '_o''P' => '_p''Q' => '_q''R' => '_r''S' => '_s''T' => '_t''U' => '_u',
                              'V' => '_v''W' => '_w''X' => '_x''Y' => '_y''Z' => '_z' ) );
                                  
                                  return 
                              $string;
                                  

                              Nur als Hinweis: Mit der Funktion bekommst du Probleme, sobald Akronyme durchgehend großgeschrieben werden: XMLParser wird zu x_m_l_parser statt xml_parser.
                              Mein (neuer) Ansatz sieht so aus:
                              PHP-Code:
                              // am Beispiel XMLParser bzw. XmlParser
                              static public function underscore($word)
                              {
                                  
                              // Namespaces durch Slash ersetzen; besser noch DIRECTORY_SEPARATOR
                                  
                              $word str_replace('::''/'$word);

                                  
                              // XMLParser wird zu XML_Parser
                                  
                              $word preg_replace('/(?<=[A-Z])([A-Z]+)([A-Z][a-z])/''\\1_\\2'$word);
                                  
                                  
                              // Jedem Großbuchstaben, der auf einen Kleinbuchstaben folgt, wird
                                  // ein Unterstrich vorangesetzt
                                  // XmlParser wird zu Xml_Parser
                                  
                              $word preg_replace('/(?<=[a-z])([A-Z])/''_\\1'$word);
                                  
                                  return 
                              strtolower($word);

                              Das Ganze ist natürlch auch ohne Lookbehind machbar, allerdings erscheint es mir für den Moment am elegantesten, auch wenn ich mir noch nicht 100%ig sicher bin. Ich bin also offen für Belehrungen.

                              Grüße
                              Nieder mit der Camel Case-Konvention

                              Kommentar

                              Lädt...
                              X