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

Yoast / wordpress-seo / 0fdb51d5ef87fb55b55da5a950cf7350c155980d

11 Mar 2025 10:16AM UTC coverage: 53.422% (-1.3%) from 54.687%
0fdb51d5ef87fb55b55da5a950cf7350c155980d

push

github

web-flow
Merge pull request #22086 from Yoast/add-pregnant-women-to-potentially-non-inclusive-phrases

Inclusive language: Add 'pregnant women' to potentially non-inclusive phrases

7918 of 13987 branches covered (56.61%)

Branch coverage included in aggregate %.

30526 of 57976 relevant lines covered (52.65%)

41097.18 hits per line

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

0.0
/wp-seo-main.php
1
<?php
2
/**
3
 * WPSEO plugin file.
4
 *
5
 * @package WPSEO\Main
6
 */
7

8
if ( ! function_exists( 'add_filter' ) ) {
9
        header( 'Status: 403 Forbidden' );
10
        header( 'HTTP/1.1 403 Forbidden' );
11
        exit();
12
}
13

14
/**
15
 * {@internal Nobody should be able to overrule the real version number as this can cause
16
 *            serious issues with the options, so no if ( ! defined() ).}}
17
 */
18
define( 'WPSEO_VERSION', '24.7-RC1' );
19

20

21
if ( ! defined( 'WPSEO_PATH' ) ) {
22
        define( 'WPSEO_PATH', plugin_dir_path( WPSEO_FILE ) );
23
}
24

25
if ( ! defined( 'WPSEO_BASENAME' ) ) {
26
        define( 'WPSEO_BASENAME', plugin_basename( WPSEO_FILE ) );
27
}
28

29
/*
30
 * {@internal The prefix constants are used to build prefixed versions of dependencies.
31
 *            These should not be changed on run-time, thus missing the ! defined() check.}}
32
 */
33
define( 'YOAST_VENDOR_NS_PREFIX', 'YoastSEO_Vendor' );
34
define( 'YOAST_VENDOR_DEFINE_PREFIX', 'YOASTSEO_VENDOR__' );
35
define( 'YOAST_VENDOR_PREFIX_DIRECTORY', 'vendor_prefixed' );
36

37
define( 'YOAST_SEO_PHP_REQUIRED', '7.2.5' );
38
define( 'YOAST_SEO_WP_TESTED', '6.7.2' );
39
define( 'YOAST_SEO_WP_REQUIRED', '6.6' );
40

41
if ( ! defined( 'WPSEO_NAMESPACES' ) ) {
42
        define( 'WPSEO_NAMESPACES', true );
43
}
44

45

46
/* ***************************** CLASS AUTOLOADING *************************** */
47

48
/**
49
 * Autoload our class files.
50
 *
51
 * @param string $class_name Class name.
52
 *
53
 * @return void
54
 */
55
function wpseo_auto_load( $class_name ) {
56
        static $classes = null;
×
57

58
        if ( $classes === null ) {
×
59
                $classes = [
×
60
                        'wp_list_table'   => ABSPATH . 'wp-admin/includes/class-wp-list-table.php',
×
61
                        'walker_category' => ABSPATH . 'wp-includes/category-template.php',
×
62
                ];
×
63
        }
64

65
        $cn = strtolower( $class_name );
×
66

67
        if ( ! class_exists( $class_name ) && isset( $classes[ $cn ] ) ) {
×
68
                require_once $classes[ $cn ];
×
69
        }
70
}
71

72
$yoast_autoload_file = WPSEO_PATH . 'vendor/autoload.php';
73

74
if ( is_readable( $yoast_autoload_file ) ) {
75
        $yoast_autoloader = require $yoast_autoload_file;
76
}
77
elseif ( ! class_exists( 'WPSEO_Options' ) ) { // Still checking since might be site-level autoload R.
78
        add_action( 'admin_init', 'yoast_wpseo_missing_autoload', 1 );
79

80
        return;
81
}
82

83
/**
84
 * Include the file from the `symfony/deprecation-contracts` dependency instead of autoloading it via composer.
85
 *
86
 * We need to do that because autoloading via composer prevents the vendor-prefixing of the dependency itself.
87
 * Note that we don't expect the function to be ever called since the OAuth2 library should not provide invalid input.
88
 */
