At 02:26 AM 01/21/2006, Peter Guy wrote:
Nice Katy!
Overloading the SoapClient object is a very elegant workaround.
I'm assuming that every call to SoapClient->[specific web method] eventually
goes through __doRequest()?
For example, the invocation:
$client = new DotNetSoapClient($wsdl);
$client->MyMethod($param1, $param2);
will eventually trigger the overloaded __doRequest() method?
That is correct, when you override __doRequest() it will be executed
whenever you make a SOAP method call. You can then modify the payload with
DOM or your preferred method. You just have to remember to call
parent::__doRequest at the end to make sure the request is actually sent
over the wire.
I don't think I'd recommend removing the xsi:type attributes, as they may be
needed by the server, but you certainly could replace them with the
"correct" type definition ("SOAP-ENC:Array").
I reviewed the SOAP 1.1 documentation at:
http://www.w3.org/TR/2000/NOTE-SOAP-20000508/#_Toc478383512
The examples given in section 5.4.2 (encoded arrays) make no mention of
supplying the xsi:type attribute even though it does say that the type is
SOAP-ENC:Array - although I only skimmed the docs quickly so I might have
missed something. In any case, .NET seems to accept
xsi:type="SOAP-ENC:Array" quite happily, so I would tend to agree with you.
The implementation would be a
bit fragile, as it would rely in PHP always using the string "SOAP-ENC" as
the local name for the soap encoding namespace, but that's probably
something upon which we can rely.
There is no need to take such a risk, it is very easy to find the right
namespace prefix:
$dom = DOMDocument::loadXML($request);
$soapencPrefix =
$dom->lookupPrefix('http://schemas.xmlsoap.org/soap/encoding/');
Of course, the workaround I posted also relies on xsi and SOAP-ENV being
defined, so if you are concerned that the namespace prefixes generated by
PHP might change, create a lookup table and use that instead:
public function __doRequest($request, $location, $action, $version)
{
// Namespace URIs and prefixes
$nsURI = array( 'env' =>
'http://schemas.xmlsoap.org/soap/envelope/',
'enc' =>
'http://schemas.xmlsoap.org/soap/encoding/',
'xsi' =>
'http://www.w3.org/2001/XMLSchema-instance');
$nsPrefix = array();
// Load request
$dom = DOMDocument::loadXML($request);
$xpath = new DOMXPath($dom);
// Find namespace prefixes
foreach ($nsURI as $identifier => $namespaceURI)
$nsPrefix[$identifier] = $dom->lookupPrefix($namespaceURI);
// Array serialisation: remove the xsi:type attribute from all
arrays encoded using SOAP-ENC:arrayType
// as this causes an "no type associated with Xml key"-type SOAP
server error in .NET
$nodes =
$xpath->query("/{$nsPrefix['env']}:Envelope/{$nsPrefix['env']}:Body/*"
. "//*[@{$nsPrefix['enc']}:arrayType and
@{$nsPrefix['xsi']}:type]");
foreach ($nodes as $node)
$node->setAttributeNS($nsURI['xsi'],
"{$nsPrefix['xsi']}:type", "{$nsPrefix['enc']}:Array");
$request = $dom->saveXML();
return parent::__doRequest($request, $location, $action,
$version);
}
This will break if you try it on a SOAP 1.2 payload, at least because the
SOAP-ENV namespace is different, and probably for other reasons too :-)
Katy.
-Peter