• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

Yoast / wordpress-seo / ffc1d9a467cb57e096ffa991b277979ae484337f

17 Jul 2026 11:51AM UTC coverage: 55.463% (+0.1%) from 55.335%
ffc1d9a467cb57e096ffa991b277979ae484337f

Pull #23402

github

web-flow
Merge ff0169807 into 4e4174596
Pull Request #23402: 943 seo data optimization error reporting refactoring

10502 of 18759 branches covered (55.98%)

Branch coverage included in aggregate %.

144 of 153 new or added lines in 9 files covered. (94.12%)

58 existing lines in 3 files now uncovered.

40126 of 72524 relevant lines covered (55.33%)

41497.76 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

98.91
/src/commands/index-command.php
1
<?php
2

3
namespace Yoast\WP\SEO\Commands;
4

5
use WP_CLI;
6
use WP_CLI\Utils;
7
use Yoast\WP\Lib\Model;
8
use Yoast\WP\SEO\Actions\Indexing\Indexable_General_Indexation_Action;
9
use Yoast\WP\SEO\Actions\Indexing\Indexable_Indexing_Complete_Action;
10
use Yoast\WP\SEO\Actions\Indexing\Indexable_Post_Indexation_Action;
11
use Yoast\WP\SEO\Actions\Indexing\Indexable_Post_Type_Archive_Indexation_Action;
12
use Yoast\WP\SEO\Actions\Indexing\Indexable_Term_Indexation_Action;
13
use Yoast\WP\SEO\Actions\Indexing\Indexation_Action_Interface;
14
use Yoast\WP\SEO\Actions\Indexing\Indexing_Prepare_Action;
15
use Yoast\WP\SEO\Actions\Indexing\Post_Link_Indexing_Action;
16
use Yoast\WP\SEO\Actions\Indexing\Term_Link_Indexing_Action;
17
use Yoast\WP\SEO\Exceptions\Indexable\Indexing_Failed_Exception;
18
use Yoast\WP\SEO\Helpers\Indexable_Helper;
19
use Yoast\WP\SEO\Main;
20

21
/**
22
 * Command to generate indexables for all posts and terms.
23
 */
