| 1 | <?php
|
|---|
| 2 |
|
|---|
| 3 | class 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 | }
|
|---|
| 346 | WP_Background_Upgrader::init();
|
|---|