[ Index ]

PHP Cross Reference of MediaWiki-1.24.0

title

Body

[close]

/includes/libs/ -> XmlTypeCheck.php (source)

   1  <?php
   2  /**
   3   * XML syntax and type checker.
   4   *
   5   * This program is free software; you can redistribute it and/or modify
   6   * it under the terms of the GNU General Public License as published by
   7   * the Free Software Foundation; either version 2 of the License, or
   8   * (at your option) any later version.
   9   *
  10   * This program is distributed in the hope that it will be useful,
  11   * but WITHOUT ANY WARRANTY; without even the implied warranty of
  12   * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  13   * GNU General Public License for more details.
  14   *
  15   * You should have received a copy of the GNU General Public License along
  16   * with this program; if not, write to the Free Software Foundation, Inc.,
  17   * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
  18   * http://www.gnu.org/copyleft/gpl.html
  19   *
  20   * @file
  21   */
  22  
  23  class XmlTypeCheck {
  24      /**
  25       * Will be set to true or false to indicate whether the file is
  26       * well-formed XML. Note that this doesn't check schema validity.
  27       */
  28      public $wellFormed = false;
  29  
  30      /**
  31       * Will be set to true if the optional element filter returned
  32       * a match at some point.
  33       */
  34      public $filterMatch = false;
  35  
  36      /**
  37       * Name of the document's root element, including any namespace
  38       * as an expanded URL.
  39       */
  40      public $rootElement = '';
  41  
  42      /**
  43       * A stack of strings containing the data of each xml element as it's processed. Append
  44       * data to the top string of the stack, then pop off the string and process it when the
  45       * element is closed.
  46       */
  47      protected $elementData = array();
  48  
  49      /**
  50       * A stack of element names and attributes, as we process them.
  51       */
  52      protected $elementDataContext = array();
  53  
  54      /**
  55       * Current depth of the data stack.
  56       */
  57      protected $stackDepth = 0;
  58  
  59      /**
  60       * Additional parsing options
  61       */
  62      private $parserOptions = array(
  63          'processing_instruction_handler' => '',
  64      );
  65  
  66      /**
  67       * @param string $input a filename or string containing the XML element
  68       * @param callable $filterCallback (optional)
  69       *        Function to call to do additional custom validity checks from the
  70       *        SAX element handler event. This gives you access to the element
  71       *        namespace, name, attributes, and text contents.
  72       *        Filter should return 'true' to toggle on $this->filterMatch
  73       * @param boolean $isFile (optional) indicates if the first parameter is a
  74       *        filename (default, true) or if it is a string (false)
  75       * @param array $options list of additional parsing options:
  76       *        processing_instruction_handler: Callback for xml_set_processing_instruction_handler
  77       */
  78  	function __construct( $input, $filterCallback = null, $isFile = true, $options = array() ) {
  79          $this->filterCallback = $filterCallback;
  80          $this->parserOptions = array_merge( $this->parserOptions, $options );
  81  
  82          if ( $isFile ) {
  83              $this->validateFromFile( $input );
  84          } else {
  85              $this->validateFromString( $input );
  86          }
  87      }
  88  
  89      /**
  90       * Alternative constructor: from filename
  91       *
  92       * @param string $fname the filename of an XML document
  93       * @param callable $filterCallback (optional)
  94       *        Function to call to do additional custom validity checks from the
  95       *        SAX element handler event. This gives you access to the element
  96       *        namespace, name, and attributes, but not to text contents.
  97       *        Filter should return 'true' to toggle on $this->filterMatch
  98       * @return XmlTypeCheck
  99       */
 100  	public static function newFromFilename( $fname, $filterCallback = null ) {
 101          return new self( $fname, $filterCallback, true );
 102      }
 103  
 104      /**
 105       * Alternative constructor: from string
 106       *
 107       * @param string $string a string containing an XML element
 108       * @param callable $filterCallback (optional)
 109       *        Function to call to do additional custom validity checks from the
 110       *        SAX element handler event. This gives you access to the element
 111       *        namespace, name, and attributes, but not to text contents.
 112       *        Filter should return 'true' to toggle on $this->filterMatch
 113       * @return XmlTypeCheck
 114       */
 115  	public static function newFromString( $string, $filterCallback = null ) {
 116          return new self( $string, $filterCallback, false );
 117      }
 118  
 119      /**
 120       * Get the root element. Simple accessor to $rootElement
 121       *
 122       * @return string
 123       */
 124  	public function getRootElement() {
 125          return $this->rootElement;
 126      }
 127  
 128      /**
 129       * Get an XML parser with the root element handler.
 130       * @see XmlTypeCheck::rootElementOpen()
 131       * @return resource a resource handle for the XML parser
 132       */
 133  	private function getParser() {
 134          $parser = xml_parser_create_ns( 'UTF-8' );
 135          // case folding violates XML standard, turn it off
 136          xml_parser_set_option( $parser, XML_OPTION_CASE_FOLDING, false );
 137          xml_set_element_handler( $parser, array( $this, 'rootElementOpen' ), false );
 138          if ( $this->parserOptions['processing_instruction_handler'] ) {
 139              xml_set_processing_instruction_handler(
 140                  $parser,
 141                  array( $this, 'processingInstructionHandler' )
 142              );
 143          }
 144          return $parser;
 145      }
 146  
 147      /**
 148       * @param string $fname the filename
 149       */
 150  	private function validateFromFile( $fname ) {
 151          $parser = $this->getParser();
 152  
 153          if ( file_exists( $fname ) ) {
 154              $file = fopen( $fname, "rb" );
 155              if ( $file ) {
 156                  do {
 157                      $chunk = fread( $file, 32768 );
 158                      $ret = xml_parse( $parser, $chunk, feof( $file ) );
 159                      if ( $ret == 0 ) {
 160                          $this->wellFormed = false;
 161                          fclose( $file );
 162                          xml_parser_free( $parser );
 163                          return;
 164                      }
 165                  } while ( !feof( $file ) );
 166  
 167                  fclose( $file );
 168              }
 169          }
 170          $this->wellFormed = true;
 171  
 172          xml_parser_free( $parser );
 173      }
 174  
 175      /**
 176       *
 177       * @param string $string the XML-input-string to be checked.
 178       */
 179  	private function validateFromString( $string ) {
 180          $parser = $this->getParser();
 181          $ret = xml_parse( $parser, $string, true );
 182          xml_parser_free( $parser );
 183          if ( $ret == 0 ) {
 184              $this->wellFormed = false;
 185              return;
 186          }
 187          $this->wellFormed = true;
 188      }
 189  
 190      /**
 191       * @param $parser
 192       * @param $name
 193       * @param $attribs
 194       */
 195  	private function rootElementOpen( $parser, $name, $attribs ) {
 196          $this->rootElement = $name;
 197  
 198          if ( is_callable( $this->filterCallback ) ) {
 199              xml_set_element_handler(
 200                  $parser,
 201                  array( $this, 'elementOpen' ),
 202                  array( $this, 'elementClose' )
 203              );
 204              xml_set_character_data_handler( $parser, array( $this, 'elementData' ) );
 205              $this->elementOpen( $parser, $name, $attribs );
 206          } else {
 207              // We only need the first open element
 208              xml_set_element_handler( $parser, false, false );
 209          }
 210      }
 211  
 212      /**
 213       * @param $parser
 214       * @param $name
 215       * @param $attribs
 216       */
 217  	private function elementOpen( $parser, $name, $attribs ) {
 218          $this->elementDataContext[] = array( $name, $attribs );
 219          $this->elementData[] = '';
 220          $this->stackDepth++;
 221      }
 222  
 223      /**
 224       * @param $parser
 225       * @param $name
 226       */
 227  	private function elementClose( $parser, $name ) {
 228          list( $name, $attribs ) = array_pop( $this->elementDataContext );
 229          $data = array_pop( $this->elementData );
 230          $this->stackDepth--;
 231  
 232          if ( call_user_func(
 233              $this->filterCallback,
 234              $name,
 235              $attribs,
 236              $data
 237          ) ) {
 238              // Filter hit!
 239              $this->filterMatch = true;
 240          }
 241      }
 242  
 243      /**
 244       * @param $parser
 245       * @param $data
 246       */
 247  	private function elementData( $parser, $data ) {
 248          // xml_set_character_data_handler breaks the data on & characters, so
 249          // we collect any data here, and we'll run the callback in elementClose
 250          $this->elementData[ $this->stackDepth - 1 ] .= trim( $data );
 251      }
 252  
 253      /**
 254       * @param $parser
 255       * @param $target
 256       * @param $data
 257       */
 258  	private function processingInstructionHandler( $parser, $target, $data ) {
 259          if ( call_user_func( $this->parserOptions['processing_instruction_handler'], $target, $data ) ) {
 260              // Filter hit!
 261              $this->filterMatch = true;
 262          }
 263      }
 264  }


Generated: Fri Nov 28 14:03:12 2014 Cross-referenced by PHPXref 0.7.1