PHP 输出缓冲区应用

0x00 前言

在平时使用框架的时候我发现咱们能够随意的设置 HTTP 头,而不用担忧以前的程序是否输出过内容。但在 PHP 官网手册中设置 HTTP 头函数 header 和设置 Cookie 函数 setcookie 却有着以下警告:php

请注意 header() 必须在任何实际输出以前调用,无论是普通的 HTML 标签,仍是文件或 PHP 输出的空行,空格。html

setcookie() 定义了 Cookie,会和剩下的 HTTP 头一块儿发送给客户端。 和其余 HTTP 头同样,必须在脚本产生任意输出以前发送 Cookie(因为协议的限制)。 请在产生任何输出以前(包括 或者空格)调用本函数。缓存

0x01 为何输出内容后就不能够修改 HTTP 头?

缘由其实很简单,由于 HTTP 响应报文结构以下:cookie

HTTP 响应报文

咱们能够看见响应头是在响应体前面的,而 PHP 的任何输出都将属于响应体。也就是说一旦 PHP 输出内容,HTTP 响应头便已经发送给客户端了。此时木已成舟就算再用 header 函数设置头已经没有用了,由于 HTTP 头都发给客户端了/(ㄒoㄒ)/~~。app

0x02 框架是怎么解决这个问题的?

不少用上框架的同窗就几乎不会遇到这种状况,想设置 HTTP 头的时候就直接调用内置函数或框架的方法设置就 OK 了,历来都不用管以前有没有输出过内容。咱们可以这样作依靠的就是:输出缓冲区。输出缓冲区的做用是将 PHP 的输出内容缓存在一块内存中,当缓冲区的内存满了或者程序执行完毕后便会将缓冲区的内容发送给客户端。这样作的主要缘由是在 Web 场景里经过 Socket 一个个字节的发送数据比一块块的发送数据效率要低,因此采用输出缓冲区,便能将数据一块块的发送提升性能。固然这不是解决这个问题最关键的部分,最关键部分的是 PHP 提供了一系列的输出控制函数让咱们可以控制输出缓存区!点此查看<输出控制函数>官网手册框架

解决此问题的重要函数 ob_start,做用以下:函数

此函数将打开输出缓冲。当输出缓冲激活后,脚本将不会输出内容(http 头除外),须要输出的内容被存储在内部缓冲区中。post

注意: ob_startchunk_size 参数,默认为 0,即缓冲区不限制大小,便不会由于输出内容长度过长而刷送性能

咱们使用的框架通常都会在用户代码执行前就调用了这个函数,所以以后的任何的输出语句都不会真正输出给客户端而是保留在输出缓存区中,因此咱们即可以随意的设置 HTTP 头,而不用担忧以前是否已经有内容输出。this

0x03 输出缓冲区的更多应用

调整内容的输出顺序

try {
    ob_start();
    $response = $this->process($this->container->get('request'), $response);
} catch (InvalidMethodException $e) {
    $response = $this->processInvalidMethod($e->getRequest(), $response);
} finally {
    $output = ob_get_clean();
}

if (!empty($output) && $response->getBody()->isWritable()) {
    $outputBuffering = $this->container->get('settings')['outputBuffering'];
    if ($outputBuffering === 'prepend') {
        // prepend output buffer content
        $body = new Http\Body(fopen('php://temp', 'r+'));
        $body->write($output . $response->getBody());
        $response = $response->withBody($body);
    } elseif ($outputBuffering === 'append') {
        // append output buffer content
        $response->getBody()->write($output);
    }
}
复制代码

这段代码是在 Slim 框架中复制来的,在 Slim 中有个配置项叫 outputBuffering,可以控制非 response 的输出内容在 response 以前、以后或不显示。实现这个功能主要利用的函数就是 ob_startob_get_clean,下面有个简易的示例:

<?php
ob_start();
echo 'world';
$str = ob_get_clean();
echo 'hello ' . $str;
// 输出 hello world
复制代码

忽略 include 的输出

有时候咱们想经过 include 包含并执行一个脚本但却不但愿输出脚本的内容,咱们能够这样处理:

ob_start();
include 'file.php';
ob_end_clean();
复制代码

0x04 总结

嗯哼,在享受框架的方便的同时最好仍是想多一步思考背后的缘由,可以带来更多的收获!另外文章中若出现错误,但愿你们可以指出,如有疑问能够互相讨论:-D。

PHP 输出缓冲区应用 - 个人博客原文

相关文章
相关标签/搜索