如何修复php中的“headers already sent”错误

kr98yfug  于 2021-06-19  发布在  Mysql
关注(0)|答案(1)|浏览(440)

**这个问题的答案是社区的努力。编辑现有答案以改进此帖子。它目前不接受新的答案或互动。

在运行脚本时,我会遇到如下错误:
警告:无法修改标头信息-标头已由(输出开始于/some/file)发送。php:12)在第23行的/some/file.php中
错误消息中提到的行包含 header() 以及 setcookie() 电话。
为什么会这样?如何修复?

vyu0f0g1

vyu0f0g11#

发送头之前没有输出!

在进行任何输出之前,必须调用发送/修改http头的函数。摘要⇊ 否则调用失败:
警告:无法修改标头信息-标头已发送(输出开始于script:line)
一些修改http头的函数有: header /
header_remove session_start /
session_regenerate_id setcookie / setrawcookie 输出可以是:
无意的:
前空格 <?php 或之后 ?> utf-8字节顺序标记
以前的错误消息或通知
有意的: print , echo 以及其他产生输出的功能
生的 <html> 前一节 <?php 代码。

为什么会这样?

为了理解为什么在输出之前必须发送头,有必要查看一个典型的http响应。php脚本主要生成html内容,但也会将一组http/cgi头传递给Web服务器:

HTTP/1.1 200 OK
Powered-By: PHP/5.3.7
Vary: Accept-Encoding
Content-Type: text/html; charset=utf-8

<html><head><title>PHP page output page</title></head>
<body><h1>Content</h1> <p>Some more output follows...</p>
and <a href="/"> <img src=internal-icon-delayed> </a>

页面/输出始终跟随标题。php必须首先将头文件传递给web服务器。它只能这样做一次。在双线中断之后,它再也不能修改它们了。
当php收到第一个输出时( print , echo , <html> )它将刷新所有收集的标头。之后,它可以发送它想要的所有输出。但是发送更多的http头是不可能的。

如何找出过早输出发生在哪里?

这个 header() 警告包含查找问题原因的所有相关信息:
警告:无法修改标头信息-标头已由发送(输出从/www/usr2345/htdocs/auth开始)。php:52)在/www/usr2345/htdocs/index.php第100行
这里的“第100行”是指 header() 调用失败。
括号内的“output started at”注解更为重要。它表示先前输出的来源。在这个例子中 auth.php 和线 52 . 这就是你必须寻找过早产出的地方。
典型原因:

打印,回音

有意输出 print 以及 echo 语句将终止发送http头的机会。必须重新构造应用程序流以避免这种情况。使用函数和模板方案。确保 header() 调用发生在消息写出之前。
产生输出的函数包括 print , echo , printf ,
vprintf trigger_error , ob_flush , ob_end_flush , var_dump ,
print_r readfile , passthru , flush , imagepng , imagejpeg 以及用户定义的函数。

原始html区域

中未分析的html节 .php 文件也可以直接输出。脚本条件将触发 header() 在任何原始数据之前必须记录调用 <html> 阻碍。

<!DOCTYPE html>
<?php
    // Too late for headers already.

使用模板方案将处理与输出逻辑分开。
将表单处理代码放在脚本上。
使用临时字符串变量延迟消息。
实际的输出逻辑和混合的html输出应该紧跟其后。

“script.php line 1”警告前面的空格

如果警告是指行中的输出 1 ,则在开始之前主要是前导空格、文本或html <?php 代币。

<?php

# There's a SINGLE space/newline before <? - Which already seals it.

类似地,附加脚本或脚本节也会发生这种情况:

?>

<?php

php实际上会在关闭标记之后吃掉一个换行符。但它不会补偿多个换行符、制表符或空格移到这样的间隙中。

utf-8物料清单

换行符和空格本身就是一个问题。但也有“看不见的”字符序列可能导致这种情况。最著名的是utf-8bom(字节顺序标记),大多数文本编辑器都不显示它。是字节序列 EF BB BF ,这对于utf-8编码的文档是可选的和冗余的。然而,php必须将其视为原始输出。它可能表现为  在输出中(如果客户机将文档解释为拉丁语-1)或类似的“垃圾”。
尤其是图形编辑器和基于java的ide对它的存在一无所知。他们没有将其可视化(受unicode标准的约束)。但是,大多数程序员和控制台编辑器会:

在那里,很容易在早期就认识到这个问题。其他编辑器可能会在文件/设置菜单中识别它的存在(windows上的notepad++可以识别并解决这个问题),另一个检查boms存在的选项是求助于hexeditor。on*nix系统 hexdump 如果不是简化审核这些问题和其他问题的图形变体,则通常可用:

