diff --git a/src/wp-includes/class-wp-roles.php b/src/wp-includes/class-wp-roles.php index 2cb9cdfe95296..c1ecb2824e7f1 100644 --- a/src/wp-includes/class-wp-roles.php +++ b/src/wp-includes/class-wp-roles.php @@ -307,7 +307,13 @@ public function init_roles() { $this->role_objects = array(); $this->role_names = array(); foreach ( array_keys( $this->roles ) as $role ) { - $this->role_objects[ $role ] = new WP_Role( $role, $this->roles[ $role ]['capabilities'] ); + $role_capabilities = $this->roles[ $role ]['capabilities'] ?? array(); + + if ( ! is_array( $role_capabilities ) ) { + $role_capabilities = array(); + } + + $this->role_objects[ $role ] = new WP_Role( $role, $role_capabilities ); $this->role_names[ $role ] = $this->roles[ $role ]['name']; } diff --git a/src/wp-includes/class-wp-user-query.php b/src/wp-includes/class-wp-user-query.php index 3815023924489..3fe7feb6a29fd 100644 --- a/src/wp-includes/class-wp-user-query.php +++ b/src/wp-includes/class-wp-user-query.php @@ -482,7 +482,10 @@ public function prepare_query( $query = array() ) { $caps_with_roles = array(); foreach ( $available_roles as $role => $role_data ) { - $role_caps = array_keys( array_filter( $role_data['capabilities'] ) ); + $role_capabilities = $role_data['capabilities'] ?? array(); + $role_caps = is_array( $role_capabilities ) + ? array_keys( array_filter( $role_capabilities ) ) + : array(); foreach ( $capabilities as $cap ) { if ( in_array( $cap, $role_caps, true ) ) { diff --git a/tests/phpunit/tests/user/query.php b/tests/phpunit/tests/user/query.php index 5978f4bf55e58..50e9ef4973413 100644 --- a/tests/phpunit/tests/user/query.php +++ b/tests/phpunit/tests/user/query.php @@ -1959,6 +1959,53 @@ public function test_capability__in_capability__not_in_combined() { } } + /** + * A role stored without a 'capabilities' key (for example, left behind by a + * deactivated plugin) should be skipped rather than causing a fatal error. + * + * @ticket 62600 + */ + public function test_capability_query_with_role_missing_capabilities_key() { + global $wp_roles; + + $wp_roles->add_role( 'role_missing_caps', 'Role Missing Caps' ); + + $roles = get_option( $wp_roles->role_key ); + unset( $roles['role_missing_caps']['capabilities'] ); + update_option( $wp_roles->role_key, $roles ); + + $wp_user_search = new WP_User_Query( array( 'capability' => 'read' ) ); + $users = $wp_user_search->get_results(); + + $this->assertNotEmpty( $users ); + + $wp_roles->remove_role( 'role_missing_caps' ); + } + + /** + * A role whose 'capabilities' value isn't an array should be skipped rather + * than causing a fatal error, the same as a missing key. + * + * @ticket 62600 + */ + public function test_capability_query_with_role_capabilities_not_an_array() { + global $wp_roles; + + $wp_roles->add_role( 'role_invalid_caps', 'Role Invalid Caps' ); + + $roles = get_option( $wp_roles->role_key ); + + $roles['role_invalid_caps']['capabilities'] = false; + update_option( $wp_roles->role_key, $roles ); + + $wp_user_search = new WP_User_Query( array( 'capability' => 'read' ) ); + $users = $wp_user_search->get_results(); + + $this->assertNotEmpty( $users ); + + $wp_roles->remove_role( 'role_invalid_caps' ); + } + /** * @ticket 16841 * @group ms-required