Created
February 3, 2017 20:53
-
-
Save apokalyptik/53ffa152ad5a9f9b1730ab849c5d2413 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/usr/bin/env php | |
<?php | |
define( 'DEBUG_RECURSION', false ); | |
if ( $_SERVER['argc'] > 1 ) { | |
if ( substr( $_SERVER['argv'][1], 0, 1 ) !== "/" ) { | |
define( 'FUNCTION_MATCH', "/" . preg_quote( $_SERVER['argv'][1] ) . "/" ); | |
} else { | |
define( 'FUNCTION_MATCH', $_SERVER['argv'][1] ); | |
} | |
} else { | |
die( "Usage: " . $_SERVER['argv'][0] . " function_pattern [scan_path]\n" ); | |
} | |
if ( $_SERVER['argc'] > 2 ) { | |
define( 'SCAN_BASE', get_real_path_to( $_SERVER['argv'][2] ) ); | |
if ( !SCAN_BASE ) { | |
die( $_SERVER['argv'][2] . " does not appear to be a directory...\n" ); | |
} | |
} else { | |
define( 'SCAN_BASE', get_real_path_to( getcwd() ) ); | |
} | |
function get_real_path_to( $path ) { | |
$path = realpath( $path ); | |
while( is_link( $path ) ) { | |
$path = realpath( readlink( $path ) ); | |
} | |
return $path; | |
} | |
define( 'SCAN_BASE_STRIP', strlen( SCAN_BASE ) + 1 ); | |
function resurse_find_ext_callback( $ext, $dir, $callback ) { | |
$base = get_real_path_to( $dir ); | |
$d = opendir( $base ); | |
if ( $d !== false ) { | |
while( false !== ( $entry = readdir( $d ) ) ) { | |
if ( in_array( $entry, array( ".", "..", ".svn", ".git" ) ) ) { | |
if ( DEBUG_RECURSION ) | |
fwrite( STDERR, "!valid\t$base/$entry\n" ); | |
continue; | |
} | |
$new = get_real_path_to( "$base/$entry" ); | |
if ( $new === $base ) { | |
// Don't scan if the entry is a link to the self | |
if ( DEBUG_RECURSION ) | |
fwrite( STDERR, "!loop\t$new\n" ); | |
continue; | |
} | |
if ( is_dir( $new ) ) { | |
if ( strpos( $new, $base ) === 0 ) { | |
resurse_find_ext_callback( $ext, $new, $callback ); | |
} else { | |
// Don't scan if we've been linked out of the current directory (eg: an nfs share of user uploads) | |
if ( DEBUG_RECURSION ) | |
fwrite( STDERR, "!subdir\t$base/$entry -> $new\n" ); | |
} | |
continue; | |
} | |
if ( substr( $new, strrpos( $new, "." ) ) === $ext ) { | |
$callback( $new ); | |
} | |
} | |
} else { | |
if ( DEBUG_RECURSION ) | |
fwrite( STDERR, "!opendir\t$d\n" ); | |
} | |
} | |
function find_prev( $tokens, $from_idx ) { | |
for( $i = $from_idx - 1; $i >= 0; $i-- ) { | |
if ( is_string( $tokens[$i] ) ) { | |
return $i; | |
} else { | |
switch( token_name( $tokens[$i][0] ) ) { | |
case 'T_WHITESPACE': | |
break; | |
default: | |
return $i; | |
} | |
} | |
} | |
return false; | |
} | |
function find_entire_function_call( $tokens ) { | |
$p_depth = 0; | |
foreach( $tokens as $idx => $token ) { | |
if ( !is_string( $token ) ) { | |
continue; | |
} | |
switch( $token ) { | |
case '(': | |
$p_depth++; | |
break; | |
case ')': | |
$p_depth--; | |
break; | |
} | |
if ( $p_depth === 0 ) { | |
return $idx+1; | |
} | |
} | |
return false; | |
} | |
function reconstruct( $tokens ) { | |
$parts = array(); | |
foreach( $tokens as $token ) { | |
if ( is_string( $token ) ) { | |
$parts[] = $token; | |
} else { | |
$parts[] = $token[1]; | |
} | |
} | |
return implode( "", $parts ); | |
} | |
resurse_find_ext_callback( ".php", SCAN_BASE, function( $file ) { | |
$tokens = token_get_all( file_get_contents( $file ) ); | |
foreach( $tokens as $token_idx => $token ) { | |
if ( is_string( $token ) ) { | |
continue; | |
} else { | |
switch( token_name( $token[0] ) ) { | |
case 'T_STRING': | |
if ( !preg_match( FUNCTION_MATCH, $token[1] ) ) { | |
continue; | |
} | |
if ( !is_string( $tokens[$token_idx+1] ) ) { | |
continue; | |
} | |
if ( $tokens[$token_idx+1] !== "(" ) { | |
continue; | |
} | |
$prev = find_prev( $tokens, $token_idx ); | |
if ( $prev !== false ) { | |
$prev = $tokens[$prev]; | |
if ( !is_string( $prev ) && token_name( $prev[0] ) === "T_FUNCTION" ) { | |
break; | |
} | |
} | |
$len = find_entire_function_call( array_slice( $tokens, $token_idx ) ); | |
if ( $len === false ) { | |
printf( "Unable to determine function call end for %s in %s on line %d\n", $token[1], $file, $token[2] ); | |
break; | |
} | |
printf( | |
"%s:%d %s\n", | |
substr( $file, SCAN_BASE_STRIP ), | |
$token[2], | |
str_replace( | |
array( "\n", "\t" ), | |
array( "\\n", "\\t" ), | |
reconstruct( array_slice( $tokens, $token_idx, $len ) ) | |
) | |
); | |
break; | |
default: | |
//printf( "%s: %s\n", token_name($token[0]), $token[1] ); | |
} | |
} | |
} | |
}); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Recursively scans a directory for .php files and pulls out any calls to the matching function (converting \t, and \n to "\t" and "\n" to keep one call on one line of output.
Just a quick handy utility for auditing...