函数文档

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.

Return

string Finalized output buffer.

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.