/wp-admin/includes/class-core-upgrader.php

  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. */ 
  21. class Core_Upgrader extends WP_Upgrader { 
  22.  
  23. /** 
  24. * Initialize the upgrade strings. 
  25. * 
  26. * @since 2.8.0 
  27. * @access public 
  28. */ 
  29. public function upgrade_strings() { 
  30. $this->strings['up_to_date'] = __('WordPress is at the latest version.'); 
  31. $this->strings['locked'] = __('Another update is currently in progress.'); 
  32. $this->strings['no_package'] = __('Update package not available.'); 
  33. $this->strings['downloading_package'] = __('Downloading update from <span class="code">%s</span>…'); 
  34. $this->strings['unpack_package'] = __('Unpacking the update…'); 
  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. * Upgrade WordPress core. 
  42. * 
  43. * @since 2.8.0 
  44. * @access public 
  45. * 
  46. * @global WP_Filesystem_Base $wp_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 null|false|WP_Error False or WP_Error on failure, null on success. 
  61. */ 
  62. public function upgrade( $current, $args = array() ) { 
  63. global $wp_filesystem; 
  64.  
  65. include( 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 ) || $current->response == 'latest' ) 
  82. return new WP_Error('up_to_date', $this->strings['up_to_date']); 
  83.  
  84. $res = $this->fs_connect( array( ABSPATH, WP_CONTENT_DIR ), $parsed_args['allow_relaxed_file_ownership'] ); 
  85. if ( ! $res || is_wp_error( $res ) ) { 
  86. return $res; 
  87.  
  88. $wp_dir = trailingslashit($wp_filesystem->abspath()); 
  89.  
  90. $partial = true; 
  91. if ( $parsed_args['do_rollback'] ) 
  92. $partial = false; 
  93. elseif ( $parsed_args['pre_check_md5'] && ! $this->check_files() ) 
  94. $partial = false; 
  95.  
  96. /** 
  97. * If partial update is returned from the API, use that, unless we're doing 
  98. * a reinstall. If we cross the new_bundled version number, then use 
  99. * the new_bundled zip. Don't though if the constant is set to skip bundled items. 
  100. * If the API returns a no_content zip, go with it. Finally, default to the full zip. 
  101. */ 
  102. if ( $parsed_args['do_rollback'] && $current->packages->rollback ) 
  103. $to_download = 'rollback'; 
  104. elseif ( $current->packages->partial && 'reinstall' != $current->response && $wp_version == $current->partial_version && $partial ) 
  105. $to_download = 'partial'; 
  106. elseif ( $current->packages->new_bundled && version_compare( $wp_version, $current->new_bundled, '<' ) 
  107. && ( ! defined( 'CORE_UPGRADE_SKIP_NEW_BUNDLED' ) || ! CORE_UPGRADE_SKIP_NEW_BUNDLED ) ) 
  108. $to_download = 'new_bundled'; 
  109. elseif ( $current->packages->no_content ) 
  110. $to_download = 'no_content'; 
  111. else 
  112. $to_download = 'full'; 
  113.  
  114. // Lock to prevent multiple Core Updates occurring 
  115. $lock = WP_Upgrader::create_lock( 'core_updater', 15 * MINUTE_IN_SECONDS ); 
  116. if ( ! $lock ) { 
  117. return new WP_Error( 'locked', $this->strings['locked'] ); 
  118.  
  119. $download = $this->download_package( $current->packages->$to_download ); 
  120. if ( is_wp_error( $download ) ) { 
  121. WP_Upgrader::release_lock( 'core_updater' ); 
  122. return $download; 
  123.  
  124. $working_dir = $this->unpack_package( $download ); 
  125. if ( is_wp_error( $working_dir ) ) { 
  126. WP_Upgrader::release_lock( 'core_updater' ); 
  127. return $working_dir; 
  128.  
  129. // Copy update-core.php from the new version into place. 
  130. if ( !$wp_filesystem->copy($working_dir . '/wordpress/wp-admin/includes/update-core.php', $wp_dir . 'wp-admin/includes/update-core.php', true) ) { 
  131. $wp_filesystem->delete($working_dir, true); 
  132. WP_Upgrader::release_lock( 'core_updater' ); 
  133. return new WP_Error( 'copy_failed_for_update_core_file', __( 'The update cannot be installed because we will be unable to copy some files. This is usually due to inconsistent file permissions.' ), 'wp-admin/includes/update-core.php' ); 
  134. $wp_filesystem->chmod($wp_dir . 'wp-admin/includes/update-core.php', FS_CHMOD_FILE); 
  135.  
  136. require_once( ABSPATH . 'wp-admin/includes/update-core.php' ); 
  137.  
  138. if ( ! function_exists( 'update_core' ) ) { 
  139. WP_Upgrader::release_lock( 'core_updater' ); 
  140. return new WP_Error( 'copy_failed_space', $this->strings['copy_failed_space'] ); 
  141.  
  142. $result = update_core( $working_dir, $wp_dir ); 
  143.  
  144. // In the event of an issue, we may be able to roll back. 
  145. if ( $parsed_args['attempt_rollback'] && $current->packages->rollback && ! $parsed_args['do_rollback'] ) { 
  146. $try_rollback = false; 
  147. if ( is_wp_error( $result ) ) { 
  148. $error_code = $result->get_error_code(); 
  149. /** 
  150. * Not all errors are equal. These codes are critical: copy_failed__copy_dir,  
  151. * mkdir_failed__copy_dir, copy_failed__copy_dir_retry, and disk_full. 
  152. * do_rollback allows for update_core() to trigger a rollback if needed. 
  153. */ 
  154. if ( false !== strpos( $error_code, 'do_rollback' ) ) 
  155. $try_rollback = true; 
  156. elseif ( false !== strpos( $error_code, '__copy_dir' ) ) 
  157. $try_rollback = true; 
  158. elseif ( 'disk_full' === $error_code ) 
  159. $try_rollback = true; 
  160.  
  161. if ( $try_rollback ) { 
  162. /** This filter is documented in wp-admin/includes/update-core.php */ 
  163. apply_filters( 'update_feedback', $result ); 
  164.  
  165. /** This filter is documented in wp-admin/includes/update-core.php */ 
  166. apply_filters( 'update_feedback', $this->strings['start_rollback'] ); 
  167.  
  168. $rollback_result = $this->upgrade( $current, array_merge( $parsed_args, array( 'do_rollback' => true ) ) ); 
  169.  
  170. $original_result = $result; 
  171. $result = new WP_Error( 'rollback_was_required', $this->strings['rollback_was_required'], (object) array( 'update' => $original_result, 'rollback' => $rollback_result ) ); 
  172.  
  173. /** This action is documented in wp-admin/includes/class-wp-upgrader.php */ 
  174. do_action( 'upgrader_process_complete', $this, array( 'action' => 'update', 'type' => 'core' ) ); 
  175.  
  176. // Clear the current updates 
  177. delete_site_transient( 'update_core' ); 
  178.  
  179. if ( ! $parsed_args['do_rollback'] ) { 
  180. $stats = array( 
  181. 'update_type' => $current->response,  
  182. 'success' => true,  
  183. 'fs_method' => $wp_filesystem->method,  
  184. 'fs_method_forced' => defined( 'FS_METHOD' ) || has_filter( 'filesystem_method' ),  
  185. 'fs_method_direct' => !empty( $GLOBALS['_wp_filesystem_direct_method'] ) ? $GLOBALS['_wp_filesystem_direct_method'] : '',  
  186. 'time_taken' => time() - $start_time,  
  187. 'reported' => $wp_version,  
  188. 'attempted' => $current->version,  
  189. ); 
  190.  
  191. if ( is_wp_error( $result ) ) { 
  192. $stats['success'] = false; 
  193. // Did a rollback occur? 
  194. if ( ! empty( $try_rollback ) ) { 
  195. $stats['error_code'] = $original_result->get_error_code(); 
  196. $stats['error_data'] = $original_result->get_error_data(); 
  197. // Was the rollback successful? If not, collect its error too. 
  198. $stats['rollback'] = ! is_wp_error( $rollback_result ); 
  199. if ( is_wp_error( $rollback_result ) ) { 
  200. $stats['rollback_code'] = $rollback_result->get_error_code(); 
  201. $stats['rollback_data'] = $rollback_result->get_error_data(); 
  202. } else { 
  203. $stats['error_code'] = $result->get_error_code(); 
  204. $stats['error_data'] = $result->get_error_data(); 
  205.  
  206. wp_version_check( $stats ); 
  207.  
  208. WP_Upgrader::release_lock( 'core_updater' ); 
  209.  
  210. return $result; 
  211.  
  212. /** 
  213. * Determines if this WordPress Core version should update to an offered version or not. 
  214. * 
  215. * @since 3.7.0 
  216. * @access public 
  217. * 
  218. * @static 
  219. * 
  220. * @param string $offered_ver The offered version, of the format x.y.z. 
  221. * @return bool True if we should update to the offered version, otherwise false. 
  222. */ 
  223. public static function should_update_to_version( $offered_ver ) { 
  224. include( ABSPATH . WPINC . '/version.php' ); // $wp_version; // x.y.z 
  225.  
  226. $current_branch = implode( '.', array_slice( preg_split( '/[.-]/', $wp_version ), 0, 2 ) ); // x.y 
  227. $new_branch = implode( '.', array_slice( preg_split( '/[.-]/', $offered_ver ), 0, 2 ) ); // x.y 
  228. $current_is_development_version = (bool) strpos( $wp_version, '-' ); 
  229.  
  230. // Defaults: 
  231. $upgrade_dev = true; 
  232. $upgrade_minor = true; 
  233. $upgrade_major = false; 
  234.  
  235. // WP_AUTO_UPDATE_CORE = true (all), 'minor', false. 
  236. if ( defined( 'WP_AUTO_UPDATE_CORE' ) ) { 
  237. if ( false === WP_AUTO_UPDATE_CORE ) { 
  238. // Defaults to turned off, unless a filter allows it 
  239. $upgrade_dev = $upgrade_minor = $upgrade_major = false; 
  240. } elseif ( true === WP_AUTO_UPDATE_CORE ) { 
  241. // ALL updates for core 
  242. $upgrade_dev = $upgrade_minor = $upgrade_major = true; 
  243. } elseif ( 'minor' === WP_AUTO_UPDATE_CORE ) { 
  244. // Only minor updates for core 
  245. $upgrade_dev = $upgrade_major = false; 
  246. $upgrade_minor = true; 
  247.  
  248. // 1: If we're already on that version, not much point in updating? 
  249. if ( $offered_ver == $wp_version ) 
  250. return false; 
  251.  
  252. // 2: If we're running a newer version, that's a nope 
  253. if ( version_compare( $wp_version, $offered_ver, '>' ) ) 
  254. return false; 
  255.  
  256. $failure_data = get_site_option( 'auto_core_update_failed' ); 
  257. if ( $failure_data ) { 
  258. // If this was a critical update failure, cannot update. 
  259. if ( ! empty( $failure_data['critical'] ) ) 
  260. return false; 
  261.  
  262. // Don't claim we can update on update-core.php if we have a non-critical failure logged. 
  263. if ( $wp_version == $failure_data['current'] && false !== strpos( $offered_ver, '.1.next.minor' ) ) 
  264. return false; 
  265.  
  266. // Cannot update if we're retrying the same A to B update that caused a non-critical failure. 
  267. // Some non-critical failures do allow retries, like download_failed. 
  268. // 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. 
  269. if ( empty( $failure_data['retry'] ) && $wp_version == $failure_data['current'] && $offered_ver == $failure_data['attempted'] ) 
  270. return false; 
  271.  
  272. // 3: 3.7-alpha-25000 -> 3.7-alpha-25678 -> 3.7-beta1 -> 3.7-beta2 
  273. if ( $current_is_development_version ) { 
  274.  
  275. /** 
  276. * Filters whether to enable automatic core updates for development versions. 
  277. * 
  278. * @since 3.7.0 
  279. * 
  280. * @param bool $upgrade_dev Whether to enable automatic updates for 
  281. * development versions. 
  282. */ 
  283. if ( ! apply_filters( 'allow_dev_auto_core_updates', $upgrade_dev ) ) 
  284. return false; 
  285. // Else fall through to minor + major branches below. 
  286.  
  287. // 4: Minor In-branch updates (3.7.0 -> 3.7.1 -> 3.7.2 -> 3.7.4) 
  288. if ( $current_branch == $new_branch ) { 
  289.  
  290. /** 
  291. * Filters whether to enable minor automatic core updates. 
  292. * 
  293. * @since 3.7.0 
  294. * 
  295. * @param bool $upgrade_minor Whether to enable minor automatic core updates. 
  296. */ 
  297. return apply_filters( 'allow_minor_auto_core_updates', $upgrade_minor ); 
  298.  
  299. // 5: Major version updates (3.7.0 -> 3.8.0 -> 3.9.1) 
  300. if ( version_compare( $new_branch, $current_branch, '>' ) ) { 
  301.  
  302. /** 
  303. * Filters whether to enable major automatic core updates. 
  304. * 
  305. * @since 3.7.0 
  306. * 
  307. * @param bool $upgrade_major Whether to enable major automatic core updates. 
  308. */ 
  309. return apply_filters( 'allow_major_auto_core_updates', $upgrade_major ); 
  310.  
  311. // If we're not sure, we don't want it 
  312. return false; 
  313.  
  314. /** 
  315. * Compare the disk file checksums against the expected checksums. 
  316. * 
  317. * @since 3.7.0 
  318. * @access public 
  319. * 
  320. * @global string $wp_version 
  321. * @global string $wp_local_package 
  322. * 
  323. * @return bool True if the checksums match, otherwise false. 
  324. */ 
  325. public function check_files() { 
  326. global $wp_version, $wp_local_package; 
  327.  
  328. $checksums = get_core_checksums( $wp_version, isset( $wp_local_package ) ? $wp_local_package : 'en_US' ); 
  329.  
  330. if ( ! is_array( $checksums ) ) 
  331. return false; 
  332.  
  333. foreach ( $checksums as $file => $checksum ) { 
  334. // Skip files which get updated 
  335. if ( 'wp-content' == substr( $file, 0, 10 ) ) 
  336. continue; 
  337. if ( ! file_exists( ABSPATH . $file ) || md5_file( ABSPATH . $file ) !== $checksum ) 
  338. return false; 
  339.  
  340. return true; 
.