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

Webservice ersetzt bald PHPDoc? (Teil 3)

In dieser dreiteiligen Artikelserie geht es um das Projekt ClassDescriptor. Es ist eine serverseitige Implementierung in PHP, die agile Live-Dokumentation von PHP-Quellcode im XML-Format erzeugt.

Teil 1 beschreibt das Konzept von ClassDescriptor und stellt die Schnittstelle des implementierten Webservice vor. Der zweite Teil geht dann in die Tiefe und zeigt aus Sicht eines Clients, wie der Webservice funktioniert. Zu sehen sind XML-Rückgaben, womit der Webservice die Eigenschaften einer Beispielklasse beschreibt.

In diesem dritten und letzten Teil der Serie stelle ich nun die Implementierung des Service vor und gebe abschließend den Quellcode zum Download frei.

Schauen wir uns die Implementierung des Webservice Top-Down an:

Zunächst das Skript, das die Anfragen des Client behandelt

require_once 'ClassDescriptor.php';
require_once 'ClassDescriptor/Server.php';
require_once 'ClassDescriptor/Provider/Mock.php';

require_once('samples/SomeGuy.php');
require_once('samples/SomeNaiveGuy.php');

$soap_server = new SOAPServer(NULL, array('uri' => 'http://class-descriptor/'));
$soap_server->setClass('ClassDescriptor_Server', new ClassDescriptor_Provider_Mock());
$soap_server->handle();

Mithilfe der SOAP Extension werden die Requests des Client an die Klasse ClassDescriptor_Server delegiert.

Diese Klasse implementiert die Schnittstelle ServiceMethods, die für Clients bereit gestellt wird und die im ersten Teil beschrieben wurde. Zur Erinnerung: Diese Schnittstelle gibt dem Client die Möglichkeit, eine PHP-Klasse selektiv beschreiben zu lassen. Die Schnittstelle enthält unter anderem die Methoden desc_interfaces(), um die implementierten Interfaces abzufragen und die Methode desc_properties() für die Eigenschaften der Klasse.

Hier ein Auszug aus der Klasse ClassDescriptor_Server, welche diese Schnittstelle implementiert. Zu sehen ist die Implementierung der Methode desc_complete(), die ein vollständige Klassenbeschreibung generiert. Alle weiteren Methoden, wie zum Beispiel desc_interfaces(), sind analog minimalistisch aufgebaut:

Klasse zur Behandlung der Requests

class ClassDescriptor_Server implements ServiceMethods
{
    private $provider;

    public function __construct(ClassDescriptor_Provider_Interface $provider)
    {
        $this->provider = $provider;
    }

    public function desc_complete($class_name)
    {
        $descriptor = new ClassDescriptor($class_name, $this->provider->getFilename($class_name));
        $descriptor->build_complete();
        return $descriptor->as_xml();
    }

    // ...
}

Die Introspektion wird dann an die Klasse ClassDescriptor delegiert. Diese ist sozusagen das Herzstück, und hier schaut der Server via Reflection in die zu beschreibende Klasse hinein und liefert die angeforderten Daten als XML zurück. Ein Auszug:

Klasse für Introspektion und XML-Erzeugung

class ClassDescriptor
{
    public function __construct($class_name = null, $file_name = null)
    {
        $this->class_name = $class_name;
        if (isset($file_name)) {
            @include_once($file_name);
        }

        if (class_exists($class_name) or interface_exists($class_name)) {
            $this->rclass = new ReflectionClass($class_name);
        }
        else {
            $this->is_error = true;
        }

        $this->create_skeleton();
    }

    private function create_skeleton()
    {
        $this->dom = new DomDocument('1.0', 'utf-8');
        $this->root_node = $this->dom->createElement(__CLASS__);
        $this->root_node->appendChild($this->get_about_node());

        if (!$this->is_error) {
            $this->root_node->appendChild($this->dom->createElement('file', $this->rclass->getFileName()));
        }

        $this->dom->appendChild($this->root_node);
    }
    
    public function build_complete()
    {
        $this->create_skeleton();
        if ($this->is_error) {
            return false;
        }
        $this->root_node->appendChild($this->get_class_tree());
        $this->root_node->appendChild($this->get_methods_tree());
        return true;
    }

    // ...

}

Intern wird also mit ReflectionClass für die Introspektion und mit DOMDocument für die Erzeugung der XML-Struktur gearbeitet. Die Details der Implementierung können dann gerne dem unten zum Download angebotenen Quellcode entnommen werden.

Vielleicht ist beim Lesen aufgefallen, dass die Klasse ClassDescriptor_Server, an die die Anfragen an den Werbservice weitergeleitet werden, mit einer Instanz des Interface ClassDescriptor_Provider_Interface instanziiert wird. Dieses Interface stellt Metainformationen über die vorhandenen Klassen bereit, unter anderem das Dictionary (Liste verfügbarer Klassen, Methode desc_classes()).

Im Beispielcode wird dafür eine Mock-Implementierung verwendet. Die Bereitstellung eines Dictionaries der vorhandenen Klassen (Methode getClassMap()) ist je nach Struktur des zu beschreibenden Repositories individuell zu implementieren. Natürlich ist beispielsweise auch eine generische Implementierung des Interface möglich, die einen Verzeichnisbaum untersucht und/oder mit Namenskonventionen arbeitet. Dieser Teil des Serverimplementierung ist jedoch abhängig davon, wie im konkreten Fall die Quellcode-Dateien strukturiert und abgelegt sind.

Ich hatte beschrieben, dass eine leichtgewichtige Plugin-Architektur nachgerüstet werden kann, um beispielsweise verschiedene Revisionen (SVN, CVS) behandeln zu können. Diese Architektur ist im Quellcode, den ich zum Download anbiete, nicht enthalten.

Der vollständige Quellcode

DOWNLOAD DES ARCHIVS

Das Archiv enthält Server- und Clientcode.

Wer damit experimentieren möchte, sollte sich die Datei client.php anschauen und die Variable $service_location gegebenenfalls anpassen. Ich bin guter Dinge, dass die Installation für fähige PHP-Programmierer einfach ist und keiner besonderen Anleitung bedarf :-)

Enthalten ist auch eine Datei frontend.php, sowie die Template Engine Smarty. Dies ist ursprünglich ein schlankes Frontend für den Webservice gewesen, mit dem sich die Live-Dokumentation als HTML praktisch in der Sidebar des Firefox unterbringen ließ. Dieses Frontend ist nur ein möglicher Anwendungsfall. Es ist nicht ohne Weiteres lauffähig, insbesondere, da die Funktionalität des Dictionary erst implementiert werden muss. Wer Spaß dran hat: Bitte damit experimentieren und es zum Laufen bringen :-) Ich freue mich, von Erfahrungswerten und möglichen Einsatzzwecken zu erfahren!

Den Code biete ich zur freien Verwendung an. Denkbar ist, dass ich zu einem späteren Zeitpunkt ein Ajax-Frontend hinzufüge, die Plugin-Architektur nachrüste und den ClassDescriptor dann unter einer Open Source Lizenz anbiete. Bis dahin berücksichtige ich gerne euer Feedback.

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