PHP Conference Japan 2024

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 条笔记

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();
?>
net_navard at yahoo dot com
18年前
大家好

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

使用 ob_start 的最常见原因是收集否则会发送到浏览器的输出数据。

以下是 ob_start() 的两种用法:

1- 您可以更好地控制输出。简单的例子:假设您想向用户显示错误消息,但脚本已经向浏览器发送了一些 HTML。这看起来很丑,页面只渲染了一半,然后才显示错误消息。使用输出缓冲功能,您可以简单地删除缓冲区,只发送错误消息,这样看起来就整洁多了。
2- 输出缓冲的最初目的是创建一个无缝的传输过程:php 引擎 -> apache -> 操作系统 -> 网页用户

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

此致,Hossein
ed.oohay (a) suamhcs_rodnan
21年前
输出缓冲甚至可以在嵌套作用域或递归结构中使用……希望这可以节省一些人猜测和测试的时间 :)

<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 上有效

Nandor =)
mjr
20年前
如果您在 PHP 中使用面向对象的代码,您可能像我一样希望使用对象内部的回调函数(即类函数)。在这种情况下,您向 ob_start 发送一个包含两个元素的数组作为其单个参数。第一个元素是对象的名称(开头没有 $),第二个元素是要调用的函数。因此,要在名为 '$template' 的对象中使用函数 'indent',您将使用 <?php ob_start(array('template', 'indent')); ?>
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();
}
?>

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

function __construct ()
{

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

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

$handlers = ob_list_handlers();
while ( ! empty($handlers) ) {
ob_end_clean();
$handlers = ob_list_handlers();
}
Chris
14年前
小心使用更改页面标头的函数;结束输出缓冲时不会撤消该更改。

例如,如果您有一个生成图像并设置相应标头的类,则在 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)。
jkloss at hotmail dot com
20年前
如果 ob_start 对您不起作用,请注意,在 Apache 2 中,flush() 函数会导致 PHP 无论是否在 flush 之前调用 ob_start,都会发送标头。

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

将导致 Apache 2 发送任何可能堆叠的标头——这意味着您不能在 flush 之后使用 header(location:xxx)。要修复,请删除 flush()。我花了几个小时才发现这一点。Apache 1.x 的工作方式不同。
lucky760 at yahoo dot com
18年前
作为对下面发布的 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;
}
}

?>
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;

如果您对此感兴趣,或者像我一样认为这是一个设计缺陷而不是特性,请访问bug #35933 (http://bugs.php.net/bug.php?id=35933) 并发表评论。
clancy hood at gmail dot com
15年前
使用`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 不是常量 ($flag)";
}
$input[] = "$flag_sent: $string<br />";
$output = "$string<br />";
if(!
$done) return $output;
// print_r($input, 1) 会导致错误,而 var_export 则不起作用
$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 不是常量 (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 标记问题*可能*是一个bug,但我无法在报告之前升级,因为我必须使用与服务器相同的版本(我使用的是 PHP 5.2.6)。如果有人有 5.2.11 或其他稳定版本,请随意测试/报告。
Charlie Farrow
17年前
在某些异常情况下,当在 `ob_start()` 内部对无法执行操作的对象(因为对象不存在或方法不存在)执行操作时发生错误,脚本将退出并打印当前函数在错误之前生成的所有内容,但不会打印其他任何内容,包括错误消息。

我不明白为什么没有错误消息出现,并且我正在尝试为开发人员获取一个比我的整个程序更简单的可运行示例!

因此,如果您正在使用 `ob_start()` 并且没有输出,请检查您的对象……您在某个地方对它们犯了错误。唯一的问题是您不知道在哪里,因为没有错误!!
admin at bobfrank dot org
19年前
如果您想在创建的字符串中间运行代码,但想等待打印……
(因此,如果您想允许类似于 BB 代码的 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;
rafa dot chacon at factorydea dot com
19年前
如果您尝试通过 `require_once` 在循环中包含一个 php 文件(例如,一个动态电子邮件模板)并更改某些变量的值(例如,每个用户不同的取消订阅 URL),则应使用

<?php

// ...一些代码

$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" 值。
匿名
19年前
我通常将我的页面分成四个部分——变量初始化、导入页眉(使用刚刚声明的变量进行配置)、主体(大部分是非 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。

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

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

内容编码错误

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

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

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

在注释掉 `ob_start` 命令的情况下,页面成功加载,浏览器通常会检测到 BOM 并不会在页面上显示它,因此一切都很难调试。
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>文件</i>: '.$error->file.'<br />'.
'<i>行号</i>: '.$error->line.
'</blockquote>'.
'</li>';
$ret.='</ul>';
}
return
$ret;
}
coldshine at gmail dot com
16年前
参考dan at roteloftet dot com的评论

RFC 2616 (HTTP) 指定了一个“透明”的Content-Encoding,“identity”(§ 3.5),这很好地符合您尝试使用(无效的)“None”所做的事情。所以这个同样有效,而且也符合RFC

<?php
header
('Content-Encoding: identity', true);
?>
tracey AT archive DOT org
17年前
方法:使所有标准输出和标准错误写入日志
从PHP脚本*内部*。
您只需要确保每隔一段时间调用一次elog()
来获取输出。
这是一种使脚本关于其日志记录“守护进程化”的好方法。

// 这允许我们捕获所有标准输出和标准错误(以及error_log()调用)
// 到这个日志文件……
// “收集的输出”将在任何时候使用“elog()”刷新……
ini_set("error_log", "/var/log/script.log");
ob_start();

function elog($str)
{
// 获取写入标准输出或标准错误的任何内容,这些内容*没有*使用elog()
// 并立即写入……
$writeme = ob_get_contents();
if ($writeme)
{
error_log($writeme);
ob_end_clean();
ob_start();
}
// 现在写入使用此方法调用的消息
error_log($str);
}
butch at enterpol dot pl
18年前
简单的代码,使phpsession $_GET 对于有效的XHTML 1.0 Transitional 友好 :)

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

ob_start("callback");

session_start();
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!');
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正文中发送的内容。
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);
?>

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

<?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
Filip Dalge
12年前
当抛出致命错误时,PHP会在打印错误消息之前,输出输出控制的当前缓冲区,而不会进行后处理。如果您正在使用多个输出控制级别,这可能不会产生预期的行为。

您可以使用输出回调处理程序来处理此问题并丢弃输出。

因此,将`ob_start("ob_error_handler")`与以下内容结合使用

函数 `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;
}
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)”,一切恢复正常。
cj at ceejayoz dot com
15年前
请注意,自PHP 5.1.x起,所有对象的析构函数都将在输出缓冲区回调函数执行之前调用。因此,全局对象将无法按预期在函数中使用。

