Retornar arrays con NuSOAP

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.

Comments

Muchas gracias por la información. He estado mucho tiempo volteando con esto pero por lo visto es mejor dejar a un lado la idea de arreglo de arreglos…. 🙁

Estoy en un proyecto que tiene este problema, solo que el cliente consumidor del WS es en Java. Al generar las clases stub para conectarse al servicio me devuelve un error diciendo que no existe el tipo xsd:Array. ¿hay algo que puedo estar omitiendo?

Muchas gracias.

Estimados, buenos días, a ver si alguien me puede ayudar… estoy trabajando con complextype un arreglo de arreglo. Y uno de los campos es un string donde debo almacenar un número, el problema que tengo es que al armar el array NuSoap o PHP me hacen una conversión implícita y me termina devolviendo un tipe integer. Qué pasa, el numero que debo almacenar desborda el rango de enteros, entonces siempre me almacena 2147483647, bueno, si alguien pudiera ayudarme le estaría agradecido.

Saludos.
El foro está de 10

Fede

Enlace permanente

Gracias por la información, he tenido algunos dolores de cabeza por los tipos complejos de nusoap cuando se retornan en un Array, gracias por el dato del xsd:Array, me sirvió de mucho.

Excelente! a mi me funciono sin especificando “tns:courseList” en vez de ‘xsd:Array’ como tu señalabas , porque mi cliente .NET me estaba exigiendo un tipo de datos declarado, en un cliente php funciona de las 2 maneras.

Gracias hermano!! =)