Skip to content

Instantly share code, notes, and snippets.

@apokalyptik
Created February 3, 2017 20:53
Show Gist options
  • Save apokalyptik/53ffa152ad5a9f9b1730ab849c5d2413 to your computer and use it in GitHub Desktop.
Save apokalyptik/53ffa152ad5a9f9b1730ab849c5d2413 to your computer and use it in GitHub Desktop.
#!/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] );
}
}
}
});
@apokalyptik
Copy link
Author

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...

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment