<?php
/**
 * Copyright (c) 2008 Jerry Jalava <jerry.jalava@iki.fi>
 * Licensed under the GPLv3 license
 * http://www.gnu.org/licenses/gpl.html
 * 
 * Version 1.2
 * 08/05/2008:
 * - Refactored to work better with more complex structures
 * - Objects stay same in two way conversions
 */

class xmlObject {
    
public $root 'root';
    
public $items = array();
}

class 
xmlObjectItem extends xmlObject
{
    
public $attributes = array();
}

class 
xmlObjectSet extends xmlObject
{
    
public $wrapper_id 'object';
}

class 
xmlObjects
{
    
public static function clean_key($key)
    {
        return 
preg_replace('/[^a-z0-9\_]/i'''$key);
    }
    
    
public static function toXml(xmlObject $data$root_name null$xml null)
    {
        if (
ini_get('zend.ze1_compatibility_mode') == 1) {
            
ini_set('zend.ze1_compatibility_mode'0);
        }
        
        if (
is_null($root_name)) {
            
$root_name $data->root;
        }        
        
        
$root_name xmlObjects::clean_key($root_name);
        
        if (
$xml == null) {
            
$xml simplexml_load_string("<?xml version='1.0' encoding='utf-8'?><$root_name/>");
        }
        
        if (
get_class($data) == 'xmlObjectItem')
        {            
            foreach (
$data->items as $key => $value)
            {
                
$key xmlObjects::clean_key($key);

                if (
is_array($value))
                {
                    
$child $xml->addChild($key);
                    
                    if (isset(
$data->attributes[$key])) {
                        foreach (
$data->attributes[$key] as $ak => $av) {
                            
$child->addAttribute($akhtmlentities($av));
                        }
                    }
                    
                    
xmlObjects::toXml($value$root_name$child);
                }
                else 
                {
                    if (
get_class($value) == 'xmlObjectItem')
                    {
                        continue;
                    }
                    if (
get_class($value) == 'xmlObjectSet')
                    {
                        
$wrapper_id preg_replace('/[^a-z0-9\_]/i'''$value->wrapper_id);
                        
$xml $xml->addChild($key);
                        
$object_node $xml->addChild($wrapper_id);
                        foreach (
$value->items as $item)
                        {                        
                            
xmlObjects::toXml($item''$object_node);
                        }
                    }
                    else
                    {
                        
$child $xml->addChild($keyhtmlentities($value));

                        if (isset(
$data->attributes[$key])) {
                            foreach (
$data->attributes[$key] as $ak => $av) {
                                
$child->addAttribute($akhtmlentities($av));
                            }
                        }
                    }
                }                
            }
        }
        elseif (
get_class($data) == 'xmlObjectSet')
        {
            foreach (
$data->items as $object)
            {
                
//print_r($data);
                
$object_node $xml->addChild($data->wrapper_id);
                
xmlObjects::toXml($object$root_name$object_node);
            }
        }
        
        return 
$xml->asXML();
    }
    
    
public static function fromXml($xml, &$data=null)
    {
        if (
get_class($xml) != 'SimpleXMLElement') {
            
$xml simplexml_load_string($xml'SimpleXMLElement'LIBXML_NOCDATA);
        }
        
        
$root_name $xml->getName();
        
        
$set = new xmlObjectSet();
        
$set->root $root_name;

        if (
count($xml->children()) > 0)
        {
            
$children $xml->children();
            
$set->wrapper_id $children[0]->getName();
            
            foreach (
$children as $child)
            {
                if (
count($child->children()) == 0) {
                    
$item xmlObjects::itemFromXml($xml);
                    
$item->root $root_name;
                    return 
$item;
                }
                else
                {
                    
$set->items[] = xmlObjects::fromXml($child);
                }
            }
        }
        
        return 
$set;
    }
    
    
public static function itemFromXml($xml)
    {
        
$childData = array();
        
$childAttrs = array();
        
        if (
count($xml->children()) == 0) {
            
$childName $xml->getName();
            
$childData[$childName] = (string) $xml;
        
            if (
$xml->attributes()) {
                if (! isset(
$childAttrs[$childName])) {
                    
$childAttrs[$childName] = array();
                }
                foreach (
$xml->attributes() as $ak => $av) {                
                    
$childAttrs[$childName][$ak] = (string) $av;
                }
            }
        }
        else
        {
            foreach (
$xml->children() as $child) {
                
$childName $child->getName();
        
                if (
$child->attributes()) {
                    if (! isset(
$childAttrs[$childName])) {
                        
$childAttrs[$childName] = array();
                    }
                    foreach (
$child->attributes() as $ak => $av) {                
                        
$childAttrs[$childName][$ak] = (string) $av;
                    }
                }
                
                if (
count($child->children()) == 0) {
                    
$childData[$childName] = (string) $child;
                }
                else
                {                    
                    if (! isset(
$childData[$childName])) {
                        
$childData[$childName] = array();
                    }
                    
                    
$childData[$childName] = xmlObjects::fromXml($child);
                }        
            }            
        }
        
        
$item = new xmlObjectItem();
        
$item->items $childData;
        
$item->attributes $childAttrs;
        
        return 
$item;
    }
}

