* If the string `]]>` exists within the JavaScript it would break
* out of any wrapping CDATA section added here, so to start, it's
* necessary to escape that sequence which requires splitting the
* content into two CDATA sections wherever it's found.
* Note: it's only necessary to escape the closing `]]>` because
* an additional `<![CDATA[` leaves the contents unchanged.
$data = str_replace( ']]>', ']]]]><![CDATA[>', $data );
// Wrap the entire escaped script inside a CDATA section.
$data = sprintf( "/* <![CDATA[ */\n%s\n/* ]]> */", $data );
$data = "\n" . trim( $data, "\n\r " ) . "\n";
* Filters attributes to be added to a script tag.
* @param array $attributes Key-value pairs representing `<script>` tag attributes.
* Only the attribute name is added to the `<script>` tag for
* entries with a boolean value, and that are true.
* @param string $data Inline data.
$attributes = apply_filters( 'wp_inline_script_attributes', $attributes, $data );
return sprintf( "<script%s>%s</script>\n", wp_sanitize_script_attributes( $attributes ), $data );
* Prints an inline script tag.
* It is possible to inject attributes in the `<script>` tag via the {@see 'wp_inline_script_attributes'} filter.
* Automatically injects type attribute if needed.
* @param string $data Data for script tag: JavaScript, importmap, speculationrules, etc.
* @param array $attributes Optional. Key-value pairs representing `<script>` tag attributes.
function wp_print_inline_script_tag( $data, $attributes = array() ) {
echo wp_get_inline_script_tag( $data, $attributes );
* Allows small styles to be inlined.
* This improves performance and sustainability, and is opt-in. Stylesheets can opt in
* by adding `path` data using `wp_style_add_data`, and defining the file's absolute path:
* wp_style_add_data( $style_handle, 'path', $file_path );
* @global WP_Styles $wp_styles
function wp_maybe_inline_styles() {
$total_inline_limit = 40000;
* The maximum size of inlined styles in bytes.
* @since 6.9.0 The default limit increased from 20K to 40K.
* @param int $total_inline_limit The file-size threshold, in bytes. Default 40000.
$total_inline_limit = apply_filters( 'styles_inline_size_limit', $total_inline_limit );
// Build an array of styles that have a path defined.
foreach ( $wp_styles->queue as $handle ) {
if ( ! isset( $wp_styles->registered[ $handle ] ) ) {
$src = $wp_styles->registered[ $handle ]->src;
$path = $wp_styles->get_data( $handle, 'path' );
$size = wp_filesize( $path );
if ( ! empty( $styles ) ) {
// Reorder styles array based on size.
static function ( $a, $b ) {
return ( $a['size'] <= $b['size'] ) ? -1 : 1;
* The total inlined size.
* On each iteration of the loop, if a style gets added inline the value of this var increases
* to reflect the total size of inlined styles.
foreach ( $styles as $style ) {
// Size check. Since styles are ordered by size, we can break the loop.
if ( $total_inline_size + $style['size'] > $total_inline_limit ) {
// Get the styles if we don't already have them.
$style['css'] = file_get_contents( $style['path'] );
* Check if the style contains relative URLs that need to be modified.
* URLs relative to the stylesheet's path should be converted to relative to the site's root.
$style['css'] = _wp_normalize_relative_css_links( $style['css'], $style['src'] );
// Keep track of the original `src` for the style that was inlined so that the `sourceURL` comment can be added.
$wp_styles->add_data( $style['handle'], 'inlined_src', $style['src'] );
// Set `src` to `false` and add styles inline.
$wp_styles->registered[ $style['handle'] ]->src = false;
if ( empty( $wp_styles->registered[ $style['handle'] ]->extra['after'] ) ) {
$wp_styles->registered[ $style['handle'] ]->extra['after'] = array();
array_unshift( $wp_styles->registered[ $style['handle'] ]->extra['after'], $style['css'] );
// Add the styles size to the $total_inline_size var.
$total_inline_size += (int) $style['size'];
* Makes URLs relative to the WordPress installation.
* @param string $css The CSS to make URLs relative to the WordPress installation.
* @param string $stylesheet_url The URL to the stylesheet.
* @return string The CSS with URLs made relative to the WordPress installation.
function _wp_normalize_relative_css_links( $css, $stylesheet_url ) {
return preg_replace_callback(
'#(url\s*\(\s*[\'"]?\s*)([^\'"\)]+)#',
static function ( $matches ) use ( $stylesheet_url ) {
list( , $prefix, $url ) = $matches;
// Short-circuit if the URL does not require normalization.
str_starts_with( $url, 'http:' ) ||
str_starts_with( $url, 'https:' ) ||
str_starts_with( $url, '/' ) ||
str_starts_with( $url, '#' ) ||
str_starts_with( $url, 'data:' )
// Build the absolute URL.
$absolute_url = dirname( $stylesheet_url ) . '/' . $url;
$absolute_url = str_replace( '/./', '/', $absolute_url );
// Convert to URL related to the site root.
$url = wp_make_link_relative( $absolute_url );
* Function that enqueues the CSS Custom Properties coming from theme.json.
function wp_enqueue_global_styles_css_custom_properties() {
wp_register_style( 'global-styles-css-custom-properties', false );
wp_add_inline_style( 'global-styles-css-custom-properties', wp_get_global_stylesheet( array( 'variables' ) ) );
wp_enqueue_style( 'global-styles-css-custom-properties' );
* Hooks inline styles in the proper place, depending on the active theme.
* @since 6.1.0 Added the `$priority` parameter.
* For block themes, styles are loaded in the head.
* For classic ones, styles are loaded in the body because the wp_head action happens before render_block.
* @link https://core.trac.wordpress.org/ticket/53494.
* @param string $style String containing the CSS styles to be added.
* @param int $priority To set the priority for the add_action.
function wp_enqueue_block_support_styles( $style, $priority = 10 ) {
$action_hook_name = 'wp_footer';
if ( wp_is_block_theme() ) {
$action_hook_name = 'wp_head';
static function () use ( $style ) {
echo "<style>$style</style>\n";
* Fetches, processes and compiles stored core styles, then combines and renders them to the page.
* Styles are stored via the style engine API.
* @link https://developer.wordpress.org/block-editor/reference-guides/packages/packages-style-engine/
* @param array $options {
* Optional. An array of options to pass to wp_style_engine_get_stylesheet_from_context().
* @type bool $optimize Whether to optimize the CSS output, e.g., combine rules.
* @type bool $prettify Whether to add new lines and indents to output.
* Default to whether the `SCRIPT_DEBUG` constant is defined.
function wp_enqueue_stored_styles( $options = array() ) {
// Note: Styles printed at wp_footer for classic themes may still end up in the head due to wp_load_classic_theme_block_styles_on_demand().
$is_block_theme = wp_is_block_theme();
$is_classic_theme = ! $is_block_theme;
* For block themes, this function prints stored styles in the header.
* For classic themes, in the footer.
( $is_block_theme && doing_action( 'wp_footer' ) ) ||
( $is_classic_theme && doing_action( 'wp_enqueue_scripts' ) )
$core_styles_keys = array( 'block-supports' );
$compiled_core_stylesheet = '';
// Adds comment if code is prettified to identify core styles sections in debugging.
$should_prettify = isset( $options['prettify'] ) ? true === $options['prettify'] : defined( 'SCRIPT_DEBUG' ) && SCRIPT_DEBUG;
foreach ( $core_styles_keys as $style_key ) {
if ( $should_prettify ) {
$compiled_core_stylesheet .= "/**\n * Core styles: $style_key\n */\n";
// Chains core store ids to signify what the styles contain.
$style_tag_id .= '-' . $style_key;
$compiled_core_stylesheet .= wp_style_engine_get_stylesheet_from_context( $style_key, $options );
if ( ! empty( $compiled_core_stylesheet ) ) {
wp_register_style( $style_tag_id, false );
wp_add_inline_style( $style_tag_id, $compiled_core_stylesheet );
wp_enqueue_style( $style_tag_id );
// Prints out any other stores registered by themes or otherwise.
$additional_stores = WP_Style_Engine_CSS_Rules_Store::get_stores();
foreach ( array_keys( $additional_stores ) as $store_name ) {
if ( in_array( $store_name, $core_styles_keys, true ) ) {
$styles = wp_style_engine_get_stylesheet_from_context( $store_name, $options );
if ( ! empty( $styles ) ) {
$key = "wp-style-engine-$store_name";
wp_register_style( $key, false );
wp_add_inline_style( $key, $styles );
wp_enqueue_style( $key );
* Enqueues a stylesheet for a specific block.
* If the theme has opted-in to load block styles on demand,
* then the stylesheet will be enqueued on-render,
* otherwise when the block inits.
* @param string $block_name The block-name, including namespace.
* An array of arguments. See wp_register_style() for full information about each argument.
* @type string $handle The handle for the stylesheet.
* @type string|false $src The source URL of the stylesheet.
* @type string[] $deps Array of registered stylesheet handles this stylesheet depends on.
* @type string|bool|null $ver Stylesheet version number.
* @type string $media The media for which this stylesheet has been defined.
* @type string|null $path Absolute path to the stylesheet, so that it can potentially be inlined.
function wp_enqueue_block_style( $block_name, $args ) {
* Callback function to register and enqueue styles.
* @param string $content When the callback is used for the render_block filter,
* the content needs to be returned so the function parameter
* is to ensure the content exists.
* @return string Block content.
$callback = static function ( $content ) use ( $args ) {
// Register the stylesheet.
if ( ! empty( $args['src'] ) ) {
wp_register_style( $args['handle'], $args['src'], $args['deps'], $args['ver'], $args['media'] );
// Add `path` data if provided.
if ( isset( $args['path'] ) ) {
wp_style_add_data( $args['handle'], 'path', $args['path'] );
// Get the RTL file path.
$rtl_file_path = str_replace( '.css', '-rtl.css', $args['path'] );
if ( file_exists( $rtl_file_path ) ) {
wp_style_add_data( $args['handle'], 'rtl', 'replace' );
wp_style_add_data( $args['handle'], 'path', $rtl_file_path );
// Enqueue the stylesheet.
wp_enqueue_style( $args['handle'] );
$hook = did_action( 'wp_enqueue_scripts' ) ? 'wp_footer' : 'wp_enqueue_scripts';
if ( wp_should_load_block_assets_on_demand() ) {
* Callback function to register and enqueue styles.
* @param string $content The block content.
* @param array $block The full block, including name and attributes.
* @return string Block content.
$callback_separate = static function ( $content, $block ) use ( $block_name, $callback ) {
if ( ! empty( $block['blockName'] ) && $block_name === $block['blockName'] ) {
return $callback( $content );
* The filter's callback here is an anonymous function because
* using a named function in this case is not possible.
* The function cannot be unhooked, however, users are still able
* to dequeue the stylesheets registered/enqueued by the callback
* which is why in this case, using an anonymous function
add_filter( 'render_block', $callback_separate, 10, 2 );
* The filter's callback here is an anonymous function because
* using a named function in this case is not possible.
* The function cannot be unhooked, however, users are still able
* to dequeue the stylesheets registered/enqueued by the callback
* which is why in this case, using an anonymous function
add_filter( $hook, $callback );
// Enqueue assets in the editor.
add_action( 'enqueue_block_assets', $callback );
* Loads classic theme styles on classic themes in the frontend.
* This is used for backwards compatibility for Button and File blocks specifically.
* @since 6.2.0 Added File block styles.
* @since 6.8.0 Moved stylesheet registration outside of this function.
function wp_enqueue_classic_theme_styles() {
if ( ! wp_theme_has_theme_json() ) {
wp_enqueue_style( 'classic-theme-styles' );
* Enqueues the assets required for the Command Palette.
function wp_enqueue_command_palette_assets() {
$command_palette_settings = array(
'is_network_admin' => is_network_admin(),
$menu_commands = array();
foreach ( $menu as $menu_item ) {
if ( empty( $menu_item[0] ) || ! empty( $menu_item[1] ) && ! current_user_can( $menu_item[1] ) ) {
// Remove all HTML tags and their contents.
$menu_label = $menu_item[0];
while ( preg_match( '/<[^>]*>/', $menu_label ) ) {
$menu_label = preg_replace( '/<[^>]*>.*?<\/[^>]*>|<[^>]*\/>|<[^>]*>/s', '', $menu_label );
$menu_label = trim( $menu_label );
$menu_slug = $menu_item[2];
if ( preg_match( '/\.php($|\?)/', $menu_slug ) || wp_http_validate_url( $menu_slug ) ) {
} elseif ( ! empty( menu_page_url( $menu_slug, false ) ) ) {
$menu_url = html_entity_decode( menu_page_url( $menu_slug, false ), ENT_QUOTES, get_bloginfo( 'charset' ) );
$menu_commands[] = array(
if ( array_key_exists( $menu_slug, $submenu ) ) {
foreach ( $submenu[ $menu_slug ] as $submenu_item ) {
if ( empty( $submenu_item[0] ) || ! empty( $submenu_item[1] ) && ! current_user_can( $submenu_item[1] ) ) {
// Remove all HTML tags and their contents.
$submenu_label = $submenu_item[0];
while ( preg_match( '/<[^>]*>/', $submenu_label ) ) {
$submenu_label = preg_replace( '/<[^>]*>.*?<\/[^>]*>|<[^>]*\/>|<[^>]*>/s', '', $submenu_label );
$submenu_label = trim( $submenu_label );
$submenu_slug = $submenu_item[2];
if ( preg_match( '/\.php($|\?)/', $submenu_slug ) || wp_http_validate_url( $submenu_slug ) ) {
$submenu_url = $submenu_slug;
} elseif ( ! empty( menu_page_url( $submenu_slug, false ) ) ) {
$submenu_url = html_entity_decode( menu_page_url( $submenu_slug, false ), ENT_QUOTES, get_bloginfo( 'charset' ) );
$menu_commands[] = array(
/* translators: 1: Menu label, 2: Submenu label. */