ob_start

(PHP 4, PHP 5, PHP 7, PHP 8)

ob_start开启输出缓冲

描述

ob_start(?callable $callback = null, int $chunk_size = 0, int $flags = PHP_OUTPUT_HANDLER_STDFLAGS): bool

此函数将开启输出缓冲。在输出缓冲处于活动状态时,脚本不会发送任何输出,而是将输出存储在内部缓冲区中。有关受影响的具体输出,请参阅 哪些输出被缓冲?

输出缓冲是可以堆叠的,也就是说,当另一个缓冲处于活动状态时,可以调用 ob_start()。如果有多个输出缓冲处于活动状态,则输出将按嵌套顺序依次通过每个缓冲进行过滤。有关更多详细信息,请参阅 嵌套输出缓冲

有关输出缓冲的详细说明,请参阅 用户级输出缓冲

参数

callback

可以指定可选的 callback callable。也可以通过传递 null 来绕过它。

当输出缓冲区被刷新(发送)、清除,或在脚本结束时输出缓冲区被刷新时,会调用 callback

callback 的签名如下

handler(string $buffer, int $phase = ?): string
buffer
输出缓冲区的内容。
phase
PHP_OUTPUT_HANDLER_* 常量 的位掩码。有关更多详细信息,请参阅 传递给输出处理程序的标志

如果 callback 返回 false,则返回缓冲区的内容。有关更多详细信息,请参阅 输出处理程序返回值

警告

在输出处理程序中调用以下任何函数将导致致命错误:ob_clean()ob_end_clean()ob_end_flush()ob_flush()ob_get_clean()ob_get_flush()ob_start()

有关 callback(输出处理程序)的更多详细信息,请参阅 输出处理程序使用输出处理程序

chunk_size

如果传递了可选参数 chunk_size,则在任何导致输出的代码块完成后,如果缓冲区的长度等于或超过 chunk_size,则会刷新缓冲区。默认值为 0,这意味着所有输出都会被缓冲,直到缓冲区被关闭。有关更多详细信息,请参阅 缓冲区大小

flags

flags 参数是一个位掩码,用于控制可以对输出缓冲区执行的操作。默认情况下,允许对输出缓冲区进行清除、刷新和删除,可以通过 缓冲区控制标志 显式设置。有关更多详细信息,请参阅 允许对缓冲区执行的操作

每个标志都控制对一组函数的访问权限,如下所示

常量 函数
PHP_OUTPUT_HANDLER_CLEANABLE ob_clean()
PHP_OUTPUT_HANDLER_FLUSHABLE ob_flush()
PHP_OUTPUT_HANDLER_REMOVABLE ob_end_clean()ob_end_flush()ob_get_clean()ob_get_flush()

返回值

成功时返回 true,失败时返回 false

示例

示例 #1 用户定义的回调函数示例

<?php

function callback($buffer)
{
// 将所有苹果替换为橙子
return (str_replace("apples", "oranges", $buffer));
}

ob_start("callback");

?>
<html>
<body>
<p>这就像把苹果和橙子进行比较。</p>
</body>
</html>
<?php

ob_end_flush
();

?>

上面的示例将输出

<html>
<body>
<p>It's like comparing oranges to oranges.</p>
</body>
</html>

示例 #2 创建一个不可擦除的输出缓冲区

<?php

ob_start
(null, 0, PHP_OUTPUT_HANDLER_STDFLAGS ^ PHP_OUTPUT_HANDLER_REMOVABLE);

?>

参见

添加笔记

用户贡献的笔记 39 笔

54
Ray Paseur (Paseur ... ImagineDB.com)
19 年前
您可以使用 PHP 生成一个静态 HTML 页面。如果您有一个复杂的脚本,出于性能原因,您不希望网站访问者反复按需运行,这将很有用。“cron” 作业可以执行 PHP 脚本以创建 HTML 页面。例如

<?php // 创建 index.html
ob_start();
/* 执行复杂查询,回显结果等。 */
$page = ob_get_contents();
ob_end_clean();
$cwd = getcwd();
$file = "$cwd" .'/'. "index.html";
@
chmod($file,0755);
$fw = fopen($file, "w");
fputs($fw,$page, strlen($page));
fclose($fw);
die();
?>
17
net_navard at yahoo dot com
18 年前
你好,朋友们

ob_start() 打开一个缓冲区,用于存储所有输出。因此,每次执行 echo 时,其输出都会添加到缓冲区。当脚本运行结束或调用 ob_flush() 时,存储的输出将发送到浏览器(如果使用 ob_gzhandler,则会先进行 gzip 压缩,这意味着下载速度更快)。

