Index: src/wp-includes/class.wp-scripts.php =================================================================== --- src/wp-includes/class.wp-scripts.php (revision 36627) +++ src/wp-includes/class.wp-scripts.php (working copy) @@ -25,6 +25,7 @@ public $concat_version = ''; public $do_concat = false; public $print_html = ''; + public $print_html_before = ''; public $print_code = ''; public $ext_handles = ''; public $ext_version = ''; @@ -144,6 +145,17 @@ $cond_after = "\n"; } + $before_handle = $this->print_inline_script( $handle, 'before', false ); + $after_handle = $this->print_inline_script( $handle, 'after', false ); + + if ( $before_handle ) { + $before_handle = sprintf( "\n", $before_handle ); + } + + if ( $after_handle ) { + $after_handle = sprintf( "\n", $after_handle ); + } + if ( $this->do_concat ) { /** * Filter the script loader source. @@ -154,7 +166,12 @@ * @param string $handle Script handle. */ $srce = apply_filters( 'script_loader_src', $src, $handle ); - if ( $this->in_default_dir( $srce ) && ! $conditional ) { + + if ( $before_handle && ! $conditional ) { + $this->print_html_before .= $before_handle; + } + + if ( $this->in_default_dir( $srce ) && ! $conditional && ! $after_handle ) { $this->print_code .= $this->print_extra_script( $handle, false ); $this->concat .= "$handle,"; $this->concat_version .= "$handle$ver"; @@ -195,7 +212,7 @@ if ( ! $src ) return true; - $tag = "{$cond_before}\n{$cond_after}"; + $tag = "{$cond_before}{$before_handle}\n{$after_handle}{$cond_after}"; /** * Filter the HTML script tag of an enqueued script. @@ -209,7 +226,11 @@ $tag = apply_filters( 'script_loader_tag', $tag, $handle, $src ); if ( $this->do_concat ) { - $this->print_html .= $tag; + if ( $after_handle ) { + $this->print_html_before .= $tag; + } else { + $this->print_html .= $tag; + } } else { echo $tag; } @@ -218,6 +239,61 @@ } /** + * Add extra code to a registered script. + * + * @since 4.5.0 + * + * @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'. + * + * @return bool True on success, false on failure. + */ + public function add_inline_script( $handle, $data, $position = 'after' ) { + if ( ! $data ) { + return false; + } + + if ( 'after' !== $position ) { + $position = 'before'; + } + + $script = (array) $this->get_data( $handle, $position ); + $script[] = $data; + + return $this->add_data( $handle, $position, $script ); + } + + /** + * Print inline scripts registered for a specific handle. + * + * @since 4.5.0 + * + * @param string $handle Name of the script to add the inline script to. Must be lowercase. + * @param string $position Optional. Whether to add the inline script before the handle + * or after. Default 'after'. + * @param bool $echo Optional. Whether to echo the script instead of just returning it. + * Default true. + * @return string|false Script on success, false otherwise. + */ + public function print_inline_script( $handle, $position = 'after', $echo = true ) { + $output = $this->get_data( $handle, $position ); + + if ( empty( $output ) ) { + return false; + } + + $output = trim( implode( "\n", $output ), "\n" ); + + if ( $echo ) { + printf( "\n", $output ); + } + + return $output; + } + + /** * Localizes a script, only if the script has already been added * * @param string $handle @@ -339,6 +415,7 @@ $this->concat = ''; $this->concat_version = ''; $this->print_html = ''; + $this->print_html_before = ''; $this->ext_version = ''; $this->ext_handles = ''; } Index: src/wp-includes/functions.wp-scripts.php =================================================================== --- src/wp-includes/functions.wp-scripts.php (revision 36627) +++ src/wp-includes/functions.wp-scripts.php (working copy) @@ -86,6 +86,36 @@ } /** + * Add extra code to a registered script. + * + * Code will only be added if the script in already in the queue. + * Accepts a string $data containing the Code. If two or more code blocks + * are added to the same script $handle, they will be printed in the order + * they were added, i.e. the latter added code can redeclare the previous. + * + * @since 4.5.0 + * + * @see WP_Scripts::add_inline_script() + * + * @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'. + * @return bool True on success, false on failure. + */ +function wp_add_inline_script( $handle, $data, $position = 'after' ) { + _wp_scripts_maybe_doing_it_wrong( __FUNCTION__ ); + + if ( false !== stripos( $data, '' ) ) { + _doing_it_wrong( __FUNCTION__, __( 'Do not pass script tags to wp_add_inline_script().' ), '4.5.0' ); + $data = trim( preg_replace( '#]*>(.*)#is', '$1', $data ) ); + } + + return wp_scripts()->add_inline_script( $handle, $data, $position ); +} + + +/** * Register a new script. * * Registers a script to be linked later using the wp_enqueue_script() function. Index: src/wp-includes/script-loader.php =================================================================== --- src/wp-includes/script-loader.php (revision 36627) +++ src/wp-includes/script-loader.php (working copy) @@ -992,6 +992,10 @@ echo "\n"; } + if ( ! empty( $wp_scripts->print_html_before ) ) { + echo $wp_scripts->print_html_before; + } + $concat = str_split( $concat, 128 ); $concat = 'load%5B%5D=' . implode( '&load%5B%5D=', $concat ); Index: tests/phpunit/tests/dependencies/scripts.php =================================================================== --- tests/phpunit/tests/dependencies/scripts.php (revision 36627) +++ tests/phpunit/tests/dependencies/scripts.php (working copy) @@ -86,6 +86,28 @@ } /** + * Test script concatenation. + */ + public function test_script_concatenation() { + global $wp_scripts; + + $wp_scripts->do_concat = true; + $wp_scripts->default_dirs = array( '/directory/' ); + + wp_enqueue_script( 'one', '/directory/script.js' ); + wp_enqueue_script( 'two', '/directory/script.js' ); + wp_enqueue_script( 'three', '/directory/script.js' ); + + wp_print_scripts(); + $print_scripts = get_echo( '_print_scripts' ); + + $ver = get_bloginfo( 'version' ); + $expected = "\n"; + + $this->assertEquals( $expected, $print_scripts ); + } + + /** * Testing `wp_script_add_data` with the data key. * @ticket 16024 */ @@ -266,4 +288,178 @@ $this->assertEquals( $expected_header, $header ); $this->assertEquals( $expected_footer, $footer ); } + + /** + * @ticket 14853 + */ + function test_wp_add_inline_script_returns_bool() { + $this->assertFalse( wp_add_inline_script( 'test-example', 'console.log("before");', 'before' ) ); + wp_enqueue_script( 'test-example', 'example.com', array(), null ); + $this->assertTrue( wp_add_inline_script( 'test-example', 'console.log("before");', 'before' ) ); + } + + /** + * @ticket 14853 + */ + function test_wp_add_inline_script_unknown_handle() { + $this->assertFalse( wp_add_inline_script( 'test-invalid', 'console.log("before");', 'before' ) ); + $this->assertEquals( '', get_echo( 'wp_print_scripts' ) ); + } + + /** + * @ticket 14853 + */ + function test_wp_add_inline_script_before() { + wp_enqueue_script( 'test-example', 'example.com', array(), null ); + wp_add_inline_script( 'test-example', 'console.log("before");', 'before' ); + + $expected = "\n"; + $expected .= "\n"; + + $this->assertEquals( $expected, get_echo( 'wp_print_scripts' ) ); + } + + /** + * @ticket 14853 + */ + function test_wp_add_inline_script_after() { + wp_enqueue_script( 'test-example', 'example.com', array(), null ); + wp_add_inline_script( 'test-example', 'console.log("after");' ); + + $expected = "\n"; + $expected .= "\n"; + + $this->assertEquals( $expected, get_echo( 'wp_print_scripts' ) ); + } + + /** + * @ticket 14853 + */ + function test_wp_add_inline_script_before_and_after() { + wp_enqueue_script( 'test-example', 'example.com', array(), null ); + wp_add_inline_script( 'test-example', 'console.log("before");', 'before' ); + wp_add_inline_script( 'test-example', 'console.log("after");' ); + + $expected = "\n"; + $expected .= "\n"; + $expected .= "\n"; + + $this->assertEquals( $expected, get_echo( 'wp_print_scripts' ) ); + } + + /** + * @ticket 14853 + */ + function test_wp_add_inline_script_multiple() { + wp_enqueue_script( 'test-example', 'example.com', array(), null ); + wp_add_inline_script( 'test-example', 'console.log("before");', 'before' ); + wp_add_inline_script( 'test-example', 'console.log("before");', 'before' ); + wp_add_inline_script( 'test-example', 'console.log("after");' ); + wp_add_inline_script( 'test-example', 'console.log("after");' ); + + $expected = "\n"; + $expected .= "\n"; + $expected .= "\n"; + + $this->assertEquals( $expected, get_echo( 'wp_print_scripts' ) ); + } + + /** + * @ticket 14853 + */ + function test_wp_add_inline_script_localized_data_is_added_first() { + wp_enqueue_script( 'test-example', 'example.com', array(), null ); + wp_localize_script( 'test-example', 'testExample', array( 'foo' => 'bar' ) ); + wp_add_inline_script( 'test-example', 'console.log("before");', 'before' ); + wp_add_inline_script( 'test-example', 'console.log("after");' ); + + $expected = "\n"; + $expected .= "\n"; + $expected .= "\n"; + $expected .= "\n"; + + $this->assertEquals( $expected, get_echo( 'wp_print_scripts' ) ); + } + + /** + * @ticket 14853-2 + */ + public function test_wp_add_inline_script_before_with_concat() { + global $wp_scripts; + + $wp_scripts->do_concat = true; + $wp_scripts->default_dirs = array( '/directory/' ); + + wp_enqueue_script( 'one', '/directory/script.js' ); + wp_enqueue_script( 'two', '/directory/script.js' ); + wp_enqueue_script( 'three', '/directory/script.js' ); + + wp_add_inline_script( 'two', 'console.log("before");', 'before' ); + + wp_print_scripts(); + $print_scripts = get_echo( '_print_scripts' ); + + $ver = get_bloginfo( 'version' ); + $expected = "\n"; + $expected .= "\n"; + + $this->assertEquals( $expected, $print_scripts ); + } + + /** + * @ticket 14853 + */ + public function test_wp_add_inline_script_after_with_concat() { + global $wp_scripts; + + $wp_scripts->do_concat = true; + $wp_scripts->default_dirs = array( '/directory/' ); + + wp_enqueue_script( 'one', '/directory/script.js' ); + wp_enqueue_script( 'two', '/directory/script.js' ); + wp_enqueue_script( 'three', '/directory/script.js' ); + + wp_add_inline_script( 'two', 'console.log("after");' ); + + wp_print_scripts(); + $print_scripts = get_echo( '_print_scripts' ); + + $ver = get_bloginfo( 'version' ); + $expected = "\n"; + $expected .= "\n"; + $expected .= "\n"; + + $this->assertEquals( $expected, $print_scripts ); + } + + /** + * @ticket 14853 + */ + public function test_wp_add_inline_script_concat_with_conditional() { + global $wp_scripts; + + $wp_scripts->do_concat = true; + $wp_scripts->default_dirs = array('/wp-admin/js/', '/wp-includes/js/'); // Default dirs as in wp-includes/script-loader.php + + $expected_localized = "\n"; + + $expected = "\n"; + + wp_enqueue_script( 'test-example', 'example.com', array(), null ); + wp_localize_script( 'test-example', 'testExample', array( 'foo' => 'bar' ) ); + wp_add_inline_script( 'test-example', 'console.log("before");', 'before' ); + wp_add_inline_script( 'test-example', 'console.log("after");' ); + wp_script_add_data( 'test-example', 'conditional', 'gte IE 9' ); + + $this->assertEquals( $expected_localized, get_echo( 'wp_print_scripts' ) ); + $this->assertEquals( $expected, $wp_scripts->print_html_before ); + $this->assertEquals( '', $wp_scripts->print_html ); + } + }