WordPress.org

Make WordPress Core

Opened 2 years ago

Closed 5 months ago

#46465 closed enhancement (wontfix)

Implement logic that performs PHP code after connection with client is closed

Reported by: umchal Owned by:
Milestone: Priority: normal
Severity: normal Version:
Component: General Keywords:
Focuses: performance Cc:

Description

Hi there,

It would be great if this can be implemented in the core.

When I write plugins, it is almost unavoidable to perform extra database queries like accessing a custom database table, updating rows, and scheduling WP Cron actions etc. The problem is that it affects the page load time.

I've been wondering how this can be avoided and how the impact on the performance can be made minimum from the user experience perspective.

And I came up with a little technique using header('Content-Length: n') and header('Connection: close' ). Here is a working example plugin.

<?php
/**
 * Plugin Name: Test - HTTP Connection Close
 */

class HTTPConnectionClose {

    public function __construct() {

        add_action( 'init', array( $this, 'startCapturing' ) );
        add_action( 'shutdown', array( $this, 'endCapturing' ), PHP_INT_MIN ); 

    }

    public function startCapturing() {
         ob_start();
    }

    /**
     * @remark      Should be called before `wp_ob_end_flush_all()`.
     */
    public function endCapturing() {

        $_iOutputBufferLength = ( integer ) ob_get_length();
        if ( ! headers_sent() ) {
            @header('Content-Length: ' . $_iOutputBufferLength ) ;
            @header('Connection: close' );
        }
        if ( $_iOutputBufferLength ) {
            ob_end_flush();
        }

    }

}
new HTTPConnectionClose;

Here are some examples that illustrate the benefits of this. You can add this code at the bottom of the above example plugin.

class Test_HTTPConnectionClose {

    public function __construct() {

        add_action( 'shutdown', array( $this, 'testHeavyTask' ), PHP_INT_MAX );
        add_action( 'plugins_loaded', array( $this, 'testWPCronCheck' ) );
        add_action( 'shutdown', array( $this, 'testWPCronSchedule' ) );
        add_action( 'test_custom_action', array( $this, 'testCustomCronTask' ) );

    }

    public function testCustomCronTask() {
        error_log( __METHOD__  . ': doing custom cron task.' );
    }

    /**
     * This causes a couple of database queries. But with the above technique, this will not be seen during the page load.
     */
    public function testWPCronSchedule() {
        $_aArguments  = array();
        $_sActionName = 'test_custom_action';
        if ( wp_next_scheduled( $_sActionName, $_aArguments ) ) {
            return;
        }
        wp_schedule_single_event( time() + 5, $_sActionName, $_aArguments );
    }

    /**
     * Checks registered WP Cron actions in the `shutdown` instead of `init` action
     * to save a little load time in every single page load.
     */
    public function testWPCronCheck() {
        remove_action( 'init', 'wp_cron' );
        if ( ! defined( 'DOING_CRON' ) ) {
                add_action( 'shutdown', 'wp_cron' );
        }
    }

    /**
     * Simulates a heavy task which consumes 10 seconds. But with the above technique, the visitor do not feel any delay caused by this.
     */
    public function testHeavyTask() {
        sleep( 10 ); // sleep 10 seconds
        error_log( 'done a heavy task which takes 10 seconds.' );
    }

}
new Test_HTTPConnectionClose;

As you can see with the above testHeavyTask() method, any heavy sub-routines can be done in the shutdown action. Say, your plugin generates outputs and has a caching mechanism that schedules a task and calls wp-cron.php when the output is expired. With this, even scheduling a WP Cron task will be no longer needed. The plugin can update the cache in the shutdown action within the page load. This saves extra page load and also helps avoid PHP timeout issues in wp-cron.php due to too many registered WP Cron actions. Needless to say, this helps for sites disabling WP Cron.

It's very short and should be technically feasible to implement. Not sure any side effects with this yet but maybe you guys can tell.

Change History (3)

#1 @sabernhardt
6 months ago

  • Focuses performance added

This ticket was mentioned in Slack in #core by peterwilsoncc. View the logs.


5 months ago

#3 @peterwilsoncc
5 months ago

  • Milestone Awaiting Review deleted
  • Resolution set to wontfix
  • Status changed from new to closed

Hi @umchal and welcome to trac!

This ticket was discussed during a triage session today.

It was decided to close this ticket off and leave changes similar to your example code as plugin territory. While in many cases considering the site "sent" once the shutdown hook starts firing is safe, it's not guaranteed.

In wp-cron.php WordPress uses fastcgi_finish_request() when possible on cron requests, you may wish to look at that with regard to a plugin.

I'm going to close this off as wontfix, which is the term used on trac for tickets that won't have action taken on them.

Thanks

Note: See TracTickets for help on using tickets.