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

Yoast / wordpress-seo / dd6e866a9e6d253114633104d9e3858d807178ba

19 Jun 2024 10:03AM UTC coverage: 48.628% (-4.3%) from 52.936%
dd6e866a9e6d253114633104d9e3858d807178ba

push

github

web-flow
Merge pull request #21431 from Yoast/21429-update-copy-in-the-introduction-and-consent-modals

Updates the copy for the introduction and consent modals

7441 of 13454 branches covered (55.31%)

Branch coverage included in aggregate %.

0 of 3 new or added lines in 2 files covered. (0.0%)

3718 existing lines in 107 files now uncovered.

25100 of 53464 relevant lines covered (46.95%)

62392.47 hits per line

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

0.0
/inc/class-wpseo-utils.php
1
<?php
2
/**
3
 * WPSEO plugin file.
4
 *
5
 * @package WPSEO\Internals
6
 * @since   1.8.0
7
 */
8

9
use Yoast\WP\SEO\Integrations\Feature_Flag_Integration;
10

11
/**
12
 * Group of utility methods for use by WPSEO.
13
 * All methods are static, this is just a sort of namespacing class wrapper.
14
 */
15
class WPSEO_Utils {
16

17
        /**
18
         * Whether the PHP filter extension is enabled.
19
         *
20
         * @since 1.8.0
21
         *
22
         * @var bool
23
         */
24
        public static $has_filters;
25

26
        /**
27
         * Check whether file editing is allowed for the .htaccess and robots.txt files.
28
         *
29
         * {@internal current_user_can() checks internally whether a user is on wp-ms and adjusts accordingly.}}
30
         *
31
         * @since 1.8.0
32
         *
33
         * @return bool
34
         */
35
        public static function allow_system_file_edit() {
×
36
                $allowed = true;
×
37

38
                if ( current_user_can( 'edit_files' ) === false ) {
×
39
                        $allowed = false;
×
40
                }
41

42
                /**
43
                 * Filter: 'wpseo_allow_system_file_edit' - Allow developers to change whether the editing of
44
                 * .htaccess and robots.txt is allowed.
45
                 *
46
                 * @param bool $allowed Whether file editing is allowed.
47
                 */
48
                return apply_filters( 'wpseo_allow_system_file_edit', $allowed );
×
49
        }
50

51
        /**
52
         * Check if the web server is running on Apache or compatible (LiteSpeed).
53
         *
54
         * @since 1.8.0
55
         *
56
         * @return bool
57
         */
UNCOV
58
        public static function is_apache() {
×
UNCOV
59
                if ( ! isset( $_SERVER['SERVER_SOFTWARE'] ) ) {
×
60
                        return false;
×
61
                }
62

UNCOV
63
                $software = sanitize_text_field( wp_unslash( $_SERVER['SERVER_SOFTWARE'] ) );
×
64

UNCOV
65
                return stripos( $software, 'apache' ) !== false || stripos( $software, 'litespeed' ) !== false;
×
66
        }
67

68
        /**
69
         * Check if the web server is running on Nginx.
70
         *
71
         * @since 1.8.0
72
         *
73
         * @return bool
74
         */
UNCOV
75
        public static function is_nginx() {
×
UNCOV
76
                if ( ! isset( $_SERVER['SERVER_SOFTWARE'] ) ) {
×
77
                        return false;
×
78
                }
79

UNCOV
80
                $software = sanitize_text_field( wp_unslash( $_SERVER['SERVER_SOFTWARE'] ) );
×
81

UNCOV
82
                return stripos( $software, 'nginx' ) !== false;
×
83
        }
84

85
        /**
86
         * Check whether a url is relative.
87
         *
88
         * @since 1.8.0
89
         *
90
         * @param string $url URL string to check.
91
         *
92
         * @return bool
93
         */
94
        public static function is_url_relative( $url ) {
×
95
                return YoastSEO()->helpers->url->is_relative( $url );
×
96
        }
97

98
        /**
99
         * Recursively trim whitespace round a string value or of string values within an array.
100
         * Only trims strings to avoid typecasting a variable (to string).
101
         *
102
         * @since 1.8.0
103
         *
104
         * @param mixed $value Value to trim or array of values to trim.
105
         *
106
         * @return mixed Trimmed value or array of trimmed values.
107
         */
108
        public static function trim_recursive( $value ) {
×
109
                if ( is_string( $value ) ) {
×
110
                        $value = trim( $value );
×
111
                }
112
                elseif ( is_array( $value ) ) {
×
113
                        $value = array_map( [ self::class, 'trim_recursive' ], $value );
×
114
                }
115

116
                return $value;
×
117
        }
118

119
        /**
120
         * Emulate the WP native sanitize_text_field function in a %%variable%% safe way.
121
         *
122
         * Sanitize a string from user input or from the db.
123
         *
124
         * - Check for invalid UTF-8;
125
         * - Convert single < characters to entity;
126
         * - Strip all tags;
127
         * - Remove line breaks, tabs and extra white space;
128
         * - Strip octets - BUT DO NOT REMOVE (part of) VARIABLES WHICH WILL BE REPLACED.
129
         *
130
         * @link https://core.trac.wordpress.org/browser/trunk/src/wp-includes/formatting.php for the original.
131
         *
132
         * @since 1.8.0
133
         *
134
         * @param string $value String value to sanitize.
135
         *
136
         * @return string
137
         */
138
        public static function sanitize_text_field( $value ) {
×
139
                $filtered = wp_check_invalid_utf8( $value );
×
140

141
                if ( strpos( $filtered, '<' ) !== false ) {
×
142
                        $filtered = wp_pre_kses_less_than( $filtered );
×
143
                        // This will strip extra whitespace for us.
144
                        $filtered = wp_strip_all_tags( $filtered, true );
×
145
                }
146
                else {
147
                        $filtered = trim( preg_replace( '`[\r\n\t ]+`', ' ', $filtered ) );
×
148
                }
149

150
                $found = false;
×
151
                while ( preg_match( '`[^%](%[a-f0-9]{2})`i', $filtered, $match ) ) {
×
152
                        $filtered = str_replace( $match[1], '', $filtered );
×
153
                        $found    = true;
×
154
                }
155
                unset( $match );
×
156

157
                if ( $found ) {
×
158
                        // Strip out the whitespace that may now exist after removing the octets.
159
                        $filtered = trim( preg_replace( '` +`', ' ', $filtered ) );
×
160
                }
161

162
                /**
163
                 * Filter a sanitized text field string.
164
                 *
165
                 * @since WP 2.9.0
166
                 *
167
                 * @param string $filtered The sanitized string.
168
                 * @param string $str      The string prior to being sanitized.
169
                 */
170
                return apply_filters( 'sanitize_text_field', $filtered, $value ); // phpcs:ignore WordPress.NamingConventions.PrefixAllGlobals -- Using WP native filter.
×
171
        }
172

173
        /**
174
         * Sanitize a url for saving to the database.
175
         * Not to be confused with the old native WP function.
176
         *
177
         * @since 1.8.0
178
         *
179
         * @param string $value             String URL value to sanitize.
180
         * @param array  $allowed_protocols Optional set of allowed protocols.
181
         *
182
         * @return string
183
         */
UNCOV
184
        public static function sanitize_url( $value, $allowed_protocols = [ 'http', 'https' ] ) {
×
185

UNCOV
186
                $url   = '';
×
UNCOV
187
                $parts = wp_parse_url( $value );
×
188

UNCOV
189
                if ( isset( $parts['scheme'], $parts['host'] ) ) {
×
UNCOV
190
                        $url = $parts['scheme'] . '://';
×
191

UNCOV
192
                        if ( isset( $parts['user'] ) ) {
×
UNCOV
193
                                $url .= rawurlencode( $parts['user'] );
×
UNCOV
194
                                $url .= isset( $parts['pass'] ) ? ':' . rawurlencode( $parts['pass'] ) : '';
×
UNCOV
195
                                $url .= '@';
×
196
                        }
197

UNCOV
198
                        $parts['host'] = preg_replace(
×
UNCOV
199
                                '`[^a-z0-9-.:\[\]\\x80-\\xff]`',
×
UNCOV
200
                                '',
×
UNCOV
201
                                strtolower( $parts['host'] )
×
202
                        );
203

UNCOV
204
                        $url .= $parts['host'] . ( isset( $parts['port'] ) ? ':' . intval( $parts['port'] ) : '' );
×
205
                }
206

UNCOV
207
                if ( isset( $parts['path'] ) && strpos( $parts['path'], '/' ) === 0 ) {
×
UNCOV
208
                        $path = explode( '/', wp_strip_all_tags( $parts['path'] ) );
×
UNCOV
209
                        $path = self::sanitize_encoded_text_field( $path );
×
UNCOV
210
                        $url .= str_replace( '%40', '@', implode( '/', $path ) );
×
211
                }
212

UNCOV
213
                if ( ! $url ) {
×
UNCOV
214
                        return '';
×
215
                }
216

UNCOV
217
                if ( isset( $parts['query'] ) ) {
×
UNCOV
218
                        wp_parse_str( $parts['query'], $parsed_query );
×
219

UNCOV
220
                        $parsed_query = array_combine(
×
UNCOV
221
                                self::sanitize_encoded_text_field( array_keys( $parsed_query ) ),
×
UNCOV
222
                                self::sanitize_encoded_text_field( array_values( $parsed_query ) )
×
223
                        );
224

UNCOV
225
                        $url = add_query_arg( $parsed_query, $url );
×
226
                }
227

UNCOV
228
                if ( isset( $parts['fragment'] ) ) {
×
UNCOV
229
                        $url .= '#' . self::sanitize_encoded_text_field( $parts['fragment'] );
×
230
                }
231

UNCOV
232
                if ( strpos( $url, '%' ) !== false ) {
×
UNCOV
233
                        $url = preg_replace_callback(
×
UNCOV
234
                                '`%[a-fA-F0-9]{2}`',
×
235
                                static function ( $octects ) {
UNCOV
236
                                        return strtolower( $octects[0] );
×
UNCOV
237
                                },
×
238
                                $url
239
                        );
240
                }
241

242
                return esc_url_raw( $url, $allowed_protocols );
243
        }
244

245
        /**
246
         * Decode, sanitize and encode the array of strings or the string.
247
         *
248
         * @since 13.3
249
         *
250
         * @param array|string $value The value to sanitize and encode.
251
         *
252
         * @return array|string The sanitized value.
253
         */
254
        public static function sanitize_encoded_text_field( $value ) {
×
255
                if ( is_array( $value ) ) {
×
256
                        return array_map( [ self::class, 'sanitize_encoded_text_field' ], $value );
257
                }
258

259
                return rawurlencode( sanitize_text_field( rawurldecode( $value ) ) );
260
        }
261

262
        /**
263
         * Validate a value as boolean.
264
         *
265
         * @since 1.8.0
266
         *
267
         * @param mixed $value Value to validate.
268
         *
269
         * @return bool
270
         */
271
        public static function validate_bool( $value ) {
×
272
                if ( ! isset( self::$has_filters ) ) {
×
273
                        self::$has_filters = extension_loaded( 'filter' );
274
                }
275

276
                if ( self::$has_filters ) {
×
277
                        return filter_var( $value, FILTER_VALIDATE_BOOLEAN );
278
                }
279
                else {
280
                        return self::emulate_filter_bool( $value );
281
                }
282
        }
283

284
        /**
285
         * Cast a value to bool.
286
         *
287
         * @since 1.8.0
288
         *
289
         * @param mixed $value Value to cast.
290
         *
291
         * @return bool
292
         */
293
        public static function emulate_filter_bool( $value ) {
×
UNCOV
294
                $true  = [
×
295
                        '1',
296
                        'true',
297
                        'True',
298
                        'TRUE',
299
                        'y',
300
                        'Y',
301
                        'yes',
302
                        'Yes',
303
                        'YES',
304
                        'on',
305
                        'On',
306
                        'ON',
307
                ];
308
                $false = [
309
                        '0',
310
                        'false',
311
                        'False',
312
                        'FALSE',
313
                        'n',
314
                        'N',
315
                        'no',
316
                        'No',
317
                        'NO',
318
                        'off',
319
                        'Off',
320
                        'OFF',
321
                ];
322

323
                if ( is_bool( $value ) ) {
×
324
                        return $value;
325
                }
326
                elseif ( is_int( $value ) && ( $value === 0 || $value === 1 ) ) {
×
327
                        return (bool) $value;
328
                }
329
                elseif ( ( is_float( $value ) && ! is_nan( $value ) ) && ( $value === (float) 0 || $value === (float) 1 ) ) {
×
330
                        return (bool) $value;
331
                }
332
                elseif ( is_string( $value ) ) {
×
333
                        $value = trim( $value );
×
334
                        if ( in_array( $value, $true, true ) ) {
×
335
                                return true;
336
                        }
337
                        elseif ( in_array( $value, $false, true ) ) {
×
338
                                return false;
339
                        }
340
                        else {
341
                                return false;
342
                        }
343
                }
344

345
                return false;
346
        }
347

348
        /**
349
         * Validate a value as integer.
350
         *
351
         * @since 1.8.0
352
         *
353
         * @param mixed $value Value to validate.
354
         *
355
         * @return int|bool Int or false in case of failure to convert to int.
356
         */
357
        public static function validate_int( $value ) {
×
358
                if ( ! isset( self::$has_filters ) ) {
×
359
                        self::$has_filters = extension_loaded( 'filter' );
360
                }
361

362
                if ( self::$has_filters ) {
×
363
                        return filter_var( $value, FILTER_VALIDATE_INT );
364
                }
365
                else {
366
                        return self::emulate_filter_int( $value );
367
                }
368
        }
369

370
        /**
371
         * Cast a value to integer.
372
         *
373
         * @since 1.8.0
374
         *
375
         * @param mixed $value Value to cast.
376
         *
377
         * @return int|bool
378
         */
379
        public static function emulate_filter_int( $value ) {
×
380
                if ( is_int( $value ) ) {
×
381
                        return $value;
382
                }
383
                elseif ( is_float( $value ) ) {
384
                        // phpcs:ignore Universal.Operators.StrictComparisons -- Purposeful loose comparison.
385
                        if ( (int) $value == $value && ! is_nan( $value ) ) {
×
386
                                return (int) $value;
387
                        }
388
                        else {
389
                                return false;
390
                        }
391
                }
392
                elseif ( is_string( $value ) ) {
×
393
                        $value = trim( $value );
×
394
                        if ( $value === '' ) {
×
395
                                return false;
396
                        }
397
                        elseif ( ctype_digit( $value ) ) {
×
398
                                return (int) $value;
399
                        }
400
                        elseif ( strpos( $value, '-' ) === 0 && ctype_digit( substr( $value, 1 ) ) ) {
×
401
                                return (int) $value;
402
                        }
403
                        else {
404
                                return false;
405
                        }
406
                }
407

408
                return false;
409
        }
410

411
        /**
412
         * Clears the WP or W3TC cache depending on which is used.
413
         *
414
         * @since 1.8.0
415
         *
416
         * @return void
417
         */
418
        public static function clear_cache() {
×
419
                if ( function_exists( 'w3tc_pgcache_flush' ) ) {
×
420
                        w3tc_pgcache_flush();
421
                }
422
                elseif ( function_exists( 'wp_cache_clear_cache' ) ) {
×
423
                        wp_cache_clear_cache();
424
                }
425
        }
426

427
        /**
428
         * Clear rewrite rules.
429
         *
430
         * @since 1.8.0
431
         *
432
         * @return void
433
         */
434
        public static function clear_rewrites() {
×
435
                update_option( 'rewrite_rules', '' );
×
436
        }
437

438
        /**
439
         * Do simple reliable math calculations without the risk of wrong results.
440
         *
441
         * In the rare case that the bcmath extension would not be loaded, it will return the normal calculation results.
442
         *
443
         * @link http://floating-point-gui.de/
444
         * @link http://php.net/language.types.float.php See the big red warning.
445
         *
446
         * @since 1.5.0
447
         * @since 1.8.0 Moved from stand-alone function to this class.
448
         *
449
         * @param mixed  $number1   Scalar (string/int/float/bool).
450
         * @param string $action    Calculation action to execute. Valid input:
451
         *                          '+' or 'add' or 'addition',
452
         *                          '-' or 'sub' or 'subtract',
453
         *                          '*' or 'mul' or 'multiply',
454
         *                          '/' or 'div' or 'divide',
455
         *                          '%' or 'mod' or 'modulus'
456
         *                          '=' or 'comp' or 'compare'.
457
         * @param mixed  $number2   Scalar (string/int/float/bool).
458
         * @param bool   $round     Whether or not to round the result. Defaults to false.
459
         *                          Will be disregarded for a compare operation.
460
         * @param int    $decimals  Decimals for rounding operation. Defaults to 0.
461
         * @param int    $precision Calculation precision. Defaults to 10.
462
         *
463
         * @return mixed Calculation Result or false if either or the numbers isn't scalar or
464
         *               an invalid operation was passed.
465
         *               - For compare the result will always be an integer.
466
         *               - For all other operations, the result will either be an integer (preferred)
467
         *                 or a float.
468
         */
469
        public static function calc( $number1, $action, $number2, $round = false, $decimals = 0, $precision = 10 ) {
×
470
                static $bc;
471

472
                if ( ! is_scalar( $number1 ) || ! is_scalar( $number2 ) ) {
×
473
                        return false;
474
                }
475

476
                if ( ! isset( $bc ) ) {
×
477
                        $bc = extension_loaded( 'bcmath' );
478
                }
479

480
                if ( $bc ) {
×
481
                        $number1 = number_format( $number1, 10, '.', '' );
×
482
                        $number2 = number_format( $number2, 10, '.', '' );
483
                }
484

485
                $result  = null;
×
486
                $compare = false;
487

488
                switch ( $action ) {
489
                        case '+':
×
490
                        case 'add':
×
491
                        case 'addition':
×
492
                                $result = ( $bc ) ? bcadd( $number1, $number2, $precision ) /* string */ : ( $number1 + $number2 );
×
493
                                break;
494

495
                        case '-':
×
496
                        case 'sub':
×
497
                        case 'subtract':
×
498
                                $result = ( $bc ) ? bcsub( $number1, $number2, $precision ) /* string */ : ( $number1 - $number2 );
×
499
                                break;
500

501
                        case '*':
×
502
                        case 'mul':
×
503
                        case 'multiply':
×
504
                                $result = ( $bc ) ? bcmul( $number1, $number2, $precision ) /* string */ : ( $number1 * $number2 );
×
505
                                break;
506

507
                        case '/':
×
508
                        case 'div':
×
509
                        case 'divide':
×
510
                                if ( $bc ) {
×
511
                                        $result = bcdiv( $number1, $number2, $precision ); // String, or NULL if right_operand is 0.
512
                                }
513
                                elseif ( $number2 != 0 ) { // phpcs:ignore Universal.Operators.StrictComparisons -- Purposeful loose comparison.
×
514
                                        $result = ( $number1 / $number2 );
515
                                }
516

517
                                if ( ! isset( $result ) ) {
×
518
                                        $result = 0;
519
                                }
520
                                break;
521

522
                        case '%':
×
523
                        case 'mod':
×
524
                        case 'modulus':
×
525
                                if ( $bc ) {
×
526
                                        $result = bcmod( $number1, $number2 ); // String, or NULL if modulus is 0.
527
                                }
528
                                elseif ( $number2 != 0 ) { // phpcs:ignore Universal.Operators.StrictComparisons -- Purposeful loose comparison.
×
529
                                        $result = ( $number1 % $number2 );
530
                                }
531

532
                                if ( ! isset( $result ) ) {
×
533
                                        $result = 0;
534
                                }
535
                                break;
536

537
                        case '=':
×
538
                        case 'comp':
×
539
                        case 'compare':
×
540
                                $compare = true;
×
541
                                if ( $bc ) {
×
542
                                        $result = bccomp( $number1, $number2, $precision ); // Returns int 0, 1 or -1.
543
                                }
544
                                else {
545
                                        // phpcs:ignore Universal.Operators.StrictComparisons -- Purposeful loose comparison.
546
                                        $result = ( $number1 == $number2 ) ? 0 : ( ( $number1 > $number2 ) ? 1 : -1 );
547
                                }
548
                                break;
549
                }
550

551
                if ( isset( $result ) ) {
×
552
                        if ( $compare === false ) {
×
553
                                if ( $round === true ) {
×
554
                                        $result = round( floatval( $result ), $decimals );
×
555
                                        if ( $decimals === 0 ) {
×
556
                                                $result = (int) $result;
557
                                        }
558
                                }
559
                                else {
560
                                        // phpcs:ignore Universal.Operators.StrictComparisons -- Purposeful loose comparison.
561
                                        $result = ( intval( $result ) == $result ) ? intval( $result ) : floatval( $result );
562
                                }
563
                        }
564

565
                        return $result;
566
                }
567

568
                return false;
569
        }
570

571
        /**
572
         * Trim whitespace and NBSP (Non-breaking space) from string.
573
         *
574
         * @since 2.0.0
575
         *
576
         * @param string $text String input to trim.
577
         *
578
         * @return string
579
         */
UNCOV
580
        public static function trim_nbsp_from_string( $text ) {
×
UNCOV
581
                $find = [ '&nbsp;', chr( 0xC2 ) . chr( 0xA0 ) ];
×
UNCOV
582
                $text = str_replace( $find, ' ', $text );
×
583
                $text = trim( $text );
584

585
                return $text;
586
        }
587

588
        /**
589
         * Check if a string is a valid datetime.
590
         *
591
         * @since 2.0.0
592
         *
593
         * @param string $datetime String input to check as valid input for DateTime class.
594
         *
595
         * @return bool
596
         */
597
        public static function is_valid_datetime( $datetime ) {
598
                return YoastSEO()->helpers->date->is_valid_datetime( $datetime );
599
        }
600

601
        /**
602
         * Format the URL to be sure it is okay for using as a redirect url.
603
         *
604
         * This method will parse the URL and combine them in one string.
605
         *
606
         * @since 2.3.0
607
         *
608
         * @param string $url URL string.
609
         *
610
         * @return mixed
611
         */
612
        public static function format_url( $url ) {
×
613
                $parsed_url = wp_parse_url( $url );
614

615
                $formatted_url = '';
×
616
                if ( ! empty( $parsed_url['path'] ) ) {
×
617
                        $formatted_url = $parsed_url['path'];
618
                }
619

620
                // Prepend a slash if first char != slash.
621
                if ( stripos( $formatted_url, '/' ) !== 0 ) {
×
622
                        $formatted_url = '/' . $formatted_url;
623
                }
624

625
                // Append 'query' string if it exists.
626
                if ( ! empty( $parsed_url['query'] ) ) {
×
627
                        $formatted_url .= '?' . $parsed_url['query'];
628
                }
629

630
                return apply_filters( 'wpseo_format_admin_url', $formatted_url );
631
        }
632

633
        /**
634
         * Retrieves the sitename.
635
         *
636
         * @since 3.0.0
637
         *
638
         * @return string
639
         */
640
        public static function get_site_name() {
641
                return YoastSEO()->helpers->site->get_site_name();
642
        }
643

644
        /**
645
         * Check if the current opened page is a Yoast SEO page.
646
         *
647
         * @since 3.0.0
648
         *
649
         * @return bool
650
         */
651
        public static function is_yoast_seo_page() {
652
                return YoastSEO()->helpers->current_page->is_yoast_seo_page();
653
        }
654

655
        /**
656
         * Check if the current opened page belongs to Yoast SEO Free.
657
         *
658
         * @since 3.3.0
659
         *
660
         * @param string $current_page The current page the user is on.
661
         *
662
         * @return bool
663
         */
664
        public static function is_yoast_seo_free_page( $current_page ) {
665
                $yoast_seo_free_pages = [
666
                        'wpseo_dashboard',
667
                        'wpseo_tools',
668
                        'wpseo_search_console',
669
                        'wpseo_licenses',
670
                ];
671

672
                return in_array( $current_page, $yoast_seo_free_pages, true );
673
        }
674

675
        /**
676
         * Determine if Yoast SEO is in development mode?
677
         *
678
         * Inspired by JetPack (https://github.com/Automattic/jetpack/blob/master/class.jetpack.php#L1383-L1406).
679
         *
680
         * @since 3.0.0
681
         *
682
         * @return bool
683
         */
684
        public static function is_development_mode() {
×
685
                $development_mode = false;
686

687
                if ( defined( 'YOAST_ENVIRONMENT' ) && YOAST_ENVIRONMENT === 'development' ) {
×
688
                        $development_mode = true;
689
                }
690
                elseif ( defined( 'WPSEO_DEBUG' ) ) {
×
691
                        $development_mode = WPSEO_DEBUG;
692
                }
693
                elseif ( site_url() && strpos( site_url(), '.' ) === false ) {
×
694
                        $development_mode = true;
695
                }
696

697
                /**
698
                 * Filter the Yoast SEO development mode.
699
                 *
700
                 * @since 3.0
701
                 *
702
                 * @param bool $development_mode Is Yoast SEOs development mode active.
703
                 */
704
                return apply_filters( 'yoast_seo_development_mode', $development_mode );
705
        }
706

707
        /**
708
         * Retrieve home URL with proper trailing slash.
709
         *
710
         * @since 3.3.0
711
         *
712
         * @param string      $path   Path relative to home URL.
713
         * @param string|null $scheme Scheme to apply.
714
         *
715
         * @return string Home URL with optional path, appropriately slashed if not.
716
         */
717
        public static function home_url( $path = '', $scheme = null ) {
718
                return YoastSEO()->helpers->url->home( $path, $scheme );
719
        }
720

721
        /**
722
         * Checks if the WP-REST-API is available.
723
         *
724
         * @since 3.6
725
         * @since 3.7 Introduced the $minimum_version parameter.
726
         *
727
         * @param string $minimum_version The minimum version the API should be.
728
         *
729
         * @return bool Returns true if the API is available.
730
         */
731
        public static function is_api_available( $minimum_version = '2.0' ) {
732
                return ( defined( 'REST_API_VERSION' )
733
                        && version_compare( REST_API_VERSION, $minimum_version, '>=' ) );
734
        }
735

736
        /**
737
         * Determine whether or not the metabox should be displayed for a post type.
738
         *
739
         * @param string|null $post_type Optional. The post type to check the visibility of the metabox for.
740
         *
741
         * @return bool Whether or not the metabox should be displayed.
742
         */
743
        protected static function display_post_type_metabox( $post_type = null ) {
×
744
                if ( ! isset( $post_type ) ) {
×
745
                        $post_type = get_post_type();
746
                }
747

748
                if ( ! isset( $post_type ) || ! WPSEO_Post_Type::is_post_type_accessible( $post_type ) ) {
×
749
                        return false;
750
                }
751

752
                if ( $post_type === 'attachment' && WPSEO_Options::get( 'disable-attachment' ) ) {
×
753
                        return false;
754
                }
755

756
                return apply_filters( 'wpseo_enable_editor_features_' . $post_type, WPSEO_Options::get( 'display-metabox-pt-' . $post_type ) );
757
        }
758

759
        /**
760
         * Determine whether or not the metabox should be displayed for a taxonomy.
761
         *
762
         * @param string|null $taxonomy Optional. The post type to check the visibility of the metabox for.
763
         *
764
         * @return bool Whether or not the metabox should be displayed.
765
         */
766
        protected static function display_taxonomy_metabox( $taxonomy = null ) {
×
767
                if ( ! isset( $taxonomy ) || ! in_array( $taxonomy, get_taxonomies( [ 'public' => true ], 'names' ), true ) ) {
×
768
                        return false;
769
                }
770

771
                return WPSEO_Options::get( 'display-metabox-tax-' . $taxonomy );
772
        }
773

774
        /**
775
         * Determines whether the metabox is active for the given identifier and type.
776
         *
777
         * @param string $identifier The identifier to check for.
778
         * @param string $type       The type to check for.
779
         *
780
         * @return bool Whether or not the metabox is active.
781
         */
782
        public static function is_metabox_active( $identifier, $type ) {
×
783
                if ( $type === 'post_type' ) {
×
784
                        return self::display_post_type_metabox( $identifier );
785
                }
786

787
                if ( $type === 'taxonomy' ) {
×
788
                        return self::display_taxonomy_metabox( $identifier );
789
                }
790

791
                return false;
792
        }
793

794
        /**
795
         * Determines whether the plugin is active for the entire network.
796
         *
797
         * @return bool Whether the plugin is network-active.
798
         */
799
        public static function is_plugin_network_active() {
800
                return YoastSEO()->helpers->url->is_plugin_network_active();
801
        }
802

803
        /**
804
         * Gets the type of the current post.
805
         *
806
         * @return string The post type, or an empty string.
807
         */
808
        public static function get_post_type() {
×
809
                $wp_screen = get_current_screen();
810

811
                if ( $wp_screen !== null && ! empty( $wp_screen->post_type ) ) {
×
812
                        return $wp_screen->post_type;
813
                }
814
                return '';
815
        }
816

817
        /**
818
         * Gets the type of the current page.
819
         *
820
         * @return string Returns 'post' if the current page is a post edit page. Taxonomy in other cases.
821
         */
822
        public static function get_page_type() {
×
823
                global $pagenow;
×
824
                if ( WPSEO_Metabox::is_post_edit( $pagenow ) ) {
×
825
                        return 'post';
826
                }
827

828
                return 'taxonomy';
829
        }
830

831
        /**
832
         * Getter for the Adminl10n array. Applies the wpseo_admin_l10n filter.
833
         *
834
         * @return array The Adminl10n array.
835
         */
836
        public static function get_admin_l10n() {
×
837
                $post_type = self::get_post_type();
×
838
                $page_type = self::get_page_type();
839

840
                $label_object = false;
×
841
                $no_index     = false;
842

843
                if ( $page_type === 'post' ) {
×
844
                        $label_object = get_post_type_object( $post_type );
×
845
                        $no_index     = WPSEO_Options::get( 'noindex-' . $post_type, false );
846
                }
847
                else {
848
                        $label_object = WPSEO_Taxonomy::get_labels();
849

850
                        $wp_screen = get_current_screen();
851

852
                        if ( $wp_screen !== null && ! empty( $wp_screen->taxonomy ) ) {
×
853
                                $taxonomy_slug = $wp_screen->taxonomy;
×
854
                                $no_index      = WPSEO_Options::get( 'noindex-tax-' . $taxonomy_slug, false );
855
                        }
856
                }
857

UNCOV
858
                $wpseo_admin_l10n = [
×
859
                        'displayAdvancedTab'    => WPSEO_Capability_Utils::current_user_can( 'wpseo_edit_advanced_metadata' ) || ! WPSEO_Options::get( 'disableadvanced_meta' ),
×
860
                        'noIndex'               => (bool) $no_index,
×
861
                        'isPostType'            => (bool) get_post_type(),
×
862
                        'postType'              => get_post_type(),
×
863
                        'postTypeNamePlural'    => ( $page_type === 'post' ) ? $label_object->label : $label_object->name,
×
864
                        'postTypeNameSingular'  => ( $page_type === 'post' ) ? $label_object->labels->singular_name : $label_object->singular_name,
×
865
                        'isBreadcrumbsDisabled' => WPSEO_Options::get( 'breadcrumbs-enable', false ) !== true && ! current_theme_supports( 'yoast-seo-breadcrumbs' ),
866
                        // phpcs:ignore Generic.ControlStructures.DisallowYodaConditions -- Bug: squizlabs/PHP_CodeSniffer#2962.
867
                        'isPrivateBlog'         => ( (string) get_option( 'blog_public' ) ) === '0',
×
868
                        'news_seo_is_active'    => ( defined( 'WPSEO_NEWS_FILE' ) ),
869
                ];
870

871
                $additional_entries = apply_filters( 'wpseo_admin_l10n', [] );
×
872
                if ( is_array( $additional_entries ) ) {
×
873
                        $wpseo_admin_l10n = array_merge( $wpseo_admin_l10n, $additional_entries );
874
                }
875

876
                return $wpseo_admin_l10n;
877
        }
878

879
        /**
880
         * Retrieves the analysis worker log level. Defaults to errors only.
881
         *
882
         * Uses bool YOAST_SEO_DEBUG as flag to enable logging. Off equals ERROR.
883
         * Uses string YOAST_SEO_DEBUG_ANALYSIS_WORKER as log level for the Analysis
884
         * Worker. Defaults to INFO.
885
         * Can be: TRACE, DEBUG, INFO, WARN or ERROR.
886
         *
887
         * @return string The log level to use.
888
         */
889
        public static function get_analysis_worker_log_level() {
×
890
                if ( defined( 'YOAST_SEO_DEBUG' ) && YOAST_SEO_DEBUG ) {
×
891
                        return defined( 'YOAST_SEO_DEBUG_ANALYSIS_WORKER' ) ? YOAST_SEO_DEBUG_ANALYSIS_WORKER : 'INFO';
892
                }
893

894
                return 'ERROR';
895
        }
896

897
        /**
898
         * Returns the unfiltered home URL.
899
         *
900
         * In case WPML is installed, returns the original home_url and not the WPML version.
901
         * In case of a multisite setup we return the network_home_url.
902
         *
903
         * @codeCoverageIgnore
904
         *
905
         * @return string The home url.
906
         */
907
        public static function get_home_url() {
908
                return YoastSEO()->helpers->url->network_safe_home_url();
909
        }
910

911
        /**
912
         * Prepares data for outputting as JSON.
913
         *
914
         * @param array $data The data to format.
915
         *
916
         * @return false|string The prepared JSON string.
917
         */
918
        public static function format_json_encode( $data ) {
×
919
                $flags = ( JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE );
920

921
                if ( self::is_development_mode() ) {
×
922
                        $flags = ( $flags | JSON_PRETTY_PRINT );
923

924
                        /**
925
                         * Filter the Yoast SEO development mode.
926
                         *
927
                         * @param array $data Allows filtering of the JSON data for debug purposes.
928
                         */
929
                        $data = apply_filters( 'wpseo_debug_json_data', $data );
930
                }
931

932
                // phpcs:ignore Yoast.Yoast.JsonEncodeAlternative.FoundWithAdditionalParams -- This is the definition of format_json_encode.
933
                return wp_json_encode( $data, $flags );
934
        }
935

936
        /**
937
         * Extends the allowed post tags with accessibility-related attributes.
938
         *
939
         * @codeCoverageIgnore
940
         *
941
         * @param array $allowed_post_tags The allowed post tags.
942
         *
943
         * @return array The allowed tags including post tags, input tags and select tags.
944
         */
945
        public static function extend_kses_post_with_a11y( $allowed_post_tags ) {
946
                static $a11y_tags;
947

948
                if ( isset( $a11y_tags ) === false ) {
949
                        $a11y_tags = [
950
                                'button'   => [
951
                                        'aria-expanded' => true,
952
                                        'aria-controls' => true,
953
                                ],
954
                                'div'      => [
955
                                        'tabindex' => true,
956
                                ],
957
                                // Below are attributes that are needed for backwards compatibility (WP < 5.1).
958
                                'span'     => [
959
                                        'aria-hidden' => true,
960
                                ],
961
                                'input'    => [
962
                                        'aria-describedby' => true,
963
                                ],
964
                                'select'   => [
965
                                        'aria-describedby' => true,
966
                                ],
967
                                'textarea' => [
968
                                        'aria-describedby' => true,
969
                                ],
970
                        ];
971

972
                        // Add the global allowed attributes to each html element.
973
                        $a11y_tags = array_map( '_wp_add_global_attributes', $a11y_tags );
974
                }
975

976
                return array_merge_recursive( $allowed_post_tags, $a11y_tags );
977
        }
978

979
        /**
980
         * Extends the allowed post tags with input, select and option tags.
981
         *
982
         * @codeCoverageIgnore
983
         *
984
         * @param array $allowed_post_tags The allowed post tags.
985
         *
986
         * @return array The allowed tags including post tags, input tags, select tags and option tags.
987
         */
988
        public static function extend_kses_post_with_forms( $allowed_post_tags ) {
989
                static $input_tags;
990

991
                if ( isset( $input_tags ) === false ) {
992
                        $input_tags = [
993
                                'input' => [
994
                                        'accept'          => true,
995
                                        'accesskey'       => true,
996
                                        'align'           => true,
997
                                        'alt'             => true,
998
                                        'autocomplete'    => true,
999
                                        'autofocus'       => true,
1000
                                        'checked'         => true,
1001
                                        'contenteditable' => true,
1002
                                        'dirname'         => true,
1003
                                        'disabled'        => true,
1004
                                        'draggable'       => true,
1005
                                        'dropzone'        => true,
1006
                                        'form'            => true,
1007
                                        'formaction'      => true,
1008
                                        'formenctype'     => true,
1009
                                        'formmethod'      => true,
1010
                                        'formnovalidate'  => true,
1011
                                        'formtarget'      => true,
1012
                                        'height'          => true,
1013
                                        'hidden'          => true,
1014
                                        'lang'            => true,
1015
                                        'list'            => true,
1016
                                        'max'             => true,
1017
                                        'maxlength'       => true,
1018
                                        'min'             => true,
1019
                                        'multiple'        => true,
1020
                                        'name'            => true,
1021
                                        'pattern'         => true,
1022
                                        'placeholder'     => true,
1023
                                        'readonly'        => true,
1024
                                        'required'        => true,
1025
                                        'size'            => true,
1026
                                        'spellcheck'      => true,
1027
                                        'src'             => true,
1028
                                        'step'            => true,
1029
                                        'tabindex'        => true,
1030
                                        'translate'       => true,
1031
                                        'type'            => true,
1032
                                        'value'           => true,
1033
                                        'width'           => true,
1034

1035
                                        /*
1036
                                         * Below are attributes that are needed for backwards compatibility (WP < 5.1).
1037
                                         * They are used for the social media image in the metabox.
1038
                                         * These can be removed once we move to the React versions of the social previews.
1039
                                         */
1040
                                        'data-target'     => true,
1041
                                        'data-target-id'  => true,
1042
                                ],
1043
                                'select' => [
1044
                                        'accesskey'       => true,
1045
                                        'autofocus'       => true,
1046
                                        'contenteditable' => true,
1047
                                        'disabled'        => true,
1048
                                        'draggable'       => true,
1049
                                        'dropzone'        => true,
1050
                                        'form'            => true,
1051
                                        'hidden'          => true,
1052
                                        'lang'            => true,
1053
                                        'multiple'        => true,
1054
                                        'name'            => true,
1055
                                        'onblur'          => true,
1056
                                        'onchange'        => true,
1057
                                        'oncontextmenu'   => true,
1058
                                        'onfocus'         => true,
1059
                                        'oninput'         => true,
1060
                                        'oninvalid'       => true,
1061
                                        'onreset'         => true,
1062
                                        'onsearch'        => true,
1063
                                        'onselect'        => true,
1064
                                        'onsubmit'        => true,
1065
                                        'required'        => true,
1066
                                        'size'            => true,
1067
                                        'spellcheck'      => true,
1068
                                        'tabindex'        => true,
1069
                                        'translate'       => true,
1070
                                ],
1071
                                'option' => [
1072
                                        'class'    => true,
1073
                                        'disabled' => true,
1074
                                        'id'       => true,
1075
                                        'label'    => true,
1076
                                        'selected' => true,
1077
                                        'value'    => true,
1078
                                ],
1079
                        ];
1080

1081
                        // Add the global allowed attributes to each html element.
1082
                        $input_tags = array_map( '_wp_add_global_attributes', $input_tags );
1083
                }
1084

1085
                return array_merge_recursive( $allowed_post_tags, $input_tags );
1086
        }
1087

1088
        /**
1089
         * Gets an array of enabled features.
1090
         *
1091
         * @return string[] The array of enabled features.
1092
         */
UNCOV
1093
        public static function retrieve_enabled_features() {
×
1094
                /**
1095
                 * The feature flag integration.
1096
                 *
1097
                 * @var Feature_Flag_Integration $feature_flag_integration;
1098
                 */
UNCOV
1099
                $feature_flag_integration = YoastSEO()->classes->get( Feature_Flag_Integration::class );
×
1100
                return $feature_flag_integration->get_enabled_features();
1101
        }
1102
}
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