lphp输出控制output controll(header, ob_xxx)
关于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中的
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)的更多相关文章
- SAS学习笔记之《SAS编程与数据挖掘商业案例》(4)DATA步循环与控制、常用全程语句、输出控制
SAS学习笔记之<SAS编程与数据挖掘商业案例>(4)DATA步循环与控制.常用全程语句.输出控制 1. 各种循环与控制 DO组 创建一个执行语句块 DO循环 根据下标变量重复执行DO和E ...
- Visual Studio将std::cout输出到Output窗口
在debug的时候,输出到Output需要使用OutputDebugString函数,但部分库的log是采用std::cout输出的,需要用控制台(黑窗)程序来查看输出.有没有一种使用GUI和Outp ...
- cout输出控制——位数和精度控制
刷到一道需要控制输出精度和位数的题目 刚开始以为单纯使用 iomanip 函数库里的 setprecision 就可以,但 OJ 给我判了答案错误,后来一想这样输出并不能限制位数只能限制有效位数. 比 ...
- unity中Debug输出控制
1 需求: (1)选择在界面.console中输出,并且能够设置保存到文档 (2)控制debug是否输出,可以在debug模式下输出,release模式下不输出 2 参考: 谢谢雨松同学的博客:htt ...
- MATLAB格式化输出控制 分类: 数学 2015-07-31 23:01 3人阅读 评论(0) 收藏
MATLAB格式化输出控制 format 默认格式 format short 5字长定点数 format long 15字长定点数 format short e 5字长浮点数 format long ...
- ios中判断控制台Log输出控制,是否是iphone5,自动调整尺寸
// 控制台Log输出控制,此确保在release版本下无Log输出 #ifdef DEBUG #define CMBLOG NSLog #else #define CMBLOG ...
- CC2530入门教程-02】CC2530的通用I/O端口输入和输出控制
第2课 CC2530的通用I/O端口输入和输出控制 广东职业技术学院 欧浩源 一.CC2530的引脚概述 CC2530微控制器采用QFN40封装,有40 个引脚.其中,有21个数字I/O端口,其中 ...
- 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 ...
- webpack 的 入口(Entry)、输出(Output)
入口(Entry) 入口定义了我们的应用代码开始执行的那个文件,webpack从这个文件开始打包.你能定义一个入口点(常见于单页应用 - Single-Page Application), 或者多个入 ...
随机推荐
- master-slave
Redis的master/slave数据复制方式可以是一主一从或者是一主多从的方式,Redis在master是非阻塞模式,也就是说在slave执行数据同步的时候,master是可以接受客户端的请求的, ...
- Postgresql-xl 调研
Postgresql-xl 调研 来历 这个项目的背后是一家叫做stormDB的公司.整个代买基于postgres-xc.开源版本应该是stormdb的一个分支. In 2010, NTT's Ope ...
- Ixia测试仪的自动化
Ixia,美国Ixia公司的通信网络测试仪. 1.Ixia的自动化测试场景 测试PC(安装IxOS)AT框架-->Tcl驱动库SIG_Teq_Ixia.tcl(加载 IxTclHal库)---- ...
- 多态与异常处理ppt作业
1.请阅读并运行AboutException.java示例,然后通过后面的几页PPT了解Java中实现异常处理的基础知识. 答:1.抛出异常 当程序发生异常时,产生一个异常事件,生成一个异常对象,并把 ...
- js关于页面坐标api
网页可见区域宽: document.body.clientWidth;网页可见区域高: document.body.clientHeight;网页可见区域宽: document.body.offset ...
- centos环境自动化批量安装软件脚本
自动化安装jdk软件部署脚本 准备工作: 1.在执行脚本的服务器上生成免密码公钥: 安装expect命令 yum install -y expect ssh-keygen 三次回车 2.将jdk-7u ...
- pascal闪电入门系列目录
第一章 准备工作 第二章 Hello,world! 第三章 输出.输入 第四章 变量常量.基本数据类型 第五章 格式.注释 第六章 运算符.表达式.优先级 第七章 分支结构 第八章 数 ...
- 我读汤姆大叔的深入理解js(二)
继续汤姆大叔的js之旅. 揭秘命名函数表达式 函数表达式和函数声明 汤姆大叔在博客中引用ECMA规范:函数声明必须带有标识符,函数表达式可以省略.对于我来说这些概念的东西真是不所适从.还是大叔的实例带 ...
- 在html中关于如果function的函数名和input的name一样会发生怎样的现象
<%@ Page Language="C#" AutoEventWireup="true" CodeBehind="2_PageMethods. ...
- 正则表达式提取string 中的表名
简单版本: Regex reg = new Regex(@"(?i)\bfrom\b(?![^\[\]]*\])\s+(\[[^\[\]]+\]|\S+)"); MatchColl ...