<?php 
/** 
 * XMLElement class file 
 * @author Mathieu Lachance <[email protected]> 
 * @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> 
*/ 
 
?> 
 
 |