wp_hoist_late_printed_styles() │ WP 6.9.0
Adds the hooks needed for CSS output to be delayed until after the content of the page has been established.
Хуков нет.
Возвращает
null. Ничего (null).
Использование
wp_hoist_late_printed_styles();
Заметки
- Смотрите: wp_load_classic_theme_block_styles_on_demand()
- Смотрите: _wp_footer_scripts()
Список изменений
| С версии 6.9.0 | Введена. |
Код wp_hoist_late_printed_styles() wp hoist late printed styles WP 6.9
function wp_hoist_late_printed_styles() {
// Skip the embed template on-demand styles aren't relevant, and there is no wp_head action.
if ( is_embed() ) {
return;
}
/*
* Add a placeholder comment into the inline styles for wp-block-library, after which where the late block styles
* can be hoisted from the footer to be printed in the header by means of a filter below on the template enhancement
* output buffer. The `wp_print_styles` action is used to ensure that if the inline style gets replaced at
* `enqueue_block_assets` or `wp_enqueue_scripts` that the placeholder will be sure to be present.
*/
$placeholder = sprintf( '/*%s*/', uniqid( 'wp_block_styles_on_demand_placeholder:' ) );
add_action(
'wp_print_styles',
static function () use ( $placeholder ) {
wp_add_inline_style( 'wp-block-library', $placeholder );
}
);
/*
* Create a substitute for `print_late_styles()` which is aware of block styles. This substitute does not print
* the styles, but it captures what would be printed for block styles and non-block styles so that they can be
* later hoisted to the HEAD in the template enhancement output buffer. This will run at `wp_print_footer_scripts`
* before `print_footer_scripts()` is called.
*/
$printed_block_styles = '';
$printed_late_styles = '';
$capture_late_styles = static function () use ( &$printed_block_styles, &$printed_late_styles ) {
// Gather the styles related to on-demand block enqueues.
$all_block_style_handles = array();
foreach ( WP_Block_Type_Registry::get_instance()->get_all_registered() as $block_type ) {
foreach ( $block_type->style_handles as $style_handle ) {
$all_block_style_handles[] = $style_handle;
}
}
$all_block_style_handles = array_merge(
$all_block_style_handles,
array(
'global-styles',
'block-style-variation-styles',
'core-block-supports',
'core-block-supports-duotone',
)
);
/*
* First print all styles related to blocks which should inserted right after the wp-block-library stylesheet
* to preserve the CSS cascade. The logic in this `if` statement is derived from `wp_print_styles()`.
*/
$enqueued_block_styles = array_values( array_intersect( $all_block_style_handles, wp_styles()->queue ) );
if ( count( $enqueued_block_styles ) > 0 ) {
ob_start();
wp_styles()->do_items( $enqueued_block_styles );
$printed_block_styles = ob_get_clean();
}
/*
* Print all remaining styles not related to blocks. This contains a subset of the logic from
* `print_late_styles()`, without admin-specific logic and the `print_late_styles` filter to control whether
* late styles are printed (since they are being hoisted anyway).
*/
ob_start();
wp_styles()->do_footer_items();
$printed_late_styles = ob_get_clean();
};
/*
* If `_wp_footer_scripts()` was unhooked from the `wp_print_footer_scripts` action, or if `wp_print_footer_scripts()`
* was unhooked from running at the `wp_footer` action, then only add a callback to `wp_footer` which will capture the
* late-printed styles.
*
* Otherwise, in the normal case where `_wp_footer_scripts()` will run at the `wp_print_footer_scripts` action, then
* swap out `_wp_footer_scripts()` with an alternative which captures the printed styles (for hoisting to HEAD) before
* proceeding with printing the footer scripts.
*/
$wp_print_footer_scripts_priority = has_action( 'wp_print_footer_scripts', '_wp_footer_scripts' );
if ( false === $wp_print_footer_scripts_priority || false === has_action( 'wp_footer', 'wp_print_footer_scripts' ) ) {
// The normal priority for wp_print_footer_scripts() is to run at 20.
add_action( 'wp_footer', $capture_late_styles, 20 );
} else {
remove_action( 'wp_print_footer_scripts', '_wp_footer_scripts', $wp_print_footer_scripts_priority );
add_action(
'wp_print_footer_scripts',
static function () use ( $capture_late_styles ) {
$capture_late_styles();
print_footer_scripts();
},
$wp_print_footer_scripts_priority
);
}
// Replace placeholder with the captured late styles.
add_filter(
'wp_template_enhancement_output_buffer',
static function ( $buffer ) use ( $placeholder, &$printed_block_styles, &$printed_late_styles ) {
// Anonymous subclass of WP_HTML_Tag_Processor which exposes underlying bookmark spans.
$processor = new class( $buffer ) extends WP_HTML_Tag_Processor {
/**
* Gets the span for the current token.
*
* @return WP_HTML_Span Current token span.
*/
private function get_span(): WP_HTML_Span {
// Note: This call will never fail according to the usage of this class, given it is always called after ::next_tag() is true.
$this->set_bookmark( 'here' );
return $this->bookmarks['here'];
}
/**
* Inserts text before the current token.
*
* @param string $text Text to insert.
*/
public function insert_before( string $text ) {
$this->lexical_updates[] = new WP_HTML_Text_Replacement( $this->get_span()->start, 0, $text );
}
/**
* Inserts text after the current token.
*
* @param string $text Text to insert.
*/
public function insert_after( string $text ) {
$span = $this->get_span();
$this->lexical_updates[] = new WP_HTML_Text_Replacement( $span->start + $span->length, 0, $text );
}
/**
* Removes the current token.
*/
public function remove() {
$span = $this->get_span();
$this->lexical_updates[] = new WP_HTML_Text_Replacement( $span->start, $span->length, '' );
}
};
/*
* Insert block styles right after wp-block-library (if it is present), and then insert any remaining styles
* at </head> (or else print everything there). The placeholder CSS comment will always be added to the
* wp-block-library inline style since it gets printed at `wp_head` before the blocks are rendered.
* This means that there may not actually be any block styles to hoist from the footer to insert after this
* inline style. The placeholder CSS comment needs to be added so that the inline style gets printed, but
* if the resulting inline style is empty after the placeholder is removed, then the inline style is
* removed.
*/
while ( $processor->next_tag( array( 'tag_closers' => 'visit' ) ) ) {
if (
'STYLE' === $processor->get_tag() &&
'wp-block-library-inline-css' === $processor->get_attribute( 'id' )
) {
$css_text = $processor->get_modifiable_text();
/*
* A placeholder CSS comment is added to the inline style in order to force an inline STYLE tag to
* be printed. Now that we've located the inline style, the placeholder comment can be removed. If
* there is no CSS left in the STYLE tag after removing the placeholder (aside from the sourceURL
* comment, then remove the STYLE entirely.)
*/
$css_text = str_replace( $placeholder, '', $css_text );
if ( preg_match( ':^/\*# sourceURL=\S+? \*/$:', trim( $css_text ) ) ) {
$processor->remove();
} else {
$processor->set_modifiable_text( $css_text );
}
// Insert the $printed_late_styles immediately after the closing inline STYLE tag. This preserves the CSS cascade.
if ( '' !== $printed_block_styles ) {
$processor->insert_after( $printed_block_styles );
// Prevent printing them again at </head>.
$printed_block_styles = '';
}
// If there aren't any late styles, there's no need to continue to finding </head>.
if ( '' === $printed_late_styles ) {
break;
}
} elseif ( 'HEAD' === $processor->get_tag() && $processor->is_tag_closer() ) {
$processor->insert_before( $printed_block_styles . $printed_late_styles );
break;
}
}
return $processor->get_updated_html();
}
);
}