Subversion-Projekte lars-tiefland.php_share

Revision

Details | Letzte Änderung | Log anzeigen | RSS feed

Revision Autor Zeilennr. Zeile
1 lars 1
<?php
2
 
3
/* vim: set expandtab tabstop=4 shiftwidth=4 softtabstop=4: */
4
 
5
/**
6
 * XML_Parser
7
 *
8
 * XML Parser package
9
 *
10
 * PHP versions 4 and 5
11
 *
12
 * LICENSE:
13
 *
14
 * Copyright (c) 2002-2008 The PHP Group
15
 * All rights reserved.
16
 *
17
 * Redistribution and use in source and binary forms, with or without
18
 * modification, are permitted provided that the following conditions
19
 * are met:
20
 *
21
 *    * Redistributions of source code must retain the above copyright
22
 *      notice, this list of conditions and the following disclaimer.
23
 *    * Redistributions in binary form must reproduce the above copyright
24
 *      notice, this list of conditions and the following disclaimer in the
25
 *      documentation and/or other materials provided with the distribution.
26
 *    * The name of the author may not be used to endorse or promote products
27
 *      derived from this software without specific prior written permission.
28
 *
29
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
30
 * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
31
 * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
32
 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
33
 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
34
 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
35
 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
36
 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
37
 * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
38
 * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
39
 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
40
 *
41
 * @category  XML
42
 * @package   XML_Parser
43
 * @author    Stig Bakken <ssb@fast.no>
44
 * @author    Tomas V.V.Cox <cox@idecnet.com>
45
 * @author    Stephan Schmidt <schst@php.net>
46
 * @copyright 2002-2008 The PHP Group
47
 * @license   http://opensource.org/licenses/bsd-license New BSD License
48
 * @version   CVS: $Id: Parser.php 302733 2010-08-24 01:09:09Z clockwerx $
49
 * @link      http://pear.php.net/package/XML_Parser
50
 */
51
 
52
/**
53
 * uses PEAR's error handling
54
 */
55
require_once 'PEAR.php';
56
 
57
/**
58
 * resource could not be created
59
 */
60
define('XML_PARSER_ERROR_NO_RESOURCE', 200);
61
 
62
/**
63
 * unsupported mode
64
 */
65
define('XML_PARSER_ERROR_UNSUPPORTED_MODE', 201);
66
 
67
/**
68
 * invalid encoding was given
69
 */
70
define('XML_PARSER_ERROR_INVALID_ENCODING', 202);
71
 
72
/**
73
 * specified file could not be read
74
 */
75
define('XML_PARSER_ERROR_FILE_NOT_READABLE', 203);
76
 
77
/**
78
 * invalid input
79
 */
80
define('XML_PARSER_ERROR_INVALID_INPUT', 204);
81
 
82
/**
83
 * remote file cannot be retrieved in safe mode
84
 */
85
define('XML_PARSER_ERROR_REMOTE', 205);
86
 
87
/**
88
 * XML Parser class.
89
 *
90
 * This is an XML parser based on PHP's "xml" extension,
91
 * based on the bundled expat library.
92
 *
93
 * Notes:
94
 * - It requires PHP 4.0.4pl1 or greater
95
 * - From revision 1.17, the function names used by the 'func' mode
96
 *   are in the format "xmltag_$elem", for example: use "xmltag_name"
97
 *   to handle the <name></name> tags of your xml file.
98
 *          - different parsing modes
99
 *
100
 * @category  XML
101
 * @package   XML_Parser
102
 * @author    Stig Bakken <ssb@fast.no>
103
 * @author    Tomas V.V.Cox <cox@idecnet.com>
104
 * @author    Stephan Schmidt <schst@php.net>
105
 * @copyright 2002-2008 The PHP Group
106
 * @license   http://opensource.org/licenses/bsd-license New BSD License
107
 * @version   Release: @package_version@
108
 * @link      http://pear.php.net/package/XML_Parser
109
 * @todo      create XML_Parser_Namespace to parse documents with namespaces
110
 * @todo      create XML_Parser_Pull
111
 * @todo      Tests that need to be made:
112
 *            - mixing character encodings
113
 *            - a test using all expat handlers
114
 *            - options (folding, output charset)
115
 */
