Xaraya PHP Cross Reference Web Portal Systems

Source: /includes/xarXML.php - 791 lines - 26378 bytes - Summary - Text - Print

Description: XML services for Xaraya

   1  <?php
   2  /**
   3   * XML services for Xaraya
   4   *
   5   * @package core
   6   * @copyright (C) 2002-2007 The Digital Development Foundation
   7   * @license GPL {@link http://www.gnu.org/licenses/gpl.html}
   8   * @link http://www.xaraya.com
   9   *
  10   * @subpackage xml
  11   * @author Marcel van der Boom <marcel@xaraya.com>
  12   */
  13  
  14  /**
  15   * This subsystem offers an interface to Xaraya to handle XML data
  16   * It includes:
  17   * - Generic XML parser with an interface to parse any XML data including the
  18   *   callback functions
  19   *
  20   * 2003-06-08: xmlrpc, translations, rss, dynamicdata all use xml. This core
  21   *             subsystem can be reused by them all
  22   */
  23  
  24  //error_reporting(E_ALL);
  25  
  26  /**
  27   * Defines make our life a bit easier.
  28   *
  29   */
  30  define('XARXML_VERSION','0.0.1');
  31  define('XARXML_PARSERCLASS' ,'xarXmlParser');
  32  define('XARXML_HANDLERCLASS','xarAbstractXmlHandler');
  33  
  34  // What class are we instatiating as the default handler?
  35  define('XARXML_DEFAULTHANDLER','xarXmlDefaultHandler');
  36  
  37  // PHP xml extension supports only three encodings
  38  define('XARXML_CHARSET_USASCII'  ,'US-ASCII');
  39  define('XARXML_CHARSET_ISO8859_1','ISO-8859-1');
  40  define('XARXML_CHARSET_UTF8'     ,'UTF-8');
  41  // The default input encoding for the parser can be set below
  42  define('XARXML_CHARSET_DEFAULT',XARXML_CHARSET_UTF8);
  43  
  44  // Separators
  45  define('XARXML_NAMESPACE_SEP',':');
  46  define('XARXML_PATH_SEP','/');
  47  define('XARXML_ENTITY_SEP', chr(12));
  48  
  49  // Nodes for parsing, copied from domxml extension
  50  if(!defined('XML_ELEMENT_NODE'))       define('XML_ELEMENT_NODE'      , 1);
  51  if(!defined('XML_ATTRIBUTE_NODE'))     define('XML_ATTRIBUTE_NODE'    , 2);
  52  if(!defined('XML_TEXT_NODE'))          define('XML_TEXT_NODE'         , 3);
  53  if(!defined('XML_CDATA_SECTION_NODE')) define('XML_CDATA_SECTION_NODE', 4);
  54  if(!defined('XML_ENTITY_REF_NODE'))    define('XML_ENTITY_REF_NODE'   ,    5);
  55  if(!defined('XML_ENTITY_NODE'))        define('XML_ENTITY_NODE'       , 6);
  56  if(!defined('XML_PI_NODE'))            define('XML_PI_NODE'           , 7);
  57  if(!defined('XML_COMMENT_NODE'))       define('XML_COMMENT_NODE'      , 8);
  58  if(!defined('XML_DOCUMENT_NODE'))      define('XML_DOCUMENT_NODE'     , 9);
  59  if(!defined('XML_DOCUMENT_TYPE_NODE')) define('XML_DOCUMENT_TYPE_NODE',10);
  60  if(!defined('XML_DOCUMENT_FRAG_NODE')) define('XML_DOCUMENT_FRAG_NODE',11);
  61  if(!defined('XML_NOTATION_NODE'))      define('XML_NOTATION_NODE'     ,12);
  62  
  63  if(!defined('XML_ENTITY_DECL_NODE'))   define('XML_ENTITY_DECL_NODE'  ,17);
  64  
  65  if(!defined('XML_GLOBAL_NAMESPACE')) define('XML_GLOBAL_NAMESPACE', 1);
  66  if(!defined('XML_LOCAL_NAMESPACE'))  define('XML_LOCAL_NAMESPACE' , 2);
  67  
  68  // Miscellaneous
  69  define('XARXML_BLOCKREAD_SIZE',4096);
  70  
  71  // Parse directly when reading a file
  72  // This is mainly a switch for using xml_parse or
  73  // xml_parse_into_struct. It all depends whether the
  74  // re-arranging of the php generated struct is faster
  75  // then creating our own struct
  76  // Using the chunk based parser, for large files
  77  // and using 'true' SAX parsing (like direct filters or
  78  // chaining handlers, will be much faster and cost less
  79  // memory. We make it definable anyway, so we can prove
  80  // the above statement.
  81  define('XARXML_PARSEWHILEREAD',true);
  82  
  83  // Handler states
  84  define('XARXML_HSTATE_INITIAL'          , 0);
  85  define('XARXML_HSTATE_NORMAL'           , 1);
  86  define('XARXML_HSTATE_DTDNAME_EXPECTED' , 2);
  87  define('XARXML_HSTATE_WAITCONDOPEN'     , 4);
  88  define('XARXML_HSTATE_DTDCLOSE_EXPECTED', 8);
  89  define('XARXML_HSTATE_DTDGATHERING'     ,16);
  90  
  91  // Attribute names for the tree constructed by the default handler
  92  define('XARXML_ATTR_TAGINDEX','tagindex');
  93  define('XARXML_ATTR_TYPE','type');
  94  define('XARXML_ATTR_NAME','name');
  95  define('XARXML_ATTR_CHILDREN','children');
  96  define('XARXML_ATTR_CONTENT','content');
  97  define('XARXML_ATTR_ATTRIBUTES','attributes');
  98  define('XARXML_ATTR_NAMESPACES','namespaces');
  99  
 100  /**
 101   * Start the XML subsystem
 102   *
 103   * This initializes the XML subsystem, not much for now
 104   * and i actually want to keep it that way. ;-)
 105   *
 106   * @access protected
 107   */
 108  function xarXml_init($args, $whatElseIsGoingLoaded)
 109  {
 110      return true;
 111  }
 112  
 113  /**
 114   * Xaraya Generic namespace aware XML parser
 115   *
 116   * The Xaraya XML parser. This parser is generic in nature
 117   * in that it isn't configured to handle a specific XML variety
 118   * Using the public methods modules can instantiate a parser
 119   * and set the handlers of it, so it can parse a certain XML
 120   * document structure and act accordingly.
 121   *
 122   * @access public
 123   * @package xml
 124   * @todo do not assume the result will be a parse tree, it's non-sax-like
 125   */
 126  class xarXmlParser
 127  {
 128      var $encoding;      // Which input encoding are we gonna use for parsing?
 129      var $handler;       // Which handler object is attached to this parser?
 130      var $parser=NULL;   // The parser object itself
 131      var $tree=array();  // Resulting parse tree
 132  
 133      /**
 134       * Construct the xarXmlParser object
 135       *
 136       * For xaraya we need to be able to set encoding,
 137       * and have support for namespaces
 138       *
 139       * @access public
 140       * @param string $encoding character encoding to use (see top of file)
 141       * @param object $handler which handler object handles the events generated
 142       * @todo build in recognition of domxml availability and set that as default handler
 143       */
 144      function xarXmlParser($encoding=XARXML_CHARSET_DEFAULT,$handler=NULL)
 145      {
 146          $this->encoding=$encoding;
 147  
 148          $defHandlerClass = XARXML_DEFAULTHANDLER;
 149          if(is_object($handler) && is_subclass_of($handler,XARXML_HANDLERCLASS))
 150              $this->handler =& $handler;
 151          else
 152              $this->handler = new $defHandlerClass();
 153      }
 154  
 155      /**
 156       * Parse a string
 157       *
 158       * @access public
 159       * @param string $xmldata string representation of xmldata to parse
 160       * @todo check the string more thoroughly, seems to be delicate
 161       */
 162      function parseString($xmldata)
 163      {
 164          $this->__activate();
 165          if(!$this->__parse($xmldata, true)) {
 166              $this->__deactivate();
 167              return false;
 168          }
 169          return $this->__deactivate();
 170      }
 171  
 172      /**
 173       * Parse a file
 174       *
 175       * @access public
 176       * @param string $fileName path to file to parse
 177       */
 178      function parseFile($fileName)
 179      {
 180          $fp = fopen($fileName,"r");
 181          if(!is_resource($fp)) {
 182              $this->lastmsg="Could't open $fileName";
 183              return false;
 184          }
 185          // If doc is empty return false
 186          if(filesize($fileName) == 0) {
 187              $this->lastmsg="File is empty";
 188              return false;
 189          }
 190          // Activate the parser with resolve base the base path of the file
 191          $resolve_base = dirname($fileName);
 192          $this->__activate($resolve_base);
 193          $xml='';
 194  
 195          // Parse in chunks
 196          while ($xmldata = fread($fp, XARXML_BLOCKREAD_SIZE)) {
 197              if(XARXML_PARSEWHILEREAD) {
 198                  if(!$this->__parse($xmldata, feof($fp))) {
 199                      $this->__deactivate();
 200                      return false;
 201                  }
 202              } else {
 203                  $xml .= $xmldata;
 204              }
 205          }
 206  
 207          // Parse in whole
 208          if (!XARXML_PARSEWHILEREAD) {
 209              if (!$this->__parse($xml,true)) {
 210                  $this->__deactivate();
 211                  return false;
 212              }
 213          }
 214          return $this->__deactivate();
 215      }
 216  
 217      /**
 218       * Central parse function
 219       *
 220       * This is the only place where the actual parsing (by php i.e.) is don
 221       *
 222       * @access private
 223       * @param string $xmldata chunk of xmldata
 224       * @param bool   $final   denotes whether this is the last chunk we can expect
 225       * @todo put the $vals and $index arrays to use, we get them nearly for free here when parsing as a whole
 226       */
 227      function __parse($xmldata, $final)
 228      {
 229          $vals=array(); $index=array();
 230          // FIXME: actually put that arrays to use in the handler,
 231          // tho we should do this in a portable way and in a 'SAX' way
 232          if(XARXML_PARSEWHILEREAD) {
 233              return xml_parse($this->parser,$xmldata, $final);
 234          } else {
 235              return xml_parse_into_struct($this->parser, $xmldata, $vals, $index);
 236          }
 237      }
 238  
 239      /**
 240       * Construct error information
 241       *
 242       * @access private
 243       *
 244       */
 245      function __getErrorInfo()
 246      {
 247          $error = xml_get_error_code($this->parser);
 248          $this->lastmsg = "[".xml_get_current_line_number($this->parser).":"
 249              .xml_get_current_column_number($this->parser)."]-"
 250              .xml_error_string($error);
 251      }
 252  
 253      /**
 254       * Set a parser option
 255       *
 256       * @access public
 257       * @param integer $option option to be set, one of the XML_OPTION_* constants
 258       * @param mixed   $value  value to set the option to
 259       */
 260      function setOption($option, $value)
 261      {
 262          return xml_parser_set_option($this->parser, $option, $value);
 263      }
 264  
 265      /**
 266       * Get a parser option
 267       *
 268       * @access public
 269       * @param  integer $option option to retrieve, one of the XML_OPTION_* constants
 270       */
 271      function getOption($option)
 272      {
 273          return xml_parser_get_option($this->parser, $option);
 274      }
 275  
 276      /**
 277       * Private methods
 278       *
 279       */
 280  
 281      /**
 282       * Activate the parser
 283       *
 284       * This method activates the parser to be set up for parsring a string
 285       * or a file. This activate/deactivate logic is necessary because the
 286       * parser can only parse 1 file/string during it's instantation. When
 287       * you try to parser consecutive documents with the same instance all
 288       * kinds of weird errors are happening.
 289       *
 290       * @access private
 291       * @param string $resolve_base the base from which system/public ids are resolved
 292       *
 293       */
 294      function __activate($resolve_base = NULL)
 295      {
 296          $this->parser=xml_parser_create_ns($this->encoding, XARXML_NAMESPACE_SEP);
 297          $this->setOption(XML_OPTION_CASE_FOLDING,false);
 298          $this->setOption(XML_OPTION_SKIP_WHITE,true);
 299          $this->__activateHandlers();
 300          $this->handler->_resolve_base = $resolve_base;
 301      }
 302  
 303      /**
 304       * Deactivate the parse
 305       *
 306       * When done parsing, this method deactivates the parser
 307       *
 308       * @access private
 309       */
 310      function __deactivate()
 311      {
 312          $this->__geterrorinfo();
 313          $this->tree = $this->handler->_tree;
 314          $this->handler->_reset();
 315          return $this->__free();
 316      }
 317  
 318      /**
 319       * Free the parser
 320       *
 321       * @access private
 322       */
 323      function __free()
 324      {
 325          return xml_parser_free($this->parser);
 326      }
 327  
 328      /**
 329       * Set the handlers
 330       *
 331       * For the registered handler to the parser, this private method
 332       * activates them.
 333       *
 334       * @access private
 335       */
 336      function __activateHandlers()
 337      {
 338          $par = $this->parser;
 339          xml_set_object($par,$this->handler);
 340          xml_set_default_handler($par,               'default_handler');
 341          xml_set_character_data_handler($par,        'character_data');
 342          xml_set_element_handler($par,               'open_tag',
 343                                                      'close_tag');
 344          xml_set_processing_instruction_handler($par,'process_instruction');
 345          xml_set_unparsed_entity_decl_handler($par,  'unparsed_entity');
 346          xml_set_notation_decl_handler($par,         'notation_declaration');
 347          xml_set_external_entity_ref_handler($par,   'external_entity_reference');
 348          xml_set_start_namespace_decl_handler($par,  'start_namespace');
 349          xml_set_end_namespace_decl_handler($par,    'end_namespace');
 350      }
 351  }
 352  
 353  /**
 354   * Base class for XML parse handlers
 355   *
 356   * This class forms the base for defining handlers. Override
 357   * this class with your own methods with the same name to create
 358   * a xml handler object which handles the parsing for you.
 359   *
 360   * @package xml
 361   * @todo test,test,test
 362   * @todo document the strange xml_set_object thingie
 363   */
 364  class xarAbstractXmlHandler
 365  {
 366      // Abstract functions
 367      function default_handler()
 368      {}
 369  
 370      function character_data()
 371      {}
 372  
 373      function open_tag()
 374      {}
 375  
 376      function close_tag()
 377      {}
 378  
 379      function process_instruction()
 380      {}
 381  
 382      function unparsed_entity()
 383      {}
 384  
 385      function notation_declaraion()
 386      {}
 387  
 388      function external_entity_declaraion()
 389      {}
 390  
 391      function start_namespace()
 392      {}
 393  
 394      function end_namespace()
 395      {}
 396  
 397      function _reset()
 398      {}
 399  }
 400  
 401  /**
 402   * The default xml handler constructs a tree out of the
 403   * parsed xml
 404   *
 405   * @package xml
 406   *
 407   */
 408  class xarXmlDefaultHandler extends xarAbstractXmlHandler
 409  {
 410      var $_tree = array();
 411      var $_depth = 1;
 412      var $_tagindex;
 413      var $_nsregister=array();
 414      var $_state = XARXML_HSTATE_INITIAL;
 415      var $_dtd_data ='';
 416  
 417      /**
 418       * We need a base for resolving entities when they are not
 419       * specified relatively. On creation of the handler this can have a number of values
 420       * 1. path of the file we're handling, so relative paths can be resolved
 421       * 2. NULL if we're not in a file at all
 422       * 3. url?
 423       */
 424      var $_resolve_base=NULL;
 425  
 426      /**
 427       * Constructor
 428       *
 429       * @param integer $indexstart where to start counting
 430       */
 431      function xarXmlDefaultHandler($indexstart=1)
 432      {
 433          $this->_tagindex=$indexstart;
 434      }
 435  
 436      /**
 437       * The default handler catches everything which is not handled by others
 438       *
 439       * @param object $parser the parser to which the handler is attached
 440       * @param string $data   string data found in the construct
 441       */
 442      function default_handler($parser, $data)
 443      {
 444          if(!trim($data)) return true; // nothing to do here
 445  
 446          // If we've never been here before add the initial doc node
 447          if($this->_state == XARXML_HSTATE_INITIAL) {
 448              $this->_tree[0][XARXML_ATTR_TYPE] = XML_DOCUMENT_NODE;
 449              $this->_tree[0][XARXML_ATTR_NAME] = '#document';
 450              $this->_tree[0][XARXML_ATTR_TAGINDEX]=$this->_tagindex;
 451              $this->_tagindex++;
 452              $this->_state = XARXML_HSTATE_NORMAL;
 453          }
 454  
 455          // Subminiparser to extract the DOCTYPE
 456          //echo "$data\n";
 457          switch($this->_state) {
 458          case XARXML_HSTATE_NORMAL:
 459              // If we have the <?xml decl, add the attributes to the document node
 460              if(substr(trim($data),0,5) == '<?xml') {
 461                  // get the attributes
 462                  preg_match_all('/ (\w+=".+")/U', $data, $matches);
 463                  foreach($matches[1] as $match) {
 464                      list($attribute_name, $attribute_value) = (explode('=',$match));
 465                      $attribute_value = str_replace('"','',$attribute_value);
 466                      $this->_tree[0][XARXML_ATTR_ATTRIBUTES][$attribute_name] = $attribute_value;
 467                  }
 468              }
 469  
 470              if(trim($data) == '<!DOCTYPE') {
 471                  // We expect the next time a name for the doctype
 472                  $this->_state = XARXML_HSTATE_DTDNAME_EXPECTED;
 473              }
 474              break;
 475          case XARXML_HSTATE_DTDNAME_EXPECTED:
 476              //echo "Adding $data as doctype\n";
 477              $this->_state = XARXML_HSTATE_WAITCONDOPEN;
 478              $this->open_tag($parser,$data,array(),XML_DOCUMENT_TYPE_NODE);
 479              //print_r($this->_tree);
 480              return true;
 481              break;
 482          case XARXML_HSTATE_WAITCONDOPEN:
 483              if($data=='[') {
 484                  //echo "Gathering dtd data\n";
 485                  $this->_state = XARXML_HSTATE_DTDGATHERING;
 486              }
 487              break;
 488          case XARXML_HSTATE_DTDGATHERING:
 489              if($data==']') {
 490                  $this->_state = XARXML_HSTATE_DTDCLOSE_EXPECTED;
 491                  // For now just add the dtd data as content to the doctype node
 492                  //echo "Finished dtd gathering, adding $this->_dtd_data as cdata\n";
 493                  $this->character_data($parser,$this->_dtd_data);
 494                  //print_r($this->_tree);
 495                  $this->_dtd_data='';
 496              } else {
 497                  $this->_dtd_data .= " " . $data;
 498              }
 499              break;
 500          case XARXML_HSTATE_DTDCLOSE_EXPECTED:
 501              if($data=='>') {
 502                  //echo "Closing the doctype\n";
 503                  $this->_state = XARXML_HSTATE_NORMAL;
 504                  $this->close_tag($parser,'');
 505                  //print_r($this->_tree);
 506                  return true;
 507              }
 508              break;
 509  
 510          }
 511          return true;
 512  
 513      }
 514  
 515  
 516      /**
 517       * Character data handler is added as 'data' for the current tag
 518       *
 519       * @param object $parser the parser to which this handler is attached
 520       * @param string $data   character data found
 521       */
 522      function character_data($parser, $data)
 523      {
 524          // this handler can be called multiple times, so make sure we're not
 525          // overwriting ourselves, trust the depth to put things in the right place
 526          if(array_key_exists(XARXML_ATTR_CONTENT,$this->_tree[$this->_depth-1])) {
 527              $this->_tree[$this->_depth-1][XARXML_ATTR_CONTENT] .= trim($data);
 528          } else {
 529              $this->_tree[$this->_depth-1][XARXML_ATTR_CONTENT] = trim($data);
 530          }
 531      }
 532  
 533      /**
 534       * Start element handler
 535       *
 536       * This gets called when the start of a new <tag> is encountered
 537       * the tagname and its attributes are passed in as parameters.
 538       *
 539       * @param $parser  object the parser which this handler is attached to
 540       * @param $tagname string the start tag found
 541       * @param $attribs array  array of attributes with [attribname] => value pairs
 542       * @todo the ID attribute should be unique, check for that somehow
 543       *
 544       */
 545      function open_tag($parser, $tagname, $attribs, $type=XML_ELEMENT_NODE)
 546      {
 547          // Next line is basically the crux of the whole thing, to construct the tree
 548          $this->_tree[$this->_depth] = &$this->_tree[$this->_depth -1][XARXML_ATTR_CHILDREN][];
 549          $this->_tree[$this->_depth][XARXML_ATTR_NAME]= $tagname;
 550          $this->_tree[$this->_depth][XARXML_ATTR_TYPE] = $type;
 551          $this->_tree[$this->_depth][XARXML_ATTR_TAGINDEX]=$this->_tagindex;
 552  
 553          $attribs and $this->_tree[$this->_depth][XARXML_ATTR_ATTRIBUTES] = $attribs;
 554          // See if the ns handler has registered namespaces
 555          if(count($this->_nsregister) > 0 ) {
 556              foreach($this->_nsregister as $prefix => $uri) {
 557                  $this->_tree[$this->_depth][XARXML_ATTR_NAMESPACES][$prefix] = $uri;
 558              }
 559              // We can now reset the ns register, as they are stored in the structure
 560              $this->_nsregister=array();
 561          }
 562          $this->_tagindex++;
 563          $this->_depth++;
 564      }
 565  
 566      /**
 567       * Close element handler
 568       *
 569       * This handler is called when a closing </tag> is found. As tags in xml
 570       * should be properly nested we can count on these functions to be
 571       * called in order
 572       *
 573       * @param $parser object the parser to which handler is attached
 574       * @param $tagnam string tag which is closing
 575       *
 576       */
 577      function close_tag($parser, $tagname)
 578      {
 579          $this->_depth--;
 580          // We did the children thing already, so, we can get away with it now
 581          unset($this->_tree[$this->_depth]);
 582      }
 583  
 584      /**
 585       * Processing instruction handler
 586       *
 587       * We handle the processing instruction the same as a normal tag, but
 588       * distinguish it by using the type flag, adding the actual instructions
 589       * as content for the tag. This is not entirely right, but enough for now
 590       *
 591       * @param object $parser the parser to which this handler is attached
 592       * @param string $target the part after the < ? in the document
 593       * @param string $data   the contents of the processing instruction
 594       */
 595      function process_instruction($parser, $target , $data)
 596      {
 597          $this->open_tag($parser,$target,array(), XML_PI_NODE);
 598          $this->character_data($parser,$data);
 599          $this->close_tag($parser,$target);
 600      }
 601  
 602      /**
 603       * Handler called when an external entity reference is found
 604       *
 605       * This can get messy. The system_id or public_id or both, refer to the
 606       * location where the contents of the exernal entity can be found.
 607       *
 608       * We support the system_id for now. We take the class of the current handler
 609       * and instantiate a subparser which parses the externatl entity. That subtree
 610       * is inserted into the children element of the entity reference node as an entity
 611       * node.
 612       *
 613       * @todo figure out the logic for public_id and system id
 614       *
 615      */
 616      function external_entity_reference($parser, $entity_names,  $resolve_base, $system_id, $public_id)
 617      {
 618          //echo "External entity ref handler\n";
 619          $entity_list = explode(XARXML_ENTITY_SEP,$entity_names);
 620          $entity = array_pop($entity_list);
 621          if($system_id) {
 622              // Which handler are we in?
 623              $ee_handlername = get_class($this);
 624              $ee_handler = new $ee_handlername($this->_tagindex);
 625              $ee_parser = new xarXmlParser($parser->encoding,$ee_handler);
 626              // FIXME: I don't know the logic when to use public id and when to use system_id
 627              //        for now i only use system_id, which is a filename.
 628              // system_id is a filename, and as the $resolve_base is always empty we have to cope here
 629              if(!file_exists($system_id)) {
 630                  // couldn't find it directly through absolute reference, try relative
 631                  // if that doesn't help, the parser will raise an error for us
 632                  if($this->_resolve_base) $system_id=$this->_resolve_base ."/". $system_id;
 633              }
 634              if(!file_exists($system_id)) return false;
 635  
 636              // External entities may be empty
 637              $ee_tree = array();
 638              if(filesize($system_id) != 0) {
 639                  if(!$ee_parser->parseFile($system_id)) {
 640                      //echo $system_id .":". $ee_parser->lastmsg."\n";
 641                      return false;
 642                  }
 643                  $ee_tree = $ee_parser->tree;
 644              }
 645          }
 646          // The node in the parent is an entity reference
 647          $this->_tagindex = $ee_parser->handler->_tagindex;
 648          $this->open_tag($parser,$entity,array(), XML_ENTITY_REF_NODE);
 649          $this->_tree[$this->_depth-1][XARXML_ATTR_CHILDREN] = $ee_tree;
 650          $this->_tree[$this->_depth-1][XARXML_ATTR_CHILDREN][0][XARXML_ATTR_TYPE] =  XML_ENTITY_NODE;
 651          $this->_tree[$this->_depth-1][XARXML_ATTR_CHILDREN][0][XARXML_ATTR_NAME] =  $entity;
 652  
 653          $this->close_tag($parser, $entity);
 654          //print_r($this->_tree);
 655          return true;
 656      }
 657  
 658      /**
 659       * Handler for unparsed entities, non xml data, like images
 660       *
 661       * Likely we don't need this, but here it is for your overriding pleasure
 662       */
 663      function unparsed_entity($parser, $entity_name, $resolve_base, $system_id, $public_id, $notation_name)
 664      {
 665          //echo "Unparsed entity handler for $entity_name, $resolve_base, $system_id, $public_id, $notation_name\n";
 666          return true;
 667      }
 668  
 669      /**
 670       * Handler for notation declarations
 671       *
 672       * Likely we don't need this, but here it is.
 673       *
 674       * @todo at least add the node into the tree for this handler
 675       */
 676      function notation_declaration($parser, $notation_name, $resolve_base, $system_id, $public_id)
 677      {
 678          //echo "Notation declaration handler for $notation_name, $resolve_base, $system_id, $public_id\n";
 679          return true;
 680      }
 681  
 682      /**
 683       * Handler for namespace declarations.
 684       *
 685       * Handler to be called when a namespace is declared.
 686       * Namespace declarations occur inside start tags.
 687       * But the namespace declaration start handler is called
 688       * before the start tag handler for each namespace declared
 689       * in that start tag.
 690       */
 691      function start_namespace($parser, $prefix, $uri)
 692      {
 693          // We found a namespace declaration, register them so, the open tag can handle it
 694          $this->_nsregister[$prefix]= $uri;
 695          return true;
 696      }
 697  
 698      /**
 699       * Handler for namespace declarations
 700       *
 701       * Handler to be called when leaving the scope of a
 702       * namespace declaration. This will be called, for each
 703       * namespace declaration, after the handler for the end
 704       * tag of the element in which the namespace was declared.
 705       *
 706       * @access protected
 707       * @param object $parser parser object to which handler is attached
 708       * @param string $prefix by which prefix is this namespace identified in the doc
 709       */
 710      function end_namespace($parser, $prefix)
 711      {
 712          // Reset the namespace register, bit paranoid, but can't hurt
 713          $this->_nsregister=array();
 714          return true;
 715      }
 716  
 717      /**
 718       * Handler reset
 719       *
 720       * @access protected
 721       */
 722      function _reset()
 723      {
 724          $this->_dtd_data='';
 725          $this->_state = XARXML_HSTATE_INITIAL;
 726          $this->_tree=array();
 727          $this->_depth=1;
 728          $this->_resolve_base=NULL;
 729      }
 730  }
 731  
 732  //
 733  // TEMPORARY FUNCTIONS
 734  //
 735  
 736  // Just for convenience for now, should go into separate class
 737  function getElementsByname($name,$tree=NULL)
 738  {
 739      $results=array();
 740      $query=array('type'  => XARXML_ATTR_NAME,
 741                   'match' => $name
 742                   );
 743      if(!$tree) return;
 744  
 745      // return array of nodes which are of type XML_ELEMENT_NODE and have name = $name
 746      // First node of the tree will always be document node
 747      $results = queryTree($tree[0],$query, XML_ELEMENT_NODE);
 748  
 749      return $results;
 750  }
 751  
 752  function getSubTree($element_id, $tree=NULL)
 753  {
 754      $results=array();
 755      $query =array('type'  => XARXML_ATTR_TAGINDEX,
 756                    'match' => $element_id);
 757      if(!$tree) return;
 758      $results = queryTree($tree[0],$query, XML_ELEMENT_NODE,true);
 759  
 760      return $results;
 761  }
 762  
 763  /**
 764   * Just for convenience for now, should go into separate class
 765   *
 766   * @todo remove the @
 767   *
 768   */
 769  function queryTree($subtree, $query, $nodetype,$returnsubtree=false)
 770  {
 771      $results = array();
 772  
 773      // If the node has children inspect them first, so we have simpler code in the second part (the unset)
 774      if(array_key_exists(XARXML_ATTR_CHILDREN, $subtree)) {
 775          foreach($subtree[XARXML_ATTR_CHILDREN] as $child) {
 776              $results = array_merge($results, queryTree($child,$query,$nodetype,$returnsubtree));
 777          }
 778      }
 779  
 780      // Inspect this node
 781      if((@$subtree[XARXML_ATTR_TYPE] == $nodetype) && ($subtree[$query['type']] === $query['match'])) {
 782          // We found a node, add it to the result array
 783          if(!$returnsubtree) {
 784              unset($subtree[XARXML_ATTR_CHILDREN]);
 785          }
 786          $results[] = $subtree;
 787      }
 788  
 789      return $results;
 790  }
 791  ?>

title

Description

title

Description

title

Description

title

title

Body