Source for file XMLParser.class.php

Documentation is available at XMLParser.class.php

  1. <?php
  2. /**
  3.     This program is free software; you can redistribute it and/or modify
  4.     it under the terms of the GNU General Public License as published by
  5.     the Free Software Foundation; either version 2 of the License, or
  6.     (at your option) any later version.
  7.  
  8.     This program is distributed in the hope that it will be useful,
  9.     but WITHOUT ANY WARRANTY; without even the implied warranty of
  10.     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  11.     GNU General Public License for more details.
  12.  
  13.     For support, please visit http://www.criticaldevelopment.net/xml/
  14. */
  15.  
  16. /**
  17.  * XML Parser Class (php5)
  18.  * 
  19.  * Parses an XML document into an object structure much like the SimpleXML extension.
  20.  *
  21.  * @author Adam A. Flynn <adamaflynn@criticaldevelopment.net>
  22.  * @copyright Copyright (c) 2005-2007, Adam A. Flynn
  23.  *
  24.  * @version 1.3.0
  25.  */
  26. class XMLParser 
  27. {
  28.     /**
  29.      * The XML parser
  30.      *
  31.      * @var resource 
  32.      */
  33.     private $parser;
  34.  
  35.     /**
  36.     * The XML document
  37.     *
  38.     * @var string 
  39.     */
  40.     private $xml;
  41.  
  42.     /**
  43.     * Document tag
  44.     *
  45.     * @var object 
  46.     */
  47.     public $document;
  48.  
  49.     /**
  50.     * Current object depth
  51.     *
  52.     * @var array 
  53.     */
  54.     private $stack;
  55.     
  56.     /**
  57.      * Whether or not to replace dashes and colons in tag
  58.      * names with underscores.
  59.      * 
  60.      * @var bool 
  61.      */
  62.     private $cleanTagNames;
  63.  
  64.     
  65.     /**
  66.      * Constructor. Loads XML document.
  67.      *
  68.      * @param string $xml The string of the XML document
  69.      * @return XMLParser 
  70.      */
  71.     function __construct($xml ''$cleanTagNames = true)
  72.     {
  73.         //Load XML document
  74.         $this->xml = $xml;
  75.  
  76.         //Set stack to an array
  77.         $this->stack = array();
  78.         
  79.         //Set whether or not to clean tag names
  80.         $this->cleanTagNames = $cleanTagNames;
  81.     }
  82.  
  83.     /**
  84.      * Initiates and runs PHP's XML parser
  85.      */
  86.     public function Parse()
  87.     {
  88.         //Create the parser resource
  89.         $this->parser = xml_parser_create("UTF-8");
  90.         
  91.         //Set the handlers
  92.         xml_set_object($this->parser$this);
  93.         xml_set_element_handler($this->parser'StartElement''EndElement');
  94.         xml_set_character_data_handler($this->parser'CharacterData');
  95.  
  96.         //Error handling
  97.         if (!xml_parse($this->parser$this->xml))
  98.  
  99.         //Free the parser
  100.         xml_parser_free($this->parser);
  101.     }
  102.     
  103.     /**
  104.      * Handles an XML parsing error
  105.      *
  106.      * @param int $code XML Error Code
  107.      * @param int $line Line on which the error happened
  108.      * @param int $col Column on which the error happened
  109.      */
  110.     private function HandleError($code$line$col)
  111.     {
  112.         trigger_error('XML Parsing Error at '.$line.':'.$col.'. Error '.$code.': '.xml_error_string($code));
  113.     }
  114.  
  115.     
  116.     /**
  117.      * Gets the XML output of the PHP structure within $this->document
  118.      *
  119.      * @return string 
  120.      */
  121.     public function GenerateXML()
  122.     {
  123.         return $this->document->GetXML();
  124.     }
  125.  
  126.     /**
  127.      * Gets the reference to the current direct parent
  128.      *
  129.      * @return object 
  130.      */
  131.     private function GetStackLocation()
  132.     {
  133.         //Returns the reference to the current direct parent
  134.         return end($this->stack);
  135.     }
  136.  
  137.     /**
  138.      * Handler function for the start of a tag
  139.      *
  140.      * @param resource $parser 
  141.      * @param string $name 
  142.      * @param array $attrs 
  143.      */
  144.     private function StartElement($parser$name$attrs = array())
  145.     {
  146.         //Make the name of the tag lower case
  147.         $name strtolower($name);
  148.         
  149.         //Check to see if tag is root-level
  150.         if (count($this->stack== 0
  151.         {
  152.             //If so, set the document as the current tag
  153.             $this->document = new XMLTag($name$attrs);
  154.  
  155.             //And start out the stack with the document tag
  156.             $this->stack = array(&$this->document);
  157.         }
  158.         //If it isn't root level, use the stack to find the parent
  159.         else
  160.         {
  161.             //Get the reference to the current direct parent
  162.             $parent $this->GetStackLocation();
  163.             
  164.             $parent->AddChild($name$attrscount($this->stack)$this->cleanTagNames);
  165.  
  166.             //If the cleanTagName feature is on, clean the tag names
  167.             if($this->cleanTagNames)
  168.                 $name str_replace(array(':''-')'_'$name);
  169.  
  170.             //Update the stack
  171.             $this->stack[end($parent->$name);        
  172.         }
  173.     }
  174.  
  175.     /**
  176.      * Handler function for the end of a tag
  177.      *
  178.      * @param resource $parser 
  179.      * @param string $name 
  180.      */
  181.     private function EndElement($parser$name)
  182.     {
  183.         //Update stack by removing the end value from it as the parent
  184.         array_pop($this->stack);
  185.     }
  186.  
  187.     /**
  188.      * Handler function for the character data within a tag
  189.      *
  190.      * @param resource $parser 
  191.      * @param string $data 
  192.      */
  193.     private function CharacterData($parser$data)
  194.     {
  195.         //Get the reference to the current parent object
  196.         $tag $this->GetStackLocation();
  197.  
  198.         //Assign data to it
  199.         // ET -----------------------------------------------------
  200.         // Trim muß weg, da sonst die Leerzeichen vor Umlauten fehlen
  201.         //$tag->tagData .= trim($data);
  202.         $tag->tagData .= $data;
  203.     }
  204. }
  205.  
  206.  
  207. /**
  208.  * XML Tag Object (php5)
  209.  * 
  210.  * This object stores all of the direct children of itself in the $children array. They are also stored by
  211.  * type as arrays. So, if, for example, this tag had 2 <font> tags as children, there would be a class member
  212.  * called $font created as an array. $font[0] would be the first font tag, and $font[1] would be the second.
  213.  * 
  214.  * To loop through all of the direct children of this object, the $children member should be used.
  215.  *
  216.  * To loop through all of the direct children of a specific tag for this object, it is probably easier
  217.  * to use the arrays of the specific tag names, as explained above.
  218.  * 
  219.  * @author Adam A. Flynn <adamaflynn@criticaldevelopment.net>
  220.  * @copyright Copyright (c) 2005-2007, Adam A. Flynn
  221.  *
  222.  * @version 1.3.0
  223.  */
  224. class XMLTag
  225. {
  226.     /**
  227.      * Array with the attributes of this XML tag
  228.      *
  229.      * @var array 
  230.      */
  231.     public $tagAttrs;
  232.     
  233.     /**
  234.      * The name of the tag
  235.      *
  236.      * @var string 
  237.      */
  238.     public $tagName;
  239.     
  240.     /**
  241.      * The data the tag contains
  242.      * 
  243.      * So, if the tag doesn't contain child tags, and just contains a string, it would go here
  244.      *
  245.      * @var stat 
  246.      */
  247.     public $tagData;
  248.     
  249.     /**
  250.      * Array of references to the objects of all direct children of this XML object
  251.      *
  252.      * @var array 
  253.      */
  254.     public $tagChildren;
  255.     
  256.     /**
  257.      * The number of parents this XML object has (number of levels from this tag to the root tag)
  258.      *
  259.      * Used presently only to set the number of tabs when outputting XML
  260.      *
  261.      * @var int 
  262.      */
  263.     public $tagParents;
  264.     
  265.     /**
  266.      * Constructor, sets up all the default values
  267.      *
  268.      * @param string $name 
  269.      * @param array $attrs 
  270.      * @param int $parents 
  271.      * @return XMLTag 
  272.      */
  273.     function __construct($name$attrs = array()$parents = 0)
  274.     {
  275.         //Make the keys of the attr array lower case, and store the value
  276.         $this->tagAttrs = array_change_key_case($attrsCASE_LOWER);
  277.         
  278.         //Make the name lower case and store the value
  279.         $this->tagName = strtolower($name);
  280.         
  281.         //Set the number of parents
  282.         $this->tagParents = $parents;
  283.         
  284.         //Set the types for children and data
  285.         $this->tagChildren = array();
  286.         $this->tagData = '';
  287.     }
  288.     
  289.     /**
  290.      * Adds a direct child to this object
  291.      *
  292.      * @param string $name 
  293.      * @param array $attrs 
  294.      * @param int $parents 
  295.      * @param bool $cleanTagName 
  296.      */
  297.     public function AddChild($name$attrs$parents$cleanTagName = true)
  298.     {    
  299.         //If the tag is a reserved name, output an error
  300.         if(in_array($namearray('tagChildren''tagAttrs''tagParents''tagData''tagName')))
  301.         {
  302.             trigger_error('You have used a reserved name as the name of an XML tag. Please consult the documentation (http://www.criticaldevelopment.net/xml/) and rename the tag named "'.$name.'" to something other than a reserved name.'E_USER_ERROR);
  303.  
  304.             return;
  305.         }
  306.  
  307.         //Create the child object itself
  308.         $child = new XMLTag($name$attrs$parents);
  309.  
  310.         //If the cleanTagName feature is on, replace colons and dashes with underscores
  311.         if($cleanTagName)
  312.             $name str_replace(array(':''-')'_'$name);
  313.         
  314.         //Toss up a notice if someone's trying to to use a colon or dash in a tag name
  315.         elseif(strstr($name':'|| strstr($name'-'))
  316.             trigger_error('Your tag named "'.$name.'" contains either a dash or a colon. Neither of these characters are friendly with PHP variable names, and, as such, you may have difficulty accessing them. You might want to think about enabling the cleanTagName feature (pass true as the second argument of the XMLParser constructor). For more details, see http://www.criticaldevelopment.net/xml/'E_USER_NOTICE);
  317.         
  318.         //If there is no array already set for the tag name being added, 
  319.         //create an empty array for it
  320.         if(!isset($this->$name))
  321.             $this->$name = array();
  322.         
  323.         //Add the reference of it to the end of an array member named for the tag's name
  324.         $this->{$name}[&$child;
  325.         
  326.         //Add the reference to the children array member
  327.         $this->tagChildren[&$child;
  328.         
  329.         //Return a reference to this object for the stack
  330.         return $this;
  331.     }
  332.     
  333.     /**
  334.      * Returns the string of the XML document which would be generated from this object
  335.      * 
  336.      * This function works recursively, so it gets the XML of itself and all of its children, which
  337.      * in turn gets the XML of all their children, which in turn gets the XML of all thier children,
  338.      * and so on. So, if you call GetXML from the document root object, it will return a string for
  339.      * the XML of the entire document.
  340.      * 
  341.      * This function does not, however, return a DTD or an XML version/encoding tag. That should be
  342.      * handled by XMLParser::GetXML()
  343.      *
  344.      * @return string 
  345.      */
  346.     public function GetXML()
  347.     {
  348.         //Start a new line, indent by the number indicated in $this->parents, add a <, and add the name of the tag
  349.         $out "\n".str_repeat("\t"$this->tagParents).'<'.$this->tagName;
  350.  
  351.         //For each attribute, add attr="value"
  352.         foreach($this->tagAttrs as $attr => $value)
  353.             $out .= ' '.$attr.'="'.$value.'"';
  354.         
  355.         //If there are no children and it contains no data, end it off with a />
  356.         if(empty($this->tagChildren&& empty($this->tagData))
  357.             $out .= " />";
  358.         
  359.         //Otherwise...
  360.         else
  361.         {    
  362.             //If there are children
  363.             if(!empty($this->tagChildren))
  364.             {
  365.                 //Close off the start tag
  366.                 $out .= '>';
  367.                 
  368.                 //For each child, call the GetXML function (this will ensure that all children are added recursively)
  369.                 foreach($this->tagChildren as $child)
  370.                     $out .= $child->GetXML();
  371.  
  372.                 //Add the newline and indentation to go along with the close tag
  373.                 $out .= "\n".str_repeat("\t"$this->tagParents);
  374.             }
  375.             
  376.             //If there is data, close off the start tag and add the data
  377.             elseif(!empty($this->tagData))
  378.                 $out .= '>'.$this->tagData;
  379.             
  380.             //Add the end tag    
  381.             $out .= '</'.$this->tagName.'>';
  382.         }
  383.         
  384.         //Return the final output
  385.         return $out;
  386.     }
  387.     
  388.     /**
  389.      * Deletes this tag's child with a name of $childName and an index
  390.      * of $childIndex
  391.      *
  392.      * @param string $childName 
  393.      * @param int $childIndex 
  394.      */
  395.     public function Delete($childName$childIndex = 0)
  396.     {
  397.         //Delete all of the children of that child
  398.         $this->{$childName}[$childIndex]->DeleteChildren();
  399.         
  400.         //Destroy the child's value
  401.         $this->{$childName}[$childIndex= null;
  402.         
  403.         //Remove the child's name from the named array
  404.         unset($this->{$childName}[$childIndex]);
  405.         
  406.         //Loop through the tagChildren array and remove any null
  407.         //values left behind from the above operation
  408.         for($x = 0$x count($this->tagChildren)$x ++)
  409.         {
  410.             if(is_null($this->tagChildren[$x]))
  411.                 unset($this->tagChildren[$x]);
  412.         }
  413.     }
  414.     
  415.     /**
  416.      * Removes all of the children of this tag in both name and value
  417.      */
  418.     private function DeleteChildren()
  419.     {
  420.         //Loop through all child tags
  421.         for($x = 0$x count($this->tagChildren)$x ++)
  422.         {
  423.             //Do this recursively
  424.             $this->tagChildren[$x]->DeleteChildren();
  425.             
  426.             //Delete the name and value
  427.             $this->tagChildren[$x= null;
  428.             unset($this->tagChildren[$x]);
  429.         }
  430.     }
  431. }

Documentation generated on Thu, 05 May 2011 14:05:59 +0000 by phpDocumentor 1.4.3