utf8 - php解析header




如何解决PHP中的“Headers already sent”错误 (8)

当运行我的脚本时,我得到了几个像这样的错误:

警告:无法修改标题信息 - 已在第23行的 /some/file.php中由( 在/some/file.php:12开始输出 )发送的标头

错误消息中提到的行包含header()setcookie()调用。

这可能是什么原因? 以及如何解决它?


在发送标题之前没有输出!

发送/修改HTTP标头的函数必须在进行任何输出之前调用。 摘要⇊否则呼叫失败:

警告:不能修改标题信息 - 已经发送的标题(输出在脚本:行开始)

一些修改HTTP头的函数是:

输出可以是:

  • 故意的:

    • printecho和其他功能产生输出
    • Raw <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接收到第一个输出( printecho<html> )时,它将刷新所有收集的标题。 之后它可以发送它想要的所有输出。 但是发送更多的HTTP头是不可能的。

你怎么知道哪里出现过早的输出?

header()警告包含所有相关信息以查找问题原因:

警告:无法修改标题信息 - 已在 / www / usr2345 / htdocs / auth.php:52 处开始的输出)发送的标题在第100行的/www/usr2345/htdocs/index.php中

这里“第100行”指的是header() 调用失败的脚本。

括号内的“ 输出始于 ”注释更为重要。 它指定了以前输出的来源。 在这个例子中它是auth.php52 。 这就是你必须寻找过早产出的地方。

典型原因:

  1. 打印,回声

    printecho语句的意图输出将终止发送HTTP头的机会。 应用程序流程必须进行重构以避免这种情况。 使用functions和模板方案。 写出消息之前确保header()调用发生。

    产生输出的函数包括

    • printechoprintfvprintf
    • trigger_errorob_flushob_end_flushvar_dumpprint_r
    • readfilepassthruflushimagepngimagejpeg


    以及其他和用户定义的功能。

  2. 原始HTML区域

    .php文件中未解析的HTML部分也是直接输出。 必须在任何原始<html>块之前记录将触发header()调用的脚本条件。

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

    使用模板方案将处理与输出逻辑分开。

    • 在脚本上放置表单处理代码。
    • 使用临时字符串变量推迟消息。
    • 实际的输出逻辑和混合的HTML输出应该最后。

  3. <?php for“script.php line 1 ”警告前的空格

    如果警告引用第1行的输出,那么它在打开<?php标记之前主要是引导空白 ,文本或HTML。

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

    同样,它可以发生在附加的脚本或脚本部分:

    ?>
    
    <?php
    

    PHP实际上在关闭标签之后吃了一个linebreak。 但它不会补偿多个换行符或制表符或空格转移到这些空白处。

  4. UTF-8 BOM

    换行和空格本身就是一个问题。 但也有可能导致这种情况的“不可见”字符序列。 最着名的是大多数文本编辑器都没有显示的UTF-8 BOM (字节顺序标记) 。 它是字节序列EF BB BF ,对于UTF-8编码的文档是可选的和冗余的。 但是PHP必须将其视为原始输出。 它可能在输出中显示为字符(如果客户端将文档解释为Latin-1)或类似的“垃圾”。

    特别是图形编辑器和基于Java的IDE不知道它的存在。 它们没有将其可视化(由Unicode标准承担责任)。 然而,大多数程序员和控制台编辑却这样做:

    在那里很容易早期发现问题。 其他编辑可能会在文件/设置菜单中识别它的存在(Windows上的Notepad ++可以识别并解决问题 ),另一种检查BOM存在的方法是使用hexeditor 。 在* nix系统上,通常可以使用hexdump ,如果不是一个简化审计这些和其他问题的图形变体:

    一个简单的解决方法是设置文本编辑器将文件保存为“UTF-8(无BOM)”或类似的术语。 通常新手会采用创建新文件的方式,并将之前的代码复制并粘贴回原来的位置。

    更正实用程序

    还有自动化工具来检查和重写文本文件( sed/awkrecode )。 对于PHP专门有phptags标签phptags 。 它将长时间和短时间的关闭和打开标签重写,但也很容易修复前导和尾随空白,Unicode和UTF-x BOM问题:

    phptags  --whitespace  *.php
    

    在整个包含或项目目录上使用是理智的。

  5. ?>之后的空格

    如果错误源在关闭后面提到?>那么这是一些空白或原始文本写出来的地方。 PHP结束标记在此处不终止脚本执行。 之后的任何文字/空格字符将作为页面内容写出。

    通常建议,特别是新手,尾随?> PHP关闭标签应该被省略。 这避开了这些情况中的一小部分。 (通常include()d脚本是罪魁祸首。)

  6. 错误来源提到“未知的第0行”

    如果没有错误来源,它通常是PHP扩展或php.ini设置。

    • 偶尔是gzip流编码设置ob_gzhandler
    • 但它也可以是任何双重加载的extension=模块,生成一个隐式的PHP启动/警告消息。

  7. 先前的错误消息

    如果另一个PHP语句或表达式导致警告消息或通知被打印出来,那也算作过早输出。

    在这种情况下,您需要避免错误,延迟语句执行,或者使用isset()@()抑制消息 - 当稍后不阻止调试时。

