Make WordPress Core

Opened 6 weeks ago

Last modified 6 weeks ago

#61114 new defect (bug)

PHP 8+ Fatal Error in WP_Upgrader due to TypeError in array_keys() Call

Reported by: verygoode's profile verygoode Owned by:
Milestone: Awaiting Review Priority: normal
Severity: normal Version: 2.8
Component: Upgrade/Install Keywords: needs-patch
Focuses: Cc:

Description

Aa PHP 8+ Fatal error occurs in multiple instances within class-wp-upgrader.php when array_keys() is called with a boolean argument instead of an array. This error is triggered under conditions where $wp_filesystem->dirlist() fails to read a directory and returns false.

This often leads to the plugins/theme being missing from the site.

It appears the error stems from the function $wp_filesystem->dirlist() returning false -- likely due to a previously reported and unfixed bug where the upgrader deletes files related to other in-progress upgrades as reported by @bpayton in https://core.trac.wordpress.org/ticket/53705

Errors Observed

As of 6.5.2, the TypeError occurs in the install_package() method at the following lines when attempting to handle plugin, theme, or language pack upgrades or installations:

PHP Fatal error:  Uncaught TypeError: array_keys(): Argument #1 ($array) must be of type array, bool given in /wordpress/core/6.5.2/wp-admin/includes/class-wp-upgrader.php:556
Stack trace:
#0 /wordpress/core/6.5.2/wp-admin/includes/class-wp-upgrader.php(556): array_keys(false)
#1 /wordpress/core/6.5.2/wp-admin/includes/class-wp-upgrader.php(883): WP_Upgrader->install_package(Array)
#2 /wordpress/core/6.5.2/wp-admin/includes/class-plugin-upgrader.php(137): WP_Upgrader->run(Array)
#3 /srv/htdocs/wp-content/themes/hello-shoppable/inc/getting-started/getting-started.php(193): Plugin_Upgrader->install('https://downloa...')
#4 /wordpress/core/6.5.2/wp-includes/class-wp-hook.php(324): Hello_Shoppable_Notice_Handler->hello_shoppable_getting_started('')
#5 /wordpress/core/6.5.2/wp-includes/class-wp-hook.php(348): WP_Hook->apply_filters('', Array)
#6 /wordpress/core/6.5.2/wp-includes/plugin.php(517): WP_Hook->do_action(Array)
#7 /wordpress/core/6.5.2/wp-admin/admin-ajax.php(192): do_action('wp_ajax_hello_s...')
#8 {main}
  thrown in /wordpress/core/6.5.2/wp-admin/includes/class-wp-upgrader.php on line 556
PHP Fatal error:  Uncaught TypeError: array_keys(): Argument #1 ($array) must be of type array, bool given in /wordpress/core/6.5.2/wp-admin/includes/class-wp-upgrader.php:556
Stack trace:
#0 /wordpress/core/6.5.2/wp-admin/includes/class-wp-upgrader.php(556): array_keys(false)
#1 /wordpress/core/6.5.2/wp-admin/includes/class-wp-upgrader.php(883): WP_Upgrader->install_package(Array)
#2 /wordpress/core/6.5.2/wp-admin/includes/class-plugin-upgrader.php(237): WP_Upgrader->run(Array)
#3 /srv/htdocs/wp-content/plugins/elementor/includes/rollback.php(171): Plugin_Upgrader->upgrade('elementor/eleme...')
#4 /srv/htdocs/wp-content/plugins/elementor/includes/rollback.php(184): Elementor\Rollback->upgrade()
#5 /srv/htdocs/wp-content/plugins/elementor/includes/settings/tools.php(172): Elementor\Rollback->run()
#6 /wordpress/core/6.5.2/wp-includes/class-wp-hook.php(324): Elementor\Tools->post_elementor_rollback('')
#7 /wordpress/core/6.5.2/wp-includes/class-wp-hook.php(348): WP_Hook->apply_filters('', Array)
#8 /wordpress/core/6.5.2/wp-includes/plugin.php(517): WP_Hook->do_action(Array)
#9 /wordpress/core/6.5.2/wp-admin/admin-post.php(85): do_action('admin_post_elem...')
#10 {main}
  thrown in /wordpress/core/6.5.2/wp-admin/includes/class-wp-upgrader.php on line 556

Steps to Reproduce

  1. Attempt to install or upgrade a plugin, theme, or language pack on a WordPress installation running under PHP 8.0 or later.
  2. Ensure that the $wp_filesystem->dirlist() function fails to return a directory list (can be simulated by providing a non-existent source directory via deletion or other methods).
  3. Observe the fatal error logs as the install_package() method fails due to array_keys() receiving false.

Expected Behavior

While this could likely be alleviated by resolving https://core.trac.wordpress.org/ticket/53705 , the install_package() method should handle cases where the directory listing is unavailable (i.e., when $wp_filesystem->dirlist() returns false) without resulting in a fatal error. Appropriate error handling should be in place to manage such failures gracefully.

