/** * WordPress scripts and styles default loader. * * Several constants are used to manage the loading, concatenating and compression of scripts and CSS: * define('SCRIPT_DEBUG', true); loads the development (non-minified) versions of all scripts and CSS, and disables compression and concatenation, * define('CONCATENATE_SCRIPTS', false); disables compression and concatenation of scripts and CSS, * define('COMPRESS_SCRIPTS', false); disables compression of scripts, * define('COMPRESS_CSS', false); disables compression of CSS, * define('ENFORCE_GZIP', true); forces gzip for compression (default is deflate). * * The globals $concatenate_scripts, $compress_scripts and $compress_css can be set by plugins * to temporarily override the above settings. Also a compression test is run once and the result is saved * as option 'can_compress_scripts' (0/1). The test will run again if that option is deleted. * * @package WordPress */ /** WordPress Dependency Class */ require ABSPATH . WPINC . '/class-wp-dependency.php'; /** WordPress Dependencies Class */ require ABSPATH . WPINC . '/class-wp-dependencies.php'; /** WordPress Scripts Class */ require ABSPATH . WPINC . '/class-wp-scripts.php'; /** WordPress Scripts Functions */ require ABSPATH . WPINC . '/functions.wp-scripts.php'; /** WordPress Styles Class */ require ABSPATH . WPINC . '/class-wp-styles.php'; /** WordPress Styles Functions */ require ABSPATH . WPINC . '/functions.wp-styles.php'; /** * Registers TinyMCE scripts. * * @since 5.0.0 * * @global string $tinymce_version * @global bool $concatenate_scripts * @global bool $compress_scripts * * @param WP_Scripts $scripts WP_Scripts object. * @param bool $force_uncompressed Whether to forcibly prevent gzip compression. Default false. */ function wp_register_tinymce_scripts( $scripts, $force_uncompressed = false ) { global $tinymce_version, $concatenate_scripts, $compress_scripts; $suffix = wp_scripts_get_suffix(); $dev_suffix = wp_scripts_get_suffix( 'dev' ); script_concat_settings(); $compressed = $compress_scripts && $concatenate_scripts && isset( $_SERVER['HTTP_ACCEPT_ENCODING'] ) && false !== stripos( $_SERVER['HTTP_ACCEPT_ENCODING'], 'gzip' ) && ! $force_uncompressed; /* * Load tinymce.js when running from /src, otherwise load wp-tinymce.js.gz (in production) * or tinymce.min.js (when SCRIPT_DEBUG is true). */ if ( $compressed ) { $scripts->add( 'wp-tinymce', includes_url( 'js/tinymce/' ) . 'wp-tinymce.js', array(), $tinymce_version ); } else { $scripts->add( 'wp-tinymce-root', includes_url( 'js/tinymce/' ) . "tinymce$dev_suffix.js", array(), $tinymce_version ); $scripts->add( 'wp-tinymce', includes_url( 'js/tinymce/' ) . "plugins/compat3x/plugin$dev_suffix.js", array( 'wp-tinymce-root' ), $tinymce_version ); } $scripts->add( 'wp-tinymce-lists', includes_url( "js/tinymce/plugins/lists/plugin$suffix.js" ), array( 'wp-tinymce' ), $tinymce_version ); } /** * Registers all the WordPress vendor scripts that are in the standardized * `js/dist/vendor/` location. * * For the order of `$scripts->add` see `wp_default_scripts`. * * @since 5.0.0 * * @global WP_Locale $wp_locale WordPress date and time locale object. * * @param WP_Scripts $scripts WP_Scripts object. */ function wp_default_packages_vendor( $scripts ) { global $wp_locale; $suffix = wp_scripts_get_suffix(); $vendor_scripts = array( 'react' => array( 'wp-polyfill' ), 'react-dom' => array( 'react' ), 'regenerator-runtime', 'moment', 'lodash', 'wp-polyfill-fetch', 'wp-polyfill-formdata', 'wp-polyfill-node-contains', 'wp-polyfill-url', 'wp-polyfill-dom-rect', 'wp-polyfill-element-closest', 'wp-polyfill-object-fit', 'wp-polyfill-inert', 'wp-polyfill' => array( 'wp-polyfill-inert', 'regenerator-runtime' ), ); $vendor_scripts_versions = array( 'react' => '18.2.0', 'react-dom' => '18.2.0', 'regenerator-runtime' => '0.14.0', 'moment' => '2.29.4', 'lodash' => '4.17.19', 'wp-polyfill-fetch' => '3.6.17', 'wp-polyfill-formdata' => '4.0.10', 'wp-polyfill-node-contains' => '4.8.0', 'wp-polyfill-url' => '3.6.4', 'wp-polyfill-dom-rect' => '4.8.0', 'wp-polyfill-element-closest' => '3.0.2', 'wp-polyfill-object-fit' => '2.3.5', 'wp-polyfill-inert' => '3.1.2', 'wp-polyfill' => '3.15.0', ); foreach ( $vendor_scripts as $handle => $dependencies ) { if ( is_string( $dependencies ) ) { $handle = $dependencies; $dependencies = array(); } $path = "/wp-includes/js/dist/vendor/$handle$suffix.js"; $version = $vendor_scripts_versions[ $handle ]; $scripts->add( $handle, $path, $dependencies, $version, 1 ); } did_action( 'init' ) && $scripts->add_inline_script( 'lodash', 'window.lodash = _.noConflict();' ); did_action( 'init' ) && $scripts->add_inline_script( 'moment', sprintf( "moment.updateLocale( '%s', %s );", get_user_locale(), wp_json_encode( array( 'months' => array_values( $wp_locale->month ), 'monthsShort' => array_values( $wp_locale->month_abbrev ), 'weekdays' => array_values( $wp_locale->weekday ), 'weekdaysShort' => array_values( $wp_locale->weekday_abbrev ), 'week' => array( 'dow' => (int) get_option( 'start_of_week', 0 ), ), 'longDateFormat' => array( 'LT' => get_option( 'time_format', __( 'g:i a' ) ), 'LTS' => null, 'L' => null, 'LL' => get_option( 'date_format', __( 'F j, Y' ) ), 'LLL' => __( 'F j, Y g:i a' ), 'LLLL' => null, ), ) ) ), 'after' ); } /** * Returns contents of an inline script used in appending polyfill scripts for * browsers which fail the provided tests. The provided array is a mapping from * a condition to verify feature support to its polyfill script handle. * * @since 5.0.0 * * @param WP_Scripts $scripts WP_Scripts object. * @param string[] $tests Features to detect. * @return string Conditional polyfill inline script. */ function wp_get_script_polyfill( $scripts, $tests ) { $polyfill = ''; foreach ( $tests as $test => $handle ) { if ( ! array_key_exists( $handle, $scripts->registered ) ) { continue; } $src = $scripts->registered[ $handle ]->src; $ver = $scripts->registered[ $handle ]->ver; if ( ! preg_match( '|^(https?:)?//|', $src ) && ! ( $scripts->content_url && str_starts_with( $src, $scripts->content_url ) ) ) { $src = $scripts->base_url . $src; } if ( ! empty( $ver ) ) { $src = add_query_arg( 'ver', $ver, $src ); } /** This filter is documented in wp-includes/class-wp-scripts.php */ $src = esc_url( apply_filters( 'script_loader_src', $src, $handle ) ); if ( ! $src ) { continue; } $polyfill .= ( // Test presence of feature... '( ' . $test . ' ) || ' . /* * ...appending polyfill on any failures. Cautious viewers may balk * at the `document.write`. Its caveat of synchronous mid-stream * blocking write is exactly the behavior we need though. */ 'document.write( \'\n"; } $concat = str_split( $concat, 128 ); $concatenated = ''; foreach ( $concat as $key => $chunk ) { $concatenated .= "&load%5Bchunk_{$key}%5D={$chunk}"; } $src = $wp_scripts->base_url . "/wp-admin/load-scripts.php?c={$zip}" . $concatenated . '&ver=' . $wp_scripts->default_version; echo "\n"; } if ( ! empty( $wp_scripts->print_html ) ) { echo $wp_scripts->print_html; } } /** * Prints the script queue in the HTML head on the front end. * * Postpones the scripts that were queued for the footer. * wp_print_footer_scripts() is called in the footer to print these scripts. * * @since 2.8.0 * * @global WP_Scripts $wp_scripts * * @return array */ function wp_print_head_scripts() { global $wp_scripts; if ( ! did_action( 'wp_print_scripts' ) ) { /** This action is documented in wp-includes/functions.wp-scripts.php */ do_action( 'wp_print_scripts' ); } if ( ! ( $wp_scripts instanceof WP_Scripts ) ) { return array(); // No need to run if nothing is queued. } return print_head_scripts(); } /** * Private, for use in *_footer_scripts hooks * * @since 3.3.0 */ function _wp_footer_scripts() { print_late_styles(); print_footer_scripts(); } /** * Hooks to print the scripts and styles in the footer. * * @since 2.8.0 */ function wp_print_footer_scripts() { /** * Fires when footer scripts are printed. * * @since 2.8.0 */ do_action( 'wp_print_footer_scripts' ); } /** * Wrapper for do_action( 'wp_enqueue_scripts' ). * * Allows plugins to queue scripts for the front end using wp_enqueue_script(). * Runs first in wp_head() where all is_home(), is_page(), etc. functions are available. * * @since 2.8.0 */ function wp_enqueue_scripts() { /** * Fires when scripts and styles are enqueued. * * @since 2.8.0 */ do_action( 'wp_enqueue_scripts' ); } /** * Prints the styles queue in the HTML head on admin pages. * * @since 2.8.0 * * @global bool $concatenate_scripts * * @return array */ function print_admin_styles() { global $concatenate_scripts; $wp_styles = wp_styles(); script_concat_settings(); $wp_styles->do_concat = $concatenate_scripts; $wp_styles->do_items( false ); /** * Filters whether to print the admin styles. * * @since 2.8.0 * * @param bool $print Whether to print the admin styles. Default true. */ if ( apply_filters( 'print_admin_styles', true ) ) { _print_styles(); } $wp_styles->reset(); return $wp_styles->done; } /** * Prints the styles that were queued too late for the HTML head. * * @since 3.3.0 * * @global WP_Styles $wp_styles * @global bool $concatenate_scripts * * @return array|void */ function print_late_styles() { global $wp_styles, $concatenate_scripts; if ( ! ( $wp_styles instanceof WP_Styles ) ) { return; } script_concat_settings(); $wp_styles->do_concat = $concatenate_scripts; $wp_styles->do_footer_items(); /** * Filters whether to print the styles queued too late for the HTML head. * * @since 3.3.0 * * @param bool $print Whether to print the 'late' styles. Default true. */ if ( apply_filters( 'print_late_styles', true ) ) { _print_styles(); } $wp_styles->reset(); return $wp_styles->done; } /** * Prints styles (internal use only). * * @ignore * @since 3.3.0 * * @global bool $compress_css */ function _print_styles() { global $compress_css; $wp_styles = wp_styles(); $zip = $compress_css ? 1 : 0; if ( $zip && defined( 'ENFORCE_GZIP' ) && ENFORCE_GZIP ) { $zip = 'gzip'; } $concat = trim( $wp_styles->concat, ', ' ); $type_attr = current_theme_supports( 'html5', 'style' ) ? '' : ' type="text/css"'; if ( $concat ) { $dir = $wp_styles->text_direction; $ver = $wp_styles->default_version; $concat = str_split( $concat, 128 ); $concatenated = ''; foreach ( $concat as $key => $chunk ) { $concatenated .= "&load%5Bchunk_{$key}%5D={$chunk}"; } $href = $wp_styles->base_url . "/wp-admin/load-styles.php?c={$zip}&dir={$dir}" . $concatenated . '&ver=' . $ver; echo "\n"; if ( ! empty( $wp_styles->print_code ) ) { echo "\n"; echo $wp_styles->print_code; echo "\n\n"; } } if ( ! empty( $wp_styles->print_html ) ) { echo $wp_styles->print_html; } } /** * Determines the concatenation and compression settings for scripts and styles. * * @since 2.8.0 * * @global bool $concatenate_scripts * @global bool $compress_scripts * @global bool $compress_css */ function script_concat_settings() { global $concatenate_scripts, $compress_scripts, $compress_css; $compressed_output = ( ini_get( 'zlib.output_compression' ) || 'ob_gzhandler' === ini_get( 'output_handler' ) ); $can_compress_scripts = ! wp_installing() && get_site_option( 'can_compress_scripts' ); if ( ! isset( $concatenate_scripts ) ) { $concatenate_scripts = defined( 'CONCATENATE_SCRIPTS' ) ? CONCATENATE_SCRIPTS : true; if ( ( ! is_admin() && ! did_action( 'login_init' ) ) || ( defined( 'SCRIPT_DEBUG' ) && SCRIPT_DEBUG ) ) { $concatenate_scripts = false; } } if ( ! isset( $compress_scripts ) ) { $compress_scripts = defined( 'COMPRESS_SCRIPTS' ) ? COMPRESS_SCRIPTS : true; if ( $compress_scripts && ( ! $can_compress_scripts || $compressed_output ) ) { $compress_scripts = false; } } if ( ! isset( $compress_css ) ) { $compress_css = defined( 'COMPRESS_CSS' ) ? COMPRESS_CSS : true; if ( $compress_css && ( ! $can_compress_scripts || $compressed_output ) ) { $compress_css = false; } } } /** * Handles the enqueueing of block scripts and styles that are common to both * the editor and the front-end. * * @since 5.0.0 */ function wp_common_block_scripts_and_styles() { if ( is_admin() && ! wp_should_load_block_editor_scripts_and_styles() ) { return; } wp_enqueue_style( 'wp-block-library' ); if ( current_theme_supports( 'wp-block-styles' ) && ! wp_should_load_separate_core_block_assets() ) { wp_enqueue_style( 'wp-block-library-theme' ); } /** * Fires after enqueuing block assets for both editor and front-end. * * Call `add_action` on any hook before 'wp_enqueue_scripts'. * * In the function call you supply, simply use `wp_enqueue_script` and * `wp_enqueue_style` to add your functionality to the Gutenberg editor. * * @since 5.0.0 */ do_action( 'enqueue_block_assets' ); } /** * Applies a filter to the list of style nodes that comes from WP_Theme_JSON::get_style_nodes(). * * This particular filter removes all of the blocks from the array. * * We want WP_Theme_JSON to be ignorant of the implementation details of how the CSS is being used. * This filter allows us to modify the output of WP_Theme_JSON depending on whether or not we are * loading separate assets, without making the class aware of that detail. * * @since 6.1.0 * * @param array $nodes The nodes to filter. * @return array A filtered array of style nodes. */ function wp_filter_out_block_nodes( $nodes ) { return array_filter( $nodes, static function ( $node ) { return ! in_array( 'blocks', $node['path'], true ); }, ARRAY_FILTER_USE_BOTH ); } /** * Enqueues the global styles defined via theme.json. * * @since 5.8.0 */ function wp_enqueue_global_styles() { $separate_assets = wp_should_load_separate_core_block_assets(); $is_block_theme = wp_is_block_theme(); $is_classic_theme = ! $is_block_theme; /* * Global styles should be printed in the head when loading all styles combined. * The footer should only be used to print global styles for classic themes with separate core assets enabled. * * See https://core.trac.wordpress.org/ticket/53494. */ if ( ( $is_block_theme && doing_action( 'wp_footer' ) ) || ( $is_classic_theme && doing_action( 'wp_footer' ) && ! $separate_assets ) || ( $is_classic_theme && doing_action( 'wp_enqueue_scripts' ) && $separate_assets ) ) { return; } /* * If loading the CSS for each block separately, then load the theme.json CSS conditionally. * This removes the CSS from the global-styles stylesheet and adds it to the inline CSS for each block. * This filter must be registered before calling wp_get_global_stylesheet(); */ add_filter( 'wp_theme_json_get_style_nodes', 'wp_filter_out_block_nodes' ); $stylesheet = wp_get_global_stylesheet(); if ( empty( $stylesheet ) ) { return; } wp_register_style( 'global-styles', false ); wp_add_inline_style( 'global-styles', $stylesheet ); wp_enqueue_style( 'global-styles' ); // Add each block as an inline css. wp_add_global_styles_for_blocks(); } /** * Enqueues the global styles custom css defined via theme.json. * * @since 6.2.0 */ function wp_enqueue_global_styles_custom_css() { if ( ! wp_is_block_theme() ) { return; } // Don't enqueue Customizer's custom CSS separately. remove_action( 'wp_head', 'wp_custom_css_cb', 101 ); $custom_css = wp_get_custom_css(); $custom_css .= wp_get_global_styles_custom_css(); if ( ! empty( $custom_css ) ) { wp_add_inline_style( 'global-styles', $custom_css ); } } /** * Checks if the editor scripts and styles for all registered block types * should be enqueued on the current screen. * * @since 5.6.0 * * @global WP_Screen $current_screen WordPress current screen object. * * @return bool Whether scripts and styles should be enqueued. */ function wp_should_load_block_editor_scripts_and_styles() { global $current_screen; $is_block_editor_screen = ( $current_screen instanceof WP_Screen ) && $current_screen->is_block_editor(); /** * Filters the flag that decides whether or not block editor scripts and styles * are going to be enqueued on the current screen. * * @since 5.6.0 * * @param bool $is_block_editor_screen Current value of the flag. */ return apply_filters( 'should_load_block_editor_scripts_and_styles', $is_block_editor_screen ); } /** * Checks whether separate styles should be loaded for core blocks on-render. * * When this function returns true, other functions ensure that core blocks * only load their assets on-render, and each block loads its own, individual * assets. Third-party blocks only load their assets when rendered. * * When this function returns false, all core block assets are loaded regardless * of whether they are rendered in a page or not, because they are all part of * the `block-library/style.css` file. Assets for third-party blocks are always * enqueued regardless of whether they are rendered or not. * * This only affects front end and not the block editor screens. * * @see wp_enqueue_registered_block_scripts_and_styles() * @see register_block_style_handle() * * @since 5.8.0 * * @return bool Whether separate assets will be loaded. */ function wp_should_load_separate_core_block_assets() { if ( is_admin() || is_feed() || ( defined( 'REST_REQUEST' ) && REST_REQUEST ) ) { return false; } /** * Filters whether block styles should be loaded separately. * * Returning false loads all core block assets, regardless of whether they are rendered * in a page or not. Returning true loads core block assets only when they are rendered. * * @since 5.8.0 * * @param bool $load_separate_assets Whether separate assets will be loaded. * Default false (all block assets are loaded, even when not used). */ return apply_filters( 'should_load_separate_core_block_assets', false ); } /** * Enqueues registered block scripts and styles, depending on current rendered * context (only enqueuing editor scripts while in context of the editor). * * @since 5.0.0 * * @global WP_Screen $current_screen WordPress current screen object. */ function wp_enqueue_registered_block_scripts_and_styles() { global $current_screen; if ( wp_should_load_separate_core_block_assets() ) { return; } $load_editor_scripts_and_styles = is_admin() && wp_should_load_block_editor_scripts_and_styles(); $block_registry = WP_Block_Type_Registry::get_instance(); foreach ( $block_registry->get_all_registered() as $block_name => $block_type ) { // Front-end and editor styles. foreach ( $block_type->style_handles as $style_handle ) { wp_enqueue_style( $style_handle ); } // Front-end and editor scripts. foreach ( $block_type->script_handles as $script_handle ) { wp_enqueue_script( $script_handle ); } if ( $load_editor_scripts_and_styles ) { // Editor styles. foreach ( $block_type->editor_style_handles as $editor_style_handle ) { wp_enqueue_style( $editor_style_handle ); } // Editor scripts. foreach ( $block_type->editor_script_handles as $editor_script_handle ) { wp_enqueue_script( $editor_script_handle ); } } } } /** * Function responsible for enqueuing the styles required for block styles functionality on the editor and on the frontend. * * @since 5.3.0 * * @global WP_Styles $wp_styles */ function enqueue_block_styles_assets() { global $wp_styles; $block_styles = WP_Block_Styles_Registry::get_instance()->get_all_registered(); foreach ( $block_styles as $block_name => $styles ) { foreach ( $styles as $style_properties ) { if ( isset( $style_properties['style_handle'] ) ) { // If the site loads separate styles per-block, enqueue the stylesheet on render. if ( wp_should_load_separate_core_block_assets() ) { add_filter( 'render_block', static function ( $html, $block ) use ( $block_name, $style_properties ) { if ( $block['blockName'] === $block_name ) { wp_enqueue_style( $style_properties['style_handle'] ); } return $html; }, 10, 2 ); } else { wp_enqueue_style( $style_properties['style_handle'] ); } } if ( isset( $style_properties['inline_style'] ) ) { // Default to "wp-block-library". $handle = 'wp-block-library'; // If the site loads separate styles per-block, check if the block has a stylesheet registered. if ( wp_should_load_separate_core_block_assets() ) { $block_stylesheet_handle = generate_block_asset_handle( $block_name, 'style' ); if ( isset( $wp_styles->registered[ $block_stylesheet_handle ] ) ) { $handle = $block_stylesheet_handle; } } // Add inline styles to the calculated handle. wp_add_inline_style( $handle, $style_properties['inline_style'] ); } } } } /** * Function responsible for enqueuing the assets required for block styles functionality on the editor. * * @since 5.3.0 */ function enqueue_editor_block_styles_assets() { $block_styles = WP_Block_Styles_Registry::get_instance()->get_all_registered(); $register_script_lines = array( '( function() {' ); foreach ( $block_styles as $block_name => $styles ) { foreach ( $styles as $style_properties ) { $block_style = array( 'name' => $style_properties['name'], 'label' => $style_properties['label'], ); if ( isset( $style_properties['is_default'] ) ) { $block_style['isDefault'] = $style_properties['is_default']; } $register_script_lines[] = sprintf( ' wp.blocks.registerBlockStyle( \'%s\', %s );', $block_name, wp_json_encode( $block_style ) ); } } $register_script_lines[] = '} )();'; $inline_script = implode( "\n", $register_script_lines ); wp_register_script( 'wp-block-styles', false, array( 'wp-blocks' ), true, array( 'in_footer' => true ) ); wp_add_inline_script( 'wp-block-styles', $inline_script ); wp_enqueue_script( 'wp-block-styles' ); } /** * Enqueues the assets required for the block directory within the block editor. * * @since 5.5.0 */ function wp_enqueue_editor_block_directory_assets() { wp_enqueue_script( 'wp-block-directory' ); wp_enqueue_style( 'wp-block-directory' ); } /** * Enqueues the assets required for the format library within the block editor. * * @since 5.8.0 */ function wp_enqueue_editor_format_library_assets() { wp_enqueue_script( 'wp-format-library' ); wp_enqueue_style( 'wp-format-library' ); } /** * Sanitizes an attributes array into an attributes string to be placed inside a `\n", wp_sanitize_script_attributes( $attributes ) ); } /** * Prints formatted ` * * In an HTML document this would print "…" to the console, * but in an XHTML document it would print "…" to the console. * * * * In an HTML document this would print "An image is in HTML", * but it's an invalid XHTML document because it interprets the `` * as an empty tag missing its closing `/`. * * @see https://www.w3.org/TR/xhtml1/#h-4.8 */ if ( ! $is_html5 ) { /* * 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 `', ']]]]>', $javascript ); // Wrap the entire escaped script inside a CDATA section. $javascript = sprintf( "/* */", $javascript ); } $javascript = "\n" . trim( $javascript, "\n\r " ) . "\n"; /** * Filters attributes to be added to a script tag. * * @since 5.7.0 * * @param array $attributes Key-value pairs representing `\n", wp_sanitize_script_attributes( $attributes ), $javascript ); } /** * Prints inline JavaScript wrapped in `" from * around an inline script after trimming whitespace. Typlically this * is used in conjunction with output buffering, where `ob_get_clean()` * is passed as the `$contents` argument. * * Example: * * // Strips exact literal empty SCRIPT tags. * $js = '; * 'sayHello();' === wp_remove_surrounding_empty_script_tags( $js ); * * // Otherwise if anything is different it warns in the JS console. * $js = ''; * 'console.error( ... )' === wp_remove_surrounding_empty_script_tags( $js ); * * @private * @since 6.4.0 * * @see wp_print_inline_script_tag() * @see wp_get_inline_script_tag() * * @param string $contents Script body with manually created SCRIPT tag literals. * @return string Script body without surrounding script tag literals, or * original contents if both exact literals aren't present. */ function wp_remove_surrounding_empty_script_tags( $contents ) { $contents = trim( $contents ); $opener = ''; if ( strlen( $contents ) > strlen( $opener ) + strlen( $closer ) && strtoupper( substr( $contents, 0, strlen( $opener ) ) ) === $opener && strtoupper( substr( $contents, -strlen( $closer ) ) ) === $closer ) { return substr( $contents, strlen( $opener ), -strlen( $closer ) ); } else { $error_message = __( 'Expected string to start with script tag (without attributes) and end with script tag, with optional whitespace.' ); _doing_it_wrong( __FUNCTION__, $error_message, '6.4' ); return sprintf( 'console.error(%s)', wp_json_encode( __( 'Function wp_remove_surrounding_empty_script_tags() used incorrectly in PHP.' ) . ' ' . $error_message ) ); } }