一个简单的解决方法是将文本编辑器设置为将文件保存为“utf-8(无bom)”或类似的术语。通常新来者会以其他方式创建新的文件,并将以前的代码复制粘贴回原处。

校正实用程序

也有自动工具来检查和重写文本文件( sed / awk 或者 recode ). 特别是对于php,有 phptags 标签更整洁。它将关闭和打开标记重写为长格式和短格式,但也可以轻松修复前导和尾随空格、unicode和utf-x bom问题:

phptags  --whitespace  *.php

在整个include或project目录中使用是明智的。

后面有空格?>

如果错误源被称为关闭后 ?> 然后这就是一些空白或原始文本被写出来的地方。php结束标记此时不终止脚本执行。之后的任何文本/空格字符仍将作为页面内容写出。
人们通常建议,尤其是对新来者来说 ?> 应该省略php close标记。这就避免了一小部分这种情况(相当普遍 include()d 脚本是罪魁祸首。)

错误源被称为“第0行未知”

如果没有具体化错误源,则通常是php扩展或php.ini设置。
偶尔是 gzip 流编码设置或 ob_gzhandler .
但它也可能是双重负载的 extension= 生成隐式php启动/警告消息的模块。

前面的错误消息

如果另一个php语句或表达式导致打印出警告消息或通知,这也算作过早输出。
在这种情况下,您需要避免错误,延迟语句的执行,或者使用。 isset() 或者 @() -任何一个都不会妨碍以后的调试。

无错误信息

如果你有 error_reporting 或者 display_errors 根据禁用 php.ini ,则不会显示任何警告。但是忽略错误并不能解决问题。过早输出后仍无法发送标头。
那什么时候 header("Location: ...") 自动重定向失败非常明智的做法是探测警告。在调用脚本顶部使用两个简单命令重新启用它们:

error_reporting(E_ALL);
ini_set("display_errors", 1);

或者 set_error_handler("var_dump"); 如果一切都失败了。
说到重定向头,您应该经常对最终代码路径使用这样的习惯用法:

exit(header("Location: /finished.html"));

更可取的是一个实用函数,它在 header() 失败。

作为解决方法的输出缓冲

phps输出缓冲是缓解这个问题的一种解决方法。它通常工作可靠,但不应取代正确的应用程序结构和将输出与控制逻辑分离。它的实际目的是最小化到web服务器的分块传输。
这个 output_buffering= 尽管如此,设置还是有帮助的。在php.ini或via.htaccess甚至在现代fpm/fastcgi设置中的.user.ini中配置它。
启用它将允许php缓冲输出,而不是立即将其传递给web服务器。因此,php可以聚合http头。
它也可以通过呼叫 ob_start(); 在调用脚本顶部。然而,由于多种原因,它的可靠性较低:
即使 <?php ob_start(); ?> 开始第一个脚本时,空格或bom可能会被洗牌,使其无效。
它可以隐藏html输出的空白。但是一旦应用程序逻辑尝试发送二进制内容(例如生成的图像),缓冲的无关输出就成为一个问题(必要的 ob_clean() 作为进一步的解决办法。)
缓冲区的大小是有限的,当保留为默认值时,很容易溢出。而且这种情况也不少见,发生时很难追踪。
因此,这两种方法都可能变得不可靠—特别是在开发设置和/或生产服务器之间切换时。这就是为什么输出缓冲被广泛地认为只是一个拐杖/严格地说是一个解决方法。
另请参见手册中的基本用法示例,有关更多优缺点:
什么是输出缓冲?
为什么在php中使用输出缓冲?
使用输出缓冲被认为是一种不好的做法吗?
输出缓冲作为“已发送头”的正确解决方案的用例

但在另一台服务器上成功了!?

如果之前没有收到headers警告,那么输出缓冲php.ini设置已经更改。它可能在当前/新服务器上未配置。

正在检查邮件头\u sent()

你可以随时使用 headers_sent() 调查是否还有可能。。。发送邮件头。这对于有条件地打印信息或应用其他回退逻辑非常有用。

if (headers_sent()) {
    die("Redirect failed. Please click on this link: <a href=...>");
}
else{
    exit(header("Location: /user.php"));
}

有用的后备解决方法包括:

html标记

如果您的应用程序在结构上很难修复,那么允许重定向的一种简单(但有些不专业)方法是注入一个html <meta> 标签。重定向可以通过以下方式实现:

<meta http-equiv="Location" content="http://example.com/">

或短暂延迟:

<meta http-equiv="Refresh" content="2; url=../target.html">

这会导致在过去使用时产生无效的html <head> 部分。大多数浏览器仍然接受它。

javascript重定向

作为一个

相关问题