Warning: object references affect XML serialization
Let’s immediately start with a quote I found on php.net:
One of the key-point of PHP5 OOP that is often mentioned is that “objects are passed by references by default” This is not completely true. This section rectifies that general thought using some examples.
A PHP reference is an alias, which allows two different variables to write to the same value. As of PHP5, an object variable doesn’t contain the object itself as value anymore. It only contains a object identifier which allows object accessors to find the actual object. When an object is sent by argument, returned or assigned to another variable, the different variables are not aliases: they hold a copy of the identifier, which points to the same object.
The problem
Most of you already know that and no longer consider it a ‘novelty’, but in some cases it can catch you off guard. A while ago one of my colleagues was struggeling with complex input types while calling an ASP.NET powered SOAP service. He was convinced that the object he passed to a method was correct, but still the webservice threw a SOAPFault. Apparently the XML serialization of the object did not correspond to the one that was defined in the WSDL. After a while he started debugging the XML request that was sent to the .NET webservice and he noticed that the PHP SOAPClient class replaced some XML nodes with references to a previously defined node. So apparently XML serialization takes object references into account.
The goal
Because the actual code my colleague wrote is too complicated and is company property, I’ve designed a similar setup that illustrates my point.
My primary goal is to demonstrate how object references affect XML serialization. If the XML structure is altered due to object referencing, the XML will still remain valid, but will no longer correspond to the complete notation which might be required by the service at the other end. In PHP this doesn’t trigger compliancy errors, but on other platforms (such as ASP.NET) it might.
My secondary goal is of course to show the fix for this problem by keeping the concept of object referencing in mind.
The setup
Our setup consists of 2 classes:
- Class A which has a public property called memberOne. It has 5 as a default value
- Class B which has 2 public properties called memberOne and memberTwo.

We create an instance of class A and store it in variable $a. The property memberOne contains as expected the value 5. We also create an instance of class B and store it in variable $b. We assign variable $a to public property memberOne as well as public property memberTwo. As you can see in the diagram, both members contain the same identifier that points to a single object.
The XML
When we serialize the object value of variable $b to XML, we get the following output:
<?xml version="1.0" encoding="UTF-8"?> <memberOne id="ref1"> <memberOne>5</memberOne> </memberOne> <memberTwo href="#ref1"/>
Instead of just storing the data of memberTwo as a copy, the XML represents it with a hyperlink. So in fact the XML serializer does what PHP does in the background with objects. If this causes compliancy issues with a webservice we’ll need to find a way to represent the object in XML without making references
The solution
My colleague got to the bottom of things by taking the PHP 5 object referencing theory as a guideline. If the problem can be explained with this theory, it can be solved with the exact same theory. So instead of referring to a single object twice, we need to have 2 objects that are referenced to by each datamember of object b individually.
It would be easy to make a new instance of the same class to solve the problem, but in a lot of cases we don’t want to re-assign all values to this object. My colleague didn’t want this either because the object was quite complex and contained nested order information.

The real solution is object cloning. The clone keyword triggers a set of actions that copies the object in a separate slot. The content of the cloned object is identical, but the reference differs.
The XML (again)
When we re-serialize object b we get the full XML
<?xml version="1.0" encoding="UTF-8"?> <memberOne> <memberOne>5</memberOne> </memberOne> <memberTwo> <memberOne>5</memberOne> <memberTwo>
The script
A nice little testing script is in order here. When you run it, you’ll see both the XML representation when using referencing and cloning
The actual script
Don’t forget the replace the webservice location with the URL of the dummy SOAP server script
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 | <?php /** * Class A will be used to link to both data members of class B * */ class A { public $memberOne = 5; } /** * Has two * */ class B { public $memberOne; public $memberTwo; } /** * Class init */ $a = new A; $b = new B; /** * First assignment of object '$a' */ $b->memberOne = $a; /** * Second assignment of object '$a' * Links to the same identifier as 'memberOne' */ $b->memberTwo = $a; /** * Perform dummy SOAP call */ $client = new SoapClient(null, array('trace'=>true, 'location' => "http://myserver/soap.php", 'uri'=> "http://test-uri/")); $client->dummy($b); /** * Print serialized XML with references */ echo $client->__getLastRequest(); /** * Instead of linking to the same object, we clone object '$a' * This will store the object in a separate memory space */ $b->memberTwo = clone $a; /** * Perform a second dummy SOAP call with the improved object */ $client->dummy($b); /** * Print serialized XML without references */ echo $client->__getLastRequest(); |
SOAP server used in the test
Since we got to the issue by performing SOAP calls, this example should involve SOAP communication as well. This script is the SOAP server that is used in the main testing script. When you check it out, you see that this is an absolute dummy service. But that’s not important because it’s the XML request that matters.
1 2 3 4 5 6 7 8 9 10 | <?php function dummy($x) { return true; } $server = new SoapServer(null, array('uri' => "http://test-uri/")); $server->addFunction("dummy"); $server->handle(); |
November 2nd, 2009 at 5:40 am
Thanks heaps, this helped me solve my problems after an upgrade from php 5.1 to 5.2. In 5.1, the SOAP module did not have this behaviour.