[ Index ]

PHP Cross Reference of WordPress Trunk (Updated Daily)

Search

title

Body

[close]

/wp-admin/includes/ -> class-wp-site-health-auto-updates.php (source)

   1  <?php
   2  /**
   3   * Class for testing automatic updates in the WordPress code.
   4   *
   5   * @package WordPress
   6   * @subpackage Site_Health
   7   * @since 5.2.0
   8   */
   9  
  10  #[AllowDynamicProperties]
  11  class WP_Site_Health_Auto_Updates {
  12      /**
  13       * WP_Site_Health_Auto_Updates constructor.
  14       *
  15       * @since 5.2.0
  16       */
  17  	public function __construct() {
  18          require_once  ABSPATH . 'wp-admin/includes/class-wp-upgrader.php';
  19      }
  20  
  21  
  22      /**
  23       * Runs tests to determine if auto-updates can run.
  24       *
  25       * @since 5.2.0
  26       *
  27       * @return array The test results.
  28       */
  29  	public function run_tests() {
  30          $tests = array(
  31              $this->test_constants( 'WP_AUTO_UPDATE_CORE', array( true, 'beta', 'rc', 'development', 'branch-development', 'minor' ) ),
  32              $this->test_wp_version_check_attached(),
  33              $this->test_filters_automatic_updater_disabled(),
  34              $this->test_wp_automatic_updates_disabled(),
  35              $this->test_if_failed_update(),
  36              $this->test_vcs_abspath(),
  37              $this->test_check_wp_filesystem_method(),
  38              $this->test_all_files_writable(),
  39              $this->test_accepts_dev_updates(),
  40              $this->test_accepts_minor_updates(),
  41          );
  42  
  43          $tests = array_filter( $tests );
  44          $tests = array_map(
  45              static function ( $test ) {
  46                  $test = (object) $test;
  47  
  48                  if ( empty( $test->severity ) ) {
  49                      $test->severity = 'warning';
  50                  }
  51  
  52                  return $test;
  53              },
  54              $tests
  55          );
  56  
  57          return $tests;
  58      }
  59  
  60      /**
  61       * Tests if auto-updates related constants are set correctly.
  62       *
  63       * @since 5.2.0
  64       * @since 5.5.1 The `$value` parameter can accept an array.
  65       *
  66       * @param string $constant         The name of the constant to check.
  67       * @param bool|string|array $value The value that the constant should be, if set,
  68       *                                 or an array of acceptable values.
  69       * @return array|null The test results if there are any constants set incorrectly,
  70       *                    or null if the test passed.
  71       */
  72  	public function test_constants( $constant, $value ) {
  73          $acceptable_values = (array) $value;
  74  
  75          if ( defined( $constant ) && ! in_array( constant( $constant ), $acceptable_values, true ) ) {
  76              return array(
  77                  'description' => sprintf(
  78                      /* translators: 1: Name of the constant used. 2: Value of the constant used. */
  79                      __( 'The %1$s constant is defined as %2$s' ),
  80                      "<code>$constant</code>",
  81                      '<code>' . esc_html( var_export( constant( $constant ), true ) ) . '</code>'
  82                  ),
  83                  'severity'    => 'fail',
  84              );
  85          }
  86  
  87          return null;
  88      }
  89  
  90      /**
  91       * Checks if updates are intercepted by a filter.
  92       *
  93       * @since 5.2.0
  94       *
  95       * @return array|null The test results if wp_version_check() is disabled,
  96       *                    or null if the test passed.
  97       */
  98  	public function test_wp_version_check_attached() {
  99          if ( ( ! is_multisite() || is_main_site() && is_network_admin() )
 100              && ! has_filter( 'wp_version_check', 'wp_version_check' )
 101          ) {
 102              return array(
 103                  'description' => sprintf(
 104                      /* translators: %s: Name of the filter used. */
 105                      __( 'A plugin has prevented updates by disabling %s.' ),
 106                      '<code>wp_version_check()</code>'
 107                  ),
 108                  'severity'    => 'fail',
 109              );
 110          }
 111  
 112          return null;
 113      }
 114  
 115      /**
 116       * Checks if automatic updates are disabled by a filter.
 117       *
 118       * @since 5.2.0
 119       *
 120       * @return array|null The test results if the {@see 'automatic_updater_disabled'} filter is set,
 121       *                    or null if the test passed.
 122       */
 123  	public function test_filters_automatic_updater_disabled() {
 124          /** This filter is documented in wp-admin/includes/class-wp-automatic-updater.php */
 125          if ( apply_filters( 'automatic_updater_disabled', false ) ) {
 126              return array(
 127                  'description' => sprintf(
 128                      /* translators: %s: Name of the filter used. */
 129                      __( 'The %s filter is enabled.' ),
 130                      '<code>automatic_updater_disabled</code>'
 131                  ),
 132                  'severity'    => 'fail',
 133              );
 134          }
 135  
 136          return null;
 137      }
 138  
 139      /**
 140       * Checks if automatic updates are disabled.
 141       *
 142       * @since 5.3.0
 143       *
 144       * @return array|false The test results if auto-updates are disabled, false otherwise.
 145       */
 146  	public function test_wp_automatic_updates_disabled() {
 147          if ( ! class_exists( 'WP_Automatic_Updater' ) ) {
 148              require_once  ABSPATH . 'wp-admin/includes/class-wp-automatic-updater.php';
 149          }
 150  
 151          $auto_updates = new WP_Automatic_Updater();
 152  
 153          if ( ! $auto_updates->is_disabled() ) {
 154              return false;
 155          }
 156  
 157          return array(
 158              'description' => __( 'All automatic updates are disabled.' ),
 159              'severity'    => 'fail',
 160          );
 161      }
 162  
 163      /**
 164       * Checks if automatic updates have tried to run, but failed, previously.
 165       *
 166       * @since 5.2.0
 167       *
 168       * @return array|false The test results if auto-updates previously failed, false otherwise.
 169       */
 170  	public function test_if_failed_update() {
 171          $failed = get_site_option( 'auto_core_update_failed' );
 172  
 173          if ( ! $failed ) {
 174              return false;
 175          }
 176  
 177          if ( ! empty( $failed['critical'] ) ) {
 178              $description  = __( 'A previous automatic background update ended with a critical failure, so updates are now disabled.' );
 179              $description .= ' ' . __( 'You would have received an email because of this.' );
 180              $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." );
 181              $description .= ' ' . sprintf(
 182                  /* translators: %s: Code of error shown. */
 183                  __( 'The error code was %s.' ),
 184                  '<code>' . $failed['error_code'] . '</code>'
 185              );
 186              return array(
 187                  'description' => $description,
 188                  'severity'    => 'warning',
 189              );
 190          }
 191  
 192          $description = __( 'A previous automatic background update could not occur.' );
 193          if ( empty( $failed['retry'] ) ) {
 194              $description .= ' ' . __( 'You would have received an email because of this.' );
 195          }
 196  
 197          $description .= ' ' . __( 'Another attempt will be made with the next release.' );
 198          $description .= ' ' . sprintf(
 199              /* translators: %s: Code of error shown. */
 200              __( 'The error code was %s.' ),
 201              '<code>' . $failed['error_code'] . '</code>'
 202          );
 203          return array(
 204              'description' => $description,
 205              'severity'    => 'warning',
 206          );
 207      }
 208  
 209      /**
 210       * Checks if WordPress is controlled by a VCS (Git, Subversion etc).
 211       *
 212       * @since 5.2.0
 213       *
 214       * @return array The test results.
 215       */
 216  	public function test_vcs_abspath() {
 217          $context_dirs = array( ABSPATH );
 218          $vcs_dirs     = array( '.svn', '.git', '.hg', '.bzr' );
 219          $check_dirs   = array();
 220  
 221          foreach ( $context_dirs as $context_dir ) {
 222              // Walk up from $context_dir to the root.
 223              do {
 224                  $check_dirs[] = $context_dir;
 225  
 226                  // Once we've hit '/' or 'C:\', we need to stop. dirname will keep returning the input here.
 227                  if ( dirname( $context_dir ) === $context_dir ) {
 228                      break;
 229                  }
 230  
 231                  // Continue one level at a time.
 232              } while ( $context_dir = dirname( $context_dir ) );
 233          }
 234  
 235          $check_dirs = array_unique( $check_dirs );
 236          $updater    = new WP_Automatic_Updater();
 237          $checkout   = false;
 238  
 239          // Search all directories we've found for evidence of version control.
 240          foreach ( $vcs_dirs as $vcs_dir ) {
 241              foreach ( $check_dirs as $check_dir ) {
 242                  if ( ! $updater->is_allowed_dir( $check_dir ) ) {
 243                      continue;
 244                  }
 245  
 246                  $checkout = is_dir( rtrim( $check_dir, '\\/' ) . "/$vcs_dir" );
 247                  if ( $checkout ) {
 248                      break 2;
 249                  }
 250              }
 251          }
 252  
 253          /** This filter is documented in wp-admin/includes/class-wp-automatic-updater.php */
 254          if ( $checkout && ! apply_filters( 'automatic_updates_is_vcs_checkout', true, ABSPATH ) ) {
 255              return array(
 256                  'description' => sprintf(
 257                      /* translators: 1: Folder name. 2: Version control directory. 3: Filter name. */
 258                      __( 'The folder %1$s was detected as being under version control (%2$s), but the %3$s filter is allowing updates.' ),
 259                      '<code>' . $check_dir . '</code>',
 260                      "<code>$vcs_dir</code>",
 261                      '<code>automatic_updates_is_vcs_checkout</code>'
 262                  ),
 263                  'severity'    => 'info',
 264              );
 265          }
 266  
 267          if ( $checkout ) {
 268              return array(
 269                  'description' => sprintf(
 270                      /* translators: 1: Folder name. 2: Version control directory. */
 271                      __( 'The folder %1$s was detected as being under version control (%2$s).' ),
 272                      '<code>' . $check_dir . '</code>',
 273                      "<code>$vcs_dir</code>"
 274                  ),
 275                  'severity'    => 'warning',
 276              );
 277          }
 278  
 279          return array(
 280              'description' => __( 'No version control systems were detected.' ),
 281              'severity'    => 'pass',
 282          );
 283      }
 284  
 285      /**
 286       * Checks if we can access files without providing credentials.
 287       *
 288       * @since 5.2.0
 289       *
 290       * @return array The test results.
 291       */
 292  	public function test_check_wp_filesystem_method() {
 293          // Make sure the `request_filesystem_credentials()` function is available during our REST API call.
 294          if ( ! function_exists( 'request_filesystem_credentials' ) ) {
 295              require_once  ABSPATH . 'wp-admin/includes/file.php';
 296          }
 297  
 298          $skin    = new Automatic_Upgrader_Skin();
 299          $success = $skin->request_filesystem_credentials( false, ABSPATH );
 300  
 301          if ( ! $success ) {
 302              $description  = __( 'Your installation of WordPress prompts for FTP credentials to perform updates.' );
 303              $description .= ' ' . __( '(Your site is performing updates over FTP due to file ownership. Talk to your hosting company.)' );
 304  
 305              return array(
 306                  'description' => $description,
 307                  'severity'    => 'fail',
 308              );
 309          }
 310  
 311          return array(
 312              'description' => __( 'Your installation of WordPress does not require FTP credentials to perform updates.' ),
 313              'severity'    => 'pass',
 314          );
 315      }
 316  
 317      /**
 318       * Checks if core files are writable by the web user/group.
 319       *
 320       * @since 5.2.0
 321       *
 322       * @global WP_Filesystem_Base $wp_filesystem WordPress filesystem subclass.
 323       *
 324       * @return array|false The test results if at least some of WordPress core files are writeable,
 325       *                     or if a list of the checksums could not be retrieved from WordPress.org.
 326       *                     False if the core files are not writeable.
 327       */
 328  	public function test_all_files_writable() {
 329          global $wp_filesystem;
 330  
 331          require  ABSPATH . WPINC . '/version.php'; // $wp_version; // x.y.z
 332  
 333          $skin    = new Automatic_Upgrader_Skin();
 334          $success = $skin->request_filesystem_credentials( false, ABSPATH );
 335  
 336          if ( ! $success ) {
 337              return false;
 338          }
 339  
 340          WP_Filesystem();
 341  
 342          if ( 'direct' !== $wp_filesystem->method ) {
 343              return false;
 344          }
 345  
 346          // Make sure the `get_core_checksums()` function is available during our REST API call.
 347          if ( ! function_exists( 'get_core_checksums' ) ) {
 348              require_once  ABSPATH . 'wp-admin/includes/update.php';
 349          }
 350  
 351          $checksums = get_core_checksums( $wp_version, 'en_US' );
 352          $dev       = ( str_contains( $wp_version, '-' ) );
 353          // Get the last stable version's files and test against that.
 354          if ( ! $checksums && $dev ) {
 355              $checksums = get_core_checksums( (float) $wp_version - 0.1, 'en_US' );
 356          }
 357  
 358          // There aren't always checksums for development releases, so just skip the test if we still can't find any.
 359          if ( ! $checksums && $dev ) {
 360              return false;
 361          }
 362  
 363          if ( ! $checksums ) {
 364              $description = sprintf(
 365                  /* translators: %s: WordPress version. */
 366                  __( "Couldn't retrieve a list of the checksums for WordPress %s." ),
 367                  $wp_version
 368              );
 369              $description .= ' ' . __( 'This could mean that connections are failing to WordPress.org.' );
 370              return array(
 371                  'description' => $description,
 372                  'severity'    => 'warning',
 373              );
 374          }
 375  
 376          $unwritable_files = array();
 377          foreach ( array_keys( $checksums ) as $file ) {
 378              if ( str_starts_with( $file, 'wp-content' ) ) {
 379                  continue;
 380              }
 381              if ( ! file_exists( ABSPATH . $file ) ) {
 382                  continue;
 383              }
 384              if ( ! is_writable( ABSPATH . $file ) ) {
 385                  $unwritable_files[] = $file;
 386              }
 387          }
 388  
 389          if ( $unwritable_files ) {
 390              if ( count( $unwritable_files ) > 20 ) {
 391                  $unwritable_files   = array_slice( $unwritable_files, 0, 20 );
 392                  $unwritable_files[] = '...';
 393              }
 394              return array(
 395                  'description' => __( 'Some files are not writable by WordPress:' ) . ' <ul><li>' . implode( '</li><li>', $unwritable_files ) . '</li></ul>',
 396                  'severity'    => 'fail',
 397              );
 398          } else {
 399              return array(
 400                  'description' => __( 'All of your WordPress files are writable.' ),
 401                  'severity'    => 'pass',
 402              );
 403          }
 404      }
 405  
 406      /**
 407       * Checks if the install is using a development branch and can use nightly packages.
 408       *
 409       * @since 5.2.0
 410       *
 411       * @return array|false|null The test results if development updates are blocked.
 412       *                          False if it isn't a development version. Null if the test passed.
 413       */
 414  	public function test_accepts_dev_updates() {
 415          require  ABSPATH . WPINC . '/version.php'; // $wp_version; // x.y.z
 416          // Only for dev versions.
 417          if ( ! str_contains( $wp_version, '-' ) ) {
 418              return false;
 419          }
 420  
 421          if ( defined( 'WP_AUTO_UPDATE_CORE' ) && ( 'minor' === WP_AUTO_UPDATE_CORE || false === WP_AUTO_UPDATE_CORE ) ) {
 422              return array(
 423                  'description' => sprintf(
 424                      /* translators: %s: Name of the constant used. */
 425                      __( 'WordPress development updates are blocked by the %s constant.' ),
 426                      '<code>WP_AUTO_UPDATE_CORE</code>'
 427                  ),
 428                  'severity'    => 'fail',
 429              );
 430          }
 431  
 432          /** This filter is documented in wp-admin/includes/class-core-upgrader.php */
 433          if ( ! apply_filters( 'allow_dev_auto_core_updates', $wp_version ) ) {
 434              return array(
 435                  'description' => sprintf(
 436                      /* translators: %s: Name of the filter used. */
 437                      __( 'WordPress development updates are blocked by the %s filter.' ),
 438                      '<code>allow_dev_auto_core_updates</code>'
 439                  ),
 440                  'severity'    => 'fail',
 441              );
 442          }
 443  
 444          return null;
 445      }
 446  
 447      /**
 448       * Checks if the site supports automatic minor updates.
 449       *
 450       * @since 5.2.0
 451       *
 452       * @return array|null The test results if minor updates are blocked,
 453       *                    or null if the test passed.
 454       */
 455  	public function test_accepts_minor_updates() {
 456          if ( defined( 'WP_AUTO_UPDATE_CORE' ) && false === WP_AUTO_UPDATE_CORE ) {
 457              return array(
 458                  'description' => sprintf(
 459                      /* translators: %s: Name of the constant used. */
 460                      __( 'WordPress security and maintenance releases are blocked by %s.' ),
 461                      "<code>define( 'WP_AUTO_UPDATE_CORE', false );</code>"
 462                  ),
 463                  'severity'    => 'fail',
 464              );
 465          }
 466  
 467          /** This filter is documented in wp-admin/includes/class-core-upgrader.php */
 468          if ( ! apply_filters( 'allow_minor_auto_core_updates', true ) ) {
 469              return array(
 470                  'description' => sprintf(
 471                      /* translators: %s: Name of the filter used. */
 472                      __( 'WordPress security and maintenance releases are blocked by the %s filter.' ),
 473                      '<code>allow_minor_auto_core_updates</code>'
 474                  ),
 475                  'severity'    => 'fail',
 476              );
 477          }
 478  
 479          return null;
 480      }
 481  }


Generated : Tue Dec 24 08:20:01 2024 Cross-referenced by PHPXref