scanner/flist2.php
Correl Roush 4244ed6d8b Importing code scanner
git-svn-id: file:///srv/svn/scanner/trunk@1 a0501263-5b7a-4423-a8ba-1edf086583e7
2007-12-20 04:49:58 +00:00

305 lines
No EOL
10 KiB
PHP

<?php
require_once( 'parser.php' );
function microtime_float()
{
list($usec, $sec) = explode(" ", microtime());
return ((float)$usec + (float)$sec);
}
$stderr = fopen( 'php://stderr', 'w' );
function err( $string ) {
global $stderr;
fputs( $stderr, $string );
}
$time_start = microtime_float();
$CODE_PATH = !empty( $argv[1] ) ? $argv[1] : '/home/correlr/code/correl';
$SCAN_PATH = !empty( $argv[2] ) ? trim( $argv[2] ) : '';
if( !empty( $SCAN_PATH ) ) { $SCAN_PATH = "/$SCAN_PATH"; }
if( !file_exists( "{$CODE_PATH}{$SCAN_PATH}" ) ) { die( "Bad scan path\n" ); }
$php_files = `find $CODE_PATH -name '*.php'`;
$php_files = split( "\n", $php_files );
$parser = new PHPParser( PHPPARSER_FETCH_FUNCTIONS );
$local_parser = new PHPParser( PHPPARSER_FETCH_FUNCTIONS );
err( "Parsing Libs\n" );
$counter = 0; $total = count( $php_files ); $lastpct = 0;
foreach( $php_files as $file ) {
$counter++;
if( $counter == 1 ) { err( 0 ); }
else {
$pct = intval( $counter / $total * 100 );
if( $pct != $lastpct && $pct % 2 == 0 ) {
err( $pct % 10 == 0 ? $pct : '.' );
$lastpct = $pct;
}
}
$parser->parseFile( $file );
} err( "\n" );
$functions = array();
$all_local_functions = array();
foreach( $parser->parsed_objects as $function ) {
$file = str_replace( "$CODE_PATH/", '', $function['file'] );
if( strpos( $file, 'libs/' ) === 0 ) {
$functions[$function['name']] = array( 'used' => 0, 'file' => $function['file'] );
}
if( !is_array( $all_local_functions[$file] ) ) { $all_local_functions[$file] = array(); }
$all_local_functions[$file][] = $function['name'];
}
$functions['eval'] = array( 'used' => 0, 'file' => 'Evil Eval' );
$php_files = `find {$CODE_PATH}{$SCAN_PATH} -name '*.php'`;
$php_files = split( "\n", $php_files );
$counters = array( 'files' => 0, 'errors' => 0, 'warnings' => 0, 'failed' => 0 );
$file_requires = array();
err( "Parsing Files\n" );
$parser->reset( PHPPARSER_FETCH_INCLUDES + PHPPARSER_FETCH_CALLS + PHPPARSER_FETCH_INTERNAL );
$counter = 0; $total = count( $php_files ); $lastpct = 0;
foreach( $php_files as $file ) {
$counter++;
if( $counter == 1 ) { err( 0 ); }
else {
$pct = intval( $counter / $total * 100 );
if( $pct != $lastpct && $pct % 2 == 0 ) {
err( $pct % 10 == 0 ? $pct : '.' );
$lastpct = $pct;
}
}
$file = trim( $file );
if( empty( $file ) ) { continue; }
$filename = $file;
$file = str_replace( "$CODE_PATH/", '', $file );
$file_requires[$file] = array( 'parsed' => false, 'bad' => 0, 'warning' => 0, 'libs' => array(), 'errors' => array() );
// If the file has bad syntax, don't even bother with it
$output = array();
exec( "php -l '$filename'", $output, $result );
if( $result != 0 ) {
$counters['failed']++;
foreach( $output as $linterror ) {
$matches = array();
if( preg_match( '/error:.*?on line (\d+)$/i', $linterror, $matches ) == 0 ) { continue; }
$file_requires[$file]['errors'][] = array( 'line' => $matches[1], 'message' => $matches[0] );
}
continue;
}
$file_requires[$file]['parsed'] = true;
$local_functions = isset( $all_local_functions[$file] ) ? $all_local_functions[$file] : array();
$includes = array();
$parser->reset();
$parser->parseFile( $filename );
//echo "<pre>", print_r( $parser->parsed_objects ), '</pre>';
foreach( $parser->parsed_objects as $object ) {
switch( $object['type'] ) {
case PHPPARSER_INCLUDE:
$includes[] = $object;
$current_dir = dirname( $file );
if( $object['name'] == 'global.php' ) {
$object['name'] = 'libs/security/lib_security_input.php';
$includes[] = $object;
$object['name'] = 'libs/get/lib_get_portal.php';
$includes[] = $object;
$object['name'] = 'libs/logging/lib_logging_errors.php';
$includes[] = $object;
}
$local_functions = array_merge( $local_functions, isset( $all_local_functions[$object['name']] ) ? $all_local_functions[$object['name']] : array() );
break;
case PHPPARSER_FUNCTION_CALL:
if( !in_array( $object['name'], array_keys( $functions ) ) ) {
if(
!in_array( $object['name'], $parser->internal_functions )
&& !in_array( $object['name'], $local_functions )
) {
$file_requires[$file]['errors'][] = array( 'line' => $object['line'], 'message' => "Undefined function '{$object['name']}'" );
$file_requires[$file]['warnings']++;
}
continue;
}
$include = $functions[$object['name']]['file'];
$include = str_replace( "$CODE_PATH/", '', $include );
if( $include == $file ) { break; }
$functions[$object['name']]['used']++;
if( !isset( $file_requires[$file]['libs'][$include] ) ) { $file_requires[$file]['libs'][$include] = array( 'lines' => array(), 'calls' => array() ); }
$lib =& $file_requires[$file]['libs'][$include];
$bad = $warning = true;
foreach( $includes as $libinc ) {
if( $libinc['name'] != $include || !in_array( $libinc['in_function'], array( '', $object['in_function'] ) ) ) { continue; }
$inc = $libinc;
$bad = false;
/* Holy shit, what a fucked up check this used to be...
$warning = !$bad && $libinc['depth'] > ( empty( $object['in_function'] ) ? 0 : 1 ) && (
$object['depth'] < $libinc['depth']
|| (
$object['depth'] >= $libinc['depth']
&& ( $object['block'] - ( $object['depth'] - $libinc['depth'] ) ) != $libinc['block']
)
);
*/
$warning = !$bad && !in_array( $libinc['block'], $object['open_blocks'] );
if( !$bad && !$warning ) { break; }
}
$open_blocks = is_array( $object['open_blocks'] ) ? implode( ',', $object['open_blocks'] ) : '';
$object['bad'] = $bad;
$object['warning'] = $warning;
$object['info'] = "called[l={$object['line']};b={$object['block']};d={$object['depth']}]" . ( !$bad ? ", required[l={$inc['line']};b={$inc['block']};d={$inc['depth']}] open[{$open_blocks}]" : '' );
$lib['lines'][] = $object['line'];
$lib['calls'][] = $object;
$file_requires[$file]['bad'] += $bad ? 1 : 0;
$file_requires[$file]['warning'] += $warning ? 1 : 0;
break;
}
}
$counters['files']++;
if( $file_requires[$file]['bad'] > 0 ) { $counters['errors']++; }
if( $file_requires[$file]['warning'] > 0 ) { $counters['warnings']++; }
if( !$file_requires[$file]['parsed'] ) { $counters['failed']++; }
} err( "\n" );
fclose( $stderr );
$files =& $php_files;
foreach( $files as $key => $value ) { $files[$key] = str_replace( "$CODE_PATH/", '', $value ); }
function ispath( $string ) { return strpos( $string, '/' ) === false ? false: true; }
function notpath( $string ) { return strpos( $string, '/' ) === false ? true: false; }
sort( $files );
$files1 = array_filter( $files, 'notpath' ); sort( $files1 );
$files2 = array_filter( $files, 'ispath' ); sort( $files2 );
$files = array_merge( $files1, $files2 );
?>
<html>
<head>
<title>PayQuik Library Function Calls</title>
<style>
.expander {
display: none;
text-decoration: none;
border-bottom: 1px solid black;
color: #000;
font-weight: bold;
background-color: #ddffdd;
margin-bottom: 5px;
cursor: pointer;
}
div.bad, dt.bad {
display: block;
background-color: #ffdddd;
}
div.warning, dt.warning {
display: block;
background-color: #ffca9f;
}
dt.good {
background-color: #ddffdd;
}
div.failed, dt.failed {
display: block;
background-color: #000;
color: #fff;
}
.expand {
padding-left: 1em;
font-size: small;
}
.code {
font-family: monospace,courier;
background-color: #eee;
border: 1px solid #aaa;
}
.line, .offset {
color: #990000;
font-weight: bold;
}
.scope {
font-style: italic;
}
.function {
font-weight: bold;
font-family: monospace,courier;
}
.info {
color: #999;
font-variant: small-caps;
}
span.bad {
color: #990000;
}
span.warning {
color: #df6300;
}
#footer {
font-size: x-small;
font-style: italic;
text-align: center;
}
</style>
<script type="text/javascript">
function expand( id ) {
var e = document.getElementById( "expand_" + id );
if( e.style.display == "none" ) {
e.style.display = "block";
} else {
e.style.display = "none";
}
}
</script>
</head>
<body>
<h1>Library Function calls by file</h1>
<?=$counters['files']?> files, of which <?=$counters['errors']?> contain errors, <?=$counters['warnings']?> contain warnings and <?=$counters['failed']?> failed to parse<br />
Last updated: <?=date( 'Y-m-d H:i:s' )?>
<h2>Legend</h2>
<dl>
<dt class="good">Good files</dt>
<dd>File parsed ok, no warnings or errors</dd>
<dt class="warning">Warnings</dt>
<dd>File contains potentially undefined function calls</dd>
<dt class="bad">Errors</dt>
<dd>File contains undefined function calls</dd>
<dt class="failed">Failed</dt>
<dd>File failed to parse (checked with <code>php -l <var>filename</var></code>)</dd>
</dl>
<h2>Files:</h2>
<?
foreach( $files as $key => $file ) {
if( !isset( $file_requires[$file] ) ) { continue; }
$failed = !$file_requires[$file]['parsed'];
$bad = $file_requires[$file]['bad'];
$warning = $file_requires[$file]['warning'];
$requires = array_keys( $file_requires[$file]['libs'] );
ksort( $requires );
?>
<div class="expander<?=( $failed ? ' failed' : ( $bad > 0 ? ' bad' : ( $warning > 0 ? ' warning' : '' ) ) )?>" id="link_<?=$key?>" onclick="expand(<?=$key?>)"><?=$file?></div>
<div id="expand_<?=$key?>" class="expand" style="display: none">
<ul>
<? foreach( $file_requires[$file]['errors'] as $error ):?>
<li><span class="line"><?=$error['line']?>: <span class="info"><?=$error['message']?></span></li>
<? endforeach;?>
</ul>
<dl>
<? foreach( $requires as $require ): ?>
<dt class="code">require_once( '<?=$require?>' );</dt>
<dd>
<ul>
<? foreach( $file_requires[$file]['libs'][$require]['calls'] as $call ): ?>
<li><span class="line"><?=$call['line']?></span>:<span class="offset"><?=$call['block']?></span>:<span class="scope"><?=$call['in_function']?></span>: <span class="function<?=( $call['bad'] ? ' bad' : ( $call['warning'] ? ' warning' : '' ) )?>"><?=$call['name']?></span> <span class="info"><?=$call['info']?></span></li>
<? endforeach; ?>
</ul>
</dd>
<? endforeach; ?>
</dl>
</div>
<?
}
$time_end = microtime_float();
?>
<div id="footer">Generated in <?=$time_end - $time_start?> seconds.</div>
</body>
</html>