PHP动态函数调用本身非受限,关键在控制调用者、条件与函数范围;需禁用高危函数、强制白名单校验、优先使用call_user_func、运行时拦截敏感函数。

PHP 动态函数调用本身不是“被限制”的功能,而是语言特性;真正需要控制的是谁、在什么条件下、能调用哪些函数。限制的关键不在于禁用语法(如 $func()),而在于切断危险函数的执行路径 + 收紧输入与上下文控制。
以下是从实战角度出发的几类有效限法:
一、禁用高危函数并验证生效
直接在 php.ini 中配置 disable_functions,列出明确禁止的函数:
disable_functions = eval,assert,system,exec,passthru,shell_exec,popen,proc_open,pcntl_exec,create_function
⚠️ 注意:
立即学习“PHP免费学习笔记(深入)”;
- 修改后必须重启 PHP-FPM 或 Web 服务器(如 Apache/Nginx)才生效;
- 用
phpinfo()或get_cfg_var('disable_functions')确认实际生效值; -
disable_functions对call_user_func/call_user_func_array无效——它们只是“调用器”,不属被禁名单,但若传入的函数名本身已被禁用,调用仍会失败(报Call to undefined function)。
二、避免动态拼接函数名,强制白名单校验
不要让用户输入直接决定函数名。例如,禁止这样写:
$func = $_GET['action']; // 危险! $func();
应改为显式白名单映射:
$allowed = ['get_time' => 'date', 'get_len' => 'strlen', 'format_json' => 'json_encode'];
$action = $_GET['action'] ?? '';
if (isset($allowed[$action])) {
echo $allowed[$action]('Y-m-d'); // 安全调用
}或用 function_exists() + 字符串白名单双重校验:
$func = $_POST['func'] ?? '';
if (in_array($func, ['date', 'md5', 'base64_encode'], true) && function_exists($func)) {
echo $func($_POST['data'] ?? '');
}三、用 call_user_func 替代裸变量调用,增强容错与可控性
虽然 call_user_func($name, ...) 本身不阻止危险函数,但它比 $name(...) 更安全:
- 会先检查
$name是否为合法函数(非空字符串、存在且可调用),不存在时返回false,而非直接Fatal error; - 支持闭包、数组形式方法调用(如
[$obj, 'method']),便于封装逻辑; - 配合白名单使用更清晰:
$whitelist = ['date', 'urlencode', 'htmlspecialchars']; if (in_array($func, $whitelist, true)) { $result = call_user_func($func, $input); }
四、运行时拦截:钩子或自定义函数包装器
对关键函数做运行前检查(适合框架或统一入口):
// 示例:重写 system() 行为(需提前定义)
function system($command) {
if (!preg_match('/^[a-z0-9_\-\.\/\s]+$/', $command)) {
error_log("Blocked unsafe system() call: " . escapeshellarg($command));
return false;
}
return \system($command); // 调用原生函数
}或借助 auto_prepend_file 加载全局拦截脚本,统一处理敏感函数调用。
不复杂但容易忽略



















