Moodle  2.2.1
http://www.collinsharper.com
C:/xampp/htdocs/moodle/backup/util/xml/parser/progressive_parser.class.php
Go to the documentation of this file.
00001 <?php
00002 
00003 // This file is part of Moodle - http://moodle.org/
00004 //
00005 // Moodle is free software: you can redistribute it and/or modify
00006 // it under the terms of the GNU General Public License as published by
00007 // the Free Software Foundation, either version 3 of the License, or
00008 // (at your option) any later version.
00009 //
00010 // Moodle is distributed in the hope that it will be useful,
00011 // but WITHOUT ANY WARRANTY; without even the implied warranty of
00012 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00013 // GNU General Public License for more details.
00014 //
00015 // You should have received a copy of the GNU General Public License
00016 // along with Moodle.  If not, see <http://www.gnu.org/licenses/>.
00017 
00054 class progressive_parser {
00055 
00056     protected $xml_parser; // PHP's low level XML SAX parser
00057     protected $file;       // full path to file being progressively parsed | => mutually exclusive
00058     protected $contents;   // contents being progressively parsed          |
00059     protected $procesor;   // progressive_parser_procesor to be used to publish processed information
00060 
00061     protected $level;      // level of the current tag
00062     protected $path;       // path of the current tag
00063     protected $accum;      // accumulated char data of the current tag
00064     protected $attrs;      // attributes of the current tag
00065 
00066     protected $topush;     // array containing current level information being parsed to be "pushed"
00067     protected $prevlevel;  // level of the previous tag processed - to detect pushing places
00068     protected $currtag;    // name/value/attributes of the tag being processed
00069 
00070     public function __construct($case_folding = false) {
00071         $this->xml_parser = xml_parser_create('UTF-8');
00072         xml_parser_set_option($this->xml_parser, XML_OPTION_CASE_FOLDING, $case_folding);
00073         xml_set_object($this->xml_parser, $this);
00074         xml_set_element_handler($this->xml_parser, array($this, 'start_tag'), array($this, 'end_tag'));
00075         xml_set_character_data_handler($this->xml_parser, array($this, 'char_data'));
00076 
00077         $this->file     = null;
00078         $this->contents = null;
00079         $this->procesor = null;
00080         $this->level    = 0;
00081         $this->path     = '';
00082         $this->accum    = '';
00083         $this->attrs    = array();
00084         $this->topush  = array();
00085         $this->prevlevel = 0;
00086         $this->currtag   = array();
00087     }
00088 
00089     /*
00090      * Sets the XML file to be processed by the parser
00091      */
00092     public function set_file($file) {
00093         if (!file_exists($file) || (!is_readable($file))) {
00094             throw new progressive_parser_exception('invalid_file_to_parse');
00095         }
00096         $this->file = $file;
00097         $this->contents = null;
00098     }
00099 
00100     /*
00101      * Sets the XML contents to be processed by the parser
00102      */
00103     public function set_contents($contents) {
00104         if (empty($contents)) {
00105             throw new progressive_parser_exception('invalid_contents_to_parse');
00106         }
00107         $this->contents = $contents;
00108         $this->file = null;
00109     }
00110 
00111     /*
00112      * Define the @progressive_parser_processor in charge of processing the parsed chunks
00113      */
00114     public function set_processor($processor) {
00115         if (!$processor instanceof progressive_parser_processor) {
00116             throw new progressive_parser_exception('invalid_parser_processor');
00117         }
00118         $this->processor = $processor;
00119     }
00120 
00121     /*
00122      * Process the XML, delegating found chunks to the @progressive_parser_processor
00123      */
00124     public function process() {
00125         if (empty($this->processor)) {
00126             throw new progressive_parser_exception('undefined_parser_processor');
00127         }
00128         if (empty($this->file) && empty($this->contents)) {
00129             throw new progressive_parser_exception('undefined_xml_to_parse');
00130         }
00131         if (is_null($this->xml_parser)) {
00132             throw new progressive_parser_exception('progressive_parser_already_used');
00133         }
00134         if ($this->file) {
00135             $fh = fopen($this->file, 'r');
00136             while ($buffer = fread($fh, 8192)) {
00137                 $this->parse($buffer, feof($fh));
00138             }
00139             fclose($fh);
00140         } else {
00141             $this->parse($this->contents, true);
00142         }
00143         xml_parser_free($this->xml_parser);
00144         $this->xml_parser = null;
00145     }
00146 
00151     public static function dirname($path) {
00152         return str_replace('\\', '/', dirname($path));
00153     }
00154 
00155 // Protected API starts here
00156 
00157     protected function parse($data, $eof) {
00158         if (!xml_parse($this->xml_parser, $data, $eof)) {
00159             throw new progressive_parser_exception(
00160                 'xml_parsing_error', null,
00161                 sprintf('XML error: %s at line %d, column %d',
00162                         xml_error_string(xml_get_error_code($this->xml_parser)),
00163                         xml_get_current_line_number($this->xml_parser),
00164                         xml_get_current_column_number($this->xml_parser)));
00165         }
00166     }
00167 
00168     protected function publish($data) {
00169         $this->processor->receive_chunk($data);
00170     }
00171 
00175     protected function inform_start($path) {
00176         $this->processor->before_path($path);
00177     }
00178 
00182     protected function inform_end($path) {
00183         $this->processor->after_path($path);
00184     }
00185 
00186     protected function postprocess_cdata($data) {
00187         return $this->processor->process_cdata($data);
00188     }
00189 
00190     protected function start_tag($parser, $tag, $attributes) {
00191 
00192         // Normal update of parser internals
00193         $this->level++;
00194         $this->path .= '/' . $tag;
00195         $this->accum = '';
00196         $this->attrs = !empty($attributes) ? $attributes : array();
00197 
00198         // Inform processor we are about to start one tag
00199         $this->inform_start($this->path);
00200 
00201         // Entering a new inner level, publish all the information available
00202         if ($this->level > $this->prevlevel) {
00203             if (!empty($this->currtag) && (!empty($this->currtag['attrs']) || !empty($this->currtag['cdata']))) {
00204                 // We always add the last not-empty repetition. Empty ones are ignored.
00205                 if (isset($this->topush['tags'][$this->currtag['name']]) && trim($this->currtag['cdata']) === '') {
00206                     // Do nothing, the tag already exists and the repetition is empty
00207                 } else {
00208                     $this->topush['tags'][$this->currtag['name']] = $this->currtag;
00209                 }
00210             }
00211             if (!empty($this->topush['tags'])) {
00212                 $this->publish($this->topush);
00213             }
00214             $this->currtag = array();
00215             $this->topush = array();
00216         }
00217 
00218         // If not set, build to push common header
00219         if (empty($this->topush)) {
00220             $this->topush['path']  = progressive_parser::dirname($this->path);
00221             $this->topush['level'] = $this->level;
00222             $this->topush['tags']  = array();
00223         }
00224 
00225         // Handling a new tag, create it
00226         $this->currtag['name'] = $tag;
00227         // And add attributes if present
00228         if ($this->attrs) {
00229             $this->currtag['attrs'] = $this->attrs;
00230         }
00231 
00232         // For the records
00233         $this->prevlevel = $this->level;
00234     }
00235 
00236     protected function end_tag($parser, $tag) {
00237 
00238         // Ending rencently started tag, add value to current tag
00239         if ($this->level == $this->prevlevel) {
00240             $this->currtag['cdata'] = $this->postprocess_cdata($this->accum);
00241             // We always add the last not-empty repetition. Empty ones are ignored.
00242             if (isset($this->topush['tags'][$this->currtag['name']]) && trim($this->currtag['cdata']) === '') {
00243                 // Do nothing, the tag already exists and the repetition is empty
00244             } else {
00245                 $this->topush['tags'][$this->currtag['name']] = $this->currtag;
00246             }
00247             $this->currtag = array();
00248         }
00249 
00250         // Leaving one level, publish all the information available
00251         if ($this->level < $this->prevlevel) {
00252             if (!empty($this->topush['tags'])) {
00253                 $this->publish($this->topush);
00254             }
00255             $this->currtag = array();
00256             $this->topush = array();
00257         }
00258 
00259         // For the records
00260         $this->prevlevel = $this->level;
00261 
00262         // Inform processor we have finished one tag
00263         $this->inform_end($this->path);
00264 
00265         // Normal update of parser internals
00266         $this->level--;
00267         $this->path = progressive_parser::dirname($this->path);
00268     }
00269 
00270     protected function char_data($parser, $data) {
00271         $this->accum .= $data;
00272     }
00273 }
00274 
00275 /*
00276  * Exception class used by all the @progressive_parser stuff
00277  */
00278 class progressive_parser_exception extends moodle_exception {
00279 
00280     public function __construct($errorcode, $a=NULL, $debuginfo=null) {
00281         parent::__construct($errorcode, 'error', '', $a, null, $debuginfo);
00282     }
00283 }
 All Data Structures Namespaces Files Functions Variables Enumerations