Polished up and enabled the undefined variable scan by default Mantis: 2691
git-svn-id: file:///srv/svn/scanner/trunk@20 a0501263-5b7a-4423-a8ba-1edf086583e7
This commit is contained in:
parent
08bc427e4a
commit
26d9f61672
4 changed files with 55 additions and 10 deletions
|
@ -9,25 +9,40 @@ class VariableModule extends ScannerModule {
|
||||||
function parserCallback( $object ) {
|
function parserCallback( $object ) {
|
||||||
$pattern = '/\$[a-zA-Z_\x7f-\xff][a-zA-Z0-9_\x7f-\xff]*/';
|
$pattern = '/\$[a-zA-Z_\x7f-\xff][a-zA-Z0-9_\x7f-\xff]*/';
|
||||||
$matches = array();
|
$matches = array();
|
||||||
|
$variable = preg_match($pattern, $object['name'], $matches) > 0 ? $matches[0] : false;
|
||||||
$scope = "{$object['in_class']}::{$object['in_function']}";
|
$scope = "{$object['in_class']}::{$object['in_function']}";
|
||||||
if (!isset($this->assigned_variables[$scope] ) )
|
if (!isset($this->assigned_variables[$scope] ) )
|
||||||
$this->assigned_variables[$scope] = array();
|
$this->assigned_variables[$scope] = array();
|
||||||
if ($object['type'] == PHPPARSER_ASSIGNMENT) {
|
if ($object['type'] == PHPPARSER_ASSIGNMENT) {
|
||||||
|
list($var, $value) = explode('=', $object['name']);
|
||||||
|
if ($variable == $var) {
|
||||||
|
// Regular variable assignment
|
||||||
//$this->fault($object, 0, "Assignment: {$object['name']}");
|
//$this->fault($object, 0, "Assignment: {$object['name']}");
|
||||||
list($variable, $value) = explode('=', $object['name']);
|
$this->assigned_variables[$scope][] = $var;
|
||||||
|
} else {
|
||||||
|
// Array index assignment
|
||||||
|
//$this->fault($object, 0, "Array index assignment: [{$variable}] {$object['name']}");
|
||||||
|
if (
|
||||||
|
!in_array($variable, $this->assigned_variables[$scope])
|
||||||
|
&& (empty($object['in_class']) && $variable == '$this')
|
||||||
|
) {
|
||||||
|
$this->fault($object, FAULT_MINOR, "Array key assignment on previously undefined variable: $var");
|
||||||
|
}
|
||||||
$this->assigned_variables[$scope][] = $variable;
|
$this->assigned_variables[$scope][] = $variable;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
if (
|
if (
|
||||||
$object['type'] == PHPPARSER_VARIABLE
|
$object['type'] == PHPPARSER_VARIABLE
|
||||||
// Cannot yet accurately scan the global scope, so functions only
|
// Cannot yet accurately scan the global scope, so functions only
|
||||||
&& !empty($object['in_function'])
|
&& !empty($object['in_function'])
|
||||||
&& !in_array($object['name'], $this->assigned_variables[$scope])
|
&& !in_array($variable, $this->assigned_variables[$scope])
|
||||||
&& !in_array($object['name'], array(
|
&& !in_array($variable, array(
|
||||||
// Superglobals are exempt, obviously
|
// Superglobals are exempt, obviously
|
||||||
'$GLOBALS', '$_SERVER', '$_GET', '$_POST', '$_FILES', '$_COOKIE', '$_SESSION', '$_REQUEST', '$_ENV'
|
'$GLOBALS', '$_SERVER', '$_GET', '$_POST', '$_FILES', '$_COOKIE', '$_SESSION', '$_REQUEST', '$_ENV'
|
||||||
))
|
))
|
||||||
|
&& (empty($object['in_class']) && $variable == '$this')
|
||||||
) {
|
) {
|
||||||
$this->fault($object, FAULT_MEDIUM, "Undefined Variable: {$object['name']}");
|
$this->fault($object, FAULT_MEDIUM, "Undefined Variable: $variable");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
34
parser.php
34
parser.php
|
@ -89,14 +89,16 @@ class PHPParser {
|
||||||
$function = $functionname = null;
|
$function = $functionname = null;
|
||||||
$string = $last_token = null;
|
$string = $last_token = null;
|
||||||
$switch = array();
|
$switch = array();
|
||||||
$expression = '';
|
$expressions = array('');
|
||||||
|
$expression = &$expressions[0];
|
||||||
$assignment = false;
|
$assignment = false;
|
||||||
$line_text = '';
|
$line_text = '';
|
||||||
|
|
||||||
$internal_functions = get_defined_functions(); $internal_functions = $internal_functions['internal'];
|
$internal_functions = get_defined_functions(); $internal_functions = $internal_functions['internal'];
|
||||||
$local_functions = array();
|
$local_functions = array();
|
||||||
$local_classes = array();
|
$local_classes = array();
|
||||||
$variables = array();
|
$expression_variables = array(array());
|
||||||
|
$variables = &$expression_variables[0];
|
||||||
$open_blocks = array( 0 );
|
$open_blocks = array( 0 );
|
||||||
$in_string = false;
|
$in_string = false;
|
||||||
$buffer = array(T_INLINE_HTML => '');
|
$buffer = array(T_INLINE_HTML => '');
|
||||||
|
@ -168,6 +170,25 @@ class PHPParser {
|
||||||
$in_foreach_params = false;
|
$in_foreach_params = false;
|
||||||
$in_list = false;
|
$in_list = false;
|
||||||
break;
|
break;
|
||||||
|
case '[':
|
||||||
|
$expressions[] = '';
|
||||||
|
$expression_variables[] = array();
|
||||||
|
$variables = &$expression_variables[count($expression_variables) - 1];
|
||||||
|
break;
|
||||||
|
case ']':
|
||||||
|
/*
|
||||||
|
TODO: Subexpression reporting is NOT quite as accurate as it should be...
|
||||||
|
See the unit test failure for an example. The assigned variable is reported fine, which is good,
|
||||||
|
but the value it is assigned will not be. Since this isn't awfully important, and the fact that
|
||||||
|
everything else does is, I'm leaving this as-is for now.
|
||||||
|
*/
|
||||||
|
$e = array_pop($expressions);
|
||||||
|
$expression = &$expressions[count($expressions) - 1];
|
||||||
|
$expression .= trim($e);
|
||||||
|
array_pop($expression_variables);
|
||||||
|
$variables = &$expression_variables[count($expression_variables) - 1];
|
||||||
|
$variables[count($variables) - 1] = end($variables) . '[' . trim($e) . ']';
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
/* Should be able to add a hook here later on to catch the use of defines,
|
/* Should be able to add a hook here later on to catch the use of defines,
|
||||||
which are basically just T_STRINGs that PHP can't find anything else to
|
which are basically just T_STRINGs that PHP can't find anything else to
|
||||||
|
@ -236,6 +257,8 @@ class PHPParser {
|
||||||
) );
|
) );
|
||||||
}
|
}
|
||||||
} else { $expression .= $token; }
|
} else { $expression .= $token; }
|
||||||
|
if ($expression !== end($expressions))
|
||||||
|
$expression = &$expressions[count($expressions) - 1];
|
||||||
if( !empty($buffer[T_INLINE_HTML]) && (bool)($this->fetch_mode & PHPPARSER_FETCH_INLINE_HTML) ) {
|
if( !empty($buffer[T_INLINE_HTML]) && (bool)($this->fetch_mode & PHPPARSER_FETCH_INLINE_HTML) ) {
|
||||||
$this->foundObject( array(
|
$this->foundObject( array(
|
||||||
'type' => PHPPARSER_INLINE_HTML,
|
'type' => PHPPARSER_INLINE_HTML,
|
||||||
|
@ -336,6 +359,13 @@ class PHPParser {
|
||||||
break;
|
break;
|
||||||
case T_VARIABLE:
|
case T_VARIABLE:
|
||||||
if ($in_list) {
|
if ($in_list) {
|
||||||
|
/*
|
||||||
|
TODO: Proper fix for this.
|
||||||
|
Has to do with the less-than-perfect sub-expressions. If an entry in a list() is a keyed value of an array (i.e. $var[$key]),
|
||||||
|
everything sort of falls apart.
|
||||||
|
*/
|
||||||
|
if (!is_array($variables[count($variables) - 1]) )
|
||||||
|
$variables[count($variables) - 1] = array(end($variables));
|
||||||
$variables[count($variables) - 1][] = $text;
|
$variables[count($variables) - 1][] = $text;
|
||||||
} else {
|
} else {
|
||||||
$variables[] = $text;
|
$variables[] = $text;
|
||||||
|
|
|
@ -266,7 +266,7 @@ for( $i = 1; $i < $argc; $i++ ) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (empty($config['modules'])) $config['modules'] = array('lint', 'functions', 'pattern');
|
if (empty($config['modules'])) $config['modules'] = array('lint', 'functions', 'variables', 'pattern');
|
||||||
|
|
||||||
if( count( $files ) == 0 ) {
|
if( count( $files ) == 0 ) {
|
||||||
if( count( $revisions ) > 0 ) {
|
if( count( $revisions ) > 0 ) {
|
||||||
|
|
|
@ -116,7 +116,7 @@ class ParserTests extends PHPUnit_Framework_TestCase {
|
||||||
'$hash["answer"]=42',
|
'$hash["answer"]=42',
|
||||||
'$data[$index[0]]="complex"',
|
'$data[$index[0]]="complex"',
|
||||||
'$index=0',
|
'$index=0',
|
||||||
'$data[$index = 0]="nested"',
|
'$data[0]="nested"',
|
||||||
);
|
);
|
||||||
$this->assertEquals($expected, $assignments);
|
$this->assertEquals($expected, $assignments);
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue