';
private $inComment = false;
private $lastLineEndTokenType = "";
private $fileToken = null;
private $currFileToken = 0;
private $lineNb = 0;
private $multiLineTok = null;
// If one of these tokens occur as the last token of a line
// then the next line can be treated as a continuation line
// depending on how it starts.
public static $contTypes = array(
"(",
",",
".",
"=",
T_LOGICAL_XOR,
T_LOGICAL_AND,
T_LOGICAL_OR,
T_PLUS_EQUAL,
T_MINUS_EQUAL,
T_MUL_EQUAL,
T_DIV_EQUAL,
T_CONCAT_EQUAL,
T_MOD_EQUAL,
T_AND_EQUAL,
T_OR_EQUAL,
T_XOR_EQUAL,
T_BOOLEAN_AND,
T_BOOLEAN_OR,
T_OBJECT_OPERATOR,
T_DOUBLE_ARROW,
"[",
"]",
T_LOGICAL_OR,
T_LOGICAL_XOR,
T_LOGICAL_AND,
T_STRING
);
/*}}}*/
/*{{{ protected function openFileReadOnly() */
/**
* Opens the file to be parsed in Read-only mode.
* Overriden to tokenize the whole file.
*
* @return FALSE on failure.
* @access protected
*/
protected function openFileReadOnly() {
$this->fileToken = @token_get_all(file_get_contents($this->filename));
return parent::openFileReadOnly();
}
/*}}}*/
/*{{{ protected function getNextToken() */
/**
* Gets the next token.
*
* The same token can be returned several times for multi-line
* tokens.
*
* @param $line The current line read from the file.
* @param &$pos The position of the next token string in the line.
* @return the next token or null at the end of the line.
* @access protected
*/
protected function getNextToken($line, &$pos) {
$lineLen = strlen($line);
if($pos >= $lineLen) {
return null;
}
if($this->multiLineTok != null) {
list($tok, $lnb, $posnl) = $this->multiLineTok;
if(is_string($tok)) {
$str = $tok;
} else {
$str = $tok[1];
}
if($posnl >= strlen($str)) {
$this->multiLineTok = null;
} else {
if(substr($str, $posnl + 1, $lineLen) == $line) {
$pos += $lineLen;
$newPosnl = $posnl + $lineLen;
} else {
$newPosnl = strpos($str, "\n", $posnl + 1);
if($newPosnl === false) {
$newPosnl = strlen($str);
}
$len = $newPosnl - $posnl - 1;
//if(substr($str, $posnl + 1, $len) != substr($line, $pos, $len)) {
//}
$pos += $len;
}
$this->multiLineTok[1]++;
$this->multiLineTok[2] = $newPosnl;
return $tok;
}
}
if(!isset($this->fileToken[$this->currFileToken])) {
return null;
}
$tok = &$this->fileToken[$this->currFileToken];
if(is_string($tok)) {
$str = $tok;
} else {
$str = $tok[1];
}
$nbnl = substr_count($str, "\n");
if($nbnl > 0 && ($posnl = strpos($str, "\n")) != strlen($str) - 1) {
$this->multiLineTok = array($tok, 1, $posnl);
$str = substr($str, 0, $posnl + 1);
}
if(substr($line, $pos, strlen($str)) == $str) {
$this->currFileToken++;
$pos += strlen($str);
return $tok;
} else {
return null;
}
}
/*}}}*/
/*{{{ protected function isMultiLineCont() */
/**
* Determines if the beginning of current line is a
* continuation of an executable multi-line token.
*
* Called at the first token of a line.
*
* @return Boolean true if it is a continuation line
* @access protected
*/
protected function isMultiLineCont() {
if($this->multiLineTok == null
|| $this->multiLineTok[1] <= 1) {
return false;
}
switch ($this->getTokenType($this->multiLineTok[0])) {
case T_COMMENT:
case T_INLINE_HTML: //
jhsk
return false;
}
return true;
}
/*}}}*/
/*{{{ protected function processLine() */
/**
* Process a line read from the file and determine if it is an
* executable line or not.
*
* This is the work horse function that does most of the parsing.
* To parse PHP, get_all_tokens() tokenizer function is used.
*
* @param $line Line to be parsed.
* @access protected
*/
protected function processLine($line) {
// Default values
$prevLineType = $this->lineType;
$this->lineType = LINE_TYPE_NOEXEC;
$tokenCnt = 0;
$this->lineNb++;
$pos = 0;
$seeMore = false;
$seenEnough = false;
$lastToken = null;
while (($token = $this->getNextToken($line, $pos))) {
if (!is_string($token)) {
$stoken = token_name($token[0]) . ' "'
. str_replace(array("\\", "\"", "\n", "\r")
, array("\\\\", "\\\"", "\\n", "\\r")
, $token[1]) . '"';
if (isset($token[2])) {
$stoken .= '[' . $token[2] . ']';
if ($token[2] != $this->lineNb) {
$stoken .= ' != [' . $this->lineNb . ']';
}
}
} else {
$stoken = $token;
}
$this->logger->debug("Token $stoken", __FILE__, __LINE__);
if (!is_string($token) && $token[0] == T_WHITESPACE) {
continue;
}
$lastToken = $token;
$tokenCnt ++;
if ($this->inHereDoc) {
$this->lineType = LINE_TYPE_CONT;
$this->logger->debug("Here doc Continuation! Token: $token",
__FILE__, __LINE__);
if ($this->getTokenType($token) == T_END_HEREDOC) {
$this->inHereDoc = false;
}
continue;
}
if ($this->inFunction) {
$this->lineType = LINE_TYPE_NOEXEC;
$this->logger->debug("Function! Token: $token",
__FILE__, __LINE__);
if ($this->getTokenType($token) == '{') {
$this->inFunction = false;
}
continue;
}
if($tokenCnt == 1 && $prevLineType != LINE_TYPE_NOEXEC
&& ($this->isMultiLineCont()
|| $this->isContinuation($token))) {
$this->lineType = LINE_TYPE_CONT;
$this->logger->debug("Continuation! Token: ".print_r($token, true),
__FILE__, __LINE__);
$seenEnough = true;
continue;
}
if ($seenEnough) {
continue;
}
if(is_string($token)) {
// FIXME: Add more cases, if needed
switch($token) {
// Any of these things, are non-executable.
// And so do not change the status of the line
case '{':
case '}':
case '(':
case ')':
case ';':
break;
// Everything else by default is executable.
default:
$this->logger->debug("Other string: $token",
__FILE__, __LINE__);
if($this->lineType == LINE_TYPE_NOEXEC) {
$this->lineType = LINE_TYPE_EXEC;
}
break;
}
$this->logger->debug("Status: " . $this->getLineTypeStr($this->lineType) . "\t\tToken: $token",
__FILE__, __LINE__);
}
else {
// The token is an array
list($tokenType, $text) = $token;
switch($tokenType) {
case T_COMMENT:
case T_DOC_COMMENT:
case T_WHITESPACE: // white space
case T_OPEN_TAG: // < ?
case T_OPEN_TAG_WITH_ECHO: // < ? =
case T_CURLY_OPEN: //
case T_INLINE_HTML: //
jhsk
//case T_STRING: //
case T_EXTENDS: // extends
case T_STATIC: // static
case T_STRING_VARNAME: // string varname?
case T_CHARACTER: // character
case T_ELSE: // else
case T_CONSTANT_ENCAPSED_STRING: // "some str"
case T_ARRAY: // array
// Any of these things, are non-executable.
// And so do not change the status of the line
// as the line starts as non-executable.
break;
case T_START_HEREDOC:
$this->inHereDoc = true;
break;
case T_PRIVATE: // private
case T_PUBLIC: // public
case T_PROTECTED: // protected
case T_VAR: // var
case T_GLOBAL: // global
case T_ABSTRACT: // abstract (Moodle added)
case T_CLASS: // class
case T_INTERFACE: // interface
case T_REQUIRE: // require
case T_REQUIRE_ONCE: // require_once
case T_INCLUDE: // include
case T_INCLUDE_ONCE: // include_once
case T_SWITCH: // switch
case T_CONST: // const
case T_TRY: // try
$this->lineType = LINE_TYPE_NOEXEC;
// No need to see any further
$seenEnough = true;
break;
case T_FUNCTION: // function
$this->lineType = LINE_TYPE_NOEXEC;
$this->inFunction = true;
// No need to see any further
$seenEnough = true;
break;
case T_VARIABLE: // $foo
$seeMore = true;
if($this->lineType == LINE_TYPE_NOEXEC) {
$this->lineType = LINE_TYPE_EXEC;
}
break;
case T_CLOSE_TAG:
if($this->lineType != LINE_TYPE_EXEC) {
$this->lineType = LINE_TYPE_NOEXEC;
}
break;
default:
$this->logger->debug("Other token: " . token_name($tokenType),
__FILE__, __LINE__);
$seeMore = false;
if($this->lineType == LINE_TYPE_NOEXEC) {
$this->lineType = LINE_TYPE_EXEC;
}
break;
}
$this->logger->debug("Status: " . $this->getLineTypeStr($this->lineType) . "\t\tToken type: $tokenType \tText: $text",
__FILE__, __LINE__);
}
if(($this->lineType == LINE_TYPE_EXEC && !$seeMore)
|| $seenEnough) {
// start moodle modification: comment debug line causing notices
//$this->logger->debug("Made a decision! Exiting. Token Type: $tokenType & Text: $text",
// __FILE__, __LINE__);
// end moodle modification
if($seenEnough) {
$this->logger->debug("Seen enough at Token Type: $tokenType & Text: $text",
__FILE__, __LINE__);
} else {
$seenEnough = true;
}
}
} // end while
$this->logger->debug("Line Type: " . $this->getLineTypeStr($this->lineType),
__FILE__, __LINE__);
$this->lastLineEndTokenType = $this->getTokenType($lastToken);
$this->logger->debug("Last End Token: " . $this->lastLineEndTokenType,
__FILE__, __LINE__);
}
/*}}}*/
/*{{{ public function getLineType() */
/**
* Returns the type of line just read
*
* @return Line type
* @access public
*/
public function getLineType() {
return $this->lineType;
}
/*}}}*/
/*{{{ protected function isContinuation() */
/**
* Check if a line is a continuation of the previous line
*
* @param &$token Second token in a line (after PHP start)
* @return Boolean True if the line is a continuation; false otherwise
* @access protected
*/
protected function isContinuation(&$token) {
if(is_string($token)) {
switch($token) {
case ".":
case ",";
case "]":
case "[":
case "(":
case ")":
case "=":
return true;
}
}
else {
list($tokenType, $text) = $token;
switch($tokenType) {
case T_CONSTANT_ENCAPSED_STRING:
case T_ARRAY:
case T_DOUBLE_ARROW:
case T_OBJECT_OPERATOR:
case T_LOGICAL_XOR:
case T_LOGICAL_AND:
case T_LOGICAL_OR:
case T_PLUS_EQUAL:
case T_MINUS_EQUAL:
case T_MUL_EQUAL:
case T_DIV_EQUAL:
case T_CONCAT_EQUAL:
case T_MOD_EQUAL:
case T_AND_EQUAL:
case T_OR_EQUAL:
case T_XOR_EQUAL:
case T_BOOLEAN_AND:
case T_BOOLEAN_OR:
case T_LNUMBER:
case T_DNUMBER:
return true;
case T_STRING:
case T_VARIABLE:
return in_array($this->lastLineEndTokenType, PHPParser::$contTypes);
}
}
return false;
}
/*}}}*/
/*{{{ protected function getTokenType() */
/**
* Get the token type of a token (if exists) or
* the token itself.
*
* @param $token Token
* @return Token type or token itself
* @access protected
*/
protected function getTokenType($token) {
if(is_string($token)) {
return $token;
}
else {
list($tokenType, $text) = $token;
return $tokenType;
}
}
/*}}}*/
/*
// Main
$obj = new PHPParser();
$obj->parse("test.php");
while(($line = $obj->getLine()) !== false) {
echo "#########################\n";
echo "[" . $line . "] Type: [" . $obj->getLineTypeStr($obj->getLineType()) . "]\n";
echo "#########################\n";
}
*/
}
?>