使用 ob_start 的最常见原因是作为一种收集数据的途径,否则这些数据将被发送到浏览器。

以下是 ob_start() 的两种用法

1- 这样你对输出有更多控制权。举个简单的例子:假设你想向用户显示一条错误消息,但脚本已经向浏览器发送了一些 HTML。它看起来很丑,页面有一半被渲染,然后是一条错误消息。使用输出缓冲功能,你可以简单地删除缓冲区并发送错误消息,这样看起来就很漂亮整洁。
2- 输出缓冲的出现是为了创建从 PHP 引擎到 Apache 到操作系统到 Web 用户的无缝传输。

如果你确保每个都使用相同的缓冲区大小,系统将使用更少的写入次数,使用更少的系统资源,并且能够处理更多流量。

此致,侯赛因
27
ed.oohay (a) suamhcs_rodnan
20 年前
输出缓冲甚至在嵌套作用域中工作,或者可以应用于递归结构……我认为这可能会节省一些人猜测和测试的时间 :)

<pre><?php

ob_start
(); // 启动输出缓冲区 1
echo "a"; // 填充 ob1

ob_start(); // 启动输出缓冲区 2
echo "b"; // 填充 ob2
$s1 = ob_get_contents(); // 读取 ob2 ("b")
ob_end_flush(); // 将 ob2 刷新到 ob1

echo "c"; // 继续填充 ob1
$s2 = ob_get_contents(); // 读取 ob1 ("a" . "b" . "c")
ob_end_flush(); // 将 ob1 刷新到浏览器

// 预计输出 "b" 后面跟着 "abc",而不是:
echo "<HR>$s1<HR>$s2<HR>";

?></pre>

……至少在 Apache 1.3.28 上有效

南多尔 =)
19
mjr
20 年前
如果你在 PHP 中使用面向对象的代码,你可能像我一样想要使用一个回调函数,该函数位于一个对象中(即类函数)。在这种情况下,你将一个包含两个元素的数组作为参数发送给 ob_start。第一个元素是对象的名称(开头没有 $),第二个是将要调用的函数。因此,要使用名为 '$template' 的对象中的函数 'indent',你需要使用 <php ob_start(array('template', 'indent')); >。
11
mchojrin at gmail dot com
12 年前
对像我一样从 5.3 升级的人来说,这里有一个警告

<?php
if ( !ob_start( !DEBUGMODE ? 'ob_gzhandler' : '' ) ) {
ob_start();
}
?>

这不再有效(我收到类似以下的错误:警告:ob_start():函数 '' 未找到或函数名无效)。

它很容易修复,只需将 '' 更改为 null,如下所示

<?php
if ( !ob_start( !DEBUGMODE ? 'ob_gzhandler' : null ) ) {
ob_start();
}
?>

这保留了代码意图,但可以工作 :)
3
jhlavon
11 年前
当在类的构造函数部分使用时,必须用 "self::" 或类名作为前缀,否则 PHP 无法创建缓冲区。

