Facebook
Twitter
Google+
Kommentare
12

PSR-0 – Namespaces richtig auflösen

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.

Über den Autor

Nils Langner

Nils Langner ist der Gründer von "the web hates me" und auch der Hauptautor. Im wahren Leben leitet er das Qualitätsmanagementteam im Gruner+Jahr-Digitalbereich und ist somit für Seiten wie stern.de, eltern.de und gala.de aus Qualitätssicht verantwortlich. Nils schreibt seit den Anfängen von phphatesme, welches er ebenfalls gegründet hat, nicht nur für diverse Blogs, sondern auch für Fachmagazine, wie das PHP Magazin, die t3n, die c't oder die iX. Nebenbei ist er noch ein gern gesehener Sprecher auf Konferenzen. Herr Langner schreibt die Texte über sich gerne in der dritten Form.
Kommentare

12 Comments

  1. 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.

    Reply
  2. @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).

    Reply
  3. @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.

    Reply
  4. @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

    Reply
  5. 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;
    }
    }

    Reply
  6. 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.)

    Reply
  7. 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/‘);

    Reply
  8. 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).

    Reply

Leave a Comment.

Link erfolgreich vorgeschlagen.

Vielen Dank, dass du einen Link vorgeschlagen hast. Wir werden ihn sobald wie möglich prüfen. Schließen