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

Yoast / wordpress-seo / 6af147e1132c0ec4c7a08552f951e6f71850163a

03 Sep 2025 07:01AM UTC coverage: 52.894% (-0.4%) from 53.318%
6af147e1132c0ec4c7a08552f951e6f71850163a

Pull #22541

github

web-flow
Merge a93bcc244 into e918c1ab5
Pull Request #22541: Start of some health check unit tests

8044 of 14881 branches covered (54.06%)

Branch coverage included in aggregate %.

31239 of 59386 relevant lines covered (52.6%)

40205.5 hits per line

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

39.89
/inc/options/class-wpseo-options.php
1
<?php
2
/**
3
 * WPSEO plugin file.
4
 *
5
 * @package WPSEO\Internals\Options
6
 */
7

8
/**
9
 * Overall Option Management class.
10
 *
11
 * Instantiates all the options and offers a number of utility methods to work with the options.
12
 */
13
class WPSEO_Options {
14

15
        /**
16
         * The option values.
17
         *
18
         * @var array|null
19
         */
20
        protected static $option_values = null;
21

22
        /**
23
         * Options this class uses.
24
         *
25
         * @var array Array format: (string) option_name  => (string) name of concrete class for the option.
26
         */
27
        public static $options = [
28
                'wpseo'               => 'WPSEO_Option_Wpseo',
29
                'wpseo_titles'        => 'WPSEO_Option_Titles',
30
                'wpseo_social'        => 'WPSEO_Option_Social',
31
                'wpseo_ms'            => 'WPSEO_Option_MS',
32
                'wpseo_taxonomy_meta' => 'WPSEO_Taxonomy_Meta',
33
                'wpseo_llmstxt'       => 'WPSEO_Option_Llmstxt',
34
        ];
35

36
        /**
37
         * Array of instantiated option objects.
38
         *
39
         * @var array
40
         */
41
        protected static $option_instances = [];
42

43
        /**
44
         * Array with the option names.
45
         *
46
         * @var array
47
         */
48
        protected static $option_names = [];
49

50
        /**
51
         * Instance of this class.
52
         *
53
         * @var WPSEO_Options
54
         */
55
        protected static $instance;
56

57
        /**
58
         * Instantiate all the WPSEO option management classes.
59
         */
60
        protected function __construct() {
2✔
61
                $this->register_hooks();
2✔
62

63
                foreach ( static::$options as $option_class ) {
2✔
64
                        static::register_option( call_user_func( [ $option_class, 'get_instance' ] ) );
2✔
65
                }
66
        }
67

68
        /**
69
         * Register our hooks.
70
         *
71
         * @return void
72
         */
73
        public function register_hooks() {
2✔
74
                add_action( 'registered_taxonomy', [ $this, 'clear_cache' ] );
2✔
75
                add_action( 'unregistered_taxonomy', [ $this, 'clear_cache' ] );
2✔
76
                add_action( 'registered_post_type', [ $this, 'clear_cache' ] );
2✔
77
                add_action( 'unregistered_post_type', [ $this, 'clear_cache' ] );
2✔
78
        }
79

80
        /**
81
         * Get the singleton instance of this class.
82
         *
83
         * @return object
84
         */
85
        public static function get_instance() {
×
86
                if ( ! ( static::$instance instanceof self ) ) {
×
87
                        static::$instance = new self();
×
88
                }
89

90
                return static::$instance;
×
91
        }
92

93
        /**
94
         * Registers an option to the options list.
95
         *
96
         * @param WPSEO_Option $option_instance Instance of the option.
97
         *
98
         * @return void
99
         */
100
        public static function register_option( WPSEO_Option $option_instance ) {
×
101
                $option_name = $option_instance->get_option_name();
×
102

103
                if ( $option_instance->multisite_only && ! static::is_multisite() ) {
×
104
                        unset( static::$options[ $option_name ], static::$option_names[ $option_name ] );
×
105

106
                        return;
×
107
                }
108

109
                $is_already_registered = array_key_exists( $option_name, static::$options );
×
110
                if ( ! $is_already_registered ) {
×
111
                        static::$options[ $option_name ] = get_class( $option_instance );
×
112
                }
113

114
                if ( $option_instance->include_in_all === true ) {
×
115
                        static::$option_names[ $option_name ] = $option_name;
×
116
                }
117

118
                static::$option_instances[ $option_name ] = $option_instance;
×
119

120
                if ( ! $is_already_registered ) {
×
121
                        static::clear_cache();
×
122
                }
123
        }
124

125
        /**
126
         * Get the group name of an option for use in the settings form.
127
         *
128
         * @param string $option_name The option for which you want to retrieve the option group name.
129
         *
130
         * @return string|bool
131
         */
132
        public static function get_group_name( $option_name ) {
×
133
                if ( isset( static::$option_instances[ $option_name ] ) ) {
×
134
                        return static::$option_instances[ $option_name ]->group_name;
×
135
                }
136

137
                return false;
×
138
        }
139

140
        /**
141
         * Get a specific default value for an option.
142
         *
143
         * @param string $option_name The option for which you want to retrieve a default.
144
         * @param string $key         The key within the option who's default you want.
145
         *
146
         * @return mixed
147
         */
148
        public static function get_default( $option_name, $key ) {
×
149
                if ( isset( static::$option_instances[ $option_name ] ) ) {
×
150
                        $defaults = static::$option_instances[ $option_name ]->get_defaults();
×
151
                        if ( isset( $defaults[ $key ] ) ) {
×
152
                                return $defaults[ $key ];
×
153
                        }
154
                }
155

156
                return null;
×
157
        }
158

159
        /**
160
         * Update a site_option.
161
         *
162
         * @param string $option_name The option name of the option to save.
163
         * @param mixed  $value       The new value for the option.
164
         *
165
         * @return bool
166
         */
167
        public static function update_site_option( $option_name, $value ) {
×
168
                if ( is_multisite() && isset( static::$option_instances[ $option_name ] ) ) {
×
169
                        return static::$option_instances[ $option_name ]->update_site_option( $value );
×
170
                }
171

172
                return false;
×
173
        }
174

175
        /**
176
         * Get the instantiated option instance.
177
         *
178
         * @param string $option_name The option for which you want to retrieve the instance.
179
         *
180
         * @return object|bool
181
         */
182
        public static function get_option_instance( $option_name ) {
×
183
                if ( isset( static::$option_instances[ $option_name ] ) ) {
×
184
                        return static::$option_instances[ $option_name ];
×
185
                }
186

187
                return false;
×
188
        }
189

190
        /**
191
         * Retrieve an array of the options which should be included in get_all() and reset().
192
         *
193
         * @return array Array of option names.
194
         */
195
        public static function get_option_names() {
8✔
196
                $option_names = array_values( static::$option_names );
8✔
197
                if ( $option_names === [] ) {
8✔
198
                        foreach ( static::$option_instances as $option_name => $option_object ) {
×
199
                                if ( $option_object->include_in_all === true ) {
×
200
                                        $option_names[] = $option_name;
×
201
                                }
202
                        }
203
                }
204

205
                /**
206
                 * Filter: wpseo_options - Allow developers to change the option name to include.
207
                 *
208
                 * @param array $option_names The option names to include in get_all and reset().
209
                 */
210
                return apply_filters( 'wpseo_options', $option_names );
8✔
211
        }
212

213
        /**
214
         * Retrieve all the options for the SEO plugin in one go.
215
         *
216
         * @param array<string> $specific_options The option groups of the option you want to get.
217
         *
218
         * @return array Array combining the values of all the options.
219
         */
220
        public static function get_all( $specific_options = [] ) {
12✔
221
                $option_names          = ( empty( $specific_options ) ) ? static::get_option_names() : $specific_options;
12✔
222
                static::$option_values = static::get_options( $option_names );
12✔
223

224
                return static::$option_values;
12✔
225
        }
226

227
        /**
228
         * Retrieve one or more options for the SEO plugin.
229
         *
230
         * @param array $option_names An array of option names of the options you want to get.
231
         *
232
         * @return array Array combining the values of the requested options.
233
         */
234
        public static function get_options( array $option_names ) {
20✔
235
                $options      = [];
20✔
236
                $option_names = array_filter( $option_names, 'is_string' );
20✔
237
                foreach ( $option_names as $option_name ) {
20✔
238
                        if ( isset( static::$option_instances[ $option_name ] ) ) {
16✔
239
                                $option = static::get_option( $option_name );
12✔
240

241
                                if ( $option !== null ) {
12✔
242
                                        $options = array_merge( $options, $option );
12✔
243
                                }
244
                        }
245
                }
246

247
                return $options;
20✔
248
        }
249

250
        /**
251
         * Retrieve a single option for the SEO plugin.
252
         *
253
         * @param string $option_name The name of the option you want to get.
254
         *
255
         * @return array Array containing the requested option.
256
         */
257
        public static function get_option( $option_name ) {
24✔
258
                $option = null;
24✔
259
                if ( is_string( $option_name ) && ! empty( $option_name ) ) {
24✔
260
                        if ( isset( static::$option_instances[ $option_name ] ) ) {
16✔
261
                                if ( static::$option_instances[ $option_name ]->multisite_only !== true ) {
12✔
262
                                        $option = get_option( $option_name );
12✔
263
                                }
264
                                else {
265
                                        $option = get_site_option( $option_name );
4✔
266
                                }
267
                        }
268
                }
269

270
                return $option;
24✔
271
        }
272

273
        /**
274
         * Retrieve a single field from any option for the SEO plugin. Keys are always unique.
275
         *
276
         * @param string        $key           The key it should return.
277
         * @param mixed         $default_value The default value that should be returned if the key isn't set.
278
         * @param array<string> $option_groups The option groups to retrieve the option from.
279
         *
280
         * @return mixed Returns value if found, $default_value if not.
281
         */
282
        public static function get( $key, $default_value = null, $option_groups = [] ) {
30✔
283
                if ( ! isset( static::$option_values[ $key ] ) ) {
30✔
284
                        static::prime_cache( $option_groups );
24✔
285
                }
286
                if ( isset( static::$option_values[ $key ] ) ) {
30✔
287
                        return static::$option_values[ $key ];
18✔
288
                }
289

290
                return $default_value;
12✔
291
        }
292

293
        /**
294
         * Resets the cache to null.
295
         *
296
         * @return void
297
         */
298
        public static function clear_cache() {
10✔
299
                static::$option_values = null;
10✔
300
        }
301

302
        /**
303
         * Primes our cache.
304
         *
305
         * @param array<string> $option_groups The option groups to prime the cache with.
306
         *
307
         * @return void
308
         */
309
        private static function prime_cache( $option_groups = [] ) {
8✔
310
                static::$option_values = static::get_all( $option_groups );
8✔
311
                static::$option_values = static::add_ms_option( static::$option_values );
8✔
312
        }
313

314
        /**
315
         * Retrieve a single field from an option for the SEO plugin.
316
         *
317
         * @param string $key          The key to set.
318
         * @param mixed  $value        The value to set.
319
         * @param string $option_group The lookup table which represents the option_group where the key is stored.
320
         *
321
         * @return mixed|null Returns value if found, $default if not.
322
         */
323
        public static function set( $key, $value, $option_group = '' ) {
10✔
324
                $lookup_table = static::get_lookup_table( $option_group );
10✔
325

326
                if ( isset( $lookup_table[ $key ] ) ) {
10✔
327
                        return static::save_option( $lookup_table[ $key ], $key, $value );
8✔
328
                }
329

330
                $patterns = static::get_pattern_table();
2✔
331
                foreach ( $patterns as $pattern => $option ) {
2✔
332
                        if ( strpos( $key, $pattern ) === 0 ) {
2✔
333
                                return static::save_option( $option, $key, $value );
×
334
                        }
335
                }
336

337
                static::$option_values[ $key ] = $value;
2✔
338
        }
339

340
        /**
341
         * Get an option only if it's been auto-loaded.
342
         *
343
         * @param string $option        The option to retrieve.
344
         * @param mixed  $default_value A default value to return.
345
         *
346
         * @return mixed
347
         */
348
        public static function get_autoloaded_option( $option, $default_value = false ) {
×
349
                $value = wp_cache_get( $option, 'options' );
×
350
                if ( $value === false ) {
×
351
                        $passed_default = func_num_args() > 1;
×
352

353
                        // phpcs:ignore WordPress.NamingConventions.PrefixAllGlobals -- Using WP native filter.
354
                        return apply_filters( "default_option_{$option}", $default_value, $option, $passed_default );
×
355
                }
356

357
                // phpcs:ignore WordPress.NamingConventions.PrefixAllGlobals -- Using WP native filter.
358
                return apply_filters( "option_{$option}", maybe_unserialize( $value ), $option );
×
359
        }
360

361
        /**
362
         * Run the clean up routine for one or all options.
363
         *
364
         * @param array|string|null $option_name     Optional. the option you want to clean or an array of
365
         *                                           option names for the options you want to clean.
366
         *                                           If not set, all options will be cleaned.
367
         * @param string|null       $current_version Optional. Version from which to upgrade, if not set,
368
         *                                           version specific upgrades will be disregarded.
369
         *
370
         * @return void
371
         */
372
        public static function clean_up( $option_name = null, $current_version = null ) {
×
373
                if ( isset( $option_name ) && is_string( $option_name ) && $option_name !== '' ) {
×
374
                        if ( isset( static::$option_instances[ $option_name ] ) ) {
×
375
                                static::$option_instances[ $option_name ]->clean( $current_version );
×
376
                        }
377
                }
378
                elseif ( isset( $option_name ) && is_array( $option_name ) && $option_name !== [] ) {
×
379
                        foreach ( $option_name as $option ) {
×
380
                                if ( isset( static::$option_instances[ $option ] ) ) {
×
381
                                        static::$option_instances[ $option ]->clean( $current_version );
×
382
                                }
383
                        }
384
                        unset( $option );
×
385
                }
386
                else {
387
                        foreach ( static::$option_instances as $instance ) {
×
388
                                $instance->clean( $current_version );
×
389
                        }
390
                        unset( $instance );
×
391

392
                        // If we've done a full clean-up, we can safely remove this really old option.
393
                        delete_option( 'wpseo_indexation' );
×
394
                }
395
        }
396

397
        /**
398
         * Check that all options exist in the database and add any which don't.
399
         *
400
         * @return void
401
         */
402
        public static function ensure_options_exist() {
×
403
                foreach ( static::$option_instances as $instance ) {
×
404
                        $instance->maybe_add_option();
×
405
                }
406
        }
407

408
        /**
409
         * Initialize some options on first install/activate/reset.
410
         *
411
         * @return void
412
         */
413
        public static function initialize() {
×
414
                /* Force WooThemes to use Yoast SEO data. */
415
                if ( function_exists( 'woo_version_init' ) ) {
×
416
                        update_option( 'seo_woo_use_third_party_data', 'true' );
×
417
                }
418
        }
419

420
        /**
421
         * Reset all options to their default values and rerun some tests.
422
         *
423
         * @return void
424
         */
425
        public static function reset() {
×
426
                if ( ! is_multisite() ) {
×
427
                        $option_names = static::get_option_names();
×
428
                        if ( is_array( $option_names ) && $option_names !== [] ) {
×
429
                                foreach ( $option_names as $option_name ) {
×
430
                                        delete_option( $option_name );
×
431
                                        update_option( $option_name, get_option( $option_name ) );
×
432
                                }
433
                        }
434
                        unset( $option_names );
×
435
                }
436
                else {
437
                        // Reset MS blog based on network default blog setting.
438
                        static::reset_ms_blog( get_current_blog_id() );
×
439
                }
440

441
                static::initialize();
×
442
        }
443

444
        /**
445
         * Initialize default values for a new multisite blog.
446
         *
447
         * @param bool $force_init Whether to always do the initialization routine (title/desc test).
448
         *
449
         * @return void
450
         */
451
        public static function maybe_set_multisite_defaults( $force_init = false ) {
×
452
                $option = get_option( 'wpseo' );
×
453

454
                if ( is_multisite() ) {
×
455
                        if ( $option['ms_defaults_set'] === false ) {
×
456
                                static::reset_ms_blog( get_current_blog_id() );
×
457
                                static::initialize();
×
458
                        }
459
                        elseif ( $force_init === true ) {
×
460
                                static::initialize();
×
461
                        }
462
                }
463
        }
464

465
        /**
466
         * Reset all options for a specific multisite blog to their default values based upon a
467
         * specified default blog if one was chosen on the network page or the plugin defaults if it was not.
468
         *
469
         * @param int|string $blog_id Blog id of the blog for which to reset the options.
470
         *
471
         * @return void
472
         */
473
        public static function reset_ms_blog( $blog_id ) {
×
474
                if ( is_multisite() ) {
×
475
                        $options      = get_site_option( 'wpseo_ms' );
×
476
                        $option_names = static::get_option_names();
×
477

478
                        if ( is_array( $option_names ) && $option_names !== [] ) {
×
479
                                $base_blog_id = $blog_id;
×
480
                                if ( $options['defaultblog'] !== '' && $options['defaultblog'] !== 0 ) {
×
481
                                        $base_blog_id = $options['defaultblog'];
×
482
                                }
483

484
                                foreach ( $option_names as $option_name ) {
×
485
                                        delete_blog_option( $blog_id, $option_name );
×
486

487
                                        $new_option = get_blog_option( $base_blog_id, $option_name );
×
488

489
                                        /* Remove sensitive, theme dependent and site dependent info. */
490
                                        if ( isset( static::$option_instances[ $option_name ] ) && static::$option_instances[ $option_name ]->ms_exclude !== [] ) {
×
491
                                                foreach ( static::$option_instances[ $option_name ]->ms_exclude as $key ) {
×
492
                                                        unset( $new_option[ $key ] );
×
493
                                                }
494
                                        }
495

496
                                        if ( $option_name === 'wpseo' ) {
×
497
                                                $new_option['ms_defaults_set'] = true;
×
498
                                        }
499

500
                                        update_blog_option( $blog_id, $option_name, $new_option );
×
501
                                }
502
                        }
503
                }
504
        }
505

506
        /**
507
         * Saves the option to the database.
508
         *
509
         * @param string $wpseo_options_group_name The name for the wpseo option group in the database.
510
         * @param string $option_name              The name for the option to set.
511
         * @param mixed  $option_value             The value for the option.
512
         *
513
         * @return bool Returns true if the option is successfully saved in the database.
514
         */
515
        public static function save_option( $wpseo_options_group_name, $option_name, $option_value ) {
8✔
516
                $options                 = static::get_option( $wpseo_options_group_name );
8✔
517
                $options[ $option_name ] = $option_value;
8✔
518

519
                if ( isset( static::$option_instances[ $wpseo_options_group_name ] ) && static::$option_instances[ $wpseo_options_group_name ]->multisite_only === true ) {
8✔
520
                        static::update_site_option( $wpseo_options_group_name, $options );
×
521
                }
522
                else {
523
                        update_option( $wpseo_options_group_name, $options );
8✔
524
                }
525

526
                // Check if everything got saved properly.
527
                $saved_option = static::get_option( $wpseo_options_group_name );
8✔
528

529
                // Clear our cache.
530
                static::clear_cache();
8✔
531

532
                return $saved_option[ $option_name ] === $options[ $option_name ];
8✔
533
        }
534

535
        /**
536
         * Adds the multisite options to the option stack if relevant.
537
         *
538
         * @param array $option The currently present options settings.
539
         *
540
         * @return array Options possibly including multisite.
541
         */
542
        protected static function add_ms_option( $option ) {
10✔
543
                if ( ! is_multisite() ) {
10✔
544
                        return $option;
6✔
545
                }
546

547
                $ms_option = static::get_option( 'wpseo_ms' );
4✔
548
                if ( $ms_option === null ) {
4✔
549
                        return $option;
×
550
                }
551

552
                return array_merge( $option, $ms_option );
4✔
553
        }
554

555
        /**
556
         * Checks if installation is multisite.
557
         *
558
         * @return bool True when is multisite.
559
         */
560
        protected static function is_multisite() {
×
561
                static $is_multisite;
×
562

563
                if ( $is_multisite === null ) {
×
564
                        $is_multisite = is_multisite();
×
565
                }
566

567
                return $is_multisite;
×
568
        }
569

570
        /**
571
         * Retrieves a lookup table to find in which option_group a key is stored.
572
         *
573
         * @param string $option_group The option_group where the key is stored.
574
         *
575
         * @return array The lookup table.
576
         */
577
        private static function get_lookup_table( $option_group = '' ) {
8✔
578
                $lookup_table  = [];
8✔
579
                $option_groups = ( $option_group === '' ) ? static::$options : [ $option_group => static::$options[ $option_group ] ];
8✔
580

581
                foreach ( array_keys( $option_groups ) as $option_name ) {
8✔
582
                        $full_option = static::get_option( $option_name );
8✔
583
                        foreach ( $full_option as $key => $value ) {
8✔
584
                                $lookup_table[ $key ] = $option_name;
8✔
585
                        }
586
                }
587

588
                return $lookup_table;
8✔
589
        }
590

591
        /**
592
         * Retrieves a lookup table to find in which option_group a key is stored.
593
         *
594
         * @return array The lookup table.
595
         */
596
        private static function get_pattern_table() {
×
597
                $pattern_table = [];
×
598
                foreach ( static::$options as $option_name => $option_class ) {
×
599
                        $instance = call_user_func( [ $option_class, 'get_instance' ] );
×
600
                        foreach ( $instance->get_patterns() as $key ) {
×
601
                                $pattern_table[ $key ] = $option_name;
×
602
                        }
603
                }
604

605
                return $pattern_table;
×
606
        }
607
}
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

© 2025 Coveralls, Inc