3 * Source Code Highlighting
\r
5 * The classes in this file are responsible for the dynamic @example, and
\r
6 * <programlisting role="tutorial"> tags output. Using the
\r
7 * WordParser, the phpDocumentor_TutorialHighlightParser
\r
8 * retrieves PHP tokens one by one from the array generated by
\r
9 * {@link WordParser} source retrieval functions
\r
10 * and then highlights them individually.
\r
12 * It accomplishes this highlighting through the assistance of methods in
\r
13 * the output Converter passed to its parse() method, and then returns the
\r
14 * fully highlighted source as a string
\r
16 * phpDocumentor :: automatic documentation generator
\r
18 * PHP versions 4 and 5
\r
20 * Copyright (c) 2003-2007 Gregory Beaver
\r
24 * This library is free software; you can redistribute it
\r
25 * and/or modify it under the terms of the GNU Lesser General
\r
26 * Public License as published by the Free Software Foundation;
\r
27 * either version 2.1 of the License, or (at your option) any
\r
30 * This library is distributed in the hope that it will be useful,
\r
31 * but WITHOUT ANY WARRANTY; without even the implied warranty of
\r
32 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
\r
33 * Lesser General Public License for more details.
\r
35 * You should have received a copy of the GNU Lesser General Public
\r
36 * License along with this library; if not, write to the Free Software
\r
37 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
\r
39 * @category ToolsAndUtilities
\r
40 * @package phpDocumentor
\r
41 * @subpackage Parsers
\r
42 * @author Gregory Beaver <cellog@php.net>
\r
43 * @copyright 2003-2007 Gregory Beaver
\r
44 * @license http://www.opensource.org/licenses/lgpl-license.php LGPL
\r
45 * @version CVS: $Id: TutorialHighlightParser.inc,v 1.7 2007/11/14 01:57:04 ashnazg Exp $
\r
46 * @tutorial tags.example.pkg, tags.filesource.pkg
\r
47 * @link http://www.phpdoc.org
\r
48 * @link http://pear.php.net/PhpDocumentor
\r
50 * @todo CS cleanup - change package to PhpDocumentor
\r
51 * @todo CS cleanup - PHPCS needs to ignore CVS Id length
\r
55 * Highlights source code using {@link parse()}
\r
57 * @category ToolsAndUtilities
\r
58 * @package phpDocumentor
\r
59 * @subpackage Parsers
\r
60 * @author Gregory Beaver <cellog@php.net>
\r
61 * @copyright 2003-2007 Gregory Beaver
\r
62 * @license http://www.opensource.org/licenses/lgpl-license.php LGPL
\r
63 * @version Release: 1.4.1
\r
64 * @link http://www.phpdoc.org
\r
65 * @link http://pear.php.net/PhpDocumentor
\r
66 * @todo CS cleanup - change package to PhpDocumentor
\r
67 * @todo CS cleanup - change classname to PhpDocumentor_*
\r
69 class phpDocumentor_TutorialHighlightParser extends Parser
\r
71 /**#@+ @access private */
\r
73 * Highlighted source is built up in this string
\r
78 * contents of the current source code line as it is parsed
\r
83 * Used to retrieve highlighted tokens
\r
84 * @var Converter a descendant of Converter
\r
88 * Path to file being highlighted, if this is from a @filesource tag
\r
89 * @var false|string full path
\r
91 var $_filesourcepath;
\r
95 var $eventHandlers = array(
\r
96 TUTORIAL_EVENT_NOEVENTS => 'defaultHandler',
\r
97 TUTORIAL_EVENT_ITAG => 'defaultHandler',
\r
98 TUTORIAL_EVENT_ATTRIBUTE => 'attrHandler',
\r
99 TUTORIAL_EVENT_OPENTAG => 'defaultHandler',
\r
100 TUTORIAL_EVENT_CLOSETAG => 'defaultHandler',
\r
101 TUTORIAL_EVENT_ENTITY => 'defaultHandler',
\r
102 TUTORIAL_EVENT_COMMENT => 'defaultHandler',
\r
103 TUTORIAL_EVENT_SINGLEQUOTE => 'defaultHandler',
\r
104 TUTORIAL_EVENT_DOUBLEQUOTE => 'defaultHandler',
\r
109 * advances output to a new line
\r
112 * @uses Converter::SourceLine() encloses {@link $_line} in a
\r
113 * converter-specific format
\r
115 function newLineNum()
\r
117 $this->_line .= $this->_converter->flushHighlightCache();
\r
118 $this->_output .= $this->_converter->SourceLine($this->_pv_curline + 1,
\r
119 $this->_line, $this->_path);
\r
124 * Start the parsing at a certain line number
\r
126 * @param int $num the line number
\r
130 function setLineNum($num)
\r
132 $this->_wp->linenum = $num;
\r
138 * The parse() method is a do...while() loop that retrieves tokens one by
\r
139 * one from the {@link $_event_stack}, and uses the token event array set up
\r
140 * by the class constructor to call event handlers.
\r
142 * The event handlers each process the tokens passed to them, and use the
\r
143 * {@link _addoutput()} method to append the processed tokens to the
\r
144 * {@link $_line} variable. The word parser calls {@link newLineNum()}
\r
145 * every time a line is reached.
\r
147 * In addition, the event handlers use special linking functions
\r
148 * {@link _link()} and its cousins (_classlink(), etc.) to create in-code
\r
149 * hyperlinks to the documentation for source code elements that are in the
\r
152 * @param string $parse_data blah
\r
153 * @param Converter &$converter blah
\r
154 * @param false|string $filesourcepath full path to file with @filesource tag,
\r
155 * if this is a @filesource parse
\r
156 * @param false|integer $linenum starting line number from
\r
157 * {@}source linenum}
\r
159 * @staticvar integer used for recursion limiting if a handler for
\r
160 * an event is not found
\r
162 * @uses setupStates() initialize parser state variables
\r
163 * @uses configWordParser() pass $parse_data to prepare retrieval of tokens
\r
164 * @todo CS cleanup - unable to get function signature below 85char wide
\r
166 function parse($parse_data, &$converter, $filesourcepath = false, $linenum = false)
\r
168 static $endrecur = 0;
\r
170 str_replace(array("\r\n", "\t"), array("\n", ' '), $parse_data);
\r
171 $this->_converter = &$converter;
\r
172 $converter->startHighlight();
\r
173 $this->_path = $filesourcepath;
\r
174 $this->setupStates($parse_data);
\r
176 $this->configWordParser(TUTORIAL_EVENT_NOEVENTS);
\r
177 if ($linenum !== false) {
\r
178 $this->setLineNum($linenum);
\r
180 // initialize variables so E_ALL error_reporting doesn't complain
\r
185 $lpevent = $pevent;
\r
186 $pevent = $this->_event_stack->getEvent();
\r
187 if ($lpevent != $pevent) {
\r
188 $this->_last_pevent = $lpevent;
\r
189 $this->configWordParser($pevent);
\r
191 $this->_wp->setWhitespace(true);
\r
193 $dbg_linenum = $this->_wp->linenum;
\r
194 $dbg_pos = $this->_wp->getPos();
\r
195 $this->_pv_last_word = $word;
\r
196 $this->_pv_curline = $this->_wp->linenum;
\r
197 $word = $this->_wp->getWord();
\r
199 if (PHPDOCUMENTOR_DEBUG == true) {
\r
201 echo "|" . $this->_pv_last_word;
\r
203 echo "PEVENT: " . $this->getParserEventName($pevent) . "\n";
\r
204 echo "LASTPEVENT: " .
\r
205 $this->getParserEventName($this->_last_pevent) . "\n";
\r
206 //DEBUG echo "LINE: " . $this->_line . "\n";
\r
207 //DEBUG echo "OUTPUT: " . $this->_output . "\n";
\r
208 echo $dbg_linenum.'-'.$dbg_pos . ": ";
\r
209 echo '|'.htmlspecialchars($word);
\r
211 echo "-------------------\n\n\n";
\r
214 if (isset($this->eventHandlers[$pevent])) {
\r
215 $handle = $this->eventHandlers[$pevent];
\r
216 $this->$handle($word, $pevent);
\r
218 debug('WARNING: possible error, no handler for event number '
\r
220 if ($endrecur++ == 25) {
\r
221 die("FATAL ERROR, recursion limit reached");
\r
224 } while (!($word === false));
\r
225 if (strlen($this->_line)) {
\r
226 $this->newLineNum();
\r
228 return $this->_output;
\r
234 * All Event Handlers use {@link checkEventPush()} and
\r
235 * {@link checkEventPop()} to set up the event stack and parser state.
\r
237 * @param string|array $word token value
\r
238 * @param integer $pevent parser event from {@link Parser.inc}
\r
244 * Most tokens only need highlighting, and this method handles them
\r
246 * @todo CS cleanup - PHPCS needs to recognize docblock template tags
\r
248 function defaultHandler($word, $pevent)
\r
250 if ($word == "\n") {
\r
251 $this->newLineNum();
\r
254 if ($this->checkEventPush($word, $pevent)) {
\r
255 $this->_wp->backupPos($word);
\r
258 $this->_addoutput($word);
\r
259 $this->checkEventPop($word, $pevent);
\r
263 * Most tokens only need highlighting, and this method handles them
\r
265 * @todo CS cleanup - PHPCS needs to recognize docblock template tags
\r
267 function attrHandler($word, $pevent)
\r
269 if ($word == "\n") {
\r
270 $this->newLineNum();
\r
273 if ($e = $this->checkEventPush($word, $pevent)) {
\r
274 if ($e == TUTORIAL_EVENT_SINGLEQUOTE
\r
275 || $e == TUTORIAL_EVENT_DOUBLEQUOTE
\r
277 $this->_addoutput($word);
\r
281 if ($this->checkEventPop($word, $pevent)) {
\r
282 $this->_wp->backupPos($word);
\r
285 $this->_addoutput($word);
\r
294 * This method adds output to {@link $_line}
\r
296 * If a string with variables like "$test this" is present, then special
\r
297 * handling is used to allow processing of the variable in context.
\r
299 * @param string $word the output to add
\r
300 * @param bool $preformatted whether or not its preformatted
\r
303 * @see _flush_save()
\r
305 function _addoutput($word, $preformatted = false)
\r
308 TUTORIAL_EVENT_ATTRIBUTE => 'attribute',
\r
309 TUTORIAL_EVENT_SINGLEQUOTE => 'attributevalue',
\r
310 TUTORIAL_EVENT_DOUBLEQUOTE => 'attributevalue',
\r
311 TUTORIAL_EVENT_CLOSETAG => 'closetag',
\r
312 TUTORIAL_EVENT_ENTITY => 'entity',
\r
313 TUTORIAL_EVENT_ITAG => 'itag',
\r
314 TUTORIAL_EVENT_OPENTAG => 'opentag',
\r
315 TUTORIAL_EVENT_COMMENT => 'comment',
\r
318 $a = $this->_event_stack->getEvent();
\r
319 if (in_array($a, array_keys($type))) {
\r
321 $this->_converter->highlightTutorialSource($type[$a], $word);
\r
323 $this->_line .= $this->_converter->flushHighlightCache();
\r
324 $this->_line .= $this->_converter->postProcess($word);
\r
330 * Tell the parser's WordParser {@link $wp} to set up tokens to parse words by.
\r
332 * Tokens are word separators. In English, a space or punctuation are
\r
333 * examples of tokens. In PHP, a token can be a ;, a parenthesis, or
\r
334 * even the word "function"
\r
336 * @param integer $e an event number
\r
341 function configWordParser($e)
\r
343 $this->_wp->setSeperator($this->tokens[($e + 100)]);
\r
347 * @param string|array $word token value
\r
348 * @param integer $pevent parser event from {@link Parser.inc}
\r
350 * @return mixed returns false, or the event number
\r
353 * This function checks whether parameter $word is a token
\r
354 * for pushing a new event onto the Event Stack.
\r
356 * @todo CS cleanup - PHPCS needs to recognize docblock template tags
\r
358 function checkEventPush($word, $pevent)
\r
361 if (isset($this->pushEvent[$pevent])) {
\r
362 if (isset($this->pushEvent[$pevent][strtolower($word)])) {
\r
363 $e = $this->pushEvent[$pevent][strtolower($word)];
\r
367 $this->_event_stack->pushEvent($e);
\r
375 * This function checks whether parameter $word is a token
\r
376 * for popping the current event off of the Event Stack.
\r
378 * @todo CS cleanup - PHPCS needs to recognize docblock template tags
\r
380 function checkEventPop($word, $pevent)
\r
382 if (!isset($this->popEvent[$pevent])) {
\r
385 if (in_array(strtolower($word), $this->popEvent[$pevent])) {
\r
386 return $this->_event_stack->popEvent();
\r
394 * Initialize all parser state variables
\r
396 * @param bool|string $parsedata true if we are highlighting an inline {@}source}
\r
397 * tag's output, or the name of class we are going
\r
401 * @uses $_wp sets to a new {@link phpDocumentor_HighlightWordParser}
\r
403 function setupStates($parsedata)
\r
405 $this->_output = '';
\r
409 $this->_wp = new WordParser;
\r
410 $this->_wp->setup($parsedata);
\r
412 $this->_event_stack = new EventStack;
\r
413 $this->_event_stack->popEvent();
\r
414 $this->_event_stack->pushEvent(TUTORIAL_EVENT_NOEVENTS);
\r
416 $this->_pv_linenum = null;
\r
417 $this->_pv_next_word = false;
\r
421 * Initialize the {@link $tokenpushEvent, $wordpushEvent} arrays
\r
423 function phpDocumentor_TutorialHighlightParser()
\r
425 $this->allowableInlineTags =
\r
426 $GLOBALS['_phpDocumentor_inline_tutorial_tags_allowed']
\r
428 $this->inlineTagHandlers =
\r
429 array('*' => 'handleDefaultInlineTag')
\r
431 $this->tokens[STATE_TUTORIAL_NOEVENTS] =
\r
432 array("\n",'{@', '<!--', '</', '<', '&');
\r
433 $this->tokens[STATE_TUTORIAL_ITAG] = array("\n","}");
\r
434 $this->tokens[STATE_TUTORIAL_OPENTAG] = array("\n","\t"," ", '>', '/>');
\r
435 $this->tokens[STATE_TUTORIAL_CLOSETAG] = array("\n",'>');
\r
436 $this->tokens[STATE_TUTORIAL_COMMENT] = array("\n",'-->');
\r
437 $this->tokens[STATE_TUTORIAL_ENTITY] = array("\n",';');
\r
438 $this->tokens[STATE_TUTORIAL_ATTRIBUTE] = array("\n",'"',"'",'>','/>');
\r
439 $this->tokens[STATE_TUTORIAL_DOUBLEQUOTE] = array("\n",'"','&','{@');
\r
440 $this->tokens[STATE_TUTORIAL_SINGLEQUOTE] = array("\n","'",'&','{@');
\r
441 /**************************************************************/
\r
443 $this->pushEvent[TUTORIAL_EVENT_NOEVENTS] = array(
\r
444 "{@" => TUTORIAL_EVENT_ITAG,
\r
445 '<' => TUTORIAL_EVENT_OPENTAG,
\r
446 '</' => TUTORIAL_EVENT_CLOSETAG,
\r
447 '&' => TUTORIAL_EVENT_ENTITY,
\r
448 '<!--' => TUTORIAL_EVENT_COMMENT,
\r
450 /**************************************************************/
\r
452 $this->pushEvent[TUTORIAL_EVENT_OPENTAG] = array(
\r
453 " " => TUTORIAL_EVENT_ATTRIBUTE,
\r
454 "\n" => TUTORIAL_EVENT_ATTRIBUTE,
\r
456 /**************************************************************/
\r
458 $this->pushEvent[TUTORIAL_EVENT_ATTRIBUTE] = array(
\r
459 "'" => TUTORIAL_EVENT_SINGLEQUOTE,
\r
460 '"' => TUTORIAL_EVENT_DOUBLEQUOTE,
\r
462 /**************************************************************/
\r
464 $this->pushEvent[TUTORIAL_EVENT_SINGLEQUOTE] = array(
\r
465 '&' => TUTORIAL_EVENT_ENTITY,
\r
466 '{@' => TUTORIAL_EVENT_ITAG,
\r
468 /**************************************************************/
\r
470 $this->pushEvent[TUTORIAL_EVENT_DOUBLEQUOTE] = array(
\r
471 '&' => TUTORIAL_EVENT_ENTITY,
\r
472 '{@' => TUTORIAL_EVENT_ITAG,
\r
474 /**************************************************************/
\r
476 $this->popEvent[TUTORIAL_EVENT_ENTITY] = array(';');
\r
477 /**************************************************************/
\r
479 $this->popEvent[TUTORIAL_EVENT_SINGLEQUOTE] = array("'");
\r
480 /**************************************************************/
\r
482 $this->popEvent[TUTORIAL_EVENT_DOUBLEQUOTE] = array('"');
\r
483 /**************************************************************/
\r
485 $this->popEvent[TUTORIAL_EVENT_OPENTAG] = array('>', '/>');
\r
486 /**************************************************************/
\r
488 $this->popEvent[TUTORIAL_EVENT_CLOSETAG] = array('>');
\r
489 /**************************************************************/
\r
491 $this->popEvent[TUTORIAL_EVENT_COMMENT] = array('-->');
\r
492 /**************************************************************/
\r
494 $this->popEvent[TUTORIAL_EVENT_ATTRIBUTE] = array('>','/>');
\r
495 /**************************************************************/
\r
497 $this->popEvent[TUTORIAL_EVENT_ITAG] = array('}');
\r
498 /**************************************************************/
\r
502 * searches for a parser event name based on its number
\r
504 * @param int $value the event number
\r
506 * @return string|int the event name, or the original value
\r
508 function getParserEventName ($value)
\r
511 TUTORIAL_EVENT_NOEVENTS => "TUTORIAL_EVENT_NOEVENTS",
\r
512 TUTORIAL_EVENT_ITAG => "TUTORIAL_EVENT_ITAG",
\r
513 TUTORIAL_EVENT_OPENTAG => "TUTORIAL_EVENT_OPENTAG",
\r
514 TUTORIAL_EVENT_ATTRIBUTE => "TUTORIAL_EVENT_ATTRIBUTE",
\r
515 TUTORIAL_EVENT_CLOSETAG => "TUTORIAL_EVENT_CLOSETAG",
\r
516 TUTORIAL_EVENT_ENTITY => "TUTORIAL_EVENT_ENTITY",
\r
517 TUTORIAL_EVENT_COMMENT => "TUTORIAL_EVENT_COMMENT",
\r
518 TUTORIAL_EVENT_SINGLEQUOTE => "TUTORIAL_EVENT_SINGLEQUOTE",
\r
519 TUTORIAL_EVENT_DOUBLEQUOTE => "TUTORIAL_EVENT_DOUBLEQUOTE",
\r
521 if (isset($lookup[$value])) {
\r
522 return $lookup[$value];
\r
533 define("TUTORIAL_EVENT_NOEVENTS", 1);
\r
536 * currently in starting state
\r
538 define("STATE_TUTORIAL_NOEVENTS", 101);
\r
541 * used when an {@}inline tag} is found
\r
543 define("TUTORIAL_EVENT_ITAG", 2);
\r
546 * currently parsing an {@}inline tag}
\r
548 define("STATE_TUTORIAL_ITAG", 102);
\r
551 * used when an open <tag> is found
\r
553 define("TUTORIAL_EVENT_OPENTAG", 3);
\r
556 * currently parsing an open <tag>
\r
558 define("STATE_TUTORIAL_OPENTAG", 103);
\r
561 * used when a <tag attr="attribute"> is found
\r
563 define("TUTORIAL_EVENT_ATTRIBUTE", 4);
\r
566 * currently parsing an open <tag>
\r
568 define("STATE_TUTORIAL_ATTRIBUTE", 104);
\r
571 * used when a close </tag> is found
\r
573 define("TUTORIAL_EVENT_CLOSETAG", 5);
\r
576 * currently parsing a close </tag>
\r
578 define("STATE_TUTORIAL_CLOSETAG", 105);
\r
581 * used when an &entity; is found
\r
583 define("TUTORIAL_EVENT_ENTITY", 6);
\r
586 * currently parsing an &entity;
\r
588 define("STATE_TUTORIAL_ENTITY", 106);
\r
591 * used when a <!-- comment --> is found
\r
593 define("TUTORIAL_EVENT_COMMENT", 7);
\r
596 * currently parsing a <!-- comment -->
\r
598 define("STATE_TUTORIAL_COMMENT", 107);
\r
601 * used when a <!-- comment --> is found
\r
603 define("TUTORIAL_EVENT_SINGLEQUOTE", 8);
\r
606 * currently parsing a <!-- comment -->
\r
608 define("STATE_TUTORIAL_SINGLEQUOTE", 108);
\r
611 * used when a <!-- comment --> is found
\r
613 define("TUTORIAL_EVENT_DOUBLEQUOTE", 9);
\r
616 * currently parsing a <!-- comment -->
\r
618 define("STATE_TUTORIAL_DOUBLEQUOTE", 109);
\r