89
$deprecation_contracts_file = WPSEO_PATH . 'vendor_prefixed/symfony/deprecation-contracts/functions.php';
90
if ( is_readable( $deprecation_contracts_file ) ) {
91
        include $deprecation_contracts_file;
92
}
93

94
if ( function_exists( 'spl_autoload_register' ) ) {
95
        spl_autoload_register( 'wpseo_auto_load' );
96
}
97
require_once WPSEO_PATH . 'src/functions.php';
98

99
/* ********************* DEFINES DEPENDING ON AUTOLOADED CODE ********************* */
100

101
/**
102
 * Defaults to production, for safety.
103
 */
104
if ( ! defined( 'YOAST_ENVIRONMENT' ) ) {
105
        define( 'YOAST_ENVIRONMENT', 'production' );
106
}
107

108
if ( YOAST_ENVIRONMENT === 'development' && isset( $yoast_autoloader ) ) {
109
        add_action(
110
                'plugins_loaded',
111
                /**
112
                 * Reregisters the autoloader so that Yoast SEO is at the front.
113
                 * This prevents conflicts with the development versions of our addons.
114
                 * An anonymous function is used so we can use the autoloader variable.
115
                 * As this is only loaded in development removing this action is not a concern.
116
                 *
117
                 * @return void
118
                 */
119
                static function () use ( $yoast_autoloader ) {
120
                        $yoast_autoloader->unregister();
121
                        $yoast_autoloader->register( true );
122
                },
123
                1
124
        );
125
}
126

127
/**
128
 * Only use minified assets when we are in a production environment.
129
 */
130
if ( ! defined( 'WPSEO_CSSJS_SUFFIX' ) ) {
131
        define( 'WPSEO_CSSJS_SUFFIX', ( YOAST_ENVIRONMENT !== 'development' ) ? '.min' : '' );
132
}
133

134
/* ***************************** PLUGIN (DE-)ACTIVATION *************************** */
135

136
/**
137
 * Run single site / network-wide activation of the plugin.
138
 *
139
 * @param bool $networkwide Whether the plugin is being activated network-wide.
140
 *
141
 * @return void
142
 */
143
function wpseo_activate( $networkwide = false ) {
144
        if ( ! is_multisite() || ! $networkwide ) {
×
145
                _wpseo_activate();
×
146
        }
147
        else {
148
                /* Multi-site network activation - activate the plugin for all blogs. */
149
                wpseo_network_activate_deactivate( true );
×
150
        }
151

152
        // This is done so that the 'uninstall_{$file}' is triggered.
153
        register_uninstall_hook( WPSEO_FILE, '__return_false' );
×
154
}
155

156
/**
157
 * Run single site / network-wide de-activation of the plugin.
158
 *
159
 * @param bool $networkwide Whether the plugin is being de-activated network-wide.
160
 *
161
 * @return void
162
 */
163
function wpseo_deactivate( $networkwide = false ) {
164
        if ( ! is_multisite() || ! $networkwide ) {
×
165
                _wpseo_deactivate();
×
166
        }
167
        else {
168
                /* Multi-site network activation - de-activate the plugin for all blogs. */
169
                wpseo_network_activate_deactivate( false );
×
170
        }
171
}
172

173
/**
174
 * Run network-wide (de-)activation of the plugin.
175
 *
176
 * @param bool $activate True for plugin activation, false for de-activation.
177
 *
178
 * @return void
179
 */
180
function wpseo_network_activate_deactivate( $activate = true ) {
181
        global $wpdb;
×
182

183
        $network_blogs = $wpdb->get_col( $wpdb->prepare( "SELECT blog_id FROM $wpdb->blogs WHERE site_id = %d", $wpdb->siteid ) );
×
184

185
        if ( is_array( $network_blogs ) && $network_blogs !== [] ) {
×
186
                foreach ( $network_blogs as $blog_id ) {
×
187
                        switch_to_blog( $blog_id );
×
188

189
                        if ( $activate === true ) {
×
190
                                _wpseo_activate();
×
191
                        }
192
                        else {
193
                                _wpseo_deactivate();
×
194
                        }
195

196
                        restore_current_blog();
×
197
                }
198
        }
199
}
200

