php里面为什么header之前有输出报错 源码分析

众所周知,php 里面 header之前有输出的话,会报错,例如下面这样

 

就这个错误,我们开始查阅php源代码,到底是怎样做的,至于php源代码分析,安装,和调试时怎样配置的,我会专门写一篇文章去记录的,这里我是使用php-cli命令行的sapi,方便啊,首先我们先看看var_dump的实现啦

1.var_dump

我们知道,var_dump是php的标准函数啦,不是扩展里面的东西,所以会容易比较好找

我们可以见到啦,var_dump调用了php_var_dump函数啦

之后又调用了php_printf,我猜这个应该都是php内核用到的输出函数吧,我们再去php_printf看看吧

可见,php_printf模仿了c的printf,又是一个不定参数的函数,上面可以看到,又调用了PHPWRITE这个宏

实际是php_output_write函数啦

 

我们看看这个函数的逻辑吧

1,输出层是否激活,如果激活就调用php_output_op函数

2,如果不激活,那么直接输出到stderr去

2。 PHP_OUT_ACTIVATED由来

我们是用php-cli这个sapi的,跟踪了php启动过程,发觉,每个sapi都会调用一个函数,那就是

这个文件时在php-cli.c里面,有兴趣的读者可以看看这个,php在开始接受请求之前都会调用它的,截取一部分函数吧

我看看见了一个函数,php_out_activate函数啦

激活了输出层啦,证明可以输出啦

3 回到php_output_op函数

流程

   1 如果开启了缓冲区的话(obstart这种函数啦),就进入php_output_handler_op函数

   2,如果没有开启的话,就直接赋值给临时通道(context)

   3,如果真的有内容输出的话,就执行php_output_header函数

我们重点看php_output_header函数

4 php_output_header

 

这个函数很简单啦,就是设置当前输出行和该页面的名称,也就是我们调用var_dump的位置啦

具体有什么用,下面就知道啦

5,header函数实现

 

跟到sapi_header_op函数

在这里我们终于可以看到报错的哪行信息啦

header_sent什么时候开始设置呢

答案就是在在上面的 php_output_header函数里面调用的php_header里面啦

6 php_header

该函数的作用就是发送一个header(content-type:text/html);的东西,然后设置 SG(headers_sent) = 1

7 下面来分析为什么开启ob_start不会报错

  答案是开启了 因为开启了缓冲区,还记得上面php_output_op函数里面的一句代码吗

