PageRenderTime 56ms CodeModel.GetById 11ms RepoModel.GetById 0ms app.codeStats 0ms

/tests/simpletest/parser.php

https://github.com/quarkness/piwik
PHP | 764 lines | 341 code | 50 blank | 373 comment | 48 complexity | 635951819bdaebf6a4c4b3ae861cdd6c MD5 | raw file
  1. <?php
  2. /**
  3. * base include file for SimpleTest
  4. * @package SimpleTest
  5. * @subpackage MockObjects
  6. * @version $Id: parser.php 1723 2008-04-08 00:34:10Z lastcraft $
  7. */
  8. /**#@+
  9. * Lexer mode stack constants
  10. */
  11. foreach (array('LEXER_ENTER', 'LEXER_MATCHED',
  12. 'LEXER_UNMATCHED', 'LEXER_EXIT',
  13. 'LEXER_SPECIAL') as $i => $constant) {
  14. if (! defined($constant)) {
  15. define($constant, $i + 1);
  16. }
  17. }
  18. /**#@-*/
  19. /**
  20. * Compounded regular expression. Any of
  21. * the contained patterns could match and
  22. * when one does, it's label is returned.
  23. * @package SimpleTest
  24. * @subpackage WebTester
  25. */
  26. class ParallelRegex {
  27. var $_patterns;
  28. var $_labels;
  29. var $_regex;
  30. var $_case;
  31. /**
  32. * Constructor. Starts with no patterns.
  33. * @param boolean $case True for case sensitive, false
  34. * for insensitive.
  35. * @access public
  36. */
  37. function ParallelRegex($case) {
  38. $this->_case = $case;
  39. $this->_patterns = array();
  40. $this->_labels = array();
  41. $this->_regex = null;
  42. }
  43. /**
  44. * Adds a pattern with an optional label.
  45. * @param string $pattern Perl style regex, but ( and )
  46. * lose the usual meaning.
  47. * @param string $label Label of regex to be returned
  48. * on a match.
  49. * @access public
  50. */
  51. function addPattern($pattern, $label = true) {
  52. $count = count($this->_patterns);
  53. $this->_patterns[$count] = $pattern;
  54. $this->_labels[$count] = $label;
  55. $this->_regex = null;
  56. }
  57. /**
  58. * Attempts to match all patterns at once against
  59. * a string.
  60. * @param string $subject String to match against.
  61. * @param string $match First matched portion of
  62. * subject.
  63. * @return boolean True on success.
  64. * @access public
  65. */
  66. function match($subject, &$match) {
  67. if (count($this->_patterns) == 0) {
  68. return false;
  69. }
  70. if (! preg_match($this->_getCompoundedRegex(), $subject, $matches)) {
  71. $match = '';
  72. return false;
  73. }
  74. $match = $matches[0];
  75. for ($i = 1; $i < count($matches); $i++) {
  76. if ($matches[$i]) {
  77. return $this->_labels[$i - 1];
  78. }
  79. }
  80. return true;
  81. }
  82. /**
  83. * Compounds the patterns into a single
  84. * regular expression separated with the
  85. * "or" operator. Caches the regex.
  86. * Will automatically escape (, ) and / tokens.
  87. * @param array $patterns List of patterns in order.
  88. * @access private
  89. */
  90. function _getCompoundedRegex() {
  91. if ($this->_regex == null) {
  92. for ($i = 0, $count = count($this->_patterns); $i < $count; $i++) {
  93. $this->_patterns[$i] = '(' . str_replace(
  94. array('/', '(', ')'),
  95. array('\/', '\(', '\)'),
  96. $this->_patterns[$i]) . ')';
  97. }
  98. $this->_regex = "/" . implode("|", $this->_patterns) . "/" . $this->_getPerlMatchingFlags();
  99. }
  100. return $this->_regex;
  101. }
  102. /**
  103. * Accessor for perl regex mode flags to use.
  104. * @return string Perl regex flags.
  105. * @access private
  106. */
  107. function _getPerlMatchingFlags() {
  108. return ($this->_case ? "msS" : "msSi");
  109. }
  110. }
  111. /**
  112. * States for a stack machine.
  113. * @package SimpleTest
  114. * @subpackage WebTester
  115. */
  116. class SimpleStateStack {
  117. var $_stack;
  118. /**
  119. * Constructor. Starts in named state.
  120. * @param string $start Starting state name.
  121. * @access public
  122. */
  123. function SimpleStateStack($start) {
  124. $this->_stack = array($start);
  125. }
  126. /**
  127. * Accessor for current state.
  128. * @return string State.
  129. * @access public
  130. */
  131. function getCurrent() {
  132. return $this->_stack[count($this->_stack) - 1];
  133. }
  134. /**
  135. * Adds a state to the stack and sets it
  136. * to be the current state.
  137. * @param string $state New state.
  138. * @access public
  139. */
  140. function enter($state) {
  141. array_push($this->_stack, $state);
  142. }
  143. /**
  144. * Leaves the current state and reverts
  145. * to the previous one.
  146. * @return boolean False if we drop off
  147. * the bottom of the list.
  148. * @access public
  149. */
  150. function leave() {
  151. if (count($this->_stack) == 1) {
  152. return false;
  153. }
  154. array_pop($this->_stack);
  155. return true;
  156. }
  157. }
  158. /**
  159. * Accepts text and breaks it into tokens.
  160. * Some optimisation to make the sure the
  161. * content is only scanned by the PHP regex
  162. * parser once. Lexer modes must not start
  163. * with leading underscores.
  164. * @package SimpleTest
  165. * @subpackage WebTester
  166. */
  167. class SimpleLexer {
  168. var $_regexes;
  169. var $_parser;
  170. var $_mode;
  171. var $_mode_handlers;
  172. var $_case;
  173. /**
  174. * Sets up the lexer in case insensitive matching
  175. * by default.
  176. * @param SimpleSaxParser $parser Handling strategy by
  177. * reference.
  178. * @param string $start Starting handler.
  179. * @param boolean $case True for case sensitive.
  180. * @access public
  181. */
  182. function SimpleLexer(&$parser, $start = "accept", $case = false) {
  183. $this->_case = $case;
  184. $this->_regexes = array();
  185. $this->_parser = &$parser;
  186. $this->_mode = new SimpleStateStack($start);
  187. $this->_mode_handlers = array($start => $start);
  188. }
  189. /**
  190. * Adds a token search pattern for a particular
  191. * parsing mode. The pattern does not change the
  192. * current mode.
  193. * @param string $pattern Perl style regex, but ( and )
  194. * lose the usual meaning.
  195. * @param string $mode Should only apply this
  196. * pattern when dealing with
  197. * this type of input.
  198. * @access public
  199. */
  200. function addPattern($pattern, $mode = "accept") {
  201. if (! isset($this->_regexes[$mode])) {
  202. $this->_regexes[$mode] = new ParallelRegex($this->_case);
  203. }
  204. $this->_regexes[$mode]->addPattern($pattern);
  205. if (! isset($this->_mode_handlers[$mode])) {
  206. $this->_mode_handlers[$mode] = $mode;
  207. }
  208. }
  209. /**
  210. * Adds a pattern that will enter a new parsing
  211. * mode. Useful for entering parenthesis, strings,
  212. * tags, etc.
  213. * @param string $pattern Perl style regex, but ( and )
  214. * lose the usual meaning.
  215. * @param string $mode Should only apply this
  216. * pattern when dealing with
  217. * this type of input.
  218. * @param string $new_mode Change parsing to this new
  219. * nested mode.
  220. * @access public
  221. */
  222. function addEntryPattern($pattern, $mode, $new_mode) {
  223. if (! isset($this->_regexes[$mode])) {
  224. $this->_regexes[$mode] = new ParallelRegex($this->_case);
  225. }
  226. $this->_regexes[$mode]->addPattern($pattern, $new_mode);
  227. if (! isset($this->_mode_handlers[$new_mode])) {
  228. $this->_mode_handlers[$new_mode] = $new_mode;
  229. }
  230. }
  231. /**
  232. * Adds a pattern that will exit the current mode
  233. * and re-enter the previous one.
  234. * @param string $pattern Perl style regex, but ( and )
  235. * lose the usual meaning.
  236. * @param string $mode Mode to leave.
  237. * @access public
  238. */
  239. function addExitPattern($pattern, $mode) {
  240. if (! isset($this->_regexes[$mode])) {
  241. $this->_regexes[$mode] = new ParallelRegex($this->_case);
  242. }
  243. $this->_regexes[$mode]->addPattern($pattern, "__exit");
  244. if (! isset($this->_mode_handlers[$mode])) {
  245. $this->_mode_handlers[$mode] = $mode;
  246. }
  247. }
  248. /**
  249. * Adds a pattern that has a special mode. Acts as an entry
  250. * and exit pattern in one go, effectively calling a special
  251. * parser handler for this token only.
  252. * @param string $pattern Perl style regex, but ( and )
  253. * lose the usual meaning.
  254. * @param string $mode Should only apply this
  255. * pattern when dealing with
  256. * this type of input.
  257. * @param string $special Use this mode for this one token.
  258. * @access public
  259. */
  260. function addSpecialPattern($pattern, $mode, $special) {
  261. if (! isset($this->_regexes[$mode])) {
  262. $this->_regexes[$mode] = new ParallelRegex($this->_case);
  263. }
  264. $this->_regexes[$mode]->addPattern($pattern, "_$special");
  265. if (! isset($this->_mode_handlers[$special])) {
  266. $this->_mode_handlers[$special] = $special;
  267. }
  268. }
  269. /**
  270. * Adds a mapping from a mode to another handler.
  271. * @param string $mode Mode to be remapped.
  272. * @param string $handler New target handler.
  273. * @access public
  274. */
  275. function mapHandler($mode, $handler) {
  276. $this->_mode_handlers[$mode] = $handler;
  277. }
  278. /**
  279. * Splits the page text into tokens. Will fail
  280. * if the handlers report an error or if no
  281. * content is consumed. If successful then each
  282. * unparsed and parsed token invokes a call to the
  283. * held listener.
  284. * @param string $raw Raw HTML text.
  285. * @return boolean True on success, else false.
  286. * @access public
  287. */
  288. function parse($raw) {
  289. if (! isset($this->_parser)) {
  290. return false;
  291. }
  292. $length = strlen($raw);
  293. while (is_array($parsed = $this->_reduce($raw))) {
  294. list($raw, $unmatched, $matched, $mode) = $parsed;
  295. if (! $this->_dispatchTokens($unmatched, $matched, $mode)) {
  296. return false;
  297. }
  298. if ($raw === '') {
  299. return true;
  300. }
  301. if (strlen($raw) == $length) {
  302. return false;
  303. }
  304. $length = strlen($raw);
  305. }
  306. if (! $parsed) {
  307. return false;
  308. }
  309. return $this->_invokeParser($raw, LEXER_UNMATCHED);
  310. }
  311. /**
  312. * Sends the matched token and any leading unmatched
  313. * text to the parser changing the lexer to a new
  314. * mode if one is listed.
  315. * @param string $unmatched Unmatched leading portion.
  316. * @param string $matched Actual token match.
  317. * @param string $mode Mode after match. A boolean
  318. * false mode causes no change.
  319. * @return boolean False if there was any error
  320. * from the parser.
  321. * @access private
  322. */
  323. function _dispatchTokens($unmatched, $matched, $mode = false) {
  324. if (! $this->_invokeParser($unmatched, LEXER_UNMATCHED)) {
  325. return false;
  326. }
  327. if (is_bool($mode)) {
  328. return $this->_invokeParser($matched, LEXER_MATCHED);
  329. }
  330. if ($this->_isModeEnd($mode)) {
  331. if (! $this->_invokeParser($matched, LEXER_EXIT)) {
  332. return false;
  333. }
  334. return $this->_mode->leave();
  335. }
  336. if ($this->_isSpecialMode($mode)) {
  337. $this->_mode->enter($this->_decodeSpecial($mode));
  338. if (! $this->_invokeParser($matched, LEXER_SPECIAL)) {
  339. return false;
  340. }
  341. return $this->_mode->leave();
  342. }
  343. $this->_mode->enter($mode);
  344. return $this->_invokeParser($matched, LEXER_ENTER);
  345. }
  346. /**
  347. * Tests to see if the new mode is actually to leave
  348. * the current mode and pop an item from the matching
  349. * mode stack.
  350. * @param string $mode Mode to test.
  351. * @return boolean True if this is the exit mode.
  352. * @access private
  353. */
  354. function _isModeEnd($mode) {
  355. return ($mode === "__exit");
  356. }
  357. /**
  358. * Test to see if the mode is one where this mode
  359. * is entered for this token only and automatically
  360. * leaves immediately afterwoods.
  361. * @param string $mode Mode to test.
  362. * @return boolean True if this is the exit mode.
  363. * @access private
  364. */
  365. function _isSpecialMode($mode) {
  366. return (strncmp($mode, "_", 1) == 0);
  367. }
  368. /**
  369. * Strips the magic underscore marking single token
  370. * modes.
  371. * @param string $mode Mode to decode.
  372. * @return string Underlying mode name.
  373. * @access private
  374. */
  375. function _decodeSpecial($mode) {
  376. return substr($mode, 1);
  377. }
  378. /**
  379. * Calls the parser method named after the current
  380. * mode. Empty content will be ignored. The lexer
  381. * has a parser handler for each mode in the lexer.
  382. * @param string $content Text parsed.
  383. * @param boolean $is_match Token is recognised rather
  384. * than unparsed data.
  385. * @access private
  386. */
  387. function _invokeParser($content, $is_match) {
  388. if (($content === '') || ($content === false)) {
  389. return true;
  390. }
  391. $handler = $this->_mode_handlers[$this->_mode->getCurrent()];
  392. return $this->_parser->$handler($content, $is_match);
  393. }
  394. /**
  395. * Tries to match a chunk of text and if successful
  396. * removes the recognised chunk and any leading
  397. * unparsed data. Empty strings will not be matched.
  398. * @param string $raw The subject to parse. This is the
  399. * content that will be eaten.
  400. * @return array/boolean Three item list of unparsed
  401. * content followed by the
  402. * recognised token and finally the
  403. * action the parser is to take.
  404. * True if no match, false if there
  405. * is a parsing error.
  406. * @access private
  407. */
  408. function _reduce($raw) {
  409. if ($action = $this->_regexes[$this->_mode->getCurrent()]->match($raw, $match)) {
  410. $unparsed_character_count = strpos($raw, $match);
  411. $unparsed = substr($raw, 0, $unparsed_character_count);
  412. $raw = substr($raw, $unparsed_character_count + strlen($match));
  413. return array($raw, $unparsed, $match, $action);
  414. }
  415. return true;
  416. }
  417. }
  418. /**
  419. * Breaks HTML into SAX events.
  420. * @package SimpleTest
  421. * @subpackage WebTester
  422. */
  423. class SimpleHtmlLexer extends SimpleLexer {
  424. /**
  425. * Sets up the lexer with case insensitive matching
  426. * and adds the HTML handlers.
  427. * @param SimpleSaxParser $parser Handling strategy by
  428. * reference.
  429. * @access public
  430. */
  431. function SimpleHtmlLexer(&$parser) {
  432. $this->SimpleLexer($parser, 'text');
  433. $this->mapHandler('text', 'acceptTextToken');
  434. $this->_addSkipping();
  435. foreach ($this->_getParsedTags() as $tag) {
  436. $this->_addTag($tag);
  437. }
  438. $this->_addInTagTokens();
  439. }
  440. /**
  441. * List of parsed tags. Others are ignored.
  442. * @return array List of searched for tags.
  443. * @access private
  444. */
  445. function _getParsedTags() {
  446. return array('a', 'base', 'title', 'form', 'input', 'button', 'textarea', 'select',
  447. 'option', 'frameset', 'frame', 'label');
  448. }
  449. /**
  450. * The lexer has to skip certain sections such
  451. * as server code, client code and styles.
  452. * @access private
  453. */
  454. function _addSkipping() {
  455. $this->mapHandler('css', 'ignore');
  456. $this->addEntryPattern('<style', 'text', 'css');
  457. $this->addExitPattern('</style>', 'css');
  458. $this->mapHandler('js', 'ignore');
  459. $this->addEntryPattern('<script', 'text', 'js');
  460. $this->addExitPattern('</script>', 'js');
  461. $this->mapHandler('comment', 'ignore');
  462. $this->addEntryPattern('<!--', 'text', 'comment');
  463. $this->addExitPattern('-->', 'comment');
  464. }
  465. /**
  466. * Pattern matches to start and end a tag.
  467. * @param string $tag Name of tag to scan for.
  468. * @access private
  469. */
  470. function _addTag($tag) {
  471. $this->addSpecialPattern("</$tag>", 'text', 'acceptEndToken');
  472. $this->addEntryPattern("<$tag", 'text', 'tag');
  473. }
  474. /**
  475. * Pattern matches to parse the inside of a tag
  476. * including the attributes and their quoting.
  477. * @access private
  478. */
  479. function _addInTagTokens() {
  480. $this->mapHandler('tag', 'acceptStartToken');
  481. $this->addSpecialPattern('\s+', 'tag', 'ignore');
  482. $this->_addAttributeTokens();
  483. $this->addExitPattern('/>', 'tag');
  484. $this->addExitPattern('>', 'tag');
  485. }
  486. /**
  487. * Matches attributes that are either single quoted,
  488. * double quoted or unquoted.
  489. * @access private
  490. */
  491. function _addAttributeTokens() {
  492. $this->mapHandler('dq_attribute', 'acceptAttributeToken');
  493. $this->addEntryPattern('=\s*"', 'tag', 'dq_attribute');
  494. $this->addPattern("\\\\\"", 'dq_attribute');
  495. $this->addExitPattern('"', 'dq_attribute');
  496. $this->mapHandler('sq_attribute', 'acceptAttributeToken');
  497. $this->addEntryPattern("=\s*'", 'tag', 'sq_attribute');
  498. $this->addPattern("\\\\'", 'sq_attribute');
  499. $this->addExitPattern("'", 'sq_attribute');
  500. $this->mapHandler('uq_attribute', 'acceptAttributeToken');
  501. $this->addSpecialPattern('=\s*[^>\s]*', 'tag', 'uq_attribute');
  502. }
  503. }
  504. /**
  505. * Converts HTML tokens into selected SAX events.
  506. * @package SimpleTest
  507. * @subpackage WebTester
  508. */
  509. class SimpleHtmlSaxParser {
  510. var $_lexer;
  511. var $_listener;
  512. var $_tag;
  513. var $_attributes;
  514. var $_current_attribute;
  515. /**
  516. * Sets the listener.
  517. * @param SimpleSaxListener $listener SAX event handler.
  518. * @access public
  519. */
  520. function SimpleHtmlSaxParser(&$listener) {
  521. $this->_listener = &$listener;
  522. $this->_lexer = &$this->createLexer($this);
  523. $this->_tag = '';
  524. $this->_attributes = array();
  525. $this->_current_attribute = '';
  526. }
  527. /**
  528. * Runs the content through the lexer which
  529. * should call back to the acceptors.
  530. * @param string $raw Page text to parse.
  531. * @return boolean False if parse error.
  532. * @access public
  533. */
  534. function parse($raw) {
  535. return $this->_lexer->parse($raw);
  536. }
  537. /**
  538. * Sets up the matching lexer. Starts in 'text' mode.
  539. * @param SimpleSaxParser $parser Event generator, usually $self.
  540. * @return SimpleLexer Lexer suitable for this parser.
  541. * @access public
  542. * @static
  543. */
  544. function &createLexer(&$parser) {
  545. $lexer = new SimpleHtmlLexer($parser);
  546. return $lexer;
  547. }
  548. /**
  549. * Accepts a token from the tag mode. If the
  550. * starting element completes then the element
  551. * is dispatched and the current attributes
  552. * set back to empty. The element or attribute
  553. * name is converted to lower case.
  554. * @param string $token Incoming characters.
  555. * @param integer $event Lexer event type.
  556. * @return boolean False if parse error.
  557. * @access public
  558. */
  559. function acceptStartToken($token, $event) {
  560. if ($event == LEXER_ENTER) {
  561. $this->_tag = strtolower(substr($token, 1));
  562. return true;
  563. }
  564. if ($event == LEXER_EXIT) {
  565. $success = $this->_listener->startElement(
  566. $this->_tag,
  567. $this->_attributes);
  568. $this->_tag = '';
  569. $this->_attributes = array();
  570. return $success;
  571. }
  572. if ($token != '=') {
  573. $this->_current_attribute = strtolower(SimpleHtmlSaxParser::decodeHtml($token));
  574. $this->_attributes[$this->_current_attribute] = '';
  575. }
  576. return true;
  577. }
  578. /**
  579. * Accepts a token from the end tag mode.
  580. * The element name is converted to lower case.
  581. * @param string $token Incoming characters.
  582. * @param integer $event Lexer event type.
  583. * @return boolean False if parse error.
  584. * @access public
  585. */
  586. function acceptEndToken($token, $event) {
  587. if (! preg_match('/<\/(.*)>/', $token, $matches)) {
  588. return false;
  589. }
  590. return $this->_listener->endElement(strtolower($matches[1]));
  591. }
  592. /**
  593. * Part of the tag data.
  594. * @param string $token Incoming characters.
  595. * @param integer $event Lexer event type.
  596. * @return boolean False if parse error.
  597. * @access public
  598. */
  599. function acceptAttributeToken($token, $event) {
  600. if ($this->_current_attribute) {
  601. if ($event == LEXER_UNMATCHED) {
  602. $this->_attributes[$this->_current_attribute] .=
  603. SimpleHtmlSaxParser::decodeHtml($token);
  604. }
  605. if ($event == LEXER_SPECIAL) {
  606. $this->_attributes[$this->_current_attribute] .=
  607. preg_replace('/^=\s*/' , '', SimpleHtmlSaxParser::decodeHtml($token));
  608. }
  609. }
  610. return true;
  611. }
  612. /**
  613. * A character entity.
  614. * @param string $token Incoming characters.
  615. * @param integer $event Lexer event type.
  616. * @return boolean False if parse error.
  617. * @access public
  618. */
  619. function acceptEntityToken($token, $event) {
  620. }
  621. /**
  622. * Character data between tags regarded as
  623. * important.
  624. * @param string $token Incoming characters.
  625. * @param integer $event Lexer event type.
  626. * @return boolean False if parse error.
  627. * @access public
  628. */
  629. function acceptTextToken($token, $event) {
  630. return $this->_listener->addContent($token);
  631. }
  632. /**
  633. * Incoming data to be ignored.
  634. * @param string $token Incoming characters.
  635. * @param integer $event Lexer event type.
  636. * @return boolean False if parse error.
  637. * @access public
  638. */
  639. function ignore($token, $event) {
  640. return true;
  641. }
  642. /**
  643. * Decodes any HTML entities.
  644. * @param string $html Incoming HTML.
  645. * @return string Outgoing plain text.
  646. * @access public
  647. * @static
  648. */
  649. function decodeHtml($html) {
  650. return html_entity_decode($html, ENT_QUOTES);
  651. }
  652. /**
  653. * Turns HTML into text browser visible text. Images
  654. * are converted to their alt text and tags are supressed.
  655. * Entities are converted to their visible representation.
  656. * @param string $html HTML to convert.
  657. * @return string Plain text.
  658. * @access public
  659. * @static
  660. */
  661. function normalise($html) {
  662. $text = preg_replace('|<!--.*?-->|', '', $html);
  663. $text = preg_replace('|<script[^>]*>.*?</script>|', '', $text);
  664. $text = preg_replace('|<img[^>]*alt\s*=\s*"([^"]*)"[^>]*>|', ' \1 ', $text);
  665. $text = preg_replace('|<img[^>]*alt\s*=\s*\'([^\']*)\'[^>]*>|', ' \1 ', $text);
  666. $text = preg_replace('|<img[^>]*alt\s*=\s*([a-zA-Z_]+)[^>]*>|', ' \1 ', $text);
  667. $text = preg_replace('|<[^>]*>|', '', $text);
  668. $text = SimpleHtmlSaxParser::decodeHtml($text);
  669. $text = preg_replace('|\s+|', ' ', $text);
  670. return trim(trim($text), "\xA0"); // TODO: The \xAO is a &nbsp;. Add a test for this.
  671. }
  672. }
  673. /**
  674. * SAX event handler.
  675. * @package SimpleTest
  676. * @subpackage WebTester
  677. * @abstract
  678. */
  679. class SimpleSaxListener {
  680. /**
  681. * Sets the document to write to.
  682. * @access public
  683. */
  684. function SimpleSaxListener() {
  685. }
  686. /**
  687. * Start of element event.
  688. * @param string $name Element name.
  689. * @param hash $attributes Name value pairs.
  690. * Attributes without content
  691. * are marked as true.
  692. * @return boolean False on parse error.
  693. * @access public
  694. */
  695. function startElement($name, $attributes) {
  696. }
  697. /**
  698. * End of element event.
  699. * @param string $name Element name.
  700. * @return boolean False on parse error.
  701. * @access public
  702. */
  703. function endElement($name) {
  704. }
  705. /**
  706. * Unparsed, but relevant data.
  707. * @param string $text May include unparsed tags.
  708. * @return boolean False on parse error.
  709. * @access public
  710. */
  711. function addContent($text) {
  712. }
  713. }
  714. ?>