/* Test data */

$single_client = new xmlObjectItem();
$single_client->items = array('id' => 1'name' => "Client 1"'contact_id' => 2'team-leader' => null);
$single_client->attributes = array(
    
'contact_id' => array(
        
'is_assoc' => true,
        
'foreign_model' => 'person',
        
'foreign_key' => 'id'
    
)
);


$resource = new xmlObjectItem();
$resource->items = array(
    
'id' => 100,
    
'name' => "Jerry Jalava",
    
'person_id' => 1
);
$resource->attributes = array(
    
'person_id' => array(
        
'is_assoc' => true,
        
'foreign_model' => 'person',
        
'foreign_key' => 'id'
    
)
);

$resource_set = new xmlObjectSet();
$resource_set->wrapper_id 'resource';
$resource_set->items[] = $resource;

$projects = array();
for (
$i=1$i <= 1$i++)
{
    
$object = new xmlObjectItem();
    
$object->items = array(
        
'id' => $i,
        
'name' => "Project {$i}",
        
'manager_id' => $i+1,
        
'resources' => $resource_set
    
);
    
    
$projects[] = $object;
}

$project_set = new xmlObjectSet();
$project_set->wrapper_id 'project';
$project_set->items $projects;

$clients = array();
for (
$i=1$i <= 1$i++)
{
    
$client = new xmlObjectItem();
    
$client->items = array(
        
'id' => $i,
        
'name' => "Client {$i}",
        
'contact_id' => $i+1,
        
'team-leader' => null,
        
'projects' => $project_set
    
);    
    
$client->attributes = array(
        
'contact_id' => array(
            
'is_assoc' => true,
            
'foreign_model' => 'person',
            
'foreign_key' => 'id'
        
)
    );
    
    
$clients[] = $client;
}
$client_set = new xmlObjectSet();
$client_set->wrapper_id 'client';
$client_set->items $clients;

echo 
"complex set:\n";
var_dump($client_set);


echo 
"simple item:\n";
var_dump($single_client);

echo 
"\n--------toXml---------\n";

$xmlSet_clients xmlObjects::toXml($client_set'clients');
echo 
"complex:\n";
echo 
$xmlSet_clients;

echo 
"\n\n";

$xmlItem_client xmlObjects::toXml($single_client'client');
echo 
"simple:\n";
echo 
$xmlItem_client;

echo 
"\n--------fromXml---------\n";

$client_set xmlObjects::fromXml($xmlSet_clients);
echo 
"client_set root: {$client_set->root}\n";
var_dump($client_set);

echo 
"\n\n";

$single_client xmlObjects::fromXml($xmlItem_client);
echo 
"single_client root: {$single_client->root}\n";
var_dump($single_client);

echo 
"\n--------back toXml---------\n";

$xmlSet_clients2 xmlObjects::toXml($client_set);
echo 
"complex:\n";
echo 
$xmlSet_clients2;

if (
$xmlSet_clients == $xmlSet_clients2) {
    echo 
"\n\nVALID SET CONVERSION\n\n";
}

$xmlItem_client2 xmlObjects::toXml($single_client);
echo 
"simple:\n";
echo 
$xmlItem_client2;

if (
$xmlItem_client == $xmlItem_client2) {
    echo 
"\n\nVALID ITEM CONVERSION\n\n";
}


?>