24
class Index_Command implements Command_Interface {
25

26
        /**
27
         * The post indexation action.
28
         *
29
         * @var Indexable_Post_Indexation_Action
30
         */
31
        private $post_indexation_action;
32

33
        /**
34
         * The term indexation action.
35
         *
36
         * @var Indexable_Term_Indexation_Action
37
         */
38
        private $term_indexation_action;
39

40
        /**
41
         * The post type archive indexation action.
42
         *
43
         * @var Indexable_Post_Type_Archive_Indexation_Action
44
         */
45
        private $post_type_archive_indexation_action;
46

47
        /**
48
         * The general indexation action.
49
         *
50
         * @var Indexable_General_Indexation_Action
51
         */
52
        private $general_indexation_action;
53

54
        /**
55
         * The term link indexing action.
56
         *
57
         * @var Term_Link_Indexing_Action
58
         */
59
        private $term_link_indexing_action;
60

61
        /**
62
         * The post link indexing action.
63
         *
64
         * @var Post_Link_Indexing_Action
65
         */
66
        private $post_link_indexing_action;
67

68
        /**
69
         * The complete indexation action.
70
         *
71
         * @var Indexable_Indexing_Complete_Action
72
         */
73
        private $complete_indexation_action;
74

75
        /**
76
         * The indexing prepare action.
77
         *
78
         * @var Indexing_Prepare_Action
79
         */
80
        private $prepare_indexing_action;
81

82
        /**
83
         * Represents the indexable helper.
84
         *
85
         * @var Indexable_Helper
86
         */
87
        protected $indexable_helper;
88

89
        /**
90
         * Generate_Indexables_Command constructor.
91
         *
92
         * @param Indexable_Post_Indexation_Action              $post_indexation_action              The post indexation
93
         *                                                                                           action.
94
         * @param Indexable_Term_Indexation_Action              $term_indexation_action              The term indexation
95
         *                                                                                           action.
96
         * @param Indexable_Post_Type_Archive_Indexation_Action $post_type_archive_indexation_action The post type archive
97
         *                                                                                           indexation action.
98
         * @param Indexable_General_Indexation_Action           $general_indexation_action           The general indexation
99
         *                                                                                           action.
100
         * @param Indexable_Indexing_Complete_Action            $complete_indexation_action          The complete indexation
101
         *                                                                                           action.
102
         * @param Indexing_Prepare_Action                       $prepare_indexing_action             The prepare indexing
103
         *                                                                                           action.
104
         * @param Post_Link_Indexing_Action                     $post_link_indexing_action           The post link indexation
105
         *                                                                                           action.
106
         * @param Term_Link_Indexing_Action                     $term_link_indexing_action           The term link indexation
107
         *                                                                                           action.
108
         * @param Indexable_Helper                              $indexable_helper                    The indexable helper.
109
         */
110
        public function __construct(
14✔
111
                Indexable_Post_Indexation_Action $post_indexation_action,
112
                Indexable_Term_Indexation_Action $term_indexation_action,
113
                Indexable_Post_Type_Archive_Indexation_Action $post_type_archive_indexation_action,
114
                Indexable_General_Indexation_Action $general_indexation_action,
115
                Indexable_Indexing_Complete_Action $complete_indexation_action,
116
                Indexing_Prepare_Action $prepare_indexing_action,
117
                Post_Link_Indexing_Action $post_link_indexing_action,
118
                Term_Link_Indexing_Action $term_link_indexing_action,
119
                Indexable_Helper $indexable_helper
120
        ) {
121
                $this->post_indexation_action              = $post_indexation_action;
14✔
122
                $this->term_indexation_action              = $term_indexation_action;
14✔
123
                $this->post_type_archive_indexation_action = $post_type_archive_indexation_action;
14✔
124
                $this->general_indexation_action           = $general_indexation_action;
14✔
125
                $this->complete_indexation_action          = $complete_indexation_action;
14✔
126
                $this->prepare_indexing_action             = $prepare_indexing_action;
14✔
127
                $this->post_link_indexing_action           = $post_link_indexing_action;
14✔
128
                $this->term_link_indexing_action           = $term_link_indexing_action;
14✔
129
                $this->indexable_helper                    = $indexable_helper;
14✔
130
        }
131

132
        /**
133
         * Gets the namespace.
134
         *
135
         * @return string
136
         */
137
        public static function get_namespace() {
2✔
138
                return Main::WP_CLI_NAMESPACE;
2✔
139
        }
140

141
        /**
142
         * Indexes all your content to ensure the best performance.
143
         *
144
         * ## OPTIONS
145
         *
146
         * [--network]
147
         * : Performs the indexation on all sites within the network.
148
         *
149
         * [--reindex]
150
         * : Removes all existing indexables and then reindexes them.
151
         *
152
         * [--skip-confirmation]
153
         * : Skips the confirmations (for automated systems).
154
         *
155
         * [--interval=<interval>]
156
         * : The number of microseconds (millionths of a second) to wait between index actions.
157
         * ---
158
         * default: 500000
159
         * ---
160
         *
161
         * ## EXAMPLES
162
         *
163
         *     wp yoast index
164
         *
165
         * @when after_wp_load
166
         *
167
         * @param array<string>|null              $args       The arguments.
168
         * @param array<string, string|bool>|null $assoc_args The associative arguments.
169
         *
170
         * @return void
171
         */
172
        public function index( $args = null, $assoc_args = null ) {
10✔
173
                if ( ! $this->indexable_helper->should_index_indexables() ) {
10✔
174
                        WP_CLI::log(
2✔
175
                                \__( 'Your WordPress environment is running on a non-production site. Indexables can only be created on production environments. Please check your `WP_ENVIRONMENT_TYPE` settings.', 'wordpress-seo' ),
2✔
176
                        );
2✔
177

178
                        return;
2✔
179
                }
180

181
                if ( ! isset( $assoc_args['network'] ) ) {
8✔
182
                        $this->run_indexation_actions( $assoc_args );
6✔
183

184
                        return;
4✔
185
                }
186

187
                $criteria = [
2✔
188
                        'fields'   => 'ids',
2✔
189
                        'spam'     => 0,
2✔
190
                        'deleted'  => 0,
2✔
191
                        'archived' => 0,
2✔
192
                ];
2✔
193
                $blog_ids = \get_sites( $criteria );
2✔
194

195
                foreach ( $blog_ids as $blog_id ) {
2✔
196
                        \switch_to_blog( $blog_id );
2✔
197
                        \do_action( '_yoast_run_migrations' );
2✔
198
                        $this->run_indexation_actions( $assoc_args );
2✔
199
                        \restore_current_blog();
2✔
200
                }
201
        }
202

203
        /**
204
         * Runs all indexation actions.
205
         *
206
         * @param array<string, string|bool> $assoc_args The associative arguments.
207
         *
208
         * @return void
209
         */
210
        protected function run_indexation_actions( $assoc_args ) {
8✔
211
                // See if we need to clear all indexables before repopulating.
212
                if ( isset( $assoc_args['reindex'] ) ) {
8✔
213

214
                        // Argument --skip-confirmation to prevent confirmation (for automated systems).
215
                        if ( ! isset( $assoc_args['skip-confirmation'] ) ) {
2✔
216
                                WP_CLI::confirm( 'This will clear all previously indexed objects. Are you certain you wish to proceed?' );
2✔
217
                        }
218

219
                        // Truncate the tables.
220
                        $this->clear();
2✔
221

222
                        // Delete the transients to make sure re-indexing runs every time.
223
                        \delete_transient( Indexable_Post_Indexation_Action::UNINDEXED_COUNT_TRANSIENT );
2✔
224
                        \delete_transient( Indexable_Post_Type_Archive_Indexation_Action::UNINDEXED_COUNT_TRANSIENT );
2✔
225
                        \delete_transient( Indexable_Term_Indexation_Action::UNINDEXED_COUNT_TRANSIENT );
2✔
226
                }
227

228
                $indexation_actions = [
8✔
229
                        'posts'              => $this->post_indexation_action,
8✔
230
                        'terms'              => $this->term_indexation_action,
8✔
231
                        'post type archives' => $this->post_type_archive_indexation_action,
8✔
232
                        'general objects'    => $this->general_indexation_action,
8✔
233
                        'post links'         => $this->post_link_indexing_action,
8✔
234
                        'term links'         => $this->term_link_indexing_action,
8✔
235
                ];
8✔
236

237
                $this->prepare_indexing_action->prepare();
8✔
238

239
                $interval = (int) $assoc_args['interval'];
8✔
240
                foreach ( $indexation_actions as $name => $indexation_action ) {
8✔
241
                        $this->run_indexation_action( $name, $indexation_action, $interval );
8✔
242
                }
243

244
                $this->complete_indexation_action->complete();
6✔
245
        }
246

247
        /**
248
         * Runs an indexation action.
249
         *
250
         * @param string                      $name              The name of the object to be indexed.
251
         * @param Indexation_Action_Interface $indexation_action The indexation action.
252
         * @param int                         $interval          Number of microseconds (millionths of a second) to wait between index actions.
253
         *
254
         * @return void
255
         */
256
        protected function run_indexation_action( $name, Indexation_Action_Interface $indexation_action, $interval ) {
8✔
257
                $total = $indexation_action->get_total_unindexed();
8✔
258
                if ( $total > 0 ) {
8✔
259
                        $limit    = $indexation_action->get_limit();
8✔
260
                        $progress = Utils\make_progress_bar( 'Indexing ' . $name, $total );
8✔
261
                        do {
262
                                try {
263
                                        $indexables = $indexation_action->index();
8✔
264
                                } catch ( Indexing_Failed_Exception $exception ) {
2✔
265
                                        $progress->finish();
2✔
266

267
                                        $previous = $exception->getPrevious();
2✔
268
                                        WP_CLI::error(
2✔
269
                                                \sprintf(
2✔
270
                                                        'Could not optimize %1$s while indexing %2$s: %3$s',
2✔
271
                                                        $exception->get_object_description(),
2✔
272
                                                        $name,
2✔
273
                                                        ( $previous !== null ) ? $previous->getMessage() : $exception->getMessage(),
2✔
274
                                                ),
2✔
275
                                        );
2✔
276

NEW
277
                                        return;
×
278
                                }
279
                                $count = \count( $indexables );
6✔
280
                                $progress->tick( $count );
6✔
281
                                \usleep( $interval );
6✔
282
                                Utils\wp_clear_object_cache();
6✔
283
                        } while ( $count >= $limit );
6✔
284
                        $progress->finish();
6✔
285
                }
286
        }
287

288
        /**
289
         * Clears the database related to the indexables.
290
         *
291
         * @return void
292
         */
293
        protected function clear() {
2✔
294
                global $wpdb;
2✔
295

296
                // For the PreparedSQLPlaceholders issue, see: https://github.com/WordPress/WordPress-Coding-Standards/issues/1903.
297
                // For the DirectDBQuery issue, see: https://github.com/WordPress/WordPress-Coding-Standards/issues/1947.
298
                // phpcs:disable WordPress.DB -- Table names should not be quoted and truncate queries can not be cached.
299
                $wpdb->query(
2✔
300
                        $wpdb->prepare(
2✔
301
                                'TRUNCATE TABLE %1$s',
2✔
302
                                Model::get_table_name( 'Indexable' ),
2✔
303
                        ),
2✔
304
                );
2✔
305
                $wpdb->query(
2✔
306
                        $wpdb->prepare(
2✔
307
                                'TRUNCATE TABLE %1$s',
2✔
308
                                Model::get_table_name( 'Indexable_Hierarchy' ),
2✔
309
                        ),
2✔
310
                );
2✔
311
                // phpcs:enable
312
        }
313
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc