Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Class to turn PHP objects into XML

Tags:

oop

php

xml

I have the following class that I have written. Seems to work OK with simple objects that have one level but does not work well when objects have multiple levels (multi-arrays) and the XML is all messed up. Can anyone help me improve this so it will work with any object?

class XMLGenerator
{ 
       function __construct($obj,$root, $element, $fullXML = true) {     

          $array = $this->object_2_array($obj);
          $this->output = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n";
          $this->output .= $this->make($array, $root, $element, $fullXML);

       }

        //convert objects into arrays
        function object_2_array($result){

            $array = array();

            foreach ($result as $key => $value){

                if (is_object($value)){
                    $array[$key]=XMLGenerator::object_2_array($value);
                } elseif (is_array($value)){
                    $array[$key]=XMLGenerator::object_2_array($value);
                } else {
                    $array[$key]=$value;
                }
            }
            return $array;
        }   

       //make XML
       function make($array, $root, $element, $fullXML) {
          if (is_numeric($root)) {
             $xml = "<{$element}>\n";
          } else {
              $xml = "<{$root}>\n";
          }

          foreach ($array as $key => $value) {
             if (is_array($value)) {
                 if ($element == 'options'){  //workaround for orders 3 level problem, need to rethink this - LT
                    $xml .= $this->make($value, $key, $key, $fullXML); 
                 } else {
                    $xml .= $this->make($value, $element, $key, $fullXML);  
                 }              
             } else {

                 //any fields with HTML need wrapping in CDATA
                 if (($key === 'Description')||( $key === 'productDescription' )){
                    $value = '<![CDATA['. $value .']]>';
                 //remove any chars XML doesnt like  
                 } else {
                    $value = htmlentities($value,ENT_QUOTES, 'UTF-8');
                    $value = functions::xml_entities($value);    
                 }
                 //decide on node name

                if (is_numeric($key)) {
                   $xml .= "<{$root}>{$value}</{$root}>\n";
                } else {
                   $xml .= "<{$key}>{$value}</{$key}>\n";
                }           
             }
          }

          if (is_numeric($root)) {
             $xml .= "</{$element}>\n";
          } else {
              $xml .= "</{$root}>\n";
          } 

          return $xml;
       } 


       //save XML to file
       function saveFile($file){
            //create DOM to ensure all XML is valid before writing to file
            $doc = new DOMDocument();
            $doc->loadXML($this->output);

            if ($doc->save("$file")){
                return TRUE;
            } else {
                return FALSE;
            }
       }

}

The below is a simple object that works well with the above class.

Products Object ( [db_connection:protected] => 3779074 [prod_id:protected] => 0 [shopkeeper:protected] => 0 [fields] => Array (

        [productDescription] => Test
        [productName] => Test
        [UPC] => 123
    )

)

The below does not work well at all.

Order Object ( [db_connection:protected] => msSqlConnect Object ( [con] => [dbName] => )

[skID:protected] => 89137
[orderID:protected] => 482325
[order] => Array
    (
        [id] => 482325
        [customer] => 491936
        [net] => 1565.98
        [vat] => 274.05
        [billing_address] => Address Object
            (
                [db_connection:protected] => msSqlConnect Object
                    (
                        [con] => 
                        [dbName] => 
                    )

                [custID:protected] => 491936
                [addID:protected] => 156928
                [fields] => Array
                    (
                        [id] => 156928
                        [surname] => test
                        [forename] => test
                        [add1] => 89 testRoad
                        [add2] => 
                        [city] => City
                        [country] => GB
                        [postcode] => POSTCODE
                    )

            )
        [items] => Array
            (
                [0] => Array
                    (
                        [id] => 716549
                        [headerID] => 482325
                        [productID] => 4084620
                        [net] => 22.99
                        [vat] => 4.0233
                        [qty] => 1
                        [options] => Array
                            (
                                [0] => Array
                                    (
                                        [id] => 
                                        [orderDetailsID] => 716549
                                        [optionid] => 763217
                                        [optionCost] => 100
                                        [optionVAT] => 17.5
                                    )

                                [1] => Array
                                    (
                                        [id] => 
                                        [orderDetailsID] => 716549
                                        [optionid] => 763241
                                        [optionCost] => 10
                                        [optionVAT] => 1.75
                                    )

                            )

                    )

                [1] => Array
                    (
                        [id] => 716551
                        [headerID] => 482325
                        [productID] => 3779074
                        [net] => 1400
                        [vat] => 245
                        [qty] => 1
                        [options] => 
                    )
            )
    ) )

Many thanks in advance for any help.

like image 573
LeeTee Avatar asked Sep 14 '26 17:09

LeeTee


1 Answers

Multiple levels require a recursive kind of processing - as you don't know the number of levels upfront. While doing the recursion you also need to take care which XML Elements are opened and such.

What you do is that you serialize a PHP object into XML. You're not the first one who needs this, PHP ships with a XML serializer that follows the WDDX specification, for example with the wddx_serialize_value function:

$object = (object) array('hello' => (object) array('value' => 'world') );

echo wddx_serialize_value($object);

Which will give this XML (Demo):

<wddxPacket version='1.0'>
  <header/>
  <data>
    <struct>
      <var name='php_class_name'>
        <string>stdClass</string>
      </var>
      <var name='hello'>
        <struct>
          <var name='php_class_name'>
            <string>stdClass</string>
          </var>
          <var name='value'>
            <string>world</string>
          </var>
        </struct>
      </var>
    </struct>
  </data>
</wddxPacket>

If you need a different output, you need to write the serialization on your own. Within symfony2 (Symfony2 Serializer Component) and Pear (XML_Serializer) you find existing PHP code that does serialization with XML output.

like image 118
hakre Avatar answered Sep 17 '26 08:09

hakre



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!