Index: src/wp-includes/class-wp-user.php
===================================================================
--- src/wp-includes/class-wp-user.php	(revision 41212)
+++ src/wp-includes/class-wp-user.php	(working copy)
@@ -91,6 +91,14 @@
 	public $filter = null;
 
 	/**
+	 * The site ID the capabilities of this user are initialized for.
+	 *
+	 * @since 4.9.0
+	 * @var int
+	 */
+	private $blog_id = 0;
+
+	/**
 	 * @static
 	 * @since 3.3.0
 	 * @var array
@@ -239,22 +247,6 @@
 	}
 
 	/**
-	 * Makes private/protected methods readable for backward compatibility.
-	 *
-	 * @since 4.3.0
-	 *
-	 * @param callable $name      Method to call.
-	 * @param array    $arguments Arguments to pass when calling.
-	 * @return mixed|false Return value of the callback, false otherwise.
-	 */
-	public function __call( $name, $arguments ) {
-		if ( '_init_caps' === $name ) {
-			return call_user_func_array( array( $this, $name ), $arguments );
-		}
-		return false;
-	}
-
-	/**
 	 * Magic method for checking the existence of a certain custom field.
 	 *
 	 * @since 3.3.0
@@ -431,6 +423,7 @@
 	 * used.
 	 *
 	 * @since 2.1.0
+	 * @deprecated 4.9.0 Use WP_User::for_blog()
 	 *
 	 * @global wpdb $wpdb WordPress database abstraction object.
 	 *
@@ -439,15 +432,15 @@
 	protected function _init_caps( $cap_key = '' ) {
 		global $wpdb;
 
-		if ( empty($cap_key) )
-			$this->cap_key = $wpdb->get_blog_prefix() . 'capabilities';
-		else
-			$this->cap_key = $cap_key;
+		_deprecated_function( __METHOD__, '4.9.0', 'WP_User::for_blog()' );
 
-		$this->caps = get_user_meta( $this->ID, $this->cap_key, true );
+		if ( empty( $cap_key ) ) {
+			$this->cap_key = $wpdb->get_blog_prefix( $this->blog_id ) . 'capabilities';
+		} else {
+			$this->cap_key = $cap_key;
+		}
 
-		if ( ! is_array( $this->caps ) )
-			$this->caps = array();
+		$this->caps = $this->get_caps_data();
 
 		$this->get_role_caps();
 	}
@@ -465,6 +458,13 @@
 	 * @return array List of all capabilities for the user.
 	 */
 	public function get_role_caps() {
+		$switch_site = false;
+		if ( is_multisite() && $this->blog_id != get_current_blog_id() ) {
+			$switch_site = true;
+
+			switch_to_blog( $this->blog_id );
+		}
+
 		$wp_roles = wp_roles();
 
 		//Filter out caps that are not role names and assign to $this->roles
@@ -479,6 +479,10 @@
 		}
 		$this->allcaps = array_merge( (array) $this->allcaps, (array) $this->caps );
 
+		if ( $switch_site ) {
+			restore_current_blog();
+		}
+
 		return $this->allcaps;
 	}
 
@@ -760,10 +764,34 @@
 	 */
 	public function for_blog( $blog_id = '' ) {
 		global $wpdb;
-		if ( ! empty( $blog_id ) )
-			$cap_key = $wpdb->get_blog_prefix( $blog_id ) . 'capabilities';
-		else
-			$cap_key = '';
-		$this->_init_caps( $cap_key );
+
+		if ( ! empty( $blog_id ) ) {
+			$this->blog_id = absint( $blog_id );
+		} else {
+			$this->blog_id = get_current_blog_id();
+		}
+
+		$this->cap_key = $wpdb->get_blog_prefix( $this->blog_id ) . 'capabilities';
+
+		$this->caps = $this->get_caps_data();
+
+		$this->get_role_caps();
+	}
+
+	/**
+	 * Gets the available user capabilities data.
+	 *
+	 * @since 4.9.0
+	 *
+	 * @return array User capabilities array.
+	 */
+	private function get_caps_data() {
+		$caps = get_user_meta( $this->ID, $this->cap_key, true );
+
+		if ( ! is_array( $caps ) ) {
+			return array();
+		}
+
+		return $caps;
 	}
 }
Index: tests/phpunit/tests/user/capabilities.php
===================================================================
--- tests/phpunit/tests/user/capabilities.php	(revision 41212)
+++ tests/phpunit/tests/user/capabilities.php	(working copy)
@@ -1840,4 +1840,28 @@
 		$this->assertFalse( user_can( self::$users['contributor']->ID,   'remove_user', self::$users['contributor']->ID ) );
 		$this->assertFalse( user_can( self::$users['subscriber']->ID,    'remove_user', self::$users['subscriber']->ID ) );
 	}
+
+	/**
+	 * @ticket 36961
+	 * @group ms-required
+	 */
+	function test_init_user_caps_for_different_site() {
+		global $wpdb;
+
+		$site_id = self::factory()->blog->create( array( 'user_id' => self::$users['administrator']->ID ) );
+
+		switch_to_blog( $site_id );
+
+		$role_name = 'uploader';
+		add_role( $role_name, 'Uploader', array(
+			'read'         => true,
+			'upload_files' => true,
+		) );
+		add_user_to_blog( $site_id, self::$users['subscriber']->ID, $role_name );
+
+		restore_current_blog();
+
+		$user = new WP_User( self::$users['subscriber']->ID, '', $site_id );
+		$this->assertTrue( $user->has_cap( 'upload_files' ) );
+	}
 }
Index: tests/phpunit/tests/user.php
===================================================================
--- tests/phpunit/tests/user.php	(revision 41212)
+++ tests/phpunit/tests/user.php	(working copy)
@@ -180,7 +180,7 @@
 		$this->assertEquals( 'foo', $user->$key );
 		$this->assertEquals( 'foo', $user->data->$key );  // This will fail with WP < 3.3
 
-		foreach ( (array) $user as $key => $value ) {
+		foreach ( get_object_vars( $user ) as $key => $value ) {
 			$this->assertEquals( $value, $user->$key );
 		}
 	}
