在平時使用框架的時候我發現咱們能夠隨意的設置 HTTP 頭,而不用擔憂以前的程序是否輸出過內容。但在 PHP 官網手冊中設置 HTTP 頭函數 header
和設置 Cookie 函數 setcookie
卻有着以下警告:php
請注意 header() 必須在任何實際輸出以前調用,無論是普通的 HTML 標籤,仍是文件或 PHP 輸出的空行,空格。html
setcookie() 定義了 Cookie,會和剩下的 HTTP 頭一塊兒發送給客戶端。 和其餘 HTTP 頭同樣,必須在腳本產生任意輸出以前發送 Cookie(因爲協議的限制)。 請在產生任何輸出以前(包括 和 或者空格)調用本函數。緩存
緣由其實很簡單,由於 HTTP 響應報文結構以下:cookie
咱們能夠看見響應頭是在響應體前面的,而 PHP 的任何輸出都將屬於響應體。也就是說一旦 PHP 輸出內容,HTTP 響應頭便已經發送給客戶端了。此時木已成舟就算再用 header
函數設置頭已經沒有用了,由於 HTTP 頭都發給客戶端了/(ㄒoㄒ)/~~。app
不少用上框架的同窗就幾乎不會遇到這種狀況,想設置 HTTP 頭的時候就直接調用內置函數或框架的方法設置就 OK 了,歷來都不用管以前有沒有輸出過內容。咱們可以這樣作依靠的就是:輸出緩衝區。輸出緩衝區的做用是將 PHP 的輸出內容緩存在一塊內存中,當緩衝區的內存滿了或者程序執行完畢後便會將緩衝區的內容發送給客戶端。這樣作的主要緣由是在 Web 場景裏經過 Socket 一個個字節的發送數據比一塊塊的發送數據效率要低,因此採用輸出緩衝區,便能將數據一塊塊的發送提升性能。固然這不是解決這個問題最關鍵的部分,最關鍵部分的是 PHP 提供了一系列的輸出控制函數讓咱們可以控制輸出緩存區!點此查看<輸出控制函數>官網手冊框架
解決此問題的重要函數 ob_start
,做用以下:函數
此函數將打開輸出緩衝。當輸出緩衝激活後,腳本將不會輸出內容(http 頭除外),須要輸出的內容被存儲在內部緩衝區中。post
注意: ob_start
的 chunk_size
參數,默認爲 0,即緩衝區不限制大小,便不會由於輸出內容長度過長而刷送性能
咱們使用的框架通常都會在用戶代碼執行前就調用了這個函數,所以以後的任何的輸出語句都不會真正輸出給客戶端而是保留在輸出緩存區中,因此咱們即可以隨意的設置 HTTP 頭,而不用擔憂以前是否已經有內容輸出。this
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_start
和 ob_get_clean
,下面有個簡易的示例:
<?php
ob_start();
echo 'world';
$str = ob_get_clean();
echo 'hello ' . $str;
// 輸出 hello world
複製代碼
有時候咱們想經過 include
包含並執行一個腳本但卻不但願輸出腳本的內容,咱們能夠這樣處理:
ob_start();
include 'file.php';
ob_end_clean();
複製代碼
嗯哼,在享受框架的方便的同時最好仍是想多一步思考背後的緣由,可以帶來更多的收穫!另外文章中若出現錯誤,但願你們可以指出,如有疑問能夠互相討論:-D。