wp/wp-admin/includes/class-wp-site-health-auto-updates.php
changeset 21 48c4eec2b7e6
parent 19 3d72ae0968f4
child 22 8c2e4d02f4ef
equal deleted inserted replaced
20:7b1b88e27a20 21:48c4eec2b7e6
     5  * @package WordPress
     5  * @package WordPress
     6  * @subpackage Site_Health
     6  * @subpackage Site_Health
     7  * @since 5.2.0
     7  * @since 5.2.0
     8  */
     8  */
     9 
     9 
       
    10 #[AllowDynamicProperties]
    10 class WP_Site_Health_Auto_Updates {
    11 class WP_Site_Health_Auto_Updates {
    11 	/**
    12 	/**
    12 	 * WP_Site_Health_Auto_Updates constructor.
    13 	 * WP_Site_Health_Auto_Updates constructor.
    13 	 *
    14 	 *
    14 	 * @since 5.2.0
    15 	 * @since 5.2.0
    17 		require_once ABSPATH . 'wp-admin/includes/class-wp-upgrader.php';
    18 		require_once ABSPATH . 'wp-admin/includes/class-wp-upgrader.php';
    18 	}
    19 	}
    19 
    20 
    20 
    21 
    21 	/**
    22 	/**
    22 	 * Run tests to determine if auto-updates can run.
    23 	 * Runs tests to determine if auto-updates can run.
    23 	 *
    24 	 *
    24 	 * @since 5.2.0
    25 	 * @since 5.2.0
    25 	 *
    26 	 *
    26 	 * @return array The test results.
    27 	 * @return array The test results.
    27 	 */
    28 	 */
    39 			$this->test_accepts_minor_updates(),
    40 			$this->test_accepts_minor_updates(),
    40 		);
    41 		);
    41 
    42 
    42 		$tests = array_filter( $tests );
    43 		$tests = array_filter( $tests );
    43 		$tests = array_map(
    44 		$tests = array_map(
    44 			static function( $test ) {
    45 			static function ( $test ) {
    45 				$test = (object) $test;
    46 				$test = (object) $test;
    46 
    47 
    47 				if ( empty( $test->severity ) ) {
    48 				if ( empty( $test->severity ) ) {
    48 					$test->severity = 'warning';
    49 					$test->severity = 'warning';
    49 				}
    50 				}
    55 
    56 
    56 		return $tests;
    57 		return $tests;
    57 	}
    58 	}
    58 
    59 
    59 	/**
    60 	/**
    60 	 * Test if auto-updates related constants are set correctly.
    61 	 * Tests if auto-updates related constants are set correctly.
    61 	 *
    62 	 *
    62 	 * @since 5.2.0
    63 	 * @since 5.2.0
    63 	 * @since 5.5.1 The `$value` parameter can accept an array.
    64 	 * @since 5.5.1 The `$value` parameter can accept an array.
    64 	 *
    65 	 *
    65 	 * @param string $constant         The name of the constant to check.
    66 	 * @param string $constant         The name of the constant to check.
    71 		$acceptable_values = (array) $value;
    72 		$acceptable_values = (array) $value;
    72 
    73 
    73 		if ( defined( $constant ) && ! in_array( constant( $constant ), $acceptable_values, true ) ) {
    74 		if ( defined( $constant ) && ! in_array( constant( $constant ), $acceptable_values, true ) ) {
    74 			return array(
    75 			return array(
    75 				'description' => sprintf(
    76 				'description' => sprintf(
    76 					/* translators: %s: Name of the constant used. */
    77 					/* translators: 1: Name of the constant used. 2: Value of the constant used. */
    77 					__( 'The %s constant is defined and enabled.' ),
    78 					__( 'The %1$s constant is defined as %2$s' ),
    78 					"<code>$constant</code>"
    79 					"<code>$constant</code>",
    79 				),
    80 					'<code>' . esc_html( var_export( constant( $constant ), true ) ) . '</code>'
    80 				'severity'    => 'fail',
    81 				),
    81 			);
    82 				'severity'    => 'fail',
    82 		}
    83 			);
    83 	}
    84 		}
    84 
    85 	}
    85 	/**
    86 
    86 	 * Check if updates are intercepted by a filter.
    87 	/**
       
    88 	 * Checks if updates are intercepted by a filter.
    87 	 *
    89 	 *
    88 	 * @since 5.2.0
    90 	 * @since 5.2.0
    89 	 *
    91 	 *
    90 	 * @return array The test results.
    92 	 * @return array The test results.
    91 	 */
    93 	 */
   103 			);
   105 			);
   104 		}
   106 		}
   105 	}
   107 	}
   106 
   108 
   107 	/**
   109 	/**
   108 	 * Check if automatic updates are disabled by a filter.
   110 	 * Checks if automatic updates are disabled by a filter.
   109 	 *
   111 	 *
   110 	 * @since 5.2.0
   112 	 * @since 5.2.0
   111 	 *
   113 	 *
   112 	 * @return array The test results.
   114 	 * @return array The test results.
   113 	 */
   115 	 */
   124 			);
   126 			);
   125 		}
   127 		}
   126 	}
   128 	}
   127 
   129 
   128 	/**
   130 	/**
   129 	 * Check if automatic updates are disabled.
   131 	 * Checks if automatic updates are disabled.
   130 	 *
   132 	 *
   131 	 * @since 5.3.0
   133 	 * @since 5.3.0
   132 	 *
   134 	 *
   133 	 * @return array|false The test results. False if auto-updates are enabled.
   135 	 * @return array|false The test results. False if auto-updates are enabled.
   134 	 */
   136 	 */
   148 			'severity'    => 'fail',
   150 			'severity'    => 'fail',
   149 		);
   151 		);
   150 	}
   152 	}
   151 
   153 
   152 	/**
   154 	/**
   153 	 * Check if automatic updates have tried to run, but failed, previously.
   155 	 * Checks if automatic updates have tried to run, but failed, previously.
   154 	 *
   156 	 *
   155 	 * @since 5.2.0
   157 	 * @since 5.2.0
   156 	 *
   158 	 *
   157 	 * @return array|false The test results. False if the auto-updates failed.
   159 	 * @return array|false The test results. False if the auto-updates failed.
   158 	 */
   160 	 */
   164 		}
   166 		}
   165 
   167 
   166 		if ( ! empty( $failed['critical'] ) ) {
   168 		if ( ! empty( $failed['critical'] ) ) {
   167 			$description  = __( 'A previous automatic background update ended with a critical failure, so updates are now disabled.' );
   169 			$description  = __( 'A previous automatic background update ended with a critical failure, so updates are now disabled.' );
   168 			$description .= ' ' . __( 'You would have received an email because of this.' );
   170 			$description .= ' ' . __( 'You would have received an email because of this.' );
   169 			$description .= ' ' . __( "When you've been able to update using the \"Update now\" button on Dashboard > Updates, we'll clear this error for future update attempts." );
   171 			$description .= ' ' . __( "When you've been able to update using the \"Update now\" button on Dashboard > Updates, this error will be cleared for future update attempts." );
   170 			$description .= ' ' . sprintf(
   172 			$description .= ' ' . sprintf(
   171 				/* translators: %s: Code of error shown. */
   173 				/* translators: %s: Code of error shown. */
   172 				__( 'The error code was %s.' ),
   174 				__( 'The error code was %s.' ),
   173 				'<code>' . $failed['error_code'] . '</code>'
   175 				'<code>' . $failed['error_code'] . '</code>'
   174 			);
   176 			);
   181 		$description = __( 'A previous automatic background update could not occur.' );
   183 		$description = __( 'A previous automatic background update could not occur.' );
   182 		if ( empty( $failed['retry'] ) ) {
   184 		if ( empty( $failed['retry'] ) ) {
   183 			$description .= ' ' . __( 'You would have received an email because of this.' );
   185 			$description .= ' ' . __( 'You would have received an email because of this.' );
   184 		}
   186 		}
   185 
   187 
   186 		$description .= ' ' . __( "We'll try again with the next release." );
   188 		$description .= ' ' . __( 'Another attempt will be made with the next release.' );
   187 		$description .= ' ' . sprintf(
   189 		$description .= ' ' . sprintf(
   188 			/* translators: %s: Code of error shown. */
   190 			/* translators: %s: Code of error shown. */
   189 			__( 'The error code was %s.' ),
   191 			__( 'The error code was %s.' ),
   190 			'<code>' . $failed['error_code'] . '</code>'
   192 			'<code>' . $failed['error_code'] . '</code>'
   191 		);
   193 		);
   194 			'severity'    => 'warning',
   196 			'severity'    => 'warning',
   195 		);
   197 		);
   196 	}
   198 	}
   197 
   199 
   198 	/**
   200 	/**
   199 	 * Check if WordPress is controlled by a VCS (Git, Subversion etc).
   201 	 * Checks if WordPress is controlled by a VCS (Git, Subversion etc).
   200 	 *
   202 	 *
   201 	 * @since 5.2.0
   203 	 * @since 5.2.0
   202 	 *
   204 	 *
   203 	 * @return array The test results.
   205 	 * @return array The test results.
   204 	 */
   206 	 */
   224 		$check_dirs = array_unique( $check_dirs );
   226 		$check_dirs = array_unique( $check_dirs );
   225 
   227 
   226 		// Search all directories we've found for evidence of version control.
   228 		// Search all directories we've found for evidence of version control.
   227 		foreach ( $vcs_dirs as $vcs_dir ) {
   229 		foreach ( $vcs_dirs as $vcs_dir ) {
   228 			foreach ( $check_dirs as $check_dir ) {
   230 			foreach ( $check_dirs as $check_dir ) {
   229 				// phpcs:ignore WordPress.CodeAnalysis.AssignmentInCondition,Squiz.PHP.DisallowMultipleAssignments
   231 				// phpcs:ignore Generic.CodeAnalysis.AssignmentInCondition,Squiz.PHP.DisallowMultipleAssignments
   230 				if ( $checkout = @is_dir( rtrim( $check_dir, '\\/' ) . "/$vcs_dir" ) ) {
   232 				if ( $checkout = @is_dir( rtrim( $check_dir, '\\/' ) . "/$vcs_dir" ) ) {
   231 					break 2;
   233 					break 2;
   232 				}
   234 				}
   233 			}
   235 			}
   234 		}
   236 		}
   264 			'severity'    => 'pass',
   266 			'severity'    => 'pass',
   265 		);
   267 		);
   266 	}
   268 	}
   267 
   269 
   268 	/**
   270 	/**
   269 	 * Check if we can access files without providing credentials.
   271 	 * Checks if we can access files without providing credentials.
   270 	 *
   272 	 *
   271 	 * @since 5.2.0
   273 	 * @since 5.2.0
   272 	 *
   274 	 *
   273 	 * @return array The test results.
   275 	 * @return array The test results.
   274 	 */
   276 	 */
   275 	public function test_check_wp_filesystem_method() {
   277 	public function test_check_wp_filesystem_method() {
   276 		// Make sure the `request_filesystem_credentials()` function is available during our REST API call.
   278 		// Make sure the `request_filesystem_credentials()` function is available during our REST API call.
   277 		if ( ! function_exists( 'request_filesystem_credentials' ) ) {
   279 		if ( ! function_exists( 'request_filesystem_credentials' ) ) {
   278 			require_once ABSPATH . '/wp-admin/includes/file.php';
   280 			require_once ABSPATH . 'wp-admin/includes/file.php';
   279 		}
   281 		}
   280 
   282 
   281 		$skin    = new Automatic_Upgrader_Skin;
   283 		$skin    = new Automatic_Upgrader_Skin();
   282 		$success = $skin->request_filesystem_credentials( false, ABSPATH );
   284 		$success = $skin->request_filesystem_credentials( false, ABSPATH );
   283 
   285 
   284 		if ( ! $success ) {
   286 		if ( ! $success ) {
   285 			$description  = __( 'Your installation of WordPress prompts for FTP credentials to perform updates.' );
   287 			$description  = __( 'Your installation of WordPress prompts for FTP credentials to perform updates.' );
   286 			$description .= ' ' . __( '(Your site is performing updates over FTP due to file ownership. Talk to your hosting company.)' );
   288 			$description .= ' ' . __( '(Your site is performing updates over FTP due to file ownership. Talk to your hosting company.)' );
   296 			'severity'    => 'pass',
   298 			'severity'    => 'pass',
   297 		);
   299 		);
   298 	}
   300 	}
   299 
   301 
   300 	/**
   302 	/**
   301 	 * Check if core files are writable by the web user/group.
   303 	 * Checks if core files are writable by the web user/group.
   302 	 *
   304 	 *
   303 	 * @since 5.2.0
   305 	 * @since 5.2.0
   304 	 *
   306 	 *
   305 	 * @global WP_Filesystem_Base $wp_filesystem WordPress filesystem subclass.
   307 	 * @global WP_Filesystem_Base $wp_filesystem WordPress filesystem subclass.
   306 	 *
   308 	 *
   309 	public function test_all_files_writable() {
   311 	public function test_all_files_writable() {
   310 		global $wp_filesystem;
   312 		global $wp_filesystem;
   311 
   313 
   312 		require ABSPATH . WPINC . '/version.php'; // $wp_version; // x.y.z
   314 		require ABSPATH . WPINC . '/version.php'; // $wp_version; // x.y.z
   313 
   315 
   314 		$skin    = new Automatic_Upgrader_Skin;
   316 		$skin    = new Automatic_Upgrader_Skin();
   315 		$success = $skin->request_filesystem_credentials( false, ABSPATH );
   317 		$success = $skin->request_filesystem_credentials( false, ABSPATH );
   316 
   318 
   317 		if ( ! $success ) {
   319 		if ( ! $success ) {
   318 			return false;
   320 			return false;
   319 		}
   321 		}
   324 			return false;
   326 			return false;
   325 		}
   327 		}
   326 
   328 
   327 		// Make sure the `get_core_checksums()` function is available during our REST API call.
   329 		// Make sure the `get_core_checksums()` function is available during our REST API call.
   328 		if ( ! function_exists( 'get_core_checksums' ) ) {
   330 		if ( ! function_exists( 'get_core_checksums' ) ) {
   329 			require_once ABSPATH . '/wp-admin/includes/update.php';
   331 			require_once ABSPATH . 'wp-admin/includes/update.php';
   330 		}
   332 		}
   331 
   333 
   332 		$checksums = get_core_checksums( $wp_version, 'en_US' );
   334 		$checksums = get_core_checksums( $wp_version, 'en_US' );
   333 		$dev       = ( false !== strpos( $wp_version, '-' ) );
   335 		$dev       = ( str_contains( $wp_version, '-' ) );
   334 		// Get the last stable version's files and test against that.
   336 		// Get the last stable version's files and test against that.
   335 		if ( ! $checksums && $dev ) {
   337 		if ( ! $checksums && $dev ) {
   336 			$checksums = get_core_checksums( (float) $wp_version - 0.1, 'en_US' );
   338 			$checksums = get_core_checksums( (float) $wp_version - 0.1, 'en_US' );
   337 		}
   339 		}
   338 
   340 
   354 			);
   356 			);
   355 		}
   357 		}
   356 
   358 
   357 		$unwritable_files = array();
   359 		$unwritable_files = array();
   358 		foreach ( array_keys( $checksums ) as $file ) {
   360 		foreach ( array_keys( $checksums ) as $file ) {
   359 			if ( 'wp-content' === substr( $file, 0, 10 ) ) {
   361 			if ( str_starts_with( $file, 'wp-content' ) ) {
   360 				continue;
   362 				continue;
   361 			}
   363 			}
   362 			if ( ! file_exists( ABSPATH . $file ) ) {
   364 			if ( ! file_exists( ABSPATH . $file ) ) {
   363 				continue;
   365 				continue;
   364 			}
   366 			}
   383 			);
   385 			);
   384 		}
   386 		}
   385 	}
   387 	}
   386 
   388 
   387 	/**
   389 	/**
   388 	 * Check if the install is using a development branch and can use nightly packages.
   390 	 * Checks if the install is using a development branch and can use nightly packages.
   389 	 *
   391 	 *
   390 	 * @since 5.2.0
   392 	 * @since 5.2.0
   391 	 *
   393 	 *
   392 	 * @return array|false The test results. False if it isn't a development version.
   394 	 * @return array|false The test results. False if it isn't a development version.
   393 	 */
   395 	 */
   394 	public function test_accepts_dev_updates() {
   396 	public function test_accepts_dev_updates() {
   395 		require ABSPATH . WPINC . '/version.php'; // $wp_version; // x.y.z
   397 		require ABSPATH . WPINC . '/version.php'; // $wp_version; // x.y.z
   396 		// Only for dev versions.
   398 		// Only for dev versions.
   397 		if ( false === strpos( $wp_version, '-' ) ) {
   399 		if ( ! str_contains( $wp_version, '-' ) ) {
   398 			return false;
   400 			return false;
   399 		}
   401 		}
   400 
   402 
   401 		if ( defined( 'WP_AUTO_UPDATE_CORE' ) && ( 'minor' === WP_AUTO_UPDATE_CORE || false === WP_AUTO_UPDATE_CORE ) ) {
   403 		if ( defined( 'WP_AUTO_UPDATE_CORE' ) && ( 'minor' === WP_AUTO_UPDATE_CORE || false === WP_AUTO_UPDATE_CORE ) ) {
   402 			return array(
   404 			return array(
   421 			);
   423 			);
   422 		}
   424 		}
   423 	}
   425 	}
   424 
   426 
   425 	/**
   427 	/**
   426 	 * Check if the site supports automatic minor updates.
   428 	 * Checks if the site supports automatic minor updates.
   427 	 *
   429 	 *
   428 	 * @since 5.2.0
   430 	 * @since 5.2.0
   429 	 *
   431 	 *
   430 	 * @return array The test results.
   432 	 * @return array The test results.
   431 	 */
   433 	 */