Cómo enviar XML a un WebService con PHP

Inicio / Cómo hacer para... / 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();

$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.).

Conclusión

El intercambio de XML usando PHP no es en sí mismo complejo, lo único con lo que debes tener cuidado es que el texto que estés enviando sea efectivamente XML… y para ello, nada mejor que SimpleXMLElement 🙂

Si te interesa conocer mejor el manejo de WebServices con PHP este curso te puede ayudar.

mchojrin

Director Académico y Docente at Leeway Academy
Hola! Soy Mauro Chojrin, estudié la Lic. en Ciencias de la Computación en la Universidad de Buenos Aires.

Me desempeño como docente de programación desde el año 1997.

Pasé por diferentes instituciones (Escuela Técnica ORT, Digital House, EducacionIT, ITMaster, Escuela DaVinci entre otros).

Actualmente coordino los cursos dictados en Leeway Academy y desarrollo sistemas usando PHP y framework Symfony

6 comentarios

  • Fernando

    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.

    • 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!

      • Fernando

        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.

        • 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:

          < ?php $client = new SoapClient('https://sccnlpservices-piloto.dirtrab.cl/Servicios/Nombradas.asmx?WSDL'); var_dump($client->__getFunctions());

          Exitos!

  • Fernando

    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!

    • 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!

Deja un comentario

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

A %d blogueros les gusta esto: