WordPress Version: 6.5
/**
* Upgrades the core of WordPress.
*
* This will create a .maintenance file at the base of the WordPress directory
* to ensure that people can not access the website, when the files are being
* copied to their locations.
*
* The files in the `$_old_files` list will be removed and the new files
* copied from the zip file after the database is upgraded.
*
* The files in the `$_new_bundled_files` list will be added to the installation
* if the version is greater than or equal to the old version being upgraded.
*
* The steps for the upgrader for after the new release is downloaded and
* unzipped is:
* 1. Test unzipped location for select files to ensure that unzipped worked.
* 2. Create the .maintenance file in current WordPress base.
* 3. Copy new WordPress directory over old WordPress files.
* 4. Upgrade WordPress to new version.
* 4.1. Copy all files/folders other than wp-content
* 4.2. Copy any language files to WP_LANG_DIR (which may differ from WP_CONTENT_DIR
* 4.3. Copy any new bundled themes/plugins to their respective locations
* 5. Delete new WordPress directory path.
* 6. Delete .maintenance file.
* 7. Remove old files.
* 8. Delete 'update_core' option.
*
* There are several areas of failure. For instance if PHP times out before step
* 6, then you will not be able to access any portion of your site. Also, since
* the upgrade will not continue where it left off, you will not be able to
* automatically remove old files and remove the 'update_core' option. This
* isn't that bad.
*
* If the copy of the new WordPress over the old fails, then the worse is that
* the new WordPress directory will remain.
*
* If it is assumed that every file will be copied over, including plugins and
* themes, then if you edit the default theme, you should rename it, so that
* your changes remain.
*
* @since 2.7.0
*
* @global WP_Filesystem_Base $wp_filesystem WordPress filesystem subclass.
* @global array $_old_files
* @global array $_old_requests_files
* @global array $_new_bundled_files
* @global wpdb $wpdb WordPress database abstraction object.
* @global string $wp_version
* @global string $required_php_version
* @global string $required_mysql_version
*
* @param string $from New release unzipped path.
* @param string $to Path to old WordPress installation.
* @return string|WP_Error New WordPress version on success, WP_Error on failure.
*/
function update_core($from, $to)
{
global $wp_filesystem, $_old_files, $_old_requests_files, $_new_bundled_files, $wpdb;
if (function_exists('set_time_limit')) {
set_time_limit(300);
}
/*
* Merge the old Requests files and directories into the `$_old_files`.
* Then preload these Requests files first, before the files are deleted
* and replaced to ensure the code is in memory if needed.
*/
$_old_files = array_merge($_old_files, array_values($_old_requests_files));
_preload_old_requests_classes_and_interfaces($to);
/**
* Filters feedback messages displayed during the core update process.
*
* The filter is first evaluated after the zip file for the latest version
* has been downloaded and unzipped. It is evaluated five more times during
* the process:
*
* 1. Before WordPress begins the core upgrade process.
* 2. Before Maintenance Mode is enabled.
* 3. Before WordPress begins copying over the necessary files.
* 4. Before Maintenance Mode is disabled.
* 5. Before the database is upgraded.
*
* @since 2.5.0
*
* @param string $feedback The core update feedback messages.
*/
apply_filters('update_feedback', __('Verifying the unpacked files…'));
// Confidence check the unzipped distribution.
$distro = '';
$roots = array('/wordpress/', '/wordpress-mu/');
foreach ($roots as $root) {
if ($wp_filesystem->exists($from . $root . 'readme.html') && $wp_filesystem->exists($from . $root . 'wp-includes/version.php')) {
$distro = $root;
break;
}
}
if (!$distro) {
$wp_filesystem->delete($from, true);
return new WP_Error('insane_distro', __('The update could not be unpacked'));
}
/*
* Import $wp_version, $required_php_version, and $required_mysql_version from the new version.
* DO NOT globalize any variables imported from `version-current.php` in this function.
*
* BC Note: $wp_filesystem->wp_content_dir() returned unslashed pre-2.8.
*/
$versions_file = trailingslashit($wp_filesystem->wp_content_dir()) . 'upgrade/version-current.php';
if (!$wp_filesystem->copy($from . $distro . 'wp-includes/version.php', $versions_file)) {
$wp_filesystem->delete($from, true);
return new WP_Error('copy_failed_for_version_file', __('The update cannot be installed because some files could not be copied. This is usually due to inconsistent file permissions.'), 'wp-includes/version.php');
}
$wp_filesystem->chmod($versions_file, FS_CHMOD_FILE);
/*
* `wp_opcache_invalidate()` only exists in WordPress 5.5 or later,
* so don't run it when upgrading from older versions.
*/
if (function_exists('wp_opcache_invalidate')) {
wp_opcache_invalidate($versions_file);
}
require WP_CONTENT_DIR . '/upgrade/version-current.php';
$wp_filesystem->delete($versions_file);
$php_version = PHP_VERSION;
$mysql_version = $wpdb->db_version();
$old_wp_version = $GLOBALS['wp_version'];
// The version of WordPress we're updating from.
/*
* Note: str_contains() is not used here, as this file is included
* when updating from older WordPress versions, in which case
* the polyfills from wp-includes/compat.php may not be available.
*/
$development_build = false !== strpos($old_wp_version . $wp_version, '-');
// A dash in the version indicates a development release.
$php_compat = version_compare($php_version, $required_php_version, '>=');
if (file_exists(WP_CONTENT_DIR . '/db.php') && empty($wpdb->is_mysql)) {
$mysql_compat = true;
} else {
$mysql_compat = version_compare($mysql_version, $required_mysql_version, '>=');
}
if (!$mysql_compat || !$php_compat) {
$wp_filesystem->delete($from, true);
}
$php_update_message = '';
if (function_exists('wp_get_update_php_url')) {
$php_update_message = '</p><p>' . sprintf(
/* translators: %s: URL to Update PHP page. */
__('<a href="%s">Learn more about updating PHP</a>.'),
esc_url(wp_get_update_php_url())
);
if (function_exists('wp_get_update_php_annotation')) {
$annotation = wp_get_update_php_annotation();
if ($annotation) {
$php_update_message .= '</p><p><em>' . $annotation . '</em>';
}
}
}
if (!$mysql_compat && !$php_compat) {
return new WP_Error('php_mysql_not_compatible', sprintf(
/* translators: 1: WordPress version number, 2: Minimum required PHP version number, 3: Minimum required MySQL version number, 4: Current PHP version number, 5: Current MySQL version number. */
__('The update cannot be installed because WordPress %1$s requires PHP version %2$s or higher and MySQL version %3$s or higher. You are running PHP version %4$s and MySQL version %5$s.'),
$wp_version,
$required_php_version,
$required_mysql_version,
$php_version,
$mysql_version
) . $php_update_message);
} elseif (!$php_compat) {
return new WP_Error('php_not_compatible', sprintf(
/* translators: 1: WordPress version number, 2: Minimum required PHP version number, 3: Current PHP version number. */
__('The update cannot be installed because WordPress %1$s requires PHP version %2$s or higher. You are running version %3$s.'),
$wp_version,
$required_php_version,
$php_version
) . $php_update_message);
} elseif (!$mysql_compat) {
return new WP_Error('mysql_not_compatible', sprintf(
/* translators: 1: WordPress version number, 2: Minimum required MySQL version number, 3: Current MySQL version number. */
__('The update cannot be installed because WordPress %1$s requires MySQL version %2$s or higher. You are running version %3$s.'),
$wp_version,
$required_mysql_version,
$mysql_version
));
}
// Add a warning when the JSON PHP extension is missing.
if (!extension_loaded('json')) {
return new WP_Error('php_not_compatible_json', sprintf(
/* translators: 1: WordPress version number, 2: The PHP extension name needed. */
__('The update cannot be installed because WordPress %1$s requires the %2$s PHP extension.'),
$wp_version,
'JSON'
));
}
/** This filter is documented in wp-admin/includes/update-core.php */
apply_filters('update_feedback', __('Preparing to install the latest version…'));
/*
* Don't copy wp-content, we'll deal with that below.
* We also copy version.php last so failed updates report their old version.
*/
$skip = array('wp-content', 'wp-includes/version.php');
$check_is_writable = array();
// Check to see which files don't really need updating - only available for 3.7 and higher.
if (function_exists('get_core_checksums')) {
// Find the local version of the working directory.
$working_dir_local = WP_CONTENT_DIR . '/upgrade/' . basename($from) . $distro;
$checksums = get_core_checksums($wp_version, isset($wp_local_package) ? $wp_local_package : 'en_US');
if (is_array($checksums) && isset($checksums[$wp_version])) {
$checksums = $checksums[$wp_version];
// Compat code for 3.7-beta2.
}
if (is_array($checksums)) {
foreach ($checksums as $file => $checksum) {
/*
* Note: str_starts_with() is not used here, as this file is included
* when updating from older WordPress versions, in which case
* the polyfills from wp-includes/compat.php may not be available.
*/
if ('wp-content' === substr($file, 0, 10)) {
continue;
}
if (!file_exists(ABSPATH . $file)) {
continue;
}
if (!file_exists($working_dir_local . $file)) {
continue;
}
if ('.' === dirname($file) && in_array(pathinfo($file, PATHINFO_EXTENSION), array('html', 'txt'), true)) {
continue;
}
if (md5_file(ABSPATH . $file) === $checksum) {
$skip[] = $file;
} else {
$check_is_writable[$file] = ABSPATH . $file;
}
}
}
}
// If we're using the direct method, we can predict write failures that are due to permissions.
if ($check_is_writable && 'direct' === $wp_filesystem->method) {
$files_writable = array_filter($check_is_writable, array($wp_filesystem, 'is_writable'));
if ($files_writable !== $check_is_writable) {
$files_not_writable = array_diff_key($check_is_writable, $files_writable);
foreach ($files_not_writable as $relative_file_not_writable => $file_not_writable) {
// If the writable check failed, chmod file to 0644 and try again, same as copy_dir().
$wp_filesystem->chmod($file_not_writable, FS_CHMOD_FILE);
if ($wp_filesystem->is_writable($file_not_writable)) {
unset($files_not_writable[$relative_file_not_writable]);
}
}
// Store package-relative paths (the key) of non-writable files in the WP_Error object.
$error_data = version_compare($old_wp_version, '3.7-beta2', '>') ? array_keys($files_not_writable) : '';
if ($files_not_writable) {
return new WP_Error('files_not_writable', __('The update cannot be installed because your site is unable to copy some files. This is usually due to inconsistent file permissions.'), implode(', ', $error_data));
}
}
}
/** This filter is documented in wp-admin/includes/update-core.php */
apply_filters('update_feedback', __('Enabling Maintenance mode…'));
// Create maintenance file to signal that we are upgrading.
$maintenance_string = '<?php $upgrading = ' . time() . '; ?>';
$maintenance_file = $to . '.maintenance';
$wp_filesystem->delete($maintenance_file);
$wp_filesystem->put_contents($maintenance_file, $maintenance_string, FS_CHMOD_FILE);
/** This filter is documented in wp-admin/includes/update-core.php */
apply_filters('update_feedback', __('Copying the required files…'));
// Copy new versions of WP files into place.
$result = copy_dir($from . $distro, $to, $skip);
if (is_wp_error($result)) {
$result = new WP_Error($result->get_error_code(), $result->get_error_message(), substr($result->get_error_data(), strlen($to)));
}
// Since we know the core files have copied over, we can now copy the version file.
if (!is_wp_error($result)) {
if (!$wp_filesystem->copy($from . $distro . 'wp-includes/version.php', $to . 'wp-includes/version.php', true)) {
$wp_filesystem->delete($from, true);
$result = new WP_Error('copy_failed_for_version_file', __('The update cannot be installed because your site is unable to copy some files. This is usually due to inconsistent file permissions.'), 'wp-includes/version.php');
}
$wp_filesystem->chmod($to . 'wp-includes/version.php', FS_CHMOD_FILE);
/*
* `wp_opcache_invalidate()` only exists in WordPress 5.5 or later,
* so don't run it when upgrading from older versions.
*/
if (function_exists('wp_opcache_invalidate')) {
wp_opcache_invalidate($to . 'wp-includes/version.php');
}
}
// Check to make sure everything copied correctly, ignoring the contents of wp-content.
$skip = array('wp-content');
$failed = array();
if (isset($checksums) && is_array($checksums)) {
foreach ($checksums as $file => $checksum) {
/*
* Note: str_starts_with() is not used here, as this file is included
* when updating from older WordPress versions, in which case
* the polyfills from wp-includes/compat.php may not be available.
*/
if ('wp-content' === substr($file, 0, 10)) {
continue;
}
if (!file_exists($working_dir_local . $file)) {
continue;
}
if ('.' === dirname($file) && in_array(pathinfo($file, PATHINFO_EXTENSION), array('html', 'txt'), true)) {
$skip[] = $file;
continue;
}
if (file_exists(ABSPATH . $file) && md5_file(ABSPATH . $file) === $checksum) {
$skip[] = $file;
} else {
$failed[] = $file;
}
}
}
// Some files didn't copy properly.
if (!empty($failed)) {
$total_size = 0;
foreach ($failed as $file) {
if (file_exists($working_dir_local . $file)) {
$total_size += filesize($working_dir_local . $file);
}
}
/*
* If we don't have enough free space, it isn't worth trying again.
* Unlikely to be hit due to the check in unzip_file().
*/
$available_space = function_exists('disk_free_space') ? @disk_free_space(ABSPATH) : false;
if ($available_space && $total_size >= $available_space) {
$result = new WP_Error('disk_full', __('There is not enough free disk space to complete the update.'));
} else {
$result = copy_dir($from . $distro, $to, $skip);
if (is_wp_error($result)) {
$result = new WP_Error($result->get_error_code() . '_retry', $result->get_error_message(), substr($result->get_error_data(), strlen($to)));
}
}
}
/*
* Custom content directory needs updating now.
* Copy languages.
*/
if (!is_wp_error($result) && $wp_filesystem->is_dir($from . $distro . 'wp-content/languages')) {
if (WP_LANG_DIR !== ABSPATH . WPINC . '/languages' || @is_dir(WP_LANG_DIR)) {
$lang_dir = WP_LANG_DIR;
} else {
$lang_dir = WP_CONTENT_DIR . '/languages';
}
/*
* Note: str_starts_with() is not used here, as this file is included
* when updating from older WordPress versions, in which case
* the polyfills from wp-includes/compat.php may not be available.
*/
// Check if the language directory exists first.
if (!@is_dir($lang_dir) && 0 === strpos($lang_dir, ABSPATH)) {
// If it's within the ABSPATH we can handle it here, otherwise they're out of luck.
$wp_filesystem->mkdir($to . str_replace(ABSPATH, '', $lang_dir), FS_CHMOD_DIR);
clearstatcache();
// For FTP, need to clear the stat cache.
}
if (@is_dir($lang_dir)) {
$wp_lang_dir = $wp_filesystem->find_folder($lang_dir);
if ($wp_lang_dir) {
$result = copy_dir($from . $distro . 'wp-content/languages/', $wp_lang_dir);
if (is_wp_error($result)) {
$result = new WP_Error($result->get_error_code() . '_languages', $result->get_error_message(), substr($result->get_error_data(), strlen($wp_lang_dir)));
}
}
}
}
/** This filter is documented in wp-admin/includes/update-core.php */
apply_filters('update_feedback', __('Disabling Maintenance mode…'));
// Remove maintenance file, we're done with potential site-breaking changes.
$wp_filesystem->delete($maintenance_file);
/*
* 3.5 -> 3.5+ - an empty twentytwelve directory was created upon upgrade to 3.5 for some users,
* preventing installation of Twenty Twelve.
*/
if ('3.5' === $old_wp_version) {
if (is_dir(WP_CONTENT_DIR . '/themes/twentytwelve') && !file_exists(WP_CONTENT_DIR . '/themes/twentytwelve/style.css')) {
$wp_filesystem->delete($wp_filesystem->wp_themes_dir() . 'twentytwelve/');
}
}
/*
* Copy new bundled plugins & themes.
* This gives us the ability to install new plugins & themes bundled with
* future versions of WordPress whilst avoiding the re-install upon upgrade issue.
* $development_build controls us overwriting bundled themes and plugins when a non-stable release is being updated.
*/
if (!is_wp_error($result) && (!defined('CORE_UPGRADE_SKIP_NEW_BUNDLED') || !CORE_UPGRADE_SKIP_NEW_BUNDLED)) {
foreach ((array) $_new_bundled_files as $file => $introduced_version) {
// If a $development_build or if $introduced version is greater than what the site was previously running.
if ($development_build || version_compare($introduced_version, $old_wp_version, '>')) {
$directory = '/' === $file[strlen($file) - 1];
list($type, $filename) = explode('/', $file, 2);
// Check to see if the bundled items exist before attempting to copy them.
if (!$wp_filesystem->exists($from . $distro . 'wp-content/' . $file)) {
continue;
}
if ('plugins' === $type) {
$dest = $wp_filesystem->wp_plugins_dir();
} elseif ('themes' === $type) {
// Back-compat, ::wp_themes_dir() did not return trailingslash'd pre-3.2.
$dest = trailingslashit($wp_filesystem->wp_themes_dir());
} else {
continue;
}
if (!$directory) {
if (!$development_build && $wp_filesystem->exists($dest . $filename)) {
continue;
}
if (!$wp_filesystem->copy($from . $distro . 'wp-content/' . $file, $dest . $filename, FS_CHMOD_FILE)) {
$result = new WP_Error("copy_failed_for_new_bundled_{$type}", __('Could not copy file.'), $dest . $filename);
}
} else {
if (!$development_build && $wp_filesystem->is_dir($dest . $filename)) {
continue;
}
$wp_filesystem->mkdir($dest . $filename, FS_CHMOD_DIR);
$_result = copy_dir($from . $distro . 'wp-content/' . $file, $dest . $filename);
/*
* If an error occurs partway through this final step,
* keep the error flowing through, but keep the process going.
*/
if (is_wp_error($_result)) {
if (!is_wp_error($result)) {
$result = new WP_Error();
}
$result->add($_result->get_error_code() . "_{$type}", $_result->get_error_message(), substr($_result->get_error_data(), strlen($dest)));
}
}
}
}
// End foreach.
}
// Handle $result error from the above blocks.
if (is_wp_error($result)) {
$wp_filesystem->delete($from, true);
return $result;
}
// Remove old files.
foreach ($_old_files as $old_file) {
$old_file = $to . $old_file;
if (!$wp_filesystem->exists($old_file)) {
continue;
}
// If the file isn't deleted, try writing an empty string to the file instead.
if (!$wp_filesystem->delete($old_file, true) && $wp_filesystem->is_file($old_file)) {
$wp_filesystem->put_contents($old_file, '');
}
}
// Remove any Genericons example.html's from the filesystem.
_upgrade_422_remove_genericons();
// Deactivate the REST API plugin if its version is 2.0 Beta 4 or lower.
_upgrade_440_force_deactivate_incompatible_plugins();
// Deactivate incompatible plugins.
_upgrade_core_deactivate_incompatible_plugins();
// Upgrade DB with separate request.
/** This filter is documented in wp-admin/includes/update-core.php */
apply_filters('update_feedback', __('Upgrading database…'));
$db_upgrade_url = admin_url('upgrade.php?step=upgrade_db');
wp_remote_post($db_upgrade_url, array('timeout' => 60));
// Clear the cache to prevent an update_option() from saving a stale db_version to the cache.
wp_cache_flush();
// Not all cache back ends listen to 'flush'.
wp_cache_delete('alloptions', 'options');
// Remove working directory.
$wp_filesystem->delete($from, true);
// Force refresh of update information.
if (function_exists('delete_site_transient')) {
delete_site_transient('update_core');
} else {
delete_option('update_core');
}
/**
* Fires after WordPress core has been successfully updated.
*
* @since 3.3.0
*
* @param string $wp_version The current WordPress version.
*/
do_action('_core_updated_successfully', $wp_version);
// Clear the option that blocks auto-updates after failures, now that we've been successful.
if (function_exists('delete_site_option')) {
delete_site_option('auto_core_update_failed');
}
return $wp_version;
}