关于php的output controll参考文档:

http://gywbd.github.io/posts/2015/1/php-output-buffer-in-deep.html

http://www.cnblogs.com/liuzhang/p/4161213.html

众所周知,调用header()函数之前不能有任何输出到浏览器,否则会报错。

如何复现这种情况:

1、php.ini设置output_buffering OFF

2、测试代码:

echo ;
header("HTTP/1.1 503 Service Unavailable");

3、这样程序就会报错:

Warning: Cannot modify header information - headers already sent by

如何修复这种错误:

方法1:

php.ini中output_buffering 4096设置一个4K的大小,这样程序的echo就会一直缓存到php(或者缓存到web server ?)直到缓存大小为4K或者调用ob_系列函数才会输出。

这也是下面代码直到sleep 2s后才能显示所有结果的原因:

echo ;
sleep();
echo ;

方法2:

方法1中output_buffering 4096表示默认php程序开启输出(针对echo printf等函数)缓存(效果等同于ob_start()函数),此时调用ob_get_level()得到的结果是1。

所以可以调用关闭缓存函数来关闭输出缓存

ob_end_clean()或者ob_get_clean()

ob_end_clean();
echo ;
header("HTTP/1.1 503 Service Unavailable");

这样就会报错了。

但是如下代码也并不会立即输出结果:

ob_get_clean();
echo ;
sleep();
echo ;

原因应该是ob_函数只关闭了php的输出缓存,而echo的东西依旧存在web server里面被缓存着,也只有等待程序完毕web server才把输出完全发送给浏览器。

如果想要程序echo一个东西浏览器就显示一个,可以如下操作:

echo str_pad('',)."\n";  //这一行很重要,强制使缓冲溢出,4096等于output_buffering 的值
echo ; ob_flush();
flush(); sleep();
echo ;

//@todo 未完成,需要重新理一理,还未完全搞懂。flush和ob系列函数有些是控制php缓存的,有些是控制web server缓存的。

 更新:

这个文档比较易懂 http://www.hackingwithphp.com/13/0/0/output-buffering

下面的栗子都是把php.ini中output buffing OFF掉来测试的。

栗子1:

ob_start();            //开启一个buffer
print "Hello First!\n";  //往buffer里面写入东西
ob_end_flush();        //关闭掉buffer, 并且把buffer里面的东西输出出来 此时输出"Hello First\n" ob_start();        //开启一个buffer
print "Hello Second!\n";  //往buffer里面写入东西
ob_end_clean();        //关闭掉buffer,并且清空掉这个buffer里面的东西 ob_start();          //开启一个buffer
print "Hello Third!\n";    //往这个buffer里面写入东西, //程序结束时,会自动输出所有buffer里面的东西,此时输出"Hello Third\n"

说明:

该栗子会输出hello first 和 hello third

栗子2:

 ob_start();        //开启
print "Hello First!\n"; //写入东西
ob_flush();        //输出该buffer的东西,和ob_end_flush唯一不同的是少了end,也就是说该buffer还没有关闭 此时输出“helle first”
print "Hello Second!\n"; //继续往这个buffer写入
ob_clean();        //清除这个buffer所有内容
print "Hello Third!\n"; //继续往这个buffer写入 //程序结束时,自动输出buffer所有内容,此时输出"hello third"

说明:

这个栗子输出hello first和hello third

和栗子1唯一不同的是这个栗子只使用了一个buffer,省去了buffer close/open 的操作,效率要高一点。

栗子3:

ob_start();                    //开启
print "Hello first!\n"; //写入 ob_start(); //开启第二个buffer
print "Hello second!\n"; //写入到第二个buffer ob_clean(); //清空第二个buffer,因为buffer的创建是栈的形式,所以满足后进先出,一个ob_函数只能影响到最近的一个buffer //程序结束后输出所有buffer内容,此时输出"hello first"

输出hello first

栗子4:

ob_start();                        //开启
print "In first buffer\n"; //写入 ob_start(); //开启第二个buffer
print "In second buffer\n"; //写入到第二个buffer
ob_end_flush(); //关闭第二个buffer并且输出第二个buffer的内容,此时因为第一个buffer没有关闭,所以第二个buffer的输出会当做内容写入到第一个buffer里面去,这三行等同于 print "In second buffer\n"; print "In first buffer\n"; //继续写入到第一个buffer
ob_end_flush(); //关闭第一个buffer并且输出所有内容,此时输出所有三行数据

输出所有

注意这个栗子4和栗子3对比。

栗子5:

