如何阻止PHP eval执行用户代码时因die退出程序?
解决用户代码执行die/exit导致统计逻辑中断的方案
方案1:注册脚本终止回调(推荐)
PHP的register_shutdown_function()可以注册一个在脚本终止时自动执行的函数——无论脚本是正常结束、调用die/exit,还是触发致命错误。我们可以把统计执行时间、收集输出、返回响应的核心逻辑放到这个回调里,确保用户代码的终止操作不会打断后续统计。
修改后的完整代码示例:
<?php $path = $_SERVER['DOCUMENT_ROOT']; require_once $path . '/wp-load.php'; require WP_PLUGIN_DIR . '/php-editor/vendor/autoload.php'; $output = []; $data = json_decode( file_get_contents( "php://input" ), true ); // 调整错误输出设置 error_reporting(E_ALL ^ E_WARNING); ini_set('display_errors', true); if ( $data['data'] ) { ob_start(); $start_time = microtime(1); // 注册终止回调,引用外部变量确保能访问统计所需数据 register_shutdown_function(function() use (&$output, $start_time) { // 收集输出内容与统计数据 $output['output'] = ob_get_contents(); $output['execution_time'] = number_format(microtime(1) - $start_time, 2, '.', '') * 100 . 'ms'; $output['buffer_length'] = number_format((float)ob_get_length(), 0, ',', '.') . 'B'; ob_end_clean(); // 返回格式化后的JSON响应 echo json_encode($output, JSON_PRETTY_PRINT); }); try { // 执行用户提交的代码 eval( $data['data'] ); } catch (\Exception $e) { $output['error'] = $e->getMessage(); } }
该方案无需依赖额外扩展,兼容性强,能覆盖所有脚本终止场景。
方案2:重定义die/exit函数(需扩展支持)
如果服务器安装了runkit扩展,可以通过runkit_function_redefine()重定义die和exit函数,让它们仅标记终止状态而非直接中断脚本,从而保留后续统计逻辑的执行机会。不过此方案依赖特定扩展,通用性不如方案1。
示例代码(需runkit扩展):
<?php // 其他初始化代码... if ( $data['data'] ) { ob_start(); $start_time = microtime(1); $user_code_terminated = false; // 重定义die函数 runkit_function_redefine('die', function($message = '') use (&$user_code_terminated) { $user_code_terminated = true; if (!empty($message)) { echo $message; } }); // 重定义exit函数(与die逻辑一致) runkit_function_redefine('exit', function($message = '') use (&$user_code_terminated) { $user_code_terminated = true; if (!empty($message)) { echo $message; } }); try { eval( $data['data'] ); } catch (\Exception $e) { $output['error'] = $e->getMessage(); } // 无论用户代码是否终止,都执行统计与响应逻辑 $output['output'] = ob_get_contents(); $output['execution_time'] = number_format(microtime(1) - $start_time, 2, '.', '') * 100 . 'ms'; $output['buffer_length'] = number_format((float)ob_get_length(), 0, ',', '.') . 'B'; ob_end_clean(); echo json_encode($output, JSON_PRETTY_PRINT); }
补充说明
- 方案1的回调函数会在所有脚本执行流程结束后触发,即使用户代码抛出未捕获的非Exception类致命错误,也能通过
error_get_last()捕获错误信息并补充到响应中。 - 注意回调函数中需通过
use关键字正确引用外部变量,避免变量作用域问题。
内容的提问来源于stack exchange,提问作者Maramal
相关产品推荐
相关产品推荐

