: str_replace(): Passing null to parameter #2 ($replace) of type array|string is deprecated in
&& $wp_filesystem->exists( $from . $root . 'wp-includes/version.php' )
$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 );
'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 = 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();
$php_update_message .= '</p><p><em>' . $annotation . '</em>';
if ( ! $mysql_compat && ! $php_compat ) {
'php_mysql_not_compatible',
/* 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.' ),
} elseif ( ! $php_compat ) {
/* 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.' ),
} elseif ( ! $mysql_compat ) {
/* 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.' ),
// Add a warning when the JSON PHP extension is missing.
if ( ! extension_loaded( 'json' ) ) {
'php_not_compatible_json',
/* 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.' ),
/** 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 ) ) {
if ( ! file_exists( ABSPATH . $file ) ) {
if ( ! file_exists( $working_dir_local . $file ) ) {
if ( '.' === dirname( $file )
&& in_array( pathinfo( $file, PATHINFO_EXTENSION ), array( 'html', 'txt' ), true )
if ( md5_file( ABSPATH . $file ) === $checksum ) {
$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 ) {
__( '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->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 /* overwrite */ ) ) {
$wp_filesystem->delete( $from, true );
'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' );
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 ) ) {
if ( ! file_exists( $working_dir_local . $file ) ) {
if ( '.' === dirname( $file )
&& in_array( pathinfo( $file, PATHINFO_EXTENSION ), array( 'html', 'txt' ), true )
if ( file_exists( ABSPATH . $file ) && md5_file( ABSPATH . $file ) === $checksum ) {
// Some files didn't copy properly.
if ( ! empty( $failed ) ) {
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.' ) );
$result = copy_dir( $from . $distro, $to, $skip );
if ( is_wp_error( $result ) ) {
$result->get_error_code() . '_retry',
$result->get_error_message(),
substr( $result->get_error_data(), strlen( $to ) )
* Custom content directory needs updating now.
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_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 );
$result = copy_dir( $from . $distro . 'wp-content/languages/', $wp_lang_dir );
if ( is_wp_error( $result ) ) {
$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 ) ) {
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() );
if ( ! $development_build && $wp_filesystem->exists( $dest . $filename ) ) {
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 );
if ( ! $development_build && $wp_filesystem->is_dir( $dest . $filename ) ) {
$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->get_error_code() . "_$type",
$_result->get_error_message(),
substr( $_result->get_error_data(), strlen( $dest ) )
// Handle $result error from the above blocks.
if ( is_wp_error( $result ) ) {
$wp_filesystem->delete( $from, true );
foreach ( $_old_files as $old_file ) {
$old_file = $to . $old_file;
if ( ! $wp_filesystem->exists( $old_file ) ) {
// 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.
// 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' );
delete_option( 'update_core' );