PageRenderTime 59ms CodeModel.GetById 12ms app.highlight 37ms RepoModel.GetById 2ms app.codeStats 0ms

/wp-content/plugins/wordpress-seo/inc/class-wpseo-utils.php

https://bitbucket.org/carloskikea/helpet
PHP | 1091 lines | 502 code | 121 blank | 468 comment | 88 complexity | f493cb59425354943bc3755c04b59a90 MD5 | raw file
   1<?php
   2/**
   3 * WPSEO plugin file.
   4 *
   5 * @package WPSEO\Internals
   6 * @since   1.8.0
   7 */
   8
   9/**
  10 * Group of utility methods for use by WPSEO.
  11 * All methods are static, this is just a sort of namespacing class wrapper.
  12 */
  13class WPSEO_Utils {
  14
  15	/**
  16	 * @var bool $has_filters Whether the PHP filter extension is enabled.
  17	 * @static
  18	 * @since 1.8.0
  19	 */
  20	public static $has_filters;
  21
  22	/**
  23	 * @var array Notifications to be shown in the JavaScript console.
  24	 * @static
  25	 * @since 3.3.2
  26	 */
  27	protected static $console_notifications = array();
  28
  29	/**
  30	 * Check whether the current user is allowed to access the configuration.
  31	 *
  32	 * @static
  33	 *
  34	 * @since 1.8.0
  35	 *
  36	 * @return boolean
  37	 */
  38	public static function grant_access() {
  39		// @todo Add deprecation warning.
  40		if ( ! is_multisite() ) {
  41			return true;
  42		}
  43
  44		$options = get_site_option( 'wpseo_ms' );
  45
  46		if ( empty( $options['access'] ) || $options['access'] === 'admin' ) {
  47			return current_user_can( 'wpseo_manage_options' );
  48		}
  49
  50		return is_super_admin();
  51	}
  52
  53	/**
  54	 * Check whether file editing is allowed for the .htaccess and robots.txt files.
  55	 *
  56	 * {@internal current_user_can() checks internally whether a user is on wp-ms and adjusts accordingly.}}
  57	 *
  58	 * @static
  59	 *
  60	 * @since    1.8.0
  61	 *
  62	 * @return bool
  63	 */
  64	public static function allow_system_file_edit() {
  65		$allowed = true;
  66
  67		if ( current_user_can( 'edit_files' ) === false ) {
  68			$allowed = false;
  69		}
  70
  71		/**
  72		 * Filter: 'wpseo_allow_system_file_edit' - Allow developers to change whether the editing of
  73		 * .htaccess and robots.txt is allowed.
  74		 *
  75		 * @api bool $allowed Whether file editing is allowed.
  76		 */
  77
  78		return apply_filters( 'wpseo_allow_system_file_edit', $allowed );
  79	}
  80
  81	/**
  82	 * Check if the web server is running on Apache.
  83	 *
  84	 * @static
  85	 *
  86	 * @since 1.8.0
  87	 *
  88	 * @return bool
  89	 */
  90	public static function is_apache() {
  91		if ( isset( $_SERVER['SERVER_SOFTWARE'] ) && stristr( $_SERVER['SERVER_SOFTWARE'], 'apache' ) !== false ) {
  92			return true;
  93		}
  94
  95		return false;
  96	}
  97
  98	/**
  99	 * Check if the web server is running on Nginx.
 100	 *
 101	 * @static
 102	 *
 103	 * @since 1.8.0
 104	 *
 105	 * @return bool
 106	 */
 107	public static function is_nginx() {
 108		if ( isset( $_SERVER['SERVER_SOFTWARE'] ) && stristr( $_SERVER['SERVER_SOFTWARE'], 'nginx' ) !== false ) {
 109			return true;
 110		}
 111
 112		return false;
 113	}
 114
 115	/**
 116	 * Register a notification to be shown in the JavaScript console.
 117	 *
 118	 * @since 3.3.2
 119	 *
 120	 * @param string $identifier    Notification identifier.
 121	 * @param string $message       Message to be shown.
 122	 * @param bool   $one_time_only Only show once (if added multiple times).
 123	 */
 124	public static function javascript_console_notification( $identifier, $message, $one_time_only = false ) {
 125		static $registered_hook;
 126
 127		if ( is_null( $registered_hook ) ) {
 128			add_action( 'admin_footer', array( __CLASS__, 'localize_console_notices' ), 999 );
 129			$registered_hook = true;
 130		}
 131
 132		$prefix = 'Yoast SEO: ';
 133		if ( substr( $message, 0, strlen( $prefix ) ) !== $prefix ) {
 134			$message = $prefix . $message;
 135		}
 136
 137		if ( $one_time_only ) {
 138			self::$console_notifications[ $identifier ] = $message;
 139		}
 140		else {
 141			self::$console_notifications[] = $message;
 142		}
 143	}
 144
 145	/**
 146	 * Localize the console notifications to JavaScript.
 147	 *
 148	 * @since 3.3.2
 149	 */
 150	public static function localize_console_notices() {
 151		if ( empty( self::$console_notifications ) ) {
 152			return;
 153		}
 154
 155		wp_localize_script( WPSEO_Admin_Asset_Manager::PREFIX . 'admin-global-script', 'wpseoConsoleNotifications', array_values( self::$console_notifications ) );
 156	}
 157
 158	/**
 159	 * Check whether a url is relative.
 160	 *
 161	 * @since 1.8.0
 162	 *
 163	 * @param string $url URL string to check.
 164	 *
 165	 * @return bool
 166	 */
 167	public static function is_url_relative( $url ) {
 168		return ( strpos( $url, 'http' ) !== 0 && strpos( $url, '//' ) !== 0 );
 169	}
 170
 171	/**
 172	 * List all the available user roles.
 173	 *
 174	 * @since 1.8.0
 175	 *
 176	 * @static
 177	 *
 178	 * @return array $roles
 179	 */
 180	public static function get_roles() {
 181		global $wp_roles;
 182
 183		if ( ! isset( $wp_roles ) ) {
 184			$wp_roles = new WP_Roles();
 185		}
 186
 187		$roles = $wp_roles->get_names();
 188
 189		return $roles;
 190	}
 191
 192	/**
 193	 * Standardize whitespace in a string.
 194	 *
 195	 * Replace line breaks, carriage returns, tabs with a space, then remove double spaces.
 196	 *
 197	 * @since 1.8.0
 198	 *
 199	 * @param string $string String input to standardize.
 200	 *
 201	 * @return string
 202	 */
 203	public static function standardize_whitespace( $string ) {
 204		return trim( str_replace( '  ', ' ', str_replace( array( "\t", "\n", "\r", "\f" ), ' ', $string ) ) );
 205	}
 206
 207	/**
 208	 * First strip out registered and enclosing shortcodes using native WordPress strip_shortcodes function.
 209	 * Then strip out the shortcodes with a filthy regex, because people don't properly register their shortcodes.
 210	 *
 211	 * @static
 212	 *
 213	 * @since 1.8.0
 214	 *
 215	 * @param string $text Input string that might contain shortcodes.
 216	 *
 217	 * @return string $text String without shortcodes.
 218	 */
 219	public static function strip_shortcode( $text ) {
 220		return preg_replace( '`\[[^\]]+\]`s', '', strip_shortcodes( $text ) );
 221	}
 222
 223	/**
 224	 * Recursively trim whitespace round a string value or of string values within an array.
 225	 * Only trims strings to avoid typecasting a variable (to string).
 226	 *
 227	 * @static
 228	 *
 229	 * @since 1.8.0
 230	 *
 231	 * @param mixed $value Value to trim or array of values to trim.
 232	 *
 233	 * @return mixed Trimmed value or array of trimmed values.
 234	 */
 235	public static function trim_recursive( $value ) {
 236		if ( is_string( $value ) ) {
 237			$value = trim( $value );
 238		}
 239		elseif ( is_array( $value ) ) {
 240			$value = array_map( array( __CLASS__, 'trim_recursive' ), $value );
 241		}
 242
 243		return $value;
 244	}
 245
 246	/**
 247	 * Translates a decimal analysis score into a textual one.
 248	 *
 249	 * @static
 250	 *
 251	 * @since 1.8.0
 252	 *
 253	 * @param int  $val       The decimal score to translate.
 254	 * @param bool $css_value Whether to return the i18n translated score or the CSS class value.
 255	 *
 256	 * @return string
 257	 */
 258	public static function translate_score( $val, $css_value = true ) {
 259		$seo_rank = WPSEO_Rank::from_numeric_score( $val );
 260
 261		if ( $css_value ) {
 262			return $seo_rank->get_css_class();
 263		}
 264
 265		return $seo_rank->get_label();
 266	}
 267
 268	/**
 269	 * Emulate the WP native sanitize_text_field function in a %%variable%% safe way.
 270	 *
 271	 * @see   https://core.trac.wordpress.org/browser/trunk/src/wp-includes/formatting.php for the original
 272	 *
 273	 * Sanitize a string from user input or from the db.
 274	 *
 275	 * - Check for invalid UTF-8,
 276	 * - Convert single < characters to entity,
 277	 * - Strip all tags,
 278	 * - Remove line breaks, tabs and extra white space,
 279	 * - Strip octets - BUT DO NOT REMOVE (part of) VARIABLES WHICH WILL BE REPLACED.
 280	 *
 281	 * @static
 282	 *
 283	 * @since 1.8.0
 284	 *
 285	 * @param string $value String value to sanitize.
 286	 *
 287	 * @return string
 288	 */
 289	public static function sanitize_text_field( $value ) {
 290		$filtered = wp_check_invalid_utf8( $value );
 291
 292		if ( strpos( $filtered, '<' ) !== false ) {
 293			$filtered = wp_pre_kses_less_than( $filtered );
 294			// This will strip extra whitespace for us.
 295			$filtered = wp_strip_all_tags( $filtered, true );
 296		}
 297		else {
 298			$filtered = trim( preg_replace( '`[\r\n\t ]+`', ' ', $filtered ) );
 299		}
 300
 301		$found = false;
 302		while ( preg_match( '`[^%](%[a-f0-9]{2})`i', $filtered, $match ) ) {
 303			$filtered = str_replace( $match[1], '', $filtered );
 304			$found    = true;
 305		}
 306		unset( $match );
 307
 308		if ( $found ) {
 309			// Strip out the whitespace that may now exist after removing the octets.
 310			$filtered = trim( preg_replace( '` +`', ' ', $filtered ) );
 311		}
 312
 313		/**
 314		 * Filter a sanitized text field string.
 315		 *
 316		 * @since WP 2.9.0
 317		 *
 318		 * @param string $filtered The sanitized string.
 319		 * @param string $str      The string prior to being sanitized.
 320		 */
 321
 322		return apply_filters( 'sanitize_text_field', $filtered, $value );
 323	}
 324
 325	/**
 326	 * Sanitize a url for saving to the database.
 327	 * Not to be confused with the old native WP function.
 328	 *
 329	 * @todo  [JRF => whomever] Check/improve url verification.
 330	 *
 331	 * @since 1.8.0
 332	 *
 333	 * @param string $value             String URL value to sanitize.
 334	 * @param array  $allowed_protocols Optional set of allowed protocols.
 335	 *
 336	 * @return string
 337	 */
 338	public static function sanitize_url( $value, $allowed_protocols = array( 'http', 'https' ) ) {
 339		return esc_url_raw( sanitize_text_field( rawurldecode( $value ) ), $allowed_protocols );
 340	}
 341
 342	/**
 343	 * Validate a value as boolean.
 344	 *
 345	 * @static
 346	 *
 347	 * @since 1.8.0
 348	 *
 349	 * @param mixed $value Value to validate.
 350	 *
 351	 * @return bool
 352	 */
 353	public static function validate_bool( $value ) {
 354		if ( ! isset( self::$has_filters ) ) {
 355			self::$has_filters = extension_loaded( 'filter' );
 356		}
 357
 358		if ( self::$has_filters ) {
 359			return filter_var( $value, FILTER_VALIDATE_BOOLEAN );
 360		}
 361		else {
 362			return self::emulate_filter_bool( $value );
 363		}
 364	}
 365
 366	/**
 367	 * Cast a value to bool.
 368	 *
 369	 * @static
 370	 *
 371	 * @since 1.8.0
 372	 *
 373	 * @param mixed $value Value to cast.
 374	 *
 375	 * @return bool
 376	 */
 377	public static function emulate_filter_bool( $value ) {
 378		$true  = array(
 379			'1',
 380			'true',
 381			'True',
 382			'TRUE',
 383			'y',
 384			'Y',
 385			'yes',
 386			'Yes',
 387			'YES',
 388			'on',
 389			'On',
 390			'ON',
 391		);
 392		$false = array(
 393			'0',
 394			'false',
 395			'False',
 396			'FALSE',
 397			'n',
 398			'N',
 399			'no',
 400			'No',
 401			'NO',
 402			'off',
 403			'Off',
 404			'OFF',
 405		);
 406
 407		if ( is_bool( $value ) ) {
 408			return $value;
 409		}
 410		elseif ( is_int( $value ) && ( $value === 0 || $value === 1 ) ) {
 411			return (bool) $value;
 412		}
 413		elseif ( ( is_float( $value ) && ! is_nan( $value ) ) && ( $value === (float) 0 || $value === (float) 1 ) ) {
 414			return (bool) $value;
 415		}
 416		elseif ( is_string( $value ) ) {
 417			$value = trim( $value );
 418			if ( in_array( $value, $true, true ) ) {
 419				return true;
 420			}
 421			elseif ( in_array( $value, $false, true ) ) {
 422				return false;
 423			}
 424			else {
 425				return false;
 426			}
 427		}
 428
 429		return false;
 430	}
 431
 432	/**
 433	 * Validate a value as integer.
 434	 *
 435	 * @static
 436	 *
 437	 * @since 1.8.0
 438	 *
 439	 * @param mixed $value Value to validate.
 440	 *
 441	 * @return int|bool Int or false in case of failure to convert to int.
 442	 */
 443	public static function validate_int( $value ) {
 444		if ( ! isset( self::$has_filters ) ) {
 445			self::$has_filters = extension_loaded( 'filter' );
 446		}
 447
 448		if ( self::$has_filters ) {
 449			return filter_var( $value, FILTER_VALIDATE_INT );
 450		}
 451		else {
 452			return self::emulate_filter_int( $value );
 453		}
 454	}
 455
 456	/**
 457	 * Cast a value to integer.
 458	 *
 459	 * @static
 460	 *
 461	 * @since 1.8.0
 462	 *
 463	 * @param mixed $value Value to cast.
 464	 *
 465	 * @return int|bool
 466	 */
 467	public static function emulate_filter_int( $value ) {
 468		if ( is_int( $value ) ) {
 469			return $value;
 470		}
 471		elseif ( is_float( $value ) ) {
 472			if ( (int) $value == $value && ! is_nan( $value ) ) {
 473				return (int) $value;
 474			}
 475			else {
 476				return false;
 477			}
 478		}
 479		elseif ( is_string( $value ) ) {
 480			$value = trim( $value );
 481			if ( $value === '' ) {
 482				return false;
 483			}
 484			elseif ( ctype_digit( $value ) ) {
 485				return (int) $value;
 486			}
 487			elseif ( strpos( $value, '-' ) === 0 && ctype_digit( substr( $value, 1 ) ) ) {
 488				return (int) $value;
 489			}
 490			else {
 491				return false;
 492			}
 493		}
 494
 495		return false;
 496	}
 497
 498	/**
 499	 * Clears the WP or W3TC cache depending on which is used.
 500	 *
 501	 * @static
 502	 *
 503	 * @since 1.8.0
 504	 */
 505	public static function clear_cache() {
 506		if ( function_exists( 'w3tc_pgcache_flush' ) ) {
 507			w3tc_pgcache_flush();
 508		}
 509		elseif ( function_exists( 'wp_cache_clear_cache' ) ) {
 510			wp_cache_clear_cache();
 511		}
 512	}
 513
 514	/**
 515	 * Flush W3TC cache after succesfull update/add of taxonomy meta option.
 516	 *
 517	 * @static
 518	 *
 519	 * @since 1.8.0
 520	 */
 521	public static function flush_w3tc_cache() {
 522		if ( defined( 'W3TC_DIR' ) && function_exists( 'w3tc_objectcache_flush' ) ) {
 523			w3tc_objectcache_flush();
 524		}
 525	}
 526
 527	/**
 528	 * Clear rewrite rules.
 529	 *
 530	 * @static
 531	 *
 532	 * @since 1.8.0
 533	 */
 534	public static function clear_rewrites() {
 535		delete_option( 'rewrite_rules' );
 536	}
 537
 538	/**
 539	 * Do simple reliable math calculations without the risk of wrong results.
 540	 *
 541	 * @see   http://floating-point-gui.de/
 542	 * @see   the big red warning on http://php.net/language.types.float.php
 543	 *
 544	 * In the rare case that the bcmath extension would not be loaded, it will return the normal calculation results.
 545	 *
 546	 * @static
 547	 *
 548	 * @since 1.5.0
 549	 * @since 1.8.0 Moved from stand-alone function to this class.
 550	 *
 551	 * @param mixed  $number1     Scalar (string/int/float/bool).
 552	 * @param string $action      Calculation action to execute. Valid input:
 553	 *                            '+' or 'add' or 'addition',
 554	 *                            '-' or 'sub' or 'subtract',
 555	 *                            '*' or 'mul' or 'multiply',
 556	 *                            '/' or 'div' or 'divide',
 557	 *                            '%' or 'mod' or 'modulus'
 558	 *                            '=' or 'comp' or 'compare'.
 559	 * @param mixed  $number2     Scalar (string/int/float/bool).
 560	 * @param bool   $round       Whether or not to round the result. Defaults to false.
 561	 *                            Will be disregarded for a compare operation.
 562	 * @param int    $decimals    Decimals for rounding operation. Defaults to 0.
 563	 * @param int    $precision   Calculation precision. Defaults to 10.
 564	 *
 565	 * @return mixed            Calculation Result or false if either or the numbers isn't scalar or
 566	 *                          an invalid operation was passed.
 567	 *                          - for compare the result will always be an integer.
 568	 *                          - for all other operations, the result will either be an integer (preferred)
 569	 *                            or a float.
 570	 */
 571	public static function calc( $number1, $action, $number2, $round = false, $decimals = 0, $precision = 10 ) {
 572		static $bc;
 573
 574		if ( ! is_scalar( $number1 ) || ! is_scalar( $number2 ) ) {
 575			return false;
 576		}
 577
 578		if ( ! isset( $bc ) ) {
 579			$bc = extension_loaded( 'bcmath' );
 580		}
 581
 582		if ( $bc ) {
 583			$number1 = number_format( $number1, 10, '.', '' );
 584			$number2 = number_format( $number2, 10, '.', '' );
 585		}
 586
 587		$result  = null;
 588		$compare = false;
 589
 590		switch ( $action ) {
 591			case '+':
 592			case 'add':
 593			case 'addition':
 594				$result = ( $bc ) ? bcadd( $number1, $number2, $precision ) /* string */ : ( $number1 + $number2 );
 595				break;
 596
 597			case '-':
 598			case 'sub':
 599			case 'subtract':
 600				$result = ( $bc ) ? bcsub( $number1, $number2, $precision ) /* string */ : ( $number1 - $number2 );
 601				break;
 602
 603			case '*':
 604			case 'mul':
 605			case 'multiply':
 606				$result = ( $bc ) ? bcmul( $number1, $number2, $precision ) /* string */ : ( $number1 * $number2 );
 607				break;
 608
 609			case '/':
 610			case 'div':
 611			case 'divide':
 612				if ( $bc ) {
 613					$result = bcdiv( $number1, $number2, $precision ); // String, or NULL if right_operand is 0.
 614				}
 615				elseif ( $number2 != 0 ) {
 616					$result = ( $number1 / $number2 );
 617				}
 618
 619				if ( ! isset( $result ) ) {
 620					$result = 0;
 621				}
 622				break;
 623
 624			case '%':
 625			case 'mod':
 626			case 'modulus':
 627				if ( $bc ) {
 628					$result = bcmod( $number1, $number2 ); // String, or NULL if modulus is 0.
 629				}
 630				elseif ( $number2 != 0 ) {
 631					$result = ( $number1 % $number2 );
 632				}
 633
 634				if ( ! isset( $result ) ) {
 635					$result = 0;
 636				}
 637				break;
 638
 639			case '=':
 640			case 'comp':
 641			case 'compare':
 642				$compare = true;
 643				if ( $bc ) {
 644					$result = bccomp( $number1, $number2, $precision ); // Returns int 0, 1 or -1.
 645				}
 646				else {
 647					$result = ( $number1 == $number2 ) ? 0 : ( ( $number1 > $number2 ) ? 1 : - 1 );
 648				}
 649				break;
 650		}
 651
 652		if ( isset( $result ) ) {
 653			if ( $compare === false ) {
 654				if ( $round === true ) {
 655					$result = round( floatval( $result ), $decimals );
 656					if ( $decimals === 0 ) {
 657						$result = (int) $result;
 658					}
 659				}
 660				else {
 661					$result = ( intval( $result ) == $result ) ? intval( $result ) : floatval( $result );
 662				}
 663			}
 664
 665			return $result;
 666		}
 667
 668		return false;
 669	}
 670
 671	/**
 672	 * Trim whitespace and NBSP (Non-breaking space) from string.
 673	 *
 674	 * @since 2.0.0
 675	 *
 676	 * @param string $string String input to trim.
 677	 *
 678	 * @return string
 679	 */
 680	public static function trim_nbsp_from_string( $string ) {
 681		$find   = array( '&nbsp;', chr( 0xC2 ) . chr( 0xA0 ) );
 682		$string = str_replace( $find, ' ', $string );
 683		$string = trim( $string );
 684
 685		return $string;
 686	}
 687
 688	/**
 689	 * Check if a string is a valid datetime.
 690	 *
 691	 * @since 2.0.0
 692	 *
 693	 * @param string $datetime String input to check as valid input for DateTime class.
 694	 *
 695	 * @return bool
 696	 */
 697	public static function is_valid_datetime( $datetime ) {
 698
 699		if ( substr( $datetime, 0, 1 ) === '-' ) {
 700			return false;
 701		}
 702
 703		try {
 704			return new DateTime( $datetime ) !== false;
 705		} catch ( Exception $exc ) {
 706			return false;
 707		}
 708	}
 709
 710	/**
 711	 * Format the URL to be sure it is okay for using as a redirect url.
 712	 *
 713	 * This method will parse the URL and combine them in one string.
 714	 *
 715	 * @since 2.3.0
 716	 *
 717	 * @param string $url URL string.
 718	 *
 719	 * @return mixed
 720	 */
 721	public static function format_url( $url ) {
 722		$parsed_url = wp_parse_url( $url );
 723
 724		$formatted_url = '';
 725		if ( ! empty( $parsed_url['path'] ) ) {
 726			$formatted_url = $parsed_url['path'];
 727		}
 728
 729		// Prepend a slash if first char != slash.
 730		if ( stripos( $formatted_url, '/' ) !== 0 ) {
 731			$formatted_url = '/' . $formatted_url;
 732		}
 733
 734		// Append 'query' string if it exists.
 735		if ( ! empty( $parsed_url['query'] ) ) {
 736			$formatted_url .= '?' . $parsed_url['query'];
 737		}
 738
 739		return apply_filters( 'wpseo_format_admin_url', $formatted_url );
 740	}
 741
 742
 743	/**
 744	 * Get plugin name from file.
 745	 *
 746	 * @since 2.3.3
 747	 *
 748	 * @param string $plugin Plugin path relative to plugins directory.
 749	 *
 750	 * @return string|bool
 751	 */
 752	public static function get_plugin_name( $plugin ) {
 753		$plugin_details = get_plugin_data( WP_PLUGIN_DIR . '/' . $plugin );
 754
 755		if ( $plugin_details['Name'] !== '' ) {
 756			return $plugin_details['Name'];
 757		}
 758
 759		return false;
 760	}
 761
 762	/**
 763	 * Retrieves the sitename.
 764	 *
 765	 * @since 3.0.0
 766	 *
 767	 * @return string
 768	 */
 769	public static function get_site_name() {
 770		return wp_strip_all_tags( get_bloginfo( 'name' ), true );
 771	}
 772
 773	/**
 774	 * Retrieves the title separator.
 775	 *
 776	 * @since 3.0.0
 777	 *
 778	 * @return string
 779	 */
 780	public static function get_title_separator() {
 781		$replacement = WPSEO_Options::get_default( 'wpseo_titles', 'separator' );
 782
 783		// Get the titles option and the separator options.
 784		$separator         = WPSEO_Options::get( 'separator' );
 785		$seperator_options = WPSEO_Option_Titles::get_instance()->get_separator_options();
 786
 787		// This should always be set, but just to be sure.
 788		if ( isset( $seperator_options[ $separator ] ) ) {
 789			// Set the new replacement.
 790			$replacement = $seperator_options[ $separator ];
 791		}
 792
 793		/**
 794		 * Filter: 'wpseo_replacements_filter_sep' - Allow customization of the separator character(s).
 795		 *
 796		 * @api string $replacement The current separator.
 797		 */
 798		return apply_filters( 'wpseo_replacements_filter_sep', $replacement );
 799	}
 800
 801	/**
 802	 * Check if the current opened page is a Yoast SEO page.
 803	 *
 804	 * @since 3.0.0
 805	 *
 806	 * @return bool
 807	 */
 808	public static function is_yoast_seo_page() {
 809		static $is_yoast_seo;
 810
 811		if ( $is_yoast_seo === null ) {
 812			$current_page = filter_input( INPUT_GET, 'page' );
 813			$is_yoast_seo = ( substr( $current_page, 0, 6 ) === 'wpseo_' );
 814		}
 815
 816		return $is_yoast_seo;
 817	}
 818
 819	/**
 820	 * Check if the current opened page belongs to Yoast SEO Free.
 821	 *
 822	 * @since 3.3.0
 823	 *
 824	 * @param string $current_page The current page the user is on.
 825	 *
 826	 * @return bool
 827	 */
 828	public static function is_yoast_seo_free_page( $current_page ) {
 829		$yoast_seo_free_pages = array(
 830			'wpseo_dashboard',
 831			'wpseo_titles',
 832			'wpseo_social',
 833			'wpseo_advanced',
 834			'wpseo_tools',
 835			'wpseo_search_console',
 836			'wpseo_licenses',
 837		);
 838
 839		return in_array( $current_page, $yoast_seo_free_pages, true );
 840	}
 841
 842	/**
 843	 * Checks if we are in the premium or free plugin.
 844	 *
 845	 * @return bool True when we are in the premium plugin.
 846	 */
 847	public static function is_yoast_seo_premium() {
 848		return defined( 'WPSEO_PREMIUM_PLUGIN_FILE' );
 849	}
 850
 851	/**
 852	 * Determine if Yoast SEO is in development mode?
 853	 *
 854	 * Inspired by JetPack (https://github.com/Automattic/jetpack/blob/master/class.jetpack.php#L1383-L1406).
 855	 *
 856	 * @since 3.0.0
 857	 *
 858	 * @return bool
 859	 */
 860	public static function is_development_mode() {
 861		$development_mode = false;
 862
 863		if ( defined( 'WPSEO_DEBUG' ) ) {
 864			$development_mode = WPSEO_DEBUG;
 865		}
 866		elseif ( site_url() && false === strpos( site_url(), '.' ) ) {
 867			$development_mode = true;
 868		}
 869
 870		/**
 871		 * Filter the Yoast SEO development mode.
 872		 *
 873		 * @since 3.0
 874		 *
 875		 * @param bool $development_mode Is Yoast SEOs development mode active.
 876		 */
 877
 878		return apply_filters( 'yoast_seo_development_mode', $development_mode );
 879	}
 880
 881	/**
 882	 * Retrieve home URL with proper trailing slash.
 883	 *
 884	 * @since 3.3.0
 885	 *
 886	 * @param string      $path   Path relative to home URL.
 887	 * @param string|null $scheme Scheme to apply.
 888	 *
 889	 * @return string Home URL with optional path, appropriately slashed if not.
 890	 */
 891	public static function home_url( $path = '', $scheme = null ) {
 892
 893		$home_url = home_url( $path, $scheme );
 894
 895		if ( ! empty( $path ) ) {
 896			return $home_url;
 897		}
 898
 899		$home_path = wp_parse_url( $home_url, PHP_URL_PATH );
 900
 901		if ( '/' === $home_path ) { // Home at site root, already slashed.
 902			return $home_url;
 903		}
 904
 905		if ( is_null( $home_path ) ) { // Home at site root, always slash.
 906			return trailingslashit( $home_url );
 907		}
 908
 909		if ( is_string( $home_path ) ) { // Home in subdirectory, slash if permalink structure has slash.
 910			return user_trailingslashit( $home_url );
 911		}
 912
 913		return $home_url;
 914	}
 915
 916	/**
 917	 * Returns a base64 URL for the svg for use in the menu.
 918	 *
 919	 * @since 3.3.0
 920	 *
 921	 * @param bool $base64 Whether or not to return base64'd output.
 922	 *
 923	 * @return string
 924	 */
 925	public static function get_icon_svg( $base64 = true ) {
 926		$svg = '<svg version="1.1" xmlns="http://www.w3.org/2000/svg" xml:space="preserve" width="100%" height="100%" style="fill:#82878c" viewBox="0 0 512 512" role="img" aria-hidden="true" focusable="false"><g><g><g><g><path d="M203.6,395c6.8-17.4,6.8-36.6,0-54l-79.4-204h70.9l47.7,149.4l74.8-207.6H116.4c-41.8,0-76,34.2-76,76V357c0,41.8,34.2,76,76,76H173C189,424.1,197.6,410.3,203.6,395z"/></g><g><path d="M471.6,154.8c0-41.8-34.2-76-76-76h-3L285.7,365c-9.6,26.7-19.4,49.3-30.3,68h216.2V154.8z"/></g></g><path stroke-width="2.974" stroke-miterlimit="10" d="M338,1.3l-93.3,259.1l-42.1-131.9h-89.1l83.8,215.2c6,15.5,6,32.5,0,48c-7.4,19-19,37.3-53,41.9l-7.2,1v76h8.3c81.7,0,118.9-57.2,149.6-142.9L431.6,1.3H338z M279.4,362c-32.9,92-67.6,128.7-125.7,131.8v-45c37.5-7.5,51.3-31,59.1-51.1c7.5-19.3,7.5-40.7,0-60l-75-192.7h52.8l53.3,166.8l105.9-294h58.1L279.4,362z"/></g></g></svg>';
 927
 928		if ( $base64 ) {
 929			return 'data:image/svg+xml;base64,' . base64_encode( $svg );
 930		}
 931
 932		return $svg;
 933	}
 934
 935	/**
 936	 * Returns the SVG for the traffic light in the metabox.
 937	 *
 938	 * @return string
 939	 */
 940	public static function traffic_light_svg() {
 941		return <<<SVG
 942<svg class="yst-traffic-light init" version="1.1" xmlns="http://www.w3.org/2000/svg"
 943	 role="img" aria-hidden="true" focusable="false"
 944	 x="0px" y="0px" viewBox="0 0 30 47" enable-background="new 0 0 30 47" xml:space="preserve">
 945<g id="BG_1_">
 946</g>
 947<g id="traffic_light">
 948	<g>
 949		<g>
 950			<g>
 951				<path fill="#5B2942" d="M22,0H8C3.6,0,0,3.6,0,7.9v31.1C0,43.4,3.6,47,8,47h14c4.4,0,8-3.6,8-7.9V7.9C30,3.6,26.4,0,22,0z
 952					 M27.5,38.8c0,3.1-2.6,5.7-5.8,5.7H8.3c-3.2,0-5.8-2.5-5.8-5.7V8.3c0-1.5,0.6-2.9,1.7-4c1.1-1,2.5-1.6,4.1-1.6h13.4
 953					c1.5,0,3,0.6,4.1,1.6c1.1,1.1,1.7,2.5,1.7,4V38.8z"/>
 954			</g>
 955			<g class="traffic-light-color traffic-light-red">
 956				<ellipse fill="#C8C8C8" cx="15" cy="23.5" rx="5.7" ry="5.6"/>
 957				<ellipse fill="#DC3232" cx="15" cy="10.9" rx="5.7" ry="5.6"/>
 958				<ellipse fill="#C8C8C8" cx="15" cy="36.1" rx="5.7" ry="5.6"/>
 959			</g>
 960			<g class="traffic-light-color traffic-light-orange">
 961				<ellipse fill="#F49A00" cx="15" cy="23.5" rx="5.7" ry="5.6"/>
 962				<ellipse fill="#C8C8C8" cx="15" cy="10.9" rx="5.7" ry="5.6"/>
 963				<ellipse fill="#C8C8C8" cx="15" cy="36.1" rx="5.7" ry="5.6"/>
 964			</g>
 965			<g class="traffic-light-color traffic-light-green">
 966				<ellipse fill="#C8C8C8" cx="15" cy="23.5" rx="5.7" ry="5.6"/>
 967				<ellipse fill="#C8C8C8" cx="15" cy="10.9" rx="5.7" ry="5.6"/>
 968				<ellipse fill="#63B22B" cx="15" cy="36.1" rx="5.7" ry="5.6"/>
 969			</g>
 970			<g class="traffic-light-color traffic-light-empty">
 971				<ellipse fill="#C8C8C8" cx="15" cy="23.5" rx="5.7" ry="5.6"/>
 972				<ellipse fill="#C8C8C8" cx="15" cy="10.9" rx="5.7" ry="5.6"/>
 973				<ellipse fill="#C8C8C8" cx="15" cy="36.1" rx="5.7" ry="5.6"/>
 974			</g>
 975			<g class="traffic-light-color traffic-light-init">
 976				<ellipse fill="#5B2942" cx="15" cy="23.5" rx="5.7" ry="5.6"/>
 977				<ellipse fill="#5B2942" cx="15" cy="10.9" rx="5.7" ry="5.6"/>
 978				<ellipse fill="#5B2942" cx="15" cy="36.1" rx="5.7" ry="5.6"/>
 979			</g>
 980		</g>
 981	</g>
 982</g>
 983</svg>
 984SVG;
 985	}
 986
 987	/**
 988	 * Checks if the WP-REST-API is available.
 989	 *
 990	 * @since 3.6
 991	 * @since 3.7 Introduced the $minimum_version parameter.
 992	 *
 993	 * @param string $minimum_version The minimum version the API should be.
 994	 *
 995	 * @return bool Returns true if the API is available.
 996	 */
 997	public static function is_api_available( $minimum_version = '2.0' ) {
 998		return ( defined( 'REST_API_VERSION' )
 999			&& version_compare( REST_API_VERSION, $minimum_version, '>=' ) );
1000	}
1001
1002	/********************** DEPRECATED METHODS **********************/
1003
1004	/**
1005	 * Returns the language part of a given locale, defaults to english when the $locale is empty.
1006	 *
1007	 * @see        WPSEO_Language_Utils::get_language()
1008	 *
1009	 * @since      3.4
1010	 *
1011	 * @param string $locale The locale to get the language of.
1012	 *
1013	 * @returns string The language part of the locale.
1014	 */
1015	public static function get_language( $locale ) {
1016		return WPSEO_Language_Utils::get_language( $locale );
1017	}
1018
1019	/**
1020	 * Returns the user locale for the language to be used in the admin.
1021	 *
1022	 * WordPress 4.7 introduced the ability for users to specify an Admin language
1023	 * different from the language used on the front end. This checks if the feature
1024	 * is available and returns the user's language, with a fallback to the site's language.
1025	 * Can be removed when support for WordPress 4.6 will be dropped, in favor
1026	 * of WordPress get_user_locale() that already fallbacks to the site's locale.
1027	 *
1028	 * @see        WPSEO_Language_Utils::get_user_locale()
1029	 *
1030	 * @since      4.1
1031	 *
1032	 * @returns string The locale.
1033	 */
1034	public static function get_user_locale() {
1035		return WPSEO_Language_Utils::get_user_locale();
1036	}
1037
1038	/**
1039	 * Determine whether or not the metabox should be displayed for a post type.
1040	 *
1041	 * @param string|null $post_type Optional. The post type to check the visibility of the metabox for.
1042	 *
1043	 * @return bool Whether or not the metabox should be displayed.
1044	 */
1045	protected static function display_post_type_metabox( $post_type = null ) {
1046		if ( ! isset( $post_type ) ) {
1047			$post_type = get_post_type();
1048		}
1049
1050		if ( ! isset( $post_type ) || ! WPSEO_Post_Type::is_post_type_accessible( $post_type ) ) {
1051			return false;
1052		}
1053
1054		return WPSEO_Options::get( 'display-metabox-pt-' . $post_type );
1055	}
1056
1057	/**
1058	 * Determine whether or not the metabox should be displayed for a taxonomy.
1059	 *
1060	 * @param string|null $taxonomy Optional. The post type to check the visibility of the metabox for.
1061	 *
1062	 * @return bool Whether or not the metabox should be displayed.
1063	 */
1064	protected static function display_taxonomy_metabox( $taxonomy = null ) {
1065		if ( ! isset( $taxonomy ) || ! in_array( $taxonomy, get_taxonomies( array( 'public' => true ), 'names' ), true ) ) {
1066			return false;
1067		}
1068
1069		return WPSEO_Options::get( 'display-metabox-tax-' . $taxonomy );
1070	}
1071
1072	/**
1073	 * Determines whether the metabox is active for the given identifier and type.
1074	 *
1075	 * @param string $identifier The identifier to check for.
1076	 * @param string $type       The type to check for.
1077	 *
1078	 * @return bool Whether or not the metabox is active.
1079	 */
1080	public static function is_metabox_active( $identifier, $type ) {
1081		if ( $type === 'post_type' ) {
1082			return self::display_post_type_metabox( $identifier );
1083		}
1084
1085		if ( $type === 'taxonomy' ) {
1086			return self::display_taxonomy_metabox( $identifier );
1087		}
1088
1089		return false;
1090	}
1091}