From 588727c7e2b8954ec3dbde293cf4c4d68b119f9b Mon Sep 17 00:00:00 2001 From: xue <> Date: Mon, 19 Jun 2006 18:38:29 +0000 Subject: Merge from 3.0 branch till 1185. --- .../phpDocumentor/HighlightParser.inc | 2354 ++++++++++++++++++++ 1 file changed, 2354 insertions(+) create mode 100644 buildscripts/PhpDocumentor/phpDocumentor/HighlightParser.inc (limited to 'buildscripts/PhpDocumentor/phpDocumentor/HighlightParser.inc') diff --git a/buildscripts/PhpDocumentor/phpDocumentor/HighlightParser.inc b/buildscripts/PhpDocumentor/phpDocumentor/HighlightParser.inc new file mode 100644 index 00000000..f2a26ee9 --- /dev/null +++ b/buildscripts/PhpDocumentor/phpDocumentor/HighlightParser.inc @@ -0,0 +1,2354 @@ +_parser = &$parser; + $this->data = &$input; + $this->_all = $input; + $this->_sourceline = 0; + $this->pos = 0; + $this->linenum = 0; + } + + /** + * debugging function + * @access private + */ + function printState() + { + $linenum = $this->linenum; + $pos = $this->pos; + if (!isset($this->_all[$this->linenum][$this->pos])) + { + $linenum++; + $pos = 0; + } + $details = ''; + $token = $this->_all[$linenum][$pos]; + if (is_array($token)) + { + $details = token_name($token[0]); + $token = htmlspecialchars($token[1]); + } else $token = htmlspecialchars($token); + debug('Next Token '.$this->linenum.'-'.$this->pos.':'.$details); + var_dump($token); + } + + /** + * Retrieve the position of the next token that will be parsed + * in the internal token array + * @return array format: array(line number, position) + */ + function nextToken() + { + $linenum = $this->linenum; + $pos = $this->pos; + if (!isset($this->_all[$this->linenum][$this->pos])) + { + $linenum++; + $pos = 0; + } + if (!isset($this->_all[$linenum][$pos])) return false; + return array($linenum, $pos); + } + + /** + * Retrieve the next token + * @return array|string either array(PHP token constant, token) or string + * non-specific separator + */ + function getWord() + { + if (!isset($this->_all[$this->linenum][$this->pos])) + { + $this->linenum++; + $this->pos = 0; + if (!isset($this->_all[$this->linenum])) return false; + $this->_parser->newLineNum(); + return $this->getWord(); + } + $word = $this->_all[$this->linenum][$this->pos++]; + return str_replace("\t",' ',$word); + } + + /** + * back the word parser to the previous token as defined by $last_token + * @param array|string token, or output from {@link nextToken()} + * @param boolean if true, backupPos interprets $last_token to be the + * position in the internal token array of the last token + */ + function backupPos($last_token, $is_pos = false) + { + if ($is_pos) + { + $this->linenum = $last_token[0]; + $this->pos = $last_token[1]; + return; + } + if ($last_token === false) return; +//fancy_debug('before',$this->linenum,$this->pos,token_name($this->_all[$this->linenum][$this->pos][0]),htmlentities($this->_all[$this->linenum][$this->pos][1]),$this->_all[$this->linenum]); + do + { + $this->pos--; + if ($this->pos < 0) + { + $this->linenum--; + if ($this->linenum < 0) { + var_dump($last_token); + break; + } + $this->pos = count($this->_all[$this->linenum]) - 1; + } + } while (!$this->tokenEquals($last_token,str_replace("\t", ' ', $this->_all[$this->linenum][$this->pos]))); + //fancy_debug('after',$this->linenum,$this->pos,token_name($this->_all[$this->linenum][$this->pos][0]),htmlentities($this->_all[$this->linenum][$this->pos][1])); + } +} + +/** + * Highlights source code using {@link parse()} + * @package phpDocumentor + * @subpackage Parsers + */ +class phpDocumentor_HighlightParser extends phpDocumentorTParser +{ + /**#@+ @access private */ + /** + * Highlighted source is built up in this string + * @var string + */ + var $_output; + /** + * contents of the current source code line as it is parsed + * @var string + */ + var $_line; + /** + * Used to retrieve highlighted tokens + * @var Converter a descendant of Converter + */ + var $_converter; + /** + * Path to file being highlighted, if this is from a @filesource tag + * @var false|string full path + */ + var $_filesourcepath; + /** + * @var array + */ + var $eventHandlers = array( + PARSER_EVENT_ARRAY => 'defaultHandler', + PARSER_EVENT_CLASS => 'handleClass', + PARSER_EVENT_COMMENT => 'handleComment', + PARSER_EVENT_DOCBLOCK_TEMPLATE => 'handleDocBlockTemplate', + PARSER_EVENT_END_DOCBLOCK_TEMPLATE => 'handleEndDocBlockTemplate', + PARSER_EVENT_LOGICBLOCK => 'handleLogicBlock', + PARSER_EVENT_METHOD_LOGICBLOCK => 'handleMethodLogicBlock', + PARSER_EVENT_NOEVENTS => 'defaultHandler', + PARSER_EVENT_OUTPHP => 'defaultHandler', + PARSER_EVENT_CLASS_MEMBER => 'handleClassMember', + PARSER_EVENT_DEFINE => 'defaultHandler', + PARSER_EVENT_DEFINE_PARAMS => 'defaultHandler', + PARSER_EVENT_DEFINE_PARAMS_PARENTHESIS => 'defaultHandler', + PARSER_EVENT_INCLUDE_PARAMS_PARENTHESIS => 'defaultHandler', + PARSER_EVENT_DOCBLOCK => 'handleDocBlock', + PARSER_EVENT_TAGS => 'handleTags', + PARSER_EVENT_DESC => 'handleDesc', + PARSER_EVENT_DOCKEYWORD => 'handleTag', + PARSER_EVENT_DOCKEYWORD_EMAIL => 'handleDockeywordEmail', + PARSER_EVENT_EOFQUOTE => 'handleQuote', + PARSER_EVENT_FUNCTION => 'handleFunction', + PARSER_EVENT_METHOD => 'handleMethod', + PARSER_EVENT_FUNCTION_PARAMS => 'handleFunctionParams', + PARSER_EVENT_FUNC_GLOBAL => 'handleFuncGlobal', + PARSER_EVENT_INLINE_DOCKEYWORD => 'handleInlineDockeyword', + PARSER_EVENT_INCLUDE => 'defaultHandler', + PARSER_EVENT_INCLUDE_PARAMS => 'defaultHandler', + PARSER_EVENT_QUOTE => 'handleQuote', + PARSER_EVENT_QUOTE_VAR => 'handleQuoteVar', + PARSER_EVENT_PHPCODE => 'handlePhpCode', + PARSER_EVENT_SINGLEQUOTE => 'handleSingleQuote', + PARSER_EVENT_STATIC_VAR => 'defaultHandler', + PARSER_EVENT_STATIC_VAR_VALUE => 'defaultHandler', + PARSER_EVENT_VAR => 'handleVar', + ); + + /** + * event handlers for @tags + * @tutorial tags.pkg + */ + var $tagHandlers = array( + '*' => 'defaultTagHandler', + 'abstract' => 'coreTagHandler', + 'access' => 'coreTagHandler', + 'author' => 'coreTagHandler', + 'category' => 'coreTagHandler', + 'copyright' => 'coreTagHandler', + 'deprecated' => 'coreTagHandler', + 'example' => 'coreTagHandler', + 'filesource' => 'coreTagHandler', + 'final' => 'coreTagHandler', + 'global' => 'globalTagHandler', + 'ignore' => 'coreTagHandler', + 'license' => 'coreTagHandler', + 'link' => 'coreTagHandler', + 'name' => 'coreTagHandler', + 'package' => 'coreTagHandler', + 'param' => 'paramTagHandler', + 'parameter' => 'paramTagHandler', + 'see' => 'coreTagHandler', + 'since' => 'coreTagHandler', + 'subpackage' => 'coreTagHandler', + 'internal' => 'coreTagHandler', + 'return' => 'returnTagHandler', + 'static' => 'coreTagHandler', + 'staticvar' => 'staticvarTagHandler', + 'throws' => 'coreTagHandler', + 'todo' => 'coreTagHandler', + 'tutorial' => 'coreTagHandler', + 'uses' => 'coreTagHandler', + 'var' => 'varTagHandler', + 'version' => 'coreTagHandler', + ); + /**#@-*/ + + /** + * @uses Converter::SourceLine() encloses {@link $_line} in a + * converter-specific format + */ + function newLineNum() + { + if ($this->_pf_no_output_yet) return; + $this->_flush_save(); + $this->_line .= $this->_converter->flushHighlightCache(); + $this->_output .= $this->_converter->SourceLine($this->_wp->linenum, $this->_line, $this->_path); + $this->_line = ''; + } + + /** + * Start the parsing at a certain line number + */ + function setLineNum($num) + { + $this->_wp->linenum = $num; + } + + /** + * Parse a new file + * + * The parse() method is a do...while() loop that retrieves tokens one by + * one from the {@link $_event_stack}, and uses the token event array set up + * by the class constructor to call event handlers. + * + * The event handlers each process the tokens passed to them, and use the + * {@link _addoutput()} method to append the processed tokens to the + * {@link $_line} variable. The word parser calls {@link newLineNum()} + * every time a line is reached. + * + * In addition, the event handlers use special linking functions + * {@link _link()} and its cousins (_classlink(), etc.) to create in-code + * hyperlinks to the documentation for source code elements that are in the + * source code. + * + * @uses setupStates() initialize parser state variables + * @uses configWordParser() pass $parse_data to prepare retrieval of tokens + * @param array $parse_data + * @param Converter $converter + * @param boolean $inlinesourceparse whether this data is from an + * inline {@}source} tag + * @param string|false if a string, it is the name of the class whose + * method we are parsing containing a {@}source} tag + * @param false|integer starting line number from {@}source linenum} + * @param false|string full path to file with @filesource tag, if this + * is a @filesource parse + * @staticvar integer used for recursion limiting if a handler for + * an event is not found + * @return bool + */ + function parse (&$parse_data, &$converter, $inlinesourceparse = false, $class = false, $linenum = false, $filesourcepath = false) + { + if (!tokenizer_ext) + { + if (is_array($parse_data)) + { + $parse_data = join($parse_data,''); + } + $parse_data = explode("\n", $parse_data); + $this->_output = ''; + foreach($parse_data as $linenum => $line) + { + if ($linenum > 0) + { + $this->_output .= $converter->SourceLine($linenum, $line, $filesourcepath); + } + } + return $converter->PreserveWhiteSpace($this->_output); + } + static $endrecur = 0; + $this->_converter = &$converter; + $converter->startHighlight(); + $this->_path = $filesourcepath; + $this->setupStates($inlinesourceparse, $class); + + $this->configWordParser($parse_data); + if ($linenum !== false) $this->setLineNum($linenum); + // initialize variables so E_ALL error_reporting doesn't complain + $pevent = 0; + $word = 0; + + do + { + $lpevent = $pevent; + $pevent = $this->_event_stack->getEvent(); + if ($lpevent != $pevent) + { + $this->_last_pevent = $lpevent; + } + + if ($pevent == PARSER_EVENT_CLASS_MEMBER) + { + $this->_wp->setWhitespace(true); + } else + { + $this->_wp->setWhitespace(false); + } + + if (!is_array($word)) $lw = $word; + if (is_array($word) && $word[0] != T_WHITESPACE) $lw = $word; + $dbg_linenum = $this->_wp->linenum; + $dbg_pos = $this->_wp->getPos(); + $word = $this->_wp->getWord(); + if (is_array($word) && $word[0] == T_WHITESPACE && $pevent != PARSER_EVENT_CLASS_MEMBER) + { +// debug("added ".$this->_wp->linenum.'-'.$this->_wp->pos); + $this->_addoutput($word); + continue; + } else $this->_pv_last_word = $lw; + if ($pevent != PARSER_EVENT_DOCBLOCK) + { + $this->_pv_last_next_word = $this->_pv_next_word; + $this->_pv_next_word = $this->_wp->nextToken(); + } + // in wordparser, have to keep track of lines +// $this->publishEvent(PHPDOCUMENTOR_EVENT_NEWLINENUM, $this->_wp->linenum); + if (0)//PHPDOCUMENTOR_DEBUG == true) + { + echo "LAST: "; + if (is_array($this->_pv_last_word)) + { + echo token_name($this->_pv_last_word[0]). ' => |'.htmlspecialchars($this->_pv_last_word[1]); + } else echo "|" . $this->_pv_last_word; + echo "|\n"; + echo "PEVENT: " . $this->getParserEventName($pevent) . "\n"; + echo "LASTPEVENT: " . $this->getParserEventName($this->_last_pevent) . "\n"; +// echo "LINE: ".$this->_line."\n"; +// echo "OUTPUT: ".$this->_output."\n"; + echo $dbg_linenum.'-'.$dbg_pos . ": "; + if (is_array($word)) + { + echo token_name($word[0]).' => |'.htmlspecialchars($word[1]); + } else echo '|'.htmlspecialchars($word); + echo "|\n"; + $this->_wp->printState(); + echo "NEXT TOKEN: "; + $tok1 = $this->_pv_next_word; + $tok = $this->_wp->_all[$tok1[0]][$tok1[1]]; + if (is_array($tok)) + { + echo token_name($tok[0]). ' => '.$tok1[0].'-'.$tok1[1].'|'.htmlspecialchars($tok[1]); + } else echo "|" . $tok; + echo "|\n"; + echo "-------------------\n\n\n"; + flush(); + } + if (isset($this->eventHandlers[$pevent])) + { + $handle = $this->eventHandlers[$pevent]; + $this->$handle($word, $pevent); + } else + { + debug('WARNING: possible error, no handler for event number '.$pevent); + if ($endrecur++ == 25) + { + die("FATAL ERROR, recursion limit reached"); + } + } + } while (!($word === false)); + if (strlen($this->_line)) $this->newLineNum(); + return $this->_output; + } + + /**#@+ + * Event Handlers + * + * All Event Handlers use {@link checkEventPush()} and + * {@link checkEventPop()} to set up the event stack and parser state. + * @access private + * @param string|array token value + * @param integer parser event from {@link Parser.inc} + */ + /** + * Most tokens only need highlighting, and this method handles them + */ + function defaultHandler($word, $pevent) + { + $this->_addoutput($word); + if ($this->checkEventPush($word, $pevent)) return; + $this->checkEventPop($word, $pevent); + } + + /** + * Handles global declarations in a function, like: + * + * + * function foobar() + * { + * global $_phpDocumentor_setting; + * } + * + * @uses _globallink() instead of _addoutput(), to link to global variables + * if they are used in a function + */ + function handleFuncGlobal($word, $pevent) + { + if ($this->checkEventPush($word, $pevent)) return; + $this->_globallink($word); + $this->checkEventPop($word, $pevent); + } + + /** + * Handles strings in quotation marks and heredoc + * + * Special handling is needed for strings that contain variables like: + * + * $a = "$test string" + * + * The tokenizer parses out tokens '"',array(T_VARIABLE,'$test'),' string', + * and '"'. Since it is possible to have $this->classvar in a string, + * we save a variable name just in case the next token is -> to allow linking + * to class members. Otherwise, the string is simply highlighted. + * + * constant strings (with no $variables in them) are passed as a single + * entity, and so will be saved in the last token parsed. This means the + * event handler must tell the word parser to re-retrieve the current token + * so that the correct event handler can process it. + */ + function handleQuote($word, $pevent) + { + if ($this->_pf_inmethod && is_array($word) && $word[0] == T_VARIABLE) $this->_pv_lastvar = $word; + if ($this->checkEventPush($word, $pevent)) + { + $this->_addoutput($word); + return; + } + if ($this->_pf_quote_active && + (($this->_pv_last_word == '"' && $this->_last_pevent != PARSER_EVENT_QUOTE) || + (is_array($this->_pv_last_word) && $this->_pv_last_word[0] == T_END_HEREDOC && + $this->_last_pevent != PARSER_EVENT_EOFQUOTE))) + { + $this->_pf_quote_active = false; + $this->_wp->backupPos($word); + $this->_event_stack->popEvent(); + return; + } + if (!$this->_pf_quote_active && + (($this->_pv_last_word == '"' && $this->_last_pevent != PARSER_EVENT_QUOTE) || + (is_array($this->_pv_last_word) && $this->_pv_last_word[0] == T_END_HEREDOC && + $this->_last_pevent != PARSER_EVENT_EOFQUOTE))) + { + if (is_array($word) && $word[0] == T_VARIABLE) $this->_pv_lastvar = $word; + $this->_pf_quote_active = true; + $this->_save_highlight_state = $this->_converter->getHighlightState(); + $this->_converter->startHighlight(); + $this->_addoutput($word); + $this->checkEventPop($word, $pevent); + return; + } elseif (is_array($this->_pv_last_word) && $this->_pv_last_word[0] == T_CONSTANT_ENCAPSED_STRING) + { +// $this->_pv_quote_data = $this->_pv_last_word[1]; + $this->_event_stack->popEvent(); + $this->_wp->backupPos($word); + return; + } + if ($this->checkEventPop($word, $pevent)) + { + $this->_pf_quote_active = false; + } + $this->_addoutput($word); + } + + /** + * Handles {$variable} within a "quote" + * + * This is a simple handler, for a very complex + * array of legal syntax. It is legal to nest control structures + * inside the {}, and other weird stuff. + */ + function handleQuoteVar($word, $pevent) + { + if ($this->checkEventPop($word, $pevent)) + { + $this->_pf_quote_active = true; + $this->_addoutput($word); + return; + } + if ($this->_pf_inmethod && is_array($word) && $word[0] == T_VARIABLE) $this->_pv_lastvar = $word; + if ($this->checkEventPush($word, $pevent)) + { + $this->_pf_quote_active = false; + if (is_string($word) && ($word == '{' || $word == '"' || $word == "'")) + { + $this->_pf_quote_active = true; + $this->_pv_lastvar = false; + } + } + $this->_addoutput($word); + } + + /** + * Handles define() statements + * + * The only thing this handler cares about is retrieving the name of the + * define variable, and the end of the define statement, so after the name + * is found, it simply makes sure parentheses are matched as in this case: + * + * + * define("test",array("hello",6 => 4, 5 => array('there'))); + * + * + * This handler and the DEFINE_PARAMS_PARENTHESIS handler (which is just + * {@link defaultHandler()} in this version, as nothing fancy is needed) + * work together to ensure proper parenthesis matching. + * + * If the define variable is documented, a link will be created to its + * documentation using the Converter passed. + */ + function handleDefine($word, $pevent) + { + static $token_save; + if (!isset($token_save)) $token_save = array(); + $e = $this->checkEventPush( $word, $pevent); + if ($e && $e != PARSER_EVENT_DEFINE_PARAMS_PARENTHESIS) return; + + if(!isset($this->_pv_define_params_data)) $this->_pv_define_params_data = ''; + + if ($this->checkEventPop($word,$pevent)) + { + unset($token_save); + $this->_addoutput($word); + } + if ($this->_pf_definename_isset) + { + $this->_addoutput($word); + } else + { + if ($word != ",") + { + $token_save[] = $word; + if (is_array($word)) $word = $word[1]; + $this->_pv_define_params_data .= $word; + } else + { + if (substr($this->_pv_define_params_data,0,1) == + substr($this->_pv_define_params_data,strlen($this->_pv_define_params_data) - 1) && + in_array(substr($this->_pv_define_params_data,0,1),array('"',"'"))) + { // remove leading and ending quotation marks if there are only two + $a = substr($this->_pv_define_params_data,0,1); + $b = substr($this->_pv_define_params_data,1,strlen($this->_pv_define_params_data) - 2); + if (strpos($b,$a) === false) + { + $this->_pv_define_params_data = $b; + } + } + $this->_pf_definename_isset = true; + $link = $this->_converter->getLink($this->_pv_define_params_data); + foreach ($token_save as $token) + { + if (is_object($link)) + { + if (is_array($token)) $token = $token[1]; + $this->_addoutput($this->_converter->returnSee($link, $token)); + } else + { + $this->_addoutput($save, $token); + } + } + $this->_pv_define_params_data = ''; + } + } + } + + /** + * Handles normal global code. Special consideration is taken for DocBlocks + * as they need to retrieve the whole DocBlock before doing any output, so + * the parser flag {@link $_pf_no_output_yet} is set to tell + * {@link _addoutput()} not to spit anything out yet. + * @uses _link() make any global code that is a documentable element link + * to the php manual or its documentation + */ + function handlePhpCode($word, $pevent) + { + $test = $this->checkEventPush($word, $pevent); + if ($test == PARSER_EVENT_DOCBLOCK || $test == PARSER_EVENT_COMMENT) + { + $this->_pf_no_output_yet = true; + $this->_pv_saveline = $this->_wp->linenum + 1; + return; + } + if (is_array($word) && $word[0] == T_DOUBLE_COLON) $this->_pf_colon_colon = true; + if (!$this->_pf_colon_colon && is_array($word) && $word[0] == T_STRING) $this->_pv_last_string = $word; + $this->_link($word); + $this->checkEventPop($word, $pevent); + } + + /** + * Handle the function declaration header + * + * This handler only sees the "function name" portion of the function + * declaration. Handling of the function parameters is by + * {@link handleFunctionParams()}, and the function body is handled by + * {@link handleLogicBlock()} + */ + function handleFunction($word, $pevent) + { + if ($this->checkEventPush($word, $pevent)) + { + $this->_addoutput($word); + return; + } + if ($this->checkEventPop($word, $pevent)) return; + $this->_link($word); + } + + /** + * Handle the method declaration header + * + * This handler only sees the "function name" portion of the method + * declaration. Handling of the method parameters is by + * {@link handleFunctionParams()}, and the method body is handled by + * {@link handleMethodLogicBlock()} + */ + function handleMethod($word, $pevent) + { + if ($this->checkEventPush($word, $pevent)) + { + $this->_addoutput($word); + return; + } + if ($this->checkEventPop($word, $pevent)) { + if ($word == ';') { + $this->_addoutput($word); + } + return; + } + $this->_methodlink($word); + } + + /** + * Handler for the stuff between ( and ) in a function declaration + * + * + * function handles($only,$these,$parameters){...} + * + */ + function handleFunctionParams($word, $pevent) + { + if ($this->checkEventPush($word, $pevent)) + { + $this->_addoutput($word); + return; + } + $this->_addoutput($word); + $this->checkEventPop($word, $pevent); + } + + /** + * Handler for function body. + * + * The function body is checked for php functions, documented constants, + * functions, and indirectly for global statements. It hyperlinks to the + * documentation for detected elements is created. Everything else is + * highlighted normally. + */ + function handleLogicBlock($word, $pevent) + { + if ($this->checkEventPush($word, $pevent)) + { + $this->_addoutput($word); + return; + } + if (is_array($word) && $word[0] == T_DOUBLE_COLON) $this->_pf_colon_colon = true; + if (!$this->_pf_colon_colon && is_array($word) && $word[0] == T_STRING) $this->_pv_last_string = $word; + $this->_link($word); + if ($this->checkEventPop($word,$pevent)) + { + $e = $this->_event_stack->popEvent(); + $this->_event_stack->pushEvent($e); + if ($e == PARSER_EVENT_FUNCTION) + { + $this->_wp->backupPos($word); + } + } + } + + /** + * Handler for method body. + * + * Like functions, the method body is checked for php functions, documented + * constants, functions, and indirectly for global statements. It also + * checks for "$this->XXXX" where XXXX is a class variable or method, and + * links to the documentation for detected elements is created. Everything + * else is highlighted normally. + */ + function handleMethodLogicBlock($word, $pevent) + { + if (isset($this->_pv_prev_var_type)) + { +// debug('prevtype is set'); + if (!is_array($word)) unset($this->_pv_prev_var_type); + else + { + if ($word[0] != T_WHITESPACE && $word[0] != T_STRING && $word[0] != T_OBJECT_OPERATOR) + { +// fancy_debug('unset',$word); + unset($this->_pv_prev_var_type); + } + } + } + $this->_pf_inmethod = true; + if ($e = $this->checkEventPush($word, $pevent)) + { + $this->_addoutput($word); + if ($e == PARSER_EVENT_CLASS_MEMBER) { + $this->_pf_no_output_yet = true; + } + return; + } + if (is_array($word) && $word[0] == T_DOUBLE_COLON) $this->_pf_colon_colon = true; + if (!$this->_pf_colon_colon && is_array($word) && $word[0] == T_STRING) $this->_pv_last_string = $word; + if (is_array($word) && $word[0] == T_VARIABLE) $this->_pv_lastvar = $word; + $this->_link($word); + if ($this->checkEventPop($word,$pevent)) + { + $this->_pf_inmethod = false; + $e = $this->_event_stack->popEvent(); + $this->_event_stack->pushEvent($e); + if ($e == PARSER_EVENT_METHOD) + { + $this->_wp->backupPos($word); + } + } + } + + /** + * Handles $obj->classmember in a method body + * + * This handler is responsible for linking to the documentation of a + * class member when it is used directly in a method body. + * + * There are two methods of determining whether to link: + * - $this->member + * - $this->member->submember + * + * The first case is handled by the $_pv_lastvar variable, and the + * second case is handled by the $_pv_prev_var_type variable. $_pv_lastvar + * is always set to the value of the last T_VARIABLE token, if and only if + * no text has occurred between the variable and a T_OBJECT_OPERATOR token + * "->". handleClassMember will only link if the last variable encountered + * was $this. + * + * When $this->variable is encountered, the variable is looked up to see + * if it can be found, and if so, the contents of its @var tag are processed + * to see if the member variable is defined to have 1 and only 1 class. + * If so, the $_pv_prev_var_type variable is set to this classname. When + * submember is processed, the HighlightParser checks to see if + * $_pv_prev_var_type::submember() or $_pv_prev_var_type::$submember exists, + * and if it does, it is linked to. + */ + function handleClassMember($word, $pevent) + { + if (!isset($this->_pv_lastvar) && !isset($this->_pv_prev_var_type)) + { +// fancy_debug('returned from',$word,$this->_pv_prev_var_type); + $this->_pf_no_output_yet = false; + $this->_event_stack->popEvent(); + return $this->defaultHandler($word, $pevent); + } + if (isset($this->_pv_cm_name)) + { + $this->_pf_obj_op = false; + $name = $this->_pv_cm_name; + unset($this->_pv_cm_name); +// debug('unset pvcmname'); + $this->_event_stack->popEvent(); + // control variable for _pv_prev_var_type + $setnow = false; + if ((isset($this->_pv_lastvar) && $this->_pv_lastvar[1] == '$this') + || isset($this->_pv_prev_var_type)) + { + if (is_array($word) && $word[0] == T_WHITESPACE) + { + // preserve value of _pv_prev_var_type + $setnow = true; + $save = $this->_wp->nextToken(); + $temp = $this->_wp->getWord(); + $this->_wp->backupPos($save, true); + } + if ((is_string($word) && $word == '(') || + (isset($temp) && is_string($temp) && $temp == '(')) + { // it's a function + $this->_pf_no_output_yet = false; + $this->_methodlink($name); + unset($this->_pv_prev_var_type); + } else + { // it's a variable +// fancy_debug('name is ',$name); + $this->_pf_no_output_yet = false; + $this->_varlink($name, true); + $templink = $this->_converter->getLink('object '.$this->_pv_class); + $class = false; + if (is_object($templink)) { + $class = $this->_converter->classes->getClass($templink->name, $templink->path); + } + if ($class) + { + $varname = $name; + if (is_array($varname)) $varname = $name[1]; + if ($varname{0} != '$') $varname = '$'.$varname; + $var = $class->getVar($this->_converter, $varname); + + if (is_object($var) && $var->docblock->var) + $type = $var->docblock->var->returnType; + if (isset($type)) + { + if (strpos($type, 'object') === false) + $type = 'object '.$type; + $type = $this->_converter->getLink($type); + if (phpDocumentor_get_class($type) == 'classlink') + { // the variable's type is a class, save it for future -> +// fancy_debug('set prev_var_type!',$type->name); + $setnow = true; + $this->_pv_prev_var_type = $type->name; + } else unset($this->_pv_prev_var_type); + } else unset($this->_pv_prev_var_type); + } else unset($this->_pv_prev_var_type); + } + } else { + $this->_pf_no_output_yet = false; + // this does NewLinenum if necessary + $this->_wp->backupPos($word); + $this->_wp->getWord(); + $this->_addoutput($name); + } + if (!$setnow) + { +// debug('unset prevtype, no setnow'); + unset($this->_pv_prev_var_type); + } + unset($this->_pv_lastvar); + $this->_pf_no_output_yet = false; + // this does NewLinenum if necessary + $this->_wp->backupPos($word); + $this->_wp->getWord(); + if ($word[0] == T_OBJECT_OPERATOR) + $this->_wp->backupPos($word); + else + $this->_addoutput($word); + return; + } + if (!$this->_pf_obj_op && is_array($this->_pv_last_word) && $this->_pv_last_word[0] == T_OBJECT_OPERATOR) + { + if ((isset($this->_pv_lastvar) && $this->_pv_lastvar[1] == '$this') || isset($this->_pv_prev_var_type)) + { + $this->_pf_obj_op = true; + } else + { + $this->_pf_no_output_yet = false; + // this does NewLinenum if necessary + $this->_wp->backupPos($word); + $this->_wp->getWord(); + $this->_addoutput($word); + $this->_event_stack->popEvent(); + } + } + if (is_array($word) && $word == T_WHITESPACE) + { + $this->_pf_no_output_yet = false; + // this does NewLinenum if necessary + $this->_wp->backupPos($word); + $this->_wp->getWord(); + $this->_addoutput($word); + return; + } + if ($this->_pf_obj_op) + { + if (!(is_array($word) && ($word[0] == T_STRING || $word[0] == T_WHITESPACE))) + { + unset($this->_pv_lastvar); +// debug('unset lastvar'); + $this->_event_stack->popEvent(); + $this->_pf_no_output_yet = false; + // this does NewLinenum if necessary + $this->_wp->backupPos($word); + $this->_wp->getWord(); + $this->_addoutput($word); + return; + } + if ($word[0] == T_STRING) + { +// fancy_debug('set pvcmname to',$word); + $this->_pv_cm_name = $word; + } else { + $this->_pf_no_output_yet = false; + // this does NewLinenum if necessary + $this->_wp->backupPos($word); + $this->_wp->getWord(); + $this->_addoutput($word); + } + } + } + + /** + * Handles comments + * + * Comments are almost always single-line tokens, and so will be + * in the last word. This handler checks to see if the current token + * is in fact a comment, and if it isn't, it backs up and returns control + * to the parent event handler with that word. + */ + function handleComment($word, $pevent) + { + $w = $this->_pv_last_word; + // don't perform this check if this is a normal comment. Docblocks + // have the _pf_no_output_yet variable set to true + if ($this->_pf_no_output_yet && + is_array($w) && (in_array($w[0], array(T_COMMENT, T_DOC_COMMENT)) && strpos($w[1],'/**') === 0)) { + $this->_event_stack->popEvent(); + $this->_event_stack->pushEvent(PARSER_EVENT_DOCBLOCK); + return $this->handleDocBlock($word, PARSER_EVENT_DOCBLOCK); + } + if ($this->_pf_no_output_yet) { + $flag = 1; + $this->_pf_no_output_yet = false; + $this->_addoutput($this->_pv_last_word); + } + if (!is_array($word) || !in_array($word[0], array(T_COMMENT, T_DOC_COMMENT)) || + (in_array($word[0], array(T_COMMENT, T_DOC_COMMENT)) && strpos($word[1],'/**') === 0)) + { + $this->_event_stack->popEvent(); + if (strpos($this->_pv_last_word[1], "\n") !== false) + { +// $this->_wp->linenum++; +// $this->newLineNum(); + } + $this->_wp->backupPos($this->_pv_last_word); + $this->_wp->getWord(); +// var_dump($this->_wp->nextToken()); + return; + } elseif (isset($flag)) { + $this->newLineNum(); + } + $this->_addoutput($word); + $this->checkEventPop($word, $pevent); + if (strpos($word[1], '*/') === strlen($word[1]) - 2) { + $this->_event_stack->popEvent(); + } + } + + /** + * Handle class declarations + * + * Handles the initial declaration line: + * + * class X + * + * or + * + * class X extends Y implements I + * + * @uses _classlink() to link to documentation for X and for Y class in + * "class X extends Y" + */ + function handleClass($word, $pevent) + { + $this->_pf_in_class = true; + $a = $this->checkEventPush( $word, $pevent); + + if (!isset($this->_pv_class) && is_array($word) && $word[0] == T_STRING) + { + $this->_pv_class = $this->_converter->class = $word[1]; + $this->_classlink($word); + return; + } + + if (is_array($word) && in_array($word[0], array(T_PRIVATE, T_PROTECTED, T_PUBLIC))) { + $starttok = $this->_wp->nextToken(); + $test = array(T_WHITESPACE); + while ($test && $test[0] == T_WHITESPACE) { + $tok = $this->_wp->nextToken(); + $test = $this->_wp->getWord(); + } // while + + if (is_array($test) && $test[0] == T_VARIABLE) { + $this->_wp->backupPos($tok, true); + return; + } + $this->_wp->backupPos($starttok, true); + } + + if (@in_array($this->_pv_last_word[0], array(T_PRIVATE, T_PROTECTED, T_PUBLIC))) { + if (is_array($word) && $word[0] == T_VARIABLE) { + $this->_wp->backupPos($this->_pv_last_word); + $this->_event_stack->pushEvent(PARSER_EVENT_VAR); + return; + } + } + + if ($this->_pf_extends_found && is_array($word) && $word[0] == T_STRING) + { + $this->_classlink($word); + return; + } + if (is_array($word) && $word[0] == T_EXTENDS) $this->_pf_extends_found = true; + if ($a == PARSER_EVENT_DOCBLOCK) + { + $this->_pf_no_output_yet = true; + $this->_pv_saveline = $this->_wp->linenum + 1; + return; + } + $this->_addoutput($word); + if ($this->checkEventPop($word,$pevent)) + { + $this->_pf_in_class = false; + unset($this->_pv_class); + } + } + + /** + * Handles class variable declaration + * + * + * class X + * { + * var $Y; + * } + * + * @uses _varlink() make a link to $Y documentation in class variable + * declaration "var $Y;" + */ + function handleVar($word, $pevent) + { + if ($this->checkEventPush($word, $pevent)) + { + $this->_addoutput($word); + return; + } + if (is_array($word) && $word[0] == T_VARIABLE) + { + return $this->_varlink($word); + } + $this->_addoutput($word); + $this->checkEventPop($word, $pevent); + } + + /** + * This handler is responsible for highlighting DocBlocks + * + * handleDocBlock determines whether the docblock is normal or a template, + * and gathers all the lines of the docblock together before doing any + * processing + * + * As it is not possible to distinguish any comment token from a docblock + * token, this handler is also called for comments, and will pass control + * to {@link handleComment()} if the comment is not a DocBlock + * @uses commonDocBlock() once all lines of the DocBlock have been retrieved + */ + function handleDocBlock($word, $pevent) + { + if (!($this->_pf_docblock || $this->_pf_docblock_template)) + { + if (strpos($this->_pv_last_word[1],'/**') !== 0) + { // not a docblock + $this->_wp->backupPos($this->_pv_last_word); + $this->_event_stack->popEvent(); + $this->_event_stack->pushEvent(PARSER_EVENT_COMMENT); + $this->_pf_no_output_yet = false; + return; + } else + { + $this->_pf_no_output_yet = true; + $this->_pv_db_lines = array(); + } + } + $last_word = $this->_pv_last_word[1]; + $dtype = '_pv_docblock'; + if ($last_word == '/**#@-*/') + { // stop using docblock template + $this->_pf_no_output_yet = false; + $this->_addDocBlockoutput('closetemplate', $last_word); + if ($this->_pv_next_word !== false) { + $this->_wp->backupPos($this->_pv_next_word,true); + } + $this->_event_stack->popEvent(); + return; + } + if (!($this->_pf_docblock || $this->_pf_docblock_template)) + { + $this->_pv_db_lines = array(); + if (strpos($last_word,'/**#@+') === 0) + { // docblock template definition + $this->_pf_docblock_template = true; + } else + { + $this->_pf_docblock = true; + } + $this->_pv_db_lines[] = $last_word; + if (strpos($last_word,'*/') !== false) + { + $this->commonDocBlock(); + return; + } + $this->_pv_db_lines[] = $word[1]; + if (strpos($word[1],'*/') !== false) + { + $this->commonDocBlock(); + } + } else + { + $this->_pv_db_lines[] = $word[1]; + } + if (($this->_pf_docblock || $this->_pf_docblock_template) && (strpos($word[1],'*/') !== false)) + { + $this->commonDocBlock(); + } + } + /**#@-*/ + /** + * This continuation of handleDocBlock splits DocBlock comments up into + * phpDocumentor tokens. It highlights DocBlock templates in a different + * manner from regular DocBlocks, recognizes inline tags, regular tags, + * and distinguishes between standard core tags and other tags, and + * recognizes parameters to tags like @var. + * + * the type in "@var type description" will be highlighted as a php type, + * and the var in "@param type $var description" will be highlighted as a + * php variable. + * @uses handleDesc() highlight inline tags in the description + * @uses handleTags() highlight all tags + * @access private + */ + function commonDocBlock() + { + $this->_event_stack->popEvent(); + $lines = $this->_pv_db_lines; + $go = count($this->_pv_db_lines); + for($i=0;$i<$go;$i++) + { + if (substr(trim($lines[$i]),0,2) == '*/' || substr(trim($lines[$i]),0,1) != '*' && substr(trim($lines[$i]),0,3) != '/**') + { + $lines[$i] = array($lines[$i],false); + } elseif (substr(trim($lines[$i]),0,3) == '/**') + { + $linesi = array(); + $linesi[1] = substr(trim($lines[$i]),3); // remove leading "/**" + if (empty($linesi[1])) + $linesi[0] = $lines[$i]; + else + $linesi[0] = substr($lines[$i],0,strpos($lines[$i],$linesi[1])); + $lines[$i] = $linesi; + } else + { + $linesi = array(); + $linesi[1] = substr(trim($lines[$i]),1); // remove leading "* " + if (empty($linesi[1])) + $linesi[0] = $lines[$i]; + else + $linesi[0] = substr($lines[$i],0,strpos($lines[$i],$linesi[1])); + $lines[$i] = $linesi; + } + } + for($i = 0;$i_pf_no_output_yet = false; + $save = $this->_wp->linenum; + $this->_wp->linenum = $this->_pv_saveline; + $this->handleDesc($desc); + $this->handleTags($tags); + $this->_pv_db_lines = array(); + $this->_wp->linenum = $save; + if (strpos($this->_pv_last_word[1],'*/') !== false) + { + $this->_wp->backupPos($this->_pv_next_word,true); + } + $this->_pf_docblock = $this->_pf_docblock_template = false; + } + + /** + * Handle the description area of a DocBlock + * + * This method simply finds inline tags and highlights them + * separately from the rest of the description. + * @uses getInlineTags() + * @access private + */ + function handleDesc($desc) + { + $dbtype = 'docblock'; + $dbtype .= ($this->_pf_docblock ? '' : 'template'); + foreach($desc as $line) + { + $this->getInlineTags($line[0].$line[1]); + if (strpos($line[0],'*/') === false) + { + $this->newLineNum(); + $this->_wp->linenum++; + } + } + if ($this->_pf_internal) + { + $this->_pf_internal = false; + } + } + + /** + * Handle phpDocumentor tags in a DocBlock + * + * This method uses the {@link $tagHandlers} array to determine which + * method will handle tags found in the docblock, and passes the data to + * the individual handlers one by one + * @access private + */ + function handleTags($tags) + { + $newtags = array(); + $curtag = array(); + for($i=0;$i < count($tags);$i++) + { + $tagsi = trim($tags[$i][1]); + if (substr($tagsi,0,1) == '@' && substr($tagsi,0,2) != '@ ') + { // start a new tag + $tags[$i][1] = array(substr($tags[$i][1],0,strpos($tags[$i][1],$tagsi)),$tagsi); + if (!empty($curtag)) + { + $newtags[] = $curtag; + $curtag = array(); + } + $curtag[] = $tags[$i]; + } else $curtag[] = $tags[$i]; + } + if (!empty($curtag)) $newtags[] = $curtag; + foreach($newtags as $tag) + { + foreach($tag as $i => $t) + { + if ($t[1] === false) continue; + if (is_array($t[1])) + { + $tag[$i][1][1] = explode(" ",str_replace("\t",' ',$t[1][1])); + $x = $tag[$i][1][1]; + } + } + $tagname = substr(array_shift($x),1); + $restoftag = $tag; + if (isset($this->tagHandlers[$tagname])) + $handle = $this->tagHandlers[$tagname]; + else + $handle = $this->tagHandlers['*']; + $this->$handle($tagname,$restoftag); + } + } + + /** + * This handler recognizes all {@}inline} tags + * + * Normal inline tags are simply highlighted. the {@}internal}} inline + * tag {@tutorial tags.inlineinternal.pkg} is highlighted differently + * to distinguish it from other inline tags. + * @access private + */ + function getInlineTags($value, $endinternal = false) + { + if (!$value) return; + if ($this->_pf_internal && !$endinternal) + { + if (strpos($value,'}}') !== false) + { + $x = strrpos($value,'}}'); + // add the rest of internal + $this->getInlineTags(substr($value,0,$x + 3), true); + // strip internal from value + $value = substr($value,strrpos($value,'}}') + 1); + // turn off internal + $this->_pf_internal = false; + } + } + if (!$value) return; + $dbtype = 'docblock'; + $dbtype .= ($this->_pf_docblock ? '' : 'template'); + $save = $value; + $value = explode('{@',$value); + $newval = array(); + // everything before the first {@ is normal text + $this->_addDocBlockoutput($dbtype, $value[0]); + for($i=1;$i_addDocBlockoutput($dbtype, '{@}'.substr($value[$i],1)); + } else + { + $save = $value[$i]; + $value[$i] = str_replace("\t"," ",$value[$i]); + $value[$i] = explode(" ",$value[$i]); + $word = array_shift($value[$i]); + $val = join(' ',$value[$i]); + if ($word == 'internal') + { + $this->_pf_internal = true; + $this->_addDocBlockoutput($dbtype, '{@internal '); + $value[$i] = substr($save,strlen('internal') + 1); + // strip internal and cycle as if it were normal text. + $this->_addDocBlockoutput($dbtype, $value[$i]); + continue; + } + if (in_array(str_replace('}','',$word),$this->allowableInlineTags)) + { + if (strpos($word,'}')) + { + $word = str_replace('}','',$word); + $val = '} '.$val; + } + $val = explode('}',$val); + if (count($val) == 1) + { +// addError(PDERROR_UNTERMINATED_INLINE_TAG,$word,'',$save); + } + $rest = $val; + $val = array_shift($rest); + if ($endinternal) + $rest = join('}',$rest); + else + $rest = join(' ',$rest); + if (isset($this->inlineTagHandlers[$word])) + $handle = $this->inlineTagHandlers[$word]; + else + $handle = $this->inlineTagHandlers['*']; + $this->$handle($word,$val); + $this->_addDocBlockoutput($dbtype, $rest); + } else + { + $val = $word.' '.$val; + $this->_addDocBlockoutput($dbtype, '{@'.$val); + } + } + } + } + + + /** + * Handles all inline tags + * @access private + */ + function handleDefaultInlineTag($name, $value) + { + $this->_addDocBlockoutput('inlinetag','{@'.$name.' '.$value.'}'); + } + + /**#@+ + * phpDocumentor DocBlock tag handlers + * @access private + * @param string tag name + * @param array array of lines contained in the tag description + */ + /** + * Handle normal tags + * + * This handler adds to outpu all comment information before the tag begins + * as in " * " before "@todo" in " * @todo" + * + * Then, it highlights the tag as a regular or coretag based on $coretag. + * Finally, it uses getInlineTags to highlight the description + * @uses getInlineTags() highlight a tag description + * @param boolean whether this tag is a core tag or not + */ + function defaultTagHandler($name, $value, $coretag = false) + { + $dbtype = 'docblock'; + $dbtype .= ($this->_pf_docblock ? '' : 'template'); + foreach($value as $line) + { + $this->_addDocBlockoutput($dbtype, $line[0]); + if ($line[1] === false) + { + if (trim($line[0]) != '*/') + { + $this->newLineNum(); + $this->_wp->linenum++; + } + continue; + } + $this->_addDocBlockoutput($dbtype, $line[1][0]); + $stored = ''; + if (is_array($line[1][1])) + { + foreach($line[1][1] as $i => $tpart) + { + if ($tpart == '@'.$name && $i == 0) + { + $tagname = 'tag'; + if ($coretag) $tagname = 'coretag'; + $this->_addDocBlockoutput($tagname,'@'.$name); + continue; + } + $stored .= ' '.$tpart; + } + } else $stored = $line[1]; + $this->getInlineTags($stored); + if (strpos($stored,'*/') === false) + { + $this->newLineNum(); + $this->_wp->linenum++; + } + } + } + + /** + * @see defaultTagHandler() + */ + function coreTagHandler($name, $value) + { + return $this->defaultTagHandler($name, $value, true); + } + + /** + * Handles @global + * + * This handler works like {@link defaultTagHandler()} except it highlights + * the type and variable (if present) in "@global type $variable" or + * "@global type description" + */ + function globalTagHandler($name, $value) + { + $this->paramTagHandler($name, $value); + } + + /** + * Handles @param + * + * This handler works like {@link defaultTagHandler()} except it highlights + * the type and variable (if present) in "@param type $variable description" + * or "@param type description" + * @param boolean private parameter, checks for $var or not + */ + function paramTagHandler($name, $value, $checkforvar = true) + { + $dbtype = 'docblock'; + $dbtype .= ($this->_pf_docblock ? '' : 'template'); + $ret = $this->retrieveType($value,0,$checkforvar); + foreach($value as $num => $line) + { + $this->_addDocBlockoutput($dbtype, $line[0]); + if ($line[1] === false) + { + if (trim($line[0]) != '*/') + { + $this->newLineNum(); + $this->_wp->linenum++; + } + continue; + } + $this->_addDocBlockoutput($dbtype, $line[1][0]); + $stored = ''; + $typeloc = 1; + $varloc = 2; + if (is_array($line[1][1])) + { + $this->_addDocBlockoutput('coretag','@'.$name.' '); + foreach($ret[0] as $text) + { + if (is_string($text)) $this->_addDocBlockoutput($dbtype,$text); + if (is_array($text)) + { + if ($text[0] != 'desc') $this->_addDocBlockoutput($text[0],$text[1]); + else $stored .= $text[1]; + } + } + } else + { + if (isset($ret[$num])) + { + foreach($ret[$num] as $text) + { + if (is_string($text)) $this->_addDocBlockoutput($dbtype,$text); + if (is_array($text)) + { + if ($text[0] != 'desc') $this->_addDocBlockoutput($text[0],$text[1]); + else $stored .= $text[1]; + } + } + } else $stored = $line[1]; + } + $this->getInlineTags($stored); + if (strpos($stored,'*/') === false) + { + $this->newLineNum(); + $this->_wp->linenum++; + } + } + } + + /** + * @see paramTagHandler() + */ + function staticvarTagHandler($name, $value) + { + return $this->paramTagHandler($name, $value); + } + + /** + * @see paramTagHandler() + */ + function varTagHandler($name, $value) + { + return $this->paramTagHandler($name, $value); + } + + /** + * Handles @return + * + * This handler works like {@link defaultTagHandler()} except it highlights + * the type in "@return type description" + */ + function returnTagHandler($name, $value) + { + $this->paramTagHandler($name, $value, false); + } + /**#@-*/ + + /** + * Retrieve the type portion of a @tag type description + * + * Tags like @param, @return and @var all have a PHP type portion in their + * description. Since the type may contain the expression "object blah" + * where blah is a classname, it makes parsing out the type field complex. + * + * Even more complicated is the case where a tag variable can contain + * multiple types, such as object blah|object blah2|false, and so this + * method handles these cases. + * @param array array of words that were separated by spaces + * @param 0|1 0 = find the type, 1 = find the var, if present + * @param boolean flag to determine whether to check for the end of a + * type is defined by a $varname + * @return array Format: array(state (0 [find type], 1 [var], 2 [done]), + * + * @access private + */ + function retrieveType($value, $state = 0, $checkforvar = false) + { + $index = 0; + $result = array(); + do + { + if (!isset($value[$index][1])) return $result; + $val = $value[$index][1]; + if (empty($val)) return $result; + if ($index == 0) + { + $val = $val[1]; + array_shift($val); + } else + { + $val = explode(' ',$val); + } + $ret = $this->_retrieveType($val, $state, $checkforvar); + $state = $ret[0]; + $result[$index++] = $ret[1]; + } while ((!$checkforvar && $state < 1) || ($state < 2 && $checkforvar)); + return $result; + } + + function _retrieveType($value, $state, $checkforvar) + { + $result = array(); + $result[] = $this->_removeWhiteSpace($value, 0); + if ($state == 0) + { + if (!count($value)) return array(2,$result); + $types = ''; + $index = 0; + if (trim($value[0]) == 'object') + { + $result[] = array('tagphptype', $value[0].' '); + $types .= array_shift($value).' '; + $result[] = $this->_removeWhiteSpace($value, 0); + if (!count($value)) + { // was just passed "object" + return array(2,$result); + } + if ($value[0]{0} == '$' || substr($value[0],0,2) == '&$') + { // was just passed "object" and the next thing is a variable name + if ($checkforvar) + { + $result[] = array('tagvarname' , $value[0].' '); + array_shift($value); + } + $result[] = array('desc', join(' ', $value)); + return array(2,$result); + } + } + $done = false; + $loop = -1; + do + { // this loop checks for type|type|type and for + // type|object classname|type|object classname2 + if (strpos($value[0], '|')) + { + $temptypes = explode('|', $value[0]); + while(count($temptypes)) + { + $type = array_shift($temptypes); + $result[] = array('tagphptype',$type); + if (count($temptypes)) $result[] = '|'; + } + if (trim($type) == 'object') + { + $result[] = array('tagphptype', $types . ' '); + $result[] = $this->_removeWhiteSpace($value,0); + } else $done = true; + array_shift($value); + if (count($value) && strlen($value[0]) && isset ($value[0]) && ($value[0]{0} == '$' || substr($value[0],0,2) == '&$')) + { // was just passed "object" and the next thing is a variable name + $result[] = array('tagvarname' , $value[0].' '); + array_shift($value); + $result[] = array('desc', join(' ', $value)); + return array(2,$result); + } + } else + { + $result[] = array('tagphptype', $value[0].' '); + array_shift($value); + $done = true; + } + $loop++; + } while (!$done && count($value)); + if ($loop) $result[] = ' '; + // still searching for type + if (!$done && !count($value)) return array(0,$result); + // still searching for var + if ($done && !count($value)) return array(1,$result); + } + $result[] = $this->_removeWhiteSpace($value,0); + $state = 1; + if ($checkforvar) + { + if (count($value)) + { + $state = 2; + if (substr($value[0],0,1) == '$' || substr($value[0],0,2) == '&$') + { + $result[] = array('tagvarname' , $value[0].' '); + array_shift($value); + } + } else $state = 1; + } + $result[] = array('desc', join(' ',$value)); + return array($state,$result); + } + + + /** + * @param array array of string + * @param integer index to seek non-whitespace to + * @access private + * @return string whitespace + */ + function _removeWhiteSpace(&$value, $index) + { + $result = ''; + if (count($value) > $index && empty($value[$index])) + { + $found = false; + for($i=$index; $i_pf_colon_colon) + { + $this->_pf_colon_colon = false; + $combo = $this->_pv_last_string[1].'::'.$word[1].'()'; +// debug('testing '.$combo); + $link = $this->_converter->getLink($combo); + if (is_object($link)) + { + $this->_addoutput($this->_converter->returnSee($link, $word[1]), true); + return; + } + $this->_addoutput($word); + return; + } + $link = $this->_converter->getLink($word[1].'()'); + if (is_object($link)) + { + $this->_addoutput($this->_converter->returnSee($link, $word[1]), true); + return; + } elseif (is_string($link) && strpos($link,'ttp://')) + { + $this->_addoutput($this->_converter->returnLink($link, $word[1]), true); + return; + } else + { + $link = $this->_converter->getLink($word[1]); + if (is_object($link)) $word[1] = $this->_converter->returnSee($link, $word[1]); + $this->_addoutput($word, true); + return; + } + } + $this->_addoutput($word); + } + + /** + * Works like {@link _link()} except it only links to global variables + */ + function _globallink($word) + { + if (!is_array($word)) return $this->_addoutput($word); + if ($word[0] != T_VARIABLE) return $this->_addoutput($word); + if (is_array($word) && $word[0] == T_VARIABLE) + { + $link = $this->_converter->getLink('global '.$word[1]); + if (is_object($link)) + { + $this->_addoutput($this->_converter->returnSee($link, $word[1]), true); + return; + } + } + $this->_addoutput($word); + } + + /** + * Works like {@link _link()} except it only links to classes + */ + function _classlink($word) + { +// debug("checking class ".$word[1]); + if (is_array($word) && $word[0] == T_STRING) + { + $link = $this->_converter->getLink($word[1]); + if (is_object($link)) + { + $this->_addoutput($this->_converter->returnSee($link, $word[1]), true); + return; + } + } + $this->_addoutput($word); + } + + /** + * Works like {@link _link()} except it only links to methods + */ + function _methodlink($word) + { + if (is_array($word) && $word[0] == T_STRING) + { +// debug("checking method ".$this->_pv_class.'::'.$word[1].'()'); + if (isset($this->_pv_prev_var_type)) + { + $link = $this->_converter->getLink($this->_pv_prev_var_type.'::'.$word[1].'()'); + } else + $link = $this->_converter->getLink($this->_pv_class.'::'.$word[1].'()'); + if (is_object($link)) + { + $this->_addoutput($this->_converter->returnSee($link, $word[1]), true); + return; + } + if (isset($this->_pv_prev_var_type)) + { + $this->_addoutput($word); + return; + } +// debug("checking method ".$word[1].'()'); + $link = $this->_converter->getLink($word[1].'()'); + if (is_object($link)) + { + $this->_addoutput($this->_converter->returnSee($link, $word[1]), true); + return; + } + } + $this->_addoutput($word); + } + + /** + * Works like {@link _link()} except it only links to class variables + */ + function _varlink($word, $justastring=false) + { + if ($justastring) + { + $word[0] = T_VARIABLE; + } + if (is_array($word) && $word[0] == T_VARIABLE) + { + $x = ($justastring ? '$' : ''); +// debug("checking var ".$this->_pv_class.'::'.$x.$word[1]); + if (isset($this->_pv_prev_var_type)) + { +// debug("checking var ".$this->_pv_prev_var_type.'::'.$x.$word[1]); + $link = $this->_converter->getLink($this->_pv_prev_var_type.'::'.$x.$word[1]); + } + else + $link = $this->_converter->getLink($this->_pv_class.'::'.$x.$word[1]); + if (is_object($link)) + { + $this->_addoutput($this->_converter->returnSee($link, $word[1]), true); + return; + } +// debug("checking var ".$x.$word[1]); + if (isset($this->_pv_prev_var_type)) + { + $this->_addoutput($word); + return; + } + $link = $this->_converter->getLink($x.$word[1]); + if (is_object($link)) + { + $this->_addoutput($this->_converter->returnSee($link, $word[1]), true); + return; + } + } + $this->_addoutput($word); + } + /**#@-*/ + + /**#@+ + * Output Methods + * @access private + */ + /** + * This method adds output to {@link $_line} + * + * If a string with variables like "$test this" is present, then special + * handling is used to allow processing of the variable in context. + * @see _flush_save() + */ + function _addoutput($word, $preformatted = false) + { + if ($this->_pf_no_output_yet) return; + if ($this->_pf_quote_active) + { + if (is_array($word)) $this->_save .= $this->_converter->highlightSource($word[0], $word[1]); + else + $this->_save .= $this->_converter->highlightSource(false, $word, true); + } else + { + $this->_flush_save(); + if (is_string($word) && trim($word) == '') + { + $this->_line .= $word; + return; + } + if (is_array($word) && trim($word[1]) == '') + { + $this->_line .= $word[1]; + return; + } + if (is_array($word)) + { + $this->_line .= $this->_converter->highlightSource($word[0], $word[1], $preformatted); + } else + { + $this->_line .= $this->_converter->highlightSource(false, $word, $preformatted); + } + } + } + + /** + * Like {@link _output()}, but for DocBlock highlighting + */ + function _addDocBlockoutput($dbtype, $word, $preformatted = false) + { + if ($this->_pf_internal) + { + $this->_line .= $this->_converter->highlightDocBlockSource('internal', $word, $preformatted); + } else + { + $this->_line .= $this->_converter->highlightDocBlockSource($dbtype, $word, $preformatted); + } + } + + /** + * Flush a saved string variable highlighting + * + * {@source} + */ + function _flush_save() + { + if (!empty($this->_save)) + { + $this->_save .= $this->_converter->flushHighlightCache(); + // clear the existing cache, reset it to the old value + $this->_converter->_setHighlightCache($this->_save_highlight_state[0], $this->_save_highlight_state[1]); + $this->_line .= $this->_converter->highlightSource(T_CONSTANT_ENCAPSED_STRING, $this->_save, true); + $this->_save = ''; + } + } + /**#@-*/ + + /** + * Give the word parser necessary data to begin a new parse + * @param array all tokens separated by line number + */ + function configWordParser(&$data) + { + $this->_wp->setup($data, $this); + $this->_wp->setWhitespace(true); + } + + /** + * Initialize all parser state variables + * @param boolean true if we are highlighting an inline {@}source} tag's + * output + * @param false|string name of class we are going to start from + * @uses $_wp sets to a new {@link phpDocumentor_HighlightWordParser} + */ + function setupStates($inlinesourceparse, $class) + { + $this->_output = ''; + $this->_line = ''; + unset($this->_wp); + $this->_wp = new phpDocumentor_HighlightWordParser; + $this->_event_stack = new EventStack; + if ($inlinesourceparse) + { + $this->_event_stack->pushEvent(PARSER_EVENT_PHPCODE); + if ($class) + { + $this->_event_stack->pushEvent(PARSER_EVENT_CLASS); + $this->_pv_class = $class; + } + } else $this->_pv_class = null; + $this->_pv_define = null; + $this->_pv_define_name = null; + $this->_pv_define_value = null; + $this->_pv_define_params_data = null; + $this->_pv_dtype = null; + $this->_pv_docblock = null; + $this->_pv_dtemplate = null; + $this->_pv_func = null; + $this->_pv_global_name = null; + $this->_pv_global_val = null; + $this->_pv_globals = null; + $this->_pv_global_count = null; + $this->_pv_include_params_data = null; + $this->_pv_include_name = null; + $this->_pv_include_value = null; + $this->_pv_linenum = null; + $this->_pv_periodline = null; + $this->_pv_paren_count = 0; + $this->_pv_statics = null; + $this->_pv_static_count = null; + $this->_pv_static_val = null; + $this->_pv_quote_data = null; + $this->_pv_function_data = null; + $this->_pv_var = null; + $this->_pv_varname = null; + $this->_pf_definename_isset = false; + $this->_pf_extends_found = false; + $this->_pf_includename_isset = false; + $this->_pf_get_source = false; + $this->_pf_getting_source = false; + $this->_pf_in_class = false; + $this->_pf_in_define = false; + $this->_pf_in_global = false; + $this->_pf_in_include = false; + $this->_pf_in_var = false; + $this->_pf_funcparam_val = false; + $this->_pf_quote_active = false; + $this->_pf_reset_quote_data = true; + $this->_pf_useperiod = false; + $this->_pf_var_equals = false; + $this->_pf_obj_op = false; + $this->_pf_docblock = false; + $this->_pf_docblock_template = false; + $this->_pf_colon_colon = false; + $this->_pv_last_string = false; + $this->_pf_inmethod = false; + $this->_pf_no_output_yet = false; + $this->_pv_saveline = 0; + $this->_pv_next_word = false; + $this->_save = ''; + } + + /** + * Initialize the {@link $tokenpushEvent, $wordpushEvent} arrays + */ + function phpDocumentor_HighlightParser() + { + if (!defined('T_INTERFACE')) { + define('T_INTERFACE', -1); + } + $this->allowableTags = $GLOBALS['_phpDocumentor_tags_allowed']; + $this->allowableInlineTags = $GLOBALS['_phpDocumentor_inline_doc_tags_allowed']; + $this->inlineTagHandlers = array('*' => 'handleDefaultInlineTag'); +/**************************************************************/ + + $this->tokenpushEvent[PARSER_EVENT_NOEVENTS] = + array( + T_OPEN_TAG => PARSER_EVENT_PHPCODE, + ); + +/**************************************************************/ + + $this->tokenpushEvent[PARSER_EVENT_PHPCODE] = + array( + T_FUNCTION => PARSER_EVENT_FUNCTION, + T_CLASS => PARSER_EVENT_CLASS, + T_INTERFACE => PARSER_EVENT_CLASS, + T_INCLUDE_ONCE => PARSER_EVENT_INCLUDE, + T_INCLUDE => PARSER_EVENT_INCLUDE, + T_START_HEREDOC => PARSER_EVENT_EOFQUOTE, + T_REQUIRE => PARSER_EVENT_INCLUDE, + T_REQUIRE_ONCE => PARSER_EVENT_INCLUDE, + T_COMMENT => PARSER_EVENT_COMMENT, + T_DOC_COMMENT => PARSER_EVENT_DOCBLOCK, + ); + $this->wordpushEvent[PARSER_EVENT_PHPCODE] = + array( + "define" => PARSER_EVENT_DEFINE, + ); +/**************************************************************/ + + $this->wordpushEvent[PARSER_EVENT_FUNCTION] = + array( + '{' => PARSER_EVENT_LOGICBLOCK, + '(' => PARSER_EVENT_FUNCTION_PARAMS, + ); + $this->tokenpushEvent[PARSER_EVENT_FUNCTION] = + array( + T_COMMENT => PARSER_EVENT_COMMENT, + T_DOC_COMMENT => PARSER_EVENT_DOCBLOCK, + ); + + $this->wordpopEvent[PARSER_EVENT_FUNCTION] = array("}"); +/**************************************************************/ + + $this->tokenpopEvent[PARSER_EVENT_EOFQUOTE] = array(T_END_HEREDOC); +/**************************************************************/ + + $this->tokenpushEvent[PARSER_EVENT_FUNCTION_PARAMS] = + array( + T_CONSTANT_ENCAPSED_STRING => PARSER_EVENT_QUOTE, + T_ARRAY => PARSER_EVENT_ARRAY, + T_COMMENT => PARSER_EVENT_COMMENT, + T_DOC_COMMENT => PARSER_EVENT_DOCBLOCK, + ); + $this->wordpushEvent[PARSER_EVENT_FUNCTION_PARAMS] = + array( + '"' => PARSER_EVENT_QUOTE, + "'" => PARSER_EVENT_QUOTE, + ); + $this->wordpopEvent[PARSER_EVENT_FUNCTION_PARAMS] = array(")"); +/**************************************************************/ + + $this->wordpushEvent[PARSER_EVENT_LOGICBLOCK] = + array( + "{" => PARSER_EVENT_LOGICBLOCK, + '"' => PARSER_EVENT_QUOTE, + ); + $this->tokenpushEvent[PARSER_EVENT_LOGICBLOCK] = + array( + T_GLOBAL => PARSER_EVENT_FUNC_GLOBAL, + T_STATIC => PARSER_EVENT_STATIC_VAR, + T_START_HEREDOC => PARSER_EVENT_EOFQUOTE, + T_CURLY_OPEN => PARSER_EVENT_LOGICBLOCK, + T_DOLLAR_OPEN_CURLY_BRACES => PARSER_EVENT_LOGICBLOCK, + ); + + $this->wordpopEvent[PARSER_EVENT_LOGICBLOCK] = array("}"); + $this->tokenpopEvent[PARSER_EVENT_LOGICBLOCK] = array(T_CURLY_OPEN); + +/**************************************************************/ + + $this->tokenpushEvent[PARSER_EVENT_ARRAY] = + array( + T_COMMENT => PARSER_EVENT_COMMENT, + T_DOC_COMMENT => PARSER_EVENT_DOCBLOCK, + ); + $this->wordpopEvent[PARSER_EVENT_ARRAY] = array(")"); +/**************************************************************/ + + $this->tokenpushEvent[PARSER_EVENT_FUNC_GLOBAL] = + array( + T_COMMENT => PARSER_EVENT_COMMENT, + T_DOC_COMMENT => PARSER_EVENT_DOCBLOCK, + ); + + $this->wordpopEvent[PARSER_EVENT_FUNC_GLOBAL] = array(";"); +/**************************************************************/ + + $this->tokenpushEvent[PARSER_EVENT_STATIC_VAR] = + array( + T_CONSTANT_ENCAPSED_STRING => PARSER_EVENT_QUOTE, + T_COMMENT => PARSER_EVENT_COMMENT, + T_DOC_COMMENT => PARSER_EVENT_DOCBLOCK, + ); + $this->wordpushEvent[PARSER_EVENT_STATIC_VAR] = + array( + "=" => PARSER_EVENT_STATIC_VAR_VALUE, + ); + $this->wordpopEvent[PARSER_EVENT_STATIC_VAR] = array(";"); +/**************************************************************/ + + $this->tokenpushEvent[PARSER_EVENT_STATIC_VAR_VALUE] = + array( + T_CONSTANT_ENCAPSED_STRING => PARSER_EVENT_QUOTE, + T_COMMENT => PARSER_EVENT_COMMENT, + T_DOC_COMMENT => PARSER_EVENT_DOCBLOCK, + T_ARRAY => PARSER_EVENT_ARRAY, + ); + $this->wordpushEvent[PARSER_EVENT_STATIC_VAR_VALUE] = + array( + '"' => PARSER_EVENT_QUOTE, + "'" => PARSER_EVENT_QUOTE, + ); + $this->wordpopEvent[PARSER_EVENT_STATIC_VAR_VALUE] = array(";",","); +/**************************************************************/ + $this->tokenpushEvent[PARSER_EVENT_QUOTE] = + array( + T_OBJECT_OPERATOR => PARSER_EVENT_CLASS_MEMBER, + T_CURLY_OPEN => PARSER_EVENT_QUOTE_VAR, + ); + + $this->wordpopEvent[PARSER_EVENT_QUOTE] = array('"'); +/**************************************************************/ + $this->tokenpushEvent[PARSER_EVENT_QUOTE_VAR] = + array( + T_OBJECT_OPERATOR => PARSER_EVENT_CLASS_MEMBER, + T_CURLY_OPEN => PARSER_EVENT_QUOTE_VAR, + ); + + $this->wordpushEvent[PARSER_EVENT_QUOTE_VAR] = + array( + "{" => PARSER_EVENT_QUOTE_VAR, + '"' => PARSER_EVENT_QUOTE_VAR, + "'" => PARSER_EVENT_QUOTE_VAR, + ); + $this->wordpopEvent[PARSER_EVENT_QUOTE_VAR] = array('}'); +/**************************************************************/ + + $this->tokenpushEvent[PARSER_EVENT_DEFINE] = + array( + T_COMMENT => PARSER_EVENT_COMMENT, + T_DOC_COMMENT => PARSER_EVENT_DOCBLOCK, + T_CONSTANT_ENCAPSED_STRING => PARSER_EVENT_QUOTE, + ); + $this->wordpushEvent[PARSER_EVENT_DEFINE] = + array( + "(" => PARSER_EVENT_DEFINE_PARAMS, + ); + $this->wordpopEvent[PARSER_EVENT_DEFINE] = array(";"); +/**************************************************************/ + + $this->tokenpushEvent[PARSER_EVENT_DEFINE_PARAMS] = + array( + T_COMMENT => PARSER_EVENT_COMMENT, + T_DOC_COMMENT => PARSER_EVENT_DOCBLOCK, + ); + $this->wordpushEvent[PARSER_EVENT_DEFINE_PARAMS] = + array( + "(" => PARSER_EVENT_DEFINE_PARAMS_PARENTHESIS, + '"' => PARSER_EVENT_QUOTE, + "'" => PARSER_EVENT_QUOTE, + ); + $this->wordpopEvent[PARSER_EVENT_DEFINE_PARAMS] = array(")"); +/**************************************************************/ + + $this->tokenpushEvent[PARSER_EVENT_DEFINE_PARAMS_PARENTHESIS] = + array( + T_COMMENT => PARSER_EVENT_COMMENT, + T_DOC_COMMENT => PARSER_EVENT_DOCBLOCK, + ); + $this->wordpushEvent[PARSER_EVENT_DEFINE_PARAMS_PARENTHESIS] = + array( + "(" => PARSER_EVENT_DEFINE_PARAMS_PARENTHESIS, + '"' => PARSER_EVENT_QUOTE, + "'" => PARSER_EVENT_QUOTE, + ); + $this->wordpopEvent[PARSER_EVENT_DEFINE_PARAMS_PARENTHESIS] = array(")"); +/**************************************************************/ + + $this->tokenpushEvent[PARSER_EVENT_VAR] = + array( + T_COMMENT => PARSER_EVENT_COMMENT, + T_DOC_COMMENT => PARSER_EVENT_DOCBLOCK, + T_ARRAY => PARSER_EVENT_ARRAY, + ); + $this->wordpopEvent[PARSER_EVENT_VAR] = array(";"); +/**************************************************************/ + + $this->tokenpushEvent[PARSER_EVENT_CLASS] = + array( + T_FUNCTION => PARSER_EVENT_METHOD, + T_VAR => PARSER_EVENT_VAR, + T_COMMENT => PARSER_EVENT_DOCBLOCK, + T_DOC_COMMENT => PARSER_EVENT_DOCBLOCK, + T_CLOSE_TAG => PARSER_EVENT_OUTPHP, + ); + $this->wordpopEvent[PARSER_EVENT_CLASS] = array("}"); + +/**************************************************************/ + + $this->wordpushEvent[PARSER_EVENT_METHOD] = + array( + '{' => PARSER_EVENT_METHOD_LOGICBLOCK, + '(' => PARSER_EVENT_FUNCTION_PARAMS, + ); + $this->tokenpushEvent[PARSER_EVENT_METHOD] = + array( + T_COMMENT => PARSER_EVENT_COMMENT, + T_DOC_COMMENT => PARSER_EVENT_DOCBLOCK, + ); + + $this->wordpopEvent[PARSER_EVENT_METHOD] = array("}", ";"); +/**************************************************************/ + + $this->wordpushEvent[PARSER_EVENT_METHOD_LOGICBLOCK] = + array( + "{" => PARSER_EVENT_METHOD_LOGICBLOCK, + '"' => PARSER_EVENT_QUOTE, + ); + $this->tokenpushEvent[PARSER_EVENT_METHOD_LOGICBLOCK] = + array( + T_OBJECT_OPERATOR => PARSER_EVENT_CLASS_MEMBER, + T_GLOBAL => PARSER_EVENT_FUNC_GLOBAL, + T_STATIC => PARSER_EVENT_STATIC_VAR, + T_CURLY_OPEN => PARSER_EVENT_LOGICBLOCK, + T_DOLLAR_OPEN_CURLY_BRACES => PARSER_EVENT_LOGICBLOCK, + ); + + $this->wordpopEvent[PARSER_EVENT_METHOD_LOGICBLOCK] = array("}"); + $this->tokenpopEvent[PARSER_EVENT_METHOD_LOGICBLOCK] = array(T_CURLY_OPEN); +/**************************************************************/ + + $this->tokenpushEvent[PARSER_EVENT_INCLUDE] = + array( + T_COMMENT => PARSER_EVENT_COMMENT, + T_DOC_COMMENT => PARSER_EVENT_DOCBLOCK, + ); + $this->wordpushEvent[PARSER_EVENT_INCLUDE] = + array( + "(" => PARSER_EVENT_INCLUDE_PARAMS, + ); + $this->wordpopEvent[PARSER_EVENT_INCLUDE] = array(";"); +/**************************************************************/ + + $this->tokenpushEvent[PARSER_EVENT_INCLUDE_PARAMS] = + array( + T_COMMENT => PARSER_EVENT_COMMENT, + T_DOC_COMMENT => PARSER_EVENT_DOCBLOCK, + ); + $this->wordpushEvent[PARSER_EVENT_INCLUDE_PARAMS] = + array( + "(" => PARSER_EVENT_INCLUDE_PARAMS_PARENTHESIS, + ); + $this->wordpopEvent[PARSER_EVENT_INCLUDE_PARAMS] = array(")"); +/**************************************************************/ + + $this->tokenpushEvent[PARSER_EVENT_INCLUDE_PARAMS_PARENTHESIS] = + array( + T_COMMENT => PARSER_EVENT_COMMENT, + T_DOC_COMMENT => PARSER_EVENT_DOCBLOCK, + ); + $this->wordpushEvent[PARSER_EVENT_INCLUDE_PARAMS_PARENTHESIS] = + array( + "(" => PARSER_EVENT_INCLUDE_PARAMS_PARENTHESIS, + ); + $this->wordpopEvent[PARSER_EVENT_INCLUDE_PARAMS_PARENTHESIS] = array(")"); + } +} +?> -- cgit v1.2.3