如何实现基于指定目录上下文的PHP-FPM环境phpinfo()输出?
问题背景
2019年我为开源主机面板Fruithost编写过一个解析器,能基于指定目录范围输出适配该目录下php.ini配置的phpinfo(),方案通过Web服务器+PHP-FPM直接执行。但因git stash操作丢失代码,现在需要重新实现。
当前遇到的核心问题:
- 直接调用
phpinfo()会输出非目标目录上下文的配置,且PHP无phpinfo('/path/to/php.ini')这类指定配置文件的调用方式 - 使用PHP CLI执行
php -i -c /destination/path/不可行:CLI与PHP-FPM运行上下文不同,加载模块不一致,且输出为纯文本而非HTML格式
期望效果:用户在面板右上角选择域名(如sample.net)后,phpinfo()以/var/fruithost/<username>/sample.net/目录为上下文,加载该目录下的php.ini并生成HTML格式的输出。
可行实现方案
方案1:通过PHP-FPM池配置指定目标配置
如果面板为每个域名/用户配置独立PHP-FPM池,或可动态修改FPM配置,可在对应池的配置中添加:
# 指定目标目录为open_basedir,限制PHP访问范围 php_admin_value[open_basedir] = /var/fruithost/<username>/sample.net/ # 指定扫描该目录下的ini文件(优先级高于全局配置) php_admin_value[php_ini_scan_dir] = /var/fruithost/<username>/sample.net/ # 或直接指定php.ini路径 # php_admin_value[php_ini] = /var/fruithost/<username>/sample.net/php.ini
面板逻辑:用户选择域名后,动态切换到对应FPM池的监听端口,请求一个专用的phpinfo.php脚本(仅包含<?php phpinfo(); ?>),此时phpinfo()会加载目标目录的配置。修改FPM配置后需执行重载:systemctl reload php-fpm。
方案2:通过Web服务器传递环境变量指定配置目录
Apache环境
在对应域名的虚拟主机配置中添加:
SetEnv PHP_INI_SCAN_DIR /var/fruithost/<username>/sample.net/
若允许.htaccess,也可在目标目录下的.htaccess中写入上述配置,面板可在用户选择域名后动态生成该文件。
Nginx环境
在域名对应的location ~ \.php$块中添加:
fastcgi_param PHP_INI_SCAN_DIR /var/fruithost/<username>/sample.net/;
将自定义php.ini(或命名为99-custom.ini确保优先级)放在该目录下,PHP-FPM处理请求时会自动扫描加载。修改配置后需重载Web服务器:systemctl reload nginx或apache2ctl reload。
方案3:应急方案——解析ini并手动设置(局限性大)
若上述方案无法快速实现,可读取目标php.ini内容,用parse_ini_file()解析后尝试用ini_set()设置(仅支持PHP_INI_USER/PHP_INI_PERDIR级别的配置,系统级配置无法修改):
<?php $targetIni = '/var/fruithost/<username>/sample.net/php.ini'; $iniData = parse_ini_file($targetIni, true); // 处理PHP核心配置 if (isset($iniData['PHP'])) { foreach ($iniData['PHP'] as $key => $val) { @ini_set($key, $val); } } // 模块配置大多无法通过ini_set修改,此处仅作示例 if (isset($iniData['mysqli'])) { foreach ($iniData['mysqli'] as $key => $val) { @ini_set("mysqli.$key", $val); } } phpinfo(); ?>
注意:此方案无法模拟PHP-FPM的模块加载差异,仅能修改部分运行时配置,不推荐作为长期方案。
关键注意事项
- 确保目标目录及
php.ini的权限正确,PHP-FPM进程用户需拥有读取权限 - 动态修改配置后必须重载对应服务(FPM/Web服务器)才能生效
- 专用的
phpinfo.php需添加面板权限验证,避免未授权访问
内容的提问来源于stack exchange,提问作者Adrian Preuss

