Retornar arrays con NuSOAP

Marzo 22, 2009

Christian escribió un artículo hace unos días sobre la manera de usar NuSOAP para crear un servicio web. El artículo describe en detalles el procedimiento. No obstante, probar de retornar un arreglo es un trabajo bastante complejo en NuSOAP, o por lo menos esta mal documentado. Una excelente referencia (en Inglés) sobre el tema se puede encontrar en el blog de Lyingonthecovers, pero el artículo faltaba (he puesto un comentario ahí) una noción importante que es que NuSOAP debe tener un ‘return’ => ‘xsd:Array’ en su método register() para retornar el formato correcto.

En este artículo, revisaremos como retornar arreglos.

Retornar arreglos simples

Primero, imaginamos, como en el artículo en Inglés de Lyingonthecovers, que queremos hacer un servicio web que retorna, en el caso de Dokeos, detalles sobre un curso.

La función que se encargar de recojer la información se llamará getCourseDetails(), tomará solo un parámetro  tipo string en entrada (para parametros complejos, ver el ejemplo de Christian) y retornará un arreglo del tipo siguiente:

Array(

['code'] => ‘ABC’,

['title'] => ‘English alphabet’,

['url'] => ‘http://www.example.com/ABC’,

['teacher'] => ‘Yannick Warnier’,

['language'] => ‘english’

)

Hemos definido nuestras necesidades, ahora vamos más adelante y registramos el servicio mismo y sus tipos de datos:

$server = new soap_server();

$server->configureWSDL(‘WSCourse’, ‘urn:WSCourse’);

// definimos el tipo complejo (arreglo asociativo, =’struct’) de detalles de curso

$server->wsdl->addComplexType(
‘courseDetails’,
‘complexType’,
’struct’,
‘all’,
”,
array(
‘name’=>’code’  , ‘type’=>’xsd:string’,
‘name’=>’title’  , ‘type’=>’xsd:string’,
‘name’=>’url’    , ‘type’=>’xsd:string’,
‘name’=>’teacher’, ‘type’=>’xsd:string’,
‘name’=>’language’,'type’=>’xsd:string’,
)
);

Por favor apunten que declaramos un tipo, pero no hay ninguna relación, al nivél de esta declaración, entre el tipo y un método. Este tipo podrá ser reutilizado para otros métodos también.

// Registramos el método y su parámetro de entrada
$server->register(‘getCourseDetails’,           // nombre método
array(‘username’ => ‘xsd:string’),            //  parametros entrantes al servicio
array(‘return’ => ‘tns:courseDetails’),     // valor(es) retornado(s)
‘urn:WSCourse’,                                            // namespace (espacio de nombre)
‘urn:WSCourse#getCourseDetails’,         // acción SOAP
‘rpc’,                                                                 // estílo
‘encoded’,                                                       // tipo de uso
‘This service returns a list of courses’    // documentación
);

En este caso, usamos directamente un ’struct’ como valor de retorno. Usamos el marcador ‘tns:’ (probablemente Type NameSpace o algo así) para decir que es un tipo complejo definido por otro lado, y definimos que tipo exactamente queremos usar.

Esto es lo que debería ocurir y, para este caso (no verificado), tal vez funcionaría. Si no funciona, prueben remplazar tns:courseDetails por xsd:Array, simplemente (vemos más abajo casos probados).

En el caso un poco más complejo en que tenga que devolver un arreglo de estos courseDetails, como hago?

Arreglos de arreglos

Una estructura a dos niveles no se puede definir en solo una definición de ComplexType. En sí, se tiene que definir cada nivel como un ComplexType:

$server->wsdl->addComplexType(
‘courseDetails’,
‘complexType’,
’struct’,
‘all’,
”,
array(
‘name’=>’code’  , ‘type’=>’xsd:string’,
‘name’=>’title’  , ‘type’=>’xsd:string’,
‘name’=>’url’    , ‘type’=>’xsd:string’,
‘name’=>’teacher’, ‘type’=>’xsd:string’,
‘name’=>’language’,'type’=>’xsd:string’,
)
);

Nada cambia para esta primera parte. Ahora definimos el arreglo que va a contener estos structs:

$server->wsdl->addComplexType(
‘courseList’,
‘complexType’,
‘array’,
”,
‘SOAP-ENC:Array’,
array(),
array(
array(‘ref’=>’SOAP:ENC:arrayType’,
‘wsdl:arrayType’=>’tns:courseDetails[]‘)
),
‘tns:courseDetails’
);

Bueno, hay mucho que todavía no hemos detallado ahí, pero no es directamente el objetivo de este artículo. Una cosa es importante: esto es la forma de declarar un arreglo (no asociativo, es decir con indices numéricos unicamente) del ComplexType courseDetails.

