question

Upvotes
Accepted
301 7 22 30

PHP SOAP and XML mixed content (empty elements in XML requests)

While using PHP SOAP as a framework for my PHP TRKD client (this is the approach that was implemented in the PHP coding samples) I noticed that some elements from the request object were not serialized properly.

(We noticed this mostly frequently with the filter for the News service.) Here is an example below.

This object

WebServices_News1_RetrieveHeadlineML1Request Object

(

[HeadlineMLRequest] => WebServices_News1_HeadlineMLRequest Object

(

[TimeOut] =>

[MaxCount] => 10

[StartTime] => 2010-11-24T13:01:41

[EndTime] => 2010-11-25T13:01:41

[Filter] => Array ( [MetaDataConstraint] => Array

(

[class] => topics

[Value] => GEG

)

)

)

)

would surprisingly serialize into an XML request with an empty Value element:

<env:Body>

<ns1:RetrieveHeadlineML_Request_1>

<ns1:HeadlineMLRequest>

<ns1:TimeOut>0</ns1:TimeOut>

<ns1:MaxCount>10</ns1:MaxCount>

<ns1:StartTime>2010-11-24T13:01:41</ns1:StartTime>

<ns1:EndTime>2010-11-25T13:01:41</ns1:EndTime>

<ns1:Filter>

<ns2:MetaDataConstraint> <ns2:Value/> </ns2:MetaDataConstraint>

</ns1:Filter>

</ns1:HeadlineMLRequest>

</ns1:RetrieveHeadlineML_Request_1>

</env:Body>

How can we work around this issue?

rkd-apirkdsoap-apiphpxml
icon clock
10 |1500

Up to 2 attachments (including images) can be used with a maximum of 512.0 KiB each and 1.0 MiB total.

1 Answer

Upvotes
Accepted
791 10 8 14

The reason that this happens is that PHP SOAP is a simple framework that is typically bundled with the PHP installation and is designed for quick and easy implementation of web-service clients. It's based on a lightweight and simple XML parser - libxml. Because of libxml's simplicity it doesn't support most of XML's sophisticated features, including complex types among which there's the mixed content type. And the Value element is defined exactly as mixed content in the News filter's XML Schema.

Here is a workaround for this:

Fortunately, PHP SOAP allows to explicitly inject XML snippet into requests as SoapVar objects. To fill in the Value element from the aforementioned example you'll need to create a SoapVar object with the bare XML of this element and set it instead of the elements actual value:

$value = new SoapVar('<ns2:Value>GEG</ns2:Value>', XSD_ANYXML);

$requestor->setFilter(Array("MetaDataConstraint"=>Array("class"=>"topics", "Value"=>$value)));

Important: keep in mind that since you inject XML explicitly, you must not miss any namespace prefixes on the injected elements, and the prefixes must comply to the prefix declarations that may occur earlier in the XML document. In this example it's assumed that the ns2 prefix is the prefix that has been bound to http://schemas.reuters.com/ns/2006/04/14/rmds/webservices/news/filter in the root element of the request's XML.

This can be checked by dumping or catching the request that is generated by the application. If you are not confident about what prefixes to set, you can still explicitly declare all necessary namespaces in the XML snippet itself with the xmlns attribute: $value = new SoapVar('<Value xmlns="http://schemas.reuters.com/ns/2006/04/14/rmds/webservices/news/filter">GEG<Value>;', XSD_ANYXML);

icon clock
10 |1500

Up to 2 attachments (including images) can be used with a maximum of 512.0 KiB each and 1.0 MiB total.