Opened 12 years ago
Closed 6 years ago
#29722 closed defect (bug) (wontfix)
wp_localize_script should be able to manage scalars
| Reported by: | Fab1en | Owned by: | |
|---|---|---|---|
| Priority: | normal | Milestone: | |
| Component: | Script Loader | Version: | 3.3 |
| Severity: | normal | Keywords: | needs-docs has-unit-tests needs-patch |
| Cc: | Focuses: | javascript |
Description
We should be able to do this :
wp_localize_script( 'twentyfourteen-script', 'localized_variable', 1 );
As of version 4.0, this is working : it generates a JavaScript scalar value, but a PHP Warning is thrown :
Warning: Cannot use a scalar value as an array in /server/wordpress/versions/git-repo/src/wp-includes/class.wp-scripts.php on line 167
Note that the correct JavaScript code is output :
<script type='text/javascript'> /* <![CDATA[ */ var localized_variable = 1; /* ]]> */ </script>
The warning is not triggered when the scalar variable is a string.
wp_localize_script( 'twentyfourteen-script', 'localized_variable', 'a string' );
This is just because strings can be accessed like arrays, but this is not the intended effect.
A simple test for scalar values can fix that.
Attachments (4)
Change History (40)
#2
@
12 years ago
To document this in wp_localize_script() I ran some tests. Inline doc would go:
- * @param array $l10n The data itself. The data can be either a single or multi-dimensional array.
+ * @param mixed $l10n The data to pass through to json_encode().
If the data is a string, html entities are decoded.
If the data is an array, first dimension string values are html decoded.
Here is how the patch changes current behavior:
No change:
null, bool(true), integer, float, array
Changes:
#1. var_dump(v): bool(false)
(was) $script : var v = [""];
(now) $script : var v = false;
#2. var_dump(v): string(19) "Save & Activate"
(was) $script : var v = "Save & Activate";
(now) $script : var v = "Save & Activate";
#3. var_dump(v): object(stdClass)#1 (1) {
["foo"]=>
string(3) "bar"
}
(was) [die]
(now) $script : var v = {"foo":"bar"};
The WP calls in script-loader.php are not affected because they all use arrays. One oddball uses a multidimensional array to protect an integer from getting made into a string by the intended old behavior (which would not happen anyway because of the bug). Another oddball wraps the array elements in esc_attr() which could be nixed:
did_action( 'init' ) && $scripts->localize( 'quicktags', 'quicktagsL10n', array(
'closeAllOpenTags' => esc_attr(__('Close all open tags')),
...
#3
follow-up:
↓ 4
@
12 years ago
I had to edit my inline doc above. It is quite a custom function. This part in particular might be up for debate, but matches current behavior:
If the data is an array, first dimension scalar values are converted to string and html decoded.
#4
in reply to: ↑ 3
@
12 years ago
Ideally, I would like to remove the "first dimension scalar values are converted to string" part, in relation to ticket #25280. Only strings should be "html decoded".
Replying to kitchin:
I had to edit my inline doc above. It is quite a custom function. This part in particular might be up for debate, but matches current behavior:
If the data is an array, first dimension scalar values are converted to string and html decoded.
#5
@
12 years ago
I'm out of time, but yeah, this is the wrong fix to be compatible with https://core.trac.wordpress.org/ticket/25280 and its tests AFAICT. I think you want:
if ( is_array($l10n) ) {
foreach ( $l10n as $key => $value ) {
if ( is_string($value) ) {
$l10n[$key] = html_entity_decode( $value, ENT_QUOTES, 'UTF-8');
}
}
} elseif ( is_string($l10n) ) {
$l10n = html_entity_decode( $l10n, ENT_QUOTES, 'UTF-8');
}
And object users can now bypass it all!
#6
@
12 years ago
Should satisfy:
* @param mixed $l10n The data to pass through to json_encode().
If the data is a string, html entities are decoded.
If the data is an array, first dimension string values
are html entity decoded.
#8
@
11 years ago
- Keywords has-patch dev-feedback 2nd-opinion added
- Type enhancement → defect (bug)
Big +1 here. Scalars should work (they actually do, but not w/o warnings). I've attached a patch (29722.3.diff) which allows scalars, and adds a supporting unit test. Changing this to a defect/bug because it's actually a bug in the way it's written (looping through $l10n by casting $l10n to an array but not checking if it's an array before stuffing an array key/value).
#9
follow-up:
↓ 10
@
11 years ago
- Keywords needs-docs added; dev-feedback 2nd-opinion removed
Scalars should work (they actually do, but not w/o warnings).
Changing this to a defect/bug because it's actually a bug in the way it's written (looping through $l10n by casting $l10n to an array but not checking if it's an array before stuffing an array key/value).
They almost work. The $l10n[$key] bit means that HTML entities in scalars aren't currently decoded. The fact that this use throws a PHP notice is probably helpful to developers, as it lets them know that the decoding/sanitization is not taking place properly.
I don't see the huge hardship in just passing a singleton array as $l10n :) but I don't see any harm in accepting scalars here. The patch looks OK to me, but we need to have improved documentation - the docs for wp_localize_script() are already subpar, and this syntax enhancement makes it even less clear how the function is intended to be used.
#10
in reply to: ↑ 9
@
11 years ago
Replying to boonebgorges:
Scalars should work (they actually do, but not w/o warnings).
Changing this to a defect/bug because it's actually a bug in the way it's written (looping through $l10n by casting $l10n to an array but not checking if it's an array before stuffing an array key/value).
They almost work. The
$l10n[$key]bit means that HTML entities in scalars aren't currently decoded. The fact that this use throws a PHP notice is probably helpful to developers, as it lets them know that the decoding/sanitization is not taking place properly.
Ah, yes, I missed that part (that the value was not being decoded/sanitized).
I don't see the huge hardship in just passing a singleton array as
$l10n:) but I don't see any harm in accepting scalars here. The patch looks OK to me, but we need to have improved documentation - the docs forwp_localize_script()are already subpar, and this syntax enhancement makes it even less clear how the function is intended to be used.
I'll add additional documentation, but I also discovered another path where the values would not be decoded/sanitized, and that is if $l10n had a multi-dimensional array. Any values deeper than the first level would be left untouched. This is because of the if ( ! is_scalar( $value ) ) { continue; } check in the loop. I'm thinking maybe we want to do the html_entity_decode( (string) $value, ENT_QUOTES, 'UTF-8') recursively? If you agree, I can update this patch to create a recursive method for doing so.
#11
follow-up:
↓ 12
@
11 years ago
I'm thinking maybe we want to do the html_entity_decode( (string) $value, ENT_QUOTES, 'UTF-8') recursively?
Groan :) If you can recurse without losing performance for one-dimensional arrays, then OK.
#12
in reply to: ↑ 11
;
follow-up:
↓ 13
@
11 years ago
Replying to boonebgorges:
I'm thinking maybe we want to do the html_entity_decode( (string) $value, ENT_QUOTES, 'UTF-8') recursively?
Groan :) If you can recurse without losing performance for one-dimensional arrays, then OK.
I don't believe it will be an issue unless you believe calling in_array for each value in a one-dimensional array will cause significant performance loss. The other option is to to check something like $is_multi = is_array( array_values( $l10n1 )[0] ), but to determine which was more performant would def. require some benchmarks. I would not be a bit surprised if multi-dimensional arrays were often used.
#13
in reply to: ↑ 12
@
11 years ago
Replying to jtsternberg:
The other option is to to check something like
$is_multi = is_array( array_values( $l10n1 )[0] )
Derp, that will only check if the first item in the array is an array. Looks like we'll have to check is_array on every value.
This ticket was mentioned in Slack in #core-i18n by swissspidy. View the logs.
10 years ago
#16
@
10 years ago
I added a new patch on 25280 that proposes adding a new helper that bundles the code and tests from the latest patch here.
#20
@
6 years ago
A quick fix for PHP 8 is to do the array casting before the foreach loop, in /wp-includes/class.wp-scripts.php:localize like:
$l10n = ( array ) $l10n;
foreach ( $l10n as $key => $value ) {
if ( ! is_scalar( $value ) ) {
continue;
}
$l10n[ $key ] = html_entity_decode( (string) $value, ENT_QUOTES, 'UTF-8' );
}
But it seems so "hacky" :)
#21
@
6 years ago
- Keywords needs-dev-note added
This may be worth mentioning PHP 8 dev note in the context of what developers need to know as warning swill now be thrown.
This ticket was mentioned in Slack in #core by hellofromtonya. View the logs.
6 years ago
#23
@
6 years ago
- Milestone Awaiting Review → 5.6
Per yesterday's scrub conversation in slack, this ticket is part of the PHP8 5.6 scope. Moving into the milestone.
This ticket was mentioned in Slack in #core by hellofromtonya. View the logs.
6 years ago
This ticket was mentioned in Slack in #core by helen. View the logs.
6 years ago
This ticket was mentioned in Slack in #core by hellofromtonya. View the logs.
6 years ago
This ticket was mentioned in PR #744 on WordPress/wordpress-develop by draganescu.
6 years ago
#28
- Keywords has-unit-tests added
## Description
- Refreshed the latest patch on 29722
- Passing scalars just outputs them,
- Passing strings now also works on PHP 8.
## Reference
Trac ticket: https://core.trac.wordpress.org/ticket/29722
This ticket was mentioned in Slack in #core by helen. View the logs.
6 years ago
#31
@
6 years ago
Notes from conversation with @SergeyBiryukov
I didn't have a chance to review 29722 yet, will try to catch up with the discussion today before the party. I have no objections for it to continue into RC, it might seem a bit late, but if it helps prevent some PHP 8 breakage, still might be a good fit for this release.
This ticket was mentioned in Slack in #core by helen. View the logs.
6 years ago
#33
@
6 years ago
- Milestone 5.6 → 5.7
It's late to be committing something that's potentially an unexpected change in that scalar values will now be included and decoded whereas previously they were skipped. Consider this scenario from @TimothyBlynJacobs and whether its outcome would change with this patch:
wp_localize_script( 'handle', 'LOCALIZED', [ 'input' => [ 'attr' => esc_html( get_unsafe_value() ) ] ] ); ?> <script type="application/javascript"> var html = '<p attr="' + LOCALIZED.input.attr + '">' </script>
Defensive programming to account for PHP 8 compat with plugins/themes/the broader ecosystem is something we're targeting for a later release, so I'm punting from 5.6 because this isn't an issue that core triggers in itself but rather is a potential issue with custom code.
#34
@
6 years ago
- Keywords needs-patch added; has-patch removed
Been chatting with @andraganescu about what's the right solution here.
This ticket is a partial duplicate of #25280. Specifically see this comment: https://core.trac.wordpress.org/ticket/25280#comment:45. In short:
- wp_localize_script() should only be used to localize scripts, not to pass arbitrary data to them. There is wp_add_inline_script() for that.
- For translations
wp_localize_script()and the underlayingWP_Scripts::localize()have been phased out in core, in favor ofWP_Scripts::set_translations().
In that terms thinking this ticket should be "wontfix", same as #25280. The only thing that needs fixing is the PHP 8.0 bug, and perhaps some more inline docs to explain why not to use this function for arbitrary data.
![(please configure the [header_logo] section in trac.ini)](/chrome/site/your_project_logo.png)
Avoid warning when scalar value is passed