function __construct ()
{

$bo = ob_start ("self::callback_ob") ;
...
}
6
Asher Haig (ahaig at ridiculouspower dot com)
16 年前
当脚本结束时,所有缓冲的输出都会被刷新(这不是错误:http://bugs.php.net/bug.php?id=42334&thanks=4)。当脚本在输出缓冲的中间出现错误(从而结束)时会发生什么?脚本会在打印错误之前吐出缓冲区中的所有内容!

这是我找到的最简单的解决方案。将其放在错误处理函数的开头,以清除所有缓冲的数据,并仅打印错误

$handlers = ob_list_handlers();
while ( ! empty($handlers) ) {
ob_end_clean();
$handlers = ob_list_handlers();
}
7
Chris
13 年前
注意,在使用更改页面头的函数时要小心;在结束输出缓冲时,这些更改不会被撤销。

例如,如果你有一个生成图像并设置适当头的类,它们在 ob 结束之后仍然会存在。

例如
<?php
ob_start
();
myClass::renderPng(); //header("Content-Type: image/png"); 在这里
$pngString = ob_get_contents();
ob_end_clean();
?>

将把图像字节放入 $pngString 中,并将内容类型设置为 image/png。虽然图像不会发送到客户端,但 png 头仍然存在;如果你在这里做 html 输出,浏览器很可能会显示 "图像错误,无法查看",至少 Firefox 是这样。

在这种情况下,你需要手动设置正确的内容类型(text/html)。
2
jkloss at hotmail dot com
20 年前
如果 ob_start 对你来说似乎不起作用,请注意,对于 Apache 2,flush() 函数会导致 PHP 发送头,无论 ob_start 是否在 flush 之前被调用。

ob_start();
echo 'test';
flush();

会导致 Apache 2 发送任何可能堆积的头部 - 这意味着你无法在 flush 之后使用 header(location:xxx)。要修复,请删除 flush()。花了几个小时才发现这一点。Apache 1.x 的工作方式不同。
4
lucky760 at yahoo dot com
17 年前
作为对下面发布的 compress() 函数的扩展,这里有一个简洁的小类,它稍微改进了这个想法。基本上,为每个页面加载运行所有 CSS 的 compress() 函数显然远远不是最佳选择,尤其是考虑到样式最多只会偶尔更改。

使用这个类,你可以简单地指定一个 CSS 文件名数组并调用 dump_style()。每个文件的内容都以 compress() 形式保存在缓存文件中,只有当相应的源 CSS 更改时才会重新创建缓存文件。

它适用于 PHP5,但如果你只是取消 OOP 所有东西,并可能定义 file_put_contents,它将以相同的方式工作。

享受!

<?php

$CSS_FILES
= array(
'_general.css'
);

$css_cache = new CSSCache($CSS_FILES);
$css_cache->dump_style();

//
// CSSCache 类
//

class CSSCache {
private
$filenames = array();
private
$cwd;

public function
__construct($i_filename_arr) {
if (!
is_array($i_filename_arr))
$i_filename_arr = array($i_filename_arr);

$this->filenames = $i_filename_arr;
$this->cwd = getcwd() . DIRECTORY_SEPARATOR;

if (
$this->style_changed())
$expire = -72000;
else
$expire = 3200;

header('Content-Type: text/css; charset: UTF-8');
header('Cache-Control: must-revalidate');
header('Expires: ' . gmdate('D, d M Y H:i:s', time() + $expire) . ' GMT');
}

public function
dump_style() {
ob_start('ob_gzhandler');

foreach (
$this->filenames as $filename)
$this->dump_cache_contents($filename);

ob_end_flush();
}

private function
get_cache_name($filename, $wildcard = FALSE) {
$stat = stat($filename);
return
$this->cwd . '.' . $filename . '.' .
(
$wildcard ? '*' : ($stat['size'] . '-' . $stat['mtime'])) . '.cache';
}

private function
style_changed() {
foreach (
$this->filenames as $filename)
if (!
is_file($this->get_cache_name($filename)))
return
TRUE;
return
FALSE;
}

private function
compress($buffer) {
$buffer = preg_replace('!/\*[^*]*\*+([^/][^*]*\*+)*/!', '', $buffer);
$buffer = str_replace(array("\r\n", "\r", "\n", "\t", ' '), '', $buffer);
$buffer = str_replace('{ ', '{', $buffer);
$buffer = str_replace(' }', '}', $buffer);
$buffer = str_replace('; ', ';', $buffer);
$buffer = str_replace(', ', ',', $buffer);
$buffer = str_replace(' {', '{', $buffer);
$buffer = str_replace('} ', '}', $buffer);
$buffer = str_replace(': ', ':', $buffer);
$buffer = str_replace(' ,', ',', $buffer);
$buffer = str_replace(' ;', ';', $buffer);
return
$buffer;
}

private function
dump_cache_contents($filename) {
$current_cache = $this->get_cache_name($filename);

// 缓存存在 - 直接输出
if (is_file($current_cache)) {
include(
$current_cache);
return;
}

// 删除任何旧的、残留的此文件缓存
if ($dead_files = glob($this->get_cache_name($filename, TRUE), GLOB_NOESCAPE))
foreach (
$dead_files as $dead_file)
unlink($dead_file);

$compressed = $this->compress(file_get_contents($filename));
file_put_contents($current_cache, $compressed);

echo
$compressed;
}
}

?>
2
ernest at vogelsinger dot at
18 年前
当您依赖 URL 重写来传递 PHP 会话 ID 时,您应该注意 ob_get_contents(),因为它可能会完全禁用 URL 重写。

示例
ob_start();
session_start();
echo '<a href=".">self link</a>';
$data = ob_get_contents();
ob_end_clean();
echo $data;

在上面的示例中,URL 重写将永远不会发生。事实上,如果您使用 ob_end_flush() 结束缓冲区外壳,重写就会发生。在我看来,重写发生在会话启动的同一个缓冲区外壳中,而不是在最终输出阶段。

如果您需要像上面一样的场景,使用一个“内部外壳”会有所帮助

ob_start();
ob_start(); // 添加内部缓冲区外壳
session_start();
echo '<a href=".">self link</a>';
ob_end_flush(); // 关闭内部外壳将激活 URL 重写
$data = ob_get_contents();
ob_end_clean();
echo $data;

如果您对此感兴趣,或者像我一样认为这是一个设计缺陷而不是一个特性,请访问错误 #35933 (http://bugs.php.net/bug.php?id=35933) 并评论它。
1
clancy hood at gmail dot com
14 年前
使用 ob 回调:请注意,发送到您方法的第二个参数将无法帮助您区分刷新调用和 ob_clean 调用,但缓冲区内容在两种情况下都会发送,因此您最终会解析不使用的数据。另外,请注意常量 PHP_OUTPUT_HANDLER_START 实际上从未发送,而是在第一次刷新时出现整数“3”。
<?php

function ob_handler($string, $flag){
static
$input = array();
$done = false;
switch(
$flag){
case
PHP_OUTPUT_HANDLER_START:
$flag_sent = "PHP_OUTPUT_HANDLER_START ($flag)";
break;
case
PHP_OUTPUT_HANDLER_CONT:
$flag_sent = "PHP_OUTPUT_HANDLER_CONT ($flag)";
break;
case
PHP_OUTPUT_HANDLER_END:
$done = true;
$flag_sent = "PHP_OUTPUT_HANDLER_END ($flag)";
break;
default:
$flag_sent = "Flag is not a constant ($flag)";
}
$input[] = "$flag_sent: $string<br />";
$output = "$string<br />";
if(!
$done) return $output;
// print_r($input, 1) causes an error and var_export just doesn't work
$output .= '<br />';
foreach(
$input as $k=>$v) $output .= "$k: $v";
return
$output;
}

ob_start('ob_handler');

echo
'flush';
ob_flush();

echo
'flush 2';
ob_flush();

echo
'clean';
ob_clean();

echo
'flush 3';
ob_flush();

echo
'end flush';
ob_end_flush();
?>

flush
flush 2
flush 3
end flush

0: Flag is not a constant (3): flush
1: PHP_OUTPUT_HANDLER_CONT (2): flush 2
2: PHP_OUTPUT_HANDLER_CONT (2): clean
3: PHP_OUTPUT_HANDLER_CONT (2): flush 3
4: PHP_OUTPUT_HANDLER_END (4): end flush

我认为 START 标志问题 *可能* 是一个错误,但我无法在报告之前升级,因为我必须使用与服务器相同的版本(我使用的是 PHP 5.2.6)。如果有人有 5.2.11 或其他稳定版本,请随时根据需要进行测试/报告。
1
Charlie Farrow
16 年前
在某些异常情况下,当在 ob_start() 内部对无法执行的操作(由于对象不存在或方法不存在)执行对象操作时,脚本将退出并打印当前函数生成的所有内容,但不会打印其他内容,包括错误消息。

我无法理解为什么没有错误消息出现,并且正在尝试为开发人员获得一个比整个程序更简单的示例!

因此,如果您使用的是 ob_start() 并且没有输出,请检查您的对象... 您在某个地方犯了错误。唯一的问题是您将不知道错误在哪里,因为没有错误消息!
1
admin at bobfrank dot org
18 年前
如果您想在您创建的字符串中间运行代码,但您想等待打印...
(因此,如果您想在 bb-code 样式中允许使用 php,并且您想按顺序执行它,并按顺序打印所有内容...)

phpRun($code) {
ob_start();
exec($code);
$output = ob_get_contents();
ob_end_clean();
return $output;
}

$str = str_replace("]\n", "]", $str);
$match = array('#\[php\](.*?)\[\/php\]#se');
$replace = array( phpRun( stripslashes('$1') ) );
$str= preg_replace($match, $replace, $str);

echo $str;
2
rafa dot chacon at factorydea dot com
19 年前
如果您尝试通过 require_once 在循环中包含一个 php 文件(例如,一个动态电子邮件模板)并更改某些变量的值(例如,取消订阅的 URL,每个用户的值不同),您应该使用

<?php

// ... some code

$usermail = array("email1", "email2", ...);

for(
$i = 0; $i < $MAX; $i++)
{
$usermail_unsuscribe = $usermail[$i];
ob_start();
include(
"email_template.php");
ob_clean();
}
?>

否则 $usermail_unsuscribe 将只获得 "email1" 值。
1
Anonymous
19 年前
我通常将页面分成四个部分 - 变量初始化、导入页眉(使用刚刚声明的变量进行配置)、主体内容(主要是非 PHP)、导入页脚。我想知道如果将主页面包含到另一个 PHP 脚本中,主页面是否可以被另一个 PHP 脚本检查。我发现可以通过在页眉末尾使用一个未关闭的函数来控制主体内容的输出,该函数在页脚开始时结束,从而将主体内容括起来。然后可以使用输出缓冲来将此内容读入变量。作为如何使用此方法来控制输出顺序的演示,请查看此示例

<?php
$output
= "";

// 回调以处理缓冲的输出
function capture($buffer)
{
$GLOBALS['output'] .= $buffer;
return
"C ";
}

// 使用输出捕获调用 printE() 函数
function captureE()
{
ob_start("capture");
printE();
ob_end_flush();
}
?>

A
<?php
// 输出 'E'(在示例场景中为主体内容)
function printE()
{
// (在此行之后结束页眉) ?>
E
<?php // (从此行开始页脚)
}
?>
B
<?php captureE(); ?>
D
<?php print $output; ?>
F
<?php printE(); ?>
G

输出为 A B C D E F E G。

对于我上面提到的应用程序,需要注意两点
- 单独执行的页面必须输出其主体内容,但检查脚本应该抑制这一点,例如通过在包含页面之前设置一个变量,然后在页脚输出行中检查该变量。
- 由于主体内容现在位于函数内,因此它具有不同的命名空间,因此可能需要进行更改以防止代码中断(例如,使用全局变量、处理在主体内容内定义的函数)。
0
mariusads at helpedia dot com
15 年前
如果您想使用 ob_start("ob_gzhandler");,请确保您使用的编辑器不会在脚本开头添加 UTF8/UTF16 BOM。

如果存在这三个字符,像 Firefox 这样的浏览器将无法解码页面,并会报告

内容编码错误

您尝试查看的页面无法显示,因为它使用了无效或不受支持的压缩形式。

您尝试查看的页面无法显示,因为它使用了无效或不受支持的压缩形式。

Google Chrome 将简单地报告 "错误 2 (net::ERR_FAILED): 未知错误。"。

将 ob_start 命令注释掉后,页面可以成功加载,浏览器通常会检测到 BOM 并且不会在页面上显示它,因此一切都难以调试。
0
fordiman at gmail dot com
16 年前
以下是一个我每天使用的巧妙函数。本质上:包含一个 PHP 文件 - 但将其输出呈现到变量中,而不是缓冲区中。它还设置为使用设置的变量加载脚本,并自动将全局变量加载到脚本的命名空间中,使其成为有效的模板方案。它还具有错误处理,因此您在使用输出缓冲时不会盲目行动。

<?php
$GLOBALS
['BufferedErrors']=Array();
function
errorParse($errno, $errstr, $errfile, $errline, $errcontext) {
$errorTypes = Array(
E_ERROR => '致命错误',
E_WARNING => '警告',
E_PARSE => '解析错误',
E_NOTICE => '通知',
E_CORE_ERROR => '致命核心错误',
E_CORE_WARNING => '核心警告',
E_COMPILE_ERROR => '编译错误',
E_COMPILE_WARNING => '编译警告',
E_USER_ERROR => '触发错误',
E_USER_WARNING => '触发警告',
E_USER_NOTICE => '触发通知',
E_STRICT => '弃用通知',
E_RECOVERABLE_ERROR => '可捕获的致命错误'
);
$ret=(object)Array(
'number'=>$errno,
'message'=>$errstr,
'file'=>$errfile,
'line'=>$errline,
'context'=>$errcontext,
'type'=>$errorTypes[$errno]
);
$GLOBALS['BufferedErrors'][]=$ret;
return
false;
}
function
parse($fileToInclude, $argumentsToFile=false) {
$bufferedErrorStack = $GLOBALS['BufferedErrors'];
set_error_handler('errorParse', error_reporting());
$GLOBALS['BufferedErrors']=Array();

if (!
file_exists($fileToInclude))
return
'';
if (
$argumentsToFile === false)
$argumentsToFile = Array();
$argumentsToFile = array_merge($GLOBALS, $argumentsToFile);
foreach (
$argumentsToFile as $variableName => $variableValue)
$
$variableName = $variableValue;
ob_start();
include(
$fileToInclude);
$ret = ob_get_contents();
ob_end_clean();

restore_error_handler();
$errors = $GLOBALS['BufferedErrors'];
$GLOBALS['BufferedErrors'] = $bufferedErrorStack;
if (
count($errors)>0) {
$ret.='<ul class="error">';
foreach (
$errors as $error)
$ret.=
'<li>'.
'<b>'.$error->type.'</b>: '.
$error->message.
'<blockquote>'.
'<i>file</i>: '.$error->file.'<br />'.
'<i>line</i>: '.$error->line.
'</blockquote>'.
'</li>';
$ret.='</ul>';
}
return
$ret;
}
0
coldshine at gmail dot com
16 年前
参考 dan at roteloftet dot com 的评论

RFC 2616 (HTTP) 指定了一个“透明”的内容编码,“identity”(§ 3.5),它很适合您尝试使用(无效)“None”来做的事情。因此,这个方法同样有效,并且符合 RFC

<?php
header
('Content-Encoding: identity', true);
?>
0
tracey AT archive DOT org
17 年前
方法是将所有 stdout 和 stderr 写入日志
从 *内部* 一个 php 脚本中。
您只需要确保在
一段时间内调用 elog() 来获取输出。
这是一种将脚本“守护进程化”以进行日志记录的好方法。

// 这使我们能够捕获所有 stdout 和 stderr(以及 error_log() 调用)
// 到这个日志文件中...
// “收集的输出”将在每次使用“elog()”时刷新...
ini_set("error_log", "/var/log/script.log");
ob_start();

function elog($str)
{
// 获取写入 stdout 或 stderr 但 *没有* 使用 elog() 的任何内容
// 并立即写入...
$writeme = ob_get_contents();
if ($writeme)
{
error_log($writeme);
ob_end_clean();
ob_start();
}
// 现在写入调用此方法时使用的消息
error_log($str);
}
0
butch at enterpol dot pl
17 年前
简单的代码使 phpsession $_GET 适用于有效的 XHTML 1.0 Transitional :)

