wp_finalize_template_enhancement_output_buffer()
云策文档标注
概述
wp_finalize_template_enhancement_output_buffer() 函数用于完成模板增强输出缓冲的处理。它检查输出缓冲是否完整且包含 HTML,如果是,则通过 wp_template_enhancement_output_buffer 过滤器处理内容;否则返回原始内容。该函数还涉及错误处理和动作触发。
关键要点
- 函数接受两个参数:$output(字符串,必需)输出缓冲内容和 $phase(整数,必需)阶段标识。
- 返回处理后的输出缓冲字符串。
- 如果 $phase 包含 PHP_OUTPUT_HANDLER_CLEAN,则直接返回 $output,不应用过滤器。
- 检测内容类型是否为 HTML(如 text/html 或 application/xhtml+xml),若非 HTML 则触发 wp_finalized_template_enhancement_output_buffer 动作并返回原始内容。
- 通过 wp_template_enhancement_output_buffer 过滤器处理 HTML 输出,用于渐进增强如优化标记以提高前端性能。
- 设置自定义错误处理器以捕获和处理错误,避免执行中断。
- 触发 wp_finalized_template_enhancement_output_buffer 动作,在缓冲刷新前执行,但禁止启动新输出缓冲。
- 如果 display_errors 启用,将捕获的错误追加到输出缓冲中。
- 相关 Hook:do_action('wp_finalized_template_enhancement_output_buffer', string $output) 和 apply_filters('wp_template_enhancement_output_buffer', string $filtered_output, string $output)。
代码示例
function wp_finalize_template_enhancement_output_buffer( string $output, int $phase ): string {
// 当输出正在清理时(例如待处理模板被错误页面替换),不通过过滤器发送。
if ( ( $phase & PHP_OUTPUT_HANDLER_CLEAN ) !== 0 ) {
return $output;
}
// 检测响应是否为 HTML 内容类型。
$is_html_content_type = null;
$html_content_types = array( 'text/html', 'application/xhtml+xml' );
foreach ( headers_list() as $header ) {
$header_parts = explode( ':', strtolower( $header ), 2 );
if (
count( $header_parts ) === 2 &&
'content-type' === $header_parts[0]
) {
$media_type = trim( strtok( $header_parts[1], ';' ), " t" );
$is_html_content_type = in_array( $media_type, $html_content_types, true );
break; // PHP 只发送列表中的第一个 Content-Type 头。
}
}
if ( null === $is_html_content_type ) {
$is_html_content_type = in_array( ini_get( 'default_mimetype' ), $html_content_types, true );
}
// 如果内容类型不是 HTML,则短路返回,因为与增强无关。
if ( ! $is_html_content_type ) {
/** This action is documented in wp-includes/template.php */
do_action( 'wp_finalized_template_enhancement_output_buffer', $output );
return $output;
}
$filtered_output = $output;
$did_just_catch = false;
$error_log = array();
set_error_handler(
static function ( int $level, string $message, ?string $file = null, ?int $line = null ) use ( &$error_log, &$did_just_catch ) {
// 将用户错误转换为异常,以便捕获并返回缓冲。
if ( E_USER_ERROR === $level ) {
throw new Exception( __( 'User error triggered:' ) . ' ' . $message );
}
// 将捕获的异常显示为错误,因为它阻止任何输出缓冲过滤器应用。
if ( $did_just_catch ) { // @phpstan-ignore if.alwaysFalse(变量在下面的 catch 块中设置。)
$level = E_USER_ERROR;
}
// 如果 display_errors 启用,捕获报告的错误以追加到处理后的输出缓冲中。
if ( error_reporting() & $level ) {
$error_log[] = compact( 'level', 'message', 'file', 'line' );
}
return false;
}
);
$original_display_errors = ini_get( 'display_errors' );
if ( $original_display_errors ) {
ini_set( 'display_errors', 0 );
}
try {
/**
* 在发送到客户端之前过滤模板增强输出缓冲。
*
* 此过滤器仅应用于包含模板的 HTML 输出。此过滤器是渐进增强,
* 用于优化标记以提高前端页面加载性能等应用。站点不得依赖此过滤器应用,
* 因为它们可能选择流式传输响应。强烈不建议此过滤器的回调使用正则表达式进行任何替换。
* 使用 HTML API(WP_HTML_Tag_Processor 或 WP_HTML_Processor),或从 PHP 8.4 起使用 DOMHtmlDocument,它完全支持 HTML5。
*
* 在此过滤器期间不要打印任何输出。虽然过滤器通常不打印任何内容,但这尤其重要,
* 因为它在输出缓冲回调期间应用。在 PHP 8.5 之前,输出将被静默省略,之后将发出弃用通知。
*
* 重要:因为此过滤器在输出缓冲回调(即显示处理器)内部应用,
* 添加到过滤器的任何回调不得尝试启动自己的输出缓冲。否则,PHP 将引发致命错误:
* “Cannot use output buffering in output buffering display handlers.”
*
* @since 6.9.0
*
* @param string $filtered_output HTML 模板增强输出缓冲。
* @param string $output 原始 HTML 模板输出缓冲。
*/
$filtered_output = (string) apply_filters( 'wp_template_enhancement_output_buffer', $filtered_output, $output );
} catch ( Throwable $throwable ) {
// 作为警告而非错误发送到错误日志,以防止执行停止。
$did_just_catch = true;
trigger_error(
sprintf(
/* translators: %s is the throwable class name */
__( 'Uncaught "%s" thrown:' ),
get_class( $throwable )
) . ' ' . $throwable->getMessage(),
E_USER_WARNING
);
$did_just_catch = false;
}
try {
/**
* 在模板增强输出缓冲完成后触发。
*
* 这发生在模板增强输出缓冲刷新之前。在此动作期间不得打印任何输出;
* 在 PHP 8.5 之前,输出将被静默省略,之后将发出弃用通知。
* 尽管如此,可以发送 HTTP 头,这使得此动作与 'send_headers' 动作互补,
* 在 'send_headers' 中头可以在模板开始渲染之前发送。相比之下,
* 此 wp_finalized_template_enhancement_output_buffer 动作是可能发送 HTTP 头的点。
* 如果“模板增强输出缓冲”未启动,此动作不会触发。
* 如果在 wp_start_template_enhancement_output_buffer() 在 'wp_before_include_template' 动作
* 优先级 1000 运行之前添加此动作,输出缓冲将自动启动。在此之前,
* 如果添加了 'wp_template_enhancement_output_buffer' 过滤器,或者如果
* 'wp_should_output_buffer_template_for_enhancement' 过滤器返回 true,输出缓冲也将自动启动。
*
* 重要:因为此动作在输出缓冲回调(即显示处理器)内部触发,
* 添加到动作的任何回调不得尝试启动自己的输出缓冲。否则,PHP 将引发致命错误:
* “Cannot use output buffering in output buffering display handlers.”
*
* @since 6.9.0
*
* @param string $output 完成的输出缓冲。
*/
do_action( 'wp_finalized_template_enhancement_output_buffer', $filtered_output );
} catch ( Throwable $throwable ) {
// 作为警告而非错误发送到错误日志,以防止执行停止。
$did_just_catch = true;
trigger_error(
sprintf(
/* translators: %s is the class name */
__( 'Uncaught "%s" thrown:' ),
get_class( $throwable )
) . ' ' . $throwable->getMessage(),
E_USER_WARNING
);
$did_just_catch = false;
}
// 在返回刷新缓冲之前追加要显示的任何错误。
if ( $original_display_errors && 'stderr' !== $original_display_errors ) {
foreach ( $error_log as $error ) {
switch ( $error['level'] ) {
case E_USER_NOTICE:
$type = 'Notice';
break;
case E_USER_DEPRECATED:
$type = 'Deprecated';
break;
case E_USER_WARNING:
$type = 'Warning';
break;
default:
$type = 'Error';
}
if ( ini_get( 'html_errors' ) ) {
/*
* 改编自 PHP 内部:。
* 自闭合标签是 XHTML 过去的遗留!
*/
$format = "%sn%s: %s in %s on line %sn%s";
} else {
// 改编自 PHP 内部:。
$format = "%sn%s: %s in %s on line %sn%s";
}
$filtered_output .= sprintf(
$format,
ini_get( 'error_prepend_string' ),
$type,
$error['message'],
$error['file'],
$error['line'],
ini_get( 'error_append_string' )
);
}
ini_set( 'display_errors', $original_display_errors );
}
restore_error_handler();
return $filtered_output;
}注意事项
- 此函数在 WordPress 6.9.0 版本中引入。
- 在输出缓冲回调内部,避免启动新输出缓冲,否则会导致 PHP 致命错误。
- 过滤器 wp_template_enhancement_output_buffer 仅用于渐进增强,站点不应依赖其应用。
- 使用 HTML API 或 DOMHtmlDocument 进行 HTML 处理,避免正则表达式替换。
- 动作 wp_finalized_template_enhancement_output_buffer 可用于发送 HTTP 头,但禁止打印输出。
原文内容
Finalizes the template enhancement output buffer.
Description
Checks to see if the output buffer is complete and contains HTML. If so, runs the content through the wp_template_enhancement_output_buffer filter. If not, the original content is returned.
See also
Parameters
$outputstringrequired-
Output buffer.
$phaseintrequired-
Phase.
Source
function wp_finalize_template_enhancement_output_buffer( string $output, int $phase ): string {
// When the output is being cleaned (e.g. pending template is replaced with error page), do not send it through the filter.
if ( ( $phase & PHP_OUTPUT_HANDLER_CLEAN ) !== 0 ) {
return $output;
}
// Detect if the response is an HTML content type.
$is_html_content_type = null;
$html_content_types = array( 'text/html', 'application/xhtml+xml' );
foreach ( headers_list() as $header ) {
$header_parts = explode( ':', strtolower( $header ), 2 );
if (
count( $header_parts ) === 2 &&
'content-type' === $header_parts[0]
) {
/*
* This is looking for very specific content types, therefore it
* doesn’t need to fully parse the header’s value. Instead, it needs
* only assert that the content type is one of the static HTML types.
*
* Example:
*
* Content-Type: text/html; charset=utf8
* Content-Type: text/html ;charset=latin4
* Content-Type:application/xhtml+xml
*/
$media_type = trim( strtok( $header_parts[1], ';' ), " t" );
$is_html_content_type = in_array( $media_type, $html_content_types, true );
break; // PHP only sends the first Content-Type header in the list.
}
}
if ( null === $is_html_content_type ) {
$is_html_content_type = in_array( ini_get( 'default_mimetype' ), $html_content_types, true );
}
// If the content type is not HTML, short-circuit since it is not relevant for enhancement.
if ( ! $is_html_content_type ) {
/** This action is documented in wp-includes/template.php */
do_action( 'wp_finalized_template_enhancement_output_buffer', $output );
return $output;
}
$filtered_output = $output;
$did_just_catch = false;
$error_log = array();
set_error_handler(
static function ( int $level, string $message, ?string $file = null, ?int $line = null ) use ( &$error_log, &$did_just_catch ) {
// Switch a user error to an exception so that it can be caught and the buffer can be returned.
if ( E_USER_ERROR === $level ) {
throw new Exception( __( 'User error triggered:' ) . ' ' . $message );
}
// Display a caught exception as an error since it prevents any of the output buffer filters from applying.
if ( $did_just_catch ) { // @phpstan-ignore if.alwaysFalse (The variable is set in the catch block below.)
$level = E_USER_ERROR;
}
// Capture a reported error to be displayed by appending to the processed output buffer if display_errors is enabled.
if ( error_reporting() & $level ) {
$error_log[] = compact( 'level', 'message', 'file', 'line' );
}
return false;
}
);
$original_display_errors = ini_get( 'display_errors' );
if ( $original_display_errors ) {
ini_set( 'display_errors', 0 );
}
try {
/**
* Filters the template enhancement output buffer prior to sending to the client.
*
* This filter only applies the HTML output of an included template. This filter is a progressive enhancement
* intended for applications such as optimizing markup to improve frontend page load performance. Sites must not
* depend on this filter applying since they may opt to stream the responses instead. Callbacks for this filter
* are highly discouraged from using regular expressions to do any kind of replacement on the output. Use the
* HTML API (either `WP_HTML_Tag_Processor` or `WP_HTML_Processor`), or else use DOMHtmlDocument as of
* PHP 8.4 which fully supports HTML5.
*
* Do not print any output during this filter. While filters normally don't print anything, this is especially
* important since this applies during an output buffer callback. Prior to PHP 8.5, the output will be silently
* omitted, whereas afterward a deprecation notice will be emitted.
*
* Important: Because this filter is applied inside an output buffer callback (i.e. display handler), any
* callbacks added to the filter must not attempt to start their own output buffers. Otherwise, PHP will raise a
* fatal error: "Cannot use output buffering in output buffering display handlers."
*
* @since 6.9.0
*
* @param string $filtered_output HTML template enhancement output buffer.
* @param string $output Original HTML template output buffer.
*/
$filtered_output = (string) apply_filters( 'wp_template_enhancement_output_buffer', $filtered_output, $output );
} catch ( Throwable $throwable ) {
// Emit to the error log as a warning not as an error to prevent halting execution.
$did_just_catch = true;
trigger_error(
sprintf(
/* translators: %s is the throwable class name */
__( 'Uncaught "%s" thrown:' ),
get_class( $throwable )
) . ' ' . $throwable->getMessage(),
E_USER_WARNING
);
$did_just_catch = false;
}
try {
/**
* Fires after the template enhancement output buffer has been finalized.
*
* This happens immediately before the template enhancement output buffer is flushed. No output may be printed
* at this action; prior to PHP 8.5, the output will be silently omitted, whereas afterward a deprecation notice
* will be emitted. Nevertheless, HTTP headers may be sent, which makes this action complimentary to the
* 'send_headers' action, in which headers may be sent before the template has started rendering. In
* contrast, this `wp_finalized_template_enhancement_output_buffer` action is the possible point at which HTTP
* headers can be sent. This action does not fire if the "template enhancement output buffer" was not started.
* This output buffer is automatically started if this action is added before
* <a href="https://developer.wordpress.org/reference/functions/wp_start_template_enhancement_output_buffer/">wp_start_template_enhancement_output_buffer()</a> runs at the 'wp_before_include_template' action
* with priority 1000. Before this point, the output buffer will also be started automatically if there was a
* 'wp_template_enhancement_output_buffer' filter added, or if the
* 'wp_should_output_buffer_template_for_enhancement' filter is made to return `true`.
*
* Important: Because this action fires inside an output buffer callback (i.e. display handler), any callbacks
* added to the action must not attempt to start their own output buffers. Otherwise, PHP will raise a fatal
* error: "Cannot use output buffering in output buffering display handlers."
*
* @since 6.9.0
*
* @param string $output Finalized output buffer.
*/
do_action( 'wp_finalized_template_enhancement_output_buffer', $filtered_output );
} catch ( Throwable $throwable ) {
// Emit to the error log as a warning not as an error to prevent halting execution.
$did_just_catch = true;
trigger_error(
sprintf(
/* translators: %s is the class name */
__( 'Uncaught "%s" thrown:' ),
get_class( $throwable )
) . ' ' . $throwable->getMessage(),
E_USER_WARNING
);
$did_just_catch = false;
}
// Append any errors to be displayed before returning flushing the buffer.
if ( $original_display_errors && 'stderr' !== $original_display_errors ) {
foreach ( $error_log as $error ) {
switch ( $error['level'] ) {
case E_USER_NOTICE:
$type = 'Notice';
break;
case E_USER_DEPRECATED:
$type = 'Deprecated';
break;
case E_USER_WARNING:
$type = 'Warning';
break;
default:
$type = 'Error';
}
if ( ini_get( 'html_errors' ) ) {
/*
* Adapted from PHP internals: <https://github.com/php/php-src/blob/a979e9f897a90a580e883b1f39ce5673686ffc67/main/main.c#L1478>.
* The self-closing tags are a vestige of the XHTML past!
*/
$format = "%s<br />n<b>%s</b>: %s in <b>%s</b> on line <b>%s</b><br />n%s";
} else {
// Adapted from PHP internals: <https://github.com/php/php-src/blob/a979e9f897a90a580e883b1f39ce5673686ffc67/main/main.c#L1492>.
$format = "%sn%s: %s in %s on line %sn%s";
}
$filtered_output .= sprintf(
$format,
ini_get( 'error_prepend_string' ),
$type,
$error['message'],
$error['file'],
$error['line'],
ini_get( 'error_append_string' )
);
}
ini_set( 'display_errors', $original_display_errors );
}
restore_error_handler();
return $filtered_output;
}
Hooks
- do_action( ‘wp_finalized_template_enhancement_output_buffer’, string $output )
-
Fires after the template enhancement output buffer has been finalized.
- apply_filters( ‘wp_template_enhancement_output_buffer’, string $filtered_output, string $output )
-
Filters the template enhancement output buffer prior to sending to the client.
Changelog
| Version | Description |
|---|---|
| 6.9.0 | Introduced. |