Kontakt
Telefon +49 2161 17 58 83
Mobil +49 179 72 66 112
E-Mail info@ulrich-borchers.de

Webservice ersetzt bald PHPDoc? (Teil 1)

Ich bin kürzlich drei Jahre in die Vergangenheit gereist, wo ein kleines Spaßprojekt ganz einsam versauerte. Also habe ich es an die Hand genommen und für uns von dort mitgebracht. Dieses Projekt habe ich nun ClassDescriptor getauft. Entstanden ist es nebenbei, aber zu schade, um ihm nicht noch die verdiente Aufmerksamkeit zu verschaffen.

Den Code habe ich also aus der Mottenkiste geholt, ihn entstaubt und ein wenig aufpoliert und schreibe nun, beginnend mit diesem Artikel, eine dreiteilige Serie darüber, weil ich glaube, dass die Idee dahinter Potenzial hat, einen Beitrag zu agilen Entwicklungsprozessen - insbesondere im PHP-Umfeld - zu leisten.

Okay, worum geht es also hier? ClassDescriptor verbindet Reflection mit einem Webservice und ist eine Idee für die agile Dokumentation von PHP-Quellcode.

Ein Client nutzt einen Webservice und ruft über diesen selektiv die Dokumentation von Quellcode ab. Auf der Seite des Servers liegt Quellcode, über welchen der Webservice auf Anfrage Dokumentation ausliefert. Das eignet sich besonders für "Dokumentations-Viewer", die eine vollautomatische Dokumentation anzeigen, die immer auf dem aktuellsten Stand ist - ohne dass der Client über aktuellen Sourcecode verfügt und ohne dass irgendjemand etwas dafür manuell tun müsste, sei es das Aktualisieren von PHPDoc oder das Anstoßen eines Build-Prozesses, um die aktuelle Dokumentation erst noch zu erzeugen. Dies entfällt vollständig. Ein ebenfalls denkbarer Einsatz des Konzepts ist die Integration in moderne IDEs.

Beginnen wir einfach: Eine Client kann - als einfachste der verfüggbaren Operationen - beim Webservice eine Liste der vorhandenen Klassen abrufen.

Clients können in einer beliebigen Sprache und für eine beliebige Plattform entwickelt werden. Zur Veranschaulichung hier ein Beispiel in PHP:

ClassDescriptor-Client in PHP

require_once 'ClassDescriptor/SOAPClient.php';

// Hier läuft der ClassDescriptor-Webservice
$service_location = 'http://www.example.com/server.php';

// Zu beschreibende Klasse. Der Client verfügt
// NICHT über den Quellcode:
$classname = 'SomeClass';

$client 
  = new ClassDescriptor_SOAPClient($service_location);

try {
    // Liste verfügbarer Klassen holen:
    $xml = $client->desc_classes();

    // Ausgabe mit header text/xml:
    Dump::response($xml, $client, 'xml');
}
catch (SoapFault $e) {
  echo $e->getMessage(), "\n";
}
catch (Exception $e) {
  echo $e->getMessage(), "\n";
}

Schnittstelle des Webservice

Der Webservice implementiert ein Interface, das alle Methoden enthält, um Informationen über die Struktur einer Klasse zu erhalten. Der enthaltene Beispielclient ClassDescriptor_SOAPClient implementiert ebenfalls dasselbe Interface (ServiceMethods), so dass sichergestellt ist, dass auf dem Client alle Methoden des Service ausgeführt werden können. Für Clients ist es aber nur wichtig (und gut zu wissen), dass der Webservice genau dieses Interface bereitstellt:

interface ServiceMethods
{
    public function desc_classes();
    public function desc_complete($class_name);
    public function desc_class($class_name);
    public function desc_interfaces($class_name);
    public function desc_properties($class_name);
    public function desc_constants($class_name);
    public function desc_methods($class_name);
    public function desc_about($class_name);
  
}

Und dann?

Dies sind also die Methoden des Webservice, die aufgerufen werden können. Wenn der Service nun beispielsweise die Anfrage desc_interfaces('Foo') erhält, schaut er via Reflection in die Klasse 'Foo' hinein und ermittelt, welche Interfaces die Klasse 'Foo' implementiert. Diese Informationen liefert er dann im XML-Format aus.

Ein Client führt dies aus:

$client
  = new ClassDescriptor_SOAPClient($service_location);
 
$xml = $client->desc_interfaces('Foo');

echo $xml;

und erzeugt damit folgende Ausgabe:

<?xml version="1.0" encoding="utf-8"?>
<ClassDescriptor>
    <about>
        <copyright>(c) 2010 Ulrich Borchers Software-Entwicklung und Beratung. All rights reserved.</copyright>
        <author>Ulrich Borchers <info@ulrich-borchers.de></author>
        <svn_id>$Id: ClassDescriptor.php 131 2010-06-03 16:31:15Z ub $</svn_id>
    </about>
    <file>/home/borchers/vhosts/borchers_bloggt/class_descriptor/samples/Foo.php</file>
    <class is_abstract="0" is_final="1" is_interface="0" is_iteraeable="0" has_constructor="1" has_destructor="" has_parent_class="1">
        <name>Foo</name>
        <parent_class>AbstractFoo</parent_class>
        <interfaces>
            <interface>Fooable</interface>
            <interface>Barable</interface>
        </interfaces>
    </class>
</ClassDescriptor>

Der Webservice, also der ClassDescriptor, in den wir bis jetzt noch nicht hineingeschaut haben, hat die Anfrage des Clients desc_interfaces('Foo') entgegengenommen, mittels Reflection in die Klasse hineingeschaut und dann diese XML-Struktur erzeugt und an den Client zurückgesendet.

