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


Generated : Fri Apr 26 08:20:02 2024 Cross-referenced by PHPXref