php - simplexmlelement - ¿Cómo devuelve una respuesta XML personalizada en la respuesta de soapServer?
soap xml php (1)
Estoy configurando un servicio web SOAP que toma entrada XML y tiene que devolver resultados XML personalizados. Todo esto se define en un WSDL. Aplico soapServer para esto (hasta que alguien dice que tiene errores que me impiden alcanzar mi objetivo :-)).
Todavía no he podido devolver XML personalizado: obtengo un resultado que parece estar basado en WSDL, con un nombre de elemento raíz estándar igual al XML de entrada más "Respuesta". En realidad, eso también me sorprende, así que como una pregunta complementaria me pregunto por qué es eso y si se puede influir. Por supuesto, es agradable que las definiciones WSDL se usen de alguna manera cuando se crean las respuestas, pero como dije, no sé cómo obtener un XML personalizado en la respuesta.
Llegué tan lejos como esto:
WSDL
<?xml version="1.0" encoding="UTF-8"?>
<definitions
xmlns="http://schemas.xmlsoap.org/wsdl/"
xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/"
xmlns:tns="http://pse/"
xmlns:xs="http://www.w3.org/2001/XMLSchema"
name="PSE"
targetNamespace="http://pse/">
<types>
<xs:schema>
<xs:import namespace="http://pse/" schemaLocation="PSE.xsd"/>
</xs:schema>
</types>
<message name="MI102Req">
<part name="cdhead" type="tns:cdhead_T"/>
<part name="instr" type="tns:instr_T"/>
</message>
<message name="Res">
<part name="cdhead" type="tns:cdhead_T"/>
</message>
<portType name="MIPortType">
<operation name="mi102">
<input message="tns:MI102Req"/>
<output message="tns:Res"/>
</operation>
</portType>
<binding name="MIBinding" type="tns:MIPortType">
<soap:binding style="rpc" transport="http://schemas.xmlsoap.org/soap/http"/>
<operation name="mi102">
<soap:operation soapAction="http://www.testURL/test_soap.php#mi102"/>
<input>
<soap:body use="literal" namespace="http://pse/"/>
</input>
<output>
<soap:body use="literal" namespace="http://pse/"/>
</output>
</operation>
</binding>
<service name="PSE">
<port name="MIPortType" binding="tns:MIBinding">
<soap:address location="http://www.testURL/test_soap.php"/>
</port>
</service>
</definitions>
XML de entrada
<Envelope xmlns="http://schemas.xmlsoap.org/soap/envelope/">
<Body>
<mi102 xmlns="http://pse">
<cdhead version="13"/>
<instr/>
</mi102>
</Body>
</Envelope>
php actual
<?php
class PSE {
function mi102 ($stdClassInput) {
$inp = file_get_contents (''php://input'');
$xml = simplexml_load_string ($inp); // Envelope
$ch = $xml -> children ();
$elt1 = $ch [0]; // Body
$ch = $elt1 -> children ();
$elt2 = $ch [0]; //mi102
$xslt = new XSLTProcessor();
$xslt -> registerPHPFunctions();
$xslt -> importStylesheet ( DOMDocument::load (''test.xslt'') );
$dom = $xslt -> transformToDoc (DOMDocument::loadXML ($elt2 -> asXML()));
$result = new SoapVar ($dom -> saveXML(), XSD_ANYXML);
return ($result);
}
}
ini_set( "soap.wsdl_cache_enabled", "0");
$server = new SoapServer ("test.wsdl");
$server -> setClass (''PSE'');
$server -> setObject (new PSE());
$server -> handle();
?>
XSLT utilizado anteriormente es solo un cambio de un atributo, y la temperatura cambia el nombre de la raíz al que devuelve el servidor siempre (por si acaso :-))
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:pse="http://pse">
<xsl:output method="xml" version="1.0" encoding="UTF-8" indent="yes"/>
<xsl:template match="/">
<xsl:apply-templates/>
</xsl:template>
<xsl:template match="pse:mi102">
<mi102Response>
<xsl:apply-templates/>
</mi102Response>
</xsl:template>
<xsl:template match="pse:cdhead">
<xsl:copy>
<xsl:apply-templates select="@*"/>
</xsl:copy>
</xsl:template>
<xsl:template match="@*">
<xsl:copy/>
</xsl:template>
<xsl:template match="@version">
<xsl:attribute name="version">14</xsl:attribute>
</xsl:template>
<xsl:template match="*"/>
</xsl:stylesheet>
Espero que el XML devuelto sea algo así como
<SOAP-ENV:Envelope xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/" xmlns:ns1="http://pse/">
<SOAP-ENV:Body>
<ns1:mi102Response>
<cdhead version="14"/>
</ns1:mi102Response>
</SOAP-ENV:Body>
</SOAP-ENV:Envelope>
Pero en cambio es
<SOAP-ENV:Envelope xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/" xmlns:ns1="http://pse/">
<SOAP-ENV:Body>
<ns1:mi102Response/>
</SOAP-ENV:Body>
</SOAP-ENV:Envelope>
La depuración de los contenidos $ dom en el php anterior muestra exactamente el XML que trato de devolver (envuelto en Soap Envelope / Body, por supuesto, al igual que el de entrada):
<?xml version="1.0" encoding="UTF-8"?>
<mi102Response xmlns:pse="http://pse">
<cdhead xmlns="http://pse" version="14"/>
</mi102Response>
¿Dónde me equivoco? ¿Cómo obtener un XML personalizado en el contenido de respuesta HTTP devuelto?
¡Uf!
Esto me llevó varios intentos y buscar en Google hasta que descubrí lo que está mal.
Creo que se puede clasificar como un error en SoapVar
.
Descubrí que, si bien SoapVar es perfectamente capaz de analizar una cadena XML, no puede hacerlo si la cadena contiene una declaración XML como <?xml version="1.0" encoding="UTF-8"?>
. Entonces, cuando tiene un DOMDocument
o un SimpleXMLElement
, primero tiene que quitar la declaración antes de analizar la cadena mediante SoapVar.
para un DOMDocument
esto se puede hacer aplicando saveXML
con un parámetro igual a una variable DOMNode
construida a partir de la propia dom, eligiendo cualquier nodo, pero normalmente este será el nodo raíz, por supuesto.
En mi servidor php, agregué lo siguiente:
$nodes = $dom -> getElementsByTagName (''cdhead'');
$node = $nodes -> item(0);
$result = new SoapVar ($dom -> saveXML($node), XSD_ANYXML);
Y ahora mi resultado está bien:
<SOAP-ENV:Envelope xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/" xmlns:ns1="http://pse/">
<SOAP-ENV:Header/>
<SOAP-ENV:Body>
<ns1:mi102Response>
<cdhead version="14"/>
</ns1:mi102Response>
</SOAP-ENV:Body>
</SOAP-ENV:Envelope>
En cuanto al nombre raíz del XML devuelto, estoy seguro de que encontraré la forma de cambiarlo a lo que yo quiera (¡en lugar de la respuesta estándar de mi102 generada por SoapVar)!