Make WordPress Core

Opened 25 hours ago

Last modified 18 hours ago

#65543 new enhancement

`Plugin_Upgrader::install()` deletes freshly rebuilt `update_plugins` transient after `upgrader_process_complete`

Reported by: pentatonicfunk's profile pentatonicfunk Owned by:
Milestone: Awaiting Review Priority: normal
Severity: normal Version: 4.0
Component: Upgrade/Install Keywords: needs-dev-feedback
Focuses: performance Cc:

Description

On Plugin_Upgrader::install() flow, WordPress appears to rebuild the update_plugins site transient during upgrader_process_complete, but then deletes it again in the same request.

The sequence seems to be:

Plugin_Upgrader::install()
  WP_Upgrader->run()
    do_action( 'upgrader_process_complete' )
      priority 9:  wp_clean_plugins_cache()
                   delete_site_transient( 'update_plugins' )

      priority 10: wp_update_plugins()
                   set_site_transient( 'update_plugins', ... )

  wp_clean_plugins_cache( true )
    delete_site_transient( 'update_plugins' )

So, during the upgrader_process_complete hook, update_plugins is refreshed (cleaned and rebuilt) as expected. But immediately after WP_Upgrader->run() returns, Plugin_Upgrader::install() calls wp_clean_plugins_cache( true ) again, which deletes the just-rebuilt transient.

That means code running later in the same request, for example on shutdown, can see false transient:

get_site_transient( 'update_plugins' ); // false returned

This also seems inefficient: the request can spend expensive compute effort for wp_update_plugins, due to HTTP remote, only to be discarded afterwards.

It maybe intentional cache invalidation, but the current order makes the update check result unavailable to later same-request getter and appears to waste the freshly rebuilt transient.

PoC mirroring wp-admin plugins install ajax

<?php
require_once ABSPATH . 'wp-admin/includes/plugin-install.php';
require_once ABSPATH . 'wp-admin/includes/class-wp-upgrader.php';
require_once ABSPATH . 'wp-admin/includes/class-plugin-upgrader.php';
require_once ABSPATH . 'wp-admin/includes/file.php';
require_once ABSPATH . 'wp-admin/includes/misc.php';
require_once ABSPATH . 'wp-admin/includes/plugin.php';
require_once ABSPATH . 'wp-admin/includes/update.php';
require_once ABSPATH . 'wp-admin/includes/admin-filters.php';


$install_plugin_url = 'https://downloads.wordpress.org/plugin/health-check.1.7.1.zip';

$http_requests_made = [];
add_filter(
        'http_request_args',
        function ( $request_args, $url ) use ( &$http_requests_made ) {
                $http_requests_made[] = $url;

                return $request_args;
        },
        10,
        2
);

printf(
        "transient before install: %s - %s\n",
        esc_html( gettype( get_site_transient( 'update_plugins' ) ) ),
        wp_json_encode( get_site_transient( 'update_plugins' ) )
);

$upgrader_process_complete_output = '';

add_action(
        'upgrader_process_complete',
        function () use ( &$upgrader_process_complete_output ) {
                $upgrader_process_complete_output = sprintf(
                        "transient at upgrader_process_complete: %s - %s\n",
                        esc_html( gettype( get_site_transient( 'update_plugins' ) ) ),
                        wp_json_encode( get_site_transient( 'update_plugins' ) )
                );
        },
        999
);

printf( "installing %s\n", esc_html( $install_plugin_url ) );
$upgrader = new Plugin_Upgrader( new WP_Ajax_Upgrader_Skin() );
$result   = $upgrader->install( $install_plugin_url );
printf( "install result: %s\n", wp_json_encode( $result ) );

echo $upgrader_process_complete_output; // phpcs:ignore

printf(
        "transient after install: %s - %s\n",
        esc_html( gettype( get_site_transient( 'update_plugins' ) ) ),
        wp_json_encode( get_site_transient( 'update_plugins' ) )
);

printf( "http requests made: %s\n", wp_json_encode( $http_requests_made ) );

Output:

transient before install: object - {"last_checked":1782413777,"response":{"jetpack\/jetpack.php":...

installing https://downloads.wordpress.org/plugin/health-check.1.7.1.zip

install result: true

transient at upgrader_process_complete: object - {"last_checked":1782413789,"response":{"jetpack\/jetpack.php":...

transient after install: boolean - false

http requests made: ["https:\/\/downloads.wordpress.org\/plugin\/health-check.1.7.1.zip","https:\/\/api.wordpress.org\/plugins\/update-check\/1.1\/",...


  • before install it correctly having existing transient
  • when upgrader_process_complete triggered, it correctly "rebuilt" the transient ( see the last_checked )
  • after install, transient wiped to false
  • wasted http requests made to https://api.wordpress.org/plugins/update-check/1.1/

PS: maybe want to check the same thing on Theme area.

Change History (1)

#1 @khokansardar
18 hours ago

  • Focuses performance added
  • Keywords needs-dev-feedback added
  • Version set to 4.0
Note: See TracTickets for help on using tickets.