Desde hace ya varios años soy fiel defensor de la distribución de aplicaciones y la interoperatibilidad usando WebServices, uno de los lenguajes que comencé por explotar con esta tecnología fue PHP, claro, usando PEAR::SOAP, uno de los inconvenientes (en realidad hay varios) de PEAR::SOAP era lo dificil de escribir de cada implementación y lo aún más difícil de "debug" (créanme, a veces pasa a ser toda una pesadilla). Bueno, de unos meses para acá me topo con otro amigo que por lo menos para un par de cosas me facilitó bastante el trabajo, se trata de PEAR::Services_Webservice.
Al igual que su compañero PEAR::SOAP, PEAR::Services_Webservice tampoco está en versión estable, así que al instalarlo recuerden cambiar su configuración de PEAR a beta. A diferencia de PEAR::SOAP, en este módulo no hace falta escribir las tediosas partes del dispatch_map o crear (por cuestiones de claridad) el service server y luego el service dispatcher. No voy a tocar como se hace en PEAR::SOAP y entraré de lleno en cómo se implementa un servicio sencillo en PEAR::Services_Webservice. Antes de continuar debo avisar que deben tener la SOAP extensión instalada en PHP para que todo funcione como se espera, PHP5 ya la trae como default.
Comenzaremos con una clase sencilla, imaginemos que tenemos una clase que suma dos números:
1
2
3
4
5
6
7
8
9
10
11 |
<?php
class SeviceExample
{
public function addNumbers($num1, $num2)
{
return $num1 + $num2;
}
}
?>
|
Bien, ahora, tomando en cuenta que ya tenemos instalado PEAR::Services_Webservice hagamos que nuestra clase sea ahora un servicio:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36 |
<?php
require_once 'Services/Webservice.php';
class ServiceExample extends Services_Webservice
{
/**
* Add two integers
*
* @param int
* @param int
* @return int
*/
public function addNumbers($num1, $num2)
{
return $num1 + $num2;
}
private function noService()
{
// boooohhhh!!!
}
}
$example = new ServiceExample(
'service name',
'service simple description',
array(
'uri' => 'ServiceExample',
'encoding' => SOAP_ENCODED,
'soap_version' => SOAP_1_2
)
);
$example->handle();
?>
|
el método "handle" de la clase Services_Webservice se encarga de manejar los request y todo lo relacionado al servicio como tal, o sea, una vez llamado este método no tenemos que preocuparnos por nada en el momento de ejecutar una llamada o retornar la descripción del servicio. Al instanciar una clase hija de Services_Webservice debemos pasar como parámetros el nombre del servicio ("service name"), una simple descripción del servicio ("simple service description") y un arreglo de opciones en el momento de instanciar la clase (el uri name del namespace correspondiente al servicio, el encoding por el cual serán transmitidos los datos y la versión de SOAP que se usará, por default creo que debemos usar para la versión y el encoding esos).
Como algunos sabrán, los mensajes SOAP tienen entre sus características ser tipificados, por lo que demos indicar siempre los tipos, en PEAR::SOAP esto lo hacíamos al inicializar el arreglo $__dispatch_map, esto aveces (en realidad casi siempre) complicaba un poco nuestro código. Services_Webservice tiene un "approach" mucho más fácil y eficaz, usar docstrings para identificar al servicio. La primera línea es tomada en cuenta como si fuera la descripción del método (para que simples mortales sepan que hace), luego los @param indican el tipo necesario para los parámetros, @return a su vez indica el tipo de retorno del método del servicio, tengan cuidado porque sus comentarios de docstring deben comenzar SIEMPRE con /**.
Imaginando que nuestra prueba esta en orden y en nuestro webserver preferido al acceder a este archivo (http://<server address>/wstest/example1.php) obtendremos algo así:

Como verán por default nos retorna una página amigable con la descripción del servicio y con cada uno de los métodos asociados. Si quisiera acceder al WSDL document, simplemente debo agregarle ?WSDL al final del URL en mi request (o sea, http://<server address>/wstest/example1.php?wsdl)
Como también se han dado cuenta, sólo se publicaran métodos públicos, los privados son pasados de largo.
Qué pasa si quiero retornar un tipo no existente en la lista de tipos aceptados por SOAP o es un tipo complejo? Bien, la respuesta es sencilla, simplemente definimos un nuevo tipo, quizás algo como esto:
1
2
3
4
5
6
7
8
9
10
11
12
13
14 |
<?php
class MyReturn
{
/**
* @var int
*/
public $result;
public function __construct($result)
{
$this->result = $result;
}
}
?>
|
Ahora sólo tendríamos que agregar el include a esta clase con el nuevo tipo y cambiar el tipo de retorno de addNumbers a MyReturn. Fácil verdad?. Bien, qué si ahora quiero retornar arreglos? la respuesta es tan sencilla que ni siquiera pondré un ejemplo, simplemente agregamos al final del tipo [], de esta manera un arreglo de enteros pasaría a ser int[], uno de cadenas a string[] y uno de MyReturn a MyReturn[]. Sencillísimo!.
Para finalizar recuerden que Services_Webservice está en beta, por lo que aún pueden faltarle una que otra cosa, pero creo que para algo sencillo tendremos más que suficiente. Recuerden que necesitaran un cliente de su Webservice para probarlo como se debe. Como siempre comentarios a la orden.