Una vez que tenemos esto listo, la lógica nos haría pensar que simplemente debería registrar este tipo complejo en mi servicio web:

// Registramos el método y su parámetro de entrada
$server->register(‘getCourseDetails’,           // nombre método
array(‘username’ => ‘xsd:string’),            //  parametros entrantes al servicio
array(‘return’ => ‘tns:courseList’),          // valor(es) retornado(s)
‘urn:WSCourse’,                                            // namespace (espacio de nombre)
‘urn:WSCourse#getCourseDetails’,         // acción SOAP
‘rpc’,                                                                 // estílo
‘encoded’,                                                       // tipo de uso
‘This service returns a list of courses’    // documentación
);

… y ahí es donde perdimos horas buscando. En realidad, parece que hay algo malo ahí, en la forma en que NuSOAP (a lo menos versión de Agosto de 2008) retorna este valor, que es incorrecto. Resulta que el cliente SOAP de PHP5 no entiende el valor retornado. Ni para este arreglo de structs, ni para el struct mismo.

Abandonar la definición de tipos complejos

La solución es de abandonar la idea de definir el tipo de valor retornado, y simplemente usar xsd:Array como valor de retorno:

// Registramos el método y su parámetro de entrada
$server->register(‘getCourseDetails’,           // nombre método
array(‘username’ => ‘xsd:string’),            //  parametros entrantes al servicio
array(‘return’ => ‘xsd:Array’),          // valor(es) retornado(s)
‘urn:WSCourse’,                                            // namespace (espacio de nombre)
‘urn:WSCourse#getCourseDetails’,         // acción SOAP
‘rpc’,                                                                 // estílo
‘encoded’,                                                       // tipo de uso
‘This service returns a list of courses’    // documentación
);

No hay lógica ahí, es cierto, pero a lo menos espero que les ayudarán. Me estoy comunicando con los desarrolladores de NuSOAP para averiguar el tema.


Servicios web con PHP utilizando la herramienta NuSoap

Marzo 16, 2009

El presente artículo esta dirigido a las personas que ya poseen conocimientos sobre Internet y programación y quieren comenzar a conocer el mundo de los web services (servicios web).

Antes de comenzar con lo que es NuSoap creo que es conveniente realizar una introducción a lo que son los Servicios Web, y para esto debemos comenzar con la definición de Web Service.

Web Service: es un sistema software diseñado para soportar la interoperabilidad máquina – máquina a través de una red. Este tiene una interfaz descripta en un formato que puede ser procesado por una máquina (específicamente WSDL, que veremos más adelante). Otros sistemas interactúan con el Web service utilizando mensajes SOAP los cuales se encuentran establecidos previamente.
Entonces podríamos decir que un Web Service es una comunicación por medio de mensajes SOAP  entre diferentes equipos a través de una red.

SOAPSimple Object Access Protocol

Es un protocolo de comunicación, el cual permite la comunicación entre aplicaciones a través de mensajes por medio de Internet. Es independiente de la plataforma, y del lenguaje. Esta basado en XML y es la base principal de los Web Services. Los mensajes SOAP son documento XML propiamente dicho, pero esto lo veremos más adelante cuando veamos un ejemplo de un mensaje SOAP.

WSDL – Web Services Description Language

Es un protocolo basado en XML que describe los accesos al Web Service. Podriamos decir que es el manual de operación del web service, porque nos indica cuales son las interfaces que provee el Servicio web y los tipos de datos necesarios para la utilización del mismo.

¿Que es NuSOAP?
NuSOAP es un kit de herramientas (ToolKit) para desarrollar Web Services bajo el lenguaje PHP. Esta compuesto por una serie de clases que nos harán mucho más fácil el desarrollo de Web Services. Provee soporte para el desarrollo de clientes (aquellos que consumen los Web Services) y de servidores (aquellos que los proveen). NuSOAP esta basado en SOAP 1.1, WSDL 1.1 y HTTP 1.0/1.1
No  es el único soporte para Web Services en PHP, existen otros, pero es uno de los que están en una fase de desarrollo mucho más avanzada. Sin ir más lejos, PHP a partir de su versión 5 comienza a dar soporte para SOAP, pero aun esta en fase experimental.

La instalación es bastante sencilla, solo basta ir a la pagina en sourceforge de NuSOAP http://sourceforge.net/projects/nusoap/ y bajar el archivo comprimido (es un .zip).

Lo descomprimimos en un directorio de nuestro servidor web (como puede ser /lib que es el directorio por default), y listo, ya podemos hacer uso de NuSOAP.

