<?php
//! $this->method(), A::method(), extends, self

/** Recursive function checking the variables initialization
* @param string $filename name of the processed file
* @param array [$initialized] initialized variables in keys
* @param string [$function] inside a function definition
* @param string [$class] inside a class definition
* @param array [$tokens] result of token_get_all() without whitespace, computed from $filename if null
* @param int [$i] position in $tokens
* @return mixed $initialized in the end of code, $i in the end of block
* @link http://code.google.com/p/php-initialized/
* @author Jakub Vrana, http://php.vrana.cz
* @copyright 2008 Jakub Vrana
* @license http://www.apache.org/licenses/LICENSE-2.0 Apache License, Version 2.0
*/
function check_variables_ex($filename$initialized = array(), $function ""$class ""$tokens null$i 0) {
    static $function_globals = array(), $function_parameters = array(), $function_calls = array();
    static 
$globals = array('$php_errormsg''$_SERVER''$_GET''$_POST''$_COOKIE''$_FILES''$_ENV''$_REQUEST''$_SESSION'); // not $GLOBALS
    
if (!isset($tokens)) {
        
$tokens = array();
        foreach (
token_get_all(file_get_contents($filename)) as $token) {
            if (!
in_array($token[0], array(T_WHITESPACET_COMMENTT_DOC_COMMENT), true)) {
                
$tokens[] = $token;
            }
        }
    }
    
$in_list false;
    for (; 
$i count($tokens); $i++) {
        
$token $tokens[$i];
        //~ echo (is_array($token) ? token_name($token[0]) . "\t" . trim($token[1]) : "\t$token") . "\n";
        //~ continue;
        
        // variables
        if ($token[0] === T_VARIABLE && $token[1] !== '$GLOBALS') {
            
$variable $token[1];
            if (isset(
$function_globals[$function][$variable])) {
                if (!
$function_globals[$function][$variable]) {
                    
$function_globals[$function][$variable] = ($in_list || $tokens[$i+1] === '=' true "$filename on line $token[2]");
                }
            } elseif (
$in_list || $tokens[$i+1] === '=' || $function_calls[count($function_calls) - 1][0]) {
                
$initialized[$variable] = true;
            } elseif (!isset(
$initialized[$variable]) && !in_array($variable$globals)) {
                if (isset(
$function_parameters[$function][$variable])) {
                    
$function_parameters[$function][$variable] = false;
                } else {
                    echo 
"Uninitialized variable $token[1] in $filename on line $token[2]\n";
                    
$initialized[$variable] = true;
                }
            }
        } elseif (
$token[0] === T_LIST || $token[0] === T_UNSET) {
            
$in_list true;
        } elseif (
$in_list && $token === ')') {
            
$in_list false;
        
        
// foreach
        } elseif ($token[0] === T_AS) {
            
$locals = array();
            do {
                $i++;
                if (
$tokens[$i][0] === T_VARIABLE) {
                    
$locals[$tokens[$i][1]] = true;
                }
            } while (
$tokens[$i+1] !== '{'); //! allow single commands
            
array_pop($function_calls);
            
$i check_variables_ex($filename$initialized $locals$function$class$tokens$i+2);
        
        
// catch
        } elseif ($token[0] === T_CATCH) {
            
$locals = array();
            do {
                $i++;
                if (
$tokens[$i][0] === T_VARIABLE) {
                    
$locals[$tokens[$i][1]] = true;
                }
            } while (
$tokens[$i+1] !== '{');
            
array_pop($function_calls);
            
$i check_variables_ex($filename$initialized $locals$function$class$tokens$i+2);
        
        
// global
        } elseif ($token[0] === T_GLOBAL && $function) {
            do {
                $i++;
                if (
$tokens[$i][0] === T_VARIABLE) {
                    
$function_globals[$function][$tokens[$i][1]] = false;
                }
            } while (
$tokens[$i] !== ';');
        
        
// static
        } elseif ($token[0] === T_STATIC && $tokens[$i+1][0] !== T_FUNCTION && $tokens[$i+2][0] !== T_FUNCTION) {
            do {
                $i++;
                if (
$function && $tokens[$i][0] === T_VARIABLE) {
                    
$initialized[$tokens[$i][1]] = true;
                }
            } while (
$tokens[$i] !== ';');
        
        
// function definition
        } elseif ($token[0] === T_FUNCTION && !in_array(T_ABSTRACT, array($tokens[$i-1][0], $tokens[$i-2][0], $tokens[$i-3][0]), true)) {
            
$locals = ($class && $tokens[$i-1][0] !== T_STATIC && $tokens[$i-2][0] !== T_STATIC ? array('$this' => true) : array());
            
$i++;
            if (
$tokens[$i] === '&') {
                $i++;
            }
            $name = ($class "$class::" "") . $tokens[$i][1];
            
$function_parameters[$name] = array();
            do {
                $i++;
                if (
$tokens[$i][0] === T_VARIABLE) {
                    
$function_parameters[$name][$tokens[$i][1]] = ($tokens[$i-1] === '&');
                    if (
$tokens[$i-1] !== '&') {
                        
$locals[$tokens[$i][1]] = true;
                    }
                }
            } while (
$tokens[$i+1] !== '{');
            
$i check_variables_ex($filename$locals$name, ($function "" $class), $tokens$i+2);
        
        
// function call
        } elseif ($token[0] === T_STRING && $tokens[$i+1] === '(') {
            
$i++;
            if (
function_exists($token[1])) {
                
$reflection = new ReflectionFunction($token[1]);
                
$parameters = array();
                foreach (
$reflection->getParameters() as $parameter) {
                    
$parameters[] = ($parameter->isPassedByReference() ? '$' $parameter->getName() : '');
                }
                
$function_calls[] = $parameters;
            } else {
                
$function_calls[] = array_values((array) $function_parameters[$token[1]]);
                if (!
$function && is_array($function_globals[$token[1]])) {
                    foreach (
$function_globals[$token[1]] as $variable => $info) {
                        if (
$info === true) {
                            
$initialized[$variable] = true;
                        } elseif (
is_string($info) && !isset($initialized[$variable])) {
                            echo 
"Uninitialized global $variable in $info\n: called in $filename on line $token[2]\n";
                        }
                    }
                }
            }
        
        
// class
        } elseif ($token[0] === T_CLASS) {
            
$i++;
            
$token $tokens[$i];
            while (
$tokens[$i+1] !== '{') {
                $i++;
            }
            $i check_variables_ex($filename, array(), $function$token[1], $tokens$i+2);
        } elseif (
$token[0] === T_VAR || (in_array($token[0], array(T_PUBLICT_PRIVATET_PROTECTED), true) && $tokens[$i+1][0] === T_VARIABLE)) {
            do {
                $i++;
            } while (
$tokens[$i] !== ';');
        
        
// include
        } elseif (in_array($token[0], array(T_INCLUDET_REQUIRET_INCLUDE_ONCET_REQUIRE_ONCE), true)) {
            if (
$tokens[$i+1][0] === T_CONSTANT_ENCAPSED_STRING && $tokens[$i+2] === ';') {
                
$initialized += check_variables_ex(stripslashes(substr($tokens[$i+1][1], 1, -1)), $initialized$function$class);
            }
        
        
// interface
        } elseif ($token[0] === T_INTERFACE) {
            while (
$tokens[$i+1] !== '}') {
                
$i++;
            }
        
        
// halt_compiler
        } elseif ($token[0] === T_HALT_COMPILER) {
            return 
$initialized;
        
        
// blocks
        } elseif ($token === '(') {
            
$function_calls[] = array();
        } elseif (
$token === ')') {
            
array_pop($function_calls);
        } elseif (
$token === ',' && $function_calls) {
            if (
$function_calls[count($function_calls) - 1][0] !== '$...') {
                
array_shift($function_calls[count($function_calls) - 1]);
            }
        } elseif (
$token === '{') {
            
$i check_variables_ex($filename$initialized$function$class$tokens$i+1);
        } elseif (
$token === '}') {
            return 
$i;
        }
        
    }
    return 
$initialized;
}

/** Print usage of uninitialized variables
* @param string $filename name of the processed file
* @return array properly initialized variables
*/
function check_variables($filename) {
    $old_error error_reporting(E_ALL & ~E_NOTICE);
    
$return check_variables_ex($filename);
    
error_reporting($old_error);
    return 
$return;
}