WordPress.org

Make WordPress Core

Ticket #22704: background-upgrader.2.php

File background-upgrader.2.php, 11.3 KB (added by dd32, 4 years ago)

A copy of the latest mu-plugin i was using before integrating as the patch

Line 
1<?php
2
3class WP_Background_Upgrader {
4
5        static $skin;
6
7        private function __construct() {}
8        static function init() {
9
10                // Detect updates for Core, Plugins, and, Themes
11                foreach ( array( 'update_core', 'update_plugins', 'update_themes' ) as $transient ) {
12                        add_action( 'set_site_transient_' . $transient, array( 'WP_Background_Upgrader', 'queue_updates_check' ) );
13                        // Uh.. Yeah, work around for #25213
14                        add_action( 'set_site_transient_' . '_site_transient_' . $transient, array( 'WP_Background_Upgrader', 'queue_updates_check' ) );
15                }
16
17                // Cron Updates hook
18                add_action( 'wp_background_upgrader_process', array( 'WP_Background_Upgrader', 'perform_queued_updates' ) );
19
20        }
21
22        /**
23         * Loads the WordPress Admin environment for performing the updates.
24         * Needed because cron runs as a front-end process
25         */
26        static function load_required_admin_includes() {
27                // include ABSPATH . 'wp-admin/includes/admin.php'; // Load the whole lot of 'em
28                include ABSPATH . 'wp-admin/includes/update.php';
29                include ABSPATH . 'wp-admin/includes/plugin.php';
30                include ABSPATH . 'wp-admin/includes/plugin-install.php';
31                include ABSPATH . 'wp-admin/includes/theme.php';
32                include ABSPATH . 'wp-admin/includes/theme-install.php';
33                include ABSPATH . 'wp-admin/includes/file.php';
34                include ABSPATH . 'wp-admin/includes/class-wp-upgrader.php';
35        }
36
37        /**
38         * Finds the best auto-update available for this site.
39         * If there's 1.2.3 and 1.3 on offer, it'll choose 1.3 if the install allows it, else, 1.2.3
40         */
41        static function find_core_auto_update() {
42                $updates = get_site_transient( 'update_core' );
43                $auto_update = false;
44                foreach ( $updates->updates as $update ) {
45                        if ( 'autoupdate' != $update->response )
46                                continue;
47
48                        if ( ! self::should_upgrade( 'core', $update, ABSPATH ) )
49                                continue;
50
51                        if ( ! $auto_update || version_compare( $update->current, $auto_update->current, '>' ) )
52                                $auto_update = $update;
53                }
54                return $auto_update;
55        }
56
57        static function upgrader_disabled() {
58                // That's a no if you don't want files changes
59                if ( defined( 'DISABLE_FILE_MODS' ) && DISABLE_FILE_MODS )
60                        return true;
61
62                if ( defined( 'AUTOMATIC_UPDATER_DISABLED' ) && AUTOMATIC_UPDATER_DISABLED )
63                        return true;
64
65                return apply_filters( 'auto_upgrader_disabled', false );
66        }
67
68        /**
69         * Tests to see if we should upgrade a specific item, does not test to see if we CAN update the item.
70         */
71        static function should_upgrade( $type, $item, $context ) {
72
73                if ( self::upgrader_disabled() )
74                        return false;
75
76                // ..and also check for GIT/SVN checkouts
77                if ( ! apply_filters( 'auto_upgrade_ignore_checkout_status', false ) ) {
78                        $stop_dirs = array(
79                                ABSPATH,
80                                untrailingslashit( $context ),
81                        );
82                        if ( ! file_exists( ABSPATH . '/wp-config.php' ) ) // wp-config.php up one folder in a deployment situation
83                                $stop_dirs[] = dirname( ABSPATH );
84                        foreach ( array_unique( $stop_dirs ) as $dir ) {
85                                if ( file_exists( $dir . '/.svn' ) || file_exists( $dir . '/.git' ) )
86                                        return false;
87                        }
88                }
89
90                // Next up, do we actually have it enabled for this type of update?
91                switch ( $type ) {
92                        default:       $upgrade = false; break;
93                        case 'core':
94                                $upgrade = self::should_upgrade_to_core_version( $item->current );
95                                break;
96                        case 'plugin': $upgrade = false; break;
97                        case 'theme':  $upgrade = false; break;
98                        case 'lang':   $upgrade = false; break;
99                }
100
101                // And does the user / plugins want it?
102                // Plugins may filter on 'auto_upgrade_plugin', and check the 2nd param, $item, to only enable it for certain Plugins/Themes
103                if ( ! apply_filters( 'auto_upgrade_' . $type, $upgrade, $item ) )
104                        return false;
105
106                // If it's a core update, are we actually compatible with it's requirements?
107                if ( 'core' == $type ) {
108                        global $wpdb;
109
110                        $php_compat = version_compare( phpversion(), $item->php_version, '>=' );
111                        if ( file_exists( WP_CONTENT_DIR . '/db.php' ) && empty( $wpdb->is_mysql ) )
112                                $mysql_compat = true;
113                        else
114                                $mysql_compat = version_compare( $wpdb->db_version(), $item->mysql_version, '>=' );
115
116                        if ( ! $php_compat || ! $mysql_compat )
117                                return false;
118                }
119
120                return true;
121        }
122
123        // Checks to see if WP_Filesystem is setup to allow unattended upgrades
124        static function can_upgrade( $context ) {
125                if ( ! self::$skin )
126                        self::$skin = new WP_Background_Upgrader_Skin();
127                return (bool) self::$skin->request_filesystem_credentials();
128        }
129
130        // Determines if this WordPress Core version should update to $offered_ver or not
131        static function should_upgrade_to_core_version( $offered_ver /* x.y.z */ ) {
132                include ABSPATH . WPINC . '/version.php'; // $wp_version; // x.y.z
133
134                $current_branch = implode( '.', array_slice( preg_split( '/[.-]/', $wp_version  ), 0, 2 ) ); // x.y
135                $new_branch     = implode( '.', array_slice( preg_split( '/[.-]/', $offered_ver ), 0, 2 ) ); // x.y
136                $current_is_development_version = (bool) strpos( $wp_version, '-' );
137
138                // Defaults:
139                $upgrade_dev   = false;
140                $upgrade_minor = true;
141                $upgrade_major = false;
142
143                // WP_AUTO_UPDATE_CORE = true (all), 'minor', false.
144                if ( defined( 'WP_AUTO_UPDATE_CORE' ) ) {
145                        if ( false === WP_AUTO_UPDATE_CORE ) {
146                                // Defaults to turned off, unless a filter allows it
147                                $upgrade_dev = $upgrade_minor = $upgrade_major = false;
148                        } elseif ( true === WP_AUTO_UPDATE_CORE ) {
149                                // ALL updates for core
150                                $upgrade_dev = $upgrade_minor = $upgrade_major = true;
151                        } elseif ( 'minor' === WP_AUTO_UPDATE_CORE ) {
152                                // Only minor updates for core
153                                $upgrade_dev = $upgrade_major = false;
154                                $upgrade_minor = true;
155                        }
156                }
157
158                // 1: If we're already on that version, not much point in updating?
159                if ( $offered_ver == $wp_version )
160                        return false;
161
162                // 2: If we're running a newer version, that's a nope
163                if ( version_compare( $wp_version, $offered_ver, '>=' ) )
164                        return false;
165
166                // 3: 3.7-alpha-25000 -> 3.7-alpha-25678 -> 3.7-beta1 -> 3.7-beta2
167                if ( $current_is_development_version ) {
168                        if ( ! apply_filters( 'allow_dev_background_core_updates', $upgrade_dev ) )
169                                return false;
170                        // else fall through to minor + major branches below
171                }
172
173                // 4: Minor In-branch updates (3.7.0 -> 3.7.1 -> 3.7.2 -> 3.7.4)
174                if ( $current_branch == $new_branch )
175                        return apply_filters( 'allow_minor_background_core_updates', $upgrade_minor );
176
177                // 5: Major version updates (3.7.0 -> 3.8.0 -> 3.9.1)
178                if ( version_compare( $new_branch, $current_branch, '>' ) )
179                        return apply_filters( 'allow_major_background_core_updates', $upgrade_major );
180
181                // If we're not sure, we don't want it
182                return false;
183        }
184
185        static function upgrade( $type, $item ) {
186
187                wp_mail(
188                        get_site_option( 'admin_email' ),
189                        __METHOD__,
190                        "Starting an upgrade for:\n\n" . var_export( compact( 'type', 'item' ), true ) . "\n\n" . wp_debug_backtrace_summary()
191                );
192
193                self::$skin = new Background_Upgrader_Skin();
194
195                switch( $type ) {
196                        case 'core':
197                                // Okay, Why does the Core upgrader not use the Upgrader's skin during the actual main part of the upgrade???
198                                add_filter( 'update_feedback', function( $message ) {
199                                        WP_Background_Upgrader::$skin->feedback( $message );
200                                } );
201                                $upgrader = new Core_Upgrader( self::$skin );
202                                $context  = ABSPATH;
203                                break;
204                        case 'plugin':
205                                $upgrader = new Plugin_Upgrader( self::$skin );
206                                $context  = WP_PLUGIN_DIR; // We don't support custom Plugin directories, or updates for WPMU_PLUGIN_DIR
207                                break;
208                        case 'theme':
209                                $upgrader = new Theme_Upgrader( self::$skin );
210                                $context  = get_theme_root( $item );
211                                break;
212                        case 'lang':
213                                return false; // Not quite yet!
214                        //      $upgrader = new Language_Upgrader( self::$skin );
215                                $context  = WP_LANG_DIR;
216                                break;
217                }
218
219                // Determine if we can perform this upgrade or not
220                if ( ! self::should_upgrade( $type, $item, $context )  || ! self::can_upgrade( $context ) )
221                        return false;
222
223                // Boom, This sites about to get a whole new splash of paint!
224                $upgrade_result = $upgrader->upgrade( $item, array(
225                        'clear_update_cache' => false,
226                ) );
227
228                // Core doesn't output this, so lets append it so we don't get confused
229                if ( 'core' == $type ) {
230                        if ( is_wp_error( $upgrade_result ) ) {
231                                self::$skin->error( __( 'Installation Failed' ), $upgrade_result );
232                        } else {
233                                self::$skin->feedback( __( 'WordPress updated successfully' ) );
234                        }
235                }
236
237                // Clear cache's and update results
238                switch ( $type ) {
239                        case 'core':
240                                delete_site_transient( 'update_core' );
241                                break;
242                        case 'theme':
243                                wp_clean_themes_cache();
244                                break;
245                        case 'plugin':
246                                wp_clean_plugins_cache();
247                                break;
248                }
249
250                var_dump( compact( 'type', 'item', 'upgrader', 'upgrade_result' ) );
251
252                wp_mail(
253                        get_site_option( 'admin_email' ),
254                        __METHOD__,
255                        var_export( array(
256                                $upgrade_result,
257                                $upgrader,
258                                self::$skin,
259                        ), true )
260                );
261
262                return $upgrade_result;
263        }
264
265        /**
266         * Queues a cron entry in the event that the automatic upgrader is enabled, and there's at least one potentially upgradable item
267         */
268        static function queue_updates_check( $updates ) {
269
270                if ( self::upgrader_disabled() )
271                        return;
272
273                $plugin_updates = get_site_transient( 'update_plugins' );
274                $theme_updates = get_site_transient( 'update_themes' );
275                if ( ! self::find_core_auto_update() && empty( $plugin_updates->response ) && empty( $theme_updates->response ) )
276                        return;
277
278                if ( ! wp_next_scheduled( 'wp_background_upgrader_process' ) ) {
279                        // If the transient update was triggered by a user pageview, update in an hours time, else, now.
280                        $when_to_update = get_current_user_id() ? time() + HOUR_IN_SECONDS : time();
281                        $when_to_update = apply_filters( 'auto_upgrade_when_to_upgrade', $when_to_update );
282
283                        wp_schedule_single_event( $when_to_update, 'wp_background_upgrader_process' );
284                }
285
286        }
287
288        /**
289         * Kicks off a upgrade request for each item in the upgrade "queue"
290         */
291        static function perform_queued_updates() {
292
293                $lock_name = 'auto_upgrader.lock';
294                if ( get_site_transient( $lock_name ) ) {
295                        // Test to see if it was set more than an hour ago, if so, cleanup.
296                        if ( true || get_site_transient( $lock_name ) < ( time() - HOUR_IN_SECONDS ) )
297                                delete_site_transient( $lock_name );
298                        else // Recent lock
299                                return;
300                }
301                // Lock upgrades for us for half an hour
302                if ( ! set_site_transient( $lock_name, microtime( true ), HOUR_IN_SECONDS / 2 ) )
303                        return;
304
305                // Admin-ize ourselves
306                self::load_required_admin_includes();
307
308                // Next, Plugins
309                wp_update_plugins(); // Check for Plugin updates
310                $plugin_updates = get_site_transient( 'update_plugins' );
311                if ( $plugin_updates && !empty( $plugin_updates->response ) ) {
312                        foreach ( array_keys( $plugin_updates->response ) as $plugin ) {
313                                self::upgrade( 'plugin', $plugin );
314                        }
315                        // Force refresh of plugin update information
316                        wp_clean_plugins_cache();
317                }
318
319                // Next, those themes we all love
320                wp_update_themes();  // Check for Theme updates
321                $theme_updates = get_site_transient( 'update_themes' );
322                if ( $theme_updates && !empty( $theme_updates->response ) ) {
323                        foreach ( array_keys( $theme_updates->response ) as $theme ) {
324                                self::upgrade( 'theme', $theme );
325                        }
326                        // Force refresh of theme update information
327                        wp_clean_themes_cache();
328                }
329
330                // Up first, Core.
331                wp_version_check(); // Check for Core updates
332                $core_update = self::find_core_auto_update();
333                if ( $core_update )
334                        self::upgrade( 'core', $core_update );
335
336                // Cleanup, These won't trigger any updates this time due to the locking transient
337                wp_version_check();  // check for Core updates
338                wp_update_themes();  // Check for Theme updates
339                wp_update_plugins(); // Check for Plugin updates
340
341                delete_site_transient( $lock_name );
342
343        }
344
345}
346WP_Background_Upgrader::init();