Ejemplo de servicios web utilizando nusoap

Les mostrare la forma de usar nusoap para aplicaciones de servicios web usando wsdl.

ejemplo : nusoap usando wsdl

(Como proveedor del servicio web)

1.- Incluimos la libreria nusoap dentro de nuestro archivo

require_once(‘lib/nusoap.php’);

2.- Creamos la instancia al servidor

$server = new soap_server();

3.- Inicializamos el soporte WSDL

$server->configureWSDL(‘hellowsdl2′, ‘urn:hellowsdl2′);

4.- Registramos la estructura de datos usado por el servicio

// Parametros de entrada
$server->wsdl->addComplexType(
‘Person’,
‘complexType’,
’struct’,
‘all’,
”,
array(
‘firstname’ => array(‘name’ => ‘firstname’, ‘type’ => ‘xsd:string’),
‘age’ => array(‘name’ => ‘age’, ‘type’ => ‘xsd:int’),
‘gender’ => array(‘name’ => ‘gender’, ‘type’ => ‘xsd:string’)
)
);
// Parametros de salida
$server->wsdl->addComplexType(
‘SweepstakesGreeting’,
‘complexType’,
’struct’,
‘all’,
”,
array(
‘greeting’ => array(‘name’ => ‘greeting’, ‘type’ => ‘xsd:string’),
‘winner’ => array(‘name’ => ‘winner’, ‘type’ => ‘xsd:boolean’)
)
);

5.- Registramos el metodo a exponer

$server->register(‘hello’,                // method name
array(‘person’ => ‘tns:Person’),        // input parameters
array(‘return’ => ‘tns:SweepstakesGreeting’),    // output parameters
‘urn:hellowsdl2′,                // namespace
‘urn:hellowsdl2#hello’,                // soapaction
‘rpc’,                        // style
‘encoded’,                    // use
‘Greet a person entering the sweepstakes’    // documentation
);

6.- Definimos el metodo como una función PHP

function hello($person) {
global $server;

$greeting = ‘Hello, ‘ . $person['firstname'] .
‘. It is nice to meet a ‘ . $person['age'] .
‘ year old ‘ . $person['gender'] . ‘.’;

if (isset($_SERVER['REMOTE_USER'])) {
$greeting .= ‘  How do you know ‘ . $_SERVER['REMOTE_USER'] . ‘?’;
}

$winner = $person['firstname'] == ‘Scott’;

return array(
‘greeting’ => $greeting,
‘winner’ => $winner
);
}

7.- Usamos el pedido para invocar el servicio

$HTTP_RAW_POST_DATA = isset($HTTP_RAW_POST_DATA) ? $HTTP_RAW_POST_DATA : ”;
$server->service($HTTP_RAW_POST_DATA);

(Como cliente, consumidor del servicio web)
——————————————-

1.- Incluimos la libreria nusoap dentro de nuestro archivo

require_once(‘lib/nusoap.php’);

2.- Creamos la instancia como cliente
$client = new soapclient(‘http://localhost/phphack/hellowsdl2.php?wsdl’, true);

3.- Chekeamos para un posible error

$err = $client->getError();
if ($err) {
// Display the error
echo ‘<h2>Constructor error</h2><pre>’ . $err . ‘</pre>’;
// At this point, you know the call that follows will fail
}

4.- Llamamos al metodo soap

$person = array(‘firstname’ => ‘Willi’, ‘age’ => 22, ‘gender’ => ‘male’);
$result = $client->call(‘hello’, array(‘person’ => $person));

5.- Chekeamos para una falla al momento de llamar al metodo
if ($client->fault) {
echo ‘<h2>Fault</h2><pre>’;
print_r($result);
echo ‘</pre>’;
} else {
// Check for errors
$err = $client->getError();
if ($err) {
// Display the error
echo ‘<h2>Error</h2><pre>’ . $err . ‘</pre>’;
} else {
// Display the result
echo ‘<h2>Result</h2><pre>’;
print_r($result);
echo ‘</pre>’;
}
}

6.- Una vez que todo este bien obtenemos la información deseada del servicio a travez del metodo que hemos invocado
se pude ver los pedidos y respuestas de esta manera:

echo ‘<h2>Request</h2>’;
echo ‘<pre>’ . htmlspecialchars($client->request, ENT_QUOTES) . ‘</pre>’;
echo ‘<h2>Response</h2>’;
echo ‘<pre>’ . htmlspecialchars($client->response, ENT_QUOTES) . ‘</pre>’;
// Display the debug messages
echo ‘<h2>Debug</h2>’;
echo ‘<pre>’ . htmlspecialchars($client->debug_str, ENT_QUOTES) . ‘</pre>’;