I client of mine recently upgraded a very basic WordPress install to version 5.6.1. While the update seemed to go well, right at the very end the site died with an error that read something like this; identifying details altered for privacy:
[Sat Feb 06 12:12:11.123456 2021] [fcgid:warn] [pid 128] [client 12.34.56.78:12345] mod_fcgid: stderr: PHP Fatal error: Uncaught Error: Class 'WP_Site_Health' not found in /var/www/html/wp-includes/rest-api.php:321
I have fairly deep experience debugging WordPress and related PHP sites, but this was baffling. According to them they disabled all of the plug-ins and themes and even downloaded a clean archive of WordPress 5.6.1 and did the basic manual upgrade process of retaining the wp-config.php, .htaccess and related user specific uploads in the wp-content directory and the error still shows up.
I’ve read advice like what was posted here on the WordPress forums that basically state one should restore from a backup and try again, but find that advice to be a classic “Hail Mary” play; sometimes it works and other times you are just left in the same exact position as before but with new file modification dates.
So given that the class WP_Site_Health and the file wp-includes/rest-api.php are both items that exist in core WordPress, what can be done to quickly patch the system to get it working again?
P.S.: And to truly confirm the files were fine, I did the following:
- Created a Tar/Gzip archive of the whole WordPress install — including all
wp-contentitems — and downloaded it to my desktop. - Then I decompressed the archive, went into that directory and ran
git initinside of it to create a Git repo. - I left the site as-is as the
masterbranch and then created a new branch calledtest. - In that test branch, I manually downloaded a 100% WordPress 5.6.1 install from WordPress directly, manually copied the clean files into the new branch and committed them.
- Then I ran local
git diff master..testto see what files were changed between the sourcemasterbranch and thetestbranch. The diff said the files from the server versus clean WordPress 5.6.1 files were 100% the same; what was on the server is 100% the same as a clean WordPress install.
Answers:
Thank you for visiting the Q&A section on Magenaut. Please note that all the answers may not help you solve the issue immediately. So please treat them as advisements. If you found the post helpful (or not), leave a comment & I’ll get back to you as soon as possible.
Method 1
Have a look at what point in your code you’re making calls to the WordPress REST API or any other code which makes use of WP_Site_Health.
If you’re making calls in your <theme>/functions.php file, for example, it won’t work because functions.php is included before class WP_Site_Health in wp-settings.php.
See wp-settings.php code (WordPress 5.7.2):
// Load the functions for the active theme, for both parent and child theme if applicable.
foreach ( wp_get_active_and_valid_themes() as $theme ) {
if ( file_exists( $theme . '/functions.php' ) ) {
include $theme . '/functions.php';
}
}
unset( $theme );
/**
* Fires after the theme is loaded.
*
* @since 3.0.0
*/
do_action( 'after_setup_theme' );
// Create an instance of WP_Site_Health so that Cron events may fire.
if ( ! class_exists( 'WP_Site_Health' ) ) {
require_once ABSPATH . 'wp-admin/includes/class-wp-site-health.php';
}
WP_Site_Health::get_instance();
Despite the lack of elegance, one thing you can do about it is to ensure the class is loaded by copying part of the code from wp-settings.php and pasting it before your code. For example, in your <theme>/functions.php, you can do this:
if ( ! class_exists( 'WP_Site_Health' ) ) {
require_once ABSPATH . 'wp-admin/includes/class-wp-site-health.php';
}
WP_Site_Health::get_instance();
---8<---
code depending on WP_Site_Health
---8<---
Method 2
The solution is to add a PHP class_exists check around line 321 in wp-includes/rest-api.php.
While patching WordPress core like this makes me wince, the check I added just confirms if WP_Site_Health exists in the code WordPress loaded to render itself. Here is what exists around line 321 in core WordPress code for wp-includes/rest-api.php:
// Site Health. $site_health = WP_Site_Health::get_instance(); $controller = new WP_REST_Site_Health_Controller( $site_health ); $controller->register_routes();
Here is what I adjusted around there to get the site back up and running:
// Site Health.
if ( ! class_exists( 'WP_Site_Health' ) ) {
require_once ABSPATH . 'wp-admin/includes/class-wp-site-health.php';
}
$site_health = WP_Site_Health::get_instance();
$controller = new WP_REST_Site_Health_Controller( $site_health );
$controller->register_routes();
The logic is pretty simple: Using the PHP function class_exists, the code simply checks WP_Site_Health exists or not. If it doesn’t exist, then WordPress loads it using require_once and moves on. If it does exists? The condition fails that’s that.
It still is baffling why — if you look at wp-settings.php which bootstraps all of the classes when WordPress is loaded — rest-api.php loads around line 240, but makes a call to WP_Site_Health around line 539. Why would rest-api.php — which is loaded first — making a call to WP_Site_Health which only loads waaay later on in the require list?
Anyway, this is not pretty but it works and should be harmless to add to quickly get a site up and running again.
All methods was sourced from stackoverflow.com or stackexchange.com, is licensed under cc by-sa 2.5, cc by-sa 3.0 and cc by-sa 4.0