Users: Lazy load user capabilities in WP_User object.

Convert the WP_User object properties caps, roles, and allcaps to protected, and introduce lazy loading for capabilities. These properties are now populated only when first accessed.

The existing magic methods (__get, __set, and __unset) have been updated to maintain backward compatibility, ensuring that reading or modifying these formerly public properties continues to work as expected.

Ensure that these properties are initialised when calling remove_all_caps(), remove_cap(), has_cap(), add_role(), and set_role() methods.

Props spacedmonkey, flixos90, peterwilsoncc, mukesh27, westonruter, swissspidy, prettyboymp.
Fixes #58001.
Built from https://develop.svn.wordpress.org/trunk@60915


git-svn-id: http://core.svn.wordpress.org/trunk@60251 1a063a9b-81f0-0310-95a4-ce76da25c4cd
This commit is contained in:
spacedmonkey
2025-10-08 20:01:35 +00:00
parent 69962171e9
commit bf485c4bfe
2 changed files with 59 additions and 14 deletions

View File

@@ -37,6 +37,9 @@
* @property string $rich_editing
* @property string $syntax_highlighting
* @property string $use_ssl
* @property array<string, bool> $caps
* @property string[] $roles
* @property array<string, bool> $allcaps
*/
#[AllowDynamicProperties]
class WP_User {
@@ -60,10 +63,10 @@ class WP_User {
* Capabilities that the individual user has been granted outside of those inherited from their role.
*
* @since 2.0.0
* @var bool[] Array of key/value pairs where keys represent a capability name
* and boolean values represent whether the user has that capability.
* @var array<string, bool>|null Array of key/value pairs where keys represent a capability name
* and boolean values represent whether the user has that capability.
*/
public $caps = array();
protected $caps = null;
/**
* User metadata option name.
@@ -79,16 +82,16 @@ class WP_User {
* @since 2.0.0
* @var string[]
*/
public $roles = array();
protected $roles = array();
/**
* All capabilities the user has, including individual and role based.
*
* @since 2.0.0
* @var bool[] Array of key/value pairs where keys represent a capability name
* and boolean values represent whether the user has that capability.
* @var array<string, bool> Array of key/value pairs where keys represent a capability name
* and boolean values represent whether the user has that capability.
*/
public $allcaps = array();
protected $allcaps = array();
/**
* The filter context applied to user data fields.
@@ -288,6 +291,10 @@ class WP_User {
$key = 'ID';
}
if ( in_array( $key, array( 'caps', 'allcaps', 'roles' ), true ) ) {
return true;
}
if ( isset( $this->data->$key ) ) {
return true;
}
@@ -321,6 +328,11 @@ class WP_User {
return $this->ID;
}
if ( in_array( $key, array( 'caps', 'allcaps', 'roles' ), true ) ) {
$this->load_capability_data();
return $this->$key;
}
if ( isset( $this->data->$key ) ) {
$value = $this->data->$key;
} else {
@@ -363,6 +375,13 @@ class WP_User {
return;
}
// Ensure capability data is loaded before setting related properties.
if ( in_array( $key, array( 'caps', 'allcaps', 'roles' ), true ) ) {
$this->load_capability_data();
$this->$key = $value;
return;
}
$this->data->$key = $value;
}
@@ -386,6 +405,10 @@ class WP_User {
);
}
if ( in_array( $key, array( 'caps', 'allcaps', 'roles' ), true ) ) {
$this->$key = null;
}
if ( isset( $this->data->$key ) ) {
unset( $this->data->$key );
}
@@ -515,6 +538,11 @@ class WP_User {
$wp_roles = wp_roles();
// Edge case: In case someone calls this method before lazy initialization, we need to initialize on demand.
if ( ! isset( $this->caps ) ) {
$this->caps = $this->get_caps_data();
}
// Filter out caps that are not role names and assign to $this->roles.
if ( is_array( $this->caps ) ) {
$this->roles = array_filter( array_keys( $this->caps ), array( $wp_roles, 'is_role' ) );
@@ -548,6 +576,7 @@ class WP_User {
if ( empty( $role ) ) {
return;
}
$this->load_capability_data();
if ( in_array( $role, $this->roles, true ) ) {
return;
@@ -577,6 +606,7 @@ class WP_User {
* @param string $role Role name.
*/
public function remove_role( $role ) {
$this->load_capability_data();
if ( ! in_array( $role, $this->roles, true ) ) {
return;
}
@@ -609,6 +639,7 @@ class WP_User {
* @param string $role Role name.
*/
public function set_role( $role ) {
$this->load_capability_data();
if ( 1 === count( $this->roles ) && current( $this->roles ) === $role ) {
return;
}
@@ -710,6 +741,7 @@ class WP_User {
* @param bool $grant Whether to grant capability to user.
*/
public function add_cap( $cap, $grant = true ) {
$this->load_capability_data();
$this->caps[ $cap ] = $grant;
update_user_meta( $this->ID, $this->cap_key, $this->caps );
$this->get_role_caps();
@@ -724,6 +756,7 @@ class WP_User {
* @param string $cap Capability name.
*/
public function remove_cap( $cap ) {
$this->load_capability_data();
if ( ! isset( $this->caps[ $cap ] ) ) {
return;
}
@@ -742,10 +775,10 @@ class WP_User {
*/
public function remove_all_caps() {
global $wpdb;
$this->caps = array();
$this->caps = null;
delete_user_meta( $this->ID, $this->cap_key );
delete_user_meta( $this->ID, $wpdb->get_blog_prefix() . 'user_level' );
$this->get_role_caps();
$this->load_capability_data();
}
/**
@@ -776,6 +809,8 @@ class WP_User {
* the given capability for that object.
*/
public function has_cap( $cap, ...$args ) {
$this->load_capability_data();
if ( is_numeric( $cap ) ) {
_deprecated_argument( __FUNCTION__, '2.0.0', __( 'Usage of user levels is deprecated. Use capabilities instead.' ) );
$cap = $this->translate_level_to_cap( $cap );
@@ -877,10 +912,7 @@ class WP_User {
}
$this->cap_key = $wpdb->get_blog_prefix( $this->site_id ) . 'capabilities';
$this->caps = $this->get_caps_data();
$this->get_role_caps();
$this->caps = null;
}
/**
@@ -911,4 +943,17 @@ class WP_User {
return $caps;
}
/**
* Loads capability data if it has not been loaded yet.
*
* @since 6.9.0
*/
private function load_capability_data() {
if ( isset( $this->caps ) ) {
return;
}
$this->caps = $this->get_caps_data();
$this->get_role_caps();
}
}

View File

@@ -16,7 +16,7 @@
*
* @global string $wp_version
*/
$wp_version = '6.9-alpha-60914';
$wp_version = '6.9-alpha-60915';
/**
* Holds the WordPress DB revision, increments when changes are made to the WordPress DB schema.