<?php
/**
* XMLElement class file
* @author Mathieu Lachance <lachance.mathieu@poincomm.com>
* @link http://www.mathieulachance.com/
* @copyright Copyright (c) 2007 Mathieu Lachance
* @license http://creativecommons.org/licenses/by/2.5/ca/
* @version 2.0 2007-03-10 15h45
*/
/**
* Class XMLElement
* The XMLElement class is used to generate an xml tree architecture.
* This xml tree architecture is similar to the SimpleXMLElement class embeded by default within PHP5.
* Each XMLElement contains :
* - all the properties inherited from the Node class, $name, $parent, $children, $count
* - an entity, the $entity protected property
* - a mapped array containing all the attributes of the xml element, the $attributes protected property
* - a possible prefix of the xml element entity, the $prefix protected property
* - the possible text content of the xml element, the $text protected property
* note : only empty xml element (without child elements) can define this property
*
* For more information please refer to the Node class documentation.
*/
class XMLElement extends Node
{
const XML_DECLARATION = "<?xml version=\"1.0\"?>\n";
/**
* @var string $entity the entity of the xml element
*/
protected $entity = null;
/**
* @var array $attributes a mapped array containing all the attributes of the xml element
*/
protected $attributes = null;
/**
* @var string $prefix the possible prefix of the xml element entity
*/
protected $prefix = null;
/**
* @var string $text the possible text content of the xml element
*/
protected $text = null;
/**
* Constructor
* @param string $entity the xml element entity
* @param array $attributes a possible mapped array containing all the xml element attributes
* @param string $prefix a possible prefix for the xml element entity
* @todo : add a param for the possible text content ?
*/
public function __construct($entity = null, $attributes = null, $prefix = null){
parent::__construct((string)$entity);
$this->prefix = (string)$prefix;
$this->attributes = (array)$attributes;
}
/**
* Assign to the mapped array $attributes the value $value at the index $name
* @param string $name the name of the attribute
* @param string $value the value of the attribute
*/
public function addAttribute($name, $value){
$this->attributes[(string)$name] = (string)$value;
}
/**
* Assign all the attributes contained in the $map parameter
* @param array $map an array containing all the attributes to assign to the xml element
*/
public function addAttributes($map){
if (!(is_array($map))) { throw new Exception("must pass a mapped array"); }
foreach ($map as $name => $value){
$this->addAttribute($name, $value);
}
}
/**
* Overload the node addChild method to ensure that no child can be assigned
* if the $text property is not null
* @param string $entity the xml element entity
* @param array $attributes a possible mapped array containing all the xml element attributes
* @param string $prefix a possible prefix for the xml element entity
* @throws Exception if the $text property is not null
*/
public function addChild($entity, $attributes = null, $prefix = null){
if ($this->text != null){ throw new Exception("Can only add element to non text element"); }
return parent::addChild(new XMLElement($entity, $attributes, $prefix));
}
/**
* @return string $prefix the possible prefix of the xml element entity
*/
public function getPrefix(){
return $this->prefix;
}
/**
* @return string / null the $text property
*/
public function getText(){
return $this->text;
}
/**
* @param string $text the string to be assign to the $text property
* @throws Exception if the xml element is not empty
*/
public function setText($text){
if (!($this->isEmpty())){ throw new Exception("Can only add text element to empty node"); }
$this->text = (string)$text;
}
/**
* Overload the node export method to integrate :
* - the possible prefix of the entity
* - the attributes of the xml element
* @param int $level the xml element indentation
* @return string $x the xml output
*/
public function export($level = null){
$level = (int)$level;
$ws = str_repeat(" ", $level); // calculate the whitespace indentation
((string)$this->prefix != "") ? $pr = "$this->prefix:" : $pr = ""; // generate the prefix string
$at = ""; // generate the attributes string
foreach ($this->attributes as $n => $v){
$at .= " $n=\"$v\"";
}
if ($this->isEmpty()){ // if the current node is empty
if ($this->text == null){ // return a closing tag of the current node name as the xml output
return "$ws<$pr$this->name$at/>\n";
}
return "$ws<$pr$this->name$at>$this->text</$pr$this->name>\n";
}
$x = "$ws<$pr$this->name$at>\n"; // open an tag of the current node name
foreach($this->children as $child){ // for each child of the current node
$x .= $child->export($level+1); // export the child node
}
$x .= "$ws</$pr$this->name>\n"; // close the opened tag of the current node name
return $x; // return the xml output of the current node
}
public function __toString(){
return self::XML_DECLARATION . $this->export();
}
/**
* Overload the import method to integrate :
* - the possible prefix of the entity
* - the attributes of the xml element
* @param DomNode $d the xml fragment of the last import method iteration
* @param XMLElement $p the parent node reference of the last import method iteration
*/
public function import(DomNode $d, XMLElement $p = null){
$this->name = $d->nodeName; // assign the node name with the root element of the DomNode
$this->parent = $p; // assign the parent node
$this->entity = $this->name; // assign the node entity as same as node name
$this->prefix = $d->prefix; // assign the node prefix
if ($d->hasAttributes()){ // if the node contains any attributes
foreach($d->attributes as $a){ // for each attributes
$this->addAttribute($a->name, $a->value); // append attribute
}
}
if ($d->hasChildNodes()){ // if the xml node is not empty
foreach($d->childNodes as $child){ // for each children
switch($child->nodeType){ // switch the node type
case XML_ELEMENT_NODE : // if it is an XML ELEMENT NODE
$n = new XMLElement(); // create the new XMLElement
$n->import($child, $this); // import all nodes contained in the child node
break;
case XML_TEXT_NODE : // if it is a XML TEXT NODE
if (trim($child->textContent)){ // if the content is not empty
$this->text = trim($child->textContent); // trim whitespaces and assign the content
}
break;
case XML_CDATA_SECTION_NODE : // if the child is an XML CDATA SECTION NODE
if (trim($child->textContent)){ // if the content is not empty
$this->text = "<![CDATA[" . trim($child->textContent) . "]]>"; // trim whitespaces and assign the content
}
break;
}
}
}
}
}
// exemple :
$n = new XMLElement("people");
$n->addAttribute("xmlns:p", "http://example.org/ns");
$n->addAttribute("xmlns:t", "http://example.org/test");
$n->addChild("person", array("t:id"=>"1"), "p");
$n->person->setText("John Doe");
$n->addChild("person", array("t:id"=>"2","a:addr"=>"123 Street" ,"xmlns:a"=>"http://example.org/addr"), "p");
$n->person[1]->setText("<![CDATA[Susie Q. Public]]>");
echo $n;
/* output :
<?xml version="1.0"?>
<people xmlns:p="http://example.org/ns" xmlns:t="http://example.org/test">
<p:person t:id="1">John Doe</p:person>
<p:person t:id="2" a:addr="123 Street" xmlns:a="http://example.org/addr"><![CDATA[Susie Q. Public]]></p:person>
</people>
*/
$d = new DomDocument();
$d->loadXML($n->export());
echo $d->saveXML();
/* output :
<?xml version="1.0"?>
<people xmlns:p="http://example.org/ns" xmlns:t="http://example.org/test">
<p:person t:id="1">John Doe</p:person>
<p:person xmlns:a="http://example.org/addr" t:id="2" a:addr="123 Street"><![CDATA[Susie Q. Public]]></p:person>
</people>
<?xml version="1.0"?>
*/
$n->import($d->documentElement);
echo $n;
/* output :
<people xmlns:p="http://example.org/ns" xmlns:t="http://example.org/test">
<p:person t:id="1">John Doe</p:person>
<p:person t:id="2" a:addr="123 Street" xmlns:a="http://example.org/addr"><![CDATA[Susie Q. Public]]></p:person>
</people>
*/
?>
|