Make WordPress Core

source: tags/6.0/src/wp-admin/includes/class-core-upgrader.php

Last change on this file was 53132, checked in by audrasjb, 8 months ago

Administration: Replace "can not" with "cannot" after [53131].

Follow-up to [53131], [52979].

See #46057, #38913

  • Property svn:eol-style set to native
File size: 14.6 KB
Line 
1<?php
2/**
3 * Upgrade API: Core_Upgrader class
4 *
5 * @package WordPress
6 * @subpackage Upgrader
7 * @since 4.6.0
8 */
9
10/**
11 * Core class used for updating core.
12 *
13 * It allows for WordPress to upgrade itself in combination with
14 * the wp-admin/includes/update-core.php file.
15 *
16 * @since 2.8.0
17 * @since 4.6.0 Moved to its own file from wp-admin/includes/class-wp-upgrader.php.
18 *
19 * @see WP_Upgrader
20 */
21class Core_Upgrader extends WP_Upgrader {
22
23        /**
24         * Initialize the upgrade strings.
25         *
26         * @since 2.8.0
27         */
28        public function upgrade_strings() {
29                $this->strings['up_to_date'] = __( 'WordPress is at the latest version.' );
30                $this->strings['locked']     = __( 'Another update is currently in progress.' );
31                $this->strings['no_package'] = __( 'Update package not available.' );
32                /* translators: %s: Package URL. */
33                $this->strings['downloading_package']   = sprintf( __( 'Downloading update from %s&#8230;' ), '<span class="code">%s</span>' );
34                $this->strings['unpack_package']        = __( 'Unpacking the update&#8230;' );
35                $this->strings['copy_failed']           = __( 'Could not copy files.' );
36                $this->strings['copy_failed_space']     = __( 'Could not copy files. You may have run out of disk space.' );
37                $this->strings['start_rollback']        = __( 'Attempting to roll back to previous version.' );
38                $this->strings['rollback_was_required'] = __( 'Due to an error during updating, WordPress has rolled back to your previous version.' );
39        }
40
41        /**
42         * Upgrade WordPress core.
43         *
44         * @since 2.8.0
45         *
46         * @global WP_Filesystem_Base $wp_filesystem                WordPress filesystem subclass.
47         * @global callable           $_wp_filesystem_direct_method
48         *
49         * @param object $current Response object for whether WordPress is current.
50         * @param array  $args {
51         *     Optional. Arguments for upgrading WordPress core. Default empty array.
52         *
53         *     @type bool $pre_check_md5    Whether to check the file checksums before
54         *                                  attempting the upgrade. Default true.
55         *     @type bool $attempt_rollback Whether to attempt to rollback the chances if
56         *                                  there is a problem. Default false.
57         *     @type bool $do_rollback      Whether to perform this "upgrade" as a rollback.
58         *                                  Default false.
59         * }
60         * @return string|false|WP_Error New WordPress version on success, false or WP_Error on failure.
61         */
62        public function upgrade( $current, $args = array() ) {
63                global $wp_filesystem;
64
65                require ABSPATH . WPINC . '/version.php'; // $wp_version;
66
67                $start_time = time();
68
69                $defaults    = array(
70                        'pre_check_md5'                => true,
71                        'attempt_rollback'             => false,
72                        'do_rollback'                  => false,
73                        'allow_relaxed_file_ownership' => false,
74                );
75                $parsed_args = wp_parse_args( $args, $defaults );
76
77                $this->init();
78                $this->upgrade_strings();
79
80                // Is an update available?
81                if ( ! isset( $current->response ) || 'latest' === $current->response ) {
82                        return new WP_Error( 'up_to_date', $this->strings['up_to_date'] );
83                }
84
85                $res = $this->fs_connect( array( ABSPATH, WP_CONTENT_DIR ), $parsed_args['allow_relaxed_file_ownership'] );
86                if ( ! $res || is_wp_error( $res ) ) {
87                        return $res;
88                }
89
90                $wp_dir = trailingslashit( $wp_filesystem->abspath() );
91
92                $partial = true;
93                if ( $parsed_args['do_rollback'] ) {
94                        $partial = false;
95                } elseif ( $parsed_args['pre_check_md5'] && ! $this->check_files() ) {
96                        $partial = false;
97                }
98
99                /*
100                 * If partial update is returned from the API, use that, unless we're doing
101                 * a reinstallation. If we cross the new_bundled version number, then use
102                 * the new_bundled zip. Don't though if the constant is set to skip bundled items.
103                 * If the API returns a no_content zip, go with it. Finally, default to the full zip.
104                 */
105                if ( $parsed_args['do_rollback'] && $current->packages->rollback ) {
106                        $to_download = 'rollback';
107                } elseif ( $current->packages->partial && 'reinstall' !== $current->response && $wp_version === $current->partial_version && $partial ) {
108                        $to_download = 'partial';
109                } elseif ( $current->packages->new_bundled && version_compare( $wp_version, $current->new_bundled, '<' )
110                        && ( ! defined( 'CORE_UPGRADE_SKIP_NEW_BUNDLED' ) || ! CORE_UPGRADE_SKIP_NEW_BUNDLED ) ) {
111                        $to_download = 'new_bundled';
112                } elseif ( $current->packages->no_content ) {
113                        $to_download = 'no_content';
114                } else {
115                        $to_download = 'full';
116                }
117
118                // Lock to prevent multiple Core Updates occurring.
119                $lock = WP_Upgrader::create_lock( 'core_updater', 15 * MINUTE_IN_SECONDS );
120                if ( ! $lock ) {
121                        return new WP_Error( 'locked', $this->strings['locked'] );
122                }
123
124                $download = $this->download_package( $current->packages->$to_download, true );
125
126                // Allow for signature soft-fail.
127                // WARNING: This may be removed in the future.
128                if ( is_wp_error( $download ) && $download->get_error_data( 'softfail-filename' ) ) {
129                        // Output the failure error as a normal feedback, and not as an error:
130                        /** This filter is documented in wp-admin/includes/update-core.php */
131                        apply_filters( 'update_feedback', $download->get_error_message() );
132
133                        // Report this failure back to WordPress.org for debugging purposes.
134                        wp_version_check(
135                                array(
136                                        'signature_failure_code' => $download->get_error_code(),
137                                        'signature_failure_data' => $download->get_error_data(),
138                                )
139                        );
140
141                        // Pretend this error didn't happen.
142                        $download = $download->get_error_data( 'softfail-filename' );
143                }
144
145                if ( is_wp_error( $download ) ) {
146                        WP_Upgrader::release_lock( 'core_updater' );
147                        return $download;
148                }
149
150                $working_dir = $this->unpack_package( $download );
151                if ( is_wp_error( $working_dir ) ) {
152                        WP_Upgrader::release_lock( 'core_updater' );
153                        return $working_dir;
154                }
155
156                // Copy update-core.php from the new version into place.
157                if ( ! $wp_filesystem->copy( $working_dir . '/wordpress/wp-admin/includes/update-core.php', $wp_dir . 'wp-admin/includes/update-core.php', true ) ) {
158                        $wp_filesystem->delete( $working_dir, true );
159                        WP_Upgrader::release_lock( 'core_updater' );
160                        return new WP_Error( 'copy_failed_for_update_core_file', __( 'The update cannot be installed because some files could not be copied. This is usually due to inconsistent file permissions.' ), 'wp-admin/includes/update-core.php' );
161                }
162                $wp_filesystem->chmod( $wp_dir . 'wp-admin/includes/update-core.php', FS_CHMOD_FILE );
163
164                wp_opcache_invalidate( ABSPATH . 'wp-admin/includes/update-core.php' );
165                require_once ABSPATH . 'wp-admin/includes/update-core.php';
166
167                if ( ! function_exists( 'update_core' ) ) {
168                        WP_Upgrader::release_lock( 'core_updater' );
169                        return new WP_Error( 'copy_failed_space', $this->strings['copy_failed_space'] );
170                }
171
172                $result = update_core( $working_dir, $wp_dir );
173
174                // In the event of an issue, we may be able to roll back.
175                if ( $parsed_args['attempt_rollback'] && $current->packages->rollback && ! $parsed_args['do_rollback'] ) {
176                        $try_rollback = false;
177                        if ( is_wp_error( $result ) ) {
178                                $error_code = $result->get_error_code();
179                                /*
180                                 * Not all errors are equal. These codes are critical: copy_failed__copy_dir,
181                                 * mkdir_failed__copy_dir, copy_failed__copy_dir_retry, and disk_full.
182                                 * do_rollback allows for update_core() to trigger a rollback if needed.
183                                 */
184                                if ( false !== strpos( $error_code, 'do_rollback' ) ) {
185                                        $try_rollback = true;
186                                } elseif ( false !== strpos( $error_code, '__copy_dir' ) ) {
187                                        $try_rollback = true;
188                                } elseif ( 'disk_full' === $error_code ) {
189                                        $try_rollback = true;
190                                }
191                        }
192
193                        if ( $try_rollback ) {
194                                /** This filter is documented in wp-admin/includes/update-core.php */
195                                apply_filters( 'update_feedback', $result );
196
197                                /** This filter is documented in wp-admin/includes/update-core.php */
198                                apply_filters( 'update_feedback', $this->strings['start_rollback'] );
199
200                                $rollback_result = $this->upgrade( $current, array_merge( $parsed_args, array( 'do_rollback' => true ) ) );
201
202                                $original_result = $result;
203                                $result          = new WP_Error(
204                                        'rollback_was_required',
205                                        $this->strings['rollback_was_required'],
206                                        (object) array(
207                                                'update'   => $original_result,
208                                                'rollback' => $rollback_result,
209                                        )
210                                );
211                        }
212                }
213
214                /** This action is documented in wp-admin/includes/class-wp-upgrader.php */
215                do_action(
216                        'upgrader_process_complete',
217                        $this,
218                        array(
219                                'action' => 'update',
220                                'type'   => 'core',
221                        )
222                );
223
224                // Clear the current updates.
225                delete_site_transient( 'update_core' );
226
227                if ( ! $parsed_args['do_rollback'] ) {
228                        $stats = array(
229                                'update_type'      => $current->response,
230                                'success'          => true,
231                                'fs_method'        => $wp_filesystem->method,
232                                'fs_method_forced' => defined( 'FS_METHOD' ) || has_filter( 'filesystem_method' ),
233                                'fs_method_direct' => ! empty( $GLOBALS['_wp_filesystem_direct_method'] ) ? $GLOBALS['_wp_filesystem_direct_method'] : '',
234                                'time_taken'       => time() - $start_time,
235                                'reported'         => $wp_version,
236                                'attempted'        => $current->version,
237                        );
238
239                        if ( is_wp_error( $result ) ) {
240                                $stats['success'] = false;
241                                // Did a rollback occur?
242                                if ( ! empty( $try_rollback ) ) {
243                                        $stats['error_code'] = $original_result->get_error_code();
244                                        $stats['error_data'] = $original_result->get_error_data();
245                                        // Was the rollback successful? If not, collect its error too.
246                                        $stats['rollback'] = ! is_wp_error( $rollback_result );
247                                        if ( is_wp_error( $rollback_result ) ) {
248                                                $stats['rollback_code'] = $rollback_result->get_error_code();
249                                                $stats['rollback_data'] = $rollback_result->get_error_data();
250                                        }
251                                } else {
252                                        $stats['error_code'] = $result->get_error_code();
253                                        $stats['error_data'] = $result->get_error_data();
254                                }
255                        }
256
257                        wp_version_check( $stats );
258                }
259
260                WP_Upgrader::release_lock( 'core_updater' );
261
262                return $result;
263        }
264
265        /**
266         * Determines if this WordPress Core version should update to an offered version or not.
267         *
268         * @since 3.7.0
269         *
270         * @param string $offered_ver The offered version, of the format x.y.z.
271         * @return bool True if we should update to the offered version, otherwise false.
272         */
273        public static function should_update_to_version( $offered_ver ) {
274                require ABSPATH . WPINC . '/version.php'; // $wp_version; // x.y.z
275
276                $current_branch = implode( '.', array_slice( preg_split( '/[.-]/', $wp_version ), 0, 2 ) ); // x.y
277                $new_branch     = implode( '.', array_slice( preg_split( '/[.-]/', $offered_ver ), 0, 2 ) ); // x.y
278
279                $current_is_development_version = (bool) strpos( $wp_version, '-' );
280
281                // Defaults:
282                $upgrade_dev   = get_site_option( 'auto_update_core_dev', 'enabled' ) === 'enabled';
283                $upgrade_minor = get_site_option( 'auto_update_core_minor', 'enabled' ) === 'enabled';
284                $upgrade_major = get_site_option( 'auto_update_core_major', 'unset' ) === 'enabled';
285
286                // WP_AUTO_UPDATE_CORE = true (all), 'beta', 'rc', 'development', 'branch-development', 'minor', false.
287                if ( defined( 'WP_AUTO_UPDATE_CORE' ) ) {
288                        if ( false === WP_AUTO_UPDATE_CORE ) {
289                                // Defaults to turned off, unless a filter allows it.
290                                $upgrade_dev   = false;
291                                $upgrade_minor = false;
292                                $upgrade_major = false;
293                        } elseif ( true === WP_AUTO_UPDATE_CORE
294                                || in_array( WP_AUTO_UPDATE_CORE, array( 'beta', 'rc', 'development', 'branch-development' ), true )
295                        ) {
296                                // ALL updates for core.
297                                $upgrade_dev   = true;
298                                $upgrade_minor = true;
299                                $upgrade_major = true;
300                        } elseif ( 'minor' === WP_AUTO_UPDATE_CORE ) {
301                                // Only minor updates for core.
302                                $upgrade_dev   = false;
303                                $upgrade_minor = true;
304                                $upgrade_major = false;
305                        }
306                }
307
308                // 1: If we're already on that version, not much point in updating?
309                if ( $offered_ver === $wp_version ) {
310                        return false;
311                }
312
313                // 2: If we're running a newer version, that's a nope.
314                if ( version_compare( $wp_version, $offered_ver, '>' ) ) {
315                        return false;
316                }
317
318                $failure_data = get_site_option( 'auto_core_update_failed' );
319                if ( $failure_data ) {
320                        // If this was a critical update failure, cannot update.
321                        if ( ! empty( $failure_data['critical'] ) ) {
322                                return false;
323                        }
324
325                        // Don't claim we can update on update-core.php if we have a non-critical failure logged.
326                        if ( $wp_version === $failure_data['current'] && false !== strpos( $offered_ver, '.1.next.minor' ) ) {
327                                return false;
328                        }
329
330                        /*
331                         * Cannot update if we're retrying the same A to B update that caused a non-critical failure.
332                         * Some non-critical failures do allow retries, like download_failed.
333                         * 3.7.1 => 3.7.2 resulted in files_not_writable, if we are still on 3.7.1 and still trying to update to 3.7.2.
334                         */
335                        if ( empty( $failure_data['retry'] ) && $wp_version === $failure_data['current'] && $offered_ver === $failure_data['attempted'] ) {
336                                return false;
337                        }
338                }
339
340                // 3: 3.7-alpha-25000 -> 3.7-alpha-25678 -> 3.7-beta1 -> 3.7-beta2.
341                if ( $current_is_development_version ) {
342
343                        /**
344                         * Filters whether to enable automatic core updates for development versions.
345                         *
346                         * @since 3.7.0
347                         *
348                         * @param bool $upgrade_dev Whether to enable automatic updates for
349                         *                          development versions.
350                         */
351                        if ( ! apply_filters( 'allow_dev_auto_core_updates', $upgrade_dev ) ) {
352                                return false;
353                        }
354                        // Else fall through to minor + major branches below.
355                }
356
357                // 4: Minor in-branch updates (3.7.0 -> 3.7.1 -> 3.7.2 -> 3.7.4).
358                if ( $current_branch === $new_branch ) {
359
360                        /**
361                         * Filters whether to enable minor automatic core updates.
362                         *
363                         * @since 3.7.0
364                         *
365                         * @param bool $upgrade_minor Whether to enable minor automatic core updates.
366                         */
367                        return apply_filters( 'allow_minor_auto_core_updates', $upgrade_minor );
368                }
369
370                // 5: Major version updates (3.7.0 -> 3.8.0 -> 3.9.1).
371                if ( version_compare( $new_branch, $current_branch, '>' ) ) {
372
373                        /**
374                         * Filters whether to enable major automatic core updates.
375                         *
376                         * @since 3.7.0
377                         *
378                         * @param bool $upgrade_major Whether to enable major automatic core updates.
379                         */
380                        return apply_filters( 'allow_major_auto_core_updates', $upgrade_major );
381                }
382
383                // If we're not sure, we don't want it.
384                return false;
385        }
386
387        /**
388         * Compare the disk file checksums against the expected checksums.
389         *
390         * @since 3.7.0
391         *
392         * @global string $wp_version       The WordPress version string.
393         * @global string $wp_local_package Locale code of the package.
394         *
395         * @return bool True if the checksums match, otherwise false.
396         */
397        public function check_files() {
398                global $wp_version, $wp_local_package;
399
400                $checksums = get_core_checksums( $wp_version, isset( $wp_local_package ) ? $wp_local_package : 'en_US' );
401
402                if ( ! is_array( $checksums ) ) {
403                        return false;
404                }
405
406                foreach ( $checksums as $file => $checksum ) {
407                        // Skip files which get updated.
408                        if ( 'wp-content' === substr( $file, 0, 10 ) ) {
409                                continue;
410                        }
411                        if ( ! file_exists( ABSPATH . $file ) || md5_file( ABSPATH . $file ) !== $checksum ) {
412                                return false;
413                        }
414                }
415
416                return true;
417        }
418}
Note: See TracBrowser for help on using the repository browser.