没有错误信息

如果每个php.ini都禁用了error_reportingdisplay_errors ,则不会显示警告。 但忽略错误不会导致问题消失。 过早输出后仍无法发送标题。

所以当header("Location: ...")默默地重定向失败时,探测警告是非常明智的。 使用调用脚本上的两个简单命令重新启用它们:

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

或者set_error_handler("var_dump"); 如果一切都失败了。

说到重定向头文件,你应该经常使用这样的习语来获得最终的代码路径:

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

最好甚至是一个实用功能,它在header()失败的情况下打印用户消息。

输出缓冲作为解决方法

PHP 输出缓冲是解决此问题的解决方法。 它经常可靠地工作,但不能取代正确的应用程序结构并将输出与控制逻辑分开。 它的实际目的是最大限度地减少到网络服务器的分块传输。

  1. output_buffering=设置仍然可以提供帮助。 在现代FPM / FastCGI设置中,在php.ini或通过.htaccess甚至.user.ini进行配置。
    启用它将允许PHP缓存输出,而不是立即将其传递到Web服务器。 PHP因此可以聚合HTTP标头。

  2. 它也可以通过调用ob_start(); 在调用脚本之上。 然而,由于多种原因,这不太可靠:

    • 即使<?php ob_start(); ?> <?php ob_start(); ?>启动第一个脚本,空白或BOM可能会在之前进行洗牌, 使其无效

    • 它可以隐藏HTML输出的空白。 但是,只要应用程序逻辑尝试发送二进制内容(例如生成的图像)​​,缓冲的无关输出就成为问题。 (必须将ob_clean()作为其他解决方法。)

    • 缓冲区的大小是有限的,并且在默认情况下很容易溢出。 这也不是一件难得的事情当它发生时很难追踪

因此,两种方法都可能变得不可靠 - 特别是在开发设置和/或生产服务器之间切换时。 这就是为什么输出缓冲被广泛认为只是一个拐杖/严格的解决方法。

另见手册中的基本使用示例 ,以及更多优点和缺点:

但它在另一台服务器上工作!?

如果您以前没有收到标题警告,那么output_buffering=已更改。 它可能在当前/新服务器上未配置。

使用headers_sent()检查

您始终可以使用headers_sent()来探测是否仍然可以发送标头。 有条件地打印信息或应用其他回退逻辑很有用。

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

有用的备用解决方法是:

  • HTML <meta>标签

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

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

    或者短暂延迟:

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

    当通过<head>部分使用时,会导致无效的HTML。 大多数浏览器仍然接受它。

  • JavaScript重定向

    作为替代, JavaScript重定向可用于页面重定向:

     <script> location.replace("target.html"); </script>
    

    虽然这通常比<meta>解决方法更符合HTML标准,但它依赖于支持JavaScript的客户端。

然而,两种方法在真正的HTTP头()调用失败时都会产生可接受的回退。 理想情况下,你总是将这个与用户友好的消息和可点击链接结合起来作为最后的手段。 (这例如是http_redirect() PECL扩展所做的)。

为什么setcookie()session_start()也受到影响

setcookie()session_start()需要发送一个Set-Cookie: HTTP头。 因此适用相同的条件,并且对于过早的输出情况将生成类似的错误消息。

(当然,它们还会受到浏览器中禁用cookies的影响,甚至是代理问题。会话功能显然也取决于可用磁盘空间和其他php.ini设置等)

更多链接


一个简单的提示:脚本中的简单空间(或不可见的特殊字符),就在第一个<?php标记之前,可以导致这个! 特别是当你在一个团队中工作时,有人正在使用“弱”IDE,或者在使用奇怪文本编辑器的文件中混淆了它。

我看过这些东西;)


使用

ob_start();

在脚本的最上面,并且

ob_end_flush()函数;

