Cómo enviar XML a un WebService con PHP

Para empezar, debemos responder una pregunta escencial: ¿se trata de un WebService de tipo SOAP o uno de tipo REST?

¡La forma de enviarlo en uno u otro caso será muy diferente!

Otra historia es cómo recibir XML a través de WebServices desarrollados usando PHP… tema para otro post 🙂

Cómo enviar XML a un WebService SOAP con PHP

Como siempre, para el caso de que se trate de un webservice de tipo SOAP, usaremos la clase SOAPClient

Tomemos como ejemplo este Servicio Web que tiene un solo método disponible (CustomerSearch), el cual recibe XML y devuelve XML.

Tenemos dos alternativas para generar el XML que queremos enviar:

  1. Escribirlo explícitamente (o «a mano»)
  2. Usar la clase SimpleXMLElement

Si aún estás en dudas, no dudes más: usa la clase, te ahorrará una gran cantidad de dolores de cabeza.

Con lo que la llamada se vería algo así como:

<?php

$url = 'https://secure.softwarekey.com/solo/webservices/XmlCustomerService.asmx?WSDL';
$client = new SoapClient($url);

$xmlr = new SimpleXMLElement("<CustomerSearch></CustomerSearch>");
$xmlr->addChild('AuthorID', 1);
$xmlr->addChild('UserID', 'mchojrin');
$xmlr->addChild('UserPassword', '1234');
$xmlr->addChild('Email', 'mauro.chojrin@leewayweb.com');

$params = new stdClass();
$params->xml = $xmlr->asXML(); // OJO: La propiedad xml es particular de este WebService, debes reemplazarla por el nombre del parámetro que espera recibir el servicio al que buscas conectarte

$result = $client->CustomerSearchS($params);

print_r($result);

echo PHP_EOL;

Si ejecutas este código te encontrarás con algo como:

stdClass Object
(
    [CustomerSearchSResult] => stdClass Object
        (
            [any] => <Customers xmlns=""><ResultCode>-1</ResultCode><ErrorMessage>Invalid Login</ErrorMessage></Customers>         
        ) 
) 

Lo que seguramente te interese es lo que está dentro de la clave any, con lo cual, para obtenerlo podrías usar echo $result->CustomerSearchSResult->any; en lugar de print_r($result);. Aunque probablemente lo que quieras no sea mostrar el resultado explícitamente, si no procesarlo de alguna manera… ¿qué mejor que recurrir nuevamente a SimpleXMLElement?

<?php

$url = 'https://secure.softwarekey.com/solo/webservices/XmlCustomerService.asmx?WSDL';
$client = new SoapClient($url);

$xmlr = new SimpleXMLElement("<CustomerSearch></CustomerSearch>");
$xmlr->addChild('AuthorID', 1);
$xmlr->addChild('UserID', 'mchojrin');
$xmlr->addChild('UserPassword', '1234');
$xmlr->addChild('Email', 'mauro.chojrin@leewayweb.com');

$params = new stdClass();
$params->xml = $xmlr->asXML();

$result = new SimpleXMLElement($client->CustomerSearchS($params)->CustomerSearchSResult->any);

$r = current($result->xpath('/Customers/ResultCode'));

if ( $r == '-1' ) {
        echo 'Fallo: '.$result->xpath('/Customers/ErrorMessage')[0];
} else {
        echo 'Exito!';
}

echo PHP_EOL;

Cómo enviar XML a un WebService REST con PHP

En el caso de tratarse de un Servicio Web basado en REST todo es más fácil.

Puedes usar cURL:

<?php

$server = 'http://www.leewayweb.com/miaplicacion';
$headers = [
    "Content-type: text/xml",
    "Content-length: " . strlen($requestXML), "Connection: close",
];

$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, $server);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
curl_setopt($ch, CURLOPT_TIMEOUT, 100);
curl_setopt($ch, CURLOPT_POST, true);
curl_setopt($ch, CURLOPT_POSTFIELDS, $requestXML);
curl_setopt($ch, CURLOPT_HTTPHEADER, $headers);
$data = curl_exec($ch);

if (curl_errno($ch)) {
    print curl_error($ch);
    echo "Algo fallo";
} else {
    curl_close($ch);
}

