8 */ |
8 */ |
9 |
9 |
10 class WP_Site_Health_Auto_Updates { |
10 class WP_Site_Health_Auto_Updates { |
11 /** |
11 /** |
12 * WP_Site_Health_Auto_Updates constructor. |
12 * WP_Site_Health_Auto_Updates constructor. |
|
13 * |
13 * @since 5.2.0 |
14 * @since 5.2.0 |
14 */ |
15 */ |
15 public function __construct() { |
16 public function __construct() { |
16 include_once ABSPATH . 'wp-admin/includes/class-wp-upgrader.php'; |
17 require_once ABSPATH . 'wp-admin/includes/class-wp-upgrader.php'; |
17 } |
18 } |
18 |
19 |
19 |
20 |
20 /** |
21 /** |
21 * Run tests to determine if auto-updates can run. |
22 * Run tests to determine if auto-updates can run. |
24 * |
25 * |
25 * @return array The test results. |
26 * @return array The test results. |
26 */ |
27 */ |
27 public function run_tests() { |
28 public function run_tests() { |
28 $tests = array( |
29 $tests = array( |
29 $this->test_constants( 'DISALLOW_FILE_MODS', false ), |
30 $this->test_constants( 'WP_AUTO_UPDATE_CORE', array( true, 'minor' ) ), |
30 $this->test_constants( 'AUTOMATIC_UPDATER_DISABLED', false ), |
|
31 $this->test_constants( 'WP_AUTO_UPDATE_CORE', true ), |
|
32 $this->test_wp_version_check_attached(), |
31 $this->test_wp_version_check_attached(), |
33 $this->test_filters_automatic_updater_disabled(), |
32 $this->test_filters_automatic_updater_disabled(), |
|
33 $this->test_wp_automatic_updates_disabled(), |
34 $this->test_if_failed_update(), |
34 $this->test_if_failed_update(), |
35 $this->test_vcs_abspath(), |
35 $this->test_vcs_abspath(), |
36 $this->test_check_wp_filesystem_method(), |
36 $this->test_check_wp_filesystem_method(), |
37 $this->test_all_files_writable(), |
37 $this->test_all_files_writable(), |
38 $this->test_accepts_dev_updates(), |
38 $this->test_accepts_dev_updates(), |
58 |
58 |
59 /** |
59 /** |
60 * Test if auto-updates related constants are set correctly. |
60 * Test if auto-updates related constants are set correctly. |
61 * |
61 * |
62 * @since 5.2.0 |
62 * @since 5.2.0 |
63 * |
63 * @since 5.5.1 The `$value` parameter can accept an array. |
64 * @param string $constant The name of the constant to check. |
64 * |
65 * @param bool $value The value that the constant should be, if set. |
65 * @param string $constant The name of the constant to check. |
|
66 * @param bool|string|array $value The value that the constant should be, if set, |
|
67 * or an array of acceptable values. |
66 * @return array The test results. |
68 * @return array The test results. |
67 */ |
69 */ |
68 public function test_constants( $constant, $value ) { |
70 public function test_constants( $constant, $value ) { |
69 if ( defined( $constant ) && constant( $constant ) != $value ) { |
71 $acceptable_values = (array) $value; |
|
72 |
|
73 if ( defined( $constant ) && ! in_array( constant( $constant ), $acceptable_values, true ) ) { |
70 return array( |
74 return array( |
71 'description' => sprintf( |
75 'description' => sprintf( |
72 /* translators: %s: Name of the constant used. */ |
76 /* translators: %s: Name of the constant used. */ |
73 __( 'The %s constant is defined and enabled.' ), |
77 __( 'The %s constant is defined and enabled.' ), |
74 "<code>$constant</code>" |
78 "<code>$constant</code>" |
93 $cookies = wp_unslash( $_COOKIE ); |
97 $cookies = wp_unslash( $_COOKIE ); |
94 $timeout = 10; |
98 $timeout = 10; |
95 $headers = array( |
99 $headers = array( |
96 'Cache-Control' => 'no-cache', |
100 'Cache-Control' => 'no-cache', |
97 ); |
101 ); |
|
102 /** This filter is documented in wp-includes/class-wp-http-streams.php */ |
|
103 $sslverify = apply_filters( 'https_local_ssl_verify', false ); |
98 |
104 |
99 // Include Basic auth in loopback requests. |
105 // Include Basic auth in loopback requests. |
100 if ( isset( $_SERVER['PHP_AUTH_USER'] ) && isset( $_SERVER['PHP_AUTH_PW'] ) ) { |
106 if ( isset( $_SERVER['PHP_AUTH_USER'] ) && isset( $_SERVER['PHP_AUTH_PW'] ) ) { |
101 $headers['Authorization'] = 'Basic ' . base64_encode( wp_unslash( $_SERVER['PHP_AUTH_USER'] ) . ':' . wp_unslash( $_SERVER['PHP_AUTH_PW'] ) ); |
107 $headers['Authorization'] = 'Basic ' . base64_encode( wp_unslash( $_SERVER['PHP_AUTH_USER'] ) . ':' . wp_unslash( $_SERVER['PHP_AUTH_PW'] ) ); |
102 } |
108 } |
106 'health-check-test-wp_version_check' => true, |
112 'health-check-test-wp_version_check' => true, |
107 ), |
113 ), |
108 admin_url( 'site-health.php' ) |
114 admin_url( 'site-health.php' ) |
109 ); |
115 ); |
110 |
116 |
111 $test = wp_remote_get( $url, compact( 'cookies', 'headers', 'timeout' ) ); |
117 $test = wp_remote_get( $url, compact( 'cookies', 'headers', 'timeout', 'sslverify' ) ); |
112 |
118 |
113 if ( is_wp_error( $test ) ) { |
119 if ( is_wp_error( $test ) ) { |
114 return array( |
120 return array( |
115 'description' => sprintf( |
121 'description' => sprintf( |
116 /* translators: %s: Name of the filter used. */ |
122 /* translators: %s: Name of the filter used. */ |
141 * @since 5.2.0 |
147 * @since 5.2.0 |
142 * |
148 * |
143 * @return array The test results. |
149 * @return array The test results. |
144 */ |
150 */ |
145 public function test_filters_automatic_updater_disabled() { |
151 public function test_filters_automatic_updater_disabled() { |
|
152 /** This filter is documented in wp-admin/includes/class-wp-automatic-updater.php */ |
146 if ( apply_filters( 'automatic_updater_disabled', false ) ) { |
153 if ( apply_filters( 'automatic_updater_disabled', false ) ) { |
147 return array( |
154 return array( |
148 'description' => sprintf( |
155 'description' => sprintf( |
149 /* translators: %s: Name of the filter used. */ |
156 /* translators: %s: Name of the filter used. */ |
150 __( 'The %s filter is enabled.' ), |
157 __( 'The %s filter is enabled.' ), |
154 ); |
161 ); |
155 } |
162 } |
156 } |
163 } |
157 |
164 |
158 /** |
165 /** |
|
166 * Check if automatic updates are disabled. |
|
167 * |
|
168 * @since 5.3.0 |
|
169 * |
|
170 * @return array|bool The test results. False if auto-updates are enabled. |
|
171 */ |
|
172 public function test_wp_automatic_updates_disabled() { |
|
173 if ( ! class_exists( 'WP_Automatic_Updater' ) ) { |
|
174 require_once ABSPATH . 'wp-admin/includes/class-wp-automatic-updater.php'; |
|
175 } |
|
176 |
|
177 $auto_updates = new WP_Automatic_Updater(); |
|
178 |
|
179 if ( ! $auto_updates->is_disabled() ) { |
|
180 return false; |
|
181 } |
|
182 |
|
183 return array( |
|
184 'description' => __( 'All automatic updates are disabled.' ), |
|
185 'severity' => 'fail', |
|
186 ); |
|
187 } |
|
188 |
|
189 /** |
159 * Check if automatic updates have tried to run, but failed, previously. |
190 * Check if automatic updates have tried to run, but failed, previously. |
160 * |
191 * |
161 * @since 5.2.0 |
192 * @since 5.2.0 |
162 * |
193 * |
163 * @return array|bool The test results. false if the auto updates failed. |
194 * @return array|bool The test results. False if the auto-updates failed. |
164 */ |
195 */ |
165 function test_if_failed_update() { |
196 function test_if_failed_update() { |
166 $failed = get_site_option( 'auto_core_update_failed' ); |
197 $failed = get_site_option( 'auto_core_update_failed' ); |
167 |
198 |
168 if ( ! $failed ) { |
199 if ( ! $failed ) { |
217 // Walk up from $context_dir to the root. |
248 // Walk up from $context_dir to the root. |
218 do { |
249 do { |
219 $check_dirs[] = $context_dir; |
250 $check_dirs[] = $context_dir; |
220 |
251 |
221 // Once we've hit '/' or 'C:\', we need to stop. dirname will keep returning the input here. |
252 // Once we've hit '/' or 'C:\', we need to stop. dirname will keep returning the input here. |
222 if ( dirname( $context_dir ) == $context_dir ) { |
253 if ( dirname( $context_dir ) === $context_dir ) { |
223 break; |
254 break; |
224 } |
255 } |
225 |
256 |
226 // Continue one level at a time. |
257 // Continue one level at a time. |
227 } while ( $context_dir = dirname( $context_dir ) ); |
258 } while ( $context_dir = dirname( $context_dir ) ); |
237 break 2; |
268 break 2; |
238 } |
269 } |
239 } |
270 } |
240 } |
271 } |
241 |
272 |
|
273 /** This filter is documented in wp-admin/includes/class-wp-automatic-updater.php */ |
242 if ( $checkout && ! apply_filters( 'automatic_updates_is_vcs_checkout', true, ABSPATH ) ) { |
274 if ( $checkout && ! apply_filters( 'automatic_updates_is_vcs_checkout', true, ABSPATH ) ) { |
243 return array( |
275 return array( |
244 'description' => sprintf( |
276 'description' => sprintf( |
245 // translators: 1: Folder name. 2: Version control directory. 3: Filter name. |
277 /* translators: 1: Folder name. 2: Version control directory. 3: Filter name. */ |
246 __( 'The folder %1$s was detected as being under version control (%2$s), but the %3$s filter is allowing updates.' ), |
278 __( 'The folder %1$s was detected as being under version control (%2$s), but the %3$s filter is allowing updates.' ), |
247 '<code>' . $check_dir . '</code>', |
279 '<code>' . $check_dir . '</code>', |
248 "<code>$vcs_dir</code>", |
280 "<code>$vcs_dir</code>", |
249 '<code>automatic_updates_is_vcs_checkout</code>' |
281 '<code>automatic_updates_is_vcs_checkout</code>' |
250 ), |
282 ), |
253 } |
285 } |
254 |
286 |
255 if ( $checkout ) { |
287 if ( $checkout ) { |
256 return array( |
288 return array( |
257 'description' => sprintf( |
289 'description' => sprintf( |
258 // translators: 1: Folder name. 2: Version control directory. |
290 /* translators: 1: Folder name. 2: Version control directory. */ |
259 __( 'The folder %1$s was detected as being under version control (%2$s).' ), |
291 __( 'The folder %1$s was detected as being under version control (%2$s).' ), |
260 '<code>' . $check_dir . '</code>', |
292 '<code>' . $check_dir . '</code>', |
261 "<code>$vcs_dir</code>" |
293 "<code>$vcs_dir</code>" |
262 ), |
294 ), |
263 'severity' => 'fail', |
295 'severity' => 'warning', |
264 ); |
296 ); |
265 } |
297 } |
266 |
298 |
267 return array( |
299 return array( |
268 'description' => __( 'No version control systems were detected.' ), |
300 'description' => __( 'No version control systems were detected.' ), |
302 * |
334 * |
303 * @since 5.2.0 |
335 * @since 5.2.0 |
304 * |
336 * |
305 * @global WP_Filesystem_Base $wp_filesystem WordPress filesystem subclass. |
337 * @global WP_Filesystem_Base $wp_filesystem WordPress filesystem subclass. |
306 * |
338 * |
307 * @return array|bool The test results. false if they're not writeable. |
339 * @return array|bool The test results. False if they're not writeable. |
308 */ |
340 */ |
309 function test_all_files_writable() { |
341 function test_all_files_writable() { |
310 global $wp_filesystem; |
342 global $wp_filesystem; |
311 |
343 |
312 include ABSPATH . WPINC . '/version.php'; // $wp_version; // x.y.z |
344 require ABSPATH . WPINC . '/version.php'; // $wp_version; // x.y.z |
313 |
345 |
314 $skin = new Automatic_Upgrader_Skin; |
346 $skin = new Automatic_Upgrader_Skin; |
315 $success = $skin->request_filesystem_credentials( false, ABSPATH ); |
347 $success = $skin->request_filesystem_credentials( false, ABSPATH ); |
316 |
348 |
317 if ( ! $success ) { |
349 if ( ! $success ) { |
318 return false; |
350 return false; |
319 } |
351 } |
320 |
352 |
321 WP_Filesystem(); |
353 WP_Filesystem(); |
322 |
354 |
323 if ( 'direct' != $wp_filesystem->method ) { |
355 if ( 'direct' !== $wp_filesystem->method ) { |
324 return false; |
356 return false; |
325 } |
357 } |
326 |
358 |
327 $checksums = get_core_checksums( $wp_version, 'en_US' ); |
359 $checksums = get_core_checksums( $wp_version, 'en_US' ); |
328 $dev = ( false !== strpos( $wp_version, '-' ) ); |
360 $dev = ( false !== strpos( $wp_version, '-' ) ); |
329 // Get the last stable version's files and test against that |
361 // Get the last stable version's files and test against that. |
330 if ( ! $checksums && $dev ) { |
362 if ( ! $checksums && $dev ) { |
331 $checksums = get_core_checksums( (float) $wp_version - 0.1, 'en_US' ); |
363 $checksums = get_core_checksums( (float) $wp_version - 0.1, 'en_US' ); |
332 } |
364 } |
333 |
365 |
334 // There aren't always checksums for development releases, so just skip the test if we still can't find any |
366 // There aren't always checksums for development releases, so just skip the test if we still can't find any. |
335 if ( ! $checksums && $dev ) { |
367 if ( ! $checksums && $dev ) { |
336 return false; |
368 return false; |
337 } |
369 } |
338 |
370 |
339 if ( ! $checksums ) { |
371 if ( ! $checksums ) { |
340 $description = sprintf( |
372 $description = sprintf( |
341 // translators: %s: WordPress version |
373 /* translators: %s: WordPress version. */ |
342 __( "Couldn't retrieve a list of the checksums for WordPress %s." ), |
374 __( "Couldn't retrieve a list of the checksums for WordPress %s." ), |
343 $wp_version |
375 $wp_version |
344 ); |
376 ); |
345 $description .= ' ' . __( 'This could mean that connections are failing to WordPress.org.' ); |
377 $description .= ' ' . __( 'This could mean that connections are failing to WordPress.org.' ); |
346 return array( |
378 return array( |
382 /** |
414 /** |
383 * Check if the install is using a development branch and can use nightly packages. |
415 * Check if the install is using a development branch and can use nightly packages. |
384 * |
416 * |
385 * @since 5.2.0 |
417 * @since 5.2.0 |
386 * |
418 * |
387 * @return array|bool The test results. false if it isn't a development version. |
419 * @return array|bool The test results. False if it isn't a development version. |
388 */ |
420 */ |
389 function test_accepts_dev_updates() { |
421 function test_accepts_dev_updates() { |
390 include ABSPATH . WPINC . '/version.php'; // $wp_version; // x.y.z |
422 require ABSPATH . WPINC . '/version.php'; // $wp_version; // x.y.z |
391 // Only for dev versions |
423 // Only for dev versions. |
392 if ( false === strpos( $wp_version, '-' ) ) { |
424 if ( false === strpos( $wp_version, '-' ) ) { |
393 return false; |
425 return false; |
394 } |
426 } |
395 |
427 |
396 if ( defined( 'WP_AUTO_UPDATE_CORE' ) && ( 'minor' === WP_AUTO_UPDATE_CORE || false === WP_AUTO_UPDATE_CORE ) ) { |
428 if ( defined( 'WP_AUTO_UPDATE_CORE' ) && ( 'minor' === WP_AUTO_UPDATE_CORE || false === WP_AUTO_UPDATE_CORE ) ) { |
402 ), |
434 ), |
403 'severity' => 'fail', |
435 'severity' => 'fail', |
404 ); |
436 ); |
405 } |
437 } |
406 |
438 |
|
439 /** This filter is documented in wp-admin/includes/class-core-upgrader.php */ |
407 if ( ! apply_filters( 'allow_dev_auto_core_updates', $wp_version ) ) { |
440 if ( ! apply_filters( 'allow_dev_auto_core_updates', $wp_version ) ) { |
408 return array( |
441 return array( |
409 'description' => sprintf( |
442 'description' => sprintf( |
410 /* translators: %s: Name of the filter used. */ |
443 /* translators: %s: Name of the filter used. */ |
411 __( 'WordPress development updates are blocked by the %s filter.' ), |
444 __( 'WordPress development updates are blocked by the %s filter.' ), |
433 ), |
466 ), |
434 'severity' => 'fail', |
467 'severity' => 'fail', |
435 ); |
468 ); |
436 } |
469 } |
437 |
470 |
|
471 /** This filter is documented in wp-admin/includes/class-core-upgrader.php */ |
438 if ( ! apply_filters( 'allow_minor_auto_core_updates', true ) ) { |
472 if ( ! apply_filters( 'allow_minor_auto_core_updates', true ) ) { |
439 return array( |
473 return array( |
440 'description' => sprintf( |
474 'description' => sprintf( |
441 /* translators: %s: Name of the filter used. */ |
475 /* translators: %s: Name of the filter used. */ |
442 __( 'WordPress security and maintenance releases are blocked by the %s filter.' ), |
476 __( 'WordPress security and maintenance releases are blocked by the %s filter.' ), |