source: branches/version-2_12-dev/data/module/XML/Parser.php @ 21713

Revision 21713, 20.8 KB checked in by AMUAMU, 12 years ago (diff)

#1604 (外部連携用APIの実装) 基本機構実装、Amazon API相当の機能の実装(商品一覧系)

Line 
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 */
55require_once 'PEAR.php';
56
57/**
58 * resource could not be created
59 */
60define('XML_PARSER_ERROR_NO_RESOURCE', 200);
61
62/**
63 * unsupported mode
64 */
65define('XML_PARSER_ERROR_UNSUPPORTED_MODE', 201);
66
67/**
68 * invalid encoding was given
69 */
70define('XML_PARSER_ERROR_INVALID_ENCODING', 202);
71
72/**
73 * specified file could not be read
74 */
75define('XML_PARSER_ERROR_FILE_NOT_READABLE', 203);
76
77/**
78 * invalid input
79 */
80define('XML_PARSER_ERROR_INVALID_INPUT', 204);
81
82/**
83 * remote file cannot be retrieved in safe mode
84 */
85define('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 */
116class 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 */
727class 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?>
Note: See TracBrowser for help on using the repository browser.