Skip to content
Advertisement

Associative array key becomes param in XML when passed to SoapClient

I have an associative array:

$params = [
  'trid' => null,
  'merchantCode' => null,
  'paymentMethod' => null,
  'returnUrl' => site_url(null),
  'notificationUrl' => site_url(null),
  'language' => 'null',
  'currency' => 'null',
  'isTestMode' => false,
  'productsXml' => null,
  'needInvoice' => true,

(Nulls hold actual values in reality)

When I check __getLastRequest I completly lose the keys in the generated XML.

SoapClient,

    $soap = new SoapClient(null, $options);
    $soap->__soapCall('Request', $params);
    $request = $soap->__getLastRequest();

Please note that I have a NON WSDL connection to the endpoint.

The generated XML will have a field value of <param[keyNum] xsi:type="xsd:typeOfVariable"> instead of having the actual key value from the associative array, <trid> for example.

<SOAP-ENV:Body><ns1:Request><param0 xsi:type="xsd:string">null</param0><param1 xsi:type="xsd:string">null</param1></Request>

Advertisement

Answer

Contrary to what the PHP manual says, it seems arguments is never interpreted as an associative array.

If we try this simple test:

<?php
$options = ['location'=>'http://localhost/unknown', 'uri'=>'test', 'trace'=>true];
$soap = new SoapClient(null, $options);
try
{
    $soap->__soapCall('Request', ['foo'=>1, 'bar'=>2]);
}
catch(SoapFault $e)
{
    header('Content-type: text/xml');
    echo $soap->__getLastRequest();
}
?>

we get:

<SOAP-ENV:Envelope xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/" xmlns:ns1="test" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:SOAP-ENC="http://schemas.xmlsoap.org/soap/encoding/" SOAP-ENV:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/">
    <SOAP-ENV:Body>
        <ns1:Request>
            <param0 xsi:type="xsd:int">1</param0>
            <param1 xsi:type="xsd:int">2</param1>
        </ns1:Request>
    </SOAP-ENV:Body>
</SOAP-ENV:Envelope>

We see that the parameter names are ignored.

Now if we try what the other answer suggests (wrap the array in another array):

<?php
$options = ['location'=>'http://localhost/unknown', 'uri'=>'test', 'trace'=>true];
$soap = new SoapClient(null, $options);
try
{
    $soap->__soapCall('Request', [['foo'=>1, 'bar'=>2]]);
}
catch(SoapFault $e)
{
    header('Content-type: text/xml');
    echo $soap->__getLastRequest();
}
?>

we get:

<SOAP-ENV:Envelope xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/" xmlns:ns1="test" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:ns2="http://xml.apache.org/xml-soap" xmlns:SOAP-ENC="http://schemas.xmlsoap.org/soap/encoding/" SOAP-ENV:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/">
    <SOAP-ENV:Body>
        <ns1:Request>
            <param0 xsi:type="ns2:Map">
                <item>
                    <key xsi:type="xsd:string">foo</key>
                    <value xsi:type="xsd:int">1</value>
                </item>
                <item>
                    <key xsi:type="xsd:string">bar</key>
                    <value xsi:type="xsd:int">2</value>
                </item>
            </param0>
        </ns1:Request>
    </SOAP-ENV:Body>
</SOAP-ENV:Envelope>

We see that there is a single parameter whose type is Map. That’s not what we want.

The solution is to use the SoapParam class:

<?php
$options = ['location'=>'http://localhost/unknown', 'uri'=>'test', 'trace'=>true];
$soap = new SoapClient(null, $options);
try
{
    $soap->__soapCall('Request', [new SoapParam(1, 'foo'), new SoapParam(2, 'bar')]);
}
catch(SoapFault $e)
{
    header('Content-type: text/xml');
    echo $soap->__getLastRequest();
}
?>

Now we get what we wanted:

<SOAP-ENV:Envelope xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/" xmlns:ns1="test" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:SOAP-ENC="http://schemas.xmlsoap.org/soap/encoding/" SOAP-ENV:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/">
    <SOAP-ENV:Body>
        <ns1:Request>
            <foo xsi:type="xsd:int">1</foo>
            <bar xsi:type="xsd:int">2</bar>
        </ns1:Request>
    </SOAP-ENV:Body>
</SOAP-ENV:Envelope>

Note that you can easily convert an associative array to SOAP parameters like this:

function getSoapParams($params)
{
    $a = [];
    foreach($params as $name=>$value)
        $a[] = new SoapParam($value, $name);
    return $a;
}
User contributions licensed under: CC BY-SA
6 People found this is helpful
Advertisement