PageRenderTime 48ms CodeModel.GetById 22ms RepoModel.GetById 1ms app.codeStats 0ms

/zarafa-7.0.8/php-webclient-ajax/server/PEAR/XML/Parser.php

#
PHP | 736 lines | 258 code | 68 blank | 410 comment | 39 complexity | 06f1bb422739e36ffbd1cc202bc104f4 MD5 | raw file
  1. <?php
  2. /*
  3. * Copyright 2005 - 2012 Zarafa B.V.
  4. *
  5. * This program is free software: you can redistribute it and/or modify
  6. * it under the terms of the GNU Affero General Public License, version 3,
  7. * as published by the Free Software Foundation with the following additional
  8. * term according to sec. 7:
  9. *
  10. * According to sec. 7 of the GNU Affero General Public License, version
  11. * 3, the terms of the AGPL are supplemented with the following terms:
  12. *
  13. * "Zarafa" is a registered trademark of Zarafa B.V. The licensing of
  14. * the Program under the AGPL does not imply a trademark license.
  15. * Therefore any rights, title and interest in our trademarks remain
  16. * entirely with us.
  17. *
  18. * However, if you propagate an unmodified version of the Program you are
  19. * allowed to use the term "Zarafa" to indicate that you distribute the
  20. * Program. Furthermore you may use our trademarks where it is necessary
  21. * to indicate the intended purpose of a product or service provided you
  22. * use it in accordance with honest practices in industrial or commercial
  23. * matters. If you want to propagate modified versions of the Program
  24. * under the name "Zarafa" or "Zarafa Server", you may only do so if you
  25. * have a written permission by Zarafa B.V. (to acquire a permission
  26. * please contact Zarafa at trademark@zarafa.com).
  27. *
  28. * The interactive user interface of the software displays an attribution
  29. * notice containing the term "Zarafa" and/or the logo of Zarafa.
  30. * Interactive user interfaces of unmodified and modified versions must
  31. * display Appropriate Legal Notices according to sec. 5 of the GNU
  32. * Affero General Public License, version 3, when you propagate
  33. * unmodified or modified versions of the Program. In accordance with
  34. * sec. 7 b) of the GNU Affero General Public License, version 3, these
  35. * Appropriate Legal Notices must retain the logo of Zarafa or display
  36. * the words "Initial Development by Zarafa" if the display of the logo
  37. * is not reasonably feasible for technical reasons. The use of the logo
  38. * of Zarafa in Legal Notices is allowed for unmodified and modified
  39. * versions of the software.
  40. *
  41. * This program is distributed in the hope that it will be useful,
  42. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  43. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  44. * GNU Affero General Public License for more details.
  45. *
  46. * You should have received a copy of the GNU Affero General Public License
  47. * along with this program. If not, see <http://www.gnu.org/licenses/>.
  48. *
  49. */
  50. ?>
  51. <?php
  52. //
  53. // +----------------------------------------------------------------------+
  54. // | PHP Version 4 |
  55. // +----------------------------------------------------------------------+
  56. // | Copyright (c) 1997-2004 The PHP Group |
  57. // +----------------------------------------------------------------------+
  58. // | This source file is subject to version 3.0 of the PHP license, |
  59. // | that is bundled with this package in the file LICENSE, and is |
  60. // | available at through the world-wide-web at |
  61. // | http://www.php.net/license/3_0.txt. |
  62. // | If you did not receive a copy of the PHP license and are unable to |
  63. // | obtain it through the world-wide-web, please send a note to |
  64. // | license@php.net so we can mail you a copy immediately. |
  65. // +----------------------------------------------------------------------+
  66. // | Author: Stig Bakken <ssb@fast.no> |
  67. // | Tomas V.V.Cox <cox@idecnet.com> |
  68. // | Stephan Schmidt <schst@php-tools.net> |
  69. // +----------------------------------------------------------------------+
  70. //
  71. // $Id: Parser.php,v 1.26 2005/09/23 11:51:10 schst Exp $
  72. /**
  73. * XML Parser class.
  74. *
  75. * This is an XML parser based on PHP's "xml" extension,
  76. * based on the bundled expat library.
  77. *
  78. * @category XML
  79. * @package XML_Parser
  80. * @author Stig Bakken <ssb@fast.no>
  81. * @author Tomas V.V.Cox <cox@idecnet.com>
  82. * @author Stephan Schmidt <schst@php-tools.net>
  83. */
  84. /**
  85. * uses PEAR's error handling
  86. */
  87. require_once 'PEAR.php';
  88. /**
  89. * resource could not be created
  90. */
  91. define('XML_PARSER_ERROR_NO_RESOURCE', 200);
  92. /**
  93. * unsupported mode
  94. */
  95. define('XML_PARSER_ERROR_UNSUPPORTED_MODE', 201);
  96. /**
  97. * invalid encoding was given
  98. */
  99. define('XML_PARSER_ERROR_INVALID_ENCODING', 202);
  100. /**
  101. * specified file could not be read
  102. */
  103. define('XML_PARSER_ERROR_FILE_NOT_READABLE', 203);
  104. /**
  105. * invalid input
  106. */
  107. define('XML_PARSER_ERROR_INVALID_INPUT', 204);
  108. /**
  109. * remote file cannot be retrieved in safe mode
  110. */
  111. define('XML_PARSER_ERROR_REMOTE', 205);
  112. /**
  113. * XML Parser class.
  114. *
  115. * This is an XML parser based on PHP's "xml" extension,
  116. * based on the bundled expat library.
  117. *
  118. * Notes:
  119. * - It requires PHP 4.0.4pl1 or greater
  120. * - From revision 1.17, the function names used by the 'func' mode
  121. * are in the format "xmltag_$elem", for example: use "xmltag_name"
  122. * to handle the <name></name> tags of your xml file.
  123. *
  124. * @category XML
  125. * @package XML_Parser
  126. * @author Stig Bakken <ssb@fast.no>
  127. * @author Tomas V.V.Cox <cox@idecnet.com>
  128. * @author Stephan Schmidt <schst@php-tools.net>
  129. * @todo create XML_Parser_Namespace to parse documents with namespaces
  130. * @todo create XML_Parser_Pull
  131. * @todo Tests that need to be made:
  132. * - mixing character encodings
  133. * - a test using all expat handlers
  134. * - options (folding, output charset)
  135. * - different parsing modes
  136. */
  137. class XML_Parser extends PEAR
  138. {
  139. // {{{ properties
  140. /**
  141. * XML parser handle
  142. *
  143. * @var resource
  144. * @see xml_parser_create()
  145. */
  146. var $parser;
  147. /**
  148. * File handle if parsing from a file
  149. *
  150. * @var resource
  151. */
  152. var $fp;
  153. /**
  154. * Whether to do case folding
  155. *
  156. * If set to true, all tag and attribute names will
  157. * be converted to UPPER CASE.
  158. *
  159. * @var boolean
  160. */
  161. var $folding = true;
  162. /**
  163. * Mode of operation, one of "event" or "func"
  164. *
  165. * @var string
  166. */
  167. var $mode;
  168. /**
  169. * Mapping from expat handler function to class method.
  170. *
  171. * @var array
  172. */
  173. var $handler = array(
  174. 'character_data_handler' => 'cdataHandler',
  175. 'default_handler' => 'defaultHandler',
  176. 'processing_instruction_handler' => 'piHandler',
  177. 'unparsed_entity_decl_handler' => 'unparsedHandler',
  178. 'notation_decl_handler' => 'notationHandler',
  179. 'external_entity_ref_handler' => 'entityrefHandler'
  180. );
  181. /**
  182. * source encoding
  183. *
  184. * @var string
  185. */
  186. var $srcenc;
  187. /**
  188. * target encoding
  189. *
  190. * @var string
  191. */
  192. var $tgtenc;
  193. /**
  194. * handler object
  195. *
  196. * @var object
  197. */
  198. var $_handlerObj;
  199. // }}}
  200. // {{{ constructor
  201. /**
  202. * Creates an XML parser.
  203. *
  204. * This is needed for PHP4 compatibility, it will
  205. * call the constructor, when a new instance is created.
  206. *
  207. * @param string $srcenc source charset encoding, use NULL (default) to use
  208. * whatever the document specifies
  209. * @param string $mode how this parser object should work, "event" for
  210. * startelement/endelement-type events, "func"
  211. * to have it call functions named after elements
  212. * @param string $tgenc a valid target encoding
  213. */
  214. function XML_Parser($srcenc = null, $mode = 'event', $tgtenc = null)
  215. {
  216. XML_Parser::__construct($srcenc, $mode, $tgtenc);
  217. }
  218. // }}}
  219. /**
  220. * PHP5 constructor
  221. *
  222. * @param string $srcenc source charset encoding, use NULL (default) to use
  223. * whatever the document specifies
  224. * @param string $mode how this parser object should work, "event" for
  225. * startelement/endelement-type events, "func"
  226. * to have it call functions named after elements
  227. * @param string $tgenc a valid target encoding
  228. */
  229. function __construct($srcenc = null, $mode = 'event', $tgtenc = null)
  230. {
  231. $this->PEAR('XML_Parser_Error');
  232. $this->mode = $mode;
  233. $this->srcenc = $srcenc;
  234. $this->tgtenc = $tgtenc;
  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. * @access public
  251. * @param string mode, either 'func' or 'event'
  252. * @return boolean|object true on success, PEAR_Error otherwise
  253. */
  254. function setMode($mode)
  255. {
  256. if ($mode != 'func' && $mode != 'event') {
  257. $this->raiseError('Unsupported mode given', XML_PARSER_ERROR_UNSUPPORTED_MODE);
  258. }
  259. $this->mode = $mode;
  260. return true;
  261. }
  262. /**
  263. * Sets the object, that will handle the XML events
  264. *
  265. * This allows you to create a handler object independent of the
  266. * parser object that you are using and easily switch the underlying
  267. * parser.
  268. *
  269. * If no object will be set, XML_Parser assumes that you
  270. * extend this class and handle the events in $this.
  271. *
  272. * @access public
  273. * @param object object to handle the events
  274. * @return boolean will always return true
  275. * @since v1.2.0beta3
  276. */
  277. function setHandlerObj(&$obj)
  278. {
  279. $this->_handlerObj = &$obj;
  280. return true;
  281. }
  282. /**
  283. * Init the element handlers
  284. *
  285. * @access private
  286. */
  287. function _initHandlers()
  288. {
  289. if (!is_resource($this->parser)) {
  290. return false;
  291. }
  292. if (!is_object($this->_handlerObj)) {
  293. $this->_handlerObj = &$this;
  294. }
  295. switch ($this->mode) {
  296. case 'func':
  297. xml_set_object($this->parser, $this->_handlerObj);
  298. xml_set_element_handler($this->parser, array(&$this, 'funcStartHandler'), array(&$this, 'funcEndHandler'));
  299. break;
  300. case 'event':
  301. xml_set_object($this->parser, $this->_handlerObj);
  302. xml_set_element_handler($this->parser, 'startHandler', 'endHandler');
  303. break;
  304. default:
  305. return $this->raiseError('Unsupported mode given', XML_PARSER_ERROR_UNSUPPORTED_MODE);
  306. break;
  307. }
  308. /**
  309. * set additional handlers for character data, entities, etc.
  310. */
  311. foreach ($this->handler as $xml_func => $method) {
  312. if (method_exists($this->_handlerObj, $method)) {
  313. $xml_func = 'xml_set_' . $xml_func;
  314. $xml_func($this->parser, $method);
  315. }
  316. }
  317. }
  318. // {{{ _create()
  319. /**
  320. * create the XML parser resource
  321. *
  322. * Has been moved from the constructor to avoid
  323. * problems with object references.
  324. *
  325. * Furthermore it allows us returning an error
  326. * if something fails.
  327. *
  328. * @access private
  329. * @return boolean|object true on success, PEAR_Error otherwise
  330. *
  331. * @see xml_parser_create
  332. */
  333. function _create()
  334. {
  335. if ($this->srcenc === null) {
  336. $xp = @xml_parser_create();
  337. } else {
  338. $xp = @xml_parser_create($this->srcenc);
  339. }
  340. if (is_resource($xp)) {
  341. if ($this->tgtenc !== null) {
  342. if (!@xml_parser_set_option($xp, XML_OPTION_TARGET_ENCODING,
  343. $this->tgtenc)) {
  344. return $this->raiseError('invalid target encoding', XML_PARSER_ERROR_INVALID_ENCODING);
  345. }
  346. }
  347. $this->parser = $xp;
  348. $result = $this->_initHandlers($this->mode);
  349. if ($this->isError($result)) {
  350. return $result;
  351. }
  352. xml_parser_set_option($xp, XML_OPTION_CASE_FOLDING, $this->folding);
  353. return true;
  354. }
  355. return $this->raiseError('Unable to create XML parser resource.', XML_PARSER_ERROR_NO_RESOURCE);
  356. }
  357. // }}}
  358. // {{{ reset()
  359. /**
  360. * Reset the parser.
  361. *
  362. * This allows you to use one parser instance
  363. * to parse multiple XML documents.
  364. *
  365. * @access public
  366. * @return boolean|object true on success, PEAR_Error otherwise
  367. */
  368. function reset()
  369. {
  370. $result = $this->_create();
  371. if ($this->isError( $result )) {
  372. return $result;
  373. }
  374. return true;
  375. }
  376. // }}}
  377. // {{{ setInputFile()
  378. /**
  379. * Sets the input xml file to be parsed
  380. *
  381. * @param string Filename (full path)
  382. * @return resource fopen handle of the given file
  383. * @throws XML_Parser_Error
  384. * @see setInput(), setInputString(), parse()
  385. * @access public
  386. */
  387. function setInputFile($file)
  388. {
  389. /**
  390. * check, if file is a remote file
  391. */
  392. if (eregi('^(http|ftp)://', substr($file, 0, 10))) {
  393. if (!ini_get('allow_url_fopen')) {
  394. return $this->raiseError('Remote files cannot be parsed, as safe mode is enabled.', XML_PARSER_ERROR_REMOTE);
  395. }
  396. }
  397. $fp = @fopen($file, 'rb');
  398. if (is_resource($fp)) {
  399. $this->fp = $fp;
  400. return $fp;
  401. }
  402. return $this->raiseError('File could not be opened.', XML_PARSER_ERROR_FILE_NOT_READABLE);
  403. }
  404. // }}}
  405. // {{{ setInputString()
  406. /**
  407. * XML_Parser::setInputString()
  408. *
  409. * Sets the xml input from a string
  410. *
  411. * @param string $data a string containing the XML document
  412. * @return null
  413. **/
  414. function setInputString($data)
  415. {
  416. $this->fp = $data;
  417. return null;
  418. }
  419. // }}}
  420. // {{{ setInput()
  421. /**
  422. * Sets the file handle to use with parse().
  423. *
  424. * You should use setInputFile() or setInputString() if you
  425. * pass a string
  426. *
  427. * @param mixed $fp Can be either a resource returned from fopen(),
  428. * a URL, a local filename or a string.
  429. * @access public
  430. * @see parse()
  431. * @uses setInputString(), setInputFile()
  432. */
  433. function setInput($fp)
  434. {
  435. if (is_resource($fp)) {
  436. $this->fp = $fp;
  437. return true;
  438. }
  439. // see if it's an absolute URL (has a scheme at the beginning)
  440. elseif (eregi('^[a-z]+://', substr($fp, 0, 10))) {
  441. return $this->setInputFile($fp);
  442. }
  443. // see if it's a local file
  444. elseif (file_exists($fp)) {
  445. return $this->setInputFile($fp);
  446. }
  447. // it must be a string
  448. else {
  449. $this->fp = $fp;
  450. return true;
  451. }
  452. return $this->raiseError('Illegal input format', XML_PARSER_ERROR_INVALID_INPUT);
  453. }
  454. // }}}
  455. // {{{ parse()
  456. /**
  457. * Central parsing function.
  458. *
  459. * @return true|object PEAR error returns true on success, or a PEAR_Error otherwise
  460. * @access public
  461. */
  462. function parse()
  463. {
  464. /**
  465. * reset the parser
  466. */
  467. $result = $this->reset();
  468. if ($this->isError($result)) {
  469. return $result;
  470. }
  471. // if $this->fp was fopened previously
  472. if (is_resource($this->fp)) {
  473. while ($data = fread($this->fp, 4096)) {
  474. if (!$this->_parseString($data, feof($this->fp))) {
  475. $error = &$this->raiseError();
  476. $this->free();
  477. return $error;
  478. }
  479. }
  480. // otherwise, $this->fp must be a string
  481. } else {
  482. if (!$this->_parseString($this->fp, true)) {
  483. $error = &$this->raiseError();
  484. $this->free();
  485. return $error;
  486. }
  487. }
  488. $this->free();
  489. return true;
  490. }
  491. /**
  492. * XML_Parser::_parseString()
  493. *
  494. * @param string $data
  495. * @param boolean $eof
  496. * @return bool
  497. * @access private
  498. * @see parseString()
  499. **/
  500. function _parseString($data, $eof = false)
  501. {
  502. return xml_parse($this->parser, $data, $eof);
  503. }
  504. // }}}
  505. // {{{ parseString()
  506. /**
  507. * XML_Parser::parseString()
  508. *
  509. * Parses a string.
  510. *
  511. * @param string $data XML data
  512. * @param boolean $eof If set and TRUE, data is the last piece of data sent in this parser
  513. * @throws XML_Parser_Error
  514. * @return Pear Error|true true on success or a PEAR Error
  515. * @see _parseString()
  516. */
  517. function parseString($data, $eof = false)
  518. {
  519. if (!isset($this->parser) || !is_resource($this->parser)) {
  520. $this->reset();
  521. }
  522. if (!$this->_parseString($data, $eof)) {
  523. $error = &$this->raiseError();
  524. $this->free();
  525. return $error;
  526. }
  527. if ($eof === true) {
  528. $this->free();
  529. }
  530. return true;
  531. }
  532. /**
  533. * XML_Parser::free()
  534. *
  535. * Free the internal resources associated with the parser
  536. *
  537. * @return null
  538. **/
  539. function free()
  540. {
  541. if (isset($this->parser) && is_resource($this->parser)) {
  542. xml_parser_free($this->parser);
  543. unset( $this->parser );
  544. }
  545. if (isset($this->fp) && is_resource($this->fp)) {
  546. fclose($this->fp);
  547. }
  548. unset($this->fp);
  549. return null;
  550. }
  551. /**
  552. * XML_Parser::raiseError()
  553. *
  554. * Throws a XML_Parser_Error
  555. *
  556. * @param string $msg the error message
  557. * @param integer $ecode the error message code
  558. * @return XML_Parser_Error
  559. **/
  560. function raiseError($msg = null, $ecode = 0)
  561. {
  562. $msg = !is_null($msg) ? $msg : $this->parser;
  563. $err = &new XML_Parser_Error($msg, $ecode);
  564. return parent::raiseError($err);
  565. }
  566. // }}}
  567. // {{{ funcStartHandler()
  568. function funcStartHandler($xp, $elem, $attribs)
  569. {
  570. $func = 'xmltag_' . $elem;
  571. if (strchr($func, '.')) {
  572. $func = str_replace('.', '_', $func);
  573. }
  574. if (method_exists($this->_handlerObj, $func)) {
  575. call_user_func(array(&$this->_handlerObj, $func), $xp, $elem, $attribs);
  576. } elseif (method_exists($this->_handlerObj, 'xmltag')) {
  577. call_user_func(array(&$this->_handlerObj, 'xmltag'), $xp, $elem, $attribs);
  578. }
  579. }
  580. // }}}
  581. // {{{ funcEndHandler()
  582. function funcEndHandler($xp, $elem)
  583. {
  584. $func = 'xmltag_' . $elem . '_';
  585. if (strchr($func, '.')) {
  586. $func = str_replace('.', '_', $func);
  587. }
  588. if (method_exists($this->_handlerObj, $func)) {
  589. call_user_func(array(&$this->_handlerObj, $func), $xp, $elem);
  590. } elseif (method_exists($this->_handlerObj, 'xmltag_')) {
  591. call_user_func(array(&$this->_handlerObj, 'xmltag_'), $xp, $elem);
  592. }
  593. }
  594. // }}}
  595. // {{{ startHandler()
  596. /**
  597. *
  598. * @abstract
  599. */
  600. function startHandler($xp, $elem, &$attribs)
  601. {
  602. return NULL;
  603. }
  604. // }}}
  605. // {{{ endHandler()
  606. /**
  607. *
  608. * @abstract
  609. */
  610. function endHandler($xp, $elem)
  611. {
  612. return NULL;
  613. }
  614. // }}}me
  615. }
  616. /**
  617. * error class, replaces PEAR_Error
  618. *
  619. * An instance of this class will be returned
  620. * if an error occurs inside XML_Parser.
  621. *
  622. * There are three advantages over using the standard PEAR_Error:
  623. * - All messages will be prefixed
  624. * - check for XML_Parser error, using is_a( $error, 'XML_Parser_Error' )
  625. * - messages can be generated from the xml_parser resource
  626. *
  627. * @package XML_Parser
  628. * @access public
  629. * @see PEAR_Error
  630. */
  631. class XML_Parser_Error extends PEAR_Error
  632. {
  633. // {{{ properties
  634. /**
  635. * prefix for all messages
  636. *
  637. * @var string
  638. */
  639. var $error_message_prefix = 'XML_Parser: ';
  640. // }}}
  641. // {{{ constructor()
  642. /**
  643. * construct a new error instance
  644. *
  645. * You may either pass a message or an xml_parser resource as first
  646. * parameter. If a resource has been passed, the last error that
  647. * happened will be retrieved and returned.
  648. *
  649. * @access public
  650. * @param string|resource message or parser resource
  651. * @param integer error code
  652. * @param integer error handling
  653. * @param integer error level
  654. */
  655. function XML_Parser_Error($msgorparser = 'unknown error', $code = 0, $mode = PEAR_ERROR_RETURN, $level = E_USER_NOTICE)
  656. {
  657. if (is_resource($msgorparser)) {
  658. $code = xml_get_error_code($msgorparser);
  659. $msgorparser = sprintf('%s at XML input line %d:%d',
  660. xml_error_string($code),
  661. xml_get_current_line_number($msgorparser),
  662. xml_get_current_column_number($msgorparser));
  663. }
  664. $this->PEAR_Error($msgorparser, $code, $mode, $level);
  665. }
  666. // }}}
  667. }
  668. ?>