201
/**
202
 * Runs on activation of the plugin.
203
 *
204
 * @return void
205
 */
206
function _wpseo_activate() {
207
        require_once WPSEO_PATH . 'inc/wpseo-functions.php';
×
208
        require_once WPSEO_PATH . 'inc/class-wpseo-installation.php';
×
209

210
        wpseo_load_textdomain(); // Make sure we have our translations available for the defaults.
×
211

212
        new WPSEO_Installation();
×
213

214
        WPSEO_Options::get_instance();
×
215
        if ( ! is_multisite() ) {
×
216
                WPSEO_Options::initialize();
×
217
        }
218
        else {
219
                WPSEO_Options::maybe_set_multisite_defaults( true );
×
220
        }
221
        WPSEO_Options::ensure_options_exist();
×
222

223
        if ( ! is_multisite() || ! ms_is_switched() ) {
×
224
                // Constructor has side effects so this registers all hooks.
225
                $GLOBALS['wpseo_rewrite'] = new WPSEO_Rewrite();
×
226
        }
227
        add_action( 'shutdown', [ 'WPSEO_Utils', 'clear_rewrites' ] );
×
228

229
        WPSEO_Options::set( 'indexing_reason', 'first_install' );
×
230
        WPSEO_Options::set( 'first_time_install', true );
×
231
        if ( ! defined( 'WP_CLI' ) || ! WP_CLI ) {
×
232
                WPSEO_Options::set( 'should_redirect_after_install_free', true );
×
233
        }
234
        else {
235
                WPSEO_Options::set( 'activation_redirect_timestamp_free', time() );
×
236
        }
237

238
        // Reset tracking to be disabled by default.
239
        if ( ! YoastSEO()->helpers->product->is_premium() && WPSEO_Options::get( 'toggled_tracking' ) !== true ) {
×
240
                WPSEO_Options::set( 'tracking', false );
×
241
        }
242
        do_action( 'wpseo_register_roles' );
×
243
        WPSEO_Role_Manager_Factory::get()->add();
×
244

245
        do_action( 'wpseo_register_capabilities' );
×
246
        WPSEO_Capability_Manager_Factory::get()->add();
×
247

248
        // Clear cache so the changes are obvious.
249
        WPSEO_Utils::clear_cache();
×
250

251
        do_action( 'wpseo_activate' );
×
252
}
253

254
/**
255
 * On deactivation, flush the rewrite rules so XML sitemaps stop working.
256
 *
257
 * @return void
258
 */
259
function _wpseo_deactivate() {
260
        require_once WPSEO_PATH . 'inc/wpseo-functions.php';
×
261

262
        add_action( 'shutdown', [ 'WPSEO_Utils', 'clear_rewrites' ] );
×
263

264
        // Register capabilities, to make sure they are cleaned up.
265
        do_action( 'wpseo_register_roles' );
×
266
        do_action( 'wpseo_register_capabilities' );
×
267

268
        // Clean up capabilities.
269
        WPSEO_Role_Manager_Factory::get()->remove();
×
270
        WPSEO_Capability_Manager_Factory::get()->remove();
×
271

272
        // Clear cache so the changes are obvious.
273
        WPSEO_Utils::clear_cache();
×
274

275
        do_action( 'wpseo_deactivate' );
×
276
}
277

278
/**
279
 * Run wpseo activation routine on creation / activation of a multisite blog if WPSEO is activated
280
 * network-wide.
281
 *
282
 * Will only be called by multisite actions.
283
 *
284
 * {@internal Unfortunately will fail if the plugin is in the must-use directory.
285
 *            {@link https://core.trac.wordpress.org/ticket/24205} }}
286
 *
287
 * @param int|WP_Site $blog_id Blog ID.
288
 *
289
 * @return void
290
 */
