home *** CD-ROM | disk | FTP | other *** search
/ Cricao de Sites - 650 Layouts Prontos / WebMasters.iso / Servidores / xampp-win32-1.6.7-installer.exe / php / PEAR / XML / XPath / result.php < prev   
Encoding:
PHP Script  |  2008-07-02  |  16.0 KB  |  516 lines

  1. <?php
  2. // {{{ license
  3.  
  4. // +----------------------------------------------------------------------+
  5. // | PHP version 4.0                                                      |
  6. // +----------------------------------------------------------------------+
  7. // | Copyright (c) 1997-2001 The PHP Group                                |
  8. // +----------------------------------------------------------------------+
  9. // | This source file is subject to version 2.0 of the PHP license,       |
  10. // | that is bundled with this package in the file LICENSE, and is        |
  11. // | available at through the world-wide-web at                           |
  12. // | http://www.php.net/license/2_02.txt.                                 |
  13. // | If you did not receive a copy of the PHP license and are unable to   |
  14. // | obtain it through the world-wide-web, please send a note to          |
  15. // | license@php.net so we can mail you a copy immediately.               |
  16. // +----------------------------------------------------------------------+
  17. // | Authors: Dan Allen <dan@mojavelinux.com>                             |
  18. // +----------------------------------------------------------------------+
  19.  
  20. // $Id: result.php,v 1.15 2005/10/12 14:48:55 toggg Exp $
  21.  
  22. // }}}
  23. // {{{ description
  24.  
  25. // Result class for the Xpath/DOM XML manipulation and query interface.
  26.  
  27. // }}}
  28. // {{{ constants
  29.  
  30. define('XML_XPATH_SORT_TEXT_ASCENDING',     1);
  31. define('XML_XPATH_SORT_NUMBER_ASCENDING',   2);
  32. define('XML_XPATH_SORT_NATURAL_ASCENDING',  3);
  33. define('XML_XPATH_SORT_TEXT_DESCENDING',    4);
  34. define('XML_XPATH_SORT_NUMBER_DESCENDING',  5);
  35. define('XML_XPATH_SORT_NATURAL_DESCENDING', 6);
  36.  
  37. // }}}
  38.  
  39. // {{{ class XML_XPath_result
  40.  
  41. /**
  42.  * Interface for an XML_XPath result so that one can cycle through the result set and manipulate
  43.  * the main tree with DOM methods using a seperate pointer then the original class.
  44.  *
  45.  * @version  Revision: 1.1
  46.  * @author   Dan Allen <dan@mojavelinux.com>
  47.  * @access   public
  48.  * @since    PHP 4.2.1
  49.  * @package  XML_XPath
  50.  */
  51.  
  52. // }}}
  53. class XML_XPath_result extends XML_XPath_common {
  54.     // {{{ properties
  55.  
  56.     /**
  57.      * original xpath query, stored when we need to sort
  58.      * @var string $query
  59.      */
  60.     var $query;
  61.  
  62.     /**
  63.      * determines if we have counted the first node of the result nodeset
  64.      * @var boolean $isRewound
  65.      */
  66.     var $isRewound;
  67.     
  68.     /**
  69.      * The type of result that the query generated
  70.      * @var int $type
  71.      */
  72.     var $type;
  73.  
  74.     /**
  75.      * either array of nodesets, string, boolean or number from xpath/DOM query
  76.      * @var mixed $data
  77.      */
  78.     var $data;
  79.     
  80.     /**
  81.      * xpath context object for the current domxml object
  82.      * @var object $ctx
  83.      */
  84.     var $ctx;
  85.  
  86.     /**
  87.      * domxml object, need for many common functions
  88.      * @var object $xml
  89.      */
  90.     var $xml;
  91.     // }}}
  92.     // {{{ constructor
  93.  
  94.     function XML_XPath_result($in_data, $in_type, $in_query, &$in_ctx, &$in_xml) 
  95.     {
  96.         $this->query = $in_query;
  97.         $this->type = $in_type;
  98.         $this->data = $in_data;
  99.         $this->ctx = &$in_ctx;
  100.         $this->xml =&$in_xml;
  101.         // move the pointer to the first node if at least one node in the result exists
  102.         // for convience, just so we don't have to call nextNode() if we expect only one
  103.         $this->rewind();
  104.     }
  105.  
  106.     // }}}
  107.     // {{{ mixed   getData()
  108.     
  109.     /**
  110.      * Return the data from the xpath query.  This function will be used mostly for xpath
  111.      * queries that result in scalar results, but in the case of nodesets, returns size
  112.      *
  113.      * @access public
  114.      * @return mixed scalar result from xpath query or size of nodeset
  115.      */
  116.     function getData()
  117.     {
  118.         switch($this->type) {
  119.             case XPATH_BOOLEAN:
  120.                 return $this->data ? true : false;
  121.             break;
  122.  
  123.             case XPATH_NODESET:
  124.                 if (!$this->pointer) {
  125.                     return null;
  126.                 }
  127.                 else {
  128.                     return $this->pointer->node_type() == XML_ATTRIBUTE_NODE ? $this->pointer->value() : $this->substringData();
  129.                 }
  130.             break;
  131.  
  132.             case XPATH_STRING:
  133.             case XPATH_NUMBER:
  134.                 return $this->data;
  135.             break;
  136.         }
  137.     }
  138.  
  139.     // }}}
  140.     // {{{ int     resultType()
  141.  
  142.     /**
  143.      * Retrieve the type of result that was returned by the xpath query.
  144.      *
  145.      * @access public
  146.      * @return int code corresponding to the xpath result types constants
  147.      */
  148.     function resultType() 
  149.     {
  150.         return $this->type;
  151.     }
  152.  
  153.     // }}}
  154.     // {{{ int     numResults()
  155.  
  156.     /**
  157.      * Return the number of nodes if the result is a nodeset or 1 for scalar results.
  158.      * result (boolean, string, numeric) xpath queries
  159.      *
  160.      * @access public
  161.      * @return int number of results returned by xpath query
  162.      */
  163.     function numResults() {
  164.         return count($this->data);
  165.     }
  166.  
  167.     // }}}
  168.     // {{{ int     getIndex()
  169.  
  170.     /**
  171.      * Return the index of the result nodeset.
  172.      *
  173.      * @access public
  174.      * @return int current index of the result nodeset
  175.      */
  176.     function getIndex()
  177.     {
  178.         return key($this->data);
  179.     }
  180.  
  181.     // }}}
  182.     // {{{ boolean sort()
  183.  
  184.     /**
  185.      * Sort the nodeset in this result.  The sort can be either ascending or descending, and
  186.      * the comparisons can be text, number or natural (see the constants above). The sort
  187.      * axis is provided as an xpath query and is the location path relative to the node given.
  188.      * For example, so sort on an attribute, you would provide '@foo' and it will look at the
  189.      * attribute for each node.
  190.      *
  191.      * NOTE: If the axis is not found, the node will comes first in the sort order for ascending 
  192.      * order and at the end for descending orde.
  193.      *
  194.      * @param  string $in_sortXpath relative xpath query location to each node in nodeset
  195.      * @param  int $in_order either XML_XPATH_SORT_TEXT_[DE|A]SCENDING, 
  196.      *                              XML_XPATH_SORT_NUMBER_[DE|A]SCENDING,
  197.      *                              XML_XPATH_SORT_NATURAL_[DE|A]SCENDING
  198.      *
  199.      * @access public
  200.      * @return boolean success (return false if nothing to sort)
  201.      */
  202.     function sort($in_sortXpath = '.', $in_order = XML_XPATH_SORT_TEXT_ASCENDING, $in_permanent = false) 
  203.     {
  204.         // make sure we are dealing with a result that is a nodeset
  205.         if ($this->type != XPATH_NODESET || !$this->numResults()) {
  206.             return false;
  207.         }
  208.  
  209.         $data = array();
  210.  
  211.         // we don't need to run it again if we are soring on the current node values
  212.         if ($in_sortXpath == '' || $in_sortXpath == '.') {
  213.             foreach ($this->data as $index => $node) {
  214.                 $data[] = $node->get_content();
  215.             }
  216.         }
  217.         // we need to run the query again
  218.         else {
  219.             // we never actually ran the query, but we can rebuild it...and we will do that now
  220.             // this is for DOM queries, such as childNodes() and getElementsByTagName()
  221.             if (is_array($this->query)) {
  222.                 $this->query = $this->getNodePath(reset($this->query)) . end($this->query);
  223.             }
  224.  
  225.             // here I am reissuing the query, but with the sort path appended followed by the
  226.             // node in a logical 'OR'.  The trick here is that I can keep the original nodes
  227.             // in sorted order and then just weed out the nodes I used to sort.
  228.             $xpathResult = @$this->ctx->xpath_eval($this->query . '/' . $in_sortXpath . '|' . $this->query);
  229.             if (!$xpathResult || empty($xpathResult->nodeset)) {
  230.                 return PEAR::raiseError(null, XML_XPATH_INVALID_QUERY, null, E_USER_NOTICE, "Query {$this->query}/$in_sortXpath", 'XML_XPath_Error', true);
  231.             }
  232.  
  233.             // Sorting Process: 
  234.             // The reason we did a double query is so that we could line up the original nodes
  235.             // with the original data set, fill in any parts of the sorted nodeset that have
  236.             // missing sort nodes, sort the sorted nodeset and then reindex the original data array
  237.  
  238.             $origIndex = 0;
  239.             $sortIndex = 0;
  240.             while(isset($this->data[$origIndex])) {
  241.                 // make sure we are lined up on original nodes, then we can proceed to check
  242.                 // the next node in each nodeset to determine of the sort node was found
  243.                 if ($this->data[$origIndex] == $xpathResult->nodeset[$sortIndex]) {
  244.                     $origIndex++;
  245.                     $sortIndex++;
  246.                     // make sure we have not advanced beyond the end of the sort nodeset
  247.                     if (isset($xpathResult->nodeset[$sortIndex])) {
  248.                         // if the values of the next two indices of the sort nodeset and the
  249.                         // original nodeset are the same, we had a missing node
  250.                         if (isset($this->data[$origIndex]) && $this->data[$origIndex] == $xpathResult->nodeset[$sortIndex]) {
  251.                             $data[] = '';
  252.                         }
  253.                         // okay, they were different, we found a sort node, get its value
  254.                         else {
  255.                             $data[] = $xpathResult->nodeset[$sortIndex]->get_content();
  256.                         }
  257.                     }
  258.                     // the last sort nodeset element is missing, which means the sort nodeset
  259.                     // was missing the last sort node
  260.                     else {
  261.                         $data[] = '';
  262.                     }
  263.                 }
  264.                 else {
  265.                     $sortIndex++;
  266.                 }
  267.             }
  268.         }
  269.  
  270.         switch ($in_order) {
  271.             case XML_XPATH_SORT_TEXT_ASCENDING:
  272.                 asort($data, SORT_STRING);
  273.             break;
  274.  
  275.             case XML_XPATH_SORT_NUMBER_ASCENDING:
  276.                 asort($data, SORT_NUMERIC);
  277.             break;
  278.  
  279.             case XML_XPATH_SORT_NATURAL_ASCENDING:
  280.                 natsort($data);
  281.             break;
  282.  
  283.             case XML_XPATH_SORT_TEXT_DESCENDING:
  284.                 arsort($data, SORT_STRING);
  285.             break;
  286.  
  287.             case XML_XPATH_SORT_NUMBER_DESCENDING:
  288.                 arsort($data, SORT_NUMERIC);
  289.             break;
  290.  
  291.             case XML_XPATH_SORT_NATURAL_DESCENDING:
  292.                 natsort($data);
  293.                 $data = array_reverse($data, true);
  294.             break;
  295.  
  296.             default:
  297.                 asort($data);
  298.             break;
  299.         }
  300.  
  301.         $dataReordered = array();
  302.         // this is NOT just array_values, we need to use the keys to put the values
  303.         // in the correct order
  304.         foreach ($data as $reindex => $value) {
  305.             $dataReordered[] = $this->data[$reindex];
  306.         }
  307.  
  308.         $this->data = $dataReordered;
  309.  
  310.         // if this is a permanent sort, make the change to the main tree
  311.         if ($in_permanent && $parent = $this->data[0]->parent_node()) {
  312.             // nix all the children by overwriting node and fixing attributes
  313.             $attributes = $parent->has_attributes() ? $parent->attributes() : array();
  314.             $parent->replace_node($clone = $parent->clone_node());
  315.             $parent = $clone;
  316.  
  317.             foreach($attributes as $attributeNode) {
  318.                // waiting on set_attribute_node() to work here
  319.                $parent->set_attribute($attributeNode->node_name(), $attributeNode->value());
  320.             }
  321.             
  322.             foreach ($this->data as $key => $sortedNode) {
  323.                 $this->data[$key] = $parent->append_child($sortedNode);
  324.             }
  325.         }
  326.  
  327.         // rewind to the beginning of the data set
  328.         $this->rewind();
  329.  
  330.         return true;
  331.     }
  332.      
  333.     // }}}
  334.     // {{{ boolean rewind()
  335.  
  336.     /**
  337.      * Reset the result index back to the beginning, if this is an XPATH_NODESET
  338.      *
  339.      * @access public
  340.      * @return boolean success
  341.      */
  342.     function rewind()
  343.     {
  344.         if (is_array($this->data)) {
  345.             $this->pointer = reset($this->data);
  346.             $this->isRewound = true;
  347.             return true;
  348.         }
  349.         
  350.         return false;
  351.     }
  352.  
  353.     // }}}
  354.     // {{{ boolean next()
  355.  
  356.     /**
  357.      * Move to the next node in the nodeset of results.  This can be used inside of a
  358.      * while loop, so that it is possible to step through the nodes one by one.
  359.      * It is important to note that the first call to next will put the pointer at
  360.      * the first index and not the second...this is just a more convenient way of
  361.      * handling the logic.  If you rewind() the data and then call next() as the conditional
  362.      * on a while loop, you can work through each of the results from the first to the last.
  363.      *
  364.      * @access public
  365.      * @return boolean success node found and pointer advanced
  366.      */
  367.     function next()
  368.     {
  369.         if (is_array($this->data)) {
  370.             if ($this->isRewound) {
  371.                 $this->isRewound = false;
  372.                 $seekFunction = 'reset';
  373.             }
  374.             else {
  375.                 $seekFunction = 'next';
  376.             }
  377.  
  378.             if ($node = $seekFunction($this->data)) {
  379.                 $this->pointer = $node;
  380.                 return true;
  381.             }
  382.         }
  383.  
  384.         return false;
  385.     }
  386.  
  387.     // }}}
  388.     // {{{ boolean nextByNodeName()
  389.  
  390.     /**
  391.      * Move to the next node in the nodeset of results where the node has the name provided.
  392.      * This can be used inside of a while loop, so that it is possible to step through the 
  393.      * nodes one by one.
  394.      *
  395.      * @param  string $in_name name of node to find
  396.      *
  397.      * @access public
  398.      * @return boolean next node existed and pointer moved
  399.      */
  400.     function nextByNodeName($in_name)
  401.     {
  402.         if (is_array($this->data)) {
  403.             if ($this->isRewound) {
  404.                 $this->isRewound = false;
  405.                 if (($node = reset($this->data)) && $node->node_name() == $in_name) {
  406.                     $this->pointer = $node;
  407.                     return true;
  408.                 }
  409.             }
  410.  
  411.             while ($node = next($this->data)) {
  412.                 if ($node->node_name() == $in_name) {
  413.                     $this->pointer = $node;
  414.                     return true;
  415.                 }
  416.             }
  417.         }
  418.  
  419.         return false;
  420.     }
  421.  
  422.     // }}}
  423.     // {{{ boolean nextByNodeType()
  424.  
  425.     /**
  426.      * Move to the next node in the nodeset of results where the node has the type provided.
  427.      * This can be used inside of a while loop, so that it is possible to step through the 
  428.      * nodes one by one.
  429.      *
  430.      * @param  int  $in_type type of node to find
  431.      *
  432.      * @access public
  433.      * @return boolean next node existed and pointer moved
  434.      */
  435.     function nextByNodeType($in_type)
  436.     {
  437.         if (is_array($this->data)) {
  438.             if ($this->isRewound) {
  439.                 $this->isRewound = false;
  440.                 if (($node = reset($this->data)) && $node->node_type() == $in_type) {
  441.                     $this->pointer = $node;
  442.                     return true;
  443.                 }
  444.             }
  445.  
  446.             while ($node = next($this->data)) {
  447.                 if ($node->node_type() == $in_type) {
  448.                     $this->pointer = $node;
  449.                     return true;
  450.                 }
  451.             }
  452.         }
  453.  
  454.         return false;
  455.     }
  456.  
  457.     // }}}
  458.     // {{{ object  current()
  459.  
  460.     /**
  461.      * Retrieve current pointer
  462.      *
  463.      * If the result is a nodeset (which is the most common use of the result object) than
  464.      * this function returns the current pointer in the result array.
  465.      *
  466.      * @return object XML_XPath pointer
  467.      * @access public
  468.      */
  469.     function current()
  470.     {
  471.         if (is_array($this->data)) {
  472.             return current($this->data);
  473.         }
  474.         
  475.         return false;
  476.     }
  477.  
  478.     // }}}
  479.     // {{{ boolean end()
  480.  
  481.     /**
  482.      * Move to last result node, if this is an XPATH_NODESET
  483.      *
  484.      * @access public
  485.      * @return boolean success
  486.      */
  487.     function end()
  488.     {
  489.         if (is_array($this->data)) {
  490.             $this->pointer = end($this->data);
  491.             return true;
  492.         }
  493.  
  494.         return false;
  495.     }
  496.  
  497.     // }}}
  498.     // {{{ void    free()
  499.  
  500.     /**
  501.      * Free the result object in order to save memory.
  502.      *
  503.      * @access public
  504.      * @return void
  505.      */
  506.     function free()
  507.     {
  508.         $this->data = null; 
  509.         $this->ctx = null; 
  510.         $this->xml = null; 
  511.     }
  512.  
  513.     // }}}
  514. }
  515. ?>
  516.