diff --git a/src/wp-includes/class-wp-user.php b/src/wp-includes/class-wp-user.php
index a5312b664ba28..738fb7bdca94c 100644
--- a/src/wp-includes/class-wp-user.php
+++ b/src/wp-includes/class-wp-user.php
@@ -36,6 +36,9 @@
  * @property string $rich_editing
  * @property string $syntax_highlighting
  * @property string $use_ssl
+ * @property array $caps
+ * @property array $roles
+ * @property array $allcaps
  */
 #[AllowDynamicProperties]
 class WP_User {
@@ -62,7 +65,7 @@ class WP_User {
 	 * @var bool[] 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 = array();
 
 	/**
 	 * User metadata option name.
@@ -78,7 +81,7 @@ 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.
@@ -87,7 +90,7 @@ class WP_User {
 	 * @var 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.
@@ -105,6 +108,15 @@ class WP_User {
 	 */
 	private $site_id = 0;
 
+
+	/**
+	 * Flag for if capability is loaded.
+	 *
+	 * @since 6.8.0
+	 * @var bool
+	 */
+	private $loaded_caps = false;
+
 	/**
 	 * @since 3.3.0
 	 * @var array
@@ -287,6 +299,10 @@ public function __isset( $key ) {
 			$key = 'ID';
 		}
 
+		if ( in_array( $key, array( 'caps', 'allcaps', 'roles' ), true ) ) {
+			return isset( $this->$key );
+		}
+
 		if ( isset( $this->data->$key ) ) {
 			return true;
 		}
@@ -320,6 +336,11 @@ public function __get( $key ) {
 			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 {
@@ -514,6 +535,10 @@ public function get_role_caps() {
 
 		$wp_roles = wp_roles();
 
+		if ( ! $this->loaded_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' ) );
@@ -547,6 +572,7 @@ public function add_role( $role ) {
 		if ( empty( $role ) ) {
 			return;
 		}
+		$this->load_capability_data();
 
 		if ( in_array( $role, $this->roles, true ) ) {
 			return;
@@ -576,6 +602,7 @@ public function add_role( $role ) {
 	 * @param string $role Role name.
 	 */
 	public function remove_role( $role ) {
+		$this->load_capability_data();
 		if ( ! in_array( $role, $this->roles, true ) ) {
 			return;
 		}
@@ -608,6 +635,7 @@ public function remove_role( $role ) {
 	 * @param string $role Role name.
 	 */
 	public function set_role( $role ) {
+		$this->load_capability_data();
 		if ( 1 === count( $this->roles ) && current( $this->roles ) === $role ) {
 			return;
 		}
@@ -709,6 +737,7 @@ public function update_user_level_from_caps() {
 	 * @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();
@@ -723,6 +752,7 @@ public function add_cap( $cap, $grant = true ) {
 	 * @param string $cap Capability name.
 	 */
 	public function remove_cap( $cap ) {
+		$this->load_capability_data();
 		if ( ! isset( $this->caps[ $cap ] ) ) {
 			return;
 		}
@@ -741,10 +771,10 @@ public function remove_cap( $cap ) {
 	 */
 	public function remove_all_caps() {
 		global $wpdb;
-		$this->caps = array();
 		delete_user_meta( $this->ID, $this->cap_key );
 		delete_user_meta( $this->ID, $wpdb->get_blog_prefix() . 'user_level' );
-		$this->get_role_caps();
+		$this->loaded_caps = false;
+		$this->load_capability_data();
 	}
 
 	/**
@@ -775,6 +805,8 @@ public function remove_all_caps() {
 	 *              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 );
@@ -875,11 +907,8 @@ public function for_site( $site_id = '' ) {
 			$this->site_id = get_current_blog_id();
 		}
 
-		$this->cap_key = $wpdb->get_blog_prefix( $this->site_id ) . 'capabilities';
-
-		$this->caps = $this->get_caps_data();
-
-		$this->get_role_caps();
+		$this->cap_key     = $wpdb->get_blog_prefix( $this->site_id ) . 'capabilities';
+		$this->loaded_caps = false;
 	}
 
 	/**
@@ -910,4 +939,18 @@ private function get_caps_data() {
 
 		return $caps;
 	}
+
+	/**
+	 * Loads capability data if it has not been loaded yet.
+	 *
+	 * @since 6.8.0
+	 */
+	private function load_capability_data() {
+		if ( $this->loaded_caps ) {
+			return;
+		}
+		$this->caps = $this->get_caps_data();
+		$this->get_role_caps();
+		$this->loaded_caps = true;
+	}
 }
diff --git a/tests/phpunit/tests/user/capabilities.php b/tests/phpunit/tests/user/capabilities.php
index 65942dca27544..10dfe3a3c8ba5 100644
--- a/tests/phpunit/tests/user/capabilities.php
+++ b/tests/phpunit/tests/user/capabilities.php
@@ -1077,6 +1077,41 @@ public function test_role_remove_cap() {
 		$this->assertFalse( $wp_roles->is_role( $role_name ) );
 	}
 
+	/**
+	 * @ticket 58001
+	 */
+	public function test_get_role_caps() {
+		$id_1   = self::$users['contributor']->ID;
+		$user_1 = new WP_User( $id_1 );
+
+		$role_caps = $user_1->get_role_caps();
+		$this->assertIsArray( $role_caps, 'User role capabilities should be an array' );
+		$this->assertArrayHasKey( 'edit_posts', $role_caps, 'User role capabilities should contain the edit_posts capability' );
+	}
+
+	/**
+	 * @ticket 58001
+	 */
+	public function test_user_lazy_capabilities() {
+		$id_1   = self::$users['contributor']->ID;
+		$user_1 = new WP_User( $id_1 );
+
+		$this->assertTrue( isset( $user_1->roles ), 'User roles should be set' );
+		$this->assertTrue( isset( $user_1->allcaps ), 'User all capabilities should be set' );
+		$this->assertTrue( isset( $user_1->caps ), 'User capabilities should be set' );
+		$this->assertIsArray( $user_1->roles, 'User roles should be an array' );
+		$this->assertSame( array( 'contributor' ), $user_1->roles, 'User roles should match' );
+		$this->assertIsArray( $user_1->allcaps, 'User allcaps should be an array' );
+		$this->assertIsArray( $user_1->caps, 'User caps should be an array' );
+
+		$caps = $this->getAllCapsAndRoles();
+		foreach ( $caps as $cap => $roles ) {
+			if ( in_array( 'contributor', $roles, true ) ) {
+				$this->assertTrue( $user_1->has_cap( $cap ), "User should have the {$cap} capability" );
+			}
+		}
+	}
+
 	/**
 	 * Add an extra capability to a user.
 	 */
diff --git a/tests/phpunit/tests/user/multisite.php b/tests/phpunit/tests/user/multisite.php
index 08d08bbe75bb3..93c09be9c4b88 100644
--- a/tests/phpunit/tests/user/multisite.php
+++ b/tests/phpunit/tests/user/multisite.php
@@ -370,12 +370,13 @@ public function test_add_user_to_blog_subscriber() {
 
 			switch_to_blog( $site_id );
 			$user = get_user_by( 'id', $user_id );
+			$this->assertContains( 'subscriber', $user->roles, 'User should have subscriber role' );
 			restore_current_blog();
 
 			wp_delete_site( $site_id );
 			wpmu_delete_user( $user_id );
 
-			$this->assertContains( 'subscriber', $user->roles );
+			$this->assertContains( 'subscriber', $user->roles, 'User should still have subscriber role' );
 		}
 
 		/**
diff --git a/tests/phpunit/tests/user/query.php b/tests/phpunit/tests/user/query.php
index 887064b8ca8de..0c2317a400204 100644
--- a/tests/phpunit/tests/user/query.php
+++ b/tests/phpunit/tests/user/query.php
@@ -162,13 +162,18 @@ public function test_get_all_primed_users() {
 		$filter = new MockAction();
 		add_filter( 'update_user_metadata_cache', array( $filter, 'filter' ), 10, 2 );
 
-		new WP_User_Query(
+		$query = new WP_User_Query(
 			array(
 				'include' => self::$author_ids,
 				'fields'  => 'all',
 			)
 		);
 
+		$users = $query->get_results();
+		foreach ( $users as $user ) {
+			$user->roles;
+		}
+
 		$args      = $filter->get_args();
 		$last_args = end( $args );
 		$this->assertIsArray( $last_args[1] );