Ein Client ist frei, mit der Rückgabe zu tun, was ihm beliebt. Der wohl sinnvollste Verwendungszweck ist die Generierung einer Dokumentation daraus. Diese Dokumentation kann life sein:

Agile Live-Dokumentation

Wenn eine solche Lösung innerhalb eines Entwicklungsprozess eingesetzt wird, ist es sinnvoll, dass der Server auf die Sourcen zugreift, die sich gerade in Entwicklung befinden. Er liefert dann eine Dokumentation des Codes in topaktuellem Zustand aus (aktuellster Commit) - und das, ohne dass die Dokumentation unbedingt angefasst werden müsste. Hat jemand beispielsweise eine Methode statisch gemacht und committet: ClassDescriptor weiß Bescheid und liefert diese Änderung sofort bei der nächsten Anfrage aus: Die Dokumentation ist life und unbestechlich. In Verbindung mit CVS oder Subversion bietet es sich an, dass der Server eine Arbeitskopie nutzt, die er automatisiert aktuell hält.

Natürlich ist es auch denkbar, die Schnittstelle ServiceMethods so zu erweitern, dass ein spezielles Tag oder eine Revisionsnummer (CVS, Subversion, Git) als Parameter mit übergeben werden kann. Der Service müsste sich dann darum kümmern, eine entsprechende Arbeitskopie bereit zu halten und aus dieser speziellen Revision die Live-Dokumentation erzeugen. Hier wäre es dann angesagt, mit Caching zu arbeiten, während der Server beispielsweise einen Thread für das Auschecken oder Aktualisieren der Arbeitskopie startet. Denkbar wäre auch, HTTP-Response-Codes dafür zu verwenden (503 Service Unavailable, temporärer Zustand), so dass Clients dies bei der Darstellung berücksichtigen und später automatisch eine neue Anfrage starten können. Implementiert ist solches Verhalten für unterschiedliche Coderevisionen - obwohl sinnvoll - bis jetzt nicht. Realisiert würde es sinnvollerweise über eine leichtgewichtige Plugin-Architektur, mit der dann beliebige, weitere Ergänzungen für den ClassDescriptor, sowie Anpassungen für den ganz individuellen Bedarf möglich sind. Für individuelle Anpassungen bietet es sich an, über die Plugin-Architektur Hooks bereit zu stellen.

Genial agil? Ist das doch das Ende von PHPDoc?

Mit diesem Konzept möchte ich PHPDoc nicht den Rang ablaufen. Es hat seine Daseinsberechtigung, auch wenn es schwerfällig ist, da bei Änderungen am Code nur selten auch die Dokumentation angepasst wird, was außerdem aufgrund der manuellen Vorgehensweise und des doppelten Aufwands fehleranfällig ist. Unschön an PHPDoc ist außerdem, dass eine daraus generierte Dokumentation immer erst nach dem nächsten Durchlauf des Build-Prozesses verfügbar ist, und der läuft in größeren Projekten aufgrund der Schwerstarbeit, die er zu leisten hat, oft nur einmal täglich - wenn überhaupt.

Was diese Lösung nicht bietet, ist sozusagen die semantische Ebene der Dokumentation, die natürlich nur ein Programmierer leisten kann (Was hat er sich dabei gedacht?). Die clevere Benennung von Methoden und anderer Elemente kann dann einen großen Beitrag zur Reduktion von Dokumentation auf das erforderliche Minimum leisten. Und ClassDescriptor liefert auch die PHPDoc-Blöcke aus: Kein Warten mehr auf den Buildprozess und kein sensibler Quellcode mehr auf den Client, abgesehen von dessen für die agile Dokumentation notwendiger, struktureller Beschreibung. Eine mögliche Arbeitserleichterung und dabei auch ein kleines Stück SOA für Developer.

Beteiligung? Gerne!

Wenn jemand Interesse hat, gemeinsam mehr daraus zu machen, freue ich mich über Anfragen. Ich fände zwei Dinge ideal, um das Projekt auszubauen: Clientsoftware, die einen Webservice anspricht und im XML-Format zurückgegebene Quellcodedokumentation hübsch aufbereiten kann. Alternativ - oder auch ergänzend dazu - wäre ein Stylesheet (XSLT) super, welches ein vorliegendes XML-Format in hübsches HTML formatiert. Transformation und Clientsoftware mit Browserkomponente lassen sich bestimmt auch kombinieren.

Fortsetzung ...

Diese Serie besteht aus drei Teilen. Im zweiten, und damit folgenden, Artikel werde ich eine Beispielklasse vorstellen und anhand dieser Klasse zeigen, welche Rückgaben der Server bei welchen Anfragen liefert. Dadurch wird noch deutlicher werden, was ClassDescriptor tut. Das Schreiben des Beispielcodes hat mir viel Spaß gemacht, und der Code ist entsprechend ausgefallen.

Im dritten Teil dieser Artikelserie werde ich schließlich auf die interne Funktionsweise des Servers, der das eigentliche Herzstück des ClassDescriptors ist, eingehen und abschließend die Sourcen zum Download bereitstellen. Stay tuned!

Zurück

Hands On PHP

Harter Hut

Programmiertes - Jenseits von Prosa.

Zend Certified Engineer
Zend Certified Engineer ZF
Oracle Certified Professional, MySQL 5.6 Developer
Sun Certified Java Programmer (SCJP)
Sun Certified Web Component Developer (SCWCD)
RSS
tl_files/open_clip_art/symbole/rss-icon.png  Abonnieren