/*
* broken up for better performance:
*  - apply op to the one active handler; note that OG(active) might be popped off the stack on a flush
*  - or apply op to the handler stack
*/
if (OG(active) && (obh_cnt = zend_stack_count(&OG(handlers)))) {

 

上面的注释大家应该应该很清楚吗,如果我们调用了一个ob_start函数,php内核会申请一个handler结构同时为这个结构申请一个缓冲区 php_output_buffer

typedef struct _php_output_handler {
    char *name;
    size_t name_len;
    int flags;
    int level;
    size_t size;
    php_output_buffer buffer;
    void *opaq;
    void (*dtor)(void *opaq TSRMLS_DC);
    union {
        php_output_handler_user_func_t *user;
        php_output_handler_context_func_t internal;
    } func;
} php_output_handler;

当然,申请完缓冲区,就会初始化它然后把它压入栈啦,然后把handler赋给 OG(handlers)

 

下一篇说下,为什么header之前不能有输出,来更加深入httpd协议

时间: 2024-10-10 07:59:02

php里面为什么header之前有输出报错 源码分析的相关文章

Cannot find class: ${jdbc.driver}——配置了sqlSessionFactoryBeanName也报错之问题分析

MyBatis中一个sqlSessionFactory代表一个数据源,那么配置多个数据源只需要注入多个sqlSessionFactory即可. 首先需要说明的是,用mybatis-spring-1.1.0貌似无法配置多个数据源(这里说的不对,应该是无法在配置数据源中使用${..}占位符),这里大概折腾了我一整天的时间.后来才想到可能是版本问题,于是换了最新的1.2.2版,问题就迎刃而解了. 下面记录一下分析这个问题的过程: 首先我在Spring的配置文件中配置了org.springframewo

hadoop源码分析,map输出

Mapper  的输入官方文档如下 The Mapper outputs are sorted and then partitioned per Reducer. The total number of partitions is the same as the number of reduce tasks for the job. Users can control which keys (and hence records) go to which Reducer by implementi

性能测试坑 ① 无法查看结果树的报错内容,分析报错内容

=.= 时至今日我也不懂他妈的怎么看不到结果树的报错.... 于是拐了个弯查看response到底是什么玩意儿,在代码中加了一段记录: //log.info("resp is :"+resp); 如果报错了,给我打印response的内容. 在jmeter.log中查看log日志. 有一个问题,就是如果报错内容非常多的时候,log日志会非常非常大,坑比吗?坑.

Mysql报错注入原理分析(count()、rand()、group by)

0x00 疑问 一直在用mysql数据库报错注入方法,但为何会报错? 百度谷歌知乎了一番,发现大家都是把官网的结论发一下截图,然后执行sql语句证明一下结论,但是没有人去深入研究为什么rand不能和order by一起使用,也没彻底说明三者同时使用报错的原理. 0x01 位置问题? select count(*),(floor(rand(0)*2))x from information_schema.tables group by x; 这是网上最常见的语句,目前位置看到的网上sql注入教程,f

Android开发 关于navigation destination xxx is unknown to this NavController 报错的复现分析与解决

问题描述 在我们使用按键点击触发  Navigation.findNavController(getView()).navigate(R.id.action_aFragment_to_bFragment);  代码跳转到另一个碎片时,在很低的概率下会出现这个 navigation destination xxx is unknown to this NavController 报错问题.这个问题的意思是在启动Fragment的时候,它需要启动的Fragment不存在. 问题原因 原因很简单,你没

十三、MapReduce--output输出源码分析

当reducetask执行完成后,就会将结果的KV写入到指定路径下.下面分析这个output过程. 1.首先看 ReduceTask.run() 这个执行入口 //--------------------------ReduceTask.java public void run(JobConf job, TaskUmbilicalProtocol umbilical) throws IOException, InterruptedException, ClassNotFoundException

docker启动报错解决及分析(Cannot create container for service *******: cannot mount volume over existing file, file exists /var/lib/docker/overlay2/)

现象: Cannot create container for service *******: cannot mount volume over existing file, file exists /var/lib/docker/overlay2/************/merged/etc/php/7.0/fpm/php.ini 之前的也出现过这种情况,没记录,时间长了也忘了怎么解决了 记录一下,传播一下 我在用docker-compose启动的时候,报这个错 说直白了,原因就在于我们启

maven项目打包分析及打包后war包缺少配置文件报错的原因分析,使用progard混淆时配置分析

1.maven打包: 一直以来我都没太注意过在myeclipse下使用run as来clean居然对项目的target目录没有进行操作,要让操作有效,需要进入到maven build...选项下,进行clean,然后再使用process resources来加入配置文件,再使用compile-->package来打包,同时,值得注意的是,maven在进行打包时默认只把java文件打包进war,如果在非资源路径下,有配置文件,如mybits的mapper.xml文件,需要在maven里边指定一下,

微信公众账号报错 返回码说明

返回码 说明 -1 系统繁忙 0 请求成功 40001 验证失败 40002 不合法的凭证类型 40003 不合法的OpenID 40004 不合法的媒体文件类型 40005 不合法的文件类型 40006 不合法的文件大小 40007 不合法的媒体文件id 40008 不合法的消息类型 40009 不合法的图片文件大小 40010 不合法的语音文件大小 40011 不合法的视频文件大小 40012 不合法的缩略图文件大小 40013 不合法的APPID 40014 不合法的access_toke