function callback($buffer)
{
$buffer = str_replace("&PHPSESSID", "&amp;PHPSESSID", $buffer);
return $buffer;
}

ob_start("callback");

session_start();
0
Aleksey
19 年前
此函数动态更改 HTML 页面的标题

function change_title($new_title) {
$output = ob_get_contents();
ob_end_clean();

$output = preg_replace("/<title>(.*?)<\/title>/", "<title>$new_title</title>", $output);
echo $output;
}

示例
ob_start();
// ... 一些输出
change_title('NEW TITLE!');
-1
me at haravikk dot com
12 年前
我认为值得注意的是,虽然您无法从回调函数中调用任何输出函数,例如 echo 或 print,但您仍然可以发送标头(可能包括 cookie,尚未检查)。当然,这仅在第一个回调中有效,如下所示

<?php

function myCallback($buffer, $flags) {
if (
$flags & PHP_OUTPUT_HANDLER_START) {
header('Server: LastMinuteHeaderServer');
}
}

ob_start('myCallback');
echo
"Hello World!";
ob_end_flush();

?>

这不是最鼓舞人心的例子,但在这种情况下,代码能够在响应的标头部分发送之前偷偷地插入一个最后一分钟的标头。如果您想避免替换不确定的标头值,这将非常有用。

例如,如果您的代码可能返回一张图像,但您不想在确定可以成功发送图像之前设置内容类型,则可以使用回调将决定推迟到最后一刻,那时您应该确定 HTTP 主体中发送的内容。
-1
dan at roteloftet dot com
16 年前
一些 Web 托管服务器(至少我的服务器有)在其 php.ini 中有以下设置
output_handler = ob_gzhandler

