Crear un webservice básico con PHP y SOAP

SOAP es un protocolo de intercambio para servicios web basado en XML, sobre el que puedes leer en esta entrada de la wikipedia. Para no liarnos con preámbulos, vamos con la chicha ¿cómo creamos un servicio web en PHP?.

Para facilitarnos la vida empezaremos por descargar NuSOAP, un toolkit para el desarrollo de servicios web con SOAP en PHP, que nos proveerá de diversas clases para trabajar con este protocolo. Basta con descargarlo, descomprimirlo, meterlo dentro de nuestro proyecto y, cuando queramos usarlo, incluir nusoap.php como librería.

Ok, con NuSOAP instalado toca crear un servidor SOAP en nuestra aplicación. Para el ejemplo crearemos un servidor, lo llamaremos producto.php, que si recibe una petición donde se le pida una lista de libros devuelva tres títulos (es un ejemplo básico, piensa que en la realiad podrías acceder a una base de datos y dar muchas más funcionalidades).

<?php
	require_once "nusoap.php";
	 
        function getProd($categoria) {
	    if ($categoria == "libros") {
	        return join(",", array(
	            "El señor de los anillos",
	            "Los límites de la Fundación",
	            "The Rails Way"));
	    }
	    else {
	            return "No hay productos de esta categoria";
	    }
	}
	 
	$server = new soap_server();
	$server->register("getProd");
	$server->service($HTTP_RAW_POST_DATA);
?>

Ok, ahora necesitas un cliente, que llamaremos cliente.php. En el constructor, el cliente recibirá el url del servidor y para acceder al método que nos devuelve los libros recurriremos al método call(), al cual le pasaremos el nombre del método del servidor al que queremos acceder y los parámetros en forma de array. Además, también controlaremos que no haya errores en la comunicación.

<?php
	require_once "nusoap.php";
	$cliente = new nusoap_client("http://localhost/producto.php");
	 
	$error = $cliente->getError();
	if ($error) {
	    echo "<h2>Constructor error</h2><pre>" . $error . "</pre>";
	}
	 
	$result = $cliente->call("getProd", array("categoria" => "libros"));
	 
	if ($cliente->fault) {
	    echo "<h2>Fault</h2><pre>";
	    print_r($result);
	    echo "</pre>";
	}
	else {
	    $error = $cliente->getError();
	    if ($error) {
	        echo "<h2>Error</h2><pre>" . $error . "</pre>";
	    }
	    else {
	        echo "<h2>Libros</h2><pre>";
	        echo $result;
	        echo "</pre>";
	    }
	}
?>

Con esto ya tienes una idea múy básica del funcionamiento de un webservice SOAP construído con PHP. Pero claro, nos falta un archivo WSDL para tener un webservice decente. Aunque dicho archivo puede ser escrito a mano, NuSOAP puede generarlo por ti pasándole ciertos parámetros, por lo que lo ideal sería generarlo en el servidor. Así que modifica tu producto.php para que quede tal que así:

<?php
	require_once "nusoap.php";
	 
	function getProd($categoria) {
	    if ($categoria == "libros") {
	        return join(",", array(
	            "El señor de los anillos",
	            "Los límites de la Fundación",
	            "The Rails Way"));
	    }
	    else {
	        return "No hay productos de esta categoria";
	    }
	}
	 
	$server = new soap_server();
	$server->configureWSDL("producto", "urn:producto");
	 
	$server->register("getProd",
	    array("categoria" => "xsd:string"),
	    array("return" => "xsd:string"),
	    "urn:producto",
	    "urn:producto#getProd",
	    "rpc",
	    "encoded",
	    "Nos da una lista de productos de cada categoría");
	 
	$server->service($HTTP_RAW_POST_DATA);
?>

