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

    <sect2 id="zend.xmlrpc.client.introduction">
        <title>Introduction</title>

        <para>
            Zend Framework possède la capacité de consommer des services distants XML-RPC, via
            la classe <classname>Zend_XmlRpc_Client</classname>. Ses caractéristiques principales
            sont la conversion automatique des types entre <acronym>PHP</acronym> et XML-RPC, un objet proxy de
            serveur, et des possibilités d'introspection du serveur.
        </para>
    </sect2>

    <sect2 id="zend.xmlrpc.client.method-calls">
        <title>Appels de méthodes</title>

        <para>
            Le constructeur de <classname>Zend_XmlRpc_Client</classname> reçoit en premier
            paramètre l'URL du serveur XML-RPC distant. L'instance retournée pourra alors être
            utilisée pour appeler n'importe quelle méthode distante.
        </para>

        <para>
            Pour appeler une méthode distante, utilisez la méthode <methodname>call()</methodname> de
            votre instance. Le code suivant montre un exemple avec le serveur XML-RPC du site de
            Zend Framework. Vous pouvez l'utiliser pour tester ou explorer les possibilités des
            composants <classname>Zend_XmlRpc</classname>.
        </para>

        <example id="zend.xmlrpc.client.method-calls.example-1">
            <title>XML-RPC appel de méthode</title>

            <programlisting language="php"><![CDATA[
$client = new Zend_XmlRpc_Client('http://framework.zend.com/xmlrpc');

echo $client->call('test.sayHello');

// hello
]]></programlisting>
        </example>

        <para>
            Le type de la valeur XML-RPC retournée sera automatiquement casté en un type
            compatible <acronym>PHP</acronym>. Dans l'exemple ci-dessus, une <code>string</code> <acronym>PHP</acronym> est retournée et
            immédiatement utilisable.
        </para>

        <para>
            Le premier paramètre de <methodname>call()</methodname> est le nom de la méthode distante à
            appeler. Si celle-ci demande des paramètres, ceux-ci doivent alors être passés via le
            deuxième paramètre de <methodname>call()</methodname>, sous forme de tableau <acronym>PHP</acronym>
            (<code>array</code>) :
        </para>

        <example id="zend.xmlrpc.client.method-calls.example-2">
            <title>XML-RPC appel de méthode avec des paramètres</title>

            <programlisting language="php"><![CDATA[
$client = new Zend_XmlRpc_Client('http://framework.zend.com/xmlrpc');

$arg1 = 1.1;
$arg2 = 'foo';

$result = $client->call('test.sayHello', array($arg1, $arg2));

// $result est un type PHP natif
]]></programlisting>
        </example>

        <para>
            Le tableau de paramètres peut contenir des types <acronym>PHP</acronym> natifs, des objets
            <classname>Zend_XmlRpc_Value</classname>, ou bien les deux à la fois.
        </para>

        <para>
            La méthode <methodname>call()</methodname> convertira automatiquement la réponse XML-RPC et
            retournera un type <acronym>PHP</acronym> natif valide. Un objet
            <classname>Zend_XmlRpc_Response</classname> pour la valeur de retour sera de même
            disponible, via un appel à <methodname>getLastResponse()</methodname>.
        </para>
    </sect2>

    <sect2 id="zend.xmlrpc.value.parameters">
        <title>Types et conversions</title>

        <para>
            Certaines méthodes distantes requièrent des paramètres. Ceux-ci sont donnés sous
            forme de tableau <acronym>PHP</acronym> à <methodname>call()</methodname>. Chaque paramètre est supposé être un type
            <acronym>PHP</acronym> natif qui sera alors lui-même converti, ou alors un objet représentant un type
            XML-RPC (un objet parmi les <classname>Zend_XmlRpc_Value</classname>).
        </para>

        <sect3 id="zend.xmlrpc.value.parameters.php-native">
            <title>Types PHP natifs comme paramètres</title>

            <para>
                Les paramètres passés à <methodname>call()</methodname> peuvent être d'un type <acronym>PHP</acronym> natif,
                à savoir <code>string</code>, <code>integer</code>, <code>float</code>,
                <code>boolean</code>, <code>array</code>, ou <code>object</code>. Dans ce cas,
                chacun des types sera converti de manière automatique en son type compatible
                XML-RPC, suivant la table suivante :
            </para>

            <table id="zend.xmlrpc.value.parameters.php-native.table-1">
                <title>PHP et XML-RPC, conversions de types</title>

                <tgroup cols="2">
                    <thead>
                        <row>
                            <entry>Type <acronym>PHP</acronym> natif</entry>
                            <entry>XML-RPC type</entry>
                        </row>
                    </thead>

                    <tbody>
                        <row>
                            <entry>integer</entry>
                            <entry>int</entry>
                        </row>
                        <row>
                            <entry>Zend_Crypt_Math_BigInteger</entry>
                            <entry>i8</entry>
                        </row>
                        <row>
                            <entry>double</entry>
                            <entry>double</entry>
                        </row>
                        <row>
                            <entry>boolean</entry>
                            <entry>boolean</entry>
                        </row>
                        <row>
                            <entry>string</entry>
                            <entry>string</entry>
                        </row>
                        <row>
                            <entry>null</entry>
                            <entry>nil</entry>
                        </row>
                        <row>
                            <entry>array</entry>
                            <entry>array</entry>
                        </row>
                        <row>
                            <entry>associative array</entry>
                            <entry>struct</entry>
                        </row>
                        <row>
                            <entry>object</entry>
                            <entry>array</entry>
                        </row>
                        <row>
                            <entry>Zend_Date</entry>
                            <entry>dateTime.iso8601</entry>
                        </row>
                        <row>
                            <entry>DateTime</entry>
                            <entry>dateTime.iso8601</entry>
                        </row>
                    </tbody>
                </tgroup>
            </table>

            <note>
                <title>Comment est casté un tableau vide ?</title>

                <para>
                    Fournir un tableau vide à une méthode XML-RPC est problématique, car il
                    peut être représenté sous la forme soit d'un tableau, soit d'une structure
                    ("struct"). <classname>Zend_XmlRpc_Client</classname> détecte ce genre de
                    conditions et fait une requête vers la méthode
                    <code>system.methodSignature</code> du serveur pour déterminer le type XML-RPC
                    approprié vers le quel casté.
                </para>

                <para>
                    Cependant, ceci peut mener malgré tout à des soucis. Premièrement, les
                    serveurs qui ne supportent <code>system.methodSignature</code> vont retourner
                    une requête de type échec, et <classname>Zend_XmlRpc_Client</classname>
                    résultera en un cast de la valeur de type tableau XML-RPC ("array"). De plus,
                    ceci sous-entend que tout appel avec des arguments de type tableau entraîneront
                    un appel additionnel au serveur distant.
                </para>

                <para>
                    Pour désactiver entièrement la recherche, vous pouvez appeler la méthode
                    <methodname>setSkipSystemLookup()</methodname> avant de réaliser votre appel XML-RPC
                    :
                </para>

                <programlisting language="php"><![CDATA[
$client->setSkipSystemLookup(true);
$result = $client->call('foo.bar', array(array()));
]]></programlisting>
            </note>
        </sect3>

        <sect3 id="zend.xmlrpc.value.parameters.xmlrpc-value">
            <title>Objets <classname>Zend_XmlRpc_Value</classname> en tant que
            paramètres</title>

            <para>
                Les paramètres peuvent aussi être des objets
                <classname>Zend_XmlRpc_Value</classname> qui spécifient alors exactement un type
                XML-RPC. Les raisons principales d'utiliser un tel procédé sont : <itemizedlist>
                        <listitem>
                        <para>
                            Lorsque vous voulez être certain du type de paramètre (la méthode
                            attend un entier et vous le récupérez sous forme de chaîne de caractères
                            depuis une base de données).
                        </para>
                    </listitem>

                    <listitem>
                        <para>
                            Lorsque la méthode attend un type <code>base64</code> ou
                            <code>dateTime.iso8601</code> (ceux-ci n'existant pas nativement dans le
                            langage <acronym>PHP</acronym>).
                        </para>
                    </listitem>

                    <listitem>
                        <para>
                            Lorsque la conversion de types (cast) peut échouer (vous voulez
                            passer une valeur XML-RPC vide comme paramètre. Mais les valeurs vides
                            en <acronym>PHP</acronym> sont représentés sous forme de tableaux vides, or si vous passez
                            un tableau vide à votre méthode <code>call</code>, il va être converti
                            en un tableau XML-RPC, comme ce n'est pas un tableau associatif).
                        </para>
                    </listitem>
                    </itemizedlist>
                </para>

            <para>
                Deux manières existent pour créer des objets
                <classname>Zend_XmlRpc_Value</classname> : instanciez une sous-classe
                <classname>Zend_XmlRpc_Value</classname> directement, ou utilisez une fabrique
                ("factory method") telle que
                <methodname>Zend_XmlRpc_Value::getXmlRpcValue()</methodname>.
            </para>

            <table id="zend.xmlrpc.value.parameters.xmlrpc-value.table-1">
                <title>Objets <classname>Zend_XmlRpc_Value</classname> comme types
                XML-RPC</title>

                <tgroup cols="3">
                    <thead>
                        <row>
                            <entry>XML-RPC Type</entry>
                            <entry><classname>Zend_XmlRpc_Value</classname> Constante</entry>
                            <entry><classname>Zend_XmlRpc_Value</classname> Objet</entry>
                        </row>
                    </thead>

                    <tbody>
                        <row>
                            <entry>int</entry>
                            <entry><constant>Zend_XmlRpc_Value::XMLRPC_TYPE_INTEGER</constant></entry>
                            <entry><classname>Zend_XmlRpc_Value_Integer</classname></entry>
                        </row>

                        <row>
                            <entry>i8</entry>
                            <entry><constant>Zend_XmlRpc_Value::XMLRPC_TYPE_I8</constant></entry>
                            <entry><classname>Zend_XmlRpc_Value_BigInteger</classname></entry>
                        </row>

                        <row>
                            <entry>ex:i8</entry>
                            <entry><constant>Zend_XmlRpc_Value::XMLRPC_TYPE_APACHEI8</constant></entry>
                            <entry><classname>Zend_XmlRpc_Value_BigInteger</classname></entry>
                        </row>

                        <row>
                            <entry>double</entry>
                            <entry><constant>Zend_XmlRpc_Value::XMLRPC_TYPE_DOUBLE</constant></entry>
                            <entry><classname>Zend_XmlRpc_Value_Double</classname></entry>
                        </row>

                        <row>
                            <entry>boolean</entry>
                            <entry><constant>Zend_XmlRpc_Value::XMLRPC_TYPE_BOOLEAN</constant></entry>
                            <entry><classname>Zend_XmlRpc_Value_Boolean</classname></entry>
                        </row>

                        <row>
                            <entry>string</entry>
                            <entry><constant>Zend_XmlRpc_Value::XMLRPC_TYPE_STRING</constant></entry>
                            <entry><classname>Zend_XmlRpc_Value_String</classname></entry>
                        </row>

                        <row>
                            <entry>nil</entry>
                            <entry><constant>Zend_XmlRpc_Value::XMLRPC_TYPE_NIL</constant></entry>
                            <entry><classname>Zend_XmlRpc_Value_Nil</classname></entry>
                        </row>

                        <row>
                            <entry>ex:nil</entry>
                            <entry><constant>Zend_XmlRpc_Value::XMLRPC_TYPE_APACHENIL</constant></entry>
                            <entry><classname>Zend_XmlRpc_Value_Nil</classname></entry>
                        </row>

                        <row>
                            <entry>base64</entry>
                            <entry><constant>Zend_XmlRpc_Value::XMLRPC_TYPE_BASE64</constant></entry>
                            <entry><classname>Zend_XmlRpc_Value_Base64</classname></entry>
                        </row>

                        <row>
                            <entry>dateTime.iso8601</entry>
                            <entry><constant>Zend_XmlRpc_Value::XMLRPC_TYPE_DATETIME</constant></entry>
                            <entry><classname>Zend_XmlRpc_Value_DateTime</classname></entry>
                        </row>

                        <row>
                            <entry>array</entry>
                            <entry><constant>Zend_XmlRpc_Value::XMLRPC_TYPE_ARRAY</constant></entry>
                            <entry><classname>Zend_XmlRpc_Value_Array</classname></entry>
                        </row>

                        <row>
                            <entry>struct</entry>
                            <entry><constant>Zend_XmlRpc_Value::XMLRPC_TYPE_STRUCT</constant></entry>
                            <entry><classname>Zend_XmlRpc_Value_Struct</classname></entry>
                        </row>
                    </tbody>
                </tgroup>
            </table>

            <para>
                <note>
                    <title>Conversion automatique</title>

                    <para>
                        Lorsque vous créez un objet <classname>Zend_XmlRpc_Value</classname>,
                        sa valeur est déterminée par un type <acronym>PHP</acronym>. Celui-ci va être converti vers le
                        type désiré en utilisant le cast <acronym>PHP</acronym>. Par exemple si une chaîne de
                        caractères est donnée comme valeur à un objet
                        <classname>Zend_XmlRpc_Value_Integer</classname>, elle sera alors convertie
                        suivant la règle <code>(int)$value</code>.
                    </para>
                    </note>
                </para>
        </sect3>
    </sect2>

    <sect2 id="zend.xmlrpc.client.requests-and-responses">
        <title>Objet proxy du serveur</title>

        <para>
            Un autre moyen d'appeler des méthodes avec un client XML-RPC est d'utiliser le
            proxy du serveur. C'est un objet <acronym>PHP</acronym> qui proxie un espace de nom XML-RPC, en
            fonctionnant autant que possible comme les objets <acronym>PHP</acronym>.
        </para>

        <para>
            Pour instancier un proxy serveur, appelez <methodname>getProxy()</methodname> de
            <classname>Zend_XmlRpc_Client</classname>. Elle retourne un objet
            <classname>Zend_XmlRpc_Client_ServerProxy</classname>. Tout appel de méthode sur l'objet
            proxy sera proxié vers le serveur XML-RPC, et les paramètres seront utilisés comme pour
            une méthode <acronym>PHP</acronym> banale.
        </para>

        <example id="zend.xmlrpc.client.requests-and-responses.example-1">
            <title>Proxy espace de nom par défaut</title>

            <programlisting language="php"><![CDATA[
$client = new Zend_XmlRpc_Client('http://framework.zend.com/xmlrpc');

$service = $client->getProxy();
// Proxy l'espace de nom par défaut

$hello = $service->test->sayHello(1, 2);
// test.Hello(1, 2) retourne "hello"
]]></programlisting>
        </example>

        <para>
            La méthode <methodname>getProxy()</methodname> reçoit un argument optionnel désignant l'espace
            de nom à utiliser par le proxy. Par défaut, il s'agit de l'espace général, voici un
            exemple utilisant un espace de nom <code>test</code> :
        </para>

        <example id="zend.xmlrpc.client.requests-and-responses.example-2">
            <title>Proxy un espace de nom</title>

            <programlisting language="php"><![CDATA[
$client = new Zend_XmlRpc_Client('http://framework.zend.com/xmlrpc');

$test  = $client->getProxy('test');
// Proxy l'espace de nommage "test"

$hello = $test->sayHello(1, 2);
// test.Hello(1,2) retourne "hello"
]]></programlisting>
        </example>

        <para>
            Si le serveur distant supporte les espaces de noms imbriqués, alors le proxy les
            supportera. Par exemple, si le serveur dans l'exemple ci-dessus acceptait les espaces de
            noms imbriqués, alors sa méthode <code>test.foo.bar()</code> aurait pu être appelée via
            <code>$test-&gt;foo-&gt;bar()</code>.
        </para>
    </sect2>

    <sect2 id="zend.xmlrpc.client.error-handling">
        <title>Gestion des erreurs</title>

        <para>
            Deux types d'erreurs peuvent être distingués : erreurs <acronym>HTTP</acronym>, ou erreurs XML-RPC.
            L'objet <classname>Zend_XmlRpc_Client</classname> reconnaît ces erreurs et fournit les
            moyens de les repérer et de les gérer.
        </para>

        <sect3 id="zend.xmlrpc.client.error-handling.http">
            <title>Erreurs HTTP</title>

            <para>
                Si une erreur <acronym>HTTP</acronym> survient, par exemple le serveur renvoie un <code>404 Not
                Found</code>, alors une <classname>Zend_XmlRpc_Client_HttpException</classname> sera
                levée.
            </para>

            <example id="zend.xmlrpc.client.error-handling.http.example-1">
                <title>Gérer les erreurs HTTP</title>

                <programlisting language="php"><![CDATA[
$client = new Zend_XmlRpc_Client('http://foo/404');

try {

    $client->call('bar', array($arg1, $arg2));

} catch (Zend_XmlRpc_Client_HttpException $e) {

    // $e->getCode() retourne 404
    // $e->getMessage() retourne "Not Found"

}
]]></programlisting>
            </example>

            <para>
                Quelque soit l'utilisation du client XML-RPC, une
                <classname>Zend_XmlRpc_Client_HttpException</classname> sera systématiquement levée
                lorsqu'une erreur <acronym>HTTP</acronym> de quelque type que ce soit est rencontrée.
            </para>
        </sect3>

        <sect3 id="zend.xmlrpc.client.error-handling.faults">
            <title>Erreurs XML-RPC (Faults)</title>

            <para>
                Une erreur XML-RPC peut être assimilée à une exception en <acronym>PHP</acronym>. C'est un type
                spécial retourné par une des méthodes du client XML-RPC, et ce type contient un
                message, et un code d'erreur. Les erreurs XML-RPC seront gérées différemment en
                fonction du contexte d'utilisation de l'objet
                <classname>Zend_XmlRpc_Client</classname>.
            </para>

            <para>
                Lors de l'utilisation de la méthode <methodname>call()</methodname>, ou de l'objet proxy
                serveur, une erreur XML-RPC aura pour effet de lancer une
                <classname>Zend_XmlRpc_Client_FaultException</classname>. Le code et le message de
                l'exception seront rendus dans leurs valeurs respectives de la réponse
                XML-RPC.
            </para>

            <example id="zend.xmlrpc.client.error-handling.faults.example-1">
                <title>Gérer les erreurs XML-RPC</title>

                <programlisting language="php"><![CDATA[
$client = new Zend_XmlRpc_Client('http://framework.zend.com/xmlrpc');

try {

    $client->call('badMethod');

} catch (Zend_XmlRpc_Client_FaultException $e) {

    // $e->getCode() retourne 1
    // $e->getMessage() retourne "Unknown method"

}
]]></programlisting>
            </example>

            <para>
                En utilisant <methodname>call()</methodname>, une exception
                <classname>Zend_XmlRpc_Client_FaultException</classname> sera donc lancée si une
                erreur survient. Un objet <classname>Zend_XmlRpc_Response</classname> contenant
                l'erreur sera de même disponible via la méthode
                <methodname>getLastResponse()</methodname>.
            </para>

            <para>
                Lors de l'utilisation de la méthode <methodname>doRequest()</methodname>, aucune exception
                ne sera levée si une erreur XML-RPC survient. Simplement, l'objet
                <classname>Zend_XmlRpc_Response</classname> retourné contiendra l'erreur.
                Vérifiez-en l'état avec <methodname>isFault()</methodname>.
            </para>
        </sect3>
    </sect2>

    <sect2 id="zend.xmlrpc.client.introspection">
        <title>Introspection du serveur</title>

        <para>
            Certains serveurs XML-RPC supportent l'introspection de leurs méthodes au travers
            de l'espace de noms <code>system.</code> <classname>Zend_XmlRpc_Client</classname>
            fournit un support d'un tel procédé.
        </para>

        <para>
            Une instance de <classname>Zend_XmlRpc_Client_ServerIntrospection</classname> sera
            retournée si vous appelez la méthode <methodname>getIntrospector()</methodname> sur l'objet
            <classname>Zend_XmlRpcClient</classname>.
        </para>
    </sect2>

    <sect2 id="zend.xmlrpc.client.request-to-response">
        <title>De la requête à la réponse</title>

        <para>
            Dans les faits, la méthode <methodname>call()</methodname> de
            <classname>Zend_XmlRpc_Client</classname> fabrique un objet
            <classname>Zend_XmlRpc_Request</classname> et l'envoie à une méthode
            <methodname>doRequest()</methodname>, qui retourne un objet de réponse
            <classname>Zend_XmlRpc_Response</classname>.
        </para>

        <para>La méthode <methodname>doRequest()</methodname> est disponible directement si besoin :</para>

        <example id="zend.xmlrpc.client.request-to-response.example-1">
            <title>Effectuer une requête et récupérer une réponse manuellement</title>

            <programlisting language="php"><![CDATA[
$client = new Zend_XmlRpc_Client('http://framework.zend.com/xmlrpc');

$request = new Zend_XmlRpc_Request();
$request->setMethod('test.sayHello');
$request->setParams(array('foo', 'bar'));

$client->doRequest($request);

// $client->getLastRequest() retoure instanceof Zend_XmlRpc_Request
// $client->getLastResponse() retourne instanceof Zend_XmlRpc_Response
]]></programlisting>
        </example>

        <para>
            Lorsqu'une méthode XML-RPC est appelée, quel qu'en soit le moyen,
            (<methodname>call()</methodname>, <methodname>doRequest()</methodname> ou proxy serveur), le dernier objet de
            requête, et son homologue de réponse, seront toujours disponibles, au travers des appels
            à <methodname>getLastRequest()</methodname> et <methodname>getLastResponse()</methodname>.
        </para>
    </sect2>

    <sect2 id="zend.xmlrpc.client.http-client">
        <title>Client HTTP et tests</title>

        <para>
            Dans tous les exemples utilisés sur cette page, nous ne parlons jamais du client
            <acronym>HTTP</acronym>. Lorsque c'est nécessaire, une instance de <classname>Zend_Http_Client</classname>
            sera créée par défaut et injectée dans <classname>Zend_XmlRpc_Client</classname> de
            manière automatique.
        </para>

        <para>
            L'objet client <acronym>HTTP</acronym> peut être récupéré à tout moment grâce à la méthode
            <methodname>getHttpClient()</methodname>. <methodname>setHttpClient()</methodname> permet d'injecter un objet
            <classname>Zend_Http_Client</classname>.
        </para>

        <para>
            <methodname>setHttpClient()</methodname> est particulièrement utilisée pour les tests
            unitaires. Lorsque combinée avec <classname>Zend_Http_Client_Adapter_Test</classname>,
            les services Web peuvent être déguisés (émulés) pour les tests. Voyez les tests
            unitaires de <classname>Zend_XmlRpc_Client</classname> pour des exemples
            concrets.
        </para>
    </sect2>
</sect1>