291
function wpseo_on_activate_blog( $blog_id ) {
292
        if ( ! function_exists( 'is_plugin_active_for_network' ) ) {
×
293
                require_once ABSPATH . 'wp-admin/includes/plugin.php';
×
294
        }
295

296
        if ( $blog_id instanceof WP_Site ) {
×
297
                $blog_id = (int) $blog_id->blog_id;
×
298
        }
299

300
        if ( is_plugin_active_for_network( WPSEO_BASENAME ) ) {
×
301
                switch_to_blog( $blog_id );
×
302
                wpseo_activate( false );
×
303
                restore_current_blog();
×
304
        }
305
}
306

307
/* ***************************** PLUGIN LOADING *************************** */
308

309
/**
310
 * Load translations.
311
 *
312
 * @return void
313
 */
314
function wpseo_load_textdomain() {
315
        $wpseo_path = str_replace( '\\', '/', WPSEO_PATH );
×
316
        $mu_path    = str_replace( '\\', '/', WPMU_PLUGIN_DIR );
×
317

318
        if ( stripos( $wpseo_path, $mu_path ) !== false ) {
×
319
                load_muplugin_textdomain( 'wordpress-seo', dirname( WPSEO_BASENAME ) . '/languages/' );
×
320
        }
321
        else {
322
                load_plugin_textdomain( 'wordpress-seo', false, dirname( WPSEO_BASENAME ) . '/languages/' );
×
323
        }
324
}
325

326
add_action( 'plugins_loaded', 'wpseo_load_textdomain' );
327

328

329
/**
330
 * On plugins_loaded: load the minimum amount of essential files for this plugin.
331
 *
332
 * @return void
333
 */
334
function wpseo_init() {
335
        require_once WPSEO_PATH . 'inc/wpseo-functions.php';
×
336
        require_once WPSEO_PATH . 'inc/wpseo-functions-deprecated.php';
×
337

338
        // Make sure our option and meta value validation routines and default values are always registered and available.
339
        WPSEO_Options::get_instance();
×
340
        WPSEO_Meta::init();
×
341

342
        if ( version_compare( WPSEO_Options::get( 'version', 1, [ 'wpseo' ] ), WPSEO_VERSION, '<' ) ) {
×
343
                if ( function_exists( 'opcache_reset' ) ) {
×
344
                        // phpcs:ignore WordPress.PHP.NoSilencedErrors.Discouraged -- Prevent notices when opcache.restrict_api is set.
345
                        @opcache_reset();
×
346
                }
347

348
                new WPSEO_Upgrade();
×
349
                // Get a cleaned up version of the $options.
350
        }
351

352
        $GLOBALS['wpseo_rewrite'] = new WPSEO_Rewrite();
×
353

354
        if ( WPSEO_Options::get( 'enable_xml_sitemap', null, [ 'wpseo' ] ) === true ) {
×
355
                $GLOBALS['wpseo_sitemaps'] = new WPSEO_Sitemaps();
×
356
        }
357

358
        if ( ! wp_doing_ajax() ) {
×
359
                require_once WPSEO_PATH . 'inc/wpseo-non-ajax-functions.php';
×
360
        }
361

362
        $integrations   = [];
×
363
        $integrations[] = new WPSEO_Slug_Change_Watcher();
×
364

365
        foreach ( $integrations as $integration ) {
×
366
                $integration->register_hooks();
×
367
        }
368
}
369

370
/**
371
 * Loads the rest api endpoints.
372
 *
373
 * @return void
374
 */
375
function wpseo_init_rest_api() {
376
        // We can't do anything when requirements are not met.
377
        if ( ! WPSEO_Utils::is_api_available() ) {
×
378
                return;
×
379
        }
380

381
        // Boot up REST API.
382
        $statistics_service = new WPSEO_Statistics_Service( new WPSEO_Statistics() );
×
383

384
        $endpoints   = [];
×
385
        $endpoints[] = new WPSEO_Endpoint_File_Size( new WPSEO_File_Size_Service() );
×
386
        $endpoints[] = new WPSEO_Endpoint_Statistics( $statistics_service );
×
387

388
        foreach ( $endpoints as $endpoint ) {
×
389
                $endpoint->register();
×
390
        }
391
}
392

393
/**
394
 * Used to load the required files on the plugins_loaded hook, instead of immediately.
395
 *
396
 * @return void
397
 */
