Want more features on Pastebin? Sign Up, it's FREE!
Guest

grom

By: a guest on Aug 10th, 2008  |  syntax: PHP  |  size: 8.90 KB  |  views: 154  |  expires: Never
download  |  raw  |  embed  |  report abuse  |  print
Text below is selected. Please press Ctrl+C to copy to your clipboard. (⌘+C on Mac)
  1. #!/usr/bin/php
  2. <?php
  3. class Token {
  4.     public $type;
  5.     public $contents;
  6.  
  7.     public function __construct($rawToken) {
  8.         if (is_array($rawToken)) {
  9.             $this->type = $rawToken[0];
  10.             $this->contents = $rawToken[1];
  11.         } else {
  12.             $this->type = -1;
  13.             $this->contents = $rawToken;
  14.         }
  15.     }
  16. }
  17.  
  18. $INCLUDE_TOKENS = array(T_INCLUDE, T_INCLUDE_ONCE, T_REQUIRE, T_REQUIRE_ONCE);
  19.  
  20. // Define predefined variables
  21. $PREDEFINED_VARIABLES = array('$GLOBALS', '$_SERVER', '$_REQUEST', '$_FILES', '$_GET', '$_POST', '$_SESSION', '$_ENV', '$_COOKIE', '$HTTP_RAW_POST_DATE', '$php_errormsg', '$this');
  22. $USER_PREDEFINED = array('$APP_PATH', '$LIB_PATH', '$WWW_PATH', '$HOSTNAME', '$IMS_EMAIL', '$LIMIT', '$page_offset', '$extra_html_header', '$extra_html_footer', '$TITLE', '$ENABLE_CALENDAR');
  23. $PREDEFINED_VARIABLES = array_merge($PREDEFINED_VARIABLES, $USER_PREDEFINED);
  24.  
  25. class VariableChecker {
  26.     private $files = array(); // List of files that have been processed
  27.     private $globalVariables = array(); // Variables defined at top level
  28.     private $functionVariables = array(); // Variables defined at function scope
  29.     private $conditionalVariables = array(); // Variables defined inside of current conditional scope
  30.     private $openBraces = 0; // Number of currently unclosed { braces
  31.     private $file; // The file being processed
  32.     private $tokens; // Tokens for current file
  33.     private $tokenCount;
  34.     private $tokenIndex = 0; // Index into $this->tokens
  35.     private $lineNo = 1;
  36.     private $declaringVariable = null; // Track the variable being declared
  37.     private $declaringConditional = false; // Declaring conditional
  38.     private $declaringFunction = false; // Track if in declaration of function
  39.     private $functionBraceLevel = -1; // Track where function body starts
  40.     private $conditionalVariableStack = array(); // Stack of conditional variables
  41.     private $conditionalBraceLevelStack = array(); // Track where conditional body starts
  42.     private $undeclaredVariables = array(); // Variables found to be undeclared
  43.  
  44.     protected function fileGetTokens($file) {
  45.         $code = file_get_contents($file);
  46.         $rawTokens = token_get_all($code);
  47.         $tokens = array();
  48.         foreach ($rawTokens as $rawToken) {
  49.             $tokens[] = new Token($rawToken);
  50.         }
  51.         return $tokens;
  52.     }
  53.  
  54.     protected function skipWhitespace() {
  55.         do {
  56.             $this->tokenIndex++;
  57.             $token = $this->tokens[$this->tokenIndex];
  58.             $this->lineNo += substr_count($token->contents, "\n");
  59.         } while ($token->type == T_WHITESPACE);
  60.         return $token;
  61.     }
  62.  
  63.     protected function registerGlobals() {
  64.         do {
  65.             $this->tokenIndex++;
  66.             $token = $this->tokens[$this->tokenIndex];
  67.             if ($token->type == T_VARIABLE) {
  68.                 $this->functionVariables[$token->contents] = true;
  69.             }
  70.             $this->lineNo += substr_count($token->contents, "\n");
  71.         } while ($token->contents != ';');        
  72.     }
  73.  
  74.     protected function inConditional() {
  75.         return count($this->conditionalBraceLevelStack) > 0;
  76.     }
  77.  
  78.     protected function getCurrentConditionalBraceLevel() {
  79.         $index = count($this->conditionalBraceLevelStack) - 1;
  80.         return $this->conditionalBraceLevelStack[$index];
  81.     }
  82.  
  83.     protected function hasConditionalVariable($variable) {
  84.         // Search current scope
  85.         if ($this->conditionalVariables[$variable]) {
  86.             return true;
  87.         }
  88.  
  89.         // Search stack
  90.         foreach ($this->conditionalVariableStack as $arr) {
  91.             if ($arr[$variable]) {
  92.                 return true;
  93.             }
  94.         }
  95.  
  96.         return false;
  97.     }
  98.  
  99.     protected function matchConditional() {
  100.         array_push($this->conditionalBraceLevelStack, $this->openBraces);
  101.         array_push($this->conditionalVariableStack, $this->conditionalVariables);
  102.         $this->conditionalVariables = array();
  103.         $this->declaringConditional = true;
  104.     }
  105.  
  106.     protected function matchOpenBrace() {
  107.         if ($this->declaringFunction) {
  108.             $this->functionBraceLevel = $this->openBraces;
  109.             $this->declaringFunction = false;
  110.         }
  111.         $this->declaringConditional = false;
  112.         $this->openBraces++;
  113.     }
  114.  
  115.     protected function matchCloseBrace() {
  116.         $this->openBraces--;
  117.         if ($this->functionBraceLevel == $this->openBraces) {
  118.             $this->functionVariables = array(); // clear function variable table
  119.             $this->functionBraceLevel = -1;
  120.         }
  121.         if ($this->inConditional()) {
  122.             if ($this->getCurrentConditionalBraceLevel() == $this->openBraces) {
  123.                 array_pop($this->conditionalBraceLevelStack);
  124.                 $this->conditionalVariables = array_pop($this->conditionalVariableStack);
  125.             }
  126.         }
  127.     }
  128.  
  129.     protected function checkVariable($variable) {
  130.         global $PREDEFINED_VARIABLES;
  131.         if ($this->functionVariables[$variable] || $this->globalVariables[$variable] || in_array($variable, $PREDEFINED_VARIABLES) || $this->hasConditionalVariable($variable)) {
  132.             return true;
  133.         } else {
  134.             if (!$this->undeclaredVariables[$variable]) {
  135.                 echo $this->file . ':' . $this->lineNo . ' - "' . $variable . '" not declared' . "\n";
  136.             }
  137.             $this->undeclaredVariables[$variable] = true;
  138.             return false;
  139.         }
  140.     }
  141.  
  142.     protected function matchVariable() {
  143.         $token = $this->tokens[$this->tokenIndex];
  144.         $variable = $token->contents;
  145.         if ($this->declaringFunction) {
  146.             $this->functionVariables[$variable] = true;
  147.         } else {
  148.             $nextToken = $this->skipWhitespace();    
  149.             if ($nextToken->contents == '=') {
  150.                 $this->declaringVariable = $variable;
  151.             } else {
  152.                 $this->checkVariable($variable);
  153.             }
  154.             $this->tokenIndex--;
  155.         }
  156.     }
  157.  
  158.     protected function declareVariable($variable) {
  159.         if ($this->inConditional()) {
  160.             $this->conditionalVariables[$variable] = true;
  161.         } elseif ($this->functionBraceLevel > 0) {
  162.             $this->functionVariables[$variable] = true;
  163.         } else {
  164.             $this->globalVariables[$variable] = true;
  165.         }
  166.         $this->declaringVariable = null;
  167.     }
  168.  
  169.     protected function matchForeachAs() {
  170.         $valToken = $this->skipWhitespace();
  171.         $nextToken = $this->skipWhitespace();
  172.         if ($nextToken->type == T_DOUBLE_ARROW) {
  173.             $keyToken = $valToken;
  174.             $valToken = $this->skipWhitespace();
  175.             $this->declareVariable($keyToken->contents);
  176.             $this->declareVariable($valToken->contents);
  177.         } else {
  178.             $this->declareVariable($valToken->contents);
  179.             $this->tokenIndex--;
  180.         }
  181.     }
  182.  
  183.     protected function matchClassVariable() {
  184.         $token = $this->skipWhitespace();
  185.         if ($token->type == T_VARIABLE) {
  186.             // we don't do anything with class variables
  187.             // since they are accessed through $this
  188.         } else {
  189.             $token->tokenIndex--;
  190.         }
  191.     }
  192.  
  193.     public function check($file) {
  194.         global $INCLUDE_TOKENS;
  195.         $this->file = $file;
  196.         $this->tokens = $this->fileGetTokens($file);
  197.         $this->tokenCount = count($this->tokens);
  198.         $this->tokenIndex = 0;
  199.         while ($this->tokenIndex < $this->tokenCount) {
  200.             $token = $this->tokens[$this->tokenIndex];
  201.             $newlineCount = substr_count($token->contents, "\n");
  202.             $this->lineNo += $newlineCount;
  203.             if ($token->type == T_FUNCTION) {
  204.                 $this->declaringFunction = true;
  205.             } elseif ($token->type == T_GLOBAL) {
  206.                 $this->registerGlobals();
  207.             } elseif ($token->type == T_IF || $token->type == T_ELSEIF) {
  208.                 $this->matchConditional();
  209.             } elseif ($token->contents == '{' || ($this->declaringConditional && $token->contents == ':')) {
  210.                 $this->matchOpenBrace();
  211.             } elseif ($token->contents == '}' || $token->type == T_ENDIF) {
  212.                 $this->matchCloseBrace();
  213.             } elseif ($token->type == T_AS) {
  214.                 $this->matchForeachAs();
  215.             } elseif ($this->declaringVariable && ($token->contents == ';' || $newlineCount > 0)) {
  216.                 $this->declareVariable($this->declaringVariable);
  217.             } elseif (in_array($token->type, array(T_PRIVATE, T_PROTECTED, T_PUBLIC, T_VAR))) {
  218.                 $this->matchClassVariable();
  219.             } elseif ($token->type == T_VARIABLE) {
  220.                 $this->matchVariable();
  221.             }
  222.             $this->tokenIndex++;
  223.         }
  224.         $this->files[] = $file;
  225.     }
  226. }
  227.  
  228. $file = $argv[1];
  229. $checker = new VariableChecker();
  230. $checker->check($file);
clone this paste RAW Paste Data