116
class XML_Parser extends PEAR
117
{
118
    // {{{ properties
119
 
120
    /**
121
     * XML parser handle
122
     *
123
     * @var  resource
124
     * @see  xml_parser_create()
125
     */
126
    var $parser;
127
 
128
    /**
129
     * File handle if parsing from a file
130
     *
131
     * @var  resource
132
     */
133
    var $fp;
134
 
135
    /**
136
     * Whether to do case folding
137
     *
138
     * If set to true, all tag and attribute names will
139
     * be converted to UPPER CASE.
140
     *
141
     * @var  boolean
142
     */
143
    var $folding = true;
144
 
145
    /**
146
     * Mode of operation, one of "event" or "func"
147
     *
148
     * @var  string
149
     */
150
    var $mode;
151
 
152
    /**
153
     * Mapping from expat handler function to class method.
154
     *
155
     * @var  array
156
     */
157
    var $handler = array(
158
        'character_data_handler'            => 'cdataHandler',
159
        'default_handler'                   => 'defaultHandler',
160
        'processing_instruction_handler'    => 'piHandler',
161
        'unparsed_entity_decl_handler'      => 'unparsedHandler',
162
        'notation_decl_handler'             => 'notationHandler',
163
        'external_entity_ref_handler'       => 'entityrefHandler'
164
    );
165
 
166
    /**
167
     * source encoding
168
     *
169
     * @var string
170
     */
171
    var $srcenc;
172
 
173
    /**
174
     * target encoding
175
     *
176
     * @var string
177
     */
178
    var $tgtenc;
179
 
180
    /**
181
     * handler object
182
     *
183
     * @var object
184
     */
185
    var $_handlerObj;
186
 
187
    /**
188
     * valid encodings
189
     *
190
     * @var array
191
     */
192
    var $_validEncodings = array('ISO-8859-1', 'UTF-8', 'US-ASCII');
193
 
194
    // }}}
195
    // {{{ php4 constructor
196
 
197
    /**
198
     * Creates an XML parser.
199
     *
200
     * This is needed for PHP4 compatibility, it will
201
     * call the constructor, when a new instance is created.
202
     *
203
     * @param string $srcenc source charset encoding, use NULL (default) to use
204
     *                       whatever the document specifies
205
     * @param string $mode   how this parser object should work, "event" for
206
     *                       startelement/endelement-type events, "func"
207
     *                       to have it call functions named after elements
208
     * @param string $tgtenc a valid target encoding
209
     */
210
    function XML_Parser($srcenc = null, $mode = 'event', $tgtenc = null)
211
    {
212
        XML_Parser::__construct($srcenc, $mode, $tgtenc);
213
    }
214
    // }}}
215
    // {{{ php5 constructor
216
 
217
    /**
218
     * PHP5 constructor
219
     *
220
     * @param string $srcenc source charset encoding, use NULL (default) to use
221
     *                       whatever the document specifies
222
     * @param string $mode   how this parser object should work, "event" for
223
     *                       startelement/endelement-type events, "func"
224
     *                       to have it call functions named after elements
225
     * @param string $tgtenc a valid target encoding
226
     */
227
    function __construct($srcenc = null, $mode = 'event', $tgtenc = null)
228
    {
229
        $this->PEAR('XML_Parser_Error');
230
 
231
        $this->mode   = $mode;
232
        $this->srcenc = $srcenc;
233
        $this->tgtenc = $tgtenc;
234
    }
235
    // }}}
236
 
237
    /**
238
     * Sets the mode of the parser.
239
     *
240
     * Possible modes are:
241
     * - func
242
     * - event
243
     *
244
     * You can set the mode using the second parameter
245
     * in the constructor.
246
     *
247
     * This method is only needed, when switching to a new
248
     * mode at a later point.
249
     *
250
     * @param string $mode mode, either 'func' or 'event'
251
     *
252
     * @return boolean|object  true on success, PEAR_Error otherwise
253
     * @access public
254
     */
255
    function setMode($mode)
256
    {
257
        if ($mode != 'func' && $mode != 'event') {
258
            $this->raiseError('Unsupported mode given',
259
                XML_PARSER_ERROR_UNSUPPORTED_MODE);
260
        }
261
 
262
        $this->mode = $mode;
263
        return true;
264
    }
265
 
266
    /**
267
     * Sets the object, that will handle the XML events
268
     *
269
     * This allows you to create a handler object independent of the
270
     * parser object that you are using and easily switch the underlying
271
     * parser.
272
     *
273
     * If no object will be set, XML_Parser assumes that you
274
     * extend this class and handle the events in $this.
275
     *
276
     * @param object &$obj object to handle the events
277
     *
278
     * @return boolean will always return true
279
     * @access public
280
     * @since v1.2.0beta3
281
     */
282
    function setHandlerObj(&$obj)
283
    {
284
        $this->_handlerObj = &$obj;
285
        return true;
286
    }
287
 
288
    /**
289
     * Init the element handlers
290
     *
291
     * @return mixed
292
     * @access private
293
     */
294
    function _initHandlers()
295
    {
296
        if (!is_resource($this->parser)) {
297
            return false;
298
        }
299
 
300
        if (!is_object($this->_handlerObj)) {
301
            $this->_handlerObj = &$this;
302
        }
303
        switch ($this->mode) {
304
 
305
        case 'func':
306
            xml_set_object($this->parser, $this->_handlerObj);
307
            xml_set_element_handler($this->parser,
308
                array(&$this, 'funcStartHandler'), array(&$this, 'funcEndHandler'));
309
            break;
310
 
311
        case 'event':
312
            xml_set_object($this->parser, $this->_handlerObj);
313
            xml_set_element_handler($this->parser, 'startHandler', 'endHandler');
314
            break;
315
        default:
316
            return $this->raiseError('Unsupported mode given',
317
                XML_PARSER_ERROR_UNSUPPORTED_MODE);
318
            break;
319
        }
320
 
321
        /**
322
         * set additional handlers for character data, entities, etc.
323
         */
324
        foreach ($this->handler as $xml_func => $method) {
325
            if (method_exists($this->_handlerObj, $method)) {
326
                $xml_func = 'xml_set_' . $xml_func;
327
                $xml_func($this->parser, $method);
328
            }
329
        }
330
    }
331
 
332
    // {{{ _create()
333
 
334
    /**
335
     * create the XML parser resource
336
     *
337
     * Has been moved from the constructor to avoid
338
     * problems with object references.
339
     *
340
     * Furthermore it allows us returning an error
341
     * if something fails.
342
     *
343
     * NOTE: uses '@' error suppresion in this method
344
     *
345
     * @return bool|PEAR_Error true on success, PEAR_Error otherwise
346
     * @access private
347
     * @see xml_parser_create
348
     */
349
    function _create()
350
    {
351
        if ($this->srcenc === null) {
352
            $xp = @xml_parser_create();
353
        } else {
354
            $xp = @xml_parser_create($this->srcenc);
355
        }
356
        if (is_resource($xp)) {
357
            if ($this->tgtenc !== null) {
358
                if (!@xml_parser_set_option($xp, XML_OPTION_TARGET_ENCODING,
359
                    $this->tgtenc)
360
                ) {
361
                    return $this->raiseError('invalid target encoding',
362
                        XML_PARSER_ERROR_INVALID_ENCODING);
363
                }
364
            }
365
            $this->parser = $xp;
366
            $result       = $this->_initHandlers($this->mode);
367
            if ($this->isError($result)) {
368
                return $result;
369
            }
370
            xml_parser_set_option($xp, XML_OPTION_CASE_FOLDING, $this->folding);
371
            return true;
372
        }
373
        if (!in_array(strtoupper($this->srcenc), $this->_validEncodings)) {
374
            return $this->raiseError('invalid source encoding',
375
                XML_PARSER_ERROR_INVALID_ENCODING);
376
        }
377
        return $this->raiseError('Unable to create XML parser resource.',
378
            XML_PARSER_ERROR_NO_RESOURCE);
379
    }
380
 
381
    // }}}
382
    // {{{ reset()
383
 
384
    /**
385
     * Reset the parser.
386
     *
387
     * This allows you to use one parser instance
388
     * to parse multiple XML documents.
389
     *
390
     * @access   public
391
     * @return   boolean|object     true on success, PEAR_Error otherwise
392
     */
393
    function reset()
394
    {
395
        $result = $this->_create();
396
        if ($this->isError($result)) {
397
            return $result;
398
        }
399
        return true;
400
    }
401
 
402
    // }}}
403
    // {{{ setInputFile()
404
 
405
    /**
406
     * Sets the input xml file to be parsed
407
     *
408
     * @param string $file Filename (full path)
409
     *
410
     * @return resource fopen handle of the given file
411
     * @access public
412
     * @throws XML_Parser_Error
413
     * @see setInput(), setInputString(), parse()
414
     */
415
    function setInputFile($file)
416
    {
417
        /**
418
         * check, if file is a remote file
419
         */
420
        if (preg_match('/^(http|ftp):\/\//i', substr($file, 0, 10))) {
421
            if (!ini_get('allow_url_fopen')) {
422
                return $this->
423
                raiseError('Remote files cannot be parsed, as safe mode is enabled.',
424
                XML_PARSER_ERROR_REMOTE);
425
            }
426
        }
427
 
428
        $fp = @fopen($file, 'rb');
429
        if (is_resource($fp)) {
430
            $this->fp = $fp;
431
            return $fp;
432
        }
433
        return $this->raiseError('File could not be opened.',
434
            XML_PARSER_ERROR_FILE_NOT_READABLE);
435
    }
436
 
437
    // }}}
438
    // {{{ setInputString()
439
 
440
    /**
441
     * XML_Parser::setInputString()
442
     *
443
     * Sets the xml input from a string
444
     *
445
     * @param string $data a string containing the XML document
446
     *
447
     * @return null
448
     */
449
    function setInputString($data)
450
    {
451
        $this->fp = $data;
452
        return null;
453
    }
454
 
455
    // }}}
456
    // {{{ setInput()
457
 
458
    /**
459
     * Sets the file handle to use with parse().
460
     *
461
     * You should use setInputFile() or setInputString() if you
462
     * pass a string
463
     *
464
     * @param mixed $fp Can be either a resource returned from fopen(),
465
     *                  a URL, a local filename or a string.
466
     *
467
     * @return mixed
468
     * @access public
469
     * @see parse()
470
     * @uses setInputString(), setInputFile()
471
     */
472
    function setInput($fp)
473
    {
474
        if (is_resource($fp)) {
475
            $this->fp = $fp;
476
            return true;
477
        } elseif (preg_match('/^[a-z]+:\/\//i', substr($fp, 0, 10))) {
478
            // see if it's an absolute URL (has a scheme at the beginning)
479
            return $this->setInputFile($fp);
480
        } elseif (file_exists($fp)) {
481
            // see if it's a local file
482
            return $this->setInputFile($fp);
483
        } else {
484
            // it must be a string
485
            $this->fp = $fp;
486
            return true;
487
        }
488
 
489
        return $this->raiseError('Illegal input format',
490
            XML_PARSER_ERROR_INVALID_INPUT);
491
    }
492
 
493
    // }}}
494
    // {{{ parse()
495
 
496
    /**
497
     * Central parsing function.
498
     *
499
     * @return bool|PEAR_Error returns true on success, or a PEAR_Error otherwise
500
     * @access public
501
     */
502
    function parse()
503
    {
504
        /**
505
         * reset the parser
506
         */
507
        $result = $this->reset();
508
        if ($this->isError($result)) {
509
            return $result;
510
        }
511
        // if $this->fp was fopened previously
512
        if (is_resource($this->fp)) {
513
 
514
            while ($data = fread($this->fp, 4096)) {
515
                if (!$this->_parseString($data, feof($this->fp))) {
516
                    $error = &$this->raiseError();
517
                    $this->free();
518
                    return $error;
519
                }
520
            }
521
        } else {
522
            // otherwise, $this->fp must be a string
523
            if (!$this->_parseString($this->fp, true)) {
524
                $error = &$this->raiseError();
525
                $this->free();
526
                return $error;
527
            }
528
        }
529
        $this->free();
530
 
531
        return true;
532
    }
533
 
534
    /**
535
     * XML_Parser::_parseString()
536
     *
537
     * @param string $data data
538
     * @param bool   $eof  end-of-file flag
539
     *
540
     * @return bool
541
     * @access private
542
     * @see parseString()
543
     **/
544
    function _parseString($data, $eof = false)
545
    {
546
        return xml_parse($this->parser, $data, $eof);
547
    }
548
 
549
    // }}}
550
    // {{{ parseString()
551
 
552
    /**
553
     * XML_Parser::parseString()
554
     *
555
     * Parses a string.
556
     *
557
     * @param string  $data XML data
558
     * @param boolean $eof  If set and TRUE, data is the last piece
559
     *                      of data sent in this parser
560
     *
561
     * @return bool|PEAR_Error true on success or a PEAR Error
562
     * @throws XML_Parser_Error
563
     * @see _parseString()
564
     */
565
    function parseString($data, $eof = false)
566
    {
567
        if (!isset($this->parser) || !is_resource($this->parser)) {
568
            $this->reset();
569
        }
570
 
571
        if (!$this->_parseString($data, $eof)) {
572
            $error = &$this->raiseError();
573
            $this->free();
574
            return $error;
575
        }
576
 
577
        if ($eof === true) {
578
            $this->free();
579
        }
580
        return true;
581
    }
582
 
583
    /**
584
     * XML_Parser::free()
585
     *
586
     * Free the internal resources associated with the parser
587
     *
588
     * @return null
589
     **/
590
    function free()
591
    {
592
        if (isset($this->parser) && is_resource($this->parser)) {
593
            xml_parser_free($this->parser);
594
            unset( $this->parser );
595
        }
596
        if (isset($this->fp) && is_resource($this->fp)) {
597
            fclose($this->fp);
598
        }
599
        unset($this->fp);
600
        return null;
601
    }
602
 
603
    /**
604
     * XML_Parser::raiseError()
605
     *
606
     * Throws a XML_Parser_Error
607
     *
608
     * @param string  $msg   the error message
609
     * @param integer $ecode the error message code
610
     *
611
     * @return XML_Parser_Error reference to the error object
612
     **/
613
    function &raiseError($msg = null, $ecode = 0)
614
    {
615
        $msg = !is_null($msg) ? $msg : $this->parser;
616
        $err = &new XML_Parser_Error($msg, $ecode);
617
        return parent::raiseError($err);
618
    }
619
 
620
    // }}}
621
    // {{{ funcStartHandler()
622
 
623
    /**
624
     * derives and calls the Start Handler function
625
     *
626
     * @param mixed $xp      ??
627
     * @param mixed $elem    ??
628
     * @param mixed $attribs ??
629
     *
630
     * @return void
631
     */
632
    function funcStartHandler($xp, $elem, $attribs)
633
    {
634
        $func = 'xmltag_' . $elem;
635
        $func = str_replace(array('.', '-', ':'), '_', $func);
636
        if (method_exists($this->_handlerObj, $func)) {
637
            call_user_func(array(&$this->_handlerObj, $func), $xp, $elem, $attribs);
638
        } elseif (method_exists($this->_handlerObj, 'xmltag')) {
639
            call_user_func(array(&$this->_handlerObj, 'xmltag'),
640
                $xp, $elem, $attribs);
641
        }
642
    }
643
 
644
    // }}}
645
    // {{{ funcEndHandler()
646
 
647
    /**
648
     * derives and calls the End Handler function
649
     *
650
     * @param mixed $xp   ??
651
     * @param mixed $elem ??
652
     *
653
     * @return void
654
     */
655
    function funcEndHandler($xp, $elem)
656
    {
657
        $func = 'xmltag_' . $elem . '_';
658
        $func = str_replace(array('.', '-', ':'), '_', $func);
659
        if (method_exists($this->_handlerObj, $func)) {
660
            call_user_func(array(&$this->_handlerObj, $func), $xp, $elem);
661
        } elseif (method_exists($this->_handlerObj, 'xmltag_')) {
662
            call_user_func(array(&$this->_handlerObj, 'xmltag_'), $xp, $elem);
663
        }
664
    }
665
 
666
    // }}}
667
    // {{{ startHandler()
668
 
669
    /**
670
     * abstract method signature for Start Handler
671
     *
672
     * @param mixed $xp       ??
673
     * @param mixed $elem     ??
674
     * @param mixed &$attribs ??
675
     *
676
     * @return null
677
     * @abstract
678
     */
679
    function startHandler($xp, $elem, &$attribs)
680
    {
681
        return null;
682
    }
683
 
684
    // }}}
685
    // {{{ endHandler()
686
 
687
    /**
688
     * abstract method signature for End Handler
689
     *
690
     * @param mixed $xp   ??
691
     * @param mixed $elem ??
692
     *
693
     * @return null
694
     * @abstract
695
     */
696
    function endHandler($xp, $elem)
697
    {
698
        return null;
699
    }
700
 
701
 
702
    // }}}me
703
}
704
 
705
/**
706
 * error class, replaces PEAR_Error
707
 *
708
 * An instance of this class will be returned
709
 * if an error occurs inside XML_Parser.
710
 *
711
 * There are three advantages over using the standard PEAR_Error:
712
 * - All messages will be prefixed
713
 * - check for XML_Parser error, using is_a( $error, 'XML_Parser_Error' )
714
 * - messages can be generated from the xml_parser resource
715
 *
716
 * @category  XML
717
 * @package   XML_Parser
718
 * @author    Stig Bakken <ssb@fast.no>
719
 * @author    Tomas V.V.Cox <cox@idecnet.com>
720
 * @author    Stephan Schmidt <schst@php.net>
721
 * @copyright 2002-2008 The PHP Group
722
 * @license   http://opensource.org/licenses/bsd-license New BSD License
723
 * @version   Release: @package_version@
724
 * @link      http://pear.php.net/package/XML_Parser
725
 * @see       PEAR_Error
726
 */
727
class XML_Parser_Error extends PEAR_Error
728
{
729
    // {{{ properties
730
 
731
    /**
732
    * prefix for all messages
733
    *
734
    * @var      string
735
    */
736
    var $error_message_prefix = 'XML_Parser: ';
737
 
738
    // }}}
739
    // {{{ constructor()
740
    /**
741
    * construct a new error instance
742
    *
743
    * You may either pass a message or an xml_parser resource as first
744
    * parameter. If a resource has been passed, the last error that
745
    * happened will be retrieved and returned.
746
    *
747
    * @param string|resource $msgorparser message or parser resource
748
    * @param integer         $code        error code
749
    * @param integer         $mode        error handling
750
    * @param integer         $level       error level
751
    *
752
    * @access   public
753
    * @todo PEAR CS - can't meet 85char line limit without arg refactoring
754
    */
755
    function XML_Parser_Error($msgorparser = 'unknown error', $code = 0, $mode = PEAR_ERROR_RETURN, $level = E_USER_NOTICE)
756
    {
757
        if (is_resource($msgorparser)) {
758
            $code        = xml_get_error_code($msgorparser);
759
            $msgorparser = sprintf('%s at XML input line %d:%d',
760
                xml_error_string($code),
761
                xml_get_current_line_number($msgorparser),
762
                xml_get_current_column_number($msgorparser));
763
        }
764
        $this->PEAR_Error($msgorparser, $code, $mode, $level);
765
    }
766
    // }}}
767
}
768
?>