← All changes
|
src/integrations/admin/background-indexing-integration.php
+232
-78
18.7
→
28.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 | } |