Facebook
Twitter
Google+
Kommentare
16

Called Annotations

Warum hat sich eigentlich niemand beschwert, dass es am Montag und Mittwoch keine Artikel gab? Ihr seid ja echt treue Leser. Aber wahrscheinlich wusstet ihr alle, dass ich auf der PHP Conference in Mainz war und deswegen nicht zum Schreiben kam. Hatte ich nur vergessen anzukündigen. Wer Lust hat, kann sich unseren Vortrag auf Slideshare durchklicken, denn dort haben wir ihn endlich veröffentlicht.

So aber wieder zum Thema Called Annotations. Wahrscheinlich habt ihr da noch nie was von gehört. Zumindest ist die Chance nicht groß, denn den Namen habe ich erfunden, aber wie ich finde, beschreibt er ganz gut, was ich da gebastelt habe. Wie immer gilt, ein wenig Code, sagt mehr als tausend Worte, deswegen hier ein paar Zeilen:

AnnotationHandler::registerAnnotation('deprecated');

AnnotationHandler::registerCallback('deprecated',
                                    AnnotationHandler::HOOK_TYPE_PRE,
                                    function($params){ echo 'PRE'; });

AnnotationHandler::registerCallback('deprecated',
                                    AnnotationHandler::HOOK_TYPE_POST,
                                    function($params){ echo 'POST'; });

$testObject = new Deprecated();
$testObject->deprecatedFunction();

Was sollen die paar Zeilen machen? Jedes mal wenn eine Methode mit einer Annotation @deprecated ausgestattet wurde, soll doch bitte vor dem eigentlichen Aufruf dieser mein registrierter Callback ausgeführt werden und danach dann auch noch mal einer. Uns was soll ich sagen, ich hab das einfach mal programmiert, so dass es funktioniert. Nicht schön, aber das muss ein Proof of Concept ja auch nicht sein. Ich glaube aber der heutige Stand reicht, um ein paar Leute anzufixen und jemanden zu finden, der den Ansatz ein wenig mitverfolgt Sourcen sind auf gitHub.zu finden.

Wer wissen will, wie ich das Feature umgesetzt habe, der muss sich einfach den Source-Code anschauen. Zumindest bis ich nächste Woche mal die Zeit gefunden haben, die Ideen zusammenzufassen. Das wird dann aber wahrscheinlich auf GitHub passieren.

Die Idee kann man jetzt noch mal ein wenig weiterspinnen. Ich glaube, dass man Asserts und Validierung recht schön so durchführen könnte. Logging ist sicherlich auch ein Kandidat und Event-Handling im allgemeinen auch sehr interessant. So viele Ideen. Also hier noch mal der Aufruf, falls jemand von euch Lust hat mitzumachen, soll er sich bei mir melden und wir bauen da eine schöne sauber implementierte Komponente, die jeder in seinem Projekt nutzen kann.

Ü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

16 Comments

  1. @Mario: Dank PHP und allem drum und dran wird es gar keine Zeit kosten, da man die Files, wenn sie ein mal geparsed sind woanders wegschreiben kann und einfach dem autoload sagt, wo er zuerst suchen soll. Debugging wird damit zwar ein wenig irritierend, aber das ist man von Symfony und Co. ja auch schon gewohnt.

    Reply
  2. Hi!

    Die Idee ist prinzipiell gut und der Vorteil, dass mit den Annotations die Information beim Code dabeisteht, ist natürlich nicht zu verachten.
    Allerdings verwende ich auch bei Doctrine immer Xml-Mapping, da ich dann aufgrund des deklarierten Xml-Schemas Autoergänzung hab und einige Tippfehler gleich auffallen, die bei Annotations mitunter langes Suchen notwendig machen.

    Oder gibts für Annotations auch schon Schemata, die man in seinen bevorzugten Editor einbinden kann?

    Reply
  3. @Johannes: Annotations sind momentan kein natives Feature von PHP, so wie sie es im zum Beispiel Java-Umfeld sind. Aus dem Grund gibt es auch keinen Standard, den man unterstützen könnte. Ich denke, dass wenn dies so bleibt die IDEs keine Chance haben Annotations einfach zu unterstützen.

    Reply
  4. Hey Nils,

    an sich ist das eine nette Idee. In der aktuellen Implementierung jedoch performance-technisch etwas öde (bei jedem load: lesen, verarbeiten, eval() – kein OpCode-Cache möglich, …).

    Mit dem Wegschreiben eines Caches der veränderten Quelldatei werden zwar die Performance-Probleme von oben adressiert, aber neue Probleme eingeführt. Zeilennummern passen nicht mehr auf die Quelldatei. Ändert sich die Quelldatei, muss der cache neu angelegt werden (also ein zusätzlicher stat), etc.

    Glücklicherweise gibt es die PECL funcall (http://pecl.php.net/package/funcall) mit der das „injecten“ der callbacks obsolet wird. Das Registrieren der callbacks kannst du immer noch über Annotations lösen (resp. automatisieren), aber die eigentliche Ausführung der Hooks an funcall übergeben. Wenn funcall verfügbar ist, könnte das die genannten Performance- und Debugging-Probleme lösen, nicht?

    Reply
  5. Die Zeilennummern sind kein wirkliches Problem, wenn die neue Hook-Funktion in einer einzelnen Zeile direkt vor der eigentlichen Funktion erstellt werden. Lediglich der Name der Funktion ändert sich marginal.
    Fieser ist, dass die Quelldateien ganz andere sind, d.h. ich aus meiner IDE heraus keine Breakpoints mehr setzen kann.

    Reply
  6. @Rodney: Die Zeilennummern passen noch, da ich neue Methoden einführe, die sich am Ende der befinden. Methodennamen ändern sich, aber da wird der Programmierer drauf „hingewiesen“.
    Dass es dort eine PECL-Extension gibt, wusste ich nicht, finde es aber nett, dass man es bei meiner Lösung auf jedem PHP-System zum Laufen bringen kann. Trotzdem werde ich mir das mal anschauen.

    @Martin: Da muss man mal schauen, wie man es löst. Vielleicht im Produktivsystem Caching anschalten und auf Dev und Stage nicht. Aber da wäre es einfach gut, wenn andere Leute ihre Ideen auch anbringen, ich denke das meiste ist lösbar.

    Reply
  7. @Martin, @Nils: ob das Deaktivieren des Cachings etwas bringt, wage ich bei eval()ed code zu bezweifeln. Von eval() wird ja kein Rückschluss auf die Quelldatei gezogen, also sind Breakpoints und Zeilennummern wieder nix.

    Reply
  8. Ich könnte mir vorstellen, dass für die Debug-Umgebung ein Mock-Objekt erzeugt wird und die Calls über entsprechende Methoden an das Original weiterreicht. Da musst du dann nur aufpassen, dass du immer schön das Mock-Objekt weiterreichst und funktioniert nicht immer (z.B. Weiterreichen von $this in privaten Methoden). Würde zusätzlich erfordern, dass alle Objekterstellungen über eine eigene Methode laufen, also nicht einfach über new, damit die Mock-Objektklasse erstellt werden kann. In der Produktiv-Umgebung wird dein Ansatz mit einem Cache genutzt.

    Reply
  9. >> Von eval() wird ja kein Rückschluss auf die Quelldatei gezogen,
    >> also sind Breakpoints und Zeilennummern wieder nix.

    Keine Ahnung ob das funktioniert. Aber man könnte ja anstatt eval zu benutzen einen Stream-Wrapper schreiben. Somit wäre es möglich den generierten Code ganz normal zu inkludieren.

    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