WordPress.org

Make WordPress Core

Opened 15 months ago

Last modified 8 months ago

#44857 new defect (bug)

get_object_subtype uses get_user_by before it's defined

Reported by: infostreams Owned by:
Milestone: Awaiting Review Priority: normal
Severity: normal Version: 4.9.8
Component: Users Keywords: reporter-feedback
Focuses: Cc:
PR Number:

Description

In 'get_object_subtype' on line 1308 in wp-includes/meta.php, the function 'get_user_by' is used. This function is defined in wp-includes/pluggable.php (line 87-109). However, 'pluggable.php' is not loaded until after the plugins have initialized (see wp-settings.php line 160). Therefore, the situation can occur that 'get_object_subtype' calls a function that isn't defined yet.

Concretely, this situation occurs in the WPML plugin, in combination with a multisite installation. However, there are many other situations where it could occur (anywhere in any plugin initialization code). If you add a new site, the WPML plugin will try to assign the network admin new capabilities by calling (WP_User)->add_cap for all of the network administrators. 'add_cap' calls 'update_user_meta', which calls 'get_object_subtype', which results in a fatal error, which then displays a HTTP 500 error whenever the user is trying to access the WordPress dashboard. Fairly dramatic, indeed.

For any googlers, you can resolve this issue by creating a file 'wpml-multisite-fix.php' in 'wp-content/mu-plugins', with the following content:

<?php

if ( ! function_exists( 'get_user_by' ) ) {
	/**
	 * Define the 'get_user_by' function here instead of in pluggable.php, because WP 4.9.8 has a bug -
	 * it uses 'get_user_by' before it's included (see meta.php line 1308, and see that it isn't
	 * included yet in wp-settings.php line 160).
	 *
	 * @param string     $field The field to retrieve the user with. id | ID | slug | email | login.
	 * @param int|string $value A value for $field. A user ID, slug, email address, or login name.
	 * @return WP_User|false WP_User object on success, false on failure.
	 */
	function get_user_by( $field, $value ) {
		$userdata = WP_User::get_data_by( $field, $value );

		if ( ! $userdata ) {
			return false;
		}

		$user = new WP_User();
		$user->init( $userdata );

		return $user;
	}
}

Change History (4)

#1 @mukesh27
15 months ago

  • Component changed from Bootstrap/Load to Users
  • Focuses performance added
  • Keywords dev-feedback added
  • Type changed from defect (bug) to enhancement

#2 @swissspidy
15 months ago

  • Focuses performance removed
  • Type changed from enhancement to defect (bug)

#3 @SergeyBiryukov
8 months ago

  • Keywords reporter-feedback added; dev-feedback removed

Hi @infostreams, welcome to WordPress Trac! Thanks for the report. Sorry it took a while for someone to get back to you.

Could you provide an example code to reproduce the issue? It sounds like it's caused by a plugin running some initialization code too early. Generally it's not recommended for plugins to run any code until the appropriate action fires, e.g. plugins_loaded or init.

#4 @infostreams
8 months ago

I'm sorry, that's a bit difficult because the WPML plugin is closed source and as such I cannot just include it here, neither can I provide a composer.json file to setup everything in such a way as to reproduce it.

Basically, reproducing it comes down to this:

  1. Install WordPress 4.9.8 - I'm going to assume it's still broken in the lastest WordPress, but it was originally reported for 4.9.8
  2. Convert it into a multisite
  3. Install the WPML plugin. You can probably get it from the WPML guys for free if you say that you're a WordPress core committer.
  4. Try to add a site
  5. Boom 💥
Last edited 8 months ago by infostreams (previous) (diff)
Note: See TracTickets for help on using tickets.