PluginProbe
Yoast SEO – Advanced SEO with real-time guidance and built-in AI / 28.5
Yoast SEO – Advanced SEO with real-time guidance and built-in AI v28.5
28.5 28.4 28.3 28.2 28.1 28.0 27.9 27.8 27.7 27.6 27.5 trunk 18.0 18.1 18.2 18.3 18.4 18.4.1 18.5 18.5.1 18.6 18.7 18.8 18.9 19.0 All 129 releases
← All changes | src/integrations/admin/background-indexing-integration.php +232 -78 18.728.5 View file →
@@ -1,18 +1,15 @@
1 1 <?php
2 2
3 3 namespace Yoast\WP\SEO\Integrations\Admin;
4 4
5 -use Yoast\WP\SEO\Actions\Indexing\Indexable_General_Indexation_Action;
6 5 use Yoast\WP\SEO\Actions\Indexing\Indexable_Indexing_Complete_Action;
7 -use Yoast\WP\SEO\Actions\Indexing\Indexable_Post_Indexation_Action;
8 -use Yoast\WP\SEO\Actions\Indexing\Indexable_Post_Type_Archive_Indexation_Action;
9 -use Yoast\WP\SEO\Actions\Indexing\Indexable_Term_Indexation_Action;
10 -use Yoast\WP\SEO\Actions\Indexing\Post_Link_Indexing_Action;
11 -use Yoast\WP\SEO\Actions\Indexing\Term_Link_Indexing_Action;
6 +use Yoast\WP\SEO\Actions\Indexing\Indexation_Action_Interface;
12 7 use Yoast\WP\SEO\Conditionals\Get_Request_Conditional;
13 8 use Yoast\WP\SEO\Conditionals\Migrations_Conditional;
9 +use Yoast\WP\SEO\Conditionals\WP_CRON_Enabled_Conditional;
14 10 use Yoast\WP\SEO\Conditionals\Yoast_Admin_And_Dashboard_Conditional;
11 +use Yoast\WP\SEO\Helpers\Indexable_Helper;
15 12 use Yoast\WP\SEO\Helpers\Indexing_Helper;
16 13 use Yoast\WP\SEO\Integrations\Integration_Interface;
17 14
18 15 /**
@@ -22,62 +19,84 @@
22 19 */
23 20 class Background_Indexing_Integration implements Integration_Interface {
24 21
25 22 /**
26 - * The post indexing action.
23 + * Represents the indexing completed action.
27 24 *
28 - * @var Indexable_Post_Indexation_Action
25 + * @var Indexable_Indexing_Complete_Action
29 26 */
30 - protected $post_indexation;
27 + protected $complete_indexation_action;
31 28
32 29 /**
33 - * The term indexing action.
30 + * Represents the indexing helper.
34 31 *
35 - * @var Indexable_Term_Indexation_Action
32 + * @var Indexing_Helper
36 33 */
37 - protected $term_indexation;
34 + protected $indexing_helper;
38 35
39 36 /**
40 - * The post type archive indexing action.
37 + * An object that checks if we are on the Yoast admin or on the dashboard page.
41 38 *
42 - * @var Indexable_Post_Type_Archive_Indexation_Action
39 + * @var Yoast_Admin_And_Dashboard_Conditional
43 40 */
44 - protected $post_type_archive_indexation;
41 + protected $yoast_admin_and_dashboard_conditional;
45 42
46 43 /**
47 - * Represents the general indexing.
44 + * All available indexing actions.
48 45 *
49 - * @var Indexable_General_Indexation_Action
46 + * @var Indexation_Action_Interface[]
50 47 */
51 - protected $general_indexation;
48 + protected $indexing_actions;
52 49
53 50 /**
54 - * Represents the indexing completed action.
51 + * An object that checks if we are handling a GET request.
55 52 *
56 - * @var Indexable_Indexing_Complete_Action
53 + * @var Get_Request_Conditional
57 54 */
58 - protected $complete_indexation_action;
55 + private $get_request_conditional;
59 56
60 57 /**
61 - * The post link indexing action.
58 + * An object that checks if WP_CRON is enabled.
62 59 *
63 - * @var Post_Link_Indexing_Action
60 + * @var WP_CRON_Enabled_Conditional
64 61 */
65 - protected $post_link_indexing_action;
62 + private $wp_cron_enabled_conditional;
66 63
67 64 /**
68 - * The term link indexing action.
65 + * The indexable helper
69 66 *
70 - * @var Term_Link_Indexing_Action
67 + * @var Indexable_Helper
71 68 */
72 - protected $term_link_indexing_action;
69 + private $indexable_helper;
73 70
74 71 /**
75 - * Represents the indexing helper.
72 + * Shutdown_Indexing_Integration constructor.
76 73 *
77 - * @var Indexing_Helper
74 + * @param Indexable_Indexing_Complete_Action $complete_indexation_action The complete indexing action.
75 + * @param Indexing_Helper $indexing_helper The indexing helper.
76 + * @param Indexable_Helper $indexable_helper The indexable helper.
77 + * @param Yoast_Admin_And_Dashboard_Conditional $yoast_admin_and_dashboard_conditional An object that checks if we are on the Yoast admin or on the dashboard page.
78 + * @param Get_Request_Conditional $get_request_conditional An object that checks if we are handling a GET request.
79 + * @param WP_CRON_Enabled_Conditional $wp_cron_enabled_conditional An object that checks if WP_CRON is enabled.
80 + * @param Indexation_Action_Interface ...$indexing_actions A list of all available indexing actions.
78 81 */
79 - protected $indexing_helper;
82 + public function __construct(
83 + Indexable_Indexing_Complete_Action $complete_indexation_action,
84 + Indexing_Helper $indexing_helper,
85 + Indexable_Helper $indexable_helper,
86 + Yoast_Admin_And_Dashboard_Conditional $yoast_admin_and_dashboard_conditional,
87 + Get_Request_Conditional $get_request_conditional,
88 + WP_CRON_Enabled_Conditional $wp_cron_enabled_conditional,
89 + Indexation_Action_Interface ...$indexing_actions
90 + ) {
91 + $this->indexing_actions = $indexing_actions;
92 + $this->complete_indexation_action = $complete_indexation_action;
93 + $this->indexing_helper = $indexing_helper;
94 + $this->indexable_helper = $indexable_helper;
95 + $this->yoast_admin_and_dashboard_conditional = $yoast_admin_and_dashboard_conditional;
96 + $this->get_request_conditional = $get_request_conditional;
97 + $this->wp_cron_enabled_conditional = $wp_cron_enabled_conditional;
98 + }
80 99
81 100 /**
82 101 * Returns the conditionals based on which this integration should be active.
83 102 *
@@ -84,51 +103,38 @@
84 103 * @return array The array of conditionals.
85 104 */
86 105 public static function get_conditionals() {
87 106 return [
88 - Yoast_Admin_And_Dashboard_Conditional::class,
89 107 Migrations_Conditional::class,
90 - Get_Request_Conditional::class,
91 108 ];
92 109 }
93 110
94 111 /**
95 - * Shutdown_Indexing_Integration constructor.
112 + * Register hooks.
96 113 *
97 - * @param Indexable_Post_Indexation_Action $post_indexation The post indexing action.
98 - * @param Indexable_Term_Indexation_Action $term_indexation The term indexing action.
99 - * @param Indexable_Post_Type_Archive_Indexation_Action $post_type_archive_indexation The post type archive indexing action.
100 - * @param Indexable_General_Indexation_Action $general_indexation The general indexing action.
101 - * @param Indexable_Indexing_Complete_Action $complete_indexation_action The complete indexing action.
102 - * @param Post_Link_Indexing_Action $post_link_indexing_action The post indexing action.
103 - * @param Term_Link_Indexing_Action $term_link_indexing_action The term indexing action.
104 - * @param Indexing_Helper $indexing_helper The indexing helper.
114 + * @return void
105 115 */
106 - public function __construct(
107 - Indexable_Post_Indexation_Action $post_indexation,
108 - Indexable_Term_Indexation_Action $term_indexation,
109 - Indexable_Post_Type_Archive_Indexation_Action $post_type_archive_indexation,
110 - Indexable_General_Indexation_Action $general_indexation,
111 - Indexable_Indexing_Complete_Action $complete_indexation_action,
112 - Post_Link_Indexing_Action $post_link_indexing_action,
113 - Term_Link_Indexing_Action $term_link_indexing_action,
114 - Indexing_Helper $indexing_helper
115 - ) {
116 - $this->post_indexation = $post_indexation;
117 - $this->term_indexation = $term_indexation;
118 - $this->post_type_archive_indexation = $post_type_archive_indexation;
119 - $this->general_indexation = $general_indexation;
120 - $this->complete_indexation_action = $complete_indexation_action;
121 - $this->post_link_indexing_action = $post_link_indexing_action;
122 - $this->term_link_indexing_action = $term_link_indexing_action;
123 - $this->indexing_helper = $indexing_helper;
116 + public function register_hooks() {
117 + \add_action( 'admin_init', [ $this, 'register_shutdown_indexing' ] );
118 + \add_action( 'wpseo_indexable_index_batch', [ $this, 'index' ] );
119 + // phpcs:ignore WordPress.WP.CronInterval -- The sniff doesn't understand values with parentheses. https://github.com/WordPress/WordPress-Coding-Standards/issues/2025
120 + \add_filter( 'cron_schedules', [ $this, 'add_cron_schedule' ] );
121 + \add_action( 'admin_init', [ $this, 'schedule_cron_indexing' ], 11 );
122 +
123 + $this->add_limit_filters();
124 124 }
125 125
126 126 /**
127 - * Register hooks.
127 + * Adds the filters that change the indexing limits.
128 + *
129 + * @return void
128 130 */
129 - public function register_hooks() {
130 - \add_action( 'admin_init', [ $this, 'register_shutdown_indexing' ], 10 );
131 + public function add_limit_filters() {
132 + \add_filter( 'wpseo_post_indexation_limit', [ $this, 'throttle_cron_indexing' ] );
133 + \add_filter( 'wpseo_post_type_archive_indexation_limit', [ $this, 'throttle_cron_indexing' ] );
134 + \add_filter( 'wpseo_term_indexation_limit', [ $this, 'throttle_cron_indexing' ] );
135 + \add_filter( 'wpseo_prominent_words_indexation_limit', [ $this, 'throttle_cron_indexing' ] );
136 + \add_filter( 'wpseo_link_indexing_limit', [ $this, 'throttle_cron_link_indexing' ] );
131 137 }
132 138
133 139 /**
134 140 * Enqueues the required scripts.
@@ -136,9 +142,9 @@
136 142 * @return void
137 143 */
138 144 public function register_shutdown_indexing() {
139 145 if ( $this->should_index_on_shutdown( $this->get_shutdown_limit() ) ) {
140 - \register_shutdown_function( [ $this, 'index' ] );
146 + $this->register_shutdown_function( 'index' );
141 147 }
142 148 }
143 149
144 150 /**
@@ -146,18 +152,155 @@
146 152 *
147 153 * @return void
148 154 */
149 155 public function index() {
150 - $this->post_indexation->index();
151 - $this->term_indexation->index();
152 - $this->general_indexation->index();
153 - $this->post_type_archive_indexation->index();
154 - $this->post_link_indexing_action->index();
155 - $this->term_link_indexing_action->index();
156 - $this->complete_indexation_action->complete();
156 + if ( \wp_doing_cron() && ! $this->should_index_on_cron() ) {
157 + $this->unschedule_cron_indexing();
158 +
159 + return;
160 + }
161 +
162 + foreach ( $this->indexing_actions as $indexation_action ) {
163 + $indexation_action->index();
164 + }
165 +
166 + if ( $this->indexing_helper->get_limited_filtered_unindexed_count_background( 1 ) === 0 ) {
167 + // We set this as complete, even though prominent words might not be complete. But that's the way we always treated that.
168 + $this->complete_indexation_action->complete();
169 + }
157 170 }
158 171
159 172 /**
173 + * Adds the 'Every fifteen minutes' cron schedule to WP-Cron.
174 + *
175 + * @param array $schedules The existing schedules.
176 + *
177 + * @return array The schedules containing the fifteen_minutes schedule.
178 + */
179 + public function add_cron_schedule( $schedules ) {
180 + if ( ! \is_array( $schedules ) ) {
181 + return $schedules;
182 + }
183 +
184 + $schedules['fifteen_minutes'] = [
185 + 'interval' => ( 15 * \MINUTE_IN_SECONDS ),
186 + 'display' => \esc_html__( 'Every fifteen minutes', 'wordpress-seo' ),
187 + ];
188 +
189 + return $schedules;
190 + }
191 +
192 + /**
193 + * Schedule background indexing every 15 minutes if the index isn't already up to date.
194 + *
195 + * @return void
196 + */
197 + public function schedule_cron_indexing() {
198 + /**
199 + * Filter: 'wpseo_unindexed_count_queries_ran' - Informs whether the expensive unindexed count queries have been ran already.
200 + *
201 + * @internal
202 + *
203 + * @param bool $have_queries_ran
204 + */
205 + $have_queries_ran = \apply_filters( 'wpseo_unindexed_count_queries_ran', false );
206 +
207 + if ( ( ! $this->yoast_admin_and_dashboard_conditional->is_met() || ! $this->get_request_conditional->is_met() ) && ! $have_queries_ran ) {
208 + return;
209 + }
210 +
211 + if ( ! \wp_next_scheduled( 'wpseo_indexable_index_batch' ) && $this->should_index_on_cron() ) {
212 + \wp_schedule_event( ( \time() + \HOUR_IN_SECONDS ), 'fifteen_minutes', 'wpseo_indexable_index_batch' );
213 + }
214 + }
215 +
216 + /**
217 + * Limit cron indexing to 15 indexables per batch instead of 25.
218 + *
219 + * @param int $indexation_limit The current limit (filter input).
220 + *
221 + * @return int The new batch limit.
222 + */
223 + public function throttle_cron_indexing( $indexation_limit ) {
224 + if ( \wp_doing_cron() ) {
225 + /**
226 + * Filter: 'wpseo_cron_indexing_limit_size' - Adds the possibility to limit the number of items that are indexed when in cron action.
227 + *
228 + * @param int $limit Maximum number of indexables to be indexed per indexing action.
229 + */
230 + return \apply_filters( 'wpseo_cron_indexing_limit_size', 15 );
231 + }
232 +
233 + return $indexation_limit;
234 + }
235 +
236 + /**
237 + * Limit cron indexing to 3 links per batch instead of 5.
238 + *
239 + * @param int $link_indexation_limit The current limit (filter input).
240 + *
241 + * @return int The new batch limit.
242 + */
243 + public function throttle_cron_link_indexing( $link_indexation_limit ) {
244 + if ( \wp_doing_cron() ) {
245 + /**
246 + * Filter: 'wpseo_cron_link_indexing_limit_size' - Adds the possibility to limit the number of links that are indexed when in cron action.
247 + *
248 + * @param int $limit Maximum number of link indexables to be indexed per link indexing action.
249 + */
250 + return \apply_filters( 'wpseo_cron_link_indexing_limit_size', 3 );
251 + }
252 +
253 + return $link_indexation_limit;
254 + }
255 +
256 + /**
257 + * Determine whether cron indexation should be performed.
258 + *
259 + * @return bool Should cron indexation be performed.
260 + */
261 + protected function should_index_on_cron() {
262 + if ( ! $this->indexable_helper->should_index_indexables() ) {
263 + return false;
264 + }
265 +
266 + // The filter supersedes everything when preventing cron indexation.
267 + if ( \apply_filters( 'Yoast\WP\SEO\enable_cron_indexing', true ) !== true ) {
268 + return false;
269 + }
270 +
271 + return $this->indexing_helper->get_limited_filtered_unindexed_count_background( 1 ) > 0;
272 + }
273 +
274 + /**
275 + * Determine whether background indexation should be performed.
276 + *
277 + * @param int $shutdown_limit The shutdown limit used to determine whether indexation should be run.
278 + *
279 + * @return bool Should background indexation be performed.
280 + */
281 + protected function should_index_on_shutdown( $shutdown_limit ) {
282 + if ( ! $this->yoast_admin_and_dashboard_conditional->is_met() || ! $this->get_request_conditional->is_met() ) {
283 + return false;
284 + }
285 +
286 + if ( ! $this->indexable_helper->should_index_indexables() ) {
287 + return false;
288 + }
289 +
290 + if ( $this->wp_cron_enabled_conditional->is_met() ) {
291 + return false;
292 + }
293 +
294 + $total_unindexed = $this->indexing_helper->get_limited_filtered_unindexed_count_background( $shutdown_limit );
295 + if ( $total_unindexed === 0 || $total_unindexed > $shutdown_limit ) {
296 + return false;
297 + }
298 +
299 + return true;
300 + }
301 +
302 + /**
160 303 * Retrieves the shutdown limit. This limit is the amount of indexables that is generated in the background.
161 304 *
162 305 * @return int The shutdown limit.
163 306 */
@@ -164,22 +307,33 @@
164 307 protected function get_shutdown_limit() {
165 308 /**
166 309 * Filter 'wpseo_shutdown_indexation_limit' - Allow filtering the number of objects that can be indexed during shutdown.
167 310 *
168 - * @api int The maximum number of objects indexed.
311 + * @param int $limit The maximum number of objects indexed.
169 312 */
170 313 return \apply_filters( 'wpseo_shutdown_indexation_limit', 25 );
171 314 }
172 315
173 316 /**
174 - * Determine whether background indexation should be performed.
317 + * Removes the cron indexing job from the scheduled event queue.
175 318 *
176 - * @param int $shutdown_limit The shutdown limit used to determine whether indexation should be run.
319 + * @return void
320 + */
321 + protected function unschedule_cron_indexing() {
322 + $scheduled = \wp_next_scheduled( 'wpseo_indexable_index_batch' );
323 + if ( $scheduled ) {
324 + \wp_unschedule_event( $scheduled, 'wpseo_indexable_index_batch' );
325 + }
326 + }
327 +
328 + /**
329 + * Registers a method to be executed on shutdown.
330 + * This wrapper mostly exists for making this class more unittestable.
177 331 *
178 - * @return bool Should background indexation be performed.
332 + * @param string $method_name The name of the method on the current instance to register.
333 + *
334 + * @return void
179 335 */
180 - public function should_index_on_shutdown( $shutdown_limit ) {
181 - $total = $this->indexing_helper->get_limited_filtered_unindexed_count( $shutdown_limit );
182 -
183 - return ( $total > 0 && $total < $shutdown_limit );
336 + protected function register_shutdown_function( $method_name ) {
337 + \register_shutdown_function( [ $this, $method_name ] );
184 338 }
185 339 }