You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

356 lines
9.1KB

  1. <?php
  2. //
  3. // +----------------------------------------------------------------------+
  4. // | PHP Version 4 |
  5. // +----------------------------------------------------------------------+
  6. // | Copyright (c) 1997-2003 The PHP Group |
  7. // +----------------------------------------------------------------------+
  8. // | This source file is subject to version 2.02 of the PHP license, |
  9. // | that is bundled with this package in the file LICENSE, and is |
  10. // | available at through the world-wide-web at |
  11. // | http://www.php.net/license/2_02.txt. |
  12. // | If you did not receive a copy of the PHP license and are unable to |
  13. // | obtain it through the world-wide-web, please send a note to |
  14. // | license@php.net so we can mail you a copy immediately. |
  15. // +----------------------------------------------------------------------+
  16. // | Author: Stig Bakken <ssb@fast.no> |
  17. // +----------------------------------------------------------------------+
  18. //
  19. // $Id$
  20. cInclude('pear', 'PEAR.php');
  21. /**
  22. * XML Parser class. This is an XML parser based on PHP's "xml" extension,
  23. * based on the bundled expat library.
  24. *
  25. * @author Stig Bakken <ssb@fast.no>
  26. * @todo Tests that need to be made:
  27. * - error class
  28. * - mixing character encodings
  29. * - a test using all expat handlers
  30. * - options (folding, output charset)
  31. * - different parsing modes
  32. *
  33. * @notes - It requires PHP 4.0.4pl1 or greater
  34. * - From revision 1.17, the function names used by the 'func' mode
  35. * are in the format "xmltag_$elem", for example: use "xmltag_name"
  36. * to handle the <name></name> tags of your xml file.
  37. */
  38. class XML_Parser extends PEAR
  39. {
  40. // {{{ properties
  41. /**
  42. * @var resource XML parser handle
  43. */
  44. var $parser;
  45. /**
  46. * @var resource File handle if parsing from a file
  47. */
  48. var $fp;
  49. /**
  50. * @var boolean Whether to do case folding
  51. */
  52. var $folding = true;
  53. /**
  54. * @var string Mode of operation, one of "event" or "func"
  55. */
  56. var $mode;
  57. /**
  58. * Mapping from expat handler function to class method.
  59. *
  60. * @var array
  61. */
  62. var $handler = array(
  63. 'character_data_handler' => 'cdataHandler',
  64. 'default_handler' => 'defaultHandler',
  65. 'processing_instruction_handler' => 'piHandler',
  66. 'unparsed_entity_decl_handler' => 'unparsedHandler',
  67. 'notation_decl_handler' => 'notationHandler',
  68. 'external_entity_ref_handler' => 'entityrefHandler'
  69. );
  70. /**
  71. * @var string source encoding
  72. */
  73. var $srcenc;
  74. /**
  75. * @var string target encoding
  76. */
  77. var $tgtenc;
  78. /*
  79. * Use call_user_func when php >= 4.0.7
  80. * @var boolean
  81. * @see setMode()
  82. */
  83. var $use_call_user_func = true;
  84. // }}}
  85. // {{{ constructor
  86. /**
  87. * Creates an XML parser.
  88. *
  89. * @param string source charset encoding, use NULL (default) to use
  90. * whatever the document specifies
  91. * @param string how this parser object should work, "event" for
  92. * startelement/endelement-type events, "func"
  93. * to have it call functions named after elements
  94. *
  95. * @see xml_parser_create
  96. */
  97. function __construct($srcenc = null, $mode = "event", $tgtenc = null)
  98. {
  99. $this->PEAR('XML_Parser_Error');
  100. if ($srcenc === null) {
  101. $xp = @xml_parser_create();
  102. } else {
  103. $xp = @xml_parser_create($srcenc);
  104. }
  105. if (is_resource($xp)) {
  106. if ($tgtenc !== null) {
  107. if (!@xml_parser_set_option($xp, XML_OPTION_TARGET_ENCODING,
  108. $tgtenc)) {
  109. return $this->raiseError("invalid target encoding");
  110. }
  111. }
  112. $this->parser = $xp;
  113. $this->setMode($mode);
  114. xml_parser_set_option($xp, XML_OPTION_CASE_FOLDING, $this->folding);
  115. }
  116. $this->srcenc = $srcenc;
  117. $this->tgtenc = $tgtenc;
  118. }
  119. // }}}
  120. // {{{ setMode()
  121. /**
  122. * Sets the mode and all handler.
  123. *
  124. * @param string
  125. * @see $handler
  126. */
  127. function setMode($mode)
  128. {
  129. $this->mode = $mode;
  130. xml_set_object($this->parser, $this);
  131. switch ($mode) {
  132. case "func":
  133. // use call_user_func() when php >= 4.0.7
  134. // or call_user_method() if not
  135. if (version_compare(phpversion(), '4.0.7', 'lt')) {
  136. $this->use_call_user_func = false;
  137. } else {
  138. $this->use_call_user_func = true;
  139. }
  140. xml_set_element_handler($this->parser, "funcStartHandler", "funcEndHandler");
  141. break;
  142. case "event":
  143. xml_set_element_handler($this->parser, "startHandler", "endHandler");
  144. break;
  145. }
  146. foreach ($this->handler as $xml_func => $method)
  147. if (method_exists($this, $method)) {
  148. $xml_func = "xml_set_" . $xml_func;
  149. $xml_func($this->parser, $method);
  150. }
  151. }
  152. // }}}
  153. // {{{ setInputFile()
  154. /**
  155. * Defines
  156. *
  157. * @param string Filename (full path)
  158. * @return resource fopen handle of the given file
  159. * @throws XML_Parser_Error
  160. * @see setInput(), parse()
  161. * @access public
  162. */
  163. function setInputFile($file)
  164. {
  165. $fp = @fopen($file, "rb");
  166. if (is_resource($fp)) {
  167. $this->fp = $fp;
  168. return $fp;
  169. }
  170. return $this->raiseError($php_errormsg);
  171. }
  172. // }}}
  173. // {{{ setInput()
  174. /**
  175. * Sets the file handle to use with parse().
  176. *
  177. * @param resource fopen
  178. * @access public
  179. * @see parse(), setInputFile()
  180. */
  181. function setInput($fp)
  182. {
  183. if (is_resource($fp)) {
  184. $this->fp = $fp;
  185. return true;
  186. }
  187. return $this->raiseError("not a file resource");
  188. }
  189. // }}}
  190. // {{{ parse()
  191. /**
  192. * Central parsing function.
  193. *
  194. * @throws XML_Parser_Error
  195. * @return boolean true on success
  196. * @see parseString()
  197. * @access public
  198. */
  199. function parse()
  200. {
  201. if (!is_resource($this->fp)) {
  202. return $this->raiseError("no input");
  203. }
  204. while ($data = fread($this->fp, 2048)) {
  205. $err = $this->parseString($data, feof($this->fp));
  206. if (PEAR::isError($err)) {
  207. fclose($this->fp);
  208. return $err;
  209. }
  210. }
  211. fclose($this->fp);
  212. return true;
  213. }
  214. // }}}
  215. // {{{ parseString()
  216. /**
  217. * Parses a string.
  218. *
  219. * @param string XML data
  220. * @param boolean ???
  221. * @throws XML_Parser_Error
  222. * @return mixed true on success or a string with the xml parser error
  223. */
  224. function parseString($data, $eof = false)
  225. {
  226. if (!xml_parse($this->parser, $data, $eof)) {
  227. $err = $this->raiseError($this->parser);
  228. xml_parser_free($this->parser);
  229. return $err;
  230. }
  231. return true;
  232. }
  233. // }}}
  234. // {{{ funcStartHandler()
  235. function funcStartHandler($xp, $elem, $attribs)
  236. {
  237. $func = 'xmltag_' . $elem;
  238. if (method_exists($this, $func)) {
  239. if ($this->use_call_user_func) {
  240. call_user_func(array(&$this, $func), $xp, $elem, $attribs);
  241. } else {
  242. call_user_method($func, $this, $xp, $elem, $attribs);
  243. }
  244. }
  245. }
  246. // }}}
  247. // {{{ funcEndHandler()
  248. function funcEndHandler($xp, $elem)
  249. {
  250. $func = 'xmltag_' . $elem . '_';
  251. if (method_exists($this, $func)) {
  252. if ($this->use_call_user_func) {
  253. call_user_func(array(&$this, $func), $xp, $elem);
  254. } else {
  255. call_user_method($func, $this, $xp, $elem);
  256. }
  257. }
  258. }
  259. // }}}
  260. // {{{ startHandler()
  261. /**
  262. *
  263. * @abstract
  264. */
  265. function startHandler($xp, $elem, &$attribs)
  266. {
  267. return NULL;
  268. }
  269. // }}}
  270. // {{{ endHandler()
  271. /**
  272. *
  273. * @abstract
  274. */
  275. function endHandler($xp, $elem)
  276. {
  277. return NULL;
  278. }
  279. // }}}
  280. }
  281. class XML_Parser_Error extends PEAR_Error
  282. {
  283. // {{{ properties
  284. var $error_message_prefix = 'XML_Parser: ';
  285. // }}}
  286. // {{{ constructor()
  287. function __construct($msgorparser = 'unknown error', $code = 0, $mode = PEAR_ERROR_RETURN, $level = E_USER_NOTICE)
  288. {
  289. if (is_resource($msgorparser)) {
  290. $code = xml_get_error_code($msgorparser);
  291. $msgorparser = sprintf("%s at XML input line %d",
  292. xml_error_string($code),
  293. xml_get_current_line_number($msgorparser));
  294. }
  295. $this->PEAR_Error($msgorparser, $code, $mode, $level);
  296. }
  297. // }}}
  298. }
  299. ?>