Facebook
Twitter
Google+
Kommentare
9

Doctrine Common – Annotations

Ein alter Hut in anderen Sprachen, aber in PHP gerade wirklich in und das mit gutem Grund. Annotations sind Meta-Informationen, die meist in PHP-Doc-Blöcken im Code anzufinden sind. Was dann mit diesen Informationen gemacht werden soll, kann man selbst bestimmten, dafür gibt es keinen Standard. Nehmen wir also ein Beispiel:

class Listener
{
  /**
   * @Event("SomeComponent.Render")
   */
  public function method1(Event $event)
  {
  }
}

$dispatcher->connectListener(new Listener);
$disptcher->notify("SomeComponent.Render")

In diesem einfachen Beispiel haben wir einen Event-Dispatcher, der einen Listener bekommt. Was hier wohl passieren wird ist folgendes: Der Dispatcher sieht die Annoatation @Event im Doc-Block und wertet sie aus. Er weiß, dass er diesen Listener anstupsten muss, wenn das Event „SomeComponent.Render“ passiert. Ich habe dieses Beispiel gewählt, da ich letzte Woche einen kleines Event-Dispatching-Modul geschrieben habe.

Jetzt könnte man natürlich die Implementierung dieser Annoation denkbar dumm halten und mit preg_match und der Reflection-API einfach den benötigten String rausholen. Ihr erinnert euch vielleicht an meinen Artikel darüber. Da das Thema Annotations aber gerade einen Hype erfährt haben sich viel schlauere Leute hingesetzt und eine saubere Lösung gebaut.

Diese Leute, die ich meine, haben Doctrine entworfen und als „Abfallprodukt“ kam noch ein (an Java angelehnter) Annotation-Reader bei raus. Um den soll es heute gehen. Eines möchte ich aber vorneweg noch sagen. Leider habe ich keine Dokumentation zu Doctrine & Co. gefunden, die wunderbar erklärt, wie man es verwendet. Alles Wissen habe ich aus den Unit-Tests.

Fangen wir also an. Die benötigten Klassen finden sich im Doctrine-Common-Paket, welches auf github gefunden werden kann. Das ganze Paket legen wir in unser Projekt und sagen dem Autoloader, wo er Doctrine finden kann. Jetzt können wir eigentlich auch schon loslegen mit der Implementierung.

Der AnnotationReader, den wir benutzen funktioniert wie folgt. Zuerst einmal sagen wir dem Reader, welche Annotations uns überhaupt interessieren und wie die Objekt aussehen soll, dass er uns dann zurückgibt. Danach übergeben wir ihm die Klasse oder Methode, die wir aufgeschlüsselt haben wollen als RefelctionClass bzw. ReflectionMethod. Dann nimmt sich der Reader den Doc-Block und fängt an ihn in seine Bestandteile zu zerlegen (lexing, tokenizing), darauf parsed er die Inhalte und baut uns die Objekte zusammen, die eine bestimme Annotation repräsentiert. Als Anwender müssen dabei gar nicht so tief eindringen, der Code, den wir schreiben sieht dann so oder so ähnlich aus:

$annotationReader = new AnnotationReader();
$annotationReader->setDefaultAnnotationNamespace('Phphatesme\Annotations\\');
$reflectionClass = new ReflectionClass(new Listener);
$refelctedMethod = $reflectionClass->getMethod('method1');
$annotations = $annotationReader->getMethodAnnotations($reflectedMethod);

Mit diesem Stück Code sind wir auch schon fast fertig. Die Zeile mit dem DefaultNamespace schauen wir uns aber noch einmal genauer an. Ich habe ja vorhin gesagt, dass wir dem Reader sagen müssen, was für Klassen er benutzen soll, um später die Annotation-Repräsentanten zu erstellen. Dies machen wir, indem wir den Namespace angeben. Für unseren @Event brauchen wir jetzt eine Klasse, die Phphatesme\Annotations\Event heißt und von \Doctrine\Common\Annotations\Annotation ableitet. In unserem Fall braucht diese Klasse nichts, außer einen Namen. Da sind wir also schnell mit fertig.

class \Phphatesme\Annotations\Event extends \Doctrine\Common\Annotations\Annotatio
{
}

Jetzt sollte der Code laufen und wir unsere Annotation-Objekte zusammengesammelt bekommen. Auf den Wert, den wir benötigen kommen wir dann mit $annotations[0]->value.

Ich glaube, mehr muss man gar nicht zeigen, denn die Grundideen sind relativ einfach und klar. Jeder der ein wenig tiefer in die Materie einsteigen will hat vielleicht mit dem Artikel einen „guten“ Einstiegspunkt.

Ü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

9 Comments

  1. Mir gefällt an dem Beipiel sehr gut, dass die Konfiguration der Listeners/Observer gleich mit im DocBlock steht und man auf einen Blick sehen kann wann wer reagieren soll.

    Reply
  2. Ich kann mir nicht helfen, aber auszuwertende Logik (Annotationen) in Kommentaren, das ist einfach falsch!
    Kommentare sollten den Code dokumentieren und nicht den Ablauf des Programms beeinflussen.
    Wieso nicht echte Annotationen in die Sprache PHP einführen?
    So fühlt sich das für mich wieder mal nach „Frickel“-Ansatz an…

    Reply
  3. @Mike: Was wäre denn der Unterschied, wenn die Sprache es nativ könnte? Du hättest dann den Lexer und Parser in C geschrieben, alles andere wäre identisch. Zumindest wenn du es mit dem nativen Java-Ansatz vergleichst. Der Entwickler würde keinen Unterschied sehen/spüren.

    Reply
  4. @Mike: Noch ist es etwas Gefrickel, weil man sich die Annotations selber aus dem Code filtern muss, aber da PHP in vielen Punkten Java ähnelt, ist es vielleicht nur noch eine Frage der Zeit. Sprachen entwickeln sich und „donwloaden“ stand auch noch nicht immer im deutschen Duden. 🙂

    Reply
  5. Ja, Logik in Kommentare zu schreiben war mir zuerst auch zuwider. Aber wenn man sich einmal darauf einlässt, ist es nicht mehr ganz so schlimm und dafür bekommt man dann so etwas wundervolles wie Doctrine 2. OK, man kann die Meta-Informationen für Doctrine auch in XML oder YAML verfassen, aber irgendwo müssen sie halt hin und da find ichs in den Kommentaren immernoch besser, weil dann alles mehr zusammenhängt und nicht eine Klasse sozusagen in mehrere Dateien aufgespalten wird.

    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