ob_start('ob_gzhandler');    //使用zip压缩内容后发送给浏览器,浏览器会自动解析,
print "My content\n";      //压缩后的内容能节约60%左右的带宽
ob_end_flush();

有一个坑儿就是要确定浏览器是否支持压缩,可以查看浏览器的request header中的

Accept-Encoding:gzip, deflate, sdch 头部。如果有gzip应该就是支持的,未验证。
 
 
栗子6 flush():
ob_start();        //开启
echo 'test'; //写入
ob_end_flush(); //关闭php的buffer并且输出buffer里面的东西 flush(); //立即将php的输出(这个输出指的是已经从buffer里面flush出来的数据,比如这个栗子把上面的ob_end_flush函数注释或者放在flush后面,这个栗子的结果就是要等脚本结束才能看到结果)呈现到浏览器,而不用等待脚本结束
          //这个flush()函数应该是把web server的东西直接输出到浏览器,而上面的ob_flush,ob_end_flush应该是php进程把输出丢给web server
sleep(); echo ;

栗子6等同于下面这个栗子(前提是下面这个栗子的php.ini中的output buffering是OFF状态)

//ob_start();    //这一行是不能加滴,加了的话,echo 'test'就会被放在buffer里面,而紧接着flush()函数是拉不到数据的,因为flush()函数只接受buffer里面flush出来的数据(ob_end_flush,ob_flush)或者非buffer里面的数据。
echo 'test'; //写入 flush(); sleep(); echo ;

说明:

栗子6的结果是分段式输出结果。

强调一点,CLI模式下的output buffering默认是OFF,implicit_flush参数默认是1,

也就是说CLI模式下,echo 东西时,不会被缓存在buffer里面,而且implicit_flush=1,相当于手动执行了flush()函数,所以,在CLI模式下,echo数据会立马看到效果。

而web模式下的php.ini,outpu buffering默认是4096, implicit_flush默认是OFF, 相当于默认开启了一个ob_start,而且没有执行flush(),如果要web server把数据立马推到web brower,必须手动调用flush().

以下情况会导致使用header等需要发送头部信息的函数报如下错误:

Warning: Cannot modify header information - headers already sent by

情况1:

header等函数放在有输出的代码的后面,这个输出指的是非buffer的输出 或者 buffer里面有输出并且调用了ob_flush/ob_end_flush函数的。

栗子:

//php.ini中output buffering OFF
echo "test";
header("HTTP/1.1 503 Service Unavailable");

或者

//php.ini中output buffering OFF
ob_start();
echo "test";
ob_end_flush();
header("HTTP/1.1 503 Service Unavailable");

或者

//php.ini中的output buffering 4096 可以用ob_start(NULL, 4096);代替
 echo str_pad(' ', );//如果echo的大小小于4096则不会报错。 
header("HTTP/1.1 503 Service Unavailable");

这三种情况都会报错header,

其中第三个栗子,header和echo都在同一个buffer

如果第三个栗子echo的内容<4096字节,那echo和header属于同一个buffer,php底层会自动先把header的内容写入header buffer,再把echo的内容写入body buffer,然后先发送header再发送body分别给SAPI,所以并不会报错

如果第三个栗子echo的内容>=4096字节,那么php会自动调用类似ob_flush/ob_end_flush的函数(也就是等同于第二个例子),把echo的内容装入body buffer传递给sapi(只会装入4096个字节),自动组装一个默认的header buffer传递给sapi,

  然后代码继续运行,如果后面的代码没有header等函数,则会把剩下的echo内容第二次装入body buffer传递给sapi并且正常运行下去,如果遇到header函数,此时因为已经传递过一个默认的header buffer给SAPI了,所以这个时候再修改header要报错。

所以导致header函数报错的最终原因是看php有没有传递header buffer给sapi,也就是看有没有手动ob_flush()/ob_end_flush()或者自动flush(echo超过output buffering设置或者ob_start(NULL, buffer size)设置时会自动触发flush)

