• PSR-0 – Namespaces richtig auflösen

    von am 27. April 2011

    Zuallererst: eine gute Dokumentation schreiben ist echt keine leichte Aufgabe. Mich nervt es seit Tagen, dass ich auf keine tolle Struktur komme, was die Doku und das Handbuch zu LiveTest angeht. Doofe Sache das. Aber ich will euch ja nicht nerven … außer es liest ein technischer Redakteur mit, der soll sich mal bei mir melden. Aber jetzt zum eigentlichen Thema.

    PHP 5.3 ist jetzt auch schon wieder zwei Jahre alt und ich glaube, ich habe kaum einen Artikel über die “neuen” Features geschrieben, was ich rückblickend komisch finde. Aber was soll’s schreiben wir einfach heute etwas zu Namespaces. Namespaces sind eine feine Sache und jeder der die neuste PHP-Version einsetzt, sollte sie auch nutzen. Die IDEs unterstützen dabei auch bereits ungemein. Die Tipparbeit ist also überschaubar und der Code ist irgendwie leichter.

    Worüber wir heute reden wollen, ist der Verzeichnisstruktur, wie man Klassen in Namespaces ablegt. PHP selbst gibt da nichts vor und wie immer kann man hier seinen eigenen Autoloader schreiben, der macht was man will. Ich könnte zum Beispiel den Klassennamen mit md5 verschlüsseln und “.php” anhängen. Wäre aber irgendwie nicht intuitiv, also lassen wir das lieber. Aber wie gesagt, ich kann mir da jede mögliche Schweinerei vorstellen. Da wir aber nicht nur für uns alleine programmieren und auch andere unseren Code nachvollziehen können sollen, sollte man sich auch hier an einen Standard halten. In diesem Fall heißt dieser PSR-0 und wurde von ein paar klugen Köpfen entworfen.

    Prinzipiell ist es ganz einfach und wer mit dem Zend Framework erste Erfahrungen gemacht hat, dem kommen sicherlich ein paar Dinge bekannt vor. Nehmen wir einen Klassennamen samt Namespace phm\LiveTest\Writer\Html. Der Klassenname wäre in diesem Fall Html und der Namespace phm\LiveTest\Writer. Diese Klasse müsste laut Standard hier liegen: Pfad/zur/Bibliothek/phm/LiveTest/Writer/Html.php. Die Regeln zum bilden sind recht einfach und können auf der Standardseite nachgelesen werden. Die wichtigsten drei stellen wir aber hier auch noch mal kurz vor:

    • Der Namespacetrenner wird durch einen Verzeichnistrenner ersetzt
    • Vorangestellt wird der Vendorname, also sozusagen euer Kürzel (phm steht hier übrigens für phphatesme)
    • Wir hängen am Ende ein .php an den Namen

    Hält man sich an diese drei Regeln, dann hat man zum Beispiel den Vorteil jede Menge guter Autoloader verwenden zu können, denn diese unterstützen (fast) alle diese Bildungsweise.Das war auch schon der kurze Ausflug ins Land der Namespaces und wie immer gilt, wer Fragen hat: fragen.

    Nils Langner

    Auch wenn Ihr es mir nicht glauben werdet, aber ich habe nichts gegen PHP. Ich rege mich einfach nur gerne auf. Ok so schlimm ist es auch nicht. Eigentlich wollte ich schon immer einen Blog haben und da ...

    Zum Profil von Nils Langner

    11 Kommentare »


    • Patrick
      am 27. April 2011 um 08:35 Uhr

      Erinnert sehr stark an den Java-Standard. So liegt eine Klasse mit dem Package-Name (hier wohl equivalent zu Namespace) de.techspread.db in dem Pfad ProjektRoot/de/techspread/db. Bei Java gibt es die Besonderheit, dass man den eigenen Domainname in umgekehrter Reihenfolge als Basis benutzt.


    • KingCrunch
      am 27. April 2011 um 10:12 Uhr

      @Pattrick: Java wird hier sicherlich Pate gestanden haben. Der PSR-0 Standard sieht dagegen (in alter Tradition der Frameworks/Library Sammlungen wie PEAR, Zend Framework und Co) einen “Vendor-String” als Root-Namespace vor (\\\).

      Bei den Regeln fehlt nich, dass der Unterstrich _ im Klassenbezeichner (nicht im Namespace!) weiterhin durch den Verzeichnistrenner / ersetzt wird (\Zend_Controller_Action => /Zend/Controller/Action.php, \My\Vendor\Class_SubClass => /My/Vendor/Class/SubClass.php).


    • Nils Langner
      am 27. April 2011 um 10:29 Uhr

      @Patrick: der kleine Unterschied bei PHP ist, dass jeder Namespace einen eigenen absoluten Pfad zur Wurzel besitzen kann.

      @King: Den hatte ich absichtlich weggelassen, weil ich es eine unschöne Art finde die beiden “Welten” zu vermischen.


    • KingCrunch
      am 27. April 2011 um 11:35 Uhr

      @Nils: Ich bin jetzt schon auf Bibliotheken gestoßen, die haben die Klassenbezeichner mit Unterstrich als weiteres Konzept “Subclasses” (äquivalent zu den normalen “Subnamespaces”) eingeführt. Es muss sich also nicht unbedingt um 2 Welten halten ;)

      Es bleibt abzuwarten, ab wann man sich in einer reinen PHP5.3-Welt bewegen kann. So lange ist diese Abwärtskompatibilität unabdingbar und überhaupt: Es ist nunmal Teil des Standards :p


    • rocky
      am 27. April 2011 um 17:43 Uhr

      So wirklich gut fand ich die Standard-Lösung nicht. Ich hatte daher eine eigene Lösung gebaut, mit der man zusätzlich ein Pfad und eine Position angeben kann. Außerdem kann durch diese Lösung auch mehrere Verzeichnisse für einen Namespace definieren und so z.B. eine Zend-Klasse sauber durch seine eigene Version überschreiben.

      Auszüge aus der Klasse:
      namespaces as $namespace) {
      if(strpos($class, $namespace['namespace'])===0) {
      $path = substr($class, strlen($namespace['namespace'])+1);
      $path = str_replace(str_split($namespace['separator']), DIRECTORY_SEPARATOR, $path);
      $path = $namespace['path'].$path.$namespace['extension'];
      $path = self::isReadable($path);
      if($path) {
      return $path;
      }
      }
      }
      }

      /**
      * Register namespace
      *
      * @param string $namespace
      * @param string $path
      * @param string $separator
      * @param string $extension
      * @param string $postion
      */
      public function registerNamespace($namespace, $path,
      $separator=self::DEFAULT_SEPARATOR,
      $extension=self::DEFAULT_EXTENSION,
      $postion=self::POSITION_APPEND)
      {
      $namespace = array(
      ‘namespace’ => $namespace,
      ‘path’ => $path,
      ‘separator’ => $separator,
      ‘extension’ => $extension
      );

      switch ($postion) {
      case self::POSITION_APPEND:
      array_push($this->namespaces, $namespace);
      break;
      case self::POSITION_PREPEND:
      array_unshift($this->namespaces, $namespace);
      break;
      default:
      break;
      }
      }


    • Nils Langner
      am 27. April 2011 um 21:07 Uhr

      @Rocky: Schau dir mal den Universal Class Loader von Symfony an, ich find ihn in der Handhabung ganz gut.

      https://github.com/symfony/symfony/tree/master/src/Symfony/Component/ClassLoader


    • Mario
      am 28. April 2011 um 02:10 Uhr

      Es ist kein Standard nur weil eine handvoll Leute es dazu erklären. Insbesondere ulkig wenn es als Rechtfertigung für bereits geschriebenen Code gedacht ist.

      Mit dem Nacheifern von Java-Klassenstrukturen in einer Interpretersprache steht PHP mal wieder recht einsam da. Und wenn einziger Zweck die Parität zu Verzeichnisstrukturen ist, dann wurde der Nutzwert von Namespaces nicht verstanden. (Aber das hat natürlich syntaktische Ursachen.)


    • rocky
      am 28. April 2011 um 10:42 Uhr

      Ja, genau. Aber bei diesem Loader werden keine IncludePaths unterstützt. Das führt dazu, dass dieser Loader z.B. nicht beim Zend-Framework verwendet werden kann.

      Bei meiner Lösung geht das und die Registrierung dafür z.B. so aus:

      Enlight()->Loader()->addIncludePath(‘var/www/Enlight/’);
      Enlight()->Loader()->addIncludePath(‘var/www/Enlight/Vendor/’);
      Enlight()->Loader()->registerNamespace(‘Zend’, ‘Zend/’);


    • mot
      am 25. Mai 2011 um 01:14 Uhr

      also das mit dem vendor habe ich noch nicht ganz getickt, was bestimmt auf eine unzulänglichkeit meinerseits zurückzuführen ist. aber davon abgesehen fand ich den PSR-0 vorschlag recht brauchbar. konnte ich auch mit unterschiedlichen verzeichnisstrukturen in einklang bringen.

      “Die Tipparbeit ist also überschaubar und der Code ist irgendwie leichter.” – da konnte ich drüber schmunzeln. ein wichtiger punkt. man braucht es auch gar nicht so schüchtern formulieren (“irgendwie”), namespaces sind hilfreich und mit ein grund die distribution zu wechseln falls sie 5.3 nicht unterstützen sollte (da gibts ja auch noch andere features, aber vom alter betrachtet und was man als entwickler will, ein echter grund).

      du fragtest nach fragen:

      - vendor: der soll wohl ein grundverzeichnis sein. wie lege ich den an?
      - welche anderen standards neben PSR-0 gibt es eigentlich noch?
      - wie kann ich ein alias über namespaces (namespaces überschreiben; auch in übergelagerten elementen) legen? nur per autoloader, oder?! PHP selber bietet da ja doch nichts an (soll keine kritik sein, keine ahnung ob so was überhaupt schon bedacht wurde in einer scripting sprache. man ist ja eh gezwungen das am anfang jeder datei (was ja was gutes hat zwecks doku [sonst "comiple" fehler] zu benennen).


    • phm-0: Benamung von Klassen | PHP hates me - Der PHP Blog
      am 15. Juni 2011 um 07:03 Uhr

      [...] Ich habe ja vor ein paar Wochen den psr-0 vorgstellt, einem Standard, der vorschreibt, wie man Namespaces auf Verzeichnisse abbildet. [...]


    • phm-0.1: Benamung von Klassen | PHP hates me - Der PHP Blog
      am 17. Juni 2011 um 07:01 Uhr

      [...] Klassen müssen im Namespace phmLibraryNameHttpRequest liegen. Ansonsten würden sie nicht dem psr-0 entsprechen. Danke dafür an [...]

    RSS Feed für Kommentare zu diesem Artikel. TrackBack URL

    Hinterlasse einen Kommentar

    Werbung
    PHP Magazin
    Ausgabe 02/2010

    Dieses Mal mit Artikeln zu den Themen OpenSocial und Apache Shindig, Graphentheorie, Smarty3

    t3n
    Ausgabe 19

    Social Media (R)evolution. Weitere Themen sind noSQL, Crowdsourcing ...

    PHP Journal
    Ausgabe 2/2010

    PHP & Windows optimal nutzen, die besten PHP-CMS im Überblick, Google-API mit Zend Framework nutzen.

    Wir wurden schon öfters gefragt, ob man uns nicht irgendwie unterstützen kann. Die Antwort war immer einfach: Klar! Am einfachsten ist es eure nächsten Einkäufe bei Amazon über unsere Link abzuwickeln. Damit würdet ihr uns schon sehr helfen. Über Co-Autoren freuen wir uns aber noch mehr.