# 用户级输出缓冲区

## 目录

- 什么输出会被缓冲？
- 嵌套输出缓冲区
- 缓冲区大小
- 缓冲区中允许的操作
- 输出处理程序
- 使用输出处理程序
- 传递给输出处理程序的 flag
- 输出处理程序的返回值

用户级输出缓冲区可以从 PHP 代码中启动、操作和终止。 每个缓冲区都包括一个输出缓冲区和一个关联的输出处理程序函数。

## 打开输出缓冲

可以通过使用 ob_start() 函数或设置 output_buffering 和 output_handler php.ini 配置来启用输出缓冲。虽然两者都可以创建输出缓冲区，但 ob_start() 更加灵活，因为接受用户定义的函数作为输出处理程序，并且还可以设置对缓冲区允许的操作（冲刷、清空、移除）。通过 ob_start() 开始的缓冲区将从调用该函数的行开始生效，而通过 output_buffering 开始的缓冲区将从脚本的第一行开始缓冲输出。

PHP 还自带了内置的 `"URL-Rewriter"` 输出处理程序，它会启动自己的输出缓冲区，并且任何时候最多只允许有两个实例运行（一个用于用户级别的 URL 重写，另一个用于透明 Session ID 支持）。这些缓冲区可以通过调用 output_add_rewrite_var() 函数或启用 session.use_trans_sid php.ini 设置来启动。

捆绑的 `zlib` 扩展有自己的输出缓冲区，可以使用 zlib.output_compression php.ini 设置启用。

> 注意: `"URL-Rewriter"` 的特殊之处在于同时只允许运行最多两个实例，但所有用户级输出缓冲区都使用 ob_start() 所使用的相同底层缓冲区，并通过自定义的输出处理函数来实现其功能。因此，它们的所有功能都可以由用户级代码模拟。

## 冲刷、访问和清理缓冲区内容

冲刷会发送并丢弃活动缓冲区中的内容。当输出的大小超过缓冲区大小时，或者在脚本结束时，又或者是调用了 ob_flush()、ob_end_flush() 或 ob_get_flush() 函数时，会冲刷输出缓冲区。

警告

调用 ob_end_flush() 或 ob_get_flush() 将关闭活跃的缓冲区。

警告

冲刷缓冲区将冲刷输出处理程序的返回值，这可能与缓冲区的内容不同。 例如，使用 ob_gzhandler() 将压缩输出并冲刷压缩后的输出。

可以通过调用 ob_get_contents()、ob_get_clean() 或 ob_get_flush() 来检索活动缓冲区的内容。

如果只需要缓冲区内容的长度，ob_get_length() 或 ob_get_status() 将返回内容的字节长度。

警告

调用 ob_get_clean() 或 ob_get_flush() 将在返回其内容后关闭活跃的缓冲区。

可以通过调用 ob_clean()、ob_end_clean() 或 ob_get_clean() 来清理活动缓冲区的内容。

警告

调用 ob_end_clean() 或 ob_get_clean() 将关闭活跃的缓冲区。

## 关闭缓冲区

可以通过调用 ob_end_clean()、ob_end_flush()、 ob_get_flush() 或 ob_get_clean() 来关闭输出缓冲区。

警告

没有使用 `PHP_OUTPUT_HANDLER_REMOVABLE` 标志启动的输出缓冲区不能关闭，可能会生成一个 `E_NOTICE`。

脚本结束时或调用 exit() 时，所有未关闭的输出缓冲区都将被冲刷并关闭。 缓冲区将按照它们启动的相反顺序被冲刷并关闭。 最后启动的缓冲区将首先被冲刷，最先启动的缓冲区将最后被冲刷并关闭。

警告

如果不希望冲刷缓冲区的内容，应该使用自定义的输出处理程序来防止在关闭期间冲刷缓冲区的内容。

## 输出处理程序中抛出异常

如果在输出处理程序中抛出未捕获的异常，则程序将终止，并在关闭过程中调用处理程序后刷新 `"Uncaught Exception"` 错误消息。

如果未捕获的异常是在由 ob_flush()、ob_end_flush() 或 ob_get_flush() 调用的处理程序中引发的， 则在错误消息之前刷新缓冲区的内容。

如果在关闭过程中的输出处理程序中引发未捕获的异常，则程序终止，而不刷新缓冲区或错误消息。

> 注意: 如果处理程序抛出异常，则设置其 `PHP_OUTPUT_HANDLER_DISABLED` 状态标志。

## 输出处理程序中引发错误

如果在输出处理程序中引发非致命错误，则程序继续执行。

如果非致命错误是在由 ob_flush()、ob_end_flush() 或 ob_get_flush() 调用的处理程序中引发的， 则根据处理程序的返回值刷新特定的数据。 如果处理程序返回 `false` 则刷新缓冲区和错误消息。 如果返回其他任何值，则刷新处理程序的返回值，但不刷新错误消息。

> 注意: 如果处理程序返回 `false`，则设置其 `PHP_OUTPUT_HANDLER_DISABLED` 状态标志。

如果输出处理程序中出现致命错误，则程序终止，并在关闭过程中调用处理程序后刷新错误消息。

如果致命错误是在由 ob_flush()、ob_end_flush() 或 ob_get_flush() 调用的处理程序中引发的， 则在错误消息之前刷新缓冲区的内容。

如果在关闭过程中的输出处理程序中引发致命错误，则程序终止，而不刷新缓冲区或错误消息。

## 输出处理程序中输出

在特定情况下，处理程序中生成的输出与缓冲区的内容一起刷新。 此输出不会附加到缓冲区，也不是由 ob_get_flush() 返回的字符串的一部分。

在冲刷操作（调用 ob_flush()、ob_end_flush()、 ob_get_flush() 和关闭过程中）期间， 如果处理程序的返回值为 `false`，则缓冲区的内容将被刷新，然后输出。 如果处理程序在关闭过程中被调用，则抛出异常或调用 exit() 会导致相同的行为。

> 注意: 如果处理程序返回 `false`，则设置其 `PHP_OUTPUT_HANDLER_DISABLED` 状态标志。

## 输出处理程序状态 flag

每次调用输出处理程序时，缓冲区的 `flags` 位掩码的 处理程序状态标志 都会被设置，并且是由 ob_get_status() 返回的 `flags` 的一部分。 如果处理程序成功执行且未返回 `false`，则设置 `PHP_OUTPUT_HANDLER_STARTED` 和 `PHP_OUTPUT_HANDLER_PROCESSED`。 如果处理程序在执行时返回 `false` 或引发异常，则设置 `PHP_OUTPUT_HANDLER_STARTED` 和 `PHP_OUTPUT_HANDLER_DISABLED`。

> 注意: 如果处理程序设置了 `PHP_OUTPUT_HANDLER_DISABLED` 标志， 则不会调用 ob_end_clean()、ob_end_flush()、 ob_get_clean()、ob_get_flush()、 ob_clean()、ob_flush() 或 PHP 的关闭过程。 在 PHP 8.4.0 之前，调用 ob_clean() 或 ob_flush() 时，此标志不起作用。

`↑` and `↓` to navigate • `Enter` to select • `Esc` to close • `/` to open