diff --git a/include/ajax.kbase.php b/include/ajax.kbase.php index d6d30a1b0476072e0d9464ff9dc8cfde00296db8..1e642847bba383d986bafeadadee6370bcec54ed 100644 --- a/include/ajax.kbase.php +++ b/include/ajax.kbase.php @@ -32,6 +32,7 @@ class KbaseAjaxAPI extends AjaxController { $ticket = Ticket::lookup($_GET['tid']); } + $resp = array(); switch($format) { case 'json': $resp['id'] = $canned->getId(); diff --git a/include/ajax.tickets.php b/include/ajax.tickets.php index c0f69e8a95c35c8546b6f9d67f9f1a7c4f314a5f..9910e27699590997a6528168bf291ca267058df3 100644 --- a/include/ajax.tickets.php +++ b/include/ajax.tickets.php @@ -236,6 +236,7 @@ class TicketsAjaxAPI extends AjaxController { function search() { $tickets = self::_search($_REQUEST); + $result = array(); if (count($tickets)) { $uid = md5($_SERVER['QUERY_STRING']); @@ -414,6 +415,8 @@ class TicketsAjaxAPI extends AjaxController { $ticket->getEmail()); echo ' </table>'; + + $options = array(); $options[]=array('action'=>'Thread ('.$ticket->getThreadCount().')','url'=>"tickets.php?id=$tid"); if($ticket->getNumNotes()) $options[]=array('action'=>'Notes ('.$ticket->getNumNotes().')','url'=>"tickets.php?id=$tid#notes"); diff --git a/include/class.api.php b/include/class.api.php index 6dec23efbc71969df796ecb8d9fc206f090b4129..23ae8a589e9192a08394134929ff9429695b2f30 100644 --- a/include/class.api.php +++ b/include/class.api.php @@ -396,7 +396,7 @@ class ApiJsonDataParser extends JsonDataParser { "name" => key($info), ); } - unset($value); + unset($info); } if (is_array($value)) { $value = $this->fixup($value); diff --git a/include/class.faq.php b/include/class.faq.php index 7cb3c9a950ee9e2461fabf79d5f8b3ec57c07f62..4445a7cf7c9a262f0faf68e1e5bfa228ea062da6 100644 --- a/include/class.faq.php +++ b/include/class.faq.php @@ -130,8 +130,9 @@ class FAQ { /* Same as update - but mainly called after one or more setters are changed. */ function apply() { + $errors = array(); //XXX: set errors and add ->getErrors() & ->getError() - return $this->update($this->ht, $errors); # nolint + return $this->update($this->ht, $errors); } function updateTopics($ids){ diff --git a/include/class.filter.php b/include/class.filter.php index 81c6a39fe17a86e8e5981385a0dd417d6e47955f..15c68cc3170596abef12fb44e1eaf4365b7c447c 100644 --- a/include/class.filter.php +++ b/include/class.filter.php @@ -175,11 +175,12 @@ class Filter { } function addRule($what, $how, $val,$extra=array()) { + $errors = array(); $rule= array_merge($extra,array('what'=>$what, 'how'=>$how, 'val'=>$val)); $rule['filter_id']=$this->getId(); - return FilterRule::create($rule,$errors); # nolint + return FilterRule::create($rule,$errors); } function removeRule($what, $how, $val) { @@ -512,7 +513,8 @@ class Filter { //Success with update/create...save the rules. We can't recover from any errors at this point. # Don't care about errors stashed in $xerrors - self::save_rules($id,$vars,$xerrors); # nolint + $xerrors = array(); + self::save_rules($id,$vars,$xerrors); return true; } diff --git a/include/class.mailparse.php b/include/class.mailparse.php index 08e2f92b04c38ba01caeb64d05ea3f9610ba462f..afc89008c32342f61c464c9962a5eabc4179cc16 100644 --- a/include/class.mailparse.php +++ b/include/class.mailparse.php @@ -60,7 +60,7 @@ class Mail_Parse { } function splitBodyHeader() { - + $match = array(); if (preg_match("/^(.*?)\r?\n\r?\n(.*)/s", $this->mime_message, $match)) { # nolint diff --git a/include/class.orm.php b/include/class.orm.php index 90557fa7677df8c44dc68125c97ad8bfd72a9b86..05201c6682bc38cb4579b14492e6f62c3d7c406e 100644 --- a/include/class.orm.php +++ b/include/class.orm.php @@ -139,6 +139,7 @@ class VerySimpleModel { function delete($pk=false) { $table = static::$meta['table']; $sql = 'DELETE FROM '.$table; + $filter = array(); if (!$pk) $pk = static::$meta['pk']; if (!is_array($pk)) $pk=array($pk); @@ -626,7 +627,7 @@ class SqlCompiler { } function compileWhere($where, $model) { - $constrints = array(); + $constraints = array(); foreach ($where as $constraint) { $filter = array(); foreach ($constraint as $field=>$value) { diff --git a/include/class.pdf.php b/include/class.pdf.php index b20eaf7ac53df9290a0532fafcb7ac62d466e491..cf17a2f7e4bc954c55a0bf807a3ff9b71f55b412 100644 --- a/include/class.pdf.php +++ b/include/class.pdf.php @@ -291,6 +291,7 @@ class Ticket2PDF extends FPDF if($entry['attachments'] && ($tentry=$ticket->getThreadEntry($entry['id'])) && ($attachments = $tentry->getAttachments())) { + $files = array(); foreach($attachments as $attachment) $files[]= $attachment['name']; diff --git a/include/class.thread.php b/include/class.thread.php index eca16c58e595f57d597c4535658e36fb13e8e163..ef1d24ed8fdd15dc79d9e0013aadfca541ffc816 100644 --- a/include/class.thread.php +++ b/include/class.thread.php @@ -403,9 +403,7 @@ Class ThreadEntry { return null; $id=0; - if (!$attachment['error'] && ($id=$this->saveAttachment($attachment))) - $files[] = $id; - else { + if ($attachment['error'] || !($id=$this->saveAttachment($attachment))) { $error = $attachment['error']; if(!$error) diff --git a/include/class.variable.php b/include/class.variable.php index 3a71bd66de4239c47439cbd196061d1557ebf0f6..36d49e6a39accdcdee68aaa06cd8059779474e00 100644 --- a/include/class.variable.php +++ b/include/class.variable.php @@ -121,7 +121,9 @@ class VariableReplacer { function _parse($text) { $input = $text; - if(!preg_match_all('/'.$this->start_delim.'([A-Za-z_][\w._]+)'.$this->end_delim.'/', $input, $result)) + $result = array(); + if(!preg_match_all('/'.$this->start_delim.'([A-Za-z_][\w._]+)'.$this->end_delim.'/', + $input, $result)) return null; $vars = array(); diff --git a/include/mysql.php b/include/mysql.php index 4e3bd7eb8caf242c92b3846fb1b7d5c845f31a87..9e8194827bd997f00d1d5f9a2bd4c61305e008a8 100644 --- a/include/mysql.php +++ b/include/mysql.php @@ -51,6 +51,7 @@ function db_version() { $version=0; + $matches = array(); if(preg_match('/(\d{1,2}\.\d{1,2}\.\d{1,2})/', mysql_result(db_query('SELECT VERSION()'),0,0), $matches)) # nolint @@ -129,6 +130,7 @@ } function db_assoc_array($res, $mode=false) { + $result = array(); if($res && db_num_rows($res)) { while ($row=db_fetch_array($res, $mode)) $result[]=$row; diff --git a/include/mysqli.php b/include/mysqli.php index 06d4032fdc5419f3ffd24bd5a0fe6abe2ea46fb4..9d942eb51e2d0d9b1d343a2d3b82090a0d23a211 100644 --- a/include/mysqli.php +++ b/include/mysqli.php @@ -77,6 +77,7 @@ function db_close() { function db_version() { $version=0; + $matches = array(); if(preg_match('/(\d{1,2}\.\d{1,2}\.\d{1,2})/', db_result(db_query('SELECT VERSION()')), $matches)) # nolint @@ -165,6 +166,7 @@ function db_fetch_field($res) { } function db_assoc_array($res, $mode=false) { + $result = array(); if($res && db_num_rows($res)) { while ($row=db_fetch_array($res, $mode)) $result[]=$row; diff --git a/setup/cli/modules/import.php b/setup/cli/modules/import.php index e0f23d57a1a11e8b8b8c24df592edc4b36cd992e..84c27396d4b790c4a5612c2146490e5da42d9090 100644 --- a/setup/cli/modules/import.php +++ b/setup/cli/modules/import.php @@ -113,6 +113,7 @@ class Importer extends Module { $sql = 'CREATE TABLE `'.TABLE_PREFIX.$info[1].'` ('; $pk = array(); $fields = array(); + $queries = array(); foreach ($info[2] as $col) { $field = "`{$col['Field']}` {$col['Type']}"; if ($col['Null'] == 'NO') diff --git a/setup/test/tests/class.php_analyze.php b/setup/test/tests/class.php_analyze.php new file mode 100644 index 0000000000000000000000000000000000000000..5ebcb37d524f5c9808bdaa4f5f4bf6f2b4328611 --- /dev/null +++ b/setup/test/tests/class.php_analyze.php @@ -0,0 +1,303 @@ +<?php +require_once('class.test.php'); + +$super_globals = array_fill_keys( + array('$_SERVER', '$_FILES', '$_SESSION', '$_GET', '$_POST', + '$_REQUEST', '$_ENV'), 1); + +class SourceAnalyzer extends Test { + var $bugs = array(); + var $globals = array(); + var $file = ''; + + function __construct($source) { + $this->tokens = token_get_all(file_get_contents($source)); + $this->file = $source; + } + + function parseFile() { + + $this->checkVariableUsage( + array('line'=>array(0, $this->file), 'name'=>'(main)'), + array(), + 1); + } + + function traverseClass($line) { + $class = array('line'=>$line); + $token = false; + $blocks = 0; + while (list($i,$token) = each($this->tokens)) { + switch ($token[0]) { + case '{': + $blocks++; + break; + case '}': + if (--$blocks == 0) + return; + break; + case T_STRING: + if (!isset($class['name'])) + $class['name'] = $token[1]; + break; + case T_FUNCTION: + $this->traverseFunction( + array($token[2], $line[1]), + array('allow_this'=>true)); + break; + case T_VAR: + // var $variable + // used inside classes to define instance variables + while (list(,$token) = each($this->tokens)) { + if (is_array($token) && $token[0] == T_VARIABLE) + // TODO: Add this to some class context in the + // future to support indefined access to $this->blah + break; + } + break; + } + } + } + + function traverseFunction($line=0, $options=array()) { + // Scan for function name + $function = array('line'=>$line, 'name'=>'(inline)'); + $token = false; + $scope = array(); + while ($token != "{") { + list(,$token) = each($this->tokens); + if (!is_array($token) + || $token[0] == T_WHITESPACE) + continue; + if ($token[0] == T_STRING) + $function['name'] = $token[1]; + elseif ($token[0] == T_VARIABLE) + $scope[$token[1]] = 1; + } + // Start inside a block -- we've already consumed the { + $this->checkVariableUsage($function, $scope, 1, $options); + } + + function checkVariableUsage($function, $scope=array(), $blocks=0, + $options=array()) { + global $super_globals; + + // Merge in defaults to the options array + $options = array_merge(array( + 'allow_this' => false, + ), $options); + // Unpack function[line][file] if set + if (is_array($function['line'])) + $function['file'] = $function['line'][1]; + while (list($i,$token) = each($this->tokens)) { + // Check variable usage and for nested blocks + switch ($token[0]) { + case '{': + $blocks++; + break; + case '}': + if (--$blocks == 0) + return; + break; + case T_VARIABLE: + // Look-ahead for assignment + $assignment = false; + while ($next = @$this->tokens[++$i]) + if (!is_array($next) || $next[0] != T_WHITESPACE) + break; + switch ($next[0]) { + case '=': + // For assignment, check if the variable is explictly + // assigned to NULL. If so, treat the assignment as an + // unset() + while ($next = @$this->tokens[++$i]) + if (!is_array($next) || $next[0] != T_WHITESPACE) + break; + if (is_array($next) && strcasecmp('NULL', $next[1]) === 0) { + $scope[$token[1]] = 'null'; + $assignment = true; + break; + } + case T_AND_EQUAL: + case T_CONCAT_EQUAL: + case T_DIV_EQUAL: + case T_MINUS_EQUAL: + case T_MOD_EQUAL: + case T_MUL_EQUAL: + case T_OR_EQUAL: + case T_PLUS_EQUAL: + case T_SL_EQUAL: + case T_SR_EQUAL: + case T_XOR_EQUAL: + $assignment = true; + $scope[$token[1]] = 1; + break; + } + if ($assignment) + break; + + if (!isset($scope[$token[1]])) { + if ($token[1] == '$this' && $options['allow_this']) { + // Always valid in a non-static class method + // TODO: Determine if this function is defined in a class + break; + } + elseif (isset($super_globals[$token[1]])) + // Super globals are always in scope + break; + elseif (!isset($function['name']) || $function['name'] == '(main)') + // Not in a function. Cowardly continue. + // TODO: Recurse into require() statements to + // determine if global variables are actually + // defined somewhere in the code base + break; + $this->bugs[] = array( + 'type' => 'UNDEF_ACCESS', + 'func' => $function['name'], + 'line' => array($token[2], $function['file']), + 'name' => $token[1], + ); + } + elseif ($scope[$token[1]] == 'null') { + // See if the next token is accessing a property of the + // object + $c = current($this->tokens); + switch ($c[0]) { + case T_OBJECT_OPERATOR: + case T_PAAMAYIM_NEKUDOTAYIM: + case '[': + $this->bugs[] = array( + 'type' => 'MAYBE_UNDEF_ACCESS', + 'func' => $function['name'], + 'line' => array($token[2], $function['file']), + 'name' => $token[1], + ); + } + } + break; + case T_PAAMAYIM_NEKUDOTAYIM: + // Handle static variables $instance::$static + $current = current($this->tokens); + if ($current[0] == T_VARIABLE) + next($this->tokens); + break; + case T_CLASS: + // XXX: PHP does not allow nested classes + $this->traverseClass( + array($token[2], $function['file'])); + break; + case T_FUNCTION: + // PHP does not automatically nest scopes. Variables + // available inside the closure must be explictly defined. + // Therefore, there is no need to pass the current scope. + // However, $this is not allowed inside inline functions + // unless declared in the use () parameters. + $this->traverseFunction( + array($token[2], $function['file']), + array('allow_this'=>false)); + break; + case T_STATIC: + $c = current($this->tokens); + // static::func() or static::$var + if ($c[0] == T_PAAMAYIM_NEKUDOTAYIM) + break; + case T_GLOBAL: + while (list(,$token) = each($this->tokens)) { + if ($token == ';') + break; + elseif (!is_array($token)) + continue; + elseif ($token[0] == T_VARIABLE) + $scope[$token[1]] = 1; + } + break; + case T_FOR: + // for ($i=0;...) + // Find first semi-colon, variables defined before it should + // be added to the current scope + while (list(,$token) = each($this->tokens)) { + if ($token == ';') + break; + elseif (!is_array($token)) + continue; + elseif ($token[0] == T_VARIABLE) + $scope[$token[1]] = 1; + } + break; + case T_FOREACH: + // foreach ($blah as $a=>$b) -- add $a, $b to the local + // scope + $after_as = false; + $parens = 0; + // Scan for the variables defined for the scope of the + // foreach block + while (list(,$token) = each($this->tokens)) { + if ($token == '(') + $parens++; + elseif ($token == ')' && --$parens == 0) + break; + elseif (!is_array($token)) + continue; + elseif ($token[0] == T_AS) + $after_as = true; + elseif ($after_as && $token[0] == T_VARIABLE) + // Technically, variables defined in a foreach() + // block are still accessible after the completion + // of the foreach block + $scope[$token[1]] = 1; + } + break; + case T_LIST: + // list($a, $b) = ... + // Find all variables defined up to the closing parenthesis + while (list(,$token) = each($this->tokens)) { + if ($token == ')') + break; + elseif (!is_array($token)) + continue; + elseif ($token[0] == T_VARIABLE) + $scope[$token[1]] = 1; + } + break; + case T_ISSET: + // isset($var) + // $var is allowed to be undefined and not be an error. + // Consume tokens until close parentheses + while (list(,$token) = each($this->tokens)) { + if ($token == ')') + break; + } + break; + case T_UNSET: + // unset($var) + // Var will no longer be in scope + while (list(,$token) = each($this->tokens)) { + if ($token == ')') + break; + elseif (is_array($token) && $token[0] == T_VARIABLE) { + // Check for unset($var[key]) -- don't unset anything + // Check for unset($this->blah) + $next = current($this->tokens); + switch ($next[0]) { + case '[': + case T_OBJECT_OPERATOR: + break; + default: + unset($scope[$token[1]]); + } + break; + } + } + break; + case T_DOLLAR_OPEN_CURLY_BRACES: + case T_CURLY_OPEN: + // "{$a .. }" + // This screws up block detection. We will see another close + // brace somewhere along the way + $blocks++; + break; + } + } + } +} +?> diff --git a/setup/test/tests/class.test.php b/setup/test/tests/class.test.php index 5ce1297619714533c19bdfd7f2c083b4b9d255b8..de9c4f7e9da3682beaf3d244bb18778f19eda664 100644 --- a/setup/test/tests/class.test.php +++ b/setup/test/tests/class.test.php @@ -2,6 +2,7 @@ class Test { var $fails = array(); + var $warnings = array(); var $name = ""; var $third_party_paths = array( @@ -57,7 +58,7 @@ class Test { } function warn($message) { - $this->fails[] = array(get_class($this), '', '', 'WARNING: '.$message); + $this->warnings[] = array(get_class($this), '', '', 'WARNING: '.$message); fputs(STDOUT, 'w'); } diff --git a/setup/test/tests/lib/phplint.tcl b/setup/test/tests/lib/phplint.tcl deleted file mode 100755 index 30e10074baba6f31705c1c0b152995b33f564f66..0000000000000000000000000000000000000000 --- a/setup/test/tests/lib/phplint.tcl +++ /dev/null @@ -1,217 +0,0 @@ -#!/usr/bin/tclsh - -# Copyright (C) 2007 Salvatore Sanfilippo <antirez at gmail dot com> -# This software is released under the GPL license version 2 - -proc scan file { - set fd [open $file] - set infunc 0 - set linenr 0 - set fnre {(^\s*)((public|private|protected|static)\s*)*function(?=\s|\()\s*([^(]+)?\s*\(((\(\)|[^)])*)\)(\s*use\s*\((.*)\))?} - while {[gets $fd line] != -1} { - incr linenr - if {[regexp $fnre $line - ind - - - fa - - ua]} { - # If $infunc is true we miss the end of the last function - # so we analyze it now. - if {$infunc} { - analyze $file $arglist $body - } - set body {} - set arglist {} - foreach arg [split $fa ,] { - # remove default value - regsub {=.*} $arg {} arg - # remove optional type spec - regsub {^.*\s+} [string trim $arg] {} arg - set arg [string trim $arg " $&"] - lappend arglist $arg - } - # Support closure variables - foreach arg [split $ua ,] { - set arg [string trim $arg " $"] - lappend arglist $arg - } - set infunc 1 - } elseif {$infunc && [regexp "^$ind\}" $line]} { - set infunc 0 - analyze $file $arglist $body - } elseif {$infunc} { - lappend body $linenr [string trim $line] - } - } -} - -proc analyze {file arglist body} { - set initialized(this) 1 - set linton 1 - foreach arg $arglist { - set initialized($arg) 1 - } - # Superglobals - set superglobals { - "GLOBALS" - "_SESSION" - "_SESSION" - "_GET" - "_POST" - "_REQUEST" - "_ENV" - "_SERVER" - "_FILES" - "php_errormsg" - } - foreach sg $superglobals { - set initialized($sg) 1 - } - # analyze body - foreach {linenr line} $body { - # Handle annotations - if {[string first {nolint} [string tolower $line]] != -1} continue - if {[string first {linton} [string tolower $line]] != -1} { - if {$linton == 1} { - puts "! Warning 'linton' annotation with lint already ON" - continue - } - set linton 1 - puts ". $skipped lines skipped in $file from line $skipstart" - } - if {[string first {lintoff} [string tolower $line]] != -1} { - if {$linton == 0} { - puts "! Warning 'lintoff' annotation with lint already OFF" - continue - } - set linton 0 - set skipped 0 - set skipstart [expr {$linenr+1}] - continue - } - if {$linton == 0} { - incr skipped - continue - } - # Skip comments - if {[string index $line 0] eq {#}} continue - if {[string index $line 0] eq {/} && [string index $line 1] eq {/}} continue - # PHP variable regexp - set varre {\$[_A-Za-z]+[_A-Za-z0-9]*(\[[^\]]*\])*} - # Check for globals - set re {\s*(global|static)\s+((?:\$[^;,]+[ ,]*)+)(;|$)} - if {[regexp $re $line -> - g]} { - set g [split [string trim $g ";"] ,] - foreach v $g { - set v [string trim $v "$ "] - set initialized($v) 1 - } - } - # Check for assignment via foreach ... as &$varname - set re {} - append re {foreach\s*\(.*\s+as\s+&?(} $varre {)\s*\)} - set l [regexp -all -inline -nocase $re $line] - foreach {- a -} $l { - set initialized([string trim $a "$ "]) 1 - } - # Check for assignment via foreach ... as $key => &$val - set re {} - append re {foreach\s*\(.*\s+as\s+(} $varre {)\s*=>\s*&?(} $varre {)\s*\)} - set l [regexp -all -inline -nocase $re $line] - foreach {- a1 - a2 -} $l { - set initialized([string trim $a1 "$ "]) 1 - set initialized([string trim $a2 "$ "]) 1 - } - # Check for assigments in the form list($a,$b,$c) = ... - set re {list\s*\(([^=]*)\)\s*=} - set l [regexp -all -inline $re $line] - foreach {- vars} $l { - foreach v [split $vars ,] { - set v [string trim $v "$ "] - set initialized($v) 1 - } - } - # Check for assigments via = operator - set re $varre - append re {\s*=} - set l [regexp -all -inline $re $line] - foreach {a -} $l { - set a [string trim $a "=$ "] - regsub -all {\[.*\]} $a {[]} a - #puts "assigmnent of $a" - set initialized($a) 1 - regsub -all {\[\]} $a {} a - set initialized($a) 1 - } - # Check for assignments via catch(Exception $e) - set re {} - append re {catch\s*\(.*\s+(} $varre {)} - set l [regexp -all -inline -nocase $re $line] - foreach {- a} $l { - set initialized([string trim $a "$ "]) 1 - } - # Check for assignments by reference - # - # funclist format is {type funcname spos epos} where spos is the - # zero-based index of the first argument that can be considered - # an assignment, while epos is the last. - # - # name is the function name to match, and type is what - # to do with the args. "assignment" to consider them assigned - # or "ingore" to ingore them for the current line. - # - # The "ignore" is used for isset() and other functions that can - # deal with not initialized vars. - unset -nocomplain -- ignore - array set ignore {} - set funclist { - assignment scanf 2 100 - assignment preg_match 2 100 - assignment preg_match_all 2 100 - assignment ereg 2 100 - ignore isset 0 0 - } - set cline $line - regsub -all {'[^']+'} $cline {''} cline - foreach {type name spos epos} $funclist { - set re {} - append re $name {\s*\(([^()]*)\)} - foreach {- fargs} [regexp -all -inline $re $cline] { - set argidx 0 - foreach a [split $fargs ,] { - set a [string trim $a ", $"] - regsub -all {\[.*\]} $a {} a - if {$argidx >= $spos && $argidx <= $epos} { - if {$type eq {assignment}} { - set initialized($a) 1 - } elseif {$type eq {ignore}} { - set ignore($a) 1 - } - } - incr argidx - } - } - } - - # Check for var accesses - set varsimplere {(?:::)?\$[_A-Za-z]+[_A-Za-z0-9]*} - set l [regexp -all -inline $varsimplere $line] - foreach a $l { - set a [string trim $a "=$ "] - regsub -all {\[.*\]} $a {} a - # Skip access to class-static variables - if {[string first :: $a] == 0} { - continue - } - #puts "access of $a" - if {![info exists initialized($a)] && - ![info exists ignore($a)]} { - puts "* In $file line $linenr: access to uninitialized var '$a'" - } - } - } -} - -proc main argv { - foreach file $argv { - scan $file - } -} - -main $argv diff --git a/setup/test/tests/test.unitialized.php b/setup/test/tests/test.unitialized.php index 09bd0509e155072ccb6044429248965c2e318b36..47978fdb2fd9741bd2c4f554d527795a4927cbed 100644 --- a/setup/test/tests/test.unitialized.php +++ b/setup/test/tests/test.unitialized.php @@ -1,5 +1,6 @@ <?php require_once "class.test.php"; +require_once "class.php_analyze.php"; class UnitializedVars extends Test { var $name = "Access to unitialized variables"; @@ -7,16 +8,21 @@ class UnitializedVars extends Test { function testUnitializedUsage() { $scripts = $this->getAllScripts(); $matches = array(); - foreach (range(0, count($scripts), 40) as $start) { - $slice = array_slice($scripts, $start, 40); - ob_start(); - # XXX: This won't run well on Windoze - system(dirname(__file__)."/lib/phplint.tcl ".implode(" ", $slice)); - $lint_errors = ob_get_clean(); - preg_match_all("/\* In (.*) line (\d+): access to uninitialized var '([^']+)'/m", - $lint_errors, $matches, PREG_SET_ORDER); - foreach ($matches as $match) - $this->fail($match[1], $match[2], "'\${$match[3]}'"); + foreach ($scripts as $s) { + $a = new SourceAnalyzer($s); + $a->parseFile(); + foreach ($a->bugs as $bug) { + if ($bug['type'] == 'UNDEF_ACCESS') { + list($line, $file) = $bug['line']; + $this->fail($file, $line, "'{$bug['name']}'"); + } + elseif ($bug['type'] == 'MAYBE_UNDEF_ACCESS') { + list($line, $file) = $bug['line']; + $this->warn("Possible access to NULL object @ $file : $line"); + } + } + if (!$a->bugs) + $this->pass(); } } }