<?xml version="1.0" encoding="UTF-8"?>
<!-- EN-Revision: 24249 -->
<!-- Reviewed: no -->
<sect1 id="zend.xmlrpc.server">
    <title>Zend_XmlRpc_Server</title>

    <sect2 id="zend.xmlrpc.server.introduction">
        <title>Einführung</title>

        <para>
            <classname>Zend_XmlRpc_Server</classname> ist als vollständiger
            <acronym>XML-RPC</acronym> Server geplant, der den <ulink
                url="http://www.xmlrpc.com/spec">Spezifikationen auf www.xmlrpc.com</ulink> folgt.
            Des Weiteren implementiert er die Methode <command>system.multicall()</command>, welche
            dem Entwickler erlaubt, mehrere Anfragen aufzureihen.
        </para>
    </sect2>

    <sect2 id="zend.xmlrpc.server.usage">
        <title>Grundlegende Benutzung</title>

        <para>
            Ein Beispiel der grundlegendsten Benutzung:
        </para>

        <programlisting language="php"><![CDATA[
$server = new Zend_XmlRpc_Server();
$server->setClass('My_Service_Class');
echo $server->handle();
]]></programlisting>
    </sect2>

    <sect2 id="zend.xmlrpc.server.structure">
        <title>Server-Struktur</title>

        <para>
            <classname>Zend_XmlRpc_Server</classname> ist aus einer Vielfalt von Komponenten
            zusammengesetzt, die vom Server selbst über Anfrage-, Antwort- und bis hin zu
            Fehler-Objekten reicht.
        </para>

        <para>
            Um den <classname>Zend_XmlRpc_Server</classname> zu erstellen, muss der Entwickler dem
            Server eine oder mehrere Klassen oder Funktionen durch die Methoden
            <methodname>setClass()</methodname> und <methodname>addFunction()</methodname>
            hinzufügen.
        </para>

        <para>
            Wenn dieses erstmal erledigt wurde, kann man entweder der Methode
            <methodname>Zend_XmlRpc_Server::handle()</methodname> ein
            <classname>Zend_XmlRpc_Request</classname>-Objekt übergeben oder es wird ein
            <classname>Zend_XmlRpc_Request_Http</classname> instanziert, falls keines angegeben
            wurde - die Anfrage wird also aus <filename>php://input</filename> geladen.
        </para>

        <para>
            <methodname>Zend_XmlRpc_Server::handle()</methodname> versucht daraufhin, den
            zuständigen Handler, der durch die angeforderte Methode bestimmt wird,
            auszuführen. Es wird entweder ein <classname>Zend_XmlRpc_Response</classname>-
            oder ein <classname>Zend_XmlRpc_Server_Fault</classname>-Objekt zurückgegeben.
            Beide Objekte besitzen eine Methode <methodname>__toString()</methodname>, die eine
            gültige <acronym>XML-RPC</acronym> Antwort im <acronym>XML</acronym>-Format zurückgibt,
            die direkt ausgegeben werden kann.
        </para>
    </sect2>

    <sect2 id="zend.xmlrpc.server.anatomy">
        <title>Anatomie eines Webservices</title>

        <sect3 id="zend.xmlrpc.server.anatomy.general">
            <title>Generelle Annahmen</title>

            <para>
                Für eine maximale Performance ist es Notwendig eine einfache Bootstrap Datei für die
                Server Komponente zu verwenden. Der Verwendung von
                <classname>Zend_XmlRpc_Server</classname> innerhalb von <link
                    linkend="zend.controller"><classname>Zend_Controller</classname></link> wird
                strengstens abgeraten um den Overhead zu vermeiden.
            </para>

            <para>
                Services ändern sich mit der Zeit, und während sich Webservices generell weniger
                intensiv ändern wie Code-native <acronym>APIs</acronym>, wird empfohlen den eigenen
                Service zu versionieren. Das legt den Grundstein für die Kompatibilität zu Clients
                welche eine ältere Version des eigenen Services verwenden und managt den
                Lebenszyklus des Services. Um das zu tun sollte eine Versionsnummer in die
                <acronym>URI</acronym> eingefügt werden. Es wird auch empfohlen den Namen des
                Remote Protokolls in der <acronym>URI</acronym> einzufügen um eine einfache
                Integration von zukünftigen Remote Technologien zu erlauben.
                http://myservice.ws/<emphasis>1.0/XMLRPC/</emphasis>.
            </para>
        </sect3>

        <sect3 id="zend.xmlrpc.server.anatomy.expose">
            <title>Was kann man freigeben?</title>

            <para>
                Meistens ist es nicht sensibel Business Objekte direkt freizugeben. Business
                Objekte sind normalerweise klein und werden häufig geändert, da die Änderung in
                diesem Layer der Anwendung sehr billig ist. Sobald Webservices bekanntgegeben und
                verwendet werden ist eine Änderung sehr hart. Andere Vorbehalte sind
                <acronym>I/O</acronym> und Latenz: die besten Aufrufe von Webservices sind jene die
                nicht stattfinden. Deswegen müssen Aufrufe zu Services grob körniger sein als die
                normale Business Logik selbst. Oft macht ein zusätzlicher Layer vor der Business
                Logik sinn. Dieser Layer wird manchmal als <ulink
                    url="http://martinfowler.com/eaaCatalog/remoteFacade.html">Remote Facade</ulink>
                bezeichnet. Solch ein Service Layer fügt ein grob körniges Interface über der
                Business Logik hinzu und gruppiert langatmige Operationen zu kleineren.
            </para>
        </sect3>
    </sect2>

    <sect2 id="zend.xmlrpc.server.conventions">
        <title>Konventionen</title>

        <para>
            <classname>Zend_XmlRpc_Server</classname> ermöglicht es dem Entwickler, Funktionen und
            Methodenaufrufe als ausführbare <acronym>XML-RPC</acronym> Methoden anzufügen. Durch
            <classname>Zend_Server_Reflection</classname> wird die Überwachung aller angefügten
            Methoden - durch Nutzung der DocBlocks der Methoden und Funktionen
            werden deren Hilfstexte und Signaturen ermittelt - ermöglicht.
        </para>

        <para>
            <acronym>XML-RPC</acronym> Typen werden nicht zwingend 1:1 zu <acronym>PHP</acronym>
            Typen konvertiert. Dennoch wird versucht, einen passenden Typ, anhand der in
            @param- und @return-Zeilen enthaltenen Werte, zu ermitteln. Einige
            <acronym>XML-RPC</acronym> Typen besitzen jedoch kein direktes Äquivalent und sollten
            deshalb mittels <acronym>PHP</acronym>doc auf einen <acronym>XML-RPC</acronym> Typen
            hinweisen. Diese beinhalten:
        </para>

        <itemizedlist>
            <listitem>
                <para>
                    <emphasis><property>dateTime.iso8601</property></emphasis>, ein String, der das
                    Format <command>YYYYMMDDTHH:mm:ss</command> besitzt
                </para>
            </listitem>

            <listitem><para><emphasis>base64</emphasis>, base64-kodierte Daten</para></listitem>

            <listitem>
                <para><emphasis>struct</emphasis>, jegliches assoziatives Array</para>
            </listitem>
        </itemizedlist>

        <para>
            'Anbei ein Beispiel für einen solchen Hinweis:
        </para>

        <programlisting language="php"><![CDATA[
/**
* Dies ist eine Beispielfunktion.
*
* @param base64 $val1 Base64-kodierte Daten
* @param dateTime.iso8601 $val2 Ein ISO-Datum
* @param struct $val3 ein assoziatives Array
* @return struct
*/
function myFunc($val1, $val2, $val3)
{
}
]]></programlisting>

        <para>
            PhpDocumentor validiert keine Typen, die in Parameter- oder
            Rückgabewerten angegeben sind, weshalb dies keinen Einfluss auf
            die <acronym>API</acronym> Dokumentation hat. Das Angeben der Hinweise ist notwendig,
            da der Server die, dem Methodenaufruf zugewiesenen, Parameter
            validiert.
        </para>

        <para>
            Es ist genauso gut möglich, mehrere Werte als Parameter oder für
            die Rückgabe anzugeben; die <acronym>XML-RPC</acronym> Spezifikation schlägt sogar
            vor, dass system.methodeSignatur ein Array, das alle möglichen
            Methodensignaturen (d.h. jegliche Kombination aus Parametern und
            Rückgabewerten) enthält, zurückgibt. Um dies zu erreichen, kann
            man, wie man es normalerweise auch beim PhpDocumentor auch tun würde,
            einfach den '|'-Operator nutzen.
        </para>

        <programlisting language="php"><![CDATA[
/**
* Dies ist eine Beispiel-Funktion.
*
* @param string|base64 $val1 String oder base64-kodierte Daten
* @param string|dateTime.iso8601 $val2 String oder ein ISO-Datum
* @param array|struct $val3 Normal indiziertes oder assoziatives Array
* @return boolean|struct
*/
function myFunc($val1, $val2, $val3)
{
}
]]></programlisting>

        <note>
            <para>
                Wenn man viele Signaturen erlaubt kann dies zu Verwirrung bei Entwicklern führen,
                welche diese Services nutzen; um die Dinge einfach zu halten sollte eine
                <acronym>XML-RPC</acronym> Methode deshalb nur eine Signatur haben.
            </para>
        </note>
    </sect2>

    <sect2 id="zend.xmlrpc.server.namespaces">
        <title>Nutzen von Namensräumen</title>

        <para>
            <acronym>XML-RPC</acronym> besitzt ein Konzept für Namensräume; Grundlegend erlaubt es
            das Gruppieren von <acronym>XML-RPC</acronym> Methoden durch Punkt-separierte
            Namensräume. Dies hilft, Namenkollisionen zwischen Methoden, die durch verschiedene
            Klassen offeriert werden, zu verhindern. Beispielsweise kann der
            <acronym>XML-RPC</acronym> Server mehrere Methoden im 'system'-Namensraum nutzen:
        </para>

        <itemizedlist>
            <listitem><para>system.listMethods</para></listitem>
            <listitem><para>system.methodHelp</para></listitem>
            <listitem><para>system.methodSignature</para></listitem>
        </itemizedlist>

        <para>
            Intern werden die Methoden zu Methoden desselben Namens in der
            Klasse <classname>Zend_XmlRpc_Server</classname> umgeleitet.
        </para>

        <para>
            Um angebotenen Methoden Namensräume hinzuzufügen, muss man lediglich beim
            Hinzufügen der gewünschten Klasse oder Funktion einen Namensraum angeben:
        </para>

        <programlisting language="php"><![CDATA[
// Alle öffentlichten Methoden in My_Service_Class sind als
// myservice.METHODNAME verfügbar
$server->setClass('My_Service_Class', 'myservice');

// Funktion 'somefunc' ist als funcs.somefunc ansprechbar.
$server->addFunction('somefunc', 'funcs');
]]></programlisting>
    </sect2>

    <sect2 id="zend.xmlrpc.server.request">
        <title>Eigene Request-Objekte</title>

        <para>
            Die meiste Zeit wird man einfach den Standard-Anfragetyp
            <classname>Zend_XmlRpc_Request_Http</classname>, welcher im
            <classname>Zend_XmlRpc_Server</classname> enthalten ist, nutzen. Jedoch gibt es
            gelegentlich Fälle, in denen <acronym>XML-RPC</acronym> über die Kommandozeile
            (<acronym>CLI</acronym>), ein grafisches Benutzerinterface (<acronym>GUI</acronym>),
            eine andere Umgebung oder beim Protokollieren von ankommenden Anfragen erreichbar sein
            muss. Um dies zu bewerkstelligen, muss man ein eigenes Anfrage-Objekt kreieren, das
            <classname>Zend_XmlRpc_Request</classname> erweitert. Die wichtigste Sache, die man
            sich merken muss, ist sicherzustellen, dass die Methoden
            <methodname>getMethod()</methodname> und <methodname>getParams()</methodname>
            implementiert sind, so dass der <acronym>XML-RPC</acronym> Server Informationen erhält,
            die er für das Abfertigen einer Anfrage benötigt.
        </para>
    </sect2>

    <sect2 id="zend.xmlrpc.server.response">
        <title>Eigene Antwort-Objekte</title>

        <para>
            Ähnlich wie bei den Anfrage-Objekten, kann der <classname>Zend_XmlRpc_Server</classname>
            auch eigene Antwortobjekte ausliefern; standardmäßig ist dies ein
            <classname>Zend_XmlRpc_Response_Http-Objekt</classname>, das einen passenden
            Content-Type <acronym>HTTP</acronym>-Header sendet, der für <acronym>XML-RPC</acronym>
            genutzt wird. Mögliche Nutzungen eines eigenen Objekts sind z.B. das Protokollieren von
            Antworten oder das Senden der Antworten zu <constant>STDOUT</constant>.
        </para>

        <para>
            Um eine eigene Antwortklasse zu nutzen, muss
            <methodname>Zend_XmlRpc_Server::setResponseClass()</methodname> vor dem Aufruf von
            <methodname>handle()</methodname> aufgerufen werden.
        </para>
    </sect2>

    <sect2 id="zend.xmlrpc.server.fault">
        <title>Verarbeiten von Exceptions durch Fehler</title>

        <para>
            <classname>Zend_XmlRpc_Server</classname> fängt die, durch eine ausgeführte Methode
            erzeugten, Exceptions and generiert daraus einen <acronym>XML-RPC</acronym> Fehler als
            Antwort, wenn eine Exception gefangen wurde. Normalerweise werden die
            Exceptionnachrichten und -codes nicht in der Fehler-Antwort genutzt. Dies ist eine
            gewollte Entscheidung um den Code zu schützen; viele Exceptions entblößen mehr
            Informationen über den Code oder die Umgebung als der Entwickler
            wünscht (ein Paradebeispiel beinhaltet Datenbankabstraktion- oder
            die Zugriffsschichten-Exceptions).
        </para>

        <para>
            Exception-Klassen können jedoch anhand einer Weißliste (Whitelist) als
            Fehler-Antworten zurückgegeben werden. Dazu muss man lediglich die gewünschte
            Exception mittels
            <methodname>Zend_XmlRpc_Server_Fault::attachFaultException()</methodname> zur
            Weißliste hinzufügen:
        </para>

        <programlisting language="php"><![CDATA[
Zend_XmlRpc_Server_Fault::attachFaultException('My_Project_Exception');
]]></programlisting>

        <para>
            Abgeleitete Exceptions lassen sich als ganze Familie von Exceptions
            hinzufügen, indem man deren Basisklasse angibt.
            <classname>Zend_XmlRpc_Server_Exception</classname>'s sind immer auf der Weißliste zu
            finden, da sie spezielle Serverfehler berichten (undefinierte Methoden, etc.).
        </para>

        <para>
            Jede Exception, die nicht auf der Weißliste zu finden ist, generiert
            eine Antwort mit dem '404' Code und der Nachricht 'Unknown error'.
        </para>
    </sect2>

    <sect2 id="zend.xmlrpc.server.caching">
        <title>Zwischenspeichern von Serverdefinitionen zwischen den Anfragen</title>

        <para>
            Das Hinzufügen einer Vielzahl von Klassen zu einer <acronym>XML-RPC</acronym> Server
            Instanz kann zu einem großen Ressourcenverbrauch führen; jede Klasse muss via Reflection
            <acronym>API</acronym> (<classname>Zend_Server_Reflection</classname>) inspiziert
            werden, welche eine Liste von allen möglichen Signaturen, die der Server verwenden kann,
            zurückgibt.
        </para>

        <para>
            Um die Einbußen zu reduzieren, kann <classname>Zend_XmlRpc_Server_Cache</classname>
            genutzt werden, welche die Serverdefinitionen zwischen den Anfragen zwischenspeichert.
            Wenn dies mit <methodname>__autoload()</methodname> kombiniert wird, kann es zu einem
            großen Geschwindigkeitsschub kommen.
        </para>

        <para>
            Ein Beispiel folgt:
        </para>

        <programlisting language="php"><![CDATA[
function __autoload($class)
{
    Zend_Loader::loadClass($class);
}

$cacheFile = dirname(__FILE__) . '/xmlrpc.cache';
$server = new Zend_XmlRpc_Server();

if (!Zend_XmlRpc_Server_Cache::get($cacheFile, $server)) {
    require_once 'My/Services/Glue.php';
    require_once 'My/Services/Paste.php';
    require_once 'My/Services/Tape.php';

    $server->setClass('My_Services_Glue', 'glue');   // glue. Namensraum
    $server->setClass('My_Services_Paste', 'paste'); // paste. Namensraum
    $server->setClass('My_Services_Tape', 'tape');   // tape. Namensraum

    Zend_XmlRpc_Server_Cache::save($cacheFile, $server);
}

echo $server->handle();
]]></programlisting>

        <para>
            Obiges Beispiel zeigt, wie der Server versucht, eine Definition
            aus der Datei <property>xmlrpc.cache</property>, welches sich im selben Ordner wie das
            Skript befindet, zu laden. Wenn dies nicht erfolgreich ist,
            lädt es die Server-Klassen, die es benötigt, und fügt sie zum
            Server hinzu. Danach wird versucht, die Cache-Datei mit der
            Serverdefinition zu erstellen.
        </para>
    </sect2>

    <sect2 id="zend.xmlrpc.server.use">
        <title>Nutzungsbeispiele</title>

        <para>
            Unten finden sich etliche Beispiele für eine Nutzung, die das
            gesamte Spektrum der verfügbaren Optionen für den Entwickler darstellen.
            These Beispiele bauen immer auf den vorangegangenen Beispielen auf.
        </para>

        <example id="zend.xmlrpc.server.use.attach-function">
            <title>Grundlegende Benutzung</title>

            <para>
                Folgendes Beispiel fügt eine Funktion als ausführbare
                <acronym>XML-RPC</acronym> Methode hinzu und verarbeitet eingehende Aufrufe.
            </para>

            <programlisting language="php"><![CDATA[
/**
 * Gibt die MD5-Summe eines Strings zurück.
 *
 * @param string $value Wert aus dem die MD5-Summe errechnet wird
 * @return string MD5-Summe des Werts
 */
function md5Value($value)
{
    return md5($value);
}

$server = new Zend_XmlRpc_Server();
$server->addFunction('md5Value');
echo $server->handle();
]]></programlisting>
        </example>

        <example id="zend.xmlrpc.server.use.attach-class">
            <title>Hinzufügen einer Klasse</title>

            <para>
                Das nächste Beispiel illustriert, wie man die öffentlichen Methoden
                eienr Klasse als ausführbare <acronym>XML-RPC</acronym> Methoden hinzufügt.
            </para>

            <programlisting language="php"><![CDATA[
$server = new Zend_XmlRpc_Server();
$server->setClass('Services_Comb');
echo $server->handle();
]]></programlisting>
        </example>

        <example id="zend.xmlrpc.server.use.attach-class-with-arguments">
            <title>Hinzufügen einer Klasse mit Argumenten</title>

            <para>
                Das folgende Beispiel zeigt wie öffentliche Methoden einer Klasse hinzugefügt werden
                und an deren Methoden Argumente übergeben werden können. Das kann verwendet werden
                um spezielle Standardwerte zu spezifizieren wenn Serviceklassen registriert werden.
            </para>

            <programlisting language="php"><![CDATA[
class Services_PricingService
{
    /**
     * Calculate current price of product with $productId
     *
     * @param ProductRepository $productRepository
     * @param PurchaseRepository $purchaseRepository
     * @param integer $productId
     */
    public function calculate(ProductRepository $productRepository,
                              PurchaseRepository $purchaseRepository,
                              $productId)
    {
        ...
    }
}

$server = new Zend_XmlRpc_Server();
$server->setClass('Services_PricingService',
                  'pricing',
                  new ProductRepository(),
                  new PurchaseRepository());
]]></programlisting>

            <para>
                Die Argumente welche an <methodname>setClass()</methodname> während der
                Erstellungszeit des Servers übergeben werden, werden dem Methodenaufruf
                <command>pricing.calculate()</command> injiziert, wenn er aufgerufen wird. Im obigen
                Beispiel wird vom Client nur das Argument <varname>$purchaseId</varname> erwartet.
            </para>
        </example>

        <example id="zend.xmlrpc.server.use.attach-class-with-arguments-constructor">
            <title>Argumente nur dem Constructor übergeben</title>

            <para>
                <classname>Zend_XmlRpc_Server</classname> erlaubt es die Übergabe von Argumenten nur
                für den Constructor zu limitieren. Das kann für Dependency Injection beim
                Constructor verwendet werden. Um die Injektion auf Constructoren zu begrenzen muss
                <methodname>sendArgumentsToAllMethods</methodname> aufgerufen und
                <constant>FALSE</constant> als Argument übergeben werden. Dies deaktiviert das
                Standardverhalten mit dem alle Argumente in die entfernte Methode injiziert werden.
                Im nächsten Beispiel werden die Instanzen von
                <classname>ProductRepository</classname> und
                <classname>PurchaseRepository</classname> nur in dem Constructor von
                <classname>Services_PricingService2</classname> injiziert.
            </para>

            <programlisting language="php"><![CDATA[
class Services_PricingService2
{
    /**
     * @param ProductRepository $productRepository
     * @param PurchaseRepository $purchaseRepository
     */
    public function __construct(ProductRepository $productRepository,
                                PurchaseRepository $purchaseRepository)
    {
        ...
    }

    /**
     * Calculate current price of product with $productId
     *
     * @param integer $productId
     * @return double
     */
    public function calculate($productId)
    {
        ...
    }
}

$server = new Zend_XmlRpc_Server();
$server->sendArgumentsToAllMethods(false);
$server->setClass('Services_PricingService2',
                  'pricing',
                  new ProductRepository(),
                  new PurchaseRepository());
]]></programlisting>
        </example>

        <example id="zend.xmlrpc.server.use.attach-instance">
            <title>Die Instanz einer Klasse anhängen</title>

            <para>
                <methodname>setClass()</methodname> erlaubt es ein vorher instanziertes Objekt beim
                Server zu registrieren. Man muss nur die Instanz statt dem Namen der Klasse
                übergeben. Natürlich ist die Übergabe von Argumenten an den Constructor bei vorher
                instanzierten Objekten nicht möglich.
            </para>
        </example>

        <example id="zend.xmlrpc.server.use.attach-several-classes-namespaces">
            <title>Mehrere Klassen unter der Nutzung von Namensräumen hinzufügen</title>

            <para>
                Das nächste Beispiel zeigt, wie man mehrer Klassen mit ihren eigenen
                Namensräumen hinzufügt.
            </para>

            <programlisting language="php"><![CDATA[
require_once 'Services/Comb.php';
require_once 'Services/Brush.php';
require_once 'Services/Pick.php';

$server = new Zend_XmlRpc_Server();

// Methoden werden als comb.* aufgerufen
$server->setClass('Services_Comb', 'comb');

// Methoden werden als brush.* aufgerufen
$server->setClass('Services_Brush', 'brush');

// Methoden werden als pick.* aufgerufen
$server->setClass('Services_Pick', 'pick');
echo $server->handle();
]]></programlisting>
        </example>

        <example id="zend.xmlrpc.server.use.exception-faults">
            <title>Bestimmen von Exceptions als gültige Fehler-Antwort</title>

            <para>
                Im nächsten Beispiel wird gezeigt, wie man jede Exception, die von
                <classname>Services_Exception</classname> abgeleitet wurde, als Fehler-Antwort
                nutzen kann, dessen Nachricht und Code erhalten bleibt.
            </para>

            <programlisting language="php"><![CDATA[
require_once 'Services/Exception.php';
require_once 'Services/Comb.php';
require_once 'Services/Brush.php';
require_once 'Services/Pick.php';

// Services_Exceptions dürfen als Fehler-Antwort genutzt werden
Zend_XmlRpc_Server_Fault::attachFaultException('Services_Exception');

$server = new Zend_XmlRpc_Server();

// Methoden werden als comb.* aufgerufen
$server->setClass('Services_Comb', 'comb');

// Methoden werden als brush.* aufgerufen
$server->setClass('Services_Brush', 'brush');

// Methoden werden als pick.* aufgerufen
$server->setClass('Services_Pick', 'pick');
echo $server->handle();
]]></programlisting>
        </example>

        <example id="zend.xmlrpc.server.use.custom-request-object">
            <title>Anpassen eigener Anfrage und Antwort Objekte</title>

            <para>
                 Einige Anwendungsfälle verlangen die Verwendung eines eigenen Request Objektes.
                 Zum Beispiel ist <acronym>XML/RPC</acronym> nicht an <acronym>HTTP</acronym> als
                 Übertragungsprotokoll gebunden. Es ist möglich auch andere Übertragungsprotokolle
                 wie <acronym>SSH</acronym> oder Telnet zu verwenden um Anfrage und Antwort Daten
                 über den Draht zu senden. Ein anderer Anwendungsfall ist die Authentifizierung
                 und Authorisierung. Im Falle eines anderen Übertragungsprotokolls muss die
                 Implementation geändert werden damit Anfrage Daten gelesen werden können.
            </para>

            <para>
                Im folgenden Beispiel wird ein eigenes Anfrage-Objekt instanziert
                und durch den Server verarbeitet.
            </para>

            <programlisting language="php"><![CDATA[
require_once 'Services/Request.php';
require_once 'Services/Exception.php';
require_once 'Services/Comb.php';
require_once 'Services/Brush.php';
require_once 'Services/Pick.php';

// Services_Exceptions dürfen als Fehler-Antwort genutzt werden
Zend_XmlRpc_Server_Fault::attachFaultException('Services_Exception');

$server = new Zend_XmlRpc_Server();

// Methoden werden als comb.* aufgerufen
$server->setClass('Services_Comb', 'comb');

// Methoden werden als brush.* aufgerufen
$server->setClass('Services_Brush', 'brush');

// Methoden werden als pick.* aufgerufen
$server->setClass('Services_Pick', 'pick');

// Ein neues Anfrage-Objekt wird erstellt
$request = new Services_Request();

echo $server->handle($request);
]]></programlisting>
        </example>

        <example id="zend.xmlrpc.server.use.custom-response-object">
            <title>Eine eigene Antwort Klasse spezifizieren</title>

            <para>
                Das nachstehende Beispiel zeigt, wie man eine eigene Antwort-Klasse
                als zurückgegebene Antwort für den Server setzt.
            </para>

            <programlisting language="php"><![CDATA[
require_once 'Services/Request.php';
require_once 'Services/Response.php';
require_once 'Services/Exception.php';
require_once 'Services/Comb.php';
require_once 'Services/Brush.php';
require_once 'Services/Pick.php';

// Services_Exceptions dürfen als Fehler-Antwort genutzt werden
Zend_XmlRpc_Server_Fault::attachFaultException('Services_Exception');

$server = new Zend_XmlRpc_Server();

// Methoden werden als comb.* aufgerufen
$server->setClass('Services_Comb', 'comb');

// Methoden werden als brush.* aufgerufen
$server->setClass('Services_Brush', 'brush');

// Methoden werden als pick.* aufgerufen
$server->setClass('Services_Pick', 'pick');

// Ein neues Anfrage-Objekt wird erstellt
$request = new Services_Request();

// Nutzen eigener Antwort-Klasse
$server->setResponseClass('Services_Response');

echo $server->handle($request);
]]></programlisting>
        </example>
    </sect2>

    <sect2 id="zend.xmlrpc.server.performance">
        <title>Performance Optimierung</title>

        <example id="zend.xmlrpc.server.performance.caching">
            <title>Zwischenspeichern von Serverdefinition zwischen den Anfragen</title>

            <para>
                Dieses Beispiel zeigt, wie man Serverdefinitionen zwischen verschiedenen
                Anfragen zwischenspeichern kann.
            </para>

            <programlisting language="php"><![CDATA[
// Definieren einer Cache-Datei
$cacheFile = dirname(__FILE__) . '/xmlrpc.cache';

// Services_Exceptions dürfen als Fehler-Antwort genutzt werden
Zend_XmlRpc_Server_Fault::attachFaultException('Services_Exception');

$server = new Zend_XmlRpc_Server();

// Versucht die Serverdefinition aus dem Cache zu laden
if (!Zend_XmlRpc_Server_Cache::get($cacheFile, $server)) {

    // Methoden werden als comb.* aufgerufen
    $server->setClass('Services_Comb', 'comb');

    // Methoden werden als brush.* aufgerufen
    $server->setClass('Services_Brush', 'brush');

    // Methoden werden als pick.* aufgerufen
    $server->setClass('Services_Pick', 'pick');

    // Speichern des Caches
    Zend_XmlRpc_Server_Cache::save($cacheFile, $server);
}

// Ein neues Anfrage-Objekt wird erstellt
$request = new Services_Request();

// Nutzen eigener Antwort-Klasse
$server->setResponseClass('Services_Response');

echo $server->handle($request);
]]></programlisting>
        </example>

        <note>
            <para>
                Die Datei des Server Caches sollte ausserhalb des Document Roots platziert
                werden.
            </para>
        </note>

        <example id="zend.xmlrpc.server.performance.xmlgen">
            <title>Optimizing XML generation</title>

            <para>
                <classname>Zend_XmlRpc_Server</classname> verwendet
                <classname>DOMDocument</classname> der <acronym>PHP</acronym> Erweiterung
                <emphasis>ext/dom</emphasis> um seine <acronym>XML</acronym> Ausgaben zu erstellen.
                Wärend <emphasis>ext/dom</emphasis> auf vielen Hosts vorhanden ist, ist es nicht
                wirklich das schnellste. Benchmarks haben gezeigt das
                <classname>XmlWriter</classname> von <emphasis>ext/xmlwriter</emphasis> schneller
                ist.
            </para>

            <para>
                Wenn <emphasis>ext/xmlwriter</emphasis> auf dem eigenen Host vorhanden ist, kann ein
                <classname>XmlWriter</classname>-basierter Generator ausgewählt werden um die
                Performance Unterschiede auszunutzen.
            </para>

            <programlisting language="php"><![CDATA[
require_once 'Zend/XmlRpc/Server.php';
require_once 'Zend/XmlRpc/Generator/XmlWriter.php';

Zend_XmlRpc_Value::setGenerator(new Zend_XmlRpc_Generator_XmlWriter());

$server = new Zend_XmlRpc_Server();
...
]]></programlisting>
        </example>

        <note>
            <title>Die eigene Anwendung benchmarken</title>

            <para>
                Performance wird durch eine Vielzahl an Parametern und Benchmarks ermittelt
                welche nur für den speziellen Testfall angewendet werden. Unterschiede basieren
                auf der <acronym>PHP</acronym> Version, installierten Erweiterungen, dem Webserver
                und dem Betriebssystem um nur einige zu nennen. Man sollte darauf achten das man die
                eigene Anwendung selbst benchmarkt und anschließend auswählt welchen Generator
                man verwendet, aber basierend auf <emphasis>eigenen</emphasis> Zahlen.
            </para>
        </note>

        <note>
            <title>Den eigenen Client benchmarken</title>

            <para>
                Diese Optimierung macht auch für die Client Seite Sinn. Man muss den
                alternativen <acronym>XML</acronym> Generator nur auswählen bevor man irgendeine
                Arbeit mit <classname>Zend_XmlRpc_Client</classname> durchführt.
            </para>
        </note>
    </sect2>
</sect1>
