Structures_DataGrid_DataSource_XML
[ class tree: Structures_DataGrid_DataSource_XML ] [ index: Structures_DataGrid_DataSource_XML ] [ all elements ]

Source for file XML.php

Documentation is available at XML.php

  1. <?php
  2. /**
  3.  * XML DataSource driver
  4.  * 
  5.  * PHP versions 4 and 5
  6.  *
  7.  * LICENSE:
  8.  * 
  9.  * Copyright (c) 1997-2007, Andrew Nagy <asnagy@webitecture.org>,
  10.  *                          Olivier Guilyardi <olivier@samalyse.com>,
  11.  *                          Mark Wiesemann <wiesemann@php.net>
  12.  * All rights reserved.
  13.  *
  14.  * Redistribution and use in source and binary forms, with or without
  15.  * modification, are permitted provided that the following conditions
  16.  * are met:
  17.  *
  18.  *    * Redistributions of source code must retain the above copyright
  19.  *      notice, this list of conditions and the following disclaimer.
  20.  *    * Redistributions in binary form must reproduce the above copyright
  21.  *      notice, this list of conditions and the following disclaimer in the
  22.  *      documentation and/or other materials provided with the distribution.
  23.  *    * The names of the authors may not be used to endorse or promote products
  24.  *      derived from this software without specific prior written permission.
  25.  *
  26.  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
  27.  * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
  28.  * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
  29.  * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
  30.  * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
  31.  * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
  32.  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
  33.  * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
  34.  * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
  35.  * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
  36.  * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  37.  *
  38.  * CSV file id: $Id: XML.php 260315 2008-05-26 14:34:19Z olivierg $
  39.  * 
  40.  * @version  $Revision: 260315 $
  41.  * @category Structures
  42.  * @package  Structures_DataGrid_DataSource_XML
  43.  * @license  http://opensource.org/licenses/bsd-license.php New BSD License
  44.  */
  45.  
  46. require_once 'Structures/DataGrid/DataSource/Array.php';
  47.  
  48. /**
  49.  * XML DataSource driver
  50.  *
  51.  * This class is a DataSource driver for XML data. It accepts strings
  52.  * and filenames. An XPath expression can be specified to extract data
  53.  * rows from the given XML data.
  54.  *
  55.  * SUPPORTED OPTIONS:
  56.  * 
  57.  * - path:             (string) XPath used to extract the data rows. The default
  58.  *                              is "*", which means all children of the context
  59.  *                              (root) node.
  60.  * - namespaces:       (array)  Pairs of prefix/uri to register for XPath
  61.  *                              processing
  62.  * - fieldAttribute:  (string)  Which attribute of the XML source should be used
  63.  *                              as column field name (only used if the XML source
  64.  *                              has attributes).
  65.  * - labelAttribute:  (string)  Which attribute of the XML source should be used
  66.  *                              as column label (only used if 'generate_columns'
  67.  *                              is true and the XML source has attributes).
  68.  *
  69.  * @example  bind-xml1.php  Bind a simple XML string
  70.  * @example  bind-xml2.php  Bind a more complex XML string using XPath
  71.  * @example  bind-atom.php  Bind an Atom feed with XPath and namespace
  72.  * @package  Structures_DataGrid_DataSource_XML
  73.  * @author   Olivier Guilyardi <olivier@samalyse.com>
  74.  * @author   Mark Wiesemann <wiesemann@php.net>
  75.  * @category Structures
  76.  * @version  $Revision: 260315 $
  77.  */
  78.     Structures_DataGrid_DataSource_Array
  79. {
  80.     // TODO: use XML_Indexing package for reading (=> streaming support)
  81.  
  82.     /**
  83.      * Constructor
  84.      * 
  85.      */
  86.     {
  87.         parent::Structures_DataGrid_DataSource_Array();
  88.         $this->_addDefaultOptions(
  89.             array(
  90.                 'path'           => '*',
  91.                 'namespaces'     => array(),
  92.                 'xpath'          => null,
  93.                 'fieldAttribute' => null,
  94.                 'labelAttribute' => null
  95.             )
  96.         );
  97.     }
  98.  
  99.     /**
  100.      * Bind XML data
  101.      * 
  102.      * @access  public
  103.      * @param   string  $xml        XML string or filename/stream
  104.      * @param   array   $options    Options as an associative array
  105.      * @return  mixed               true on success, PEAR_Error on failure
  106.      */
  107.     function bind($xml$options = array())
  108.     {
  109.         if ($options{
  110.             $this->setOptions($options);
  111.         }
  112.  
  113.         $this->doc $this->_loadDocument($xml);
  114.         if (PEAR::isError($this->doc)) {
  115.             return $this->doc;
  116.         }
  117.  
  118.         if ($path $this->_options['xpath']{
  119.             $this->_options['path'= "$path/*";
  120.         }
  121.  
  122.         $nodes $this->doc->xpath($this->_options['path']
  123.                                    $this->_options['namespaces'])
  124.  
  125.         foreach ($nodes as $rowNode{
  126.             $this->_ar[$this->_processRow($rowNode);
  127.         }
  128.  
  129.         if (!$this->_options['labels']{
  130.             $this->_options['labels'$this->_extractLabels($nodes);
  131.         }
  132.  
  133.         if ($this->_ar && !$this->_options['fields']{
  134.             $this->setOption('fields'array_keys($this->_ar[0]));
  135.         }
  136.  
  137.         $this->doc->free();
  138.  
  139.         return true;
  140.     }
  141.  
  142.     /**
  143.      * Load XML Document Model
  144.      *
  145.      * @access  private
  146.      * @param   string  $xml    XML string or filename
  147.      * @return  object  Structures_DataGrid_DataSource_XMLDomWrapper (PHP5) or
  148.      *                   Structures_DataGrid_DataSource_XMLDomXmlWrapper (PHP4)
  149.      */
  150.     function _loadDocument($xml)
  151.     {
  152.         if (extension_loaded('dom')) {
  153.             $doc = new Structures_DataGrid_DataSource_XMLDomWrapper();
  154.         elseif (extension_loaded('domxml')) {
  155.             $doc = new Structures_DataGrid_DataSource_XMLDomXmlWrapper();
  156.         else {
  157.             return PEAR::raiseError('DOM (PHP5) or DOM XML (PHP4) is required '.
  158.                                     'for XML processing');
  159.         }
  160.  
  161.         $test strstr($xml'<'
  162.             ? $doc->loadString($xml$doc->loadFile($xml);
  163.  
  164.         if (!$test{
  165.             return PEAR::raiseError('XML couldn\'t be read.');
  166.         }
  167.  
  168.         return $doc;
  169.     }
  170.  
  171.     /**
  172.      * Extract a data row out of a row node
  173.      *
  174.      * @access private
  175.      * @param  object $node Row node
  176.      * @return array        Fields and values
  177.      */
  178.     function _processRow($rowNode)
  179.     {
  180.         $row = array();
  181.         foreach ($rowNode->childNodes(as $fieldNode{
  182.             $this->_extractFields($row$fieldNode);
  183.         }
  184.         foreach ($rowNode->attributes(as $name => $value{
  185.             $row[= array('field' => "attributes$name"'content' => $value);
  186.         }
  187.         $flat = array();
  188.         $indexes = array();
  189.         foreach ($row as $item{
  190.             $field $item['field'];
  191.             if (isset($indexes[$field])) {
  192.                 $i = ++$indexes[$field];
  193.             else {
  194.                 $indexes[$field= 0;
  195.                 $i '';
  196.             }
  197.             if (isset($item['content'])) {
  198.                 $flat["$field$i"$item['content'];
  199.             }
  200.             if (isset($item['attributes'])) {
  201.                 foreach ($item['attributes'as $name => $value{
  202.                     $flat["$field{$i}attributes$name"$value;
  203.                 }
  204.             }
  205.         }
  206.         return $flat;
  207.     }
  208.  
  209.     /**
  210.      * Extract one or more data fields out of a field node
  211.      *
  212.      * @access private
  213.      * @param array  $row       reference to serialized row data, to put the
  214.      *                           news fields into
  215.      * @param object $fieldNode DOM field node
  216.      * @return void 
  217.      */
  218.     function _extractFields(&$row$fieldNode)
  219.     {
  220.         $content '';
  221.         foreach ($fieldNode->childNodes(as $valueNode{
  222.             $nodeType $valueNode->nodeType();
  223.             if (($nodeType == XML_CDATA_SECTION_NODE
  224.                     || ($nodeType == XML_TEXT_NODE)) {
  225.                 $content .= $valueNode->nodeValue();
  226.             else {
  227.                 $content .= $this->doc->getXML($valueNode);
  228.             }
  229.         }
  230.         $fieldName $this->_getFieldName($fieldNode);
  231.         $nodeName $fieldNode->nodeName();
  232.         if ($nodeName != $fieldName{
  233.             $row[= array('field' => $fieldName'content' => $content);
  234.             $row[= array('field' => $nodeName,
  235.                     'attributes' => $fieldNode->attributes());
  236.         else {
  237.             $row[= array('field' => $nodeName'content' => $content
  238.                     'attributes' => $fieldNode->attributes());
  239.         }
  240.     }
  241.  
  242.     /**
  243.      * Determine the main field name of a field node
  244.      *
  245.      * @access private
  246.      * @param object $fieldNode DOM field node
  247.      * @return string 
  248.      */
  249.     function _getFieldName($fieldNode)
  250.     {
  251.         $nodeName $fieldNode->nodeName();
  252.         $fieldName $nodeName;
  253.         foreach ($fieldNode->attributes(as $name => $content{
  254.             if ($name == $this->_options['fieldAttribute']{
  255.                 $fieldName $content;
  256.             }
  257.         }
  258.         return $fieldName;
  259.     }
  260.  
  261.     /**
  262.      * Extract column labels
  263.      *
  264.      * @access private
  265.      * @param  array  $nodes Array of row nodes
  266.      * @return array         Fields and Labels
  267.      */
  268.     function _extractLabels($nodes)
  269.     {
  270.         $labels = array();
  271.         $labelAttr $this->_options['labelAttribute'];
  272.         $fieldAttr $this->_options['fieldAttribute'];
  273.  
  274.         if (count($nodes&& $labelAttr{
  275.             foreach ($nodes[0]->childNodes(as $fieldNode{
  276.                 if (($name $fieldAttr&& $fieldNode->hasAttribute($name)) {
  277.                     $fieldName $fieldNode->getAttribute($name);
  278.                 else {
  279.                     $fieldName $fieldNode->nodeName();
  280.                 }
  281.                 if ($fieldNode->hasAttribute($labelAttr)) {
  282.                     $labels[$fieldName
  283.                         = $fieldNode->getAttribute($labelAttr);
  284.                 }
  285.             }
  286.         }
  287.         return $labels;
  288.     }
  289. }
  290.  
  291.  
  292. /**
  293.  * XML Document Model core Wrapper
  294.  *
  295.  * @access private
  296.  * @package  Structures_DataGrid_DataSource_XML
  297.  * @author   Olivier Guilyardi <olivier@samalyse.com>
  298.  * @category Structures
  299.  */
  300. class Structures_DataGrid_DataSource_XMLWrapper
  301. {
  302.     var $object;
  303.  
  304.     /**
  305.      * Constructor
  306.      *
  307.      * @param object $domObject DOM or DOM XML object
  308.      */
  309.     function Structures_DataGrid_DataSource_XMLWrapper($domObject = null)
  310.     {
  311.         $this->object $domObject;
  312.     }
  313.  
  314.     /**
  315.      * Decorate items of an iterable object
  316.      *
  317.      * @param  array  $mixed Array or Iterable DOM/DOM XML object
  318.      * @return array         Array of wrapped items
  319.      */
  320.     function wrapArray($object)
  321.     {
  322.         $wrapped = array();
  323.         $class get_class($this);
  324.         foreach ($object as $key => $value{
  325.             $wrapped[$key= new $class($value);
  326.         }
  327.         return $wrapped;
  328.     }
  329. }
  330.  
  331. /**
  332.  * XML Document Model DOM (PHP5) Wrapper
  333.  *
  334.  * @access private
  335.  * @package  Structures_DataGrid_DataSource_XML
  336.  * @author   Olivier Guilyardi <olivier@samalyse.com>
  337.  * @category Structures
  338.  */
  339. class Structures_DataGrid_DataSource_XMLDomWrapper
  340.     extends Structures_DataGrid_DataSource_XMLWrapper
  341. {
  342.     /**
  343.      * Load an XML string
  344.      * 
  345.      * @param  string $xml 
  346.      * @return bool   true on success, false on failure
  347.      */
  348.     function loadString($xml)
  349.     {
  350.         $this->object = new DOMDocument();
  351.         $this->object->preserveWhiteSpace = false;
  352.         return $this->object->loadXML($xml);
  353.     }
  354.  
  355.     /**
  356.      * Load an XML file
  357.      * 
  358.      * @param  string $filename 
  359.      * @return bool   true on success, false on failure
  360.      */
  361.     function loadFile($filename)
  362.     {
  363.         $this->object = new DOMDocument();
  364.         $this->object->preserveWhiteSpace = false;
  365.         return $this->object->load($filename);
  366.     }
  367.  
  368.     /**
  369.      * Run an xpath query, registering namespaces
  370.      *
  371.      * @param  string $query      XPath query
  372.      * @param  array  $namespaces prefix/uri pairs
  373.      * @return array              Nodes found
  374.      */
  375.     function xpath($query$namespaces)
  376.     {
  377.         $xpath = new DOMXPath($this->object);
  378.         foreach ($namespaces as $prefix => $uri{
  379.             $xpath->registerNamespace($prefix$uri);
  380.         }
  381.  
  382.         return $this->wrapArray($xpath->query($query))
  383.     }
  384.  
  385.     /**
  386.      * Return child nodes
  387.      *
  388.      * @return array Child nodes
  389.      */
  390.     function childNodes()
  391.     {
  392.         return $this->wrapArray($this->object->childNodes);
  393.     }
  394.  
  395.     /**
  396.      * Dump a node into an XML string
  397.      *
  398.      * @param  object $node Node to dump
  399.      * @return string       XML
  400.      */
  401.     function getXML($node)
  402.     {
  403.         return $this->object->saveXML($node->object);
  404.     }
  405.  
  406.     /**
  407.      * Get the node name
  408.      *
  409.      * @return string 
  410.      */
  411.     function nodeName()
  412.     {
  413.         return $this->object->nodeName;
  414.     }
  415.  
  416.     /**
  417.      * Get all node's attributes
  418.      *
  419.      * @return array name/value pairs
  420.      */
  421.     function attributes()
  422.     {
  423.         $attributes = array();
  424.         foreach ($this->object->attributes as $item{
  425.             $attributes[$item->name$item->value;
  426.         }
  427.         return $attributes;
  428.     }
  429.  
  430.     /**
  431.      * Check for attribute existence
  432.      *
  433.      * @return bool 
  434.      */
  435.     function hasAttribute($name)
  436.     {
  437.         return $this->object->hasAttribute($name);
  438.     }
  439.  
  440.     /**
  441.      * Get an attribute value
  442.      *
  443.      * @param  string $name 
  444.      * @return string 
  445.      */
  446.     function getAttribute($name)
  447.     {
  448.         return $this->object->getAttribute($name);
  449.     }
  450.  
  451.     /**
  452.      * Get this node type
  453.      *
  454.      * @param  string $name 
  455.      * @return string 
  456.      */
  457.     function nodeType()
  458.     {
  459.         return $this->object->nodeType;
  460.     }
  461.  
  462.     /**
  463.      * Get this node value
  464.      *
  465.      * @param  string $name 
  466.      * @return string 
  467.      */
  468.     function nodeValue()
  469.     {
  470.         return $this->object->nodeValue;
  471.     }
  472.  
  473.     /**
  474.      * Free document memory
  475.      */
  476.     function free()
  477.     {
  478.         $this->object = null;
  479.     }
  480. }
  481.  
  482. /**
  483.  * XML Document Model DOM XML (PHP4) Wrapper
  484.  *
  485.  * @access private
  486.  * @package  Structures_DataGrid_DataSource_XML
  487.  * @author   Olivier Guilyardi <olivier@samalyse.com>
  488.  * @category Structures
  489.  */
  490. class Structures_DataGrid_DataSource_XMLDomXmlWrapper
  491.     extends Structures_DataGrid_DataSource_XMLWrapper
  492. {
  493.     /**
  494.      * Load an XML string
  495.      * 
  496.      * @param  string $xml 
  497.      * @return bool   true on success, false on failure
  498.      */
  499.     function loadString($xml)
  500.     {
  501.         $this->object = domxml_open_mem($xmlDOMXML_LOAD_DONT_KEEP_BLANKS);
  502.         return (bool) $this->object;
  503.     }
  504.  
  505.     /**
  506.      * Load an XML file
  507.      * 
  508.      * @param  string $filename 
  509.      * @return bool   true on success, false on failure
  510.      */
  511.     function loadFile($filename)
  512.     {
  513.         $this->object = domxml_open_file($filenameDOMXML_LOAD_DONT_KEEP_BLANKS);
  514.         return (bool) $this->object;
  515.     }
  516.  
  517.     /**
  518.      * Run an xpath query, registering namespaces
  519.      *
  520.      * @param  string $query      XPath query
  521.      * @param  array  $namespaces prefix/uri pairs
  522.      * @return array              Nodes found
  523.      */
  524.     function xpath($query$namespaces)
  525.     {
  526.         $xpath = xpath_new_context($this->object);
  527.         foreach ($namespaces as $prefix => $uri{
  528.             xpath_register_ns($xpath$prefix$uri);
  529.         }
  530.         $result = xpath_eval($xpath$query);
  531.         return $this->wrapArray($result->nodeset);
  532.     }
  533.  
  534.     /**
  535.      * Return child nodes
  536.      *
  537.      * @return array Child nodes
  538.      */
  539.     function childNodes()
  540.     {
  541.         return $this->wrapArray($this->object->child_nodes());
  542.     }
  543.  
  544.     /**
  545.      * Dump a node into an XML string
  546.      *
  547.      * @param  object $node Node to dump
  548.      * @return string       XML
  549.      */
  550.     function getXML($node)
  551.     {
  552.         return $this->object->dump_node($node->object);
  553.     }
  554.  
  555.     /**
  556.      * Get the node name
  557.      *
  558.      * @return string 
  559.      */
  560.     function nodeName()
  561.     {
  562.         return $this->object->node_name();
  563.     }
  564.  
  565.     /**
  566.      * Get all node's attributes
  567.      *
  568.      * @return array name/value pairs
  569.      */
  570.     function attributes()
  571.     {
  572.         $attributes = array();
  573.         if ($items $this->object->attributes()) {
  574.             foreach ($items as $item{
  575.                 $attributes[$item->name()$item->value();
  576.             }
  577.         }
  578.         return $attributes;
  579.     }
  580.  
  581.     /**
  582.      * Check for attribute existence
  583.      *
  584.      * @return bool 
  585.      */
  586.     function hasAttribute($name)
  587.     {
  588.         if (method_exists($this->object'has_attribute')) {
  589.             return $this->object->has_attribute($name);
  590.         }
  591.         return false;
  592.     }
  593.  
  594.     /**
  595.      * Get an attribute value
  596.      *
  597.      * @param  string $name 
  598.      * @return string 
  599.      */
  600.     function getAttribute($name)
  601.     {
  602.         return $this->object->get_attribute($name);
  603.     }
  604.  
  605.     /**
  606.      * Get this node type
  607.      *
  608.      * @param  string $name 
  609.      * @return string 
  610.      */
  611.     function nodeType()
  612.     {
  613.         return $this->object->node_type();
  614.     }
  615.  
  616.     /**
  617.      * Get this node value
  618.      *
  619.      * @param  string $name 
  620.      * @return string 
  621.      */
  622.     function nodeValue()
  623.     {
  624.         return $this->object->node_value();
  625.     }
  626.  
  627.     /**
  628.      * Free document memory
  629.      */
  630.     function free()
  631.     {
  632.         $this->object->free();
  633.         $this->object = null;
  634.     }
  635. }
  636.  
  637. /* vim: set expandtab tabstop=4 shiftwidth=4: */
  638. ?>

Documentation generated on Sun, 10 Oct 2010 13:30:03 +0000 by phpDocumentor 1.4.3. PEAR Logo Copyright © PHP Group 2004.