Como usar webservices (SOAP) de forma correcta.

Durante varios años trabajando con servicios web he visto de todo en cuanto a implantaciones en PHP. Si tienes opción, utiliza REST, ya que es algo más moderno y sencillo de afrontar en PHP.

Antes que nada hay que comprender que SOAP describe como compartir información entre aplicaciones mediante XML. Este intercambio necesita una definición de que es lo que se intercambia en una definición WSDL. Normalmente un desarrollador PHP atacaría a un servidor SOAP, pero tener que hacer un servidor puede ser probable.

¿Qué cosas no se deben hacer?

Lo primero a tener en mente es que hay poca documentación. PHP de serie no dio soporte hasta PHP 5.

NuSOAP: Fue la solución previo a PHP 5, pero requiere de que tengas este código en tu proyecto y la verdad nunca me dio buenos resultados

Regexp: Recomendaría el uso de Expresiones Regulares para muchas cosas menos para esto. Es un error típico el intentar sustraer información de un Request de un webservice e intentar sacar por pantalla un XML que entienda el resultado.

Parsear XML: Es mejor que el caso anterior, pero tendremos muchos problemas si es que la estructura XML es demasiado libre.

ECHO: Sacar por pantalla resultados e intentar que el formato sea correcto.

Estas 3 prácticas nos van a llevar a debuggear el código muchísimas veces y no tener algo 100% correcto.

¿Por dónde empezar?

Primero que nada, busca este software que es libre y gratuito: SOAPUI. Para poder hacer verdadero testing de lo que está sucediendo. Luego, hay que utilizar lo estándar como SoapClientSoapServer.

Ejemplo de cliente SOAP que se conecta a un webservice para realizar la acción buscarClientes():

<?php

#crear un cliente del servicio indicado por la url hacia el WSDL.
$cliente = new SoapClient("http://servidor.com/servicio.aspx?wsdl");

#al crear el cliente, se crean automáticamente los métodos definidos en el WSDL.
$respuesta = $cliente->buscarClientes("Finlandia");

Esta es la forma estándar de utilizar SOAP en PHP. Si necestamos más información podemos o bien utilizar SOAPUI para ver los request que van y vienen o al crear el cliente hacerlo de esta manera:

<?php
#el segundo parámetro del constructor de un soapclient son configuraciones y utilizando trace = 1 nos da acceso a 2 variables importantes.
$cliente = new SoapClient("http://servidor.com/servicio.aspx?wsdl",[ 'trace' => 1 ]);

$respuesta = $cliente->buscarClientes("Finlandia");
#para ver el request enviado al servidor SOAP
var_dump( $cliente->__getLastRequest() );
#para ver el response del servidor SOAP
var_dump( $cliente->__getLastResponse() );

Para crear un servidor SOAP, debemos cumplir con un WSDL que seguramente ya hayamos establecido con la otra parte, por lo que debemos hacer un código de esta manera:

<?php

if( isset($_REQUEST['wsdl']) ){
    #enviar headers de un archivo XML
    header("Content-type: text/xml; charset=utf-8");
    #imprimir el archivo wsdl que tenemos
    readfile('path_al_wsdl.wsdl');
}
else{
    #ejecutar servidor
}

Ahora debemos crear las Clases PHP y los métodos para cumplir con el WSDL y hacer que SoapServer nos lo gestione.

Sopongamos que el WSDL define un objeto wsCliente y un objeto wsResponseBuscarClientes que es la respuesta de un método llamado buscarClientes que recibe por parámetro un país en String.

<?php

#declarar las clases necesarias para el servicio web según el WSDL
class Cliente {
    public $nombre;
    private $id;
    public $pais;
}

class ResponseBuscarClientes {
    public $clientes;
    public function agregarCliente($cliente){
        $this->clientes[] = $cliente;
    }
}

function buscarCliente($pais){
    #esta funcion busca clientes  y genera un Clientes
    $response = new Clientes();
    foreach( $clientes as $cliente )
        $clientes->agregarCliente($cliente);
    return $clientes;
}
if( isset($_REQUEST['wsdl']) ){
    #enviar headers de un archivo XML
    header("Content-type: text/xml; charset=utf-8");
    #imprimir el archivo wsdl que tenemos
    readfile('service.wsdl');
}
else{
    #ejecutar servidor

    #mapear las clases definidas en el wsdl a clases php.
    $clasmap = array( 'wsCliente' => 'Cliente', 'wsResponseBuscarClientes' => 'ResponseBuscarClientes' )
    #crear un servidor basado en un wsdl.
    $server = new SoapServer('service.wsdl',array( 'classmap' => $classmap ));

    $server->addFunction("buscarCliente");

    #indicarle a SoapServer que gestione el Request que entra.
    $server->handle();
}

En este ejemplo vemos como se gestiona el servidor SOAP y además siempre utilizando clases PHP.

¿Cómo debuggear lo que sucede?

Como se ve en el ejemplo uno está más o menos a ciegas, aunque si se tiene cuidado mappeando las clases correctamente la mitad del trabajo está hecho. De todas formas si uno quiere saber que XMLs van y vienen se pueden utilizando el parámetro trace de SoapClient.

<?php ... #crear el soapclient agregando la opción de trace.     $cliente = new SoapClient("http://servidor.com/servicio.aspx?wsdl", array( 'trace' => true ) );
    
    #obtener el XML de la petición al servidor
    var_dump( $cliente->__getLastRequest() );
    #obtener el XML de la respuesta servidor
    var_dump( $cliente->__getLastResponse() );

Estos XMLs pueden ser utilizados conjuntamente con SOAPUI para comprobar que lo que suceda sea correcto.

SOAPUI se descarga de http://www.soapui.org/

