[php] Creating a SOAP call using PHP with an XML body

I'm trying to call a SOAP method using PHP.

Here's the code I've got:

$data = array('Acquirer' =>
  array(
    'Id' => 'MyId',
    'UserId' => 'MyUserId',
    'Password' => 'MyPassword'
  ));
$method = 'Echo';
$client = new SoapClient(NULL,
           array('location' => 'https://example.com/ExampleWebServiceDL/services/ExampleHandler', 
           'uri' => 'http://example.com/wsdl', 'trace' => 1));
$result = $client->$method($data);

Here's the request it creates:

  <?xml version="1.0" encoding="UTF-8"?>
  <SOAP-ENV:Envelope SOAP-ENV:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/" xmlns:SOAP-ENC="http://schemas.xmlsoap.org/soap/encoding/" xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/" xmlns:ns1="http://example.com/wsdl" xmlns:ns2="http://xml.apache.org/xml-soap" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
    <SOAP-ENV:Body>
      <ns1:Echo>
        <param0 xsi:type="ns2:Map">
          <item>
            <key xsi:type="xsd:string">Acquirer</key>
            <value xsi:type="ns2:Map">
              <item>
                <key xsi:type="xsd:string">Id</key>
                <value xsi:type="xsd:string">mcp</value>
              </item>
              <item>
                <key xsi:type="xsd:string">UserId</key>
                <value xsi:type="xsd:string">tst001</value>
              </item>
              <item>
                <key xsi:type="xsd:string">Password</key>
                <value xsi:type="xsd:string">test</value>
              </item>
            </value>
          </item>
        </param0>
      </ns1:Echo>
    </SOAP-ENV:Body>
  </SOAP-ENV:Envelope>

And here's what I want the request to look like:

  <?xml version="1.0" encoding="UTF-8"?>
  <SOAP-ENV:Envelope SOAP-ENV:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/" xmlns:SOAP-ENC="http://schemas.xmlsoap.org/soap/encoding/" xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/" xmlns:ns1="http://example.com/wsdl" xmlns:ns2="http://xml.apache.org/xml-soap" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
    <SOAP-ENV:Body>
      <Echo>
        <Acquirer>
          <Id>MyId</Id>
          <UserId>MyUserId</UserId>
          <Password>MyPassword</Password>
        </Acquirer>
      </Echo>
    </SOAP-ENV:Body>
  </SOAP-ENV:Envelope>

This question is related to php soap

The answer is


There are a couple of ways to solve this. The least hackiest and almost what you want:

$client = new SoapClient(
    null,
    array(
        'location' => 'https://example.com/ExampleWebServiceDL/services/ExampleHandler',
        'uri' => 'http://example.com/wsdl',
        'trace' => 1,
        'use' => SOAP_LITERAL,
    )
);
$params = new \SoapVar("<Acquirer><Id>MyId</Id><UserId>MyUserId</UserId><Password>MyPassword</Password></Acquirer>", XSD_ANYXML);
$result = $client->Echo($params);

This gets you the following XML:

<?xml version="1.0" encoding="UTF-8"?>
<SOAP-ENV:Envelope xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/" xmlns:ns1="http://example.com/wsdl">
    <SOAP-ENV:Body>
        <ns1:Echo>
            <Acquirer>
                <Id>MyId</Id>
                <UserId>MyUserId</UserId>
                <Password>MyPassword</Password>
            </Acquirer>
        </ns1:Echo>
    </SOAP-ENV:Body>
</SOAP-ENV:Envelope>

That is almost exactly what you want, except for the namespace on the method name. I don't know if this is a problem. If so, you can hack it even further. You could put the <Echo> tag in the XML string by hand and have the SoapClient not set the method by adding 'style' => SOAP_DOCUMENT, to the options array like this:

$client = new SoapClient(
    null,
    array(
        'location' => 'https://example.com/ExampleWebServiceDL/services/ExampleHandler',
        'uri' => 'http://example.com/wsdl',
        'trace' => 1,
        'use' => SOAP_LITERAL,
        'style' => SOAP_DOCUMENT,
    )
);
$params = new \SoapVar("<Echo><Acquirer><Id>MyId</Id><UserId>MyUserId</UserId><Password>MyPassword</Password></Acquirer></Echo>", XSD_ANYXML);
$result = $client->MethodNameIsIgnored($params);

This results in the following request XML:

<SOAP-ENV:Envelope xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/">
    <SOAP-ENV:Body>
        <Echo>
            <Acquirer>
                <Id>MyId</Id>
                <UserId>MyUserId</UserId>
                <Password>MyPassword</Password>
            </Acquirer>
        </Echo>
    </SOAP-ENV:Body>
</SOAP-ENV:Envelope>

Finally, if you want to play around with SoapVar and SoapParam objects, you can find a good reference in this comment in the PHP manual: http://www.php.net/manual/en/soapvar.soapvar.php#104065. If you get that to work, please let me know, I failed miserably.


First off, you have to specify you wish to use Document Literal style:

$client = new SoapClient(NULL, array(
    'location' => 'https://example.com/path/to/service',
    'uri' => 'http://example.com/wsdl',
    'trace' => 1,
    'use' => SOAP_LITERAL)
);

Then, you need to transform your data into a SoapVar; I've written a simple transform function:

function soapify(array $data)
{
        foreach ($data as &$value) {
                if (is_array($value)) {
                        $value = soapify($value);
                }
        }

        return new SoapVar($data, SOAP_ENC_OBJECT);
}

Then, you apply this transform function onto your data:

$data = soapify(array(
    'Acquirer' => array(
        'Id' => 'MyId',
        'UserId' => 'MyUserId',
        'Password' => 'MyPassword',
    ),
));

Finally, you call the service passing the Data parameter:

$method = 'Echo';

$result = $client->$method(new SoapParam($data, 'Data'));