WordPress.org

Make WordPress Core

Opened 5 years ago

Closed 5 years ago

#29692 closed defect (bug) (fixed)

Multisite create new site return fatal error

Reported by: kkalvaa Owned by: jeremyfelt
Milestone: 4.0.1 Priority: normal
Severity: blocker Version: 4.0
Component: Networks and Sites Keywords: has-patch commit fixed-major
Focuses: administration, multisite Cc:
PR Number:

Description

After updating to 4.0 I'm having problems adding a new site to our multisite install.

WordPress ends up with a white page saying:
Fatal error: Call to protected method WP_Roles::init() from context " in [URL] on line 1382

Upon returning to the network sites page, the new site has been created, but without content and a non-functioning URL.

Attachments (2)

29692.diff (428 bytes) - added by jeremyfelt 5 years ago.
29692.2.diff (484 bytes) - added by jeremyfelt 5 years ago.

Download all attachments as: .zip

Change History (40)

#1 @jesin
5 years ago

Can you specify the file path mentioned in the [URL] portion.

#2 @SergeyBiryukov
5 years ago

  • Keywords reporter-feedback added

#3 @kkalvaa
5 years ago

../wp-includes/ms-functions.php

#4 @SergeyBiryukov
5 years ago

Could not reproduce on a clean multisite install.

Does it still happen with all plugins disabled and a default theme (Twenty Fourteen or Twenty Thirteen) activated?

#5 @kkalvaa
5 years ago

The site is currently running several live client sites, I'm currently unable to test without plugins and a default theme.

I could try to make a local duplicate and test to see if the bug is reproducible there.

The server's running PHP 5.2.6-1+lenny16 and MySQL 5.0.51a if that's relevant to know.

#6 @SergeyBiryukov
5 years ago

  • Milestone changed from Awaiting Review to 4.0.1

Introduced in [28503]. Moving for investigation.

This ticket was mentioned in IRC in #wordpress-dev by nacin. View the logs.


5 years ago

#8 @nacin
5 years ago

  • Owner set to jeremyfelt
  • Status changed from new to assigned

#9 follow-up: @jeremyfelt
5 years ago

I can't reproduce the error with PHP 5.5 or 5.2 on a clean install, so core doesn't seem to break on the protected WP_Roles::_init(). I could see a scenario where a sudden change to protected could confuse an existing script that is using install_blog(), but I haven't yet been able to reproduce it. When install_blog() is used by core to add a new site, it doesn't appear to care whether $wp_roles is even available.

That said, I'm mostly confused. :)

The use of $wp_roles->_init() to MU in general was first added via MU:543, this specific use was introduced in MU:564 along with some error suppressing in another function around $wp_roles->_init() that evolved over time. reinit() was not available at the time.

If we switch to the public $wp_roles->reinit(), we would not be making use of the existing $wp_user_roles global to set $wp_roles->roles. I don't know if that causes issues elsewhere. I can't immediately see how it could. This happens to be the only place left in core that uses $wp_roles->_init(). The others (also in ms-functions.php) were moved in [21485] to $wp_roles->reinit() due to their association with switch_to_blog().

@kkalvaa - Do you have any mu-plugins installed, any role management plugins, or any type of multisite "helper" plugin—for domain mapping, multiple networks, etc..,.? It would also be helpful to see the full stack trace if you're able to reproduce locally. Also - can you confirm that the error is WP_Roles::_init() not WP_Roles::init()?

The same issue is reported on this forum. I haven't been able to find any other reports.

I'm wondering if something else is controlling the site-new.php process.

#10 @kkalvaa
5 years ago

I'm afraid there was a typo in my initial report, it is indeed WP_Roles::_init()

Fatal error: Call to protected method WP_Roles::_init() from context '' in /var/www/vhosts/admoment.no/httpdocs/wp-includes/ms-functions.php on line 1382

I was unable to get the multisite running locally so I couldn't test there, I'm sorry. (I'm not a developer, but designer, therefore my technical knowledge is a bit limited).

More information:
WordPress 4.0 running multisite with nb_NO as main language, there's also language packs for da_DK, nn_NO and sv_SE installed. Considering that the other issue you linked to is on a French site it could perhaps have some correlation to international installs?

No mu-plugins

The following 3 plugins are network activated (all installed from worpdress.org):

  • Multisite Enhancements
  • WordPress MU Domain Mapping
  • WP Super Cache
Last edited 5 years ago by SergeyBiryukov (previous) (diff)

#11 @jeremyfelt
5 years ago

Thank you for those details, that definitely helps.

I did a fresh installation of the nb_NO 3.9.2 package, upgraded to 4.0, and was able to create a new site through the network dashboard without issue. I'll start adding plugins to see if I can trigger it.

@kkalvaa - Are you adding new sites through {site-url}/wp-admin/network/site-new.php or a different page?

#12 follow-up: @jeremyfelt
5 years ago

Grabbing at straws... The only way I can reproduce this is to remove the new __call() method. I don't think there is a way to disable magic methods.

Is there any chance wp-includes/capabilities.php was partially updated?

#13 in reply to: ↑ 12 @nacin
5 years ago

Replying to jeremyfelt:

Is there any chance wp-includes/capabilities.php was partially updated?

Updated with protected but not with __call? Most of the time when we see an incomplete copy, the file is truncated. I don't see this as possible, but my mind went down the same path. Also, the __call comes earlier in the file.

I have no explanation for this. kkalvaa, is this still reproducible? Or was it just something you saw in your logs? Can you look at your file to confirm there is a method __call defined at line 94, and protected function __init defined at line 110?

#14 in reply to: ↑ 9 @nacin
5 years ago

Replying to jeremyfelt:

When install_blog() is used by core to add a new site, it doesn't appear to care whether $wp_roles is even available.

It's initialized in wp-settings.php.

If we switch to the public $wp_roles->reinit(), we would not be making use of the existing $wp_user_roles global to set $wp_roles->roles. I don't know if that causes issues elsewhere. I can't immediately see how it could. This happens to be the only place left in core that uses $wp_roles->_init(). The others (also in ms-functions.php) were moved in [21485] to $wp_roles->reinit() due to their association with switch_to_blog().

$wp_user_roles is a global define, it's designed to override anything from the DB. reinit() simply reinitializes the object. However, I now know why _init() is used — because populate_roles() will stomp all over the role definitions and we want to re-init from $wp_user_roles. It'd probably be easier to unset( $wp_roles ) and re-construct it, but I'm not inclined to touch anything without digging in further.

@kkalvaa - Do you have any mu-plugins installed, any role management plugins, or any type of multisite "helper" plugin—for domain mapping, multiple networks, etc..,.? It would also be helpful to see the full stack trace if you're able to reproduce locally.

I imagine this would cause the problem:

class Grasping_At_Straws extends WP_Roles {
	function __call( $name, $arguments ) {}

	function adding_a_method( $but_why ) {}
}
$wp_roles = new Grasping_At_Straws;

Extending WP_Roles isn't entirely out of the question for a role management plugin (but why...) but actually defining a custom __call method would also be required, and only if it doesn't fall back to calling $name. In short I don't see this happening, but it's the only explanation I can think of it is happening consistently.

#15 @kkalvaa
5 years ago

I tried to overwrite wp-includes/capabilities.php from a fresh 4.0 download to see if it was partially updated, no luck. And yes, I'm trying to add new sites from {site-url}/wp-admin/network/site-new.php. The site is currently closed for other registration possibilities.

@nacin: The bug is still reproducible, the site is currently incapable of creating new sites.

Also, Jetpack used to be network activated, but I had to deactivate it from the network after a theme conflicted with Jetpack. Jetpack is now only active on sites using Jetpack.

#16 @jeremyfelt
5 years ago

@kkalvaa - Do you have any role management plugins installed on the network or in mu-plugins?

#17 follow-up: @nacin
5 years ago

Can you try running code like this?

add_action( 'admin_footer', function() {
    var_dump( get_class( $GLOBALS['wp_roles'] ) );
});

You can drop this into functions.php and look at your dashboard footer for some text.

#18 in reply to: ↑ 17 @jesin
5 years ago

I believe OP is using PHP 5.2.6, so anonymous functions won't work. Better try

add_action( 'admin_footer', 'debug_29692' );
function debug_29692() {
    var_dump( get_class( $GLOBALS['wp_roles'] ) );
}
Last edited 5 years ago by jesin (previous) (diff)

#19 @kkalvaa
5 years ago

I get the following result: string(8) "WP_Roles"

Some new information. When the WordPress returns the error it kind of creates a new site, but there's no way of accessing it, WordPress simply sends my to the main site. When I then delete these half created sites it seems to also delete the contents of the uploads folder for the main site.

#20 @tmoorewp
5 years ago

@kkalvaa could you network deactivate the Multisite Enhancements plugin, then try creating a new site? That plugin should not effect live sites.

#21 @garubi
5 years ago

I just had the same bug.

Some details:
WP 4.0
Server: OS Linux Software Apache Version 32Bit
System: PHP 5.2.4-2ubuntu5.10 Zend 2.2.0
Database: SQL 5.0.51a Build 5.0.51a-3ubuntu5.4 Charset utf8

I added a new site from http://[mysite]/wp-admin/network/site-new.php

Multisite configured with subdomains and Domain Mapping (WordPress MU Domain Mapping)

The new site is created but no user is added and I got the following message:

Fatal error: Call to protected method WP_Roles::_init() from context '' in /var/www/wp-includes/ms-functions.php on line 1382

If I chewck the new site's Edit page I can see the following issues (see iamge):
https://www.dropbox.com/s/grf99w700tlrie7/Modifica%20sito-%20ufficiotemporaneo-it.sosufficiarredati.it-%20%E2%80%B9%20Amministratore%20del%20network-%20Sosufficiarredati.it%20Blogs%20%E2%80%94%20WordPress%202014-10-03%2016-55-05.png?dl=0

Don't know if thiscan help to identify the problem but we've got a similar issue some days ago while activating JetPack on a subsite. Even then then error was realated to a protected method (see https://github.com/Automattic/jetpack/issues/1117).

If you need me doing other test etc I'll more then happy to help.

Stefano

Last edited 5 years ago by SergeyBiryukov (previous) (diff)

@jeremyfelt
5 years ago

#22 follow-up: @jeremyfelt
5 years ago

Thanks for that information, @garubi.

One report with 5.2.4, one with 5.2.6. No PHP version is specified in the other forum thread. When I tested on PHP 5.2 earlier, it was 5.2.17. I don't have a 5.2.4 or 5.2.6 VM at the moment—and slow internet for 2 more days. :)

The PHP 5.2.9 release notes specify - Changed __call() to be invoked on private/protected method access, similar to properties and __get(). via PHP bug 42937. It's somewhat confusing (to me) as to whether this applies here or only when those protected methods are defined in a sub class, but it seems like it could.

I've attached 29692.diff, which unsets $wp_roles and then starts over.

This seems to be the only place in core where we're using a protected _init(). install_blog() is also the only place in core trying to access it.

#23 in reply to: ↑ 22 @wouayo
5 years ago

Attachment 29692.diff​ added

Thanks, it worked for me.

#24 @SergeyBiryukov
5 years ago

  • Keywords has-patch commit added; reporter-feedback removed

Reproduced on a clean install with PHP 5.2.4. 29692.diff works.

#25 @garubi
5 years ago

Just applied the 29692.diff
Now everithing is ok: I can create new sites and they are ok.

#26 follow-up: @SergeyBiryukov
5 years ago

  • Severity changed from normal to blocker

Replying to kkalvaa:

When I then delete these half created sites it seems to also delete the contents of the uploads folder for the main site.

Confirmed. The sites have an empty upload_path, so wpmu_delete_blog() leaves the whole wp-content/uploads directory empty.

#27 @kkalvaa
5 years ago

I just tried the 29692.diff and everything works as it should.

This ticket was mentioned in IRC in #wordpress-dev by jeremyfelt. View the logs.


5 years ago

#29 @mr.seejee
5 years ago

Same problem im facing on local (127.0.0.1). I have PHP 5.2.8. Kindly help in it.

#30 @jeremyfelt
5 years ago

Thanks to an unrelated comment from @simonwheatley, I was reminded that unset() may not work (or be necessary) in this scenario. We can probably just setup the new WP_Roles() object.

#31 in reply to: ↑ 26 @SergeyBiryukov
5 years ago

Replying to SergeyBiryukov:

Replying to kkalvaa:

When I then delete these half created sites it seems to also delete the contents of the uploads folder for the main site.

Confirmed. The sites have an empty upload_path, so wpmu_delete_blog() leaves the whole wp-content/uploads directory empty.

Related: #30121

#32 @jeremyfelt
5 years ago

  • Resolution set to fixed
  • Status changed from assigned to closed

In 30242:

Create new $wp_roles object in install_blog() to avoid protected _init() call

Our call to $wp_roles->_init() relied on the __call() method in WP_Roles to handle the link to the protected method. This works back to PHP 5.2.9, when a bug was fixed allowing access to protected methods through this exact approach.

install_blog() needs a fresh $wp_roles object after populate_roles() resets everything in its path. We can create this new object from scratch, effectively doing the same thing with the call to _init() via the constructor.

Fixes #29692 for trunk.

#33 @jeremyfelt
5 years ago

  • Resolution fixed deleted
  • Status changed from closed to reopened

Reopening for 4.0.1

#34 @nacin
5 years ago

  • Keywords fixed-major added

#35 @johnjamesjacoby
5 years ago

#23016 is loosely related. If bbPress were network activated, we are stomping the global again with no way to modify it just-in-time.

@jeremyfelt
5 years ago

#36 @jeremyfelt
5 years ago

29692.2.diff applies cleanly against the 4.0 branch and is ready for merge.

#37 @jeremyfelt
5 years ago

[30242] is good to merge with 4.0.

#38 @nacin
5 years ago

  • Resolution set to fixed
  • Status changed from reopened to closed

In 30255:

Create new $wp_roles object in install_blog() to avoid protected _init() call

Merges [30242] to the 4.0 branch.

Our call to $wp_roles->_init() relied on the __call() method in WP_Roles to handle the link to the protected method. This works back to PHP 5.2.9, when a bug was fixed allowing access to protected methods through this exact approach.

install_blog() needs a fresh $wp_roles object after populate_roles() resets everything in its path. We can create this new object from scratch, effectively doing the same thing with the call to _init() via the constructor.

props jeremyfelt.
fixes #29692.

Note: See TracTickets for help on using tickets.