398
function wpseo_admin_init() {
399
        new WPSEO_Admin_Init();
×
400
}
401

402
/* ***************************** BOOTSTRAP / HOOK INTO WP *************************** */
403
$spl_autoload_exists = function_exists( 'spl_autoload_register' );
404

405
if ( ! $spl_autoload_exists ) {
406
        add_action( 'admin_init', 'yoast_wpseo_missing_spl', 1 );
407
}
408

409
if ( ! wp_installing() && ( $spl_autoload_exists ) ) {
410
        add_action( 'plugins_loaded', 'wpseo_init', 14 );
411
        add_action( 'setup_theme', [ 'Yoast_Dynamic_Rewrites', 'instance' ], 1 );
412
        add_action( 'rest_api_init', 'wpseo_init_rest_api' );
413

414
        if ( is_admin() ) {
415

416
                new Yoast_Notifications();
417

418
                $yoast_addon_manager = new WPSEO_Addon_Manager();
419
                $yoast_addon_manager->register_hooks();
420

421
                if ( wp_doing_ajax() ) {
422
                        require_once WPSEO_PATH . 'admin/ajax.php';
423

424
                        // Plugin conflict ajax hooks.
425
                        new Yoast_Plugin_Conflict_Ajax();
426

427
                        // phpcs:ignore WordPress.Security.NonceVerification.Missing -- Reason: We are not processing form information but only loading the admin init class.
428
                        if ( isset( $_POST['action'] ) && is_string( $_POST['action'] ) ) {
429
                                // phpcs:ignore WordPress.Security.NonceVerification.Missing,WordPress.Security.ValidatedSanitizedInput.InputNotSanitized -- Reason: We are not processing form information but only loading the admin init class, We are strictly comparing only.
430
                                if ( wp_unslash( $_POST['action'] ) === 'inline-save' ) {
431
                                        add_action( 'plugins_loaded', 'wpseo_admin_init', 15 );
432
                                }
433
                        }
434
                }
435
                else {
436
                        add_action( 'plugins_loaded', 'wpseo_admin_init', 15 );
437
                }
438
        }
439

440
        add_action( 'plugins_loaded', 'load_yoast_notifications' );
441

442
        add_action( 'init', [ 'WPSEO_Replace_Vars', 'setup_statics_once' ] );
443

444
        // Initializes the Yoast indexables for the first time.
445
        YoastSEO();
446

447
        /**
448
         * Action called when the Yoast SEO plugin file has loaded.
449
         */
450
        do_action( 'wpseo_loaded' );
451
}
452

453
// Activation and deactivation hook.
454
register_activation_hook( WPSEO_FILE, 'wpseo_activate' );
455
register_deactivation_hook( WPSEO_FILE, 'wpseo_deactivate' );
456

457
add_action( 'wp_initialize_site', 'wpseo_on_activate_blog', 99 );
458
add_action( 'activate_blog', 'wpseo_on_activate_blog' );
459

460
// Registers SEO capabilities.
461
$wpseo_register_capabilities = new WPSEO_Register_Capabilities();
462
$wpseo_register_capabilities->register_hooks();
463

464
// Registers SEO roles.
465
$wpseo_register_capabilities = new WPSEO_Register_Roles();
466
$wpseo_register_capabilities->register_hooks();
467

468
/**
469
 * Wraps for notifications center class.
470
 *
471
 * @return void
472
 */
473
function load_yoast_notifications() {
474
        // Init Yoast_Notification_Center class.
475
        Yoast_Notification_Center::get();
×
476
}
477

478

479
/**
480
 * Throw an error if the PHP SPL extension is disabled (prevent white screens) and self-deactivate plugin.
481
 *
482
 * @since 1.5.4
483
 *
484
 * @return void
485
 */
486
function yoast_wpseo_missing_spl() {
487
        if ( is_admin() ) {
×
488
                add_action( 'admin_notices', 'yoast_wpseo_missing_spl_notice' );
×
489

490
                yoast_wpseo_self_deactivate();
×
491
        }
492
}
493

494
/**
495
 * Returns the notice in case of missing spl extension.
496
 *
497
 * @return void
498
 */