在脚本的底部。 这将打开输出缓冲,并且页面缓冲后将创建您的标题。

常见问题:

====================

(复制来自: source答案)

1)header(.......);之前不应有任何输出(即echo..或HTML代码header(.......); 命令。

2)<?php和after ?>标记之前删除任何空格 (或换行符 )。

3) 黄金规则! - 检查该php文件(以及include其他文件)是否使用UTF8而不使用BOM编码(而不仅仅是UTF-8 )。 这在很多情况下都是问题(因为UTF8编码的文件在php文件的开始处有一些特殊字符,您的文本编辑器不会显示)!!!!!!!!!!!

4) header(...); 你必须使用exit;

5)总是使用301或302参考:

header("location: http://example.com",  true,  301 );  exit;

6) 打开错误报告。 并告诉错误。

7)如果以上都没有帮助,请使用JAVSCRIPT重定向(但强烈不推荐的方法),可能是自定义情况下的最后机会...:

echo "<script type='text/javascript'>window.top.location='http://website.com/';</script>"; exit;

另一个不好的做法可能会引发这个尚未说明的问题。

看到这段代码片段:

<?php
include('a_important_file.php'); //really really really bad practise
header("Location:A location");
?>

事情没问题,对吧?

如果“a_important_file.php”是这样的话:

<?php
//some php code 
//another line of php code
//no line above is generating any output
?>

 ----------This is the end of the an_important_file-------------------

这不会起作用? 为什么?因为已经有了一条新线。

现在,虽然这不是一个常见的情况,但如果您正在使用一个MVC框架,在将您的控制器切换之前加载大量文件,该怎么办? 这并不罕见。 为此做好准备。

PSR-2 2.2开始:

  • 所有的PHP文件都必须使用Unix LF (linefeed) line ending
  • 所有的PHP文件必须以一个single blank line结束。
  • 关闭?>标记必须从only php包含only php文件中omitted

相信我,遵循这些标准可以让你从生活中拯救你很多时间:)


我得到了这个错误很多次,我相信所有的PHP程序员至少有一次出现这个错误。 要解决这个错误,您可以根据问题级别解决使用问题:

可能的解决方案1

你可能会在之前之后留下空格(在?>之后的文件末尾)即ie

THERE SHOULD BE NO BLANK SPACES HERE
<?php  

   echo "your code here";

?>
DO CHECK FOR BLANK SPACES HERE AS WELL; THIS LINE (blank line) SHOULD NOT EXIST.

大多数情况下,这应该可以解决您的问题。请检查与您require文件关联的所有文件。

注意: 有时EDITOR(IDE)像gedit(一个默认的linux编辑器)在保存文件上添加一行空白行。这不应该发生。 如果你使用的是Linux。 您可以使用VI编辑器在页面末尾的?>后删除空格/行。

如果这不是你的情况,那么你可以使用ob_start来输出缓冲,如下所示:

可能的解决方案2

<?php
  ob_start();

  // code 

 ob_end_flush();
?> 

有时,当开发过程同时拥有WIN工作站和LINUX系统(托管)时,并且在代码中,在相关行之前没有看到任何输出,这可能是文件的格式化以及缺少Unix LF(换行符)行结尾。

我们通常为了快速解决这个问题而做的是重命名文件,并在LINUX系统上创建一个新文件而不是重命名的文件,然后将内容复制到该文件中。 很多时候,这解决了这个问题,因为在WIN中创建的一些文件一旦移到托管引起此问题。

此修补程序是我们通过FTP管理的网站的简单修复程序,有时可以为我们的新团队成员节省一些时间。


而不是下面的线

//header("Location:".ADMIN_URL."/index.php");

echo("<script>location.href = '".ADMIN_URL."/index.php?msg=$msg';</script>");

要么

?><script><?php echo("location.href = '".ADMIN_URL."/index.php?msg=$msg';");?></script><?php

它一定会解决你的问题。 我面临同样的问题,但我通过以上述方式编写标题位置来解决问题。


通常,当我们在回显或打印后发送头文件时会出现此错误。 如果此错误出现在特定页面上,请确保该页面在调用start_session()之前不回显任何内容。

不可预知的错误示例:

 <?php //a white-space before <?php also send for output and arise error
session_start();
session_regenerate_id();

//your page content

再举一个例子:

<?php
includes 'functions.php';
?> <!-- This new line will also arise error -->
<?php
session_start();
session_regenerate_id();

//your page content

结论:在调用session_start()header()函数之前,不要输出任何字符,甚至不能使用空格或换行符





header