Skip to content
Closed
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
120 changes: 120 additions & 0 deletions src/wp-includes/functions.php
Original file line number Diff line number Diff line change
Expand Up @@ -7773,3 +7773,123 @@ function is_php_version_compatible( $required ) {
function wp_fuzzy_number_match( $expected, $actual, $precision = 1 ) {
return abs( (float) $expected - (float) $actual ) <= $precision;
}

/**
* Sanitizes an attributes array into an attributes string to be placed inside a `<script>` tag.
*
* Automatically injects type attribute if needed.
* Used by {@see wp_get_script_tag()} and {@see wp_get_inline_script_tag()}.
*
* @since 5.6.0
*
* @param array $attributes Key-value pairs representing `<script>` tag attributes.
* @return string String made of sanitized `<script>` tag attributes.
*/
function wp_sanitize_script_attributes( $attributes ) {
$html5_script_support = ! is_admin() && ! current_theme_supports( 'html5', 'script' );
$attributes_string = '';

// If HTML5 scirpt tag is supported, only the attribute name is added
// to $attributes_string for entries with a boolean value, and that are true.
foreach ( $attributes as $attribute_name => $attribute_value ) {
if ( is_bool( $attribute_value ) ) {
if ( $attribute_value ) {
$attributes_string .= $html5_script_support ? sprintf( ' %1$s="%2$s"', $attribute_name, esc_attr( $attribute_name ) ) : ' ' . $attribute_name;
}
} else {
$attributes_string .= sprintf( ' %1$s="%2$s"', $attribute_name, esc_attr( $attribute_value ) );
}
}

return $attributes_string;
}

/**
* Formats `<script>` loader tags.
*
* It is possible to inject attributes in the `<script>` tag via the {@see 'wp_script_attributes'} filter.
* Automatically injects type attribute if needed.
*
* @since 5.6.0
*
* @param array $attributes Key-value pairs representing `<script>` tag attributes.
* @return string String containing `<script>` opening and closing tags.
*/
function wp_get_script_tag( $attributes ) {
if ( ! isset( $attributes['type'] ) && ! is_admin() && ! current_theme_supports( 'html5', 'script' ) ) {
$attributes['type'] = 'text/javascript';
}
/**
* Filters attributes to be added to a script tag.
*
* @since 5.6.0
*
* @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.
*/
$attributes = apply_filters( 'wp_script_attributes', $attributes );

return sprintf( "<script%s></script>\n", wp_sanitize_script_attributes( $attributes ) );
}

/**
* Prints formatted `<script>` loader tag.
*
* It is possible to inject attributes in the `<script>` tag via the {@see 'wp_script_attributes'} filter.
* Automatically injects type attribute if needed.
*
* @since 5.6.0
*
* @param array $attributes Key-value pairs representing `<script>` tag attributes.
*/
function wp_print_script_tag( $attributes ) {
echo wp_get_script_tag( $attributes );
}

/**
* Wraps inline JavaScript in `<script>` tag.
*
* It is possible to inject attributes in the `<script>` tag via the {@see 'wp_script_attributes'} filter.
* Automatically injects type attribute if needed.
*
* @since 5.6.0
*
* @param string $javascript Inline JavaScript code.
* @param array $attributes Optional. Key-value pairs representing `<script>` tag attributes.
* @return string String containing inline JavaScript code wrapped around `<script>` tag.
*/
function wp_get_inline_script_tag( $javascript, $attributes = array() ) {
if ( ! isset( $attributes['type'] ) && ! is_admin() && ! current_theme_supports( 'html5', 'script' ) ) {
$attributes['type'] = 'text/javascript';
}
/**
* Filters attributes to be added to a script tag.
*
* @since 5.6.0
*
* @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.
*/
$attributes = apply_filters( 'wp_script_attributes', $attributes );

$javascript = "\n" . trim( $javascript, "\n\r " ) . "\n";

return sprintf( "<script%s>%s</script>\n", wp_sanitize_script_attributes( $attributes ), $javascript );
}

/**
* Prints inline JavaScript wrapped in `<script>` tag.
*
* It is possible to inject attributes in the `<script>` tag via the {@see 'wp_script_attributes'} filter.
* Automatically injects type attribute if needed.
*
* @since 5.6.0
*
* @param string $javascript Inline JavaScript code.
* @param array $attributes Optional. Key-value pairs representing `<script>` tag attributes.
*/
function wp_print_inline_script_tag( $javascript, $attributes = array() ) {
echo wp_get_inline_script_tag( $javascript, $attributes );
}
119 changes: 119 additions & 0 deletions tests/phpunit/tests/functions/wpInlineScriptTag.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,119 @@
<?php

/**
* Test wp_get_inline_script_tag() and wp_print_inline_script_tag().
*
* @group functions.php
*/
class Tests_Functions_wpInlineScriptTag extends WP_UnitTestCase {

private $event_handler = <<<'JS'
document.addEventListener( 'DOMContentLoaded', function () {
document.getElementById( 'elementID' )
.addEventListener( 'click', function( event ) {
event.preventDefault();
});
});
JS;

function get_inline_script_tag_type_set() {
add_theme_support( 'html5', array( 'script' ) );

$this->assertSame(
'<script type="application/javascript" nomodule>' . "\n{$this->event_handler}\n</script>\n",
wp_get_inline_script_tag(
$this->event_handler,
array(
'type' => 'application/javascript',
'async' => false,
'nomodule' => true,
)
)
);

remove_theme_support( 'html5' );

$this->assertSame(
'<script type="application/javascript" nomodule>' . "\n{$this->event_handler}\n</script>\n",
wp_get_inline_script_tag(
$this->event_handler,
array(
'type' => 'application/javascript',
'async' => false,
'nomodule' => true,
)
)
);
}

function test_get_inline_script_tag_type_not_set() {
add_theme_support( 'html5', array( 'script' ) );

$this->assertSame(
"<script nomodule>\n{$this->event_handler}\n</script>\n",
wp_get_inline_script_tag(
$this->event_handler,
array(
'async' => false,
'nomodule' => true,
)
)
);

remove_theme_support( 'html5' );
}

function test_get_inline_script_tag_unescaped_src() {
add_theme_support( 'html5', array( 'script' ) );

$this->assertSame(
"<script>\n{$this->event_handler}\n</script>\n",
wp_get_inline_script_tag( $this->event_handler )
);

remove_theme_support( 'html5' );
}

function test_print_script_tag_prints_get_inline_script_tag() {
add_filter(
'wp_script_attributes',
function ( $attributes ) {
if ( isset( $attributes['id'] ) && 'utils-js-extra' === $attributes['id'] ) {
$attributes['async'] = true;
}
return $attributes;
}
);

add_theme_support( 'html5', array( 'script' ) );

$attributes = array(
'id' => 'utils-js-before',
'nomodule' => true,
);

$this->assertSame(
wp_get_inline_script_tag( $this->event_handler, $attributes ),
get_echo(
'wp_print_inline_script_tag',
array(
$this->event_handler,
$attributes,
)
)
);

remove_theme_support( 'html5' );

$this->assertSame(
wp_get_inline_script_tag( $this->event_handler, $attributes ),
get_echo(
'wp_print_inline_script_tag',
array(
$this->event_handler,
$attributes,
)
)
);
}
}
130 changes: 130 additions & 0 deletions tests/phpunit/tests/functions/wpSanitizeScriptAttributes.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,130 @@
<?php

/**
* Test wp_sanitize_script_attributes().
*
* @group functions.php
*/
class Tests_Functions_wpSanitizeScriptAttributes extends WP_UnitTestCase {

function test_sanitize_script_attributes_type_set() {
add_theme_support( 'html5', array( 'script' ) );

$this->assertSame(
' type="application/javascript" src="https://DOMAIN.TLD/PATH/FILE.js" nomodule',
wp_sanitize_script_attributes(
array(
'type' => 'application/javascript',
'src' => 'https://DOMAIN.TLD/PATH/FILE.js',
'async' => false,
'nomodule' => true,
)
)
);

remove_theme_support( 'html5' );

$this->assertSame(
' src="https://DOMAIN.TLD/PATH/FILE.js" type="application/javascript" nomodule="nomodule"',
wp_sanitize_script_attributes(
array(
'src' => 'https://DOMAIN.TLD/PATH/FILE.js',
'type' => 'application/javascript',
'async' => false,
'nomodule' => true,
)
)
);
}

function test_sanitize_script_attributes_type_not_set() {
add_theme_support( 'html5', array( 'script' ) );

$this->assertSame(
' src="https://DOMAIN.TLD/PATH/FILE.js" nomodule',
wp_sanitize_script_attributes(
array(
'src' => 'https://DOMAIN.TLD/PATH/FILE.js',
'async' => false,
'nomodule' => true,
)
)
);

remove_theme_support( 'html5' );

$this->assertSame(
' src="https://DOMAIN.TLD/PATH/FILE.js" nomodule="nomodule"',
wp_sanitize_script_attributes(
array(
'src' => 'https://DOMAIN.TLD/PATH/FILE.js',
'async' => false,
'nomodule' => true,
)
)
);
}


function test_sanitize_script_attributes_no_attributes() {
add_theme_support( 'html5', array( 'script' ) );

$this->assertSame(
'',
wp_sanitize_script_attributes( array() )
);

remove_theme_support( 'html5' );
}

function test_sanitize_script_attributes_relative_src() {
add_theme_support( 'html5', array( 'script' ) );

$this->assertSame(
' src="PATH/FILE.js" nomodule',
wp_sanitize_script_attributes(
array(
'src' => 'PATH/FILE.js',
'async' => false,
'nomodule' => true,
)
)
);

remove_theme_support( 'html5' );
}


function test_sanitize_script_attributes_only_false_boolean_attributes() {
add_theme_support( 'html5', array( 'script' ) );

$this->assertSame(
'',
wp_sanitize_script_attributes(
array(
'async' => false,
'nomodule' => false,
)
)
);

remove_theme_support( 'html5' );
}

function test_sanitize_script_attributes_only_true_boolean_attributes() {
add_theme_support( 'html5', array( 'script' ) );

$this->assertSame(
' async nomodule',
wp_sanitize_script_attributes(
array(
'async' => true,
'nomodule' => true,
)
)
);

remove_theme_support( 'html5' );
}

}
Loading