Opened 9 months ago
Closed 3 months ago
#63427 closed defect (bug) (fixed)
User roles property should always be an array, but they sometimes become an object in localized data
| Reported by: |
|
Owned by: |
|
|---|---|---|---|
| Milestone: | 6.9 | Priority: | normal |
| Severity: | normal | Version: | 2.0 |
| Component: | Users | Keywords: | has-patch has-test-info has-unit-tests commit |
| Focuses: | Cc: |
Description
When filtering roles via array_filter() in wp-includes/class-wp-user.php, the resulting array ($this->roles) can retain non-sequential numeric keys, if any roles are filtered out. This causes WP_User->roles to be treated as an object when JSON-encoded (e.g., in wp_localize_script()), instead of a proper array, which breaks client-side expectations.
$current_user = wp_get_current_user();
$local_data = array(
'current_user' => array(
'id' => $current_user->ID,
'roles' => $current_user->roles,
),
);
wp_localize_script( 'myplugin-frontend', '_myplugin_object', $local_data );
If roles contains keys like [0 => 'editor', 2 => 'seo'] , the resulting JSON becomes:
{ roles: { "0": "editor", "2": "custom" } }
Instead of
{ roles: ["editor", "custom"] }
Attachments (1)
Change History (27)
This ticket was mentioned in PR #8790 on WordPress/wordpress-develop by @haruncpi.
9 months ago
#1
When filtering roles via array_filter() in wp-includes/class-wp-user.php, the resulting array ($this->roles) can retain non-sequential numeric keys, if any roles are filtered out. This causes WP_User->roles to be treated as an object when JSON-encoded (e.g., in wp_localize_script()), instead of a proper array, which breaks client-side expectations.
$current_user = wp_get_current_user();
$local_data = array(
'current_user' => array(
'id' => $current_user->ID,
'roles' => $current_user->roles,
),
);
wp_localize_script( 'myplugin-frontend', '_myplugin_object', $local_data );
If roles contains keys like [0 => 'editor', 2 => 'seo'], the resulting JSON becomes:
{ roles: { "0": "editor", "2": "custom" } }
Instead of
{ roles: ["editor", "custom"] }
Ticket’s URL
https://core.trac.wordpress.org/ticket/63427
@getsyash commented on PR #8790:
9 months ago
#2
### Test Report
I’ve tested this PR and can confirm that it works as expected. After applying the changes, the $current_user->roles array is properly reindexed using array_values(), ensuring sequential keys. The localized JSON output now correctly produces an array instead of a JavaScript object, which resolves the client-side issue.
Tested with multiple role combinations, including cases where some roles were removed — everything behaves correctly. ✅
### Testing environment:
WordPress Version: 6.8.1
PHP Version: 8.2.27
Theme: TwentyTwentyFive
Plugins: None installed
OS: Windows
Thanks for the Fix
#3
@
9 months ago
- Resolution set to worksforme
- Status changed from assigned to closed
Could you kindly take a look and review this fix? @SergeyBiryukov @desrosj
#4
@
9 months ago
- Component changed from General to Users
- Focuses sustainability removed
- Keywords has-test-info dev-feedback has-unit-tests added
- Resolution worksforme deleted
- Status changed from closed to reopened
@haruncpi If you have a patch and a test report, you dont have to close the ticket. Leave it open for review with the patch. Closing with worksforme is used when someone reports a bug, you test it and you see there is no bug.
PS: I have not tested the patch, I'm just informing the user.
#6
@
9 months ago
- Milestone changed from Awaiting Review to 6.9
- Owner changed from haruncpi to SergeyBiryukov
- Status changed from reopened to reviewing
#8
follow-up:
↓ 10
@
8 months ago
- Keywords changes-requested added; needs-testing removed
@haruncpi I've added some code review in your PR. Give it a check when you can.
#9
@
8 months ago
- Keywords reporter-feedback added
- Version changed from 6.8 to 2.0
<has-code-review>
I've been able to track the introduction of array_keys back to 1.6 [2793], which is not the problem itself, but is the main reason of why this is happening. Because this thing has probably existed historically, from the inception of this function.
The question here is: @haruncpi why you don't simply preprocess the resulting json into an array, before delivering it to wp_localize_script?
#10
in reply to:
↑ 8
@
8 months ago
Replying to SirLouen:
@haruncpi I've added some code review in your PR. Give it a check when you can.
PR is updated
#11
follow-up:
↓ 12
@
8 months ago
- Keywords 2nd-opinion added; changes-requested reporter-feedback removed
Still I have the question:
@haruncpi why you don't simply preprocess the resulting json into an array, before delivering it to wp_localize_script?
This not sequential order has been for ages, and I'm not sure if it's a bug or just something expected. After checking the history of this function, this has been there for easily 15+ years (since WP 1.6 when they introduced array_keys). Not saying that everything old is correct, but I'm still trying to find an useful reason.
#12
in reply to:
↑ 11
;
follow-up:
↓ 13
@
8 months ago
Replying to SirLouen:
Still I have the question:
@haruncpi why you don't simply preprocess the resulting json into an array, before delivering it to
wp_localize_script?
This not sequential order has been for ages, and I'm not sure if it's a bug or just something expected. After checking the history of this function, this has been there for easily 15+ years (since WP 1.6 when they introduced
array_keys). Not saying that everything old is correct, but I'm still trying to find an useful reason.
Thanks for the question!
The issue isn’t really about whether this behavior has existed since early WordPress versions. It likely wasn’t noticed, triggered, or was often preprocessed by developers before reaching wp_localize_script.
However, delivering correct, consistent data directly from core is always the better approach. It ensures data integrity at the source and removes the need for extra preprocessing by developers. This makes the data safer and more predictable for all use cases.
Additionally, the process is now optimized to linear time, preventing potential cubic complexity.
#13
in reply to:
↑ 12
@
8 months ago
Replying to haruncpi:
However, delivering correct, consistent data directly from core is always the better approach. It ensures data integrity at the source and removes the need for extra preprocessing by developers. This makes the data safer and more predictable for all use cases.
I understand your concern which from my point of view is valid
I think that for this reason, its required to use the maybe_unserialize WP function.
$current_user = wp_get_current_user();
$local_data = array(
'current_user' => array(
'id' => $current_user->ID,
'roles' => maybe_unserialize( $current_user->roles ),
),
);
wp_localize_script( 'myplugin-frontend', '_myplugin_object', $local_data );
Probably due to this kind of issues all over the code.
Still I agree with you that this could/should be fixed to provide a more consistent data and also now I feel that with a simplified complexity (not from cubic, but cuadratic because original there were only two iterations), now becomes linear so it also provides some performance improvement.
#14
@
8 months ago
- Keywords 2nd-opinion removed
At this point, I think it is ready to be shipped
cc @SergeyBiryukov
#15
follow-up:
↓ 16
@
3 months ago
@haruncpi @SirLouen Can you rebase the pull request on top of the trunk branch to run CI and ensure that the tests don't fail?
#16
in reply to:
↑ 15
@
3 months ago
I have merged the latest changes from WordPress:trunk into my PR branch.
I ran the tests/phpunit/tests/user.php suite, and all tests passed successfully.
Additionally, the test_user_roles_property_is_sequential_array test ran without any failures.
Replying to wildworks:
@haruncpi @SirLouen Can you rebase the pull request on top of the trunk branch to run CI and ensure that the tests don't fail?
#17
@
3 months ago
Hi @SergeyBiryukov @wildworks,
This was completed and tested about 5 months ago, and @SirLouen confirmed it’s ready to ship.
When you have a moment, please review and merge the PR.
#18
@
3 months ago
@haruncpi Thanks for the update!
@SergeyBiryukov, Do you have the bandwidth to commit to PR 8790 before the RC1 release?
#20
@
3 months ago
- Keywords changes-requested added; commit removed
I will change the keywords because new feedback has been added to the pull request.
#21
@
3 months ago
- Keywords changes-requested removed
@wildworks
Thanks! I’ve updated the PR with all the requested changes except one.
I’ve also added an explanation for why a simple foreach loop was used instead of array_values() — mainly for performance and clarity reasons, as suggested by @SirLouen in this commit https://github.com/WordPress/wordpress-develop/pull/8790/commits/4e32ecf82553567ffc7598296a04490dda85c95b
@wildworks commented on PR #8790:
3 months ago
#22
@haruncpi It appears the unit tests are failing. Could you investigate whether the problem lies in the logic itself or if the tests need to be updated?
@haruncpi commented on PR #8790:
3 months ago
#23
@t-hamano unit test case updated, now it's working fine.
Adds array reindexing to ensure roles remains a proper indexed array