499
function yoast_wpseo_missing_spl_notice() {
500
        $message = esc_html__( 'The Standard PHP Library (SPL) extension seem to be unavailable. Please ask your web host to enable it.', 'wordpress-seo' );
×
501
        yoast_wpseo_activation_failed_notice( $message );
×
502
}
503

504
/**
505
 * Throw an error if the Composer autoload is missing and self-deactivate plugin.
506
 *
507
 * @return void
508
 */
509
function yoast_wpseo_missing_autoload() {
510
        if ( is_admin() ) {
×
511
                add_action( 'admin_notices', 'yoast_wpseo_missing_autoload_notice' );
×
512

513
                yoast_wpseo_self_deactivate();
×
514
        }
515
}
516

517
/**
518
 * Returns the notice in case of missing Composer autoload.
519
 *
520
 * @return void
521
 */
522
function yoast_wpseo_missing_autoload_notice() {
523
        /* translators: %1$s expands to Yoast SEO, %2$s / %3$s: links to the installation manual in the Readme for the Yoast SEO code repository on GitHub */
524
        $message = esc_html__( 'The %1$s plugin installation is incomplete. Please refer to %2$sinstallation instructions%3$s.', 'wordpress-seo' );
×
525
        $message = sprintf( $message, 'Yoast SEO', '<a href="https://github.com/Yoast/wordpress-seo#installation">', '</a>' );
×
526
        yoast_wpseo_activation_failed_notice( $message );
×
527
}
528

529
/**
530
 * Throw an error if the filter extension is disabled (prevent white screens) and self-deactivate plugin.
531
 *
532
 * @since 2.0
533
 * @deprecated 23.3
534
 * @codeCoverageIgnore
535
 *
536
 * @return void
537
 */
538
function yoast_wpseo_missing_filter() {
539
        _deprecated_function( __FUNCTION__, 'Yoast SEO 23.3' );
540
}
541

542
/**
543
 * Returns the notice in case of missing filter extension.
544
 *
545
 * @deprecated 23.3
546
 * @codeCoverageIgnore
547
 *
548
 * @return void
549
 */
550
function yoast_wpseo_missing_filter_notice() {
551
        _deprecated_function( __FUNCTION__, 'Yoast SEO 23.3' );
552
}
553

554
/**
555
 * Echo's the Activation failed notice with any given message.
556
 *
557
 * @param string $message Message string.
558
 *
559
 * @return void
560
 */
561
function yoast_wpseo_activation_failed_notice( $message ) {
562
        $title = sprintf(
×
563
                /* translators: %s: Yoast SEO. */
564
                esc_html__( '%s activation failed', 'wordpress-seo' ),
×
565
                'Yoast SEO'
×
566
        );
×
567

568
        // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped -- This function is only called in 3 places that are safe.
569
        echo '<div class="error yoast-migrated-notice"><h4 class="yoast-notice-migrated-header">' . $title . '</h4><div class="notice-yoast-content"><p>' . strip_tags( $message, '<a>' ) . '</p></div></div>';
×
570
}
571

572
/**
573
 * The method will deactivate the plugin, but only once, done by the static $is_deactivated.
574
 *
575
 * @return void
576
 */
577
function yoast_wpseo_self_deactivate() {
578
        static $is_deactivated;
×
579

580
        if ( $is_deactivated === null ) {
×
581
                $is_deactivated = true;
×
582
                deactivate_plugins( WPSEO_BASENAME );
×
583
                if ( isset( $_GET['activate'] ) ) {
×
584
                        unset( $_GET['activate'] );
×
585
                }
586
        }
587
}
588

589
/**
590
 * Aliasses added in order to keep compatibility with Yoast SEO: Local.
591
 */
592
class_alias( '\Yoast\WP\SEO\Initializers\Initializer_Interface', '\Yoast\WP\SEO\WordPress\Initializer' );
593
class_alias( '\Yoast\WP\SEO\Loadable_Interface', '\Yoast\WP\SEO\WordPress\Loadable' );
594
class_alias( '\Yoast\WP\SEO\Integrations\Integration_Interface', '\Yoast\WP\SEO\WordPress\Integration' );
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