Actual Behavior

Actual Behavior:
When $wp_filesystem->dirlist() returns false, the absence of a check before the array_keys() function call leads to a fatal error due to a type mismatch (boolean given instead of array).

Change History (9)

#1 @verygoode
6 weeks ago

Error for line 603.

PHP Fatal error:  Uncaught TypeError: array_keys(): Argument #1 ($array) must be of type array, false given in /wordpress/core/6.5.2/wp-admin/includes/class-wp-upgrader.php:603
Stack trace:
#0 /wordpress/core/6.5.2/wp-admin/includes/class-wp-upgrader.php(603): array_keys(false)
#1 /wordpress/core/6.5.2/wp-admin/includes/class-wp-upgrader.php(876): WP_Upgrader->install_package(Array)
#2 /wordpress/core/6.5.2/wp-admin/includes/class-plugin-upgrader.php(380): WP_Upgrader->run(Array)
#3 /wordpress/core/6.5.2/wp-admin/includes/ajax-actions.php(4645): Plugin_Upgrader->bulk_upgrade(Array)
#4 /wordpress/core/6.5.2/wp-includes/class-wp-hook.php(324): wp_ajax_update_plugin('')
#5 /wordpress/core/6.5.2/wp-includes/class-wp-hook.php(348): WP_Hook->apply_filters('', Array)
#6 /wordpress/core/6.5.2/wp-includes/plugin.php(517): WP_Hook->do_action(Array)
#7 /wordpress/core/6.5.2/wp-admin/admin-ajax.php(192): do_action('wp_ajax_update-...')
#8 {main}
  thrown in /wordpress/core/6.5.2/wp-admin/includes/class-wp-upgrader.php on line 603

#2 @swissspidy
6 weeks ago

  • Keywords needs-patch added
  • Severity changed from major to normal
  • Version changed from 6.5 to 2.8

This ticket was mentioned in Slack in #core-upgrade-install by afragen. View the logs.


6 weeks ago

#4 @afragen
6 weeks ago

What PHP version is this on? We may need to tag that too.

#5 @afragen
6 weeks ago

This is the line.

https://github.com/WordPress/wordpress-develop/blob/2f2dbbf24671f1aa12d013f7929491059055d6bf/src/wp-admin/includes/class-wp-upgrader.php#L607

So perhaps casting to an array?

$source_files = array_keys( (array) $wp_filesystem->dirlist( $source ) );

#6 @verygoode
6 weeks ago

@afragen PHP 8.1 was the primary version I've seen but this can occur on any current PHP version ≥8

#7 @afragen
6 weeks ago

Thanks @verygoode that confirms my suspicions. The above casting should fix the issue, can you test and report back?

#8 @verygoode
6 weeks ago

A quick way to reproduce is to spin up a site, set one or more plugins to a previous version like wp plugin install hello-dolly --version=1.6 --force

Then, run multiple requests at the same time to wp plugin update hello-dolly and/or other plugin updates -- simulating a situation where multiple events are firing off to update something and leading to failures like reported on #53705

Without the casting, we can observe the fatal.

wp plugin update hello-dolly
Enabling Maintenance mode...
Downloading update from https://downloads.wordpress.org/plugin/hello-dolly.1.7.3.zip...
Unpacking the update...
Installing the latest version...
Error: There has been a critical error on this website.Learn more about troubleshooting WordPress. There has been a critical error on this website.

With your casting recommendation, this fails without a fatal @afragen

wp plugin update hello-dolly
Enabling Maintenance mode...
Downloading update from https://downloads.wordpress.org/plugin/hello-dolly.1.7.3.zip
Unpacking the update...
Installing the latest version...
Warning: Could not move the old version to the upgrade-temp-backup directory.
Plugin update failed.
Disabling Maintenance mode...
+-------------+-------------+-------------+--------+
| name        | old_version | new_version | status |
+-------------+-------------+-------------+--------+
| hello-dolly | 1.6         | 1.7.2       | Error  |
+-------------+-------------+-------------+--------+
Error: No plugins updated (1 failed).
Last edited 6 weeks ago by verygoode (previous) (diff)

#9 @verygoode
6 weeks ago

With the casting, without a fatal, a warning may also be observed in WP-CLI

Warning: Directory listing failed. "hello-dolly"

The following warning is also logged in some cases, given that other events are clearing out the upgrade folder.

[02-May-2024 19:28:03 UTC] PHP Warning:  dir(/Users/test/Local Sites/testing-upgrader/app/public/wp-content/upgrade/woocommerce.8.8.3-6ZaMui/woocommerce/src/Internal/Admin/Onboarding/): 
Failed to open directory: No such file or directory in /Users/test/Local Sites/testing-upgrader/app/public/wp-admin/includes/class-wp-filesystem-direct.php on line 636
Note: See TracTickets for help on using tickets.