这被证明对返回图像或一般二进制文件的 php 脚本存在问题,因为无法确定压缩文件的长度。

由于我花了很多时间在网上搜索解决方法(由于各种原因,.htaccess 修改不可行),我发现这对于取消 php.ini 中指定的 ob_gzhandler 很有效

<?php
while (ob_get_level())
ob_end_clean();
header("Content-Encoding: None", true);
?>

将此放在脚本的最顶部,在任何其他内容写入页面之前,脚本结果将不会被压缩。
-1
Bitwise
14 年前
没有启动标志问题。只需要注意到第二个参数不是模式,而是由按位或运算的标志组成的。

<?php
function ob_handler($string, $flags) {
static
$input = array();
if (
$flags & PHP_OUTPUT_HANDLER_START )
$flags_sent[] = "PHP_OUTPUT_HANDLER_START";
if (
$flags & PHP_OUTPUT_HANDLER_CONT )
$flags_sent[] = "PHP_OUTPUT_HANDLER_CONT";
if (
$flags & PHP_OUTPUT_HANDLER_END )
$flags_sent[] = "PHP_OUTPUT_HANDLER_END";
$input[] = implode(' | ', $flags_sent) . " ($flags): $string<br />";
$output = "$string<br />";
if (
$flags & PHP_OUTPUT_HANDLER_END ) {
$output .= '<br />';
foreach(
$input as $k => $v) $output .= "$k: $v";
}
return
$output;
}

ob_start('ob_handler');

echo
'flush';
ob_flush();

echo
'flush 2';
ob_flush();

echo
'clean';
ob_clean();

echo
'flush 3';
ob_flush();

echo
'end flush';
ob_end_flush();
?>

flush
flush 2
flush 3
end flush

0: PHP_OUTPUT_HANDLER_START | PHP_OUTPUT_HANDLER_CONT (3): flush
1: PHP_OUTPUT_HANDLER_CONT (2): flush 2
2: PHP_OUTPUT_HANDLER_CONT (2): clean
3: PHP_OUTPUT_HANDLER_CONT (2): flush 3
4: PHP_OUTPUT_HANDLER_END (4): end flush
-1
Filip Dalge
12 年前
当抛出致命错误时,PHP 会在打印错误消息之前输出输出控制的当前缓冲区,而不会进行后处理。如果您使用多个输出控制级别,这可能不会产生预期的行为。

您可以使用输出回调处理程序来处理这种情况并丢弃输出。

因此,将 ob_start("ob_error_handler") 与以下代码结合使用

function ob_error_handler($str) {
$error = error_get_last();
if ($error && $error["type"] == E_USER_ERROR || $error["type"] == E_ERROR) {
return ini_get("error_prepend_string").
"\nFatal error: $error[message] in $error[file] on line $error[line]\n".
ini_get("error_append_string");
}
return $str;
}
-1
bty-adminf2 at trebly dot net
9 年前
这涉及到 ob_start() 使用的默认值。

