I recently had a Linux client that was, for whatever odd reason, making infinite recursive HTTP calls to a single script, which was making the server process count skyrocket. I decided to use the same module as I did in my Painless migration from PHP MySQL to MySQLi post, which is to say, overriding base functions for fun and profit using the PHP runkit extension. I did this so I could gather, for debugging, logs of when and where the calls that were causing this to occur.
The below code overrides all functions listed on the line that says “List of functions to intercept” [Line 9]. It works by first renaming these built in functions to “OVERRIDE_$FuncName” [Line 12], and replacing them with a call to “GlobalRunFunc()” [Line 13], which receives the original function name and argument list. The GlobalRunFunc():
The “GlobalShutdown()” [Line 30] is then called when the script is closing [Line 38] and saves all the logs, if any exist, to “$GlobalLogDir/$DATETIME.srl”.
I have it using “serialize()” to encode the log data [Line 25], as opposed to “json_encode()” or “print_r()” calls, as the latter were getting too large for the logs. You may want to have it use one of these other encoding functions for easier log perusal, if running out of space is not a concern.
<?
//The log data to save is stored here
global $GlobalLogArr, $GlobalLogDir;
$GlobalLogArr=Array();
$GlobalLogDir='./LOG_DIRECTORY_NAME';
//Override the functions here to instead have them call to GlobalRunFunc, which will in turn call the original functions
foreach(Array(
'fopen', 'file_get_contents', 'curl_init', 'curl_setopt', //List of functions to intercept
) as $FuncName)
{
runkit_function_rename($FuncName, "OVERRIDE_$FuncName");
runkit_function_add($FuncName, '', "return GlobalRunFunc('$FuncName', func_get_args());");
}
//This optionally
function GlobalRunFunc($FuncName, $Args)
{
global $GlobalLogArr;
if(
($FuncName=='curl_setopt' && $Args[1]==10002) || //CURLOPT enumeration can be found at https://curl.haxx.se/mail/archive-2004-07/0100.html
($FuncName=='curl_init' && isset($Args[0])) ||
(($FuncName=='file_get_contents' || $FuncName=='fopen') && $Args[0][0]!='/')
)
$GlobalLogArr[]=serialize(Array('FuncName'=>$FuncName, 'Args'=>$Args, 'Trace'=>array_slice(debug_backtrace(), 1, 2)));
return call_user_func_array("OVERRIDE_$FuncName", $Args);
}
function GlobalShutdown()
{
global $GlobalLogArr, $GlobalLogDir;
$Time=microtime(true);
if(count($GlobalLogArr))
file_put_contents($GlobalLogDir.date('Y-m-d_H:i:s.'.substr($Time-floor($Time), 2, 3), floor($Time)).'.srl', implode("\n", $GlobalLogArr));
}
register_shutdown_function('GlobalShutdown');
?>