Como ves, el cambio en es cuando llamamos a register, ya que en vez de pasarle, como antes, el método en cuestión, le añadimos también varios argumentos para generar el WSDL:

  • El primer array nos permite definir el argumento de entrada y su tipo de datos
  • El segundo define la función de retorno y su tipo de datos
  • urn:producto es la definición del namespace
  • urn:producto#getProd es donde definimos la acción SOAP
  • Luego viene el tipo de llamada,que puede ser rpc, como en el ejemplo, o document
  • Tras esto definimos el valor del atribute use, que puede ser encoded o literal
  • Finalmente viene una descripción de qué hace el método al que llamamos

Ahora basta con que en el navegador accedas a producto.php?wsdl y verás el WSDL generado. Ya puedes copiarlo y añadirlo a tu directorio web (crea un archivo y llámalo, por ejemplo, libros.wsdl). Para que el cliente lo utilice debes modificar el código, y en el constructor, en vez del url le pasas el nombre del archivo, de forma que quede como en el ejemplo:

         $client = new nusoap_client("libros.wsdl", true);

Ahora sí, ya tienes montado un pequeño servicio web. El ejemplo es simplón, pero piensa en todas las funcionalidades que podrías incorporarle.

65 comentarios en “Crear un webservice básico con PHP y SOAP

  1. Hola quisiera saber si me puedes ayudar con dos inconvenientes que tengo. He seguido tal cual tu tutorial pero cuando ejecuto cliente.php me aparece este error:

    Error
    wsdl error: Bad path to WSDL file libro.wsdl

    Y libro.wsdl se encuentra en el mismo directorio que cliente.php, no entiendo que pasa ahí.

    Por otro lado si intento mejor utilizar la dirección de producto.php?wdsl me aparece esto:

    Error
    Response not of type text/xml: text/html; charset=UTF-8

    Entonces de ninguna forma me ha funcionado, me podrías dar una pista de algo que deba revisar??

    Gracias.

  2. Nestor

    Muchas gracias por el aporte ha sido muy claro y especifico, obviamente es algo muy simple, pero no se necesita más para crear un web service, el resto es la complejidad que le quieramos dar nosotros mismo.

    Muchas gracias por tu aporte

  3. Ricardo Vega Mejía

    Hola, estoy siguiendo el ejemplo publicado pero al tratar de acceder a «https://ws/producto.php?wsdl», el navegador visualiza que no es posible mostrar la página …..

  4. Pingback: Crear un webservice básico con PHP y SOA...

  5. Hola, te agradezco por este post que me facilito el panorama de los web services.
    Al probar el ejemplo, y acceder al client.php se queda como pegado esperando la respuesta, y despues de unos 15 segundos deja de cargar, no imprime ningún error, sabes a que se debe esto?

    1. Alejandro

      Que tal, me pasa lo mismo que a ti, pudiste solucionar el problema??…solo se queda cargando unos 30 segundos y al final no imprime nada…sin erro, sin informacion…

  6. David

    Está muy claro tu ejemplo. Gracias.

    Me indicaba un error en:
    $server->service($HTTP_RAW_POST_DATA);

    Pero le agregue lo siguiente y quedó sin problemas:
    $HTTP_RAW_POST_DATA = isset($HTTP_RAW_POST_DATA) ? $HTTP_RAW_POST_DATA : »;

    Nuevamente gracias y saludos!

  7. wend

    tengo un error al querer obtener el wdsl al seleccionar el wdsl me sale :
    Error de lectura XML: contenido incorrecto tras un elemento XML del documento
    Ubicación: http://localhost/webservice/producto.php?wsdl
    Número de línea 2, columna 1:
    Warning: Creating default object from empty value in /home/masqweb/Documentos/webservice/NuSOAP/lib/nusoap.php on line 75
    ^
    y al querer usar el cliente me da un error de :

    XML error parsing SOAP payload on line 2: Invalid document end

    1. Así de primeras se me ocurre que puede que el WSDL esté mal formado. ¿En el servidor te da algún error o sólo te lo da con el cliente?. Puede que algún caracter no ASCII en el xml te la esté jugando.

  8. Fredy Sanchez

    Hola , me sale esto cuabndo veo el WSDL:

    Este archivo XML no parece tener ninguna información de estilo asociada. A continuación se muestra el árbol del documento.

  9. Mauricio

    Hola amigos, muy bueno el tutorial. Pero me sale un error al ejecutar el cliente, lo escribo a continuación

    Error

    no operations defined in the WSDL document!

    Gracias

  10. Daniel

    Hola, muy buen tutorila facil y claro de hacer, pero tengo un problema en la funcion “function getProd” no puedo enviar los datos a otro lado porque me sale “HTTP Error: no data present after HTTP headers” y debo en esa funcion antes del join llamarla con los datos recibidos, gracias por la ayuda!

  11. Amigo te hace falta una linea antes de:
    $server->service($HTTP_RAW_POST_DATA);
    Es esta:
    $HTTP_RAW_POST_DATA = isset($HTTP_RAW_POST_DATA) ? $HTTP_RAW_POST_DATA : ”;
    El servidor quedaría:
    require_once “nusoap.php”;
    function getProd($categoria) {
    if ($categoria == “libros”) {
    return json_encode(array(
    “El señor de los anillos”,
    “Los límites de la Fundación”,
    “The Rails Way”));
    }
    else {
    return “No hay productos de esta categoria”;
    }
    }
    $server = new soap_server();
    $server->configureWSDL(“producto”, “urn:producto”);
    $server->register(“getProd”,
    array(“categoria” => “xsd:string”),
    array(“return” => “xsd:string”),
    “urn:producto”,
    “urn:producto#getProd”,
    “rpc”,
    “encoded”,
    “Nos da una lista de productos de cada categoría”);
    $HTTP_RAW_POST_DATA = isset($HTTP_RAW_POST_DATA)? $HTTP_RAW_POST_DATA : ”;
    $server->service($HTTP_RAW_POST_DATA);

    1. Daniel Alcala

      hola Rafael, tengo un web service, te podre pasar el link para que me ayudes hacer una prueba?? es que mi proveedor de pagina web me dice q esta bloqueado, y yo he podido accesar desde fuera de mi red. GRacias

  12. Muy interesante el código, sobre todo por la ñ del «El señor de los anillos». En mi webservices no envio el caracter ñ en el reponse sino que me viene dado en el request. El problema es que no lo traga. Ya he cambiado la variable de nusoap.php a $soap_defencoding = ‘ISO-8859-1’; y ni por esas. ¿Alguna sugerencia? Muchas gracias

    1. Podrías probar configurando el server en utf-8:

      $server->soap_defencoding = ‘UTF-8’;
      $server->decode_utf8 = false;
      $server->encode_utf8 = true;

      Y también el cliente:
      $cliente->soap_defencoding = ‘UTF-8’;
      $cliente->decode_utf8 = false;

  13. Buen post, la verdad yo ignoraba cómo funcionaba un WebService.
    Hasta ahora para las aplicaciones o sitios en los que necesitaba interactuar, yo enviaba requests ya sea por POST o GET a un .php y de este devolvía un XML o JSON con la data.
    Esto en teoría reemplazaría a esta práctica, pero con más seguridad. ¿Es correcto? ¿O de alguna forma se puede ver o intervenir los parámetros de entrada y salida?
    Saludos.

  14. Saludos a todos, tengo un proyecto para iniciar en webservice, pero no le hallo mucho a esto, alguien podría asesorarme?, desde ya les agradezco, saludos.

    Les explico, la idea es que de una base de datos capturada desde una pantalla de access se extraigan datos para que a su vez se conviertan en json con wsdl y puedan ser enviados a través del webservice, estara bien mi proceso o me brinque algo? La intensión es que se comparta la información generada en json hacia otros usuarios.

      1. Daniel Alcala

        buenas tardes Amigo, tengo un web service, te podre pasar el link para que me ayudes hacer una prueba?? es que mi proveedor de pagina web me dice q esta bloqueado, y yo he podido accesar desde fuera de mi red. GRacias

  15. Hola,

    me pasa lo mismo que a Diego, al hacer localhost/producto.php?wsdl no devuelve nada. He revisado la configuración del servidor y el php.ini y está todo correcto. ¿Alguna ayuda?

    Dani ¿resolviste el problema?

    Gracias y saludos.

    1. Este el es código:

      soap_defencoding = ‘UTF-8′;
      $server->decode_utf8 = false;
      $server->encode_utf8 = true;
      $server->configureWSDL(«producto», «urn:producto»);

      $server->register(«getProd»,
      array(«categoria» => «xsd:string»),
      array(«return» => «xsd:string»),
      «urn:producto»,
      «urn:producto#getProd»,
      «rpc»,
      «encoded»,
      «Nos da una lista de productos de cada categoría»);

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

      ?>

  16. soap_defencoding = ‘UTF-8′;
    $server->decode_utf8 = false;
    $server->encode_utf8 = true;
    $server->configureWSDL(«producto», «urn:producto»);

    $server->register(«getProd»,
    array(«categoria» => «xsd:string»),
    array(«return» => «xsd:string»),
    «urn:producto»,
    «urn:producto#getProd»,
    «rpc»,
    «encoded»,
    «Nos da una lista de productos de cada categoría»);

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

    ?>

  17. Javier

    Muy bueno el codigo pero te hago una pregunta podrias armar un ejemplo, pasadole mas de un parametro a la función getProd, por ejemplo categoria y editorial

  18. Miguel Enriquez

    Hola Amigos, el primer ejemplo el WebService me salio, tuve un errorcillo pero era del path:
    la carpeta donde esta el soap la tengo dentro demi proyecto nadamas basto con declarar la siguiente linea en ambos archivos:

    require_once «nusoap/lib/nusoap.php»;

    mi carpeta del proyecto se llama test, entonces cuando llame:

    http://localhost/test/cliente.php

    funciona perfecto (claro en cliente.php adecue el llamado a producto.php quedando de esta manera: $cliente = new nusoap_client(«http://localhost/test/producto.php»);

    ejecute:

    http://localhost/test/cliente.php

    y me trae el resultado correcto:

    Libros

    El señor de los anillos,Los límites de la Fundación,The Rails Way

    ahora cambie el producto.php como aconsejan accedo a:

    http://localhost/test/producto.php?wsdl

    y me marca el siguiente error:

    This page contains the following errors:

    error on line 2 at column 1: Extra content at the end of the document
    Below is a rendering of the page up to the first error.

    que me estara fallando? les agradezco.

    Mi objetivo final son 2:

    1) extraer informacion de una BD similar a como esta el ejemplo.
    2) insertar datos (enviarle al webservice datos y que los inserte en una tabla) nos e como hacerlo, alguna idea de como investigar?

    Gracias.

  19. ¡¡Muchas gracias amigo!! Me costó un montón encontrar un artículo que explicase de forma sencilla, clara y certera como hacer esta operación. Me fue muy útil, y te lo agradezco de verdad. Saludos.

  20. Fernando

    Me sale el siguiente error: Response not of type text/xml: text/html
    He revisado que tengo la librería nusoap.php en la carpeta correcta. Y los ficheros clientes.php y producto también. Cree el fichero libros.wsdl tal como indicaste.
    Los he incluido todos en la misma carpeta pero no me funciona.
    Puedes ayudarme.
    Muchas gracias.

  21. muy claro y tan fácil como copiar, pegar y probar. muy buen aporte.
    a mi me funciono correctamente.

    tengo la carpeta miservicio/lib/[archivos de nuSOAP]
    tengo mis ficheros:
    + miservicio
    |-+ [lib] (carpeta de nusoap)
    |– micliente.php
    |– servicio.php
    |– servicio.config.php (opcional) para la creacion del
    |– servicio.wsdl (guarda el contenido creado en ejecutar servicios.config.php), copiar y pegar

    los contenidos son:
    servicio.php
    —————-
    register(«getProd»);
    $server->service($HTTP_RAW_POST_DATA);
    ?>

    micliente
    ————-

    getError();
    if ($error) {
    echo «Constructor error

    " . $error . "

    «;
    }

    $result = $cliente->call(«getProd», array(«categoria» => «libros»));

    if ($cliente->fault) {
    echo «Fault

    ";
            print_r($result);
            echo "

    «;
    }
    else {
    $error = $cliente->getError();
    if ($error) {
    echo «Error

    " . $error . "

    «;
    }
    else {
    echo «Libros

    ";
                echo $result;
                echo "

    «;
    }
    }
    ?>

    servicio.config.php
    ————————-

    configureWSDL(«producto», «urn:producto»);

    $server->register(«getProd»,
    array(«categoria» => «xsd:string»),
    array(«return» => «xsd:string»),
    «urn:producto»,
    «urn:producto#getProd»,
    «rpc»,
    «encoded»,
    «Nos da una lista de productos de cada categoría»);

    $server->service($HTTP_RAW_POST_DATA);
    ?>

    servicio.wsdl
    —————–

    Nos da una lista de productos de cada categoría

    # descripcion:
    # —————-

    /*
    al ejecutar en el navegador servicio.config.php devolvera el xml generado del wsdl, lo copias y pegas en el fichero servicio.wsdl y ese fichero lo asignas en tu cliente.

    $cliente = new nusoap_client(«servicio.wsdl», true);

    el tutorial esta muy bien explicado pero si lo quieren masticadito aqui esta, facil y asi revisan en que fallan a la hora de implementarlo
    */

    1. creo q al pegar el código y comentarlo se desarmo pero el contenido es lo que el autor del post coloco como ejemplo.

      reemplazando servicio.php con el primer codigo
      reemplazando servicio.config.php con el tercer codigo el que genera el xml
      y reemplazando cliente.php con el segundo codigo que consume el servicio

  22. Almudena

    Hola buenas tardes, he leido tu artículo y me parece bastante intersante.

    Pero me gustaría hacerte una pregunta. ¿Cómo puedo hacer lo mismo que hace SOAPui pero sin usarlo? Tengo que hacer un testeo autoático de tres valores que pregunto con SOAP a un servidor, pero esta acción la tengo que automatizar para que haga este testeo un número «X» de veces.

    Muchas gracias de antemano.

  23. Almudena

    Buenas tardes,

    Tengo que hacer un proyecto, con web services y no tengo mucha idea (para qué mentir).

    El problema, es que el web service, ya lo tengo montado, para mi es una caja negra, a la que ingreso una serie de datos y esta me tiene que devolver unos campos.

    Por así decirlo mi servicio web, me logueo, y luego en una caja de texto meto un número de teléfono y dando al botón de comprobar, este me devuelve una serie de campos que me muestra por pantalla.

    Pues bien, me gustaría poder hacer una consulta masiva y poder mostrar esos campos en un excell.

    Me podeis echar una mano?

    Gracias de antemano.

  24. Carlos Diaz

    Qué diferéncia hay entre este WebService y una petición normal al servidor a través de php usando Ajax?

    Lo pregunto porque estoy acostumbrado a hacer aplicaciones de gestion web y a cada rato estoy haciendo peticiones a la base de datos con php y realizando la llamada con ajax para no tener que recargar la pagina.

    1. John Tux

      Con un WebService puedes acceder a la información alojada en cualquier servidor (através del propio WS que te sirve como puente), con AJAX sólo se puede acceder a la información del servidor en el que se ejecuta el script de PHP que mandas a llamar, y este script puede contener la información de consulta a BD directamente, sin ser un puente.

      Esto debido a la Política del mismo origen.

      Si quieres mostrar en tu servidor A información que está en una BD en el servidor B, haces un WS que instalas en el servidor B y que consultarás desde el servidor A.
      Esto con AJAX no se podría, sino me crees inténtalo y si lo logras: compártelo 😉 😀
      Saludos.

      1. Carlos Diaz

        Esta bien si, es que no lograba ver la diferencia con este ejemplo. Ya me ha quedado claro, una conexión entre servidores. Gracias por la aclaración.

Deja un comentario

Este sitio utiliza Akismet para reducir el spam. Conoce cómo se procesan los datos de tus comentarios.