Advertisement
Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- #!/usr/bin/php
- <?php
- class Token {
- public $type;
- public $contents;
- public function __construct($rawToken) {
- if (is_array($rawToken)) {
- $this->type = $rawToken[0];
- $this->contents = $rawToken[1];
- } else {
- $this->type = -1;
- $this->contents = $rawToken;
- }
- }
- }
- $INCLUDE_TOKENS = array(T_INCLUDE, T_INCLUDE_ONCE, T_REQUIRE, T_REQUIRE_ONCE);
- // Define predefined variables
- $PREDEFINED_VARIABLES = array('$GLOBALS', '$_SERVER', '$_REQUEST', '$_FILES', '$_GET', '$_POST', '$_SESSION', '$_ENV', '$_COOKIE', '$HTTP_RAW_POST_DATE', '$php_errormsg', '$this');
- $USER_PREDEFINED = array('$APP_PATH', '$LIB_PATH', '$WWW_PATH', '$HOSTNAME', '$IMS_EMAIL', '$LIMIT', '$page_offset', '$extra_html_header', '$extra_html_footer', '$TITLE', '$ENABLE_CALENDAR');
- $PREDEFINED_VARIABLES = array_merge($PREDEFINED_VARIABLES, $USER_PREDEFINED);
- class VariableChecker {
- private $files = array(); // List of files that have been processed
- private $globalVariables = array(); // Variables defined at top level
- private $functionVariables = array(); // Variables defined at function scope
- private $conditionalVariables = array(); // Variables defined inside of current conditional scope
- private $openBraces = 0; // Number of currently unclosed { braces
- private $file; // The file being processed
- private $tokens; // Tokens for current file
- private $tokenCount;
- private $tokenIndex = 0; // Index into $this->tokens
- private $lineNo = 1;
- private $declaringVariable = null; // Track the variable being declared
- private $declaringConditional = false; // Declaring conditional
- private $declaringFunction = false; // Track if in declaration of function
- private $functionBraceLevel = -1; // Track where function body starts
- private $conditionalVariableStack = array(); // Stack of conditional variables
- private $conditionalBraceLevelStack = array(); // Track where conditional body starts
- private $undeclaredVariables = array(); // Variables found to be undeclared
- protected function fileGetTokens($file) {
- $code = file_get_contents($file);
- $rawTokens = token_get_all($code);
- $tokens = array();
- foreach ($rawTokens as $rawToken) {
- $tokens[] = new Token($rawToken);
- }
- return $tokens;
- }
- protected function skipWhitespace() {
- do {
- $this->tokenIndex++;
- $token = $this->tokens[$this->tokenIndex];
- $this->lineNo += substr_count($token->contents, "\n");
- } while ($token->type == T_WHITESPACE);
- return $token;
- }
- protected function registerGlobals() {
- do {
- $this->tokenIndex++;
- $token = $this->tokens[$this->tokenIndex];
- if ($token->type == T_VARIABLE) {
- $this->functionVariables[$token->contents] = true;
- }
- $this->lineNo += substr_count($token->contents, "\n");
- } while ($token->contents != ';');
- }
- protected function inConditional() {
- return count($this->conditionalBraceLevelStack) > 0;
- }
- protected function getCurrentConditionalBraceLevel() {
- $index = count($this->conditionalBraceLevelStack) - 1;
- return $this->conditionalBraceLevelStack[$index];
- }
- protected function hasConditionalVariable($variable) {
- // Search current scope
- if ($this->conditionalVariables[$variable]) {
- return true;
- }
- // Search stack
- foreach ($this->conditionalVariableStack as $arr) {
- if ($arr[$variable]) {
- return true;
- }
- }
- return false;
- }
- protected function matchConditional() {
- array_push($this->conditionalBraceLevelStack, $this->openBraces);
- array_push($this->conditionalVariableStack, $this->conditionalVariables);
- $this->conditionalVariables = array();
- $this->declaringConditional = true;
- }
- protected function matchOpenBrace() {
- if ($this->declaringFunction) {
- $this->functionBraceLevel = $this->openBraces;
- $this->declaringFunction = false;
- }
- $this->declaringConditional = false;
- $this->openBraces++;
- }
- protected function matchCloseBrace() {
- $this->openBraces--;
- if ($this->functionBraceLevel == $this->openBraces) {
- $this->functionVariables = array(); // clear function variable table
- $this->functionBraceLevel = -1;
- }
- if ($this->inConditional()) {
- if ($this->getCurrentConditionalBraceLevel() == $this->openBraces) {
- array_pop($this->conditionalBraceLevelStack);
- $this->conditionalVariables = array_pop($this->conditionalVariableStack);
- }
- }
- }
- protected function checkVariable($variable) {
- global $PREDEFINED_VARIABLES;
- if ($this->functionVariables[$variable] || $this->globalVariables[$variable] || in_array($variable, $PREDEFINED_VARIABLES) || $this->hasConditionalVariable($variable)) {
- return true;
- } else {
- if (!$this->undeclaredVariables[$variable]) {
- echo $this->file . ':' . $this->lineNo . ' - "' . $variable . '" not declared' . "\n";
- }
- $this->undeclaredVariables[$variable] = true;
- return false;
- }
- }
- protected function matchVariable() {
- $token = $this->tokens[$this->tokenIndex];
- $variable = $token->contents;
- if ($this->declaringFunction) {
- $this->functionVariables[$variable] = true;
- } else {
- $nextToken = $this->skipWhitespace();
- if ($nextToken->contents == '=') {
- $this->declaringVariable = $variable;
- } else {
- $this->checkVariable($variable);
- }
- $this->tokenIndex--;
- }
- }
- protected function declareVariable($variable) {
- if ($this->inConditional()) {
- $this->conditionalVariables[$variable] = true;
- } elseif ($this->functionBraceLevel > 0) {
- $this->functionVariables[$variable] = true;
- } else {
- $this->globalVariables[$variable] = true;
- }
- $this->declaringVariable = null;
- }
- protected function matchForeachAs() {
- $valToken = $this->skipWhitespace();
- $nextToken = $this->skipWhitespace();
- if ($nextToken->type == T_DOUBLE_ARROW) {
- $keyToken = $valToken;
- $valToken = $this->skipWhitespace();
- $this->declareVariable($keyToken->contents);
- $this->declareVariable($valToken->contents);
- } else {
- $this->declareVariable($valToken->contents);
- $this->tokenIndex--;
- }
- }
- protected function matchClassVariable() {
- $token = $this->skipWhitespace();
- if ($token->type == T_VARIABLE) {
- // we don't do anything with class variables
- // since they are accessed through $this
- } else {
- $token->tokenIndex--;
- }
- }
- public function check($file) {
- global $INCLUDE_TOKENS;
- $this->file = $file;
- $this->tokens = $this->fileGetTokens($file);
- $this->tokenCount = count($this->tokens);
- $this->tokenIndex = 0;
- while ($this->tokenIndex < $this->tokenCount) {
- $token = $this->tokens[$this->tokenIndex];
- $newlineCount = substr_count($token->contents, "\n");
- $this->lineNo += $newlineCount;
- if ($token->type == T_FUNCTION) {
- $this->declaringFunction = true;
- } elseif ($token->type == T_GLOBAL) {
- $this->registerGlobals();
- } elseif ($token->type == T_IF || $token->type == T_ELSEIF) {
- $this->matchConditional();
- } elseif ($token->contents == '{' || ($this->declaringConditional && $token->contents == ':')) {
- $this->matchOpenBrace();
- } elseif ($token->contents == '}' || $token->type == T_ENDIF) {
- $this->matchCloseBrace();
- } elseif ($token->type == T_AS) {
- $this->matchForeachAs();
- } elseif ($this->declaringVariable && ($token->contents == ';' || $newlineCount > 0)) {
- $this->declareVariable($this->declaringVariable);
- } elseif (in_array($token->type, array(T_PRIVATE, T_PROTECTED, T_PUBLIC, T_VAR))) {
- $this->matchClassVariable();
- } elseif ($token->type == T_VARIABLE) {
- $this->matchVariable();
- }
- $this->tokenIndex++;
- }
- $this->files[] = $file;
- }
- }
- $file = $argv[1];
- $checker = new VariableChecker();
- $checker->check($file);
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement