php.net |  support |  documentation |  report a bug |  advanced search |  search howto |  statistics |  random bug |  login
Doc Bug #41490 xsd:choice with maxOccurs="unbounded" does not preserve element order
Submitted: 2007-05-24 13:36 UTC Modified: 2007-08-17 08:05 UTC
Votes:3
Avg. Score:4.0 ± 0.8
Reproduced:3 of 3 (100.0%)
Same Version:1 (33.3%)
Same OS:2 (66.7%)
From: simon at highlyillogical dot org Assigned:
Status: Closed Package: Documentation problem
PHP Version: 5.2.2 OS: Windows/Linux
Private report: No CVE-ID: None
 [2007-05-24 13:36 UTC] simon at highlyillogical dot org
Description:
------------
When the PHP soap client parses a SOAP response that contains multiple recurrences of an <xsd:choice maxOccurs="unbounded"> element, the ordering of elements can be lost, as the results are grouped by element type.

For example, take the following complex type:
<xsd:complexType name="sentence">
  <xsd:choice maxOccurs="unbounded">
    <xsd element name="noun" type="xsd:string" />
    <xsd element name="verb" type="xsd:string" />
    <xsd element name="other" type="xsd:string" />
  </xsd:choice>
</xsd:type>

This type can describe a sentence, with one element per word. An example sentence might be:
<sentence>
  <other>The</other>
  <noun>cat</noun>
  <other>is</other>
  <verb>playing</verb>
  <other>with</other>
  <noun>string</noun>
</sentence>

When PHP parses this, it groups the elements by type. Thus, a sentence which reads "The cat is playing with string", is mangled to read "cat string playing The is with"

I've posted a wsdl to demonstrate the problem at:
http://zx81.highlyillogical.org/~simon/phpbugtest/phpbugtest.wsdl

A valid XML response similar to the case above is at:
http://zx81.highlyillogical.org/~simon/phpbugtest/bugtest.xml

To reproduce the problem, simply call the bugtest operation on the above wsdl. It will always return the above xml response.

Reproduce code:
---------------
$client = new SoapClient ( "http://zx81.highlyillogical.org/~simon/phpbugtest/phpbugtest.wsdl" ) ;

// we're just hitting an XML file, so we don't care about the input
$result = $client->bugtest ( ) ; 

var_dump ( $result ) ;

Expected result:
----------------
An output with the resulting objects are presented in the order in which they occurred in the document.

Actual result:
--------------
object(stdClass)#2 (3) {
  ["noun"]=>
  array(2) {
    [0]=>
    string(3) "cat"
    [1]=>
    string(6) "string"
  }
  ["verb"]=>
  string(7) "playing"
  ["other"]=>
  array(3) {
    [0]=>
    string(3) "The"
    [1]=>
    string(2) "is"
    [2]=>
    string(4) "with"
  }
}

Patches

Add a Patch

Pull Requests

Add a Pull Request

History

AllCommentsChangesGit/SVN commitsRelated reports
 [2007-06-14 07:27 UTC] dmitry@php.net
I don's see a way to fix this. PHP is not able to keep several elemtns with the same names in object or array. So it is not possible to keep order of elements with different names.

You can probaly change WSDL to have a sequence of 'words', where each word is an element defined as extension of type 'string' with 'part_of_speach' attribute. So you'll have to pass something like this:

<sentence>
  <word part_of_speach="other">The</word>
  <word part_of_speach="noun">cat</word>
  <word part_of_speach="other">is</word>
  <word part_of_speach="verb">playing</word>
  <word part_of_speach="other">with</word>
  <word part_of_speach="noun">string</word>
</sentence>

 [2007-06-14 09:58 UTC] simon at highlyillogical dot org
In practice, that's not possible. The 'words' example is a much-simplified example of what I want to happen. In reality my document is a sequence of objects of different types, but must remain in the correct order.

Think of an XHTML document. It's valid XML and could theoretically be used in a similar way. However, in such a document you've got no idea whether a <p> element, a <ul> element, a <div>, a <form>, or a <table> is coming next... But they do have to have the order retained. If you globbed all of the paragraps together, with all of the unordered lists after, you'd end up with something that didn't make much sense.

In fact, if you look at the schema for XHTML (http://www.w3.org/TR/xhtml1-schema/#xhtml1-strict) you can see that it uses exactly this mechanism (xs:choice maxOccurs="unbounded") in order to implement this. (See the "Block" complexType) -- So, if you want to parse soap messages with an XHTML payload correctly, a solution to this problem needs to be found.
 [2007-06-18 14:57 UTC] dmitry@php.net
You can access element order using the following trick.

<?php
$wsdl = "http://zx81.highlyillogical.org/~simon/phpbugtest/phpbugtest.wsdl";
$options=Array(
		'typemap' => array(array("type_ns"   => "http://zx81.highlyillogical.org/~simon/phpbugtest",
		                         "type_name" => "sentence",
		                         "from_xml"  => "sentence_from_xml"))
		);

function sentence_from_xml($x) {
	return simplexml_load_string($x);
}

$client = new SoapClient($wsdl, $options) ;
$result = $client->bugtest(); 
var_dump($result);
foreach ($result as $key => $val) {
	echo $key . ":" . (string)$val . "\n";
}

Actual result:
--------------
object(SimpleXMLElement)#2 (3) {
  ["other"]=>
  array(3) {
    [0]=>
    string(3) "The"
    [1]=>
    string(2) "is"
    [2]=>
    string(4) "with"
  }
  ["noun"]=>
  array(2) {
    [0]=>
    string(3) "cat"
    [1]=>
    string(6) "string"
  }
  ["verb"]=>
  string(7) "playing"
}
other:The
noun:cat
other:is
verb:playing
other:with
noun:string

 [2007-06-26 01:00 UTC] php-bugs at lists dot php dot net
No feedback was provided for this bug for over a week, so it is
being suspended automatically. If you are able to provide the
information that was originally requested, please do so and change
the status of the bug back to "Open".
 [2007-06-28 08:31 UTC] simon at highlyillogical dot org
Yeah, I'm happy to write my own serialisers and deserialisers, to work around the problem. (Although it's time consuming -- I don't have to do this in other languages) 

That 'typemap' trick doesn't seem to be documented anywhere or I'd have gone with it straight away.

I haven't made it work in practice yet, but it looks like it should work. I can implement my own complex classes and ensure that I (de)serialize everything in the correct order.
 [2007-06-28 10:19 UTC] dmitry@php.net
There is no way to automatic preserve order of objects without breaking compatibility, however as I showed, it is possible to do this with sereral lines of additional code.

So nothing to fix (except documentation).
 [2007-08-17 08:05 UTC] vrana@php.net
This bug has been fixed in the documentation's XML sources. Since the
online and downloadable versions of the documentation need some time
to get updated, we would like to ask you to be a bit patient.

Thank you for the report, and for helping us make our documentation better.

"The typemap option is an array of type mappings. Type mapping is an array with keys type_name, type_ns (namespace URI), from_xml  (callback accepting one string parameter) and to_xml  (callback accepting one object parameter)."
 
PHP Copyright © 2001-2024 The PHP Group
All rights reserved.
Last updated: Sat May 18 21:01:33 2024 UTC