Lo más importante

Recordar que hay muchos puntos de error utilizando WebServices SOAP por lo que es primordial tener claros los conceptos de WSDL y utilizar lo estándar que ya provee el lenguaje. Evitar a toda costa parsear XMLs.

Share on FacebookShare on Google+Tweet about this on TwitterShare on LinkedInShare on StumbleUpon
Pablo Oneto

Soy un desarrollador de software web especializado en PHP.

12 Comments

    1. Pablo Oneto
      Pablo Oneto

      si no te da respuesta puede que no le hayas pasado parámetros correctos, si el parámetro está con “?” quiere decir que no lo envías (Por ejemplo) ?… también tienes seguramente algún header que completar con usuario y contraseña.

  1. Roberto Rodriguez

    Hola Pablo, es primera vez que tengo la experiencia con WebService, estoy tratando de tener acceso a los metodos de un sistema que se llama magaya, como primer paso intento conectar a un metodo llamado StartSession donde ocupa parametro user y pass, sin embargo no me imprime nada al ejecutar, que podria ser el problema?
    require_once(“lib/nusoap.php”);
    $wsdl=”http://192.168.0.231:3691/CSSoapService?wsdl”;
    $client= new nusoap_client($wsdl, ‘wsdl’);
    if ($client->fault) {
    echo ‘Fallo’;}
    else
    { echo ‘exito’; }
    $err = $client->getError();
    if ($err) {
    echo ‘Error ‘ . $err ;} else {
    echo ‘Conexion Satisfactoria’;}
    $param = array(‘user’ => ‘Administrador’,’pass’ => ‘xxxx’);
    $result=$client->call(‘StartSession’,$param);
    print_r($result);

    1. Pablo Oneto
      Pablo Oneto

      Te recomiendo antes que nada no utilizar “nusoap” sino SoapClient que ya viene en PHP. Prueba descargar SoapUI para testear el servicio web y ver que todo funciona correctamente. Con SoapClient puedes debugar las request utilizando $cliente->__getLastRequest() y $cliente->:__getLastResponse()

  2. Roberto Rodriguez

    Pablo gracias por tu respuesta, te comento he hablado con un programador del sistema externo al cual deseo conectarme, solo que el maneja mas lo que es .net y me comento lo siguiente:
    Lo que haces para crear el cliente está bien, lo único es que debes enviar adicionalmente en el arreglo de parámetros del constructor una opción donde debes especificar el IP.
    Pero el fuerte del programador es .net y no esta seguro exactamente donde ponerlo

  3. Nelson Castillo

    Hola que tal? Pablo orientame en el tema estoy haciendo una aplicacion sencilla, tratnado de conectarme a un servicio web, esto lo he logrado localmente ya que el servicio web estaba corriendo en mi maquina, luego se hizo el cambio a un servidor en otro edificio y desde ese dia me genera el siguiente mensaje: Fatal error: Uncaught SoapFault exception: [WSDL] SOAP-ERROR: Parsing WSDL: Couldn’t load from ‘//url/Servicio?wsdl’ : failed to load external entity “/url/Servicio?wsdl”
    Que podria ser porque si me funcionaba

    1. Pablo Oneto
      Pablo Oneto

      Intenta acceder a la url agregando ?wsdl al final. Si no te aparece un xml es que está mal el servidor porque tiene que generar la definición del servicio web. Por otro lado, si el xml esta ok, pero al cambiar de server se ha cambiado la url de acceso ten en cuenta que en el wsdl tiene que indicar que el servicio está en esa url y no en la antigua. Para descartar otros temas intenta este código antes de crear el cliente para que no pille la cache de wsdl:

      <?php
      ini_set('soap.wsdl_cache_enabled', '0'); 
      ini_set('soap.wsdl_cache_ttl', '0');
      
  4. Nelson Castillo

    Fijate Pablo que el xml me aparece perfectamente, ingrese las dos lineas que me has recomendado pero de igual forma sigue apareciendo el mensaje que te puse anteriormente, basicamente es el mismo codigo que he dejado como cuando me funcionaba en mi maquina local a excepcion que le he cambiado el url a la publica donde esta corriendo mi servidor

    1. Pablo Oneto
      Pablo Oneto

      Esto quizás no tenga nada que ver con SOAP sino que desde el servidor nuevo no veas el servicio web. Has intentado hacer un “wget” para descargar el XML desde la consola del servidor y ver si es correcto?

  5. Pingback: Consumir Webservices SOAP desde PHP - vardump.esvardump.es

  6. Francisco

    Excelente tutorial Pablo, he logrado hacer mis primeros pininos en esto de los webservice, quiero comentarle que me ha sucedido algo similar al caso del compañero, inicialmente tenia un web service en mi maquina, cree la aplicacion para consumirlo localmente y me funciono de maravilla, el detalle esta cuando intento consumir un web server que esta en un servidor, me genera Uncaught SoapFault exception: [WSDL] , he visualizado directamente el xml y puedo ingresar sin ningun problema, agradeceria mucho si me puede dar una mano porque ya no se que es.

  7. Francisco

    pongo un pedazo del codigo para que verifique:

    ini_set(‘soap.wsdl_cache_enabled’,0);
    ini_set(‘soap.wsdl_cache_ttl’,0);
    ini_set(‘default_socket_timeout’, 1000);
    $cliente = new SoapClient(“http://192.168.9.50:3691/Service?wsdl”,array(‘trace’ => true));
    try
    {
    $respuesta = $cliente->StartSession(‘roberto’,’diosesamor’);
    }
    catch (Exception $e)
    {
    echo $e->getMessage();
    }
    var_dump($respuesta);
    var_dump( $cliente->__getLastRequest() );

Deja un comentario