Make WordPress Core

Opened 10 years ago

Closed 10 years ago

#29692 closed defect (bug) (fixed)

Multisite create new site return fatal error

Reported by: kkalvaa's profile kkalvaa Owned by: jeremyfelt's profile 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:

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 10 years ago.
29692.2.diff (484 bytes) - added by jeremyfelt 10 years ago.

Download all attachments as: .zip

Change History (40)

#1 @jesin
10 years ago

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

#2 @SergeyBiryukov
10 years ago

  • Keywords reporter-feedback added

#3 @kkalvaa
10 years ago

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

#4 @SergeyBiryukov
10 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
10 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
10 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.


10 years ago

#8 @nacin
10 years ago

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

#9 follow-up: @jeremyfelt
10 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
10 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
Version 0, edited 10 years ago by kkalvaa (next)

#11 @jeremyfelt
10 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
10 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
10 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
10 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
10 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
10 years ago

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

#17 follow-up: @nacin
10 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
10 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 10 years ago by jesin (previous) (diff)

#19 @kkalvaa
10 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
10 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
10 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 10 years ago by SergeyBiryukov (previous) (diff)

@jeremyfelt
10 years ago

#22 follow-up: @jeremyfelt
10 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
10 years ago

Attachment 29692.diff​ added

Thanks, it worked for me.

#24 @SergeyBiryukov
10 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
10 years ago

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

#26 follow-up: @SergeyBiryukov
10 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
10 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.


10 years ago

#29 @mr.seejee
10 years ago

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

#30 @jeremyfelt
10 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
10 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
10 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
10 years ago

  • Resolution fixed deleted
  • Status changed from closed to reopened

Reopening for 4.0.1

#34 @nacin
10 years ago

  • Keywords fixed-major added

#35 @johnjamesjacoby
10 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
10 years ago

#36 @jeremyfelt
10 years ago

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

#37 @jeremyfelt
10 years ago

[30242] is good to merge with 4.0.

#38 @nacin
10 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.