[wp-trac] [WordPress Trac] #58001: Lazy load user capabilities in WP_User object

WordPress Trac noreply at wordpress.org
Fri Oct 10 17:44:59 UTC 2025


#58001: Lazy load user capabilities in WP_User object
----------------------------------+-------------------------------------
 Reporter:  spacedmonkey          |       Owner:  flixos90
     Type:  enhancement           |      Status:  reopened
 Priority:  normal                |   Milestone:  6.9
Component:  Users                 |     Version:
 Severity:  normal                |  Resolution:
 Keywords:  early needs-dev-note  |     Focuses:  multisite, performance
----------------------------------+-------------------------------------

Comment (by bradshawtm):

 > As for get_object_vars, this was considered, but it feels super edge
 case

 I'm not convinced this point is edge case given it came up twice shortly
 after this change was merged. I suspect directly serializing a `WP_User`
 object is fairly common practice. I just realized this also affects
 `json_encode()`/`wp_json_encode()`, neither of which will export the
 protected properties even if populated:

 ```
 <?php
 function test_json_encode() {
         $user = new WP_User(1);
         error_log(var_export( wp_json_encode( $user ), true)); // no roles
         $user->roles;
         error_log(var_export( wp_json_encode( $user ), true)); // no roles
 }
 add_action('wp_footer', 'test_json_encode');
 ```

 ----


 > I used a magic setter, so this should work.

 One can't directly modify the array elements of a protected property, and
 I don't think there's a way for the magic setter to work around that.

 This is discussed as being a breaking change above, and I'm personally
 okay with a dev note on this point rather than allowing the edge case.
 That said, here's a simple example:

 ```
 function test_changed_behavior() {
         $user = new WP_User(1);
         error_log(var_export($user, true)); // `roles` shows as array()
         error_log(var_export($user->roles, true)); // `roles` shows as
 array( 'administrator' )

         // these worked before the change, but not after. Each gives a
 warning: `Indirect modification of overloaded property`
         array_shift( $user->roles );
         $user->roles[] = 'subscriber';
         $user->roles[0] = 'editor';

         error_log(var_export($user->roles, true)); // `roles` shows as
 array( 'administrator' )
 }
 add_action('wp_footer', 'test_changed_behavior');

 function test_mitigation() {
         $user = new WP_User(1);
         error_log(var_export($user, true)); // `roles` shows as array()
         error_log(var_export($user->roles, true)); // `roles` shows as
 array( 'administrator' )

         // Workaround: modify a different var
         $roles = $user->roles;
         array_shift( $roles );
         $roles[] = 'subscriber';
         $roles[0] = 'editor';
         $user->roles = $roles;

         error_log(var_export($user->roles, true)); // `roles` shows as
 array( 'administrator' )
 }
 add_action('wp_footer', 'test_mitigation');
 ```

-- 
Ticket URL: <https://core.trac.wordpress.org/ticket/58001#comment:43>
WordPress Trac <https://core.trac.wordpress.org/>
WordPress publishing platform


More information about the wp-trac mailing list