ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

PHP8.1网站打开空白怎么开启错误提示

PHP8.1网站打开空白怎么开启错误提示 前言网站打开是空白页这种情况在升级到 PHP 8.1 之后出现得特别频繁。典型表现是浏览器返回 HTTP 500但响应体是空的页面一片白服务器上的error_log里翻不到东西或者只有一行毫无信息量的连接被重置用php -l检查文件语法却全部通过。道理其实很简单PHP 默认把错误信息写给日志而不是写给浏览器。当display_errors关闭时一个致命错误Fatal error会让脚本立刻停止此时如果输出缓冲区output buffering里还没有任何内容浏览器就只能收到一个空的 500 响应。而在 PHP 8.x 里这种本来只是警告、现在变成致命错误的情况明显变多了未定义常量从 Warning 升级为 Error、不再抑制致命错误、mysqli的默认报错模式从静默失败改成抛异常——这些都会让原本能勉强跑起来的老代码升级后直接白屏。所以本文要解决的不是猜哪里错了而是先让错误显示出来。内容分四步先解释为什么空白再给出正确开启错误提示的完整配置区分 CLI、FPM、Apache 三种环境然后讲 PHP 8.1 特有的几类白屏原因最后给一份能自动捕获致命错误的引导脚本。一、为什么是空白而不是一堆报错三个原因叠加造成了空白这个现象理解它们才能对症下药。原因机制表现display_errors关闭错误只写日志不写响应体浏览器收到空响应致命错误发生在输出之前脚本中止缓冲区无内容HTTP 500 且响应体为空框架或代码自己吞掉了错误set_error_handler返回真、或异常处理器只记日志有日志没输出或两者都没有其中第三条最容易被忽略Laravel、ThinkPHP 这类框架都注册了自己的错误处理器它们的生产模式默认只把异常写日志。你的代码如果是抛异常而不是致命错误框架就把它接住了表现为空白页 日志里一条异常记录。这里有个前提要记住display_errors是 PHP 的配置能在运行时用ini_set()修改但编译期的错误语法错误、类重复定义发生在ini_set()执行之前。也就是说如果白屏的原因是某个被包含的文件有语法错误你在入口文件顶部写ini_set(display_errors, 1)是没用的——脚本还没跑到那一行就已经停了。二、四步定位从最快的办法开始2.1 第一步命令行直接跑命令行下 PHP 会把错误打到标准错误是最快的验证方式。# 语法检查先排除编译期错误 php -l index.php # 直接执行入口文件观察错误输出 php index.php # 查看 CLI 实际加载的是哪个 ini php --ini # 查看当前生效的配置值 php -i | grep -E display_errors|error_reporting|log_errors命令行执行时出现的错误信息会直接打印到终端。如果命令行能跑出错误、浏览器里却是空白那说明请求链路Web 服务器、FPM、框架里还有东西在吞错误。2.2 第二步确认真正生效的php.ini 是哪一个这是最容易浪费时间的一步CLI 用的 php.ini 和 PHP-FPM 用的通常不是同一个文件。你在终端里改了/etc/php/8.1/cli/php.ini跑网站的 FPM 读的还是另一个目录下的php.ini怎么改都没效果。# 命令行环境 php --ini # FPM 环境把探针文件放到站点根目录用浏览器访问 # 探针文件 probe.php 的内容只有一行调用 phpinfo() 打印环境信息用浏览器访问探针文件后页面上的 Loaded Configuration File 那一行才是 FPM 真正加载的配置文件路径。改配置要改这个文件改完还要让 FPM 重新加载。# 让 FPM 重新读取配置文件不中断请求 kill -USR2 $(cat /run/php/php8.1-fpm.pid) # 或者用服务管理器 systemctl reload php8.1-fpm2.3 第三步正确开启错误提示的配置下面这几项是排查期间应该打开的配置。注意display_startup_errors专门负责启动阶段的错误没有它某些早期错误依然不会显示。; php.ini 中排查期的配置 display_errors On display_startup_errors On error_reporting E_ALL log_errors On error_log /var/log/php_errors.log配置项可修改范围排查期取值说明display_errors运行时也能改On把错误输出到响应体display_startup_errors运行时也能改On显示启动阶段错误error_reporting运行时也能改E_ALL包含弃用提示log_errors运行时也能改On保证日志里也有一份error_log受open_basedir限制具体路径不写的话走 Web 服务器日志如果要临时在代码里打开写法是?php // 需要 PHP 7.4放在入口文件最前面但无法捕获更早的编译期错误 ini_set(display_errors, 1); ini_set(display_startup_errors, 1); error_reporting(E_ALL); echo 如果能看到这一行说明脚本已经跑起来了, \n;2.4 第四步用 .user.ini 或 FPM 池配置兜底当错误发生在ini_set()之前比如被包含的文件有语法错误运行时设置就来不及了。这时有两个办法.user.ini在网站根目录放一个.user.iniPHP 以 CGI 或 FastCGI 方式运行时会自动读取。它负责的是PHP_INI_PERDIR级别的配置项而auto_prepend_file正好属于这一级可以拿它作为所有请求都会先执行的钩子。FPM 池配置在 pool 的配置文件里写php_admin_value这种写法优先级最高ini_set()无法覆盖适合确保生产环境日志永远打开。; php-fpm 池配置片段例如 www.conf php_admin_flag[log_errors] on php_admin_value[error_log] /var/log/php-fpm/www-error.log php_admin_value[error_reporting] E_ALL; 网站根目录下的 .user.ini作为排查期的临时钩子 auto_prepend_file /var/www/html/debug_boot.php display_errors On error_reporting E_ALL注意.user.ini有缓存时间user_ini.cache_ttl默认 300 秒改完之后不要立刻断定没生效等一会儿或者重启 FPM。三、PHP 8.1 上特别容易白屏的几类改动升级到 8.1 之后白屏八成是下面这几条之一。它们共同的特点是在 7.x 上最多是个警告在 8.1 上直接中断或抛异常。变更引入版本白屏原因未定义常量变成 Error8.0以前当字符串用现在直接中断不再抑制致命错误8.0老代码靠兜底的地方全部暴露PDO 默认错误模式改为抛异常8.0查询失败从返回 false 变成抛异常mysqli默认报错模式改为抛异常8.1原来的静默失败变成未捕获异常传 null 给非空内部函数参数被弃用8.1大量弃用日志配合框架的严格模式可能升级为异常整体写入$GLOBALS被禁止8.1老代码里的$GLOBALS [...]直接报错track_errors与$php_errormsg移除8.0靠它取错误信息的老代码拿不到任何线索其中mysqli那一条最典型PHP 8.1 之前mysqli_query()出错是返回false你可以自己判断后处理PHP 8.1 起默认会抛出异常如果代码里没有 try/catch异常一路冒到顶层就是一次致命错误。修法是显式恢复旧行为或者补齐异常处理?php // 需要 PHP 7.4行为与 PHP 8.1 之前一致 mysqli_report(MYSQLI_REPORT_OFF); $link mysqli_connect(127.0.0.1, user, pass, app); mysqli_set_charset($link, utf8mb4); $result mysqli_query($link, SELECT 1); if ($result false) { // 现在需要自己判断并处理而不是依赖异常 error_log(查询失败 . mysqli_error($link)); }代码实战能抓住致命错误的引导脚本普通try/catch抓不到致命错误比如调用未定义函数但register_shutdown_function配合error_get_last()可以。把下面这段保存成debug_boot.php然后用auto_prepend_file挂到所有请求前面即使是白屏也能在响应体里看到最后一条错误。?php // 需要 PHP 8.0用到了 str_starts_with用于排查期问题定位后请移除 declare(strict_types1); ini_set(display_errors, 1); ini_set(display_startup_errors, 1); error_reporting(E_ALL); // 只对内网访问者开放避免把路径信息暴露给公网 $ip $_SERVER[REMOTE_ADDR] ?? cli; $isInternal PHP_SAPI cli || str_starts_with($ip, 127.) || str_starts_with($ip, 10.) || str_starts_with($ip, 192.168.); if (!$isInternal) { return; } register_shutdown_function(static function (): void { $err error_get_last(); // 只有这几种类型属于会导致脚本中断的错误 $fatal [E_ERROR, E_PARSE, E_CORE_ERROR, E_COMPILE_ERROR, E_USER_ERROR]; if ($err null || !in_array($err[type], $fatal, true)) { return; } // 清掉可能残留的输出缓冲保证错误信息能送出去 while (ob_get_level() 0) { ob_end_clean(); } header(Content-Type: text/plain; charsetutf-8); printf( [致命错误] %s\n位置: %s:%d\n, $err[message], $err[file], $err[line] ); }); // 下面故意调用一个不存在的函数用来验证钩子是否生效 // not_exists_function();挂载方式FPM 环境; .user.ini 或 php.ini 中 auto_prepend_file /var/www/html/debug_boot.php这里要提醒一句这个脚本只用于排查期。它把文件路径和行号直接输出到响应体绝不能长期留在公网上。定位到问题之后立刻删掉自动加载配置。常见坑点1. 改了 CLI 的 php.ini 却去测网站❌ 终端里php -i看到display_errorsOn就以为网站也开了✅ 用探针文件在浏览器里确认 Loaded Configuration File 的真实路径CLI 和 FPM 通常不是同一个文件2. 只在代码里 ini_set遇到编译期错误无效❌ 入口文件第一行写ini_set(display_errors, 1)结果被包含的文件语法错误依然白屏✅ 这种错误发生在ini_set()之前必须改 php.ini 或用.user.ini先用php -l逐个文件检查语法3. 忘记 display_startup_errors❌ 只开了display_errors扩展加载失败之类的早期错误依然不显示✅ 排查期把display_startup_errors一起打开4. 框架的错误处理器把异常吞了❌ 页面空白日志里有一条框架记录的异常但看不出来自业务代码的哪一行✅ 临时把框架切到调试模式例如 Laravel 的APP_DEBUGtrue拿到完整调用栈后再关回去5. opcache 让你看到旧版本的代码❌ 改完文件刷新页面错误信息没变怀疑改动没生效✅ 检查opcache.validate_timestamps为 0 时文件改动不会被感知需要重启 FPM 或调用opcache_reset()6. 靠 $php_errormsg 取错误信息❌ 老代码里foo(); if ($php_errormsg) { ... }在 PHP 8 上什么也拿不到✅track_errors和$php_errormsg在 PHP 8.0 已被移除改用error_get_last()或自定义错误处理器7. 生产环境长期开着 display_errors❌ 排查完忘了关页面上直接显示数据库连接串和绝对路径✅ 排查结束后恢复display_errors Off保留log_errors On用日志排查后续问题调试开关最好按来源 IP 判断8. 只盯着 PHP 日志忽略了 Web 服务器日志❌ PHP 完全没记录因为进程根本没起来FPM 崩溃、502✅ 白屏时同时看 Nginx 的error_log和 FPM 的日志区分应用报错和进程不可用总结问题关键动作空白页没有提示先确认 FPM 真正加载的 php.ini再开display_errors与error_reporting命令行能看到错误、浏览器看不到检查 Web 服务器与 FPM 配置排查框架是否吞掉了异常编译期错误不显示ini_set()来不及用.user.ini或 php.ini先跑php -l8.1 升级后突然白屏重点查未定义常量、语义变化、mysqli 与 PDO 的默认异常模式需要自动抓到致命错误register_shutdown_function加error_get_last()配合auto_prepend_file开启错误提示的本质是让沉默的错误开口说话。排查顺序建议固定下来命令行跑一遍拿到最原始的错误浏览器探针确认配置文件路径再按环境打开显示开关。定位到根因后把显示关掉、日志留着——白屏不可怕可怕的是白屏时你既看不到错误也不知道错误被谁藏起来了。
返回列表