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 / DTD / XmlValidator.php
Encoding:
PHP Script  |  2008-07-02  |  9.0 KB  |  259 lines

  1. <?php
  2. //
  3. // +----------------------------------------------------------------------+
  4. // | DTD_XML_Validator class                                              |
  5. // +----------------------------------------------------------------------+
  6. // | Copyright (c) 2003 Tomas Von Veschler Cox                            |
  7. // +----------------------------------------------------------------------+
  8. // | This source file is subject to version 2.02 of the PHP license,      |
  9. // | that is bundled with this package in the file LICENSE, and is        |
  10. // | available at through the world-wide-web at                           |
  11. // | http://www.php.net/license/2_02.txt.                                 |
  12. // | If you did not receive a copy of the PHP license and are unable to   |
  13. // | obtain it through the world-wide-web, please send a note to          |
  14. // | license@php.net so we can mail you a copy immediately.               |
  15. // +----------------------------------------------------------------------+
  16. // | Authors:    Tomas V.V.Cox <cox@idecnet.com>                          |
  17. // |                                                                      |
  18. // +----------------------------------------------------------------------+
  19. //
  20. // $Id: XmlValidator.php,v 1.5 2004/05/17 19:51:45 schst Exp $
  21. //
  22. //
  23. // TODO:
  24. //   - Give better error messages :-)
  25. //   - Implement error codes and better error reporting
  26. //   - Add support for //XXX Missing .. (you may find them arround the code)
  27. //
  28.  
  29. require_once 'XML/DTD.php';
  30. require_once 'XML/Tree.php';
  31.  
  32. /**
  33.  * XML_DTD_XmlValidator
  34.  * 
  35.  * Usage:
  36.  * 
  37.  * <code>
  38.  * $validator = XML_DTD_XmlValidator;
  39.  * // This will check if the xml is well formed
  40.  * // and will validate it against its DTD
  41.  * if (!$validator->isValid($dtd_file, $xml_file)) {
  42.  *   die($validator->getMessage());
  43.  * }
  44.  * </code>
  45.  * 
  46.  * @package XML_DTD
  47.  * @category XML
  48.  * @author Tomas V.V.Cox <cox@idecnet.com> 
  49.  * @copyright Copyright (c) 2003
  50.  * @version $Id: XmlValidator.php,v 1.5 2004/05/17 19:51:45 schst Exp $
  51.  * @access public 
  52. */
  53. class XML_DTD_XmlValidator
  54. {
  55.  
  56.     var $dtd = array();
  57.     var $_errors = false;
  58.  
  59.     /**
  60.      * XML_DTD_XmlValidator::isValid()
  61.      *
  62.      * Checks an XML file against its DTD
  63.      *  
  64.      * @param string $dtd_file The DTD file name
  65.      * @param string $xml_file The XML file
  66.      * @return bool True if the XML conforms the definition
  67.      **/
  68.     function isValid($dtd_file, $xml_file)
  69.     {
  70.         $xml_tree =& new XML_Tree($xml_file);
  71.         $nodes = $xml_tree->getTreeFromFile();
  72.         if (PEAR::isError($nodes)) {
  73.             $this->_errors($nodes->getMessage());
  74.             return false;
  75.         }
  76.         $dtd_parser =& new XML_DTD_Parser;
  77.         $this->dtd = @$dtd_parser->parse($dtd_file);
  78.         $this->_runTree($nodes);
  79.         return ($this->_errors) ? false : true;
  80.     }
  81.  
  82.     /**
  83.      * XML_DTD_XmlValidator::_runTree()
  84.      * 
  85.      * Runs recursively over the XML_Tree tree of objects
  86.      * validating each of its nodes
  87.      * 
  88.      * @param object $node an XML_Tree_Node type object
  89.      * @return null
  90.      * @access private
  91.      **/
  92.     function _runTree(&$node)
  93.     {
  94.         //echo "Parsing node: $node->name\n";
  95.         $children = array();
  96.         $lines    = array();
  97.  
  98.         // Get the list of children under the parent node
  99.         foreach ($node->children as $child) {
  100.             // a text node
  101.             if (!strlen($child->name)) {
  102.                 $children[] = '#PCDATA';
  103.             } else {
  104.                 $children[] = $child->name;
  105.             }
  106.             $lines[]    = $child->lineno;
  107.         }
  108.  
  109.         $this->_validateNode($node, $children, $lines);
  110.         // Recursively run the tree
  111.         foreach ($node->children as $child) {
  112.             if (strlen($child->name)) {
  113.                 $this->_runTree($child);
  114.             }
  115.         }
  116.     }
  117.  
  118.     /**
  119.      * XML_DTD_XmlValidator::_validateNode()
  120.      * 
  121.      * Validate a XML_Tree_Node: allowed childs, allowed content
  122.      * and allowed attributes
  123.      * 
  124.      * @param object $node an XML_Tree_Node type object
  125.      * @param array  $children the list of children
  126.      * @param array  $linenos  linenumbers of the children
  127.      * @return null
  128.      * @access private
  129.      **/
  130.     function _validateNode($node, $children, $linenos)
  131.     {
  132.         $name = $node->name;
  133.         $lineno = $node->lineno;
  134.         if (!$this->dtd->elementIsDeclared($name)) {
  135.             $this->_errors("No declaration for tag <$name> in DTD", $lineno);
  136.             // We don't run over the childs of undeclared elements
  137.             // contrary of what xmllint does
  138.             return;
  139.         }
  140.  
  141.         //
  142.         // Children validation
  143.         //
  144.         $dtd_children = $this->dtd->getChildren($name);
  145.         do {
  146.             // There are children when no children allowed
  147.             if (count($children) && !count($dtd_children)) {
  148.                 $this->_errors("No children allowed under <$name>", $lineno);
  149.                 break;
  150.             }
  151.             // Search for children names not allowed
  152.             $was_error = false;
  153.             $i = 0;
  154.             foreach ($children as $child) {
  155.                 if (!in_array($child, $dtd_children)) {
  156.                     $this->_errors("<$child> not allowed under <$name>", $linenos[$i]);
  157.                     $was_error = true;
  158.                 }
  159.                 $i++;
  160.             }
  161.             // Validate the order of the children
  162.             if (!$was_error && count($dtd_children)) {
  163.                 $children_list = implode(',', $children);
  164.                 $regex = $this->dtd->getPcreRegex($name);
  165.                 if (!preg_match('/^'.$regex.'$/', $children_list)) {
  166.                     $dtd_regex = $this->dtd->getDTDRegex($name);
  167.                     $this->_errors("In element <$name> the children list found:\n'$children_list', ".
  168.                                    "does not conform the DTD definition: '$dtd_regex'", $lineno);
  169.                 }
  170.             }
  171.         } while (false);
  172.  
  173.         //
  174.         // Content Validation
  175.         //
  176.         $node_content = $node->content;
  177.         $dtd_content  = $this->dtd->getContent($name);
  178.         if (strlen($node_content)) {
  179.             if ($dtd_content == null) {
  180.                 $this->_errors("No content allowed for tag <$name>", $lineno);
  181.             } elseif ($dtd_content == 'EMPTY') {
  182.                 $this->_errors("No content allowed for tag <$name />, declared as 'EMPTY'", $lineno);
  183.             }
  184.         }
  185.         // XXX Missing validate #PCDATA or ANY
  186.  
  187.         //
  188.         // Attributes validation
  189.         //
  190.         $atts = $this->dtd->getAttributes($name);
  191.         $node_atts = $node->attributes;
  192.         foreach ($atts as $attname => $attvalue) {
  193.             $opts    = $attvalue['opts'];
  194.             $default = $attvalue['defaults'];
  195.             if ($default == '#REQUIRED' && !isset($node_atts[$attname])) {
  196.                 $this->_errors("Missing required '$attname' attribute in <$name>", $lineno);
  197.             }
  198.             if ($default == '#FIXED') {
  199.                 if (isset($node_atts[$attname]) && $node_atts[$attname] != $attvalue['fixed_value']) {
  200.                     $this->_errors("The value '{$node_atts[$attname]}' for attribute '$attname' ".
  201.                                    "in <$name> can only be '{$attvalue['fixed_value']}'", $lineno);
  202.                 }
  203.             }
  204.             if (isset($node_atts[$attname])) {
  205.                 $node_val = $node_atts[$attname];
  206.                 // Enumerated type validation
  207.                 if (is_array($opts)) {
  208.                     if (!in_array($node_val, $opts)) {
  209.                         $this->_errors("'$node_val' value for attribute '$attname' under <$name> ".
  210.                                        "can only be: '". implode(', ', $opts) . "'", $lineno);
  211.                     }
  212.                 }
  213.                 unset($node_atts[$attname]);
  214.             }
  215.         }
  216.         // XXX Missing NMTOKEN, ID
  217.  
  218.         // If there are still attributes those are not declared in DTD
  219.         if (count($node_atts) > 0) {
  220.             $this->_errors("The attributes: '" . implode(', ', array_keys($node_atts)) .
  221.                            "' are not declared in DTD for tag <$name>", $lineno);
  222.         }
  223.     }
  224.  
  225.     /**
  226.      * XML_DTD_XmlValidator::_errors()
  227.      * 
  228.      * Stores errors
  229.      * 
  230.      * @param string  $str     the error message to append
  231.      * @param integer $lineno  the line number where the tag is declared
  232.      * @return null
  233.      * @access private
  234.      **/
  235.     function _errors($str, $lineno = null)
  236.     {
  237.         if (is_null($lineno)) {
  238.             $this->_errors .= "$str\n";
  239.         } else {
  240.             $this->_errors .= "line $lineno: $str\n";
  241.         }
  242.     }
  243.  
  244.     /**
  245.      * XML_DTD_XmlValidator::getMessage()
  246.      *
  247.      * Gets all the errors the validator found in the
  248.      * conformity of the xml document
  249.      *  
  250.      * @return string the error message 
  251.      **/
  252.     function getMessage()
  253.     {
  254.         return $this->_errors;
  255.     }
  256.  
  257. }
  258. ?>
  259.