diff --git a/README.md b/README.md index 093703c..0303b37 100644 --- a/README.md +++ b/README.md @@ -31,6 +31,7 @@ A library for managing asset registration and enqueuing in WordPress. * [Conditional enqueuing](#conditional-enqueuing) * [Firing a callback after enqueuing occurs](#firing-a-callback-after-enqueuing-occurs) * [Output JS data](#output-js-data) + * [Using a callable to provide localization data](#using-a-callable-to-provide-localization-data) * [Output content before/after a JS asset is output](#output-content-beforeafter-a-js-asset-is-output) * [Style meta data](#style-meta-data) @@ -500,6 +501,28 @@ Note the `my-second-script-mod` handle is overriding a specific nested key, `boomshakalaka.project.secondScriptData.animal`, in the `boomshakalaka.project.secondScriptData` object while preserving the other keys. +#### Using a callable to provide localization data + +If you need to provide localization data dynamically, you can use a callable to do so. The callable will be called +when the asset is enqueued and the return value will be used. The callable will be passed the asset as the first +argument and should return an array. + +```php +Asset::add( 'my-script', 'js/some-asset.js' ) + ->add_localize_script( + 'boomshakalaka.project.myScriptData', + function( Asset $asset ) { + return [ + 'animal' => 'cat', + 'color' => 'orange', + ]; + } + ) + ->register(); +``` + +Any valid callable can be used, including Closures, like in the example above. + ### Output content before/after a JS asset is output There may be times when you wish to output markup or text immediately before or immediately after outputting the JS diff --git a/src/Assets/Asset.php b/src/Assets/Asset.php index d93b479..2797f81 100644 --- a/src/Assets/Asset.php +++ b/src/Assets/Asset.php @@ -414,11 +414,13 @@ public function call_after_enqueue( $callable ) { * @since 1.0.0 * * @param string $object_name JS object name. - * @param array $data Data assigned to the JS object. + * @param array|callable $data Data assigned to the JS object. If a callable is passed, it will be called + * when the asset is enqueued and the return value will be used. The callable + * will be passed the asset as the first argument and should return an array. * * @return static */ - public function add_localize_script( string $object_name, array $data ) { + public function add_localize_script( string $object_name, $data ) { if ( str_contains( $object_name, '.' ) ) { $this->custom_localize_script_objects[] = [ $object_name, $data ]; } else { diff --git a/src/Assets/Assets.php b/src/Assets/Assets.php index afdf42e..6fddbcf 100755 --- a/src/Assets/Assets.php +++ b/src/Assets/Assets.php @@ -357,7 +357,7 @@ public function filter_add_localization_data( $tag, $handle ) { // If we have a Callable as the Localize data we execute it. if ( is_callable( $localize ) ) { - $localize = call_user_func( $localize, $asset ); + $localize = $localize( $asset ); } wp_localize_script( $asset->get_slug(), $key, $localize ); @@ -378,6 +378,11 @@ public function filter_add_localization_data( $tag, $handle ) { * Print the dot.notation namespaced objects for the asset. */ foreach ( $custom_localize_scripts as [$object_name, $data] ) { + // If we have a Callable as the Localize data we execute it. + if ( is_callable( $data ) ) { + $data = $data( $asset ); + } + $localized_key = "{$asset->get_slug()}::{$object_name}"; if ( in_array( $localized_key, $this->localized, true ) ) { diff --git a/tests/wpunit/AssetsTest.php b/tests/wpunit/AssetsTest.php index 3345662..b94c677 100644 --- a/tests/wpunit/AssetsTest.php +++ b/tests/wpunit/AssetsTest.php @@ -215,4 +215,55 @@ public function should_allow_localizing_data_in_normal_and_namespaced_form_for_s $apply_filters ); } + + /** + * It should allow localizing data using a Closure + * + * @test + */ + public function should_allow_localizing_data_using_a_closure(): void { + $resolved_one = false; + $resolved_two = false; + $data_callback_one = function () use ( &$resolved_one ) { + $resolved_one = true; + + return [ + 'animal' => 'cat', + 'color' => 'orange', + ]; + }; + $data_callback_two = function () use ( &$resolved_two ) { + $resolved_two = true; + + return [ + 'animal' => 'dog', + 'color' => 'green', + ]; + }; + + Asset::add( 'my-script-with-closure-data', 'test-script.js' ) + ->add_localize_script( 'scriptWithClosureData', $data_callback_one ) + ->add_localize_script( 'acme.project.closureData', $data_callback_two ) + ->register(); + + $this->assertFalse( $resolved_one, 'The first callback should not have been resolved yet.' ); + $this->assertFalse( $resolved_two, 'The second callback should not have been resolved yet.' ); + + $apply_filters = apply_filters( 'script_loader_tag', '', 'my-script-with-closure-data' ); + $this->assertEquals( <<< SCRIPT + + +SCRIPT, + $apply_filters + ); + + $this->assertTrue( $resolved_one ); + $this->assertTrue( $resolved_two ); + } }