O bien algo un poco más elaborado como Guzzle:

<?php

use GuzzleHttp\Client;
use GuzzleHttp\Psr7\Request;

$client = new Client();
$request = new Request(
    'POST',
    $uri,
    [
        'Content-Type' => 'text/xml; charset=UTF8'
    ],
    $xml
);

echo $response->getBody();

(Para que funcione este ejemplo hay que tener instalada la librería o incluirla, por ejemplo, usando composer.).

mchojrin

Por mchojrin

Ayudo a desarrolladores PHP a acceder mercados y clientes más sofisticados y exigentes

21 comentarios

  1. Hola entiendo como agregar parámetros con la instruccion : «$xmlr->addChild(‘AuthorID’, 1);»
    pero como agrego por ejemplo una sección en donde van adentro esos parametros como «» que va dentro de la principal?

  2. Hola una consulta, necesito obtener los datos de un proveedor de productos y es por protocolo soap y al intentar realizar las consultas me sale este error

    javax.ejb.EJBException que puede ser? es problema del servidor?

  3. alguien podria ayudarme necesito enviar un archivo xml donde el nombre es en algoritmo md5 al web servbice de arba. los parametros son, usuario, password y archivo xml donde recibo como respuesta el archvo xml. Manualmente lo hago y funciona perfecto pero la idea es q sea algo automatico para q el usuario escriba el cuit de un cliente y le salga que tipo de retencion tiene. ARBA no da mucha informacion mas que, se debe enviar un form multipart con los datos q puse. ya probe de mil maneras y anda.

    1. Hola Juan:

      Gracias por tu pregunta 🙂

      En general tratar con entes gubernamentales es un problema…

      La primera pregunta que se me ocurre es si se trata de un WebService de tipo SOAP o REST.

      Cuando decís «manualmente lo hago y funciona»… ¿a qué te referís? ¿Cómo lo hacés «manualmente»? ¿Usás cURL o algo similar?

      ¿Podrías ampliar sobre «un archivo xml donde el nombre es en algoritmo md5»? Me parece que no te comprendí del todo.

      Saludos!

  4. Buen día, existe una plataforma que se llama wialon, dicha plataforma permite crear un repetidor (apunta a un servidor que se defina) en protocolo SOAP, pero no me indican la estructura, sera posible leer este formato apuntando por ejemplo a un servidor que yo cree y que este lea todo el contenido que emite?

    1. Hola Hugo!

      Gracias por tu pregunta. Es difícil responder tu duda sin conocer más sobre el funcionamiento de wialon. Imagino que algo que podrías hacer es apuntar a un servidor tuyo que almacene en un formato de texto todo lo que le envía el servidor y, de ese modo, tratar de adivinar el formato que usa… no suena muy divertido ciertamente :p

      Lo más fácil sería contactar con gente de la plataforma y preguntarle a ellos.

      Exitos!

    1. No conozco tanto sobre SQL Server como para decirte cómo invocar un WebService desde ahí (Encontré esto que tal vez te ayude).

      En principio, si el webservice es accesible desde la computadora donde está el SQL Server, las credenciales están bien y en general no hay problemas de conectividad no veo por qué no podrías usarlo.

  5. Nuevamente agradezco tu respuesta Mauro. He resuelto el problema, pero no utilizando la clase SOAP sino curl_init() y sus respectivos llamados. Por ejemplo de esta forma puedo llevar el header a través de curl_setopt($ch, CURLOPT_HTTPHEADER, $headers); y el xml a través de curl_setopt($ch, CURLOPT_POSTFIELDS, $xml_data); No necesito construir clases ni definir tipo de estructura de dato, nada de eso, y con esto obtengo una respuesta inmediata, ya que lo que envío es precisamente un xml en forma de string. Pero al parecer hay algo que desconozco o quizás no sea la forma correcta de utilizar cuando ocupo
    $url = «https://laurldondeseejecuta?WSDL»;
    $client = new SoapClient($url);
    $client->lafuncionquetieneelwsdldondeapunto($param);
    por ejemplo con curl, $params es un string en forma de xml, pero cuando utilizo $param con el ejemplo $client->lafuncionquetieneelwsdldondeapunto($param); esto no es valido, y me pide declarar el «objeto» como si tuviera que crearlo antes como una clase… En tus ejemplo no veo que tenga que declarar una clase en php para «tags» de xml. Además me queda la duda, pues llevar en un array $params=[«dato que no es header => «algo»] no es una característica de Json?. Intente con la clase Soap Client hacer lo siguiente:
    $client = new SoapClient($wsdlURL);
    $header = new SoapHeader($wsdlURL, ‘UserCredentials’, $cred);
    $client->lafuncionquetieneelwsdldondeapunto($param,NULL,NULL,$header);
    Y aún así «error».
    Nunca he pensado que esto deba ser un «gran» trabajo en declarar clases y otras cuestiones, pero me cuesta entender la clase SoapClient cuando es necesario tener un header.
    Una vez más gracias por tu respuesta!

    1. Hola:

      ¿Cómo estás? Primero que nada, me alegro de que hayas resuelto tu problema :).

      Me resulta un poco raro que te haya ido mejor usando cURL «a pelo» que con las clases de SoapClient pero bueno… si funciona…

      Lo que veo que me produce una duda es el contenido de la variable $param en $client->lafuncionquetieneelwsdldondeapunto($param);.

      Si se trata de un objeto de tipo SimpleXMLElement es probable que tengas que convertirlo a un string (Usando el método asXML())… eso dependerá de lo que diga tu WSDL (Pero siendo que te funcionó bien enviando un string vía cURL me suena que viene por ahí el asunto).

      Me temo que no comprendí tu pregunta

      «llevar en un array $params=[“dato que no es header => “algo”] no es una característica de Json?»

      ¿Podrías elaborar un poco más?

      Entiendo que la autenticación se realiza a través de headers soap, ¿correcto? Porque si la autenticación se realiza por HTTP eso no te va a servir.

      Tal vez si publicas el mensaje de error te pueda ayudar mejor…

      El tema del header en el SoapClient no es algo muy común… publica mejor el código que usaste para conectarte con cURL y veré si puedo darte una versión que sólo use SoapClient.

      Saludos!

  6. Como podría enviar un xml SOAP con el header, en tu ejemplo no se construye esa parte ni se declara. Desde ya te agradezco si puedes orientarme.

    1. Hola Fernando:

      Si lo que estás intentando es comunicarte con un webservice de tipo SOAP te conviene usar las clases que vienen con PHP. Fíjate en este post.

      Me preguntas cualquier cosa. Exitos!

      1. Gracias por responder Mauro, lo que necesito es enviar archivos xml en formato wsdl a un web service soap, este es un ejemplo de un archivo de entrada (supongo como lo debo enviar) y su correspondiente respuesta
        https://sccnlpservices-piloto.dirtrab.cl/Servicios/Nombradas.asmx?op=registrarNombradas
        He probado con todos tus ejemplos, y con otros que no se construyen con la clase SoapClient, en este ultimo caso obtengo respuesta del servidor ocupando curl_init(), peo cuando llega a un campo con definición de entero, por ejemplo rutEmpresa me da este error:
        soap:ServerSystem.Web.Services.Protocols.SoapException: Server was unable to process request. —> System.NullReferenceException: Object reference not set to an instance of an object. at Services.RelacionLaboral.registrarContratos(Int32 rutEmpresa, Int32 idPuerto, List`1 contratos) in C:\VSTS-Apl-Agent\_work\48\s\SCCNLP_Services\Services\Servicios\RelacionLaboral.asmx.cs:line 44 — End of inner exception stack trace —
        Me parece que falta incorporar alguna definición de tipo del archivo xml antes de construirlo directamente… Te agradezco el tiempo que das para responder, saludos.

        1. No entendí bien lo que comentas en «lo que necesito es enviar archivos xml en formato wsdl a un web service soap», pero lo que veo es que el wsdl parece estar acá.

          Me imagino que lo que deberías hacer es crear un SOAPClient usando ese WSDL.

          Si usas este código podrás ver cómo usar todas las funciones del webservice:

          __getFunctions());

          Exitos!

¿Te quedó alguna duda? Publica aca tu pregunta

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