Skip to content

Instantly share code, notes, and snippets.

@apokalyptik
Created February 3, 2017 20:53
Show Gist options
  • Select an option

  • Save apokalyptik/53ffa152ad5a9f9b1730ab849c5d2413 to your computer and use it in GitHub Desktop.

Select an option

Save apokalyptik/53ffa152ad5a9f9b1730ab849c5d2413 to your computer and use it in GitHub Desktop.

Revisions

  1. apokalyptik created this gist Feb 3, 2017.
    169 changes: 169 additions & 0 deletions extract-php-func-calls
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,169 @@
    #!/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] );
    }
    }
    }
    });