请注意,我已验证 ob_start() 和 ob_start(null,0) 不总是等效的。

似乎使用 ob_start() 时,输出缓冲区会使用您配置中当前的默认参数打开,前提是这些参数已被显式定义。

因此,如果您以前已将 $chunk_size 设置为任何值,并且发送的数据大于 $chunk_size,则数据将自动按 $chunk_size 的块大小刷新。

如果您显式定义 $chunk_size=0,稍后,当您使用任何函数(例如 $my_ob_dataoutput=ob_get_clean();)时,您将获得输出的完整内容(使用 $chunk_size=0 时,它几乎是无限的)。

我发现这一点是因为我的变量 $my_ob_dataoutput 被截断。使用 "ob_get_status (true)" 函数,我可以验证一个错误(在较低级别或默认情况下不受我的控制)之前将 ob 的 $chunk_size 设置为 4096。
我将 "ob_start()" 更改为 "ob_start(null,0)",一切都正常了。
-1
cj at ceejayoz dot com
15 年前
请注意,从 PHP 5.1.x 开始,所有对象在输出缓冲区回调函数执行之前都会调用它们的析构函数。因此,全局化的对象在函数中将无法按预期使用。

根据 http://bugs.php.net/bug.php?id=40104,这是预期的行为。
-1
mbutscher at gmx dot de
7 年前
如果出现错误,输出缓冲区的内容将与错误消息一起显示。如果必须确保这种情况绝对不会发生,您可以执行以下操作(但这也会消耗掉错误消息)

<?php
$outputbufferOutput
= FALSE;

function
switchableOutputHandler($buffer) {
global
$outputbufferOutput;

if (
$outputbufferOutput)
return
$buffer;
else
return
"";
}

function
internob_start() {
global
$outputbufferOutput;

$outputbufferOutput = FALSE;
return
ob_start("switchableOutputHandler");
}

function
internob_get_clean() {
global
$outputbufferOutput;

$outputbufferOutput = TRUE;
$result = ob_get_clean();
$outputbufferOutput = FALSE;
return
$result;
}

?>

然后,您可以使用 internob_start() 和 internob_get_clean() 代替 ob_start() 和 ob_get_clean()。其他函数可以相应地替换。
-1
geoffrey at nevra dot net
19 年前
使用 ob_start() 的回调时,ob_get_contents() 等函数不会使用它,请使用 ob_end_flush() 代替。

注意:尚未对所有 ob_* 函数进行测试,仅测试了 ob_get_contents() 和 ob_end_flush()
-2
Anonymous
13 年前
如果您需要对标志进行可读的表示,以下是一个作为私有类成员的变体

<?php
class foo {
private function
getFlagsReadable($flags) {
$flagNames = array('PHP_OUTPUT_HANDLER_START', 'PHP_OUTPUT_HANDLER_CONT', 'PHP_OUTPUT_HANDLER_END');
$readable = '';
foreach(
$flagNames as $flagName)
if (
$flags & constant($flagName) )
$readable .= (strlen($readable) ? ' | ' : '') . $flagName
;
return
$readable;
}
}
?>
-2
Francois Hill
16 年前
根据 clement dot ayme at st dot com 的评论

根据我的经验,输出似乎被缓冲了,但也发送到了标准输出!
-2
codextasy at gmail dot com
15 年前
文档和实际回调函数调用之间存在差异。
手册说:"当调用 ob_end_flush() 或在请求结束时将输出缓冲区刷新到浏览器时,将调用该函数。"

