mirror of
https://github.com/macintoshplus/doc-fr.git
synced 2026-03-29 04:12:21 +02:00
git-svn-id: https://svn.php.net/repository/phpdoc/fr/trunk@274693 c90b9560-bf6c-de11-be94-00142212c4b1
1203 lines
40 KiB
XML
1203 lines
40 KiB
XML
<?xml version="1.0" encoding="utf-8"?>
|
|
<!-- $Revision: 1.1 $ -->
|
|
<!-- EN-Revision: 1.2 Maintainer: dams Status: ready -->
|
|
<!-- Reviewed: no -->
|
|
|
|
<chapter xml:id="sca.examples" xmlns="http://docbook.org/ns/docbook" xmlns:xlink="http://www.w3.org/1999/xlink">
|
|
&reftitle.examples;
|
|
<para>
|
|
Les exemples de la section suivante illustrent différents aspects de
|
|
PHP pour SCA :
|
|
</para>
|
|
|
|
<!-- {{{ itemizedlist of examples -->
|
|
<itemizedlist>
|
|
<listitem>
|
|
<para>
|
|
Comment les annotations PHP peuvent être utilisées pour
|
|
définir des classes PHP comme des composants SCA et comment
|
|
les annotations sont utilisées pour définir des services.
|
|
</para>
|
|
</listitem>
|
|
<listitem>
|
|
<para>
|
|
Comment les composants SCA peuvent être exposés comme services Web.
|
|
</para>
|
|
</listitem>
|
|
<listitem>
|
|
<para>
|
|
Comment un composant SCA peut utiliser un service Web, qu'il soit
|
|
fournit par un autre composant SCA ou par un autre service qui n'a
|
|
pas de rapport avec SCA.
|
|
</para>
|
|
</listitem>
|
|
<listitem>
|
|
<para>
|
|
Comment un composant SCA peut appeler un autre composant SCA localement,
|
|
avec le même processus et la même pile d'appels.
|
|
</para>
|
|
</listitem>
|
|
<listitem>
|
|
<para>
|
|
Comment un script client qui n'est pas un composant SCA peut
|
|
utiliser la fonction <function>getService</function> pour obtenir
|
|
un proxy d'accès à un composant SCA.
|
|
</para>
|
|
</listitem>
|
|
<listitem>
|
|
<para>
|
|
Comment des structures de données telles que Adresses, ou Commandes
|
|
sont représentées comme des Service Data Objects (SDO), et gérées.
|
|
</para>
|
|
</listitem>
|
|
<listitem>
|
|
<para>
|
|
Comment des composants SCA sont déployés, et en particulier, comment
|
|
et quand des fichiers WSDL sont générés pour un service.
|
|
</para>
|
|
</listitem>
|
|
<listitem>
|
|
<para>
|
|
Comment les paramètres sont toujours passés par valeur (et non par
|
|
référence), entre les composants, même si les appels sont locaux.
|
|
Cela garantit que la sémantique de l'appel ne change pas, en fonction
|
|
de l'emplacement du composant.
|
|
</para>
|
|
</listitem>
|
|
<listitem>
|
|
<para>
|
|
Comment les paramètres de position d'un service sont supportés,
|
|
quand le WSDL sous-jacent est un document littéral, et qui ne supporte
|
|
que des paramètres nommés.
|
|
</para>
|
|
</listitem>
|
|
<listitem>
|
|
<para>
|
|
Comment les exceptions métier et d'exécution sont gérées.
|
|
</para>
|
|
</listitem>
|
|
|
|
</itemizedlist>
|
|
<!-- }}} -->
|
|
|
|
<!-- {{{ Section Structure -->
|
|
<section xml:id="SCA.examples.structure">
|
|
<title>La structure d'un composant de service</title>
|
|
<para>
|
|
Un composant de service est implémenté sous forme de classe.
|
|
Pour l'identifier, il doit contenir une annotation
|
|
<literal>@service</literal>. L'exécutable SCA
|
|
va utiliser le nom du fichier de script pour déterminer le nom
|
|
du composant, par convention. La classe et le fichier de script
|
|
doivent donc porter le même nom.
|
|
</para>
|
|
|
|
<para>
|
|
Les composants PHP SCA doivent toujours exposer un service, et il n'y a
|
|
aucun moyen pour un composant d'être appelé autrement que comme le
|
|
résultat d'un service Web, ou bien directement par un autre composant
|
|
ou un script. Pour cette raison, un composant PHP SCA valide va toujours
|
|
contenir une annotation <literal>@service</literal> et au moins une
|
|
méthode publique.
|
|
</para>
|
|
|
|
<para>
|
|
Chaque composant SCA requiert que le script SCA.php soit inclus. En plus
|
|
de contenir la définition de la classe SCA, ce script contient du code PHP
|
|
qui est exécuté à chaque appel, et qui est responsable du comportement
|
|
général du composant.
|
|
</para>
|
|
|
|
<caution>
|
|
|
|
<para>
|
|
Il est très important que les autres inclusions de votre script
|
|
interviennent avant celle du fichier SCA.php. S'il y a des inclusions
|
|
après SCA.php, ils ne seront pas pris en compte lorsque l'exécutable
|
|
SCA traitera votre classe.
|
|
</para>
|
|
</caution>
|
|
|
|
<para>
|
|
L'exemple ci-dessous montre la structure générale.
|
|
</para>
|
|
|
|
<para>
|
|
<example>
|
|
<title> Structure d'un composant SCA pour PHP</title>
|
|
|
|
<programlisting role="php">
|
|
<![CDATA[
|
|
<?php
|
|
|
|
// les inclusions
|
|
|
|
include "SCA/SCA.php";
|
|
|
|
/**
|
|
* @service
|
|
*/
|
|
|
|
class ConvertedStockQuote {
|
|
|
|
// variables, logique métier, inclusion d'au moins une méthode publique
|
|
|
|
}
|
|
?>
|
|
]]>
|
|
</programlisting>
|
|
</example>
|
|
</para>
|
|
</section>
|
|
<!-- }}} -->
|
|
|
|
<!-- {{{ Section Proxies -->
|
|
<section xml:id="SCA.examples.proxies">
|
|
<title> Obtention d'un proxy pour un autre composant de service </title>
|
|
<para>
|
|
Un composant SCA peut appeler le service proposé par un autre composant
|
|
SCA. Le service que fournit un composant est constitué de toutes ses
|
|
méthodes publiques. SCA pour PHP propose deux méthodes pour que les
|
|
composants s'appellent les uns les autres : soit localement (i.e., via
|
|
le même exécutable PHP, et avec la même pile), ou bien à distance, via
|
|
un service Web.
|
|
</para>
|
|
|
|
<para>
|
|
Afin qu'un composant en appelle un autre, le composant d'appel a besoin
|
|
d'un proxy jusqu'au composant appelé. Ce proxy est généralement fourni
|
|
sous forme de variable dans le composant appelant, même si les proxy
|
|
peuvent être obtenu via la fonction <function>SCA::getService()</function>.
|
|
Quand un composant est construit, les proxy sont constitués pour toute
|
|
variable d'instance qui fait référence à un autre composant, et ces proxy
|
|
sont injectés dans les variables. Les proxy sont toujours utilisé, que
|
|
le composant soit distant ou locale, afin de propose une interface
|
|
d'appel identique, quelque soit la localisation des composants.
|
|
Les proxy savent comment localiser un composant, et leur passer les
|
|
appels.
|
|
</para>
|
|
|
|
<para>
|
|
Les variables d'instance qui sont destinées à contenir les proxy sont
|
|
identifiées par deux annotations PHPDocumentor :
|
|
<literal>@reference</literal> et <literal>@binding</literal>.
|
|
Ces deux annotations sont rangées dans la section de documentation d'une
|
|
classe, tel que le montre le code ci-dessous.
|
|
</para>
|
|
|
|
<para>
|
|
L'annotation <literal>@reference</literal> pour une variable d'instance
|
|
indique que la variable doit être initialisée avec un proxy de composant.
|
|
</para>
|
|
|
|
<para>
|
|
L'annotation <literal>@binding</literal> prend deux formes :
|
|
<literal>@binding.php</literal> et <literal>@binding.soap</literal>.
|
|
Elles indiquent que le proxy est soit local, soit un service Web
|
|
distant, respectivement. Pour les deux <literal>@binding.php</literal>
|
|
et <literal>@binding.soap</literal>, l'annotation indique l'URI cible.
|
|
</para>
|
|
|
|
<para>
|
|
A ce moment, avec la méthode de spécification des dépendances via
|
|
les annotations, le seul moyen de modifier la cible d'une référence
|
|
est de modifier l'annotation dans le composant.
|
|
</para>
|
|
|
|
<para>
|
|
Dans notre exemple, la variable <varname>$exchange_rate</varname>
|
|
est initialisée avec un proxy vers le composant ExchangeRate,
|
|
à chaque fois qu'une instance de ConvertedStockQuote est construite.
|
|
</para>
|
|
|
|
<para>
|
|
<example>
|
|
<title> Obtention d'un proxy pour une classe PHP locale </title>
|
|
|
|
<programlisting role="php">
|
|
<![CDATA[
|
|
<?php
|
|
/**
|
|
* Le service de change à utiliser
|
|
*
|
|
* @reference
|
|
* @binding.php ../ExchangeRate/ExchangeRate.php
|
|
*/
|
|
public $exchange_rate;
|
|
?>
|
|
]]>
|
|
</programlisting>
|
|
</example>
|
|
</para>
|
|
|
|
<para>
|
|
Pour <literal>@binding.php</literal>, l'URI identifie la localisation
|
|
du script qui contient l'implémentation du composant. Le composant sera
|
|
appelé localement. Le service fournit est un jeu de méthodes publiques
|
|
dans le composant. L'URI doit être un simple chemin, absolu ou relatif.
|
|
Le composant sera chargé avec une fonction <function>include</function>
|
|
de PHP, après avoir vérifié que le composant n'est pas chargé, avec
|
|
<function>class_exists</function>. Si l'URI est relative, elle est
|
|
résolue relativement au composant qui contient l'annotation. Notez que
|
|
cela est différent du comportement habituel de PHP, où les scripts
|
|
cherchent les fichiers dans l'include_path. Ceci est nécessaire
|
|
pour fournir une indépendance de localisation dans les références.
|
|
</para>
|
|
|
|
<para>
|
|
Si ce service ExchangeRate était distant, et qu'il était appelé comme un
|
|
service Web, seule la ligne <literal>@binding</literal> aurait changé.
|
|
Au lieu de donner la localisation d'une classe PHP, il faudrait donner
|
|
la localisation d'un fichier WSDL, décrivant un service Web. Dans notre
|
|
exemple de composant, ceci est illustré par la seconde référence :
|
|
</para>
|
|
|
|
<para>
|
|
<example>
|
|
<title> Obtention d'un proxy pour un service Web </title>
|
|
<programlisting role="php">
|
|
<![CDATA[
|
|
<?php
|
|
/**
|
|
* Le service de cotation des actions à utiliser
|
|
*
|
|
* @reference
|
|
* @binding.soap ../StockQuote/StockQuote.wsdl
|
|
*/
|
|
public $stock_quote;
|
|
?>
|
|
]]>
|
|
</programlisting>
|
|
</example>
|
|
</para>
|
|
|
|
<para>
|
|
Le composant StockQuote sera appelé comme un service Web. Dans ce cas,
|
|
l'URI du WSDL peut être un simple chemin, ou utiliser un gestionnaire
|
|
de flux PHP, et commencer, par exemple, avec <varname>file://</varname>
|
|
ou <varname>http://</varname>. Dans l'événement où c'est un simple chemin
|
|
de fichiers, il peut être absolu ou relatif, et dans le cas d'un chemin
|
|
relatif, il sera résolu relativement au fichier qui contient l'annotation.
|
|
Notez que c'est le même comportement que pour
|
|
<literal>@binding.php</literal>, et que c'est différent
|
|
du comportement habituel de PHP.
|
|
</para>
|
|
|
|
</section>
|
|
<!-- }}} -->
|
|
|
|
<!-- {{{ Section Calling -->
|
|
<section xml:id="SCA.examples.calling">
|
|
<title> Appel d'un autre composant de service </title>
|
|
|
|
<para>
|
|
L'exemple ConvertedStockQuote fait aussi appel à des proxy pour
|
|
deux composants dont il dépend.
|
|
</para>
|
|
|
|
<para>
|
|
<example>
|
|
<title> Appels de services </title>
|
|
<programlisting role="php">
|
|
<![CDATA[
|
|
<?php
|
|
$quote = $this->stock_quote->getQuote($ticker);
|
|
$rate = $this->exchange_rate->getRate($currency);
|
|
?>
|
|
]]>
|
|
</programlisting>
|
|
</example>
|
|
</para>
|
|
|
|
<para>
|
|
L'appel du service StockQuote est un appel à un service local;
|
|
l'appel à ExchangeRate est un appel à un service distant. Notez que ces
|
|
deux appels se ressemblent beaucoup, même si l'un est local et l'autre
|
|
distant.
|
|
</para>
|
|
|
|
<para>
|
|
Les proxy ont été injecté pour s'assurer que les appels à ces
|
|
deux composants sont bien les mêmes, quelle que soit leur nature.
|
|
Par exemple, le proxy d'un service local fait une copie des arguments,
|
|
et ne fait que passer ces copies, afin de s'assurer que les appels
|
|
sont bien fait par valeur, comme cela serait le cas dans un service
|
|
Web. De même, le proxy d'un service Web prend les arguments dans une
|
|
liste à position, et s'assurent qu'ils sont correctement transmis
|
|
dans une requête SOAP, puis convertis de nouveau en variable, au retour.
|
|
</para>
|
|
|
|
<para>
|
|
Dans l'exemple ci-dessus, <varname>$ticker</varname> et
|
|
<varname>$currency</varname> sont clairement des valeurs PHP scalaires.
|
|
Les composants peuvent passer des types scalaires, &string;, &integer;,
|
|
&float; et &boolean;, mais les structures de données pour les appels de
|
|
services sont toujours passés comme Service Data Objects (SDOs).
|
|
Une section ultérieur montre comment un composant peut créer un SDO à passer
|
|
à un service local, ou distant. La documentation PHP montre comment
|
|
manipuler les <link linkend='ref.sdo'>objets SDO</link>.
|
|
</para>
|
|
|
|
</section>
|
|
<!-- }}} -->
|
|
|
|
<!-- {{{ Section nonscascript -->
|
|
<section xml:id="SCA.examples.nonSCAscript">
|
|
<title>Localisation et appel d'un service, depuis un script qui n'est
|
|
pas un composant SCA</title>
|
|
|
|
<para>
|
|
Les composants SCA peuvent obtenir des proxy d'autres composants ou
|
|
services qui ne sont pas annotés avec <literal>@reference</literal>,
|
|
mais ce n'est pas possible pour un script qui n'est pas lui-même un
|
|
composant. Un script client qui n'est pas un client doit utiliser la
|
|
fonction statique <function>SCA::getService</function> pour obtenir
|
|
un proxy au service, qu'il soit local ou distant. La méthode
|
|
<function>getService</function> prendre une URI comme argument.
|
|
Typiquement, ceci est la localisation d'un script PHP contenant un
|
|
composant, ou un fichier WSDL, et il est utilisé exactement comme
|
|
la cible d'une annotation <literal>@binding</literal>, décrite
|
|
dans la section précédente : c'est à dire, les URI relatives sont
|
|
décrites par rapport au fichier de l'annotation, et non pas
|
|
l'include_path de PHP.
|
|
</para>
|
|
|
|
<para>
|
|
Par exemple, un script qui a besoin d'obtenir des proxy pour
|
|
ExchangeRate et StockQuote mais n'est pas un composant peut utiliser
|
|
la fonction <function>SCA::getService</function> comme ceci :
|
|
</para>
|
|
|
|
<para>
|
|
<example>
|
|
<title> Obtention d'un proxy avec getService </title>
|
|
<programlisting role="php">
|
|
<![CDATA[
|
|
<?php
|
|
$exchange_rate = SCA::getService('../ExchangeRate/ExchangeRate.php');
|
|
$stock_quote = SCA::getService('../StockQuote/StockQuote.wsdl');
|
|
?>
|
|
]]>
|
|
</programlisting>
|
|
</example>
|
|
</para>
|
|
|
|
<para>
|
|
Les méthodes d'un service peuvent être appelé sur un proxy ainsi
|
|
retourné, tout comme elles peuvent l'être sur un composant.
|
|
</para>
|
|
<para>
|
|
<example>
|
|
<title> Faire des appels sur un proxy </title>
|
|
<programlisting role="php">
|
|
<![CDATA[
|
|
<?php
|
|
$quote = $stock_quote->getQuote($ticker);
|
|
$rate = $exchange_rate->getRate($currency);
|
|
?>
|
|
]]>
|
|
</programlisting>
|
|
</example>
|
|
</para>
|
|
|
|
</section>
|
|
<!-- }}} -->
|
|
|
|
<!-- {{{ Section Exposing Webservice Componenent -->
|
|
<section xml:id="sca.examples.exposing-webservice">
|
|
<title> Exposer un composant comme un service Web </title>
|
|
|
|
<para>
|
|
SCA pour PHP peut générer des fichiers WSDL à partir des annotations
|
|
dans un composant, ce qui fait qu'il peut être facilement déployé et
|
|
exposé comme un service Web. Pour fournir à SCA les informations dont
|
|
il a besoin pour produire le WSDL, il est nécessaire d'ajouter l'annotation
|
|
<literal>@binding.soap</literal> dans l'annotation
|
|
<literal>@service</literal> et de spécifier les paramètres
|
|
et les valeurs de retour avec les annotations <literal>@param</literal> et
|
|
<literal>@return.</literal> Ces annotations seront lues quand
|
|
le fichier WSDL sera produit, et l'ordre et les types des
|
|
paramètres va déterminer le contenu de la section
|
|
<schema> du fichier WSDL.
|
|
</para>
|
|
|
|
<para>
|
|
SCA pour PHP produit toujours des fichiers WSDL pour les composants
|
|
qui exposent des services Web. Notez que cela n'empêche pas les
|
|
composants de consommer des services Web qui ne sont pas des
|
|
composants SCA, et qui sont documentés avec des fichiers WDSL qui
|
|
ne sont pas dans le même format.
|
|
</para>
|
|
|
|
<para>
|
|
Les types scalaires qui peuvent être utilisé dans les annotations
|
|
<literal>@param</literal> sont les quatres types scalaires communs de PHP :
|
|
&boolean;, &integer;, &float; et &string;. Ils sont simplement transformés
|
|
en un équivalent au format XML. L'exemple ci-dessous, qui est une
|
|
implémentation triviale du service StockQuote avec des appels à
|
|
ConvertedStockQuote illustre l'utilisation de &string; et &float;.
|
|
</para>
|
|
|
|
<para>
|
|
<example>
|
|
<title> Service StockQuote </title>
|
|
<programlisting role="php">
|
|
<![CDATA[
|
|
<?php
|
|
|
|
include "SCA/SCA.php";
|
|
|
|
/**
|
|
* Implémentation d'un service distant StockQuote Web.
|
|
*
|
|
* @service
|
|
* @binding.soap
|
|
*
|
|
*/
|
|
class StockQuote {
|
|
|
|
/**
|
|
* Lit une cotation pour un symbole
|
|
*
|
|
* @param string $ticker Le symbole de l'action
|
|
* @return float La cotation de l'action
|
|
*/
|
|
function getQuote($ticker) {
|
|
return 80.9;
|
|
}
|
|
}
|
|
?>
|
|
]]>
|
|
</programlisting>
|
|
</example>
|
|
</para>
|
|
|
|
<para>
|
|
Un fichier WSDL, semblable au document suivant (mais avec une localisation
|
|
de service autre que <literal>'localhost'</literal>, probablement)
|
|
sera généré pour le service :
|
|
</para>
|
|
|
|
<para>
|
|
<example>
|
|
<title> Generated WSDL </title>
|
|
<programlisting role="xml">
|
|
<![CDATA[
|
|
<?xml version="1.0" encoding="UTF-8"?>
|
|
<definitions xmlns="http://schemas.xmlsoap.org/wsdl/" xsi:type="tDefinitions"
|
|
xmlns:tns2="http://StockQuote" xmlns:tns="http://schemas.xmlsoap.org/wsdl/"
|
|
xmlns:tns3="http://schemas.xmlsoap.org/wsdl/soap/"
|
|
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" targetNamespace="http://StockQuote">
|
|
<types>
|
|
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema"
|
|
targetNamespace="http://StockQuote">
|
|
<xs:element name="getQuote">
|
|
<xs:complexType>
|
|
<xs:sequence>
|
|
<xs:element name="ticker" type="xs:string"/>
|
|
</xs:sequence>
|
|
</xs:complexType>
|
|
</xs:element>
|
|
<xs:element name="getQuoteResponse">
|
|
<xs:complexType>
|
|
<xs:sequence>
|
|
<xs:element name="getQuoteReturn" type="xs:float"/>
|
|
</xs:sequence>
|
|
</xs:complexType>
|
|
</xs:element>
|
|
</xs:schema>
|
|
</types>
|
|
|
|
<message name="getQuoteRequest">
|
|
<part name="getQuoteRequest" element="tns2:getQuote"/>
|
|
</message>
|
|
<message name="getQuoteResponse">
|
|
<part name="return" element="tns2:getQuoteResponse"/>
|
|
</message>
|
|
<portType name="StockQuotePortType">
|
|
<operation name="getQuote">
|
|
<input message="tns2:getQuoteRequest"/>
|
|
<output message="tns2:getQuoteResponse"/>
|
|
</operation>
|
|
</portType>
|
|
<binding name="StockQuoteBinding" type="tns2:StockQuotePortType">
|
|
<operation name="getQuote">
|
|
<input>
|
|
<tns3:body xsi:type="tBody" use="literal"/>
|
|
</input>
|
|
<output>
|
|
<tns3:body xsi:type="tBody" use="literal"/>
|
|
</output>
|
|
<tns3:operation xsi:type="tOperation" soapAction=""/>
|
|
</operation>
|
|
<tns3:binding xsi:type="tBinding" transport="http://schemas.xmlsoap.org/soap/http" style="document"/>
|
|
</binding>
|
|
<service name="StockQuoteService">
|
|
<port name="StockQuotePort" binding="tns2:StockQuoteBinding">
|
|
<tns3:address xsi:type="tAddress" location="http://localhost/StockQuote/StockQuote.php"/>
|
|
</port>
|
|
</service>
|
|
</definitions>
|
|
|
|
<!-- this line identifies this file as WSDL generated by SCA for PHP. Do not remove -->
|
|
]]>
|
|
|
|
</programlisting>
|
|
</example>
|
|
</para>
|
|
|
|
</section>
|
|
<!-- }}} -->
|
|
|
|
<!-- {{{ Section Deploy -->
|
|
<section xml:id="SCA.examples.deploy">
|
|
<title> Déploiement d'un composant SCA </title>
|
|
<para>
|
|
Il n'y pas d'instruction particulière pour déployer un composant PHP SCA.
|
|
Il est suffisant de placer le script PHP dans le serveur Web, comme un
|
|
autre script PHP. C'est l'exécutable <function>SCA::initComponent</function>
|
|
dans le composant qui va l'exécuter correctement, quelque soit la méthode
|
|
utilisée, et qui est responsable de proposer la réponse adaptée à la
|
|
situation, que ce soit un appel local, Web service ou une requête WDSL.
|
|
</para>
|
|
|
|
</section>
|
|
<!-- }}} -->
|
|
|
|
<!-- {{{ Section Obtaining WSDL -->
|
|
<section xml:id="SCA.examples.obtaining-wsdl">
|
|
<title>Obtenir le WSDL d'un composant SCA qui offre un service Web</title>
|
|
|
|
<para>
|
|
Les composant SCA qui exposent un service Web (i.e. qui ont une annotation
|
|
<literal>@binding.soap</literal>) vont retourner une définition
|
|
WSDL en réponse à une requête HTTP avec un paramètre GET "wsdl".
|
|
L'approche habituelle pour cela est d'ajouter
|
|
"?wsdl" à la fin de l'URL. L'exemple ci-dessous utilise la fonction
|
|
<function>file_get_contents</function> pour obtenir un fichier WSDL d'un
|
|
service Web, et l'écrit dans un fichier temporaire, avant de demander un
|
|
proxy. Vous pouvez évidemment obtenir le fichier WDSL dans votre navigateur,
|
|
ou par d'autres moyens, et le sauver vous-mêmes.
|
|
</para>
|
|
|
|
<para>
|
|
<example>
|
|
<title> WSDL généré </title>
|
|
<programlisting role="xml">
|
|
<![CDATA[
|
|
<?php
|
|
$wsdl = file_get_contents('http://www.example.com/Services/Example.php?wsdl');
|
|
file_put_contents("service.wsdl",$wsdl); //write the wsdl to a file
|
|
$service = SCA::getService('service.wsdl');
|
|
?>
|
|
]]>
|
|
</programlisting>
|
|
</example>
|
|
</para>
|
|
|
|
<para>
|
|
Note : si le fichier WSDL requiert des importations de xsd, elles
|
|
devront être lus séparément.
|
|
</para>
|
|
|
|
</section>
|
|
<!-- }}} -->
|
|
|
|
<!-- {{{ Section Understanding WSDL -->
|
|
<section xml:id="SCA.examples.understanding-wsdl">
|
|
<title> Comprendre la génération du fichier WSDL </title>
|
|
<para>
|
|
SCA pour PHP génère un fichier WSDL pour les composants qui contiennent
|
|
une annotation <literal>@binding.soap</literal>,
|
|
après l'annotation <literal>@service.</literal> Pour générer
|
|
le service, SCA analyse la classe, et les annotations
|
|
<literal>@param</literal> et <literal>@return</literal>
|
|
pour chaque méthode publique, ainsi que les annotations de
|
|
<literal>@types</literal> dans le composant. Les
|
|
informations des annotations <literal>@param</literal> et
|
|
<literal>@return</literal> sont utilisées pour construire
|
|
la section <types> du fichier WDSL.
|
|
Toutes les annotations <literal>@types</literal> qui
|
|
spécifient un schéma distinct engendreront
|
|
un élément <import> pour ce schéma dans le WSDL.
|
|
</para>
|
|
|
|
<section xml:id="SCA.examples.understanding-wsdl.location"><!-- {{{ -->
|
|
<title> Attribut de localisation pour l'élément <service></title>
|
|
<para>
|
|
A la fin du fichier WSDL, l'élément <service> utilise un attribut
|
|
de localisation 'location', pour identifier l'URL du service.
|
|
Par exemple :
|
|
</para>
|
|
|
|
<para>
|
|
<example>
|
|
<title> Attribut location </title>
|
|
<programlisting role="xml">
|
|
<![CDATA[
|
|
<service name="ConvertedStockQuote"
|
|
...
|
|
location="http://localhost/ConvertedStockQuote/ConvertedStockQuote.php"/>
|
|
]]>
|
|
</programlisting>
|
|
</example>
|
|
</para>
|
|
<para>
|
|
Notez que cette localisation est relative à la racine du document
|
|
du serveur Web, et ne peut pas être connue à l'avance. Elle ne
|
|
peut être résolue que lorsque le composant est mis en place dans
|
|
un serveur Web en fonctionnement, lorsque le nom d'hôte et le port
|
|
peuvent être connus et placés dans le fichier WDSL. Des informations
|
|
de l'URL appelante sont aussi utilisés, ce qui fait que si le WSDL
|
|
est produit en réponse à la requête
|
|
http://www.example.com:1111/ConvertedStockQuote/ConvertedStockQuote.php?wsdl,
|
|
une localisation de
|
|
http://www.example.com:1111/ConvertedStockQuote/ConvertedStockQuote.php
|
|
sera insérée dans l'attribut.
|
|
</para>
|
|
</section>
|
|
<!-- }}} -->
|
|
|
|
<section xml:id="SCA.examples.understanding-wsdl.positional-parameters"> <!-- {{{ -->
|
|
<title>Inclusion des documents dans le WSDL et paramètres positionnés</title>
|
|
<para>
|
|
SCA pour PHP produit des fichiers WSDL en incluant les documents et
|
|
littéraux. Ce style encadre les paramètres et les valeurs de retour d'une
|
|
méthode dans un 'gestionnaire' qui est nommé à partir de la méthode.
|
|
L'élément <types> au début du fichier WSDL définit chacun de ces
|
|
gestionnaires. Si nous étudions la méthode <function>getQuote</function>
|
|
de notre exemple ConvertedStockQuote :
|
|
</para>
|
|
|
|
<para>
|
|
<example>
|
|
<title> une méthode avec deux arguments </title>
|
|
<programlisting role="php">
|
|
<![CDATA[
|
|
<?php
|
|
/**
|
|
* Lit une cotation pour une action à partir de son symbole et dans une devise
|
|
*
|
|
* @param string $ticker Le symbole
|
|
* @param string $currency La devise cible
|
|
* @return float La valeur de l'action dans la devise demandée
|
|
*/
|
|
function getQuote($ticker, $currency)
|
|
{
|
|
$quote = $this->stock_quote->getQuote($ticker);
|
|
$rate = $this->exchange_rate->getRate($currency);
|
|
return $rate * $quote;
|
|
}
|
|
?>
|
|
]]>
|
|
|
|
</programlisting>
|
|
</example>
|
|
</para>
|
|
|
|
<para>
|
|
Pour définir cette méthode, le fichier WSDL va donner des noms
|
|
à la méthode et aux paramètres, puis donner au schéma XML le type
|
|
des paramètres. La section de type du fichier WSDL produit ressemble
|
|
à ceci :
|
|
</para>
|
|
|
|
<para>
|
|
<example>
|
|
<title> Section types avec paramètres nommés </title>
|
|
<programlisting role="xml">
|
|
<![CDATA[
|
|
<types>
|
|
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema"
|
|
targetNamespace="http://ConvertedStockQuote">
|
|
<xs:element name="getQuote">
|
|
<xs:complexType>
|
|
<xs:sequence>
|
|
<xs:element name="ticker" type="xs:string"/>
|
|
<xs:element name="currency" type="xs:string"/>
|
|
</xs:sequence>
|
|
</xs:complexType>
|
|
</xs:element>
|
|
<xs:element name="getQuoteResponse">
|
|
<xs:complexType>
|
|
<xs:sequence>
|
|
<xs:element name="getQuoteReturn" type="xs:float"/>
|
|
</xs:sequence>
|
|
</xs:complexType>
|
|
</xs:element>
|
|
</xs:schema>
|
|
</types>
|
|
]]>
|
|
</programlisting>
|
|
</example>
|
|
</para>
|
|
|
|
<para>
|
|
L'exécutable SCA gère la conversion de la liste des paramètres
|
|
positionnés en une requête SOAP, puis de nouveau en liste de
|
|
paramètres, au retour de la requête. Pour comprendre où cela est
|
|
important, vous pouvez étudier comment PHP qui utiliserait une autre
|
|
interface pour faire un appel SOAP devrait construire la liste de
|
|
paramètres. Un script PHP utilisant la classe
|
|
<classname>SoapClient</classname>, a besoin de passer
|
|
les paramètres "ticker" et "currency", peut-être sous forme
|
|
de tableau associatif. Pour cela, il faudrait que les appels à des
|
|
services Web ou des fonctions locales se fassent avec des interfaces
|
|
de programmation différente, et ce n'est pas voulu.
|
|
</para>
|
|
|
|
<para>
|
|
Quand SCA génère un fichier WSDL pour un composant SCA, il inclut des
|
|
commentaires dans le WDSL qui indique le ce composant est un composant
|
|
SCA. Dans ce cas, quand un composant SCA est appelé par un autre composant
|
|
via un service Web, l'exécutable SCA prend la liste de paramètres
|
|
et assigne les valeurs une à une dans le message SOAP. Par exemple,
|
|
un appelle à la méthode <function>getQuote</function> définie ci-dessus
|
|
qui passe les valeurs 'IBM' et 'USD' ressemble à ceci :
|
|
</para>
|
|
|
|
<para>
|
|
<programlisting role="php">
|
|
<![CDATA[
|
|
$quote = $remote_service->getQuote('IBM','USD');
|
|
]]>
|
|
</programlisting>
|
|
</para>
|
|
|
|
<para>
|
|
Elle produira un message SOAP qui contient ce code XML :
|
|
</para>
|
|
|
|
<para>
|
|
<programlisting role="php">
|
|
<![CDATA[
|
|
<getQuote>
|
|
<ticker>IBM</ticker>
|
|
<currency>USD</currency>
|
|
</getQuote>
|
|
]]>
|
|
</programlisting>
|
|
</para>
|
|
|
|
<para>
|
|
Dans le composant de réception, l'exécutable SCA prend les paramètres
|
|
dans le même ordre, et forme une liste de paramètres positionnés,
|
|
en reformant la liste des arguments
|
|
('IBM','USD').
|
|
</para>
|
|
|
|
<caution>
|
|
|
|
<para>
|
|
Dans les deux cas, l'exécutable SCA s'appuie sur l'ordre dans lequel
|
|
les arguments sont placés dans le message SOAP : cet ordre doit
|
|
correspondre à l'ordre d'appel des paramètres dans la fonction.
|
|
Cet ordre est finalement défini par l'ordre des annotations
|
|
<literal>@param</literal> : il déterminent l'ordre dans lequel
|
|
les paramètres apparaissent dans le fichier WSDL, et dans le
|
|
message SOAP. Par conséquent, il est essentiel que cet ordre
|
|
d'annotations <literal>@param</literal> correspondent aux paramètres
|
|
de la méthode publique.
|
|
</para>
|
|
</caution>
|
|
|
|
</section>
|
|
<!-- }}} -->
|
|
|
|
</section>
|
|
<!-- }}} -->
|
|
|
|
<section xml:id="SCA.examples.structures"><!-- {{{ -->
|
|
<title> Travailler avec des structures de données </title>
|
|
<para>
|
|
Les composants SCA peuvent échanger les quatre types scalaires
|
|
de PHP : &boolean;, &integer;, &float; et &string;, mais aussi passer
|
|
des structures de données : SCA utilise les Service Data Objects (SDOs).
|
|
Les SDO sont décrits en détails dans
|
|
<link linkend='ref.sdo'>les pages SDO</link>
|
|
de ce manuel. Les lecteurs habitués aux SDO sauront qu'ils sont
|
|
adaptés à la représentation de toutes sortes de données structurées
|
|
et semi-structurées, qui sont souvent modélisées en CML, et qui se
|
|
linéarises naturellement pour passer entre composants distants.
|
|
Les SDO sont actuellement le seul moyen pour échanger des structures
|
|
de données. Il n'est pas possible d'échanger des objets PHP ou des
|
|
tableaux.
|
|
</para>
|
|
|
|
<para>
|
|
L'exécutable SCA s'assure toujours que les données sont passées par valeur,
|
|
même dans les appels locaux. Pour cela, SCA copie les SDO dans la
|
|
liste de paramètre avant de les passer, comme il le fait pour les valeurs
|
|
scalaires.
|
|
</para>
|
|
|
|
<section xml:id="sca.examples.structures.defined"><!-- {{{ -->
|
|
<title> Comment les structures de données sont définies par SCA </title>
|
|
<para>
|
|
Actuellement, le seul mécanisme pour spécifier la localisation d'une
|
|
définition de structure de données est de spécifier un type dans un
|
|
schéma XML. Cependant, à l'avenir, il est peut être possible de
|
|
définir des types d'une autre manière, comme en se basant sur des
|
|
classes PHP ou des interfaces, ou encore des tableaux associatifs.
|
|
</para>
|
|
|
|
<para>
|
|
Pour illustrer l'utilisation de SDO, voici un nouveau composant.
|
|
Le service PortfolioMangement retourne un SDO qui représente un
|
|
porte-feuille d'actions, pour un client donné.
|
|
</para>
|
|
|
|
<para>
|
|
<example>
|
|
<title>Un composant SCA qui utilise des SDO</title>
|
|
<programlisting role="php">
|
|
<![CDATA[
|
|
<?php
|
|
|
|
include "SCA/SCA.php";
|
|
|
|
/**
|
|
* Gestion de porte-feuille d'un client
|
|
*
|
|
* @service
|
|
* @binding.soap
|
|
*
|
|
* @types http://www.example.org/Portfolio PortfolioTypes.xsd
|
|
*
|
|
*/
|
|
class PortfolioManagement {
|
|
|
|
/**
|
|
* Lit le porte-feuille d'un client donné.
|
|
*
|
|
* @param integer $customer_id L'identifiant du client
|
|
* @return Portfolio http://www.example.org/Portfolio Le porte-feuille d'actions (symboles et quantités)
|
|
*/
|
|
function getPortfolio($customer_id) {
|
|
// Supposons que nous l'avons lu dans la base de données
|
|
$portfolio = SCA::createDataObject('http://www.example.org/Portfolio', 'Portfolio');
|
|
$holding = $portfolio->createDataObject('holding');
|
|
$holding->ticker = 'AAPL';
|
|
$holding->number = 100.5;
|
|
$holding = $portfolio->createDataObject('holding');
|
|
$holding->ticker = 'INTL';
|
|
$holding->number = 100.5;
|
|
$holding = $portfolio->createDataObject('holding');
|
|
$holding->ticker = 'IBM';
|
|
$holding->number = 100.5;
|
|
return $portfolio;
|
|
}
|
|
|
|
}
|
|
?>
|
|
]]>
|
|
</programlisting>
|
|
</example>
|
|
</para>
|
|
|
|
<para>
|
|
L'annotation <literal>@types</literal> :
|
|
</para>
|
|
|
|
<para>
|
|
<programlisting role="php">
|
|
<![CDATA[
|
|
<?php
|
|
@types http://www.example.org/Portfolio PortfolioTypes.xsd
|
|
?>
|
|
]]>
|
|
</programlisting>
|
|
</para>
|
|
|
|
<para>
|
|
elle indique que les types sont de l'espace de noms
|
|
http://www.example.org/Portfolio seront disponibles dans le schéma
|
|
PortfolioTypes.xsd. Le fichier WSDL produit va reproduire ces
|
|
informations avec une commande d'importation comme ceci :
|
|
</para>
|
|
|
|
<para>
|
|
<programlisting role="xml">
|
|
<![CDATA[
|
|
<xs:import schemaLocation="PortfolioTypes.xsd"
|
|
namespace="http://www.example.org/Portfolio"/>
|
|
]]>
|
|
</programlisting>
|
|
</para>
|
|
|
|
<para>
|
|
Ce qui fait que l'URI, absolue ou relative, doit pouvoir être
|
|
résolue lors de l'inclusion dans l'attribut schemaLocation.
|
|
</para>
|
|
|
|
</section>
|
|
<!-- }}} -->
|
|
|
|
<section xml:id="sca.examples.structures.creating"><!-- {{{ -->
|
|
<title>Création de SDO </title>
|
|
<para>
|
|
Les lecteurs familiers avec la notion de SDO savent qu'ils doivent
|
|
toujours être créés en conformité avec une description de la
|
|
structure autorisée (parfois aussi appelé schéma, ou modèle). Et que
|
|
au lieu de créer directement le SDO en utilisant l'opérateur
|
|
'new', une certaine forme d'usine d'objet doit être utilisée,
|
|
mais que, parfois, et notamment pour obtenir le premier objet,
|
|
il faut autre chose qu'une usine.
|
|
</para>
|
|
|
|
<para>
|
|
En SCA, soit c'est la classe d'exécution ou les proxy, qu'ils soient
|
|
locaux ou distants, qui servent d'usine à SDO. Les critères de choix entre
|
|
les deux sont décrits dans les prochaines sections.
|
|
</para>
|
|
<para>
|
|
Nous allons passer à un autre exemple pour illustrer la création de SDO,
|
|
afin de la passer à un service et pour en recueillir un.
|
|
</para>
|
|
</section>
|
|
<!-- }}} -->
|
|
|
|
<section xml:id="sca.examples.structures.services"><!-- {{{ -->
|
|
<title>Création de SDO à passer à un service</title>
|
|
<para>
|
|
Un utilisateur de service qui requiert une structure de données
|
|
doit utiliser un proxy vers ce service pour avoir une usine à
|
|
données qui produira les SDO désirés. Par exemple, supposons
|
|
qu'un composant utilise un proxy pour un
|
|
service vers un carnet d'adresse AddressBook local.
|
|
</para>
|
|
|
|
<para>
|
|
<programlisting role="php">
|
|
<![CDATA[
|
|
<?php
|
|
/**
|
|
* @reference
|
|
* @binding.local AddressBook.php
|
|
*/
|
|
$address_book;
|
|
?>
|
|
]]>
|
|
</programlisting>
|
|
</para>
|
|
<para>
|
|
Le composant AddressBook qui doit être utilisé est défini comme ceci :
|
|
</para>
|
|
<para>
|
|
<programlisting role="php">
|
|
<![CDATA[
|
|
<?php
|
|
/**
|
|
* @service
|
|
* @binding.soap
|
|
* @types http://addressbook ../AddressBook/AddressBook.xsd
|
|
*/
|
|
class AddressBook {
|
|
|
|
/**
|
|
* @param personType $person http://addressbook (un objet de personne)
|
|
* @return addressType http://addressbook (un objet d'adress pour l'objet de personne)
|
|
*/
|
|
function lookupAddress($person) {
|
|
...
|
|
}
|
|
}
|
|
?>
|
|
]]>
|
|
</programlisting>
|
|
</para>
|
|
|
|
<para>
|
|
Le composant AddressBook fournit une méthode de service appelée
|
|
<function>lookupAddress</function> qui utilise les types du schéma
|
|
http://addressbook. La méthode lookupAddress prend une structure
|
|
personType et retourne un objet addressType. Les deux types sont
|
|
définis dans le schéma addressbook.xsd.
|
|
</para>
|
|
|
|
<para>
|
|
Une fois que le composant qui veut utiliser le composant AddressBook
|
|
a été construit, de manière à ce que la variable d'instance
|
|
<varname>$address_book</varname> contiennent un proxy vers le service,
|
|
le composant appelant peut utiliser ce proxy dans
|
|
<varname>$address_book</varname> pour créer un SDO de personne,
|
|
comme ceci :
|
|
</para>
|
|
|
|
<para>
|
|
<programlisting role="php">
|
|
<![CDATA[
|
|
<?php
|
|
$william_shakespeare = $address_book->createDataObject('http://addressbook','personType');
|
|
$william_shakespeare ->name = "William Shakespeare";
|
|
$address = $address_book->lookupAddress($william_shakespeare);
|
|
?>
|
|
]]>
|
|
</programlisting>
|
|
</para>
|
|
|
|
<para>
|
|
Notez que l'utilisation du proxy pour produire un SDO n'est pas limitée
|
|
aux seuls composants SCA. Si un service est appelé depuis un script
|
|
PHP ordinaire, et que le proxy a été obtenu via la méthode
|
|
<function>getService</function> alors la même approche est utilisée.
|
|
</para>
|
|
|
|
<para>
|
|
<programlisting role="php">
|
|
<![CDATA[
|
|
<?php
|
|
$address_book = SCA::getService('AddressBook.php');
|
|
$william_shakespeare = $address_book->createDataObject('http://addressbook','personType');
|
|
?>
|
|
]]>
|
|
|
|
</programlisting>
|
|
</para>
|
|
|
|
</section>
|
|
<!-- }}} -->
|
|
|
|
<section xml:id="sca.examples.structures.services.returning"><!-- {{{ -->
|
|
<title> Création d'un SDO à retourner d'un composant </title>
|
|
<para>
|
|
Un composant qui a besoin de créer un objet de données à retourner
|
|
à son appelant ne dispose pas d'un proxy à utiliser comme usine à données,
|
|
et dans ce cas, il utilise la méthode statique
|
|
<function>createDataObject</function> du fichier
|
|
<filename>SCA.php</filename>. Ainsi, si le composant AddressBook
|
|
décrit ci-dessus a besoin de créer un objet de type
|
|
<classname>addressType</classname> dans l'espace de noms
|
|
http://addressbook, il peut le faire comme ceci :
|
|
</para>
|
|
|
|
<para>
|
|
<programlisting role="php">
|
|
<![CDATA[
|
|
<?php
|
|
$address = SCA::createDataObject('http://addressbook','addressType');
|
|
?>
|
|
]]>
|
|
|
|
</programlisting>
|
|
</para>
|
|
</section>
|
|
<!-- }}} -->
|
|
</section>
|
|
<!-- }}} -->
|
|
|
|
<section xml:id="SCA.examples.errorhandling"><!-- {{{ -->
|
|
<title> Gestion d'erreurs </title>
|
|
<para>
|
|
Cette section décrit comment sont gérées les erreurs.
|
|
Il y a deux types d'erreur :
|
|
</para>
|
|
<itemizedlist>
|
|
<listitem>
|
|
<para>
|
|
Les exceptions de l'exécutable SCA sont celles qui signalent
|
|
des problèmes dans la gestion de l'exécution des composants,
|
|
et dans l'interaction avec les services. Cela peut survenir à cause
|
|
de problèmes de configuration réseau.
|
|
</para>
|
|
</listitem>
|
|
|
|
<listitem>
|
|
<para>
|
|
Les exceptions métier sont celles qui sont définies par le programmeur.
|
|
Elles étendent la classe PHP Exception, et sont émises puis
|
|
interceptées explicitement comme partie intégrante de la logique métier.
|
|
</para>
|
|
</listitem>
|
|
|
|
</itemizedlist>
|
|
|
|
<section xml:id="sca.examples.errorhandling.runtime"><!-- {{{ -->
|
|
<title> Gestion des erreurs d'exécution </title>
|
|
<para>
|
|
Il y a deux types d'erreur SCA :
|
|
</para>
|
|
|
|
<itemizedlist>
|
|
<listitem>
|
|
<para>
|
|
<classname>SCA_RuntimeException</classname> :
|
|
elle signale un problème dans ou survenant
|
|
dans l'exécutable SCA. Elle peut être émise pour diverses raisons,
|
|
et certaines d'entre elles peuvent être indépendantes de la connexion
|
|
à un service local ou distant : une erreur dans l'une des annotations
|
|
un fichier WSDL ou PHP manquant, etc. Dans le cas des services Web, une
|
|
exception <classname>SCA_RuntimeException</classname> peut aussi
|
|
être émise si une erreur <classname>SoapFault</classname> a été
|
|
émise par un service Web et que le code d'erreur
|
|
<classname>SoapFault</classname> indique qu'une
|
|
nouvelle tentative a peu de chances d'aboutir.
|
|
</para>
|
|
</listitem>
|
|
|
|
<listitem>
|
|
<para>
|
|
<classname>SCA_ServiceUnavailableException</classname> :
|
|
cette classe est une sous-classe de
|
|
<classname>SCA_RuntimeException</classname>,
|
|
et signale un problème lors de la connexion ou de l'utilisation
|
|
d'un service distant, mais une erreur qui peut être dépassée si
|
|
une nouvelle tentative est faite. Dans le cas des services Web,
|
|
cette exception est émise si une erreur <classname>SoapFault</classname>
|
|
est reçue avec un code qui indique qu'une nouvelle tentative
|
|
pourrait être couronnée de succès.
|
|
</para>
|
|
</listitem>
|
|
|
|
</itemizedlist>
|
|
</section>
|
|
<!-- }}} -->
|
|
<section xml:id="sca.examples.errorhandlilng.business"> <!-- {{{ -->
|
|
<title> Gestion des exceptions métier </title>
|
|
<para>
|
|
Les exceptions métier peuvent être définies et émise par un composant
|
|
de manière classique, indépendamment du fait que le service a été
|
|
appelé localement ou à distance. L'exécutable SCA n'intercepte pas
|
|
les exceptions locales, ce qui fait qu'elles seront acheminées au composant
|
|
appelant de manière classique. Si un composant a été appelé comme
|
|
service Web, l'exécutable SCA va les intercepter, et s'assurera qu'elles
|
|
sont transmises au composant appelant, puis émise à nouveau.
|
|
En supposant que le script appelant a une définition de
|
|
l'exception métier (c'est à dire qu'il est capable d'inclure
|
|
un fichier contenant la définition PHP de l'exception),
|
|
la nouvelle exception sera émise avec les mêmes détails que l'originale,
|
|
de qui fait que les informations de <function>getLine</function> et
|
|
<function>getFile</function> contiendront les indications d'origine de
|
|
l'erreur dans les fichiers du service appelé. Cette exception sera passée
|
|
via le champ détail de la classe <classname>SoapFault</classname>.
|
|
</para>
|
|
</section>
|
|
<!-- }}} -->
|
|
|
|
</section>
|
|
<!-- }}} -->
|
|
|
|
</chapter>
|
|
|
|
<!-- Keep this comment at the end of the file
|
|
Local variables:
|
|
mode: sgml
|
|
sgml-omittag:t
|
|
sgml-shorttag:t
|
|
sgml-minimize-attributes:nil
|
|
sgml-always-quote-attributes:t
|
|
sgml-indent-step:1
|
|
sgml-indent-data:t
|
|
indent-tabs-mode:nil
|
|
sgml-parent-document:nil
|
|
sgml-default-dtd-file:"../../../manual.ced"
|
|
sgml-exposed-tags:nil
|
|
sgml-local-catalogs:nil
|
|
sgml-local-ecat-files:nil
|
|
End:
|
|
vim600: syn=xml fen fdm=syntax fdl=2 si
|
|
vim: et tw=78 syn=sgml
|
|
vi: ts=1 sw=1
|
|
-->
|
|
|