lphp输出控制output controll(header, ob_xxx)的更多相关文章

  1. SAS学习笔记之《SAS编程与数据挖掘商业案例》(4)DATA步循环与控制、常用全程语句、输出控制

    SAS学习笔记之<SAS编程与数据挖掘商业案例>(4)DATA步循环与控制.常用全程语句.输出控制 1. 各种循环与控制 DO组 创建一个执行语句块 DO循环 根据下标变量重复执行DO和E ...

  2. Visual Studio将std::cout输出到Output窗口

    在debug的时候,输出到Output需要使用OutputDebugString函数,但部分库的log是采用std::cout输出的,需要用控制台(黑窗)程序来查看输出.有没有一种使用GUI和Outp ...

  3. cout输出控制——位数和精度控制

    刷到一道需要控制输出精度和位数的题目 刚开始以为单纯使用 iomanip 函数库里的 setprecision 就可以,但 OJ 给我判了答案错误,后来一想这样输出并不能限制位数只能限制有效位数. 比 ...

  4. unity中Debug输出控制

    1 需求: (1)选择在界面.console中输出,并且能够设置保存到文档 (2)控制debug是否输出,可以在debug模式下输出,release模式下不输出 2 参考: 谢谢雨松同学的博客:htt ...

  5. MATLAB格式化输出控制 分类: 数学 2015-07-31 23:01 3人阅读 评论(0) 收藏

    MATLAB格式化输出控制 format 默认格式 format short 5字长定点数 format long 15字长定点数 format short e 5字长浮点数 format long ...

  6. ios中判断控制台Log输出控制,是否是iphone5,自动调整尺寸

    // 控制台Log输出控制,此确保在release版本下无Log输出 #ifdef DEBUG #define CMBLOG          NSLog #else #define CMBLOG  ...

  7. CC2530入门教程-02】CC2530的通用I/O端口输入和输出控制

    第2课  CC2530的通用I/O端口输入和输出控制 广东职业技术学院  欧浩源 一.CC2530的引脚概述 CC2530微控制器采用QFN40封装,有40 个引脚.其中,有21个数字I/O端口,其中 ...

  8. tensorflow笔记6:tf.nn.dynamic_rnn 和 bidirectional_dynamic_rnn:的输出,output和state,以及如何作为decoder 的输入

    一.tf.nn.dynamic_rnn :函数使用和输出 官网:https://www.tensorflow.org/api_docs/python/tf/nn/dynamic_rnn 使用说明: A ...

  9. webpack 的 入口(Entry)、输出(Output)

    入口(Entry) 入口定义了我们的应用代码开始执行的那个文件,webpack从这个文件开始打包.你能定义一个入口点(常见于单页应用 - Single-Page Application), 或者多个入 ...

随机推荐

  1. Kafka报错-as it has seen zxid 0x83808 our last zxid is 0x0 client must try another server

    as it has seen zxid 0x83808 our last zxid is 0x0 client must try another server 停止zookeeper,删除datadi ...

  2. sql 查出一张表中重复的所有记录数据

    1.在面试的时候碰到一个 问题,就是让写一张表中有id和name 两个字段,查询出name重复的所有数据,现在列下: select * from xi a where (a.username) in ...

  3. MD5在java中的使用

    MD5是什么? MD5是message-digest algorithm 5(信息-摘要算法)的缩写,被广泛用于加密和解密技术上,它可以说是文件的"数字指纹".任何一个文件,无论是 ...

  4. 【转】 C# 小技巧之获取变量名称

    link: http://www.cnblogs.com/gongy/p/lm-2015-04-03.html 今天在自我规范程序设计的时候,变量名匹配字符串来自配置文件,网上找了一会儿发现也有朋友在 ...

  5. 使用Nginx镜像代理.NET Core MVC

    1.获取microsoft/dotnet镜像 docker pull registry.cn-hangzhou.aliyuncs.com/cjx/tutorial 如果有问题确认已经使用阿里云镜像加速 ...

  6. Java中基本数据类型的对比记忆

    Java中八种基本类型数据情况: 数据类型 所占字节数 所占位数(二进制位数) 可表示范围 默认值 包装类 备注 byte(字节) 1 8 -128 - 127  0  Byte   short(短整 ...

  7. win7 双屏双任务栏

    扩展屏幕下都显示任务栏!!! 第一步:Dual Monitor Taskbar 下载 下载链接:链接: http://pan.baidu.com/s/1pKxYUFL 密码: gu5c 第二步:安装完 ...

  8. b/s结构的物业管理系统(一)-------登录篇

    最近计划做一个非框架的物业管理系统前端使用bootstrap js jquery 等希望各位指点一下共同学习 ---前端登录页面------ 这个页面的输入框组用的bootstrap的,我设置了几张背 ...

  9. Spring+springmvc+Mybatis整合案例 annotation版(myeclipse)详细版

    Spring+springmvc+Mybatis整合案例 Version:annotation版 文档结构图: 从底层开始做起: 01.配置web.xml文件 <?xml version=&qu ...

  10. [转载] 深入理解Android之Java虚拟机Dalvik

    本文转载自: http://blog.csdn.net/innost/article/details/50377905 一.背景 这个选题很大,但并不是一开始就有这么高大上的追求.最初之时,只是源于对 ...