Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
78 changes: 73 additions & 5 deletions src/wp-includes/class-wp-scripts.php
Original file line number Diff line number Diff line change
Expand Up @@ -508,15 +508,20 @@ public function do_item( $handle, $group = false ) {
* Adds extra code to a registered script.
*
* @since 4.5.0
* @since x.x.x Added the `$type` parameter.
*
* @param string $handle Name of the script to add the inline script to.
* Must be lowercase.
* @param string $data String containing the JavaScript to be added.
* @param string $position Optional. Whether to add the inline script
* before the handle or after. Default 'after'.
* @param string $type Optional. Value of the type attribute for the inline script tag.
* Use this to set a custom type such as 'application/json' or
* 'application/ld+json'. Scripts with a custom type are rendered
* in their own script tag separate from JavaScript. Default empty string.
* @return bool True on success, false on failure.
*/
public function add_inline_script( $handle, $data, $position = 'after' ) {
public function add_inline_script( $handle, $data, $position = 'after', $type = '' ) {
if ( ! $data ) {
return false;
}
Expand All @@ -528,6 +533,17 @@ public function add_inline_script( $handle, $data, $position = 'after' ) {
$script = (array) $this->get_data( $handle, $position );
$script[] = $data;

// Store the type for this specific inline script if provided.
if ( '' !== $type ) {
$types_key = "{$position}_types";
$types = $this->get_data( $handle, $types_key );
if ( ! is_array( $types ) ) {
$types = array();
}
$types[ count( $script ) - 1 ] = $type;
$this->add_data( $handle, $types_key, $types );
}

return $this->add_data( $handle, $position, $script );
}

Expand Down Expand Up @@ -562,7 +578,12 @@ public function print_inline_script( $handle, $position = 'after', $display = tr
/**
* Gets data for inline scripts registered for a specific handle.
*
* Returns only the JavaScript inline scripts (those without a custom type).
* Scripts with a custom type are excluded as they are not JavaScript and
* should not be concatenated with JS code.
*
* @since 6.3.0
* @since x.x.x Scripts with a custom type are excluded from the returned data.
*
* @param string $handle Name of the script to get data for.
* Must be lowercase.
Expand All @@ -576,6 +597,25 @@ public function get_inline_script_data( $handle, $position = 'after' ) {
return '';
}

// Exclude scripts that have a custom type (non-JavaScript).
$types = $this->get_data( $handle, "{$position}_types" );
if ( is_array( $types ) && ! empty( $types ) ) {
$data = array_filter(
$data,
static function ( $index ) use ( $types ) {
return ! isset( $types[ $index ] ) || '' === $types[ $index ];
},
ARRAY_FILTER_USE_KEY
);
}

// Remove empty entries that may be artifacts of data initialization.
$data = array_filter( $data );

if ( empty( $data ) ) {
return '';
}

/*
* Print sourceURL comment regardless of concatenation.
*
Expand All @@ -593,7 +633,11 @@ public function get_inline_script_data( $handle, $position = 'after' ) {
/**
* Gets tags for inline scripts registered for a specific handle.
*
* Scripts with a custom type are rendered in separate script tags
* from the default JavaScript inline scripts.
*
* @since 6.3.0
* @since x.x.x Scripts with a custom type are rendered in their own script tag.
*
* @param string $handle Name of the script to get associated inline script tag for.
* Must be lowercase.
Expand All @@ -602,14 +646,38 @@ public function get_inline_script_data( $handle, $position = 'after' ) {
* @return string Inline script, which may be empty string.
*/
public function get_inline_script_tag( $handle, $position = 'after' ) {
$js = $this->get_inline_script_data( $handle, $position );
if ( empty( $js ) ) {
$data = $this->get_data( $handle, $position );
if ( empty( $data ) || ! is_array( $data ) ) {
return '';
}

$id = "{$handle}-js-{$position}";
$types = $this->get_data( $handle, "{$position}_types" );
if ( ! is_array( $types ) ) {
$types = array();
}

$output = '';

return wp_get_inline_script_tag( $js, compact( 'id' ) );
// Render the default JavaScript inline scripts (concatenated).
$js = $this->get_inline_script_data( $handle, $position );
if ( ! empty( $js ) ) {
$id = "{$handle}-js-{$position}";
$output .= wp_get_inline_script_tag( $js, compact( 'id' ) );
}

// Render scripts with custom types in their own separate script tags.
if ( ! empty( $types ) ) {
foreach ( $types as $index => $type ) {
if ( '' === $type || ! isset( $data[ $index ] ) ) {
continue;
}

$id = "{$handle}-js-{$position}-{$index}";
$output .= wp_get_inline_script_tag( $data[ $index ], compact( 'id', 'type' ) );
}
}

return $output;
}

/**
Expand Down
9 changes: 7 additions & 2 deletions src/wp-includes/functions.wp-scripts.php
Original file line number Diff line number Diff line change
Expand Up @@ -161,16 +161,21 @@ function wp_print_scripts( $handles = false ) {
* they were added, i.e. the latter added code can redeclare the previous.
*
* @since 4.5.0
* @since x.x.x Added the `$type` parameter.
*
* @see WP_Scripts::add_inline_script()
*
* @param string $handle Name of the script to add the inline script to.
* @param string $data String containing the JavaScript to be added.
* @param string $position Optional. Whether to add the inline script before the handle
* or after. Default 'after'.
* @param string $type Optional. Value of the type attribute for the inline script tag.
* Use this to set a custom type such as 'application/json' or
* 'application/ld+json'. Scripts with a custom type are rendered
* in their own script tag separate from JavaScript. Default empty string.
* @return bool True on success, false on failure.
*/
function wp_add_inline_script( $handle, $data, $position = 'after' ) {
function wp_add_inline_script( $handle, $data, $position = 'after', $type = '' ) {
_wp_scripts_maybe_doing_it_wrong( __FUNCTION__, $handle );

if ( false !== stripos( $data, '</script>' ) ) {
Expand All @@ -187,7 +192,7 @@ function wp_add_inline_script( $handle, $data, $position = 'after' ) {
$data = trim( (string) preg_replace( '#<script[^>]*>(.*)</script>#is', '$1', $data ) );
}

return wp_scripts()->add_inline_script( $handle, $data, $position );
return wp_scripts()->add_inline_script( $handle, $data, $position, $type );
}

/**
Expand Down
68 changes: 68 additions & 0 deletions tests/phpunit/tests/dependencies/scripts.php
Original file line number Diff line number Diff line change
Expand Up @@ -4307,6 +4307,74 @@ public function data_varying_versions_handle_args() {
);
}

/**
* @ticket 51124
*
* @covers ::wp_add_inline_script
* @covers WP_Scripts::add_inline_script
* @covers WP_Scripts::get_inline_script_tag
*/
public function test_wp_add_inline_script_with_custom_type() {
wp_enqueue_script( 'test-example', 'example.com', array(), null );
wp_add_inline_script( 'test-example', '{"key":"value"}', 'after', 'application/json' );
wp_add_inline_script( 'test-example', '{"config":true}', 'before', 'application/json' );

$expected = "<script id='test-example-js-before-1' type='application/json'>\n{\"config\":true}\n</script>\n";
$expected .= "<script src='http://example.com' id='test-example-js'></script>\n";
$expected .= "<script id='test-example-js-after-1' type='application/json'>\n{\"key\":\"value\"}\n</script>\n";

$this->assertEqualHTML( $expected, get_echo( 'wp_print_scripts' ) );
}

/**
* @ticket 51124
*
* @covers ::wp_add_inline_script
* @covers WP_Scripts::get_inline_script_data
* @covers WP_Scripts::get_inline_script_tag
*/
public function test_wp_add_inline_script_custom_type_separated_from_js() {
wp_enqueue_script( 'test-example', 'example.com', array(), null );
wp_add_inline_script( 'test-example', 'console.log("js");', 'after' );
wp_add_inline_script( 'test-example', '{"key":"value"}', 'after', 'application/json' );
wp_add_inline_script( 'test-example', 'console.log("more js");', 'after' );

$expected = "<script src='http://example.com' id='test-example-js'></script>\n";
$expected .= "<script id='test-example-js-after'>\nconsole.log(\"js\");\nconsole.log(\"more js\");\n//# sourceURL=test-example-js-after\n</script>\n";
$expected .= "<script id='test-example-js-after-2' type='application/json'>\n{\"key\":\"value\"}\n</script>\n";

$output = get_echo( 'wp_print_scripts' );
$this->assertEqualHTML( $expected, $output );

// Typed scripts must not appear in get_inline_script_data() output.
wp_enqueue_script( 'test-verify', 'example2.com', array(), null );
wp_add_inline_script( 'test-verify', 'var x = 1;', 'after' );
wp_add_inline_script( 'test-verify', '{"excluded":true}', 'after', 'application/ld+json' );

global $wp_scripts;
$data = $wp_scripts->get_inline_script_data( 'test-verify', 'after' );
$this->assertStringContainsString( 'var x = 1;', $data );
$this->assertStringNotContainsString( '{"excluded":true}', $data );
}

/**
* @ticket 51124
*
* @covers ::wp_add_inline_script
* @covers WP_Scripts::get_inline_script_tag
*/
public function test_wp_add_inline_script_multiple_custom_types() {
wp_enqueue_script( 'test-example', 'example.com', array(), null );
wp_add_inline_script( 'test-example', '{"config":1}', 'after', 'application/json' );
wp_add_inline_script( 'test-example', '{"@type":"Thing"}', 'after', 'application/ld+json' );

$expected = "<script src='http://example.com' id='test-example-js'></script>\n";
$expected .= "<script id='test-example-js-after-1' type='application/json'>\n{\"config\":1}\n</script>\n";
$expected .= "<script id='test-example-js-after-2' type='application/ld+json'>\n{\"@type\":\"Thing\"}\n</script>\n";

$this->assertEqualHTML( $expected, get_echo( 'wp_print_scripts' ) );
}

/**
* Normalizes markup for snapshot.
*
Expand Down
Loading