根据http://bugs.php.net/bug.php?id=40104,这被声明为预期行为。
mbutscher at gmx dot de
8年前
如果发生错误,输出缓冲区的内容将与错误消息一起显示。如果确实非常重要,决不能发生这种情况,您可以执行以下操作(但这也会消耗错误消息)

<?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()`。其他函数可以相应地替换。
geoffrey at nevra dot net
19年前
当使用回调函数与`ob_start()`时,像`ob_get_contents()`这样的函数不会使用它,而是使用`ob_end_flush()`。

注:并非所有`ob_*`函数都经过测试,仅测试了`ob_get_contents()`和`ob_end_flush()`。
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;
}
}
?>
Francois Hill
17年前
根据clement dot ayme at st dot com的评论

根据我的经验,输出似乎是被缓冲的,但也发送到标准输出!
codextasy at gmail dot com
16年前
文档和实际回调函数调用之间存在差异。
手册中说:“当调用`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()`,也要注意这一点。
Anonymous
22年前
如果您使用的是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/

希望对您有所帮助。
aaron at offtone.com
20年前
我的回调函数存储在一个函数类中,使用`ob_start ('Class::callback')`不起作用。不想实例化类(不需要,它是一个函数类),我尝试了这个,它运行得很完美:

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

PHP 4.3.4
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) {
/* simple string concat to show use of a
template string and the buffer output */
return $buffer . $this->template;
}
}
/* template does not get set:
$buffer1 = new Buffer();
$buffer1->template = ' - template set in instance';
echo 'some buffer content';
*/
/* this works as expected */
$buffer2 = &new Buffer();
$buffer2->template = ' - template set in instance';
echo
'some buffer content';
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优化器的问题。不敢关闭它去查看。
php REMOVETHIS at kennel17 dot co dot uk
3年前
从回调函数返回`false`时要非常小心!

尽管文档中说明了这一点,但这与返回未修改的`$buffer`值并不相同。在“清除”操作(例如`ob_clean()`)的上下文中,这将导致您的回调函数被禁用并跳过任何后续输出。

解决方法是确保始终从回调函数返回字符串值。这可能是一个bug,以后可能会被修复,但这适用于从PHP 5.4到当前版本(撰写本文时为8.1)的所有版本。请注意,在PHP 5.3及以下版本中这不是问题。

可以在这个Stack Overflow问题中看到更多细节:https://stackoverflow.com/questions/69845122/

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