实际上,一旦通过 ob_start() 设置回调函数,无论如何都会调用该函数。
以下函数会立即调用回调函数
ob_clean
ob_end_clean
ob_end_flush
ob_flush
ob_get_clean

但只有其中两个函数返回回调返回的结果(ob_end_flush,ob_flush),其他函数会丢弃它。

在请求结束时,即使没有调用上面列出的任何函数,回调也会被调用,并且它的结果将返回到浏览器(好吧,至少这与手册一致)。

还有一个技巧
如果您使用 chunk_size > 1 设置回调函数,则每次输出缓冲区等于或超过 chunk_size 时,都会调用回调函数,并且它的结果将输出到浏览器,即使您稍后调用了任何 ob_clean()、ob_end_clean()、ob_get_clean() 函数,因此请注意这一点。
-1
Anonymous
21 年前
如果您使用的是 Apache(1.3x 或 2.0),您可能需要考虑将自动压缩功能添加到您的交付页面。

我假设你们都知道如何在程序中构建压缩类并使用它们,但没有人能够提供与二进制编译模块相同的速度和健壮性。此外,此类模块还会将“可压缩”命中记录到 Web 日志文件中,从而允许您最喜欢的 Web 分析程序向您显示节省带宽的报告。

话虽如此,您可能需要考虑以下两个模块用于 Apache

1) Apache 1.3x:使用 mod_gzip,可从以下位置获取
http://sourceforge.net/projects/mod-gzip/

2) Apache 2.x:使用 mod_gz,请参阅此处
http://www.mail-archive.com/[email protected]/msg00734.html

3) Apache 1.3x:您可能还想使用 mod_defalte,可从以下位置获取
ftp://ftp.lexa.ru/pub/apache-rus/contrib/

希望有帮助。
-1
aaron at offtone.com
19 年前
我的回调存储在一个函数类中,使用 ob_start ('Class::callback') 不起作用。不想实例化类(没有必要,它是一个函数类),我尝试了以下方法,它非常有效

ob_start (array (Class, 'callback'));

PHP 4.3.4
-1
simon
18 年前
发现调用 ob_start() 后,类实例中的变量没有被设置。
在设置变量后调用 ob_start,它可以工作,但这似乎没有解决自包含模板类的目标。
解决方法是用“&new”按引用分配类。
以下是一个简化的工作示例
<?php
class Buffer {
var
$template = ' - template set in class constructor';
function
Buffer() {
$this->startBuffer();
}
function
startBuffer() {
ob_start(array(&$this, 'doFlush'));
}
function
doFlush($buffer) {
/* 简单字符串连接,显示模板字符串和缓冲区输出的使用 */
return $buffer . $this->template;
}
}
/* 模板没有设置:
$buffer1 = new Buffer();
$buffer1->template = ' - template set in instance';
echo 'some buffer content';
*/
/* 这按预期工作 */
$buffer2 = &new Buffer();
$buffer2->template = ' - template set in instance';
echo
'some buffer content';
-1
oersoep at gmail dot com
19 年前
这些很方便。第一个之前已经提到过。

ob_start( array( 'lib_class', 'parse_output' ) );
ob_start( array( $this, 'parse_output' ) );

注意:$this 不是引用。回调保存或记录的任何内容都会在 ob_start 使用的克隆中消失。
它确实允许回调使用 $this 的属性,例如 $this->ar_tpl_value 或你喜欢的任何风格。

手册中说
“如果传递了可选参数 chunk_size,则回调函数将在每输出 chunk_size 字节后的第一个换行符处被调用。通过传递 NULL 值可以绕过 output_callback 参数。”
这在我的 4.3.11 上不起作用。可能是 Zend 优化器的问题。不敢关闭它去看。
-4
php REMOVETHIS at kennel17 dot co dot uk
2 年前
从回调函数返回 false 时要非常小心!

尽管文档中说了什么,但这与返回未修改的提供的 $buffer 值*并不*相同。在“清理”操作(例如 ob_clean())的上下文中,这会导致你的回调函数被禁用,并且在任何后续输出中都会被跳过。

答案是确保你始终从回调中返回一个字符串值。这很可能是一个错误,它可能在某个时候会被修复,但它适用于 PHP 5.4 以后的所有版本,直到当前版本(在撰写本文时为 8.1)。注意,它在 PHP 5.3 及更低版本上不是问题。

在这个 Stack Overflow 问题中可以了解到更多详细信息:https://stackoverflow.com/questions/69845122/

在这个 fiddle 中设置了问题的演示:https://3v4l.org/66cai
To Top