Search code examples
phpsoap-client

PHP Soap complex params with repeated keys


I need to generate the following XML

<SOAP-ENV:Envelope xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/" xmlns:ns1="http://services.bloomberg.com/datalicense/dlws/ps/20071001">
    <SOAP-ENV:Body>
        <ns1:submitGetHistoryRequest>
            <ns1:headers>
                <ns1:daterange>
                    <ns1:period>
                        <ns1:start>2018-05-08</ns1:start>
                        <ns1:end>2018-05-08</ns1:end>
                    </ns1:period>
                </ns1:daterange>
            </ns1:headers>
            <ns1:fields>
                <ns1:field>PX_LAST</ns1:field>
            </ns1:fields>
            <ns1:instruments>
                <ns1:instrument>
                    <ns1:id>US0000000002</ns1:id>
                    <ns1:yellowkey>Equity</ns1:yellowkey>
                    <ns1:type>ISIN</ns1:type>
                </ns1:instrument>
                <ns1:instrument>
                    <ns1:id>US0000000001</ns1:id>
                    <ns1:yellowkey>Equity</ns1:yellowkey>
                    <ns1:type>ISIN</ns1:type>
                </ns1:instrument>
            </ns1:instruments>
        </ns1:submitGetHistoryRequest>
    </SOAP-ENV:Body>
</SOAP-ENV:Envelope>

I use PHP 7.1.17 and SoapClient.

I can not pass options to SoapClient, as the instrument key is repeated and PHP's associated array can not have same key twice. I tried constructing object and setting instrument properties as SoapVar, but it generates incorrect XML. Here is code and result:

$options = new \stdClass();
$options->headers = new \stdClass();
$options->headers->daterange = new \stdClass();
$options->headers->daterange->period = new \stdClass();
$options->headers->daterange->period->start = '2018-05-08';
$options->headers->daterange->period->end = '2018-05-08';
$options->fields = new \stdClass();
$options->fields->field = 'PX_LAST';

//first instrument
$instrument = new \stdClass();
$instrument->id = 'US0000000002';
$instrument->type = 'ISIN';
$instrument->yellowkey = 'Equity';
$options->instruments[] = new \SoapVar(
    $instrument,
    SOAP_ENC_OBJECT,
    'stdClass',
    "http://soapinterop.org/xsd",
    "instrument"
);
//second instrument
$instrument = new \stdClass();
$instrument->id = 'US0000000001';
$instrument->type = 'ISIN';
$instrument->yellowkey = 'Equity';
$options->instruments[] = new \SoapVar(
    $instrument,
    SOAP_ENC_OBJECT,
    'stdClass',
    "http://soapinterop.org/xsd",
    "instrument"
);

<ns1:instruments/> remains empty in resulting XML:

<SOAP-ENV:Envelope xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/" xmlns:ns1="http://services.bloomberg.com/datalicense/dlws/ps/20071001">
    <SOAP-ENV:Body>
        <ns1:submitGetHistoryRequest>
            <ns1:headers>
                <ns1:daterange>
                    <ns1:period>
                        <ns1:start>2018-05-08</ns1:start>
                        <ns1:end>2018-05-08</ns1:end>
                    </ns1:period>
                </ns1:daterange>
            </ns1:headers>
            <ns1:fields>
                <ns1:field>PX_LAST</ns1:field>
            </ns1:fields>
            <ns1:instruments/>
        </ns1:submitGetHistoryRequest>
    </SOAP-ENV:Body>
</SOAP-ENV:Envelope>

How can I pass options to SoapClient, to generate XML with repeated instrument keys?


Solution

  • I solved this. Turns out when you pass an ordered array (not associative one) as object's property, as many XML nodes are generated using that property's name, as number of elements in the array. So instead of building SoapVar objects and putting them into instruments[] array, I just assigned array of instruments to instruments as instrument property:

    $options->instruments = new \stdClass();
    
    //first instrument
    $instrument = new \stdClass();
    $instrument->id = 'US0000000002';
    $instrument->type = 'ISIN';
    $instrument->yellowkey = 'Equity';
    
    $options->instruments->instrument[] = $instrument;
    
    //second instrument
    $instrument = new \stdClass();
    $instrument->id = 'US0000000001';
    $instrument->type = 'ISIN';
    $instrument->yellowkey = 'Equity';
    
    $options->instruments->instrument[] = $instrument;
    

    I build instruments in a foreach loop. Btw converting array to object instead of building stdClass also works fine:

    $options->instruments->instrument[] = (object)[
        'id' => 'US0000000001',
        'type' => 'ISIN',
        'yellowkey' => 'Equity'
    ]