Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
54.05% covered (warning)
54.05%
20 / 37
50.00% covered (danger)
50.00%
4 / 8
CRAP
0.00% covered (danger)
0.00%
0 / 1
LCP_Invalidator
54.05% covered (warning)
54.05%
20 / 37
50.00% covered (danger)
50.00%
4 / 8
40.83
0.00% covered (danger)
0.00%
0 / 1
 init
100.00% covered (success)
100.00%
6 / 6
100.00% covered (success)
100.00%
1 / 1
1
 reset_data
0.00% covered (danger)
0.00%
0 / 5
0.00% covered (danger)
0.00%
0 / 1
2
 reset_and_analyze
0.00% covered (danger)
0.00%
0 / 2
0.00% covered (danger)
0.00%
0 / 1
2
 handle_environment_change
0.00% covered (danger)
0.00%
0 / 2
0.00% covered (danger)
0.00%
0 / 1
6
 handle_post_update
0.00% covered (danger)
0.00%
0 / 8
0.00% covered (danger)
0.00%
0 / 1
6
 handle_style_save
100.00% covered (success)
100.00%
3 / 3
100.00% covered (success)
100.00%
1 / 1
3
 schedule_style_reanalysis
100.00% covered (success)
100.00%
2 / 2
100.00% covered (success)
100.00%
1 / 1
1
 reanalyze_cornerstone_pages
100.00% covered (success)
100.00%
9 / 9
100.00% covered (success)
100.00%
1 / 1
5
1<?php
2/**
3 * LCP Invalidator
4 *
5 * Reset LCP analysis data on certain events.
6 */
7
8namespace Automattic\Jetpack_Boost\Modules\Optimizations\Lcp;
9
10use Automattic\Jetpack_Boost\Lib\Cornerstone\Cornerstone_Utils;
11
12class LCP_Invalidator {
13
14    const STYLE_REANALYSIS_HOOK = 'jetpack_boost_lcp_style_reanalysis';
15
16    /**
17     * Post types whose saves can restyle the LCP image on every cornerstone page.
18     */
19    const STYLE_POST_TYPES = array( 'wp_template', 'wp_template_part', 'wp_global_styles', 'custom_css' );
20
21    public static function init() {
22        add_action( 'jetpack_boost_deactivate', array( self::class, 'reset_data' ) );
23        add_action( 'update_option_jetpack_boost_ds_cornerstone_pages_list', array( self::class, 'reset_and_analyze' ) );
24        add_action( 'jetpack_boost_environment_changed', array( self::class, 'handle_environment_change' ) );
25        add_action( 'post_updated', array( self::class, 'handle_post_update' ) );
26        add_action( 'save_post', array( self::class, 'handle_style_save' ), 10, 2 );
27        add_action( self::STYLE_REANALYSIS_HOOK, array( self::class, 'reanalyze_cornerstone_pages' ) );
28    }
29
30    /**
31     * Reset any LCP analysis data (state and storage).
32     *
33     * @since 4.0.0
34     */
35    public static function reset_data() {
36        $state = new LCP_State();
37        $state->clear();
38
39        $storage = new LCP_Storage();
40        $storage->clear();
41
42        wp_clear_scheduled_hook( self::STYLE_REANALYSIS_HOOK );
43    }
44
45    /**
46     * Reset the LCP analysis data, and analyze the pages again.
47     *
48     * @since 4.0.0
49     */
50    public static function reset_and_analyze() {
51        self::reset_data();
52
53        /**
54         * Indicate that the latest LCP analysis data has been invalidated.
55         */
56        do_action( 'jetpack_boost_lcp_invalidated' );
57    }
58
59    /**
60     * Respond to environment changes; deciding whether or not to clear LCP analysis data.
61     *
62     * @since 4.0.0
63     */
64    public static function handle_environment_change( $is_major_change ) {
65        if ( $is_major_change ) {
66            self::reset_and_analyze();
67        }
68    }
69
70    /**
71     * Handle post updates to check if the post is a cornerstone page and schedule preload if needed.
72     *
73     * @since 4.0.0
74     * @param int $post_id The ID of the post being updated.
75     * @return void
76     */
77    public static function handle_post_update( int $post_id ) {
78        if ( Cornerstone_Utils::is_cornerstone_page( $post_id ) ) {
79            $url = get_permalink( $post_id );
80
81            $analyzer = new LCP_Analyzer();
82            $analyzer->start_partial_analysis(
83                array(
84                    Cornerstone_Utils::prepare_provider_data( $url ),
85                )
86            );
87        }
88    }
89
90    /**
91     * Schedule a re-analysis of every cornerstone page after a template or site-wide style is saved.
92     *
93     * @since $$next-version$$
94     * @param int      $post_id The ID of the saved post.
95     * @param \WP_Post $post    The saved post.
96     * @return void
97     */
98    public static function handle_style_save( $post_id, $post ) {
99        if ( 'publish' !== $post->post_status || ! in_array( $post->post_type, self::STYLE_POST_TYPES, true ) ) {
100            return;
101        }
102
103        // One Site Editor save writes several of these, and the cloud caps LCP URLs per hour, so wait for the last.
104        self::schedule_style_reanalysis();
105    }
106
107    /**
108     * Schedule the style re-analysis five minutes from now, replacing any already scheduled.
109     *
110     * @return void
111     */
112    private static function schedule_style_reanalysis() {
113        wp_clear_scheduled_hook( self::STYLE_REANALYSIS_HOOK );
114        wp_schedule_single_event( time() + 5 * MINUTE_IN_SECONDS, self::STYLE_REANALYSIS_HOOK );
115    }
116
117    /**
118     * Re-analyse every cornerstone page, serving the current results until new ones arrive.
119     *
120     * @since $$next-version$$
121     * @return void
122     */
123    public static function reanalyze_cornerstone_pages() {
124        $state = ( new LCP_State() )->get();
125        if ( empty( $state['pages'] ) ) {
126            return;
127        }
128
129        // The cloud drops a request while the site has an LCP job queued, leaving pages it doesn't cover pending.
130        // A pending state an hour old has lost its callback, so run anyway.
131        $updated = isset( $state['updated'] ) ? (float) $state['updated'] : 0.0;
132        if ( LCP_State::ANALYSIS_STATES['pending'] === ( $state['status'] ?? null ) && microtime( true ) - $updated < HOUR_IN_SECONDS ) {
133            self::schedule_style_reanalysis();
134            return;
135        }
136
137        $pages = array_map( array( Cornerstone_Utils::class, 'prepare_provider_data' ), Cornerstone_Utils::get_list() );
138
139        ( new LCP_Analyzer() )->start_partial_analysis( $pages );
140    }
141}