[ Index ]

PHP Cross Reference of WordPress Trunk (Updated Daily)

Search

title

Body

[close]

/wp-includes/ -> compat.php (source)

   1  <?php
   2  /**
   3   * WordPress implementation for PHP functions either missing from older PHP versions or not included by default.
   4   *
   5   * This file is loaded extremely early and the functions can be relied upon by drop-ins.
   6   * Ergo, please ensure you do not rely on external functions when writing code for this file.
   7   * Only use functions built into PHP or are defined in this file and have adequate testing
   8   * and error suppression to ensure the file will run correctly and not break websites.
   9   *
  10   * @package PHP
  11   * @access private
  12   */
  13  
  14  // If gettext isn't available.
  15  if ( ! function_exists( '_' ) ) {
  16      /**
  17       * Compat function to mimic _(), an alias of gettext().
  18       *
  19       * @since 0.71
  20       *
  21       * @see https://php.net/manual/en/function.gettext.php
  22       *
  23       * @param string $message The message being translated.
  24       * @return string
  25       */
  26      function _( $message ) {
  27          return $message;
  28      }
  29  }
  30  
  31  /**
  32   * Returns whether PCRE/u (PCRE_UTF8 modifier) is available for use.
  33   *
  34   * @ignore
  35   * @since 4.2.2
  36   * @access private
  37   *
  38   * @param bool $set - Used for testing only
  39   *             null   : default - get PCRE/u capability
  40   *             false  : Used for testing - return false for future calls to this function
  41   *             'reset': Used for testing - restore default behavior of this function
  42   */
  43  function _wp_can_use_pcre_u( $set = null ) {
  44      static $utf8_pcre = 'reset';
  45  
  46      if ( null !== $set ) {
  47          $utf8_pcre = $set;
  48      }
  49  
  50      if ( 'reset' === $utf8_pcre ) {
  51          // phpcs:ignore WordPress.PHP.NoSilencedErrors.Discouraged -- intentional error generated to detect PCRE/u support.
  52          $utf8_pcre = @preg_match( '/^./u', 'a' );
  53      }
  54  
  55      return $utf8_pcre;
  56  }
  57  
  58  /**
  59   * Indicates if a given slug for a character set represents the UTF-8 text encoding.
  60   *
  61   * A charset is considered to represent UTF-8 if it is a case-insensitive match
  62   * of "UTF-8" with or without the hyphen.
  63   *
  64   * Example:
  65   *
  66   *     true  === _is_utf8_charset( 'UTF-8' );
  67   *     true  === _is_utf8_charset( 'utf8' );
  68   *     false === _is_utf8_charset( 'latin1' );
  69   *     false === _is_utf8_charset( 'UTF 8' );
  70   *
  71   *     // Only strings match.
  72   *     false === _is_utf8_charset( [ 'charset' => 'utf-8' ] );
  73   *
  74   * `is_utf8_charset` should be used outside of this file.
  75   *
  76   * @ignore
  77   * @since 6.6.1
  78   *
  79   * @param string $charset_slug Slug representing a text character encoding, or "charset".
  80   *                             E.g. "UTF-8", "Windows-1252", "ISO-8859-1", "SJIS".
  81   *
  82   * @return bool Whether the slug represents the UTF-8 encoding.
  83   */
  84  function _is_utf8_charset( $charset_slug ) {
  85      if ( ! is_string( $charset_slug ) ) {
  86          return false;
  87      }
  88  
  89      return (
  90          0 === strcasecmp( 'UTF-8', $charset_slug ) ||
  91          0 === strcasecmp( 'UTF8', $charset_slug )
  92      );
  93  }
  94  
  95  if ( ! function_exists( 'mb_substr' ) ) :
  96      /**
  97       * Compat function to mimic mb_substr().
  98       *
  99       * @ignore
 100       * @since 3.2.0
 101       *
 102       * @see _mb_substr()
 103       *
 104       * @param string      $string   The string to extract the substring from.
 105       * @param int         $start    Position to being extraction from in `$string`.
 106       * @param int|null    $length   Optional. Maximum number of characters to extract from `$string`.
 107       *                              Default null.
 108       * @param string|null $encoding Optional. Character encoding to use. Default null.
 109       * @return string Extracted substring.
 110       */
 111  	function mb_substr( $string, $start, $length = null, $encoding = null ) { // phpcs:ignore Universal.NamingConventions.NoReservedKeywordParameterNames.stringFound
 112          return _mb_substr( $string, $start, $length, $encoding );
 113      }
 114  endif;
 115  
 116  /**
 117   * Internal compat function to mimic mb_substr().
 118   *
 119   * Only understands UTF-8 and 8bit. All other character sets will be treated as 8bit.
 120   * For `$encoding === UTF-8`, the `$str` input is expected to be a valid UTF-8 byte
 121   * sequence. The behavior of this function for invalid inputs is undefined.
 122   *
 123   * @ignore
 124   * @since 3.2.0
 125   *
 126   * @param string      $str      The string to extract the substring from.
 127   * @param int         $start    Position to being extraction from in `$str`.
 128   * @param int|null    $length   Optional. Maximum number of characters to extract from `$str`.
 129   *                              Default null.
 130   * @param string|null $encoding Optional. Character encoding to use. Default null.
 131   * @return string Extracted substring.
 132   */
 133  function _mb_substr( $str, $start, $length = null, $encoding = null ) {
 134      if ( null === $str ) {
 135          return '';
 136      }
 137  
 138      if ( null === $encoding ) {
 139          $encoding = get_option( 'blog_charset' );
 140      }
 141  
 142      /*
 143       * The solution below works only for UTF-8, so in case of a different
 144       * charset just use built-in substr().
 145       */
 146      if ( ! _is_utf8_charset( $encoding ) ) {
 147          return is_null( $length ) ? substr( $str, $start ) : substr( $str, $start, $length );
 148      }
 149  
 150      if ( _wp_can_use_pcre_u() ) {
 151          // Use the regex unicode support to separate the UTF-8 characters into an array.
 152          preg_match_all( '/./us', $str, $match );
 153          $chars = is_null( $length ) ? array_slice( $match[0], $start ) : array_slice( $match[0], $start, $length );
 154          return implode( '', $chars );
 155      }
 156  
 157      $regex = '/(
 158          [\x00-\x7F]                  # single-byte sequences   0xxxxxxx
 159          | [\xC2-\xDF][\x80-\xBF]       # double-byte sequences   110xxxxx 10xxxxxx
 160          | \xE0[\xA0-\xBF][\x80-\xBF]   # triple-byte sequences   1110xxxx 10xxxxxx * 2
 161          | [\xE1-\xEC][\x80-\xBF]{2}
 162          | \xED[\x80-\x9F][\x80-\xBF]
 163          | [\xEE-\xEF][\x80-\xBF]{2}
 164          | \xF0[\x90-\xBF][\x80-\xBF]{2} # four-byte sequences   11110xxx 10xxxxxx * 3
 165          | [\xF1-\xF3][\x80-\xBF]{3}
 166          | \xF4[\x80-\x8F][\x80-\xBF]{2}
 167      )/x';
 168  
 169      // Start with 1 element instead of 0 since the first thing we do is pop.
 170      $chars = array( '' );
 171  
 172      do {
 173          // We had some string left over from the last round, but we counted it in that last round.
 174          array_pop( $chars );
 175  
 176          /*
 177           * Split by UTF-8 character, limit to 1000 characters (last array element will contain
 178           * the rest of the string).
 179           */
 180          $pieces = preg_split( $regex, $str, 1000, PREG_SPLIT_DELIM_CAPTURE | PREG_SPLIT_NO_EMPTY );
 181  
 182          $chars = array_merge( $chars, $pieces );
 183  
 184          // If there's anything left over, repeat the loop.
 185      } while ( count( $pieces ) > 1 && $str = array_pop( $pieces ) );
 186  
 187      return implode( '', array_slice( $chars, $start, $length ) );
 188  }
 189  
 190  if ( ! function_exists( 'mb_strlen' ) ) :
 191      /**
 192       * Compat function to mimic mb_strlen().
 193       *
 194       * @ignore
 195       * @since 4.2.0
 196       *
 197       * @see _mb_strlen()
 198       *
 199       * @param string      $string   The string to retrieve the character length from.
 200       * @param string|null $encoding Optional. Character encoding to use. Default null.
 201       * @return int String length of `$string`.
 202       */
 203  	function mb_strlen( $string, $encoding = null ) { // phpcs:ignore Universal.NamingConventions.NoReservedKeywordParameterNames.stringFound
 204          return _mb_strlen( $string, $encoding );
 205      }
 206  endif;
 207  
 208  /**
 209   * Internal compat function to mimic mb_strlen().
 210   *
 211   * Only understands UTF-8 and 8bit. All other character sets will be treated as 8bit.
 212   * For `$encoding === UTF-8`, the `$str` input is expected to be a valid UTF-8 byte
 213   * sequence. The behavior of this function for invalid inputs is undefined.
 214   *
 215   * @ignore
 216   * @since 4.2.0
 217   *
 218   * @param string      $str      The string to retrieve the character length from.
 219   * @param string|null $encoding Optional. Character encoding to use. Default null.
 220   * @return int String length of `$str`.
 221   */
 222  function _mb_strlen( $str, $encoding = null ) {
 223      if ( null === $encoding ) {
 224          $encoding = get_option( 'blog_charset' );
 225      }
 226  
 227      /*
 228       * The solution below works only for UTF-8, so in case of a different charset
 229       * just use built-in strlen().
 230       */
 231      if ( ! _is_utf8_charset( $encoding ) ) {
 232          return strlen( $str );
 233      }
 234  
 235      if ( _wp_can_use_pcre_u() ) {
 236          // Use the regex unicode support to separate the UTF-8 characters into an array.
 237          preg_match_all( '/./us', $str, $match );
 238          return count( $match[0] );
 239      }
 240  
 241      $regex = '/(?:
 242          [\x00-\x7F]                  # single-byte sequences   0xxxxxxx
 243          | [\xC2-\xDF][\x80-\xBF]       # double-byte sequences   110xxxxx 10xxxxxx
 244          | \xE0[\xA0-\xBF][\x80-\xBF]   # triple-byte sequences   1110xxxx 10xxxxxx * 2
 245          | [\xE1-\xEC][\x80-\xBF]{2}
 246          | \xED[\x80-\x9F][\x80-\xBF]
 247          | [\xEE-\xEF][\x80-\xBF]{2}
 248          | \xF0[\x90-\xBF][\x80-\xBF]{2} # four-byte sequences   11110xxx 10xxxxxx * 3
 249          | [\xF1-\xF3][\x80-\xBF]{3}
 250          | \xF4[\x80-\x8F][\x80-\xBF]{2}
 251      )/x';
 252  
 253      // Start at 1 instead of 0 since the first thing we do is decrement.
 254      $count = 1;
 255  
 256      do {
 257          // We had some string left over from the last round, but we counted it in that last round.
 258          --$count;
 259  
 260          /*
 261           * Split by UTF-8 character, limit to 1000 characters (last array element will contain
 262           * the rest of the string).
 263           */
 264          $pieces = preg_split( $regex, $str, 1000 );
 265  
 266          // Increment.
 267          $count += count( $pieces );
 268  
 269          // If there's anything left over, repeat the loop.
 270      } while ( $str = array_pop( $pieces ) );
 271  
 272      // Fencepost: preg_split() always returns one extra item in the array.
 273      return --$count;
 274  }
 275  
 276  // sodium_crypto_box() was introduced in PHP 7.2.
 277  if ( ! function_exists( 'sodium_crypto_box' ) ) {
 278      require  ABSPATH . WPINC . '/sodium_compat/autoload.php';
 279  }
 280  
 281  if ( ! function_exists( 'is_countable' ) ) {
 282      /**
 283       * Polyfill for is_countable() function added in PHP 7.3.
 284       *
 285       * Verify that the content of a variable is an array or an object
 286       * implementing the Countable interface.
 287       *
 288       * @since 4.9.6
 289       *
 290       * @param mixed $value The value to check.
 291       * @return bool True if `$value` is countable, false otherwise.
 292       */
 293  	function is_countable( $value ) {
 294          return ( is_array( $value )
 295              || $value instanceof Countable
 296              || $value instanceof SimpleXMLElement
 297              || $value instanceof ResourceBundle
 298          );
 299      }
 300  }
 301  
 302  if ( ! function_exists( 'array_key_first' ) ) {
 303      /**
 304       * Polyfill for array_key_first() function added in PHP 7.3.
 305       *
 306       * Get the first key of the given array without affecting
 307       * the internal array pointer.
 308       *
 309       * @since 5.9.0
 310       *
 311       * @param array $array An array.
 312       * @return string|int|null The first key of array if the array
 313       *                         is not empty; `null` otherwise.
 314       */
 315  	function array_key_first( array $array ) { // phpcs:ignore Universal.NamingConventions.NoReservedKeywordParameterNames.arrayFound
 316          if ( empty( $array ) ) {
 317              return null;
 318          }
 319  
 320          foreach ( $array as $key => $value ) {
 321              return $key;
 322          }
 323      }
 324  }
 325  
 326  if ( ! function_exists( 'array_key_last' ) ) {
 327      /**
 328       * Polyfill for `array_key_last()` function added in PHP 7.3.
 329       *
 330       * Get the last key of the given array without affecting the
 331       * internal array pointer.
 332       *
 333       * @since 5.9.0
 334       *
 335       * @param array $array An array.
 336       * @return string|int|null The last key of array if the array
 337       *.                        is not empty; `null` otherwise.
 338       */
 339  	function array_key_last( array $array ) { // phpcs:ignore Universal.NamingConventions.NoReservedKeywordParameterNames.arrayFound
 340          if ( empty( $array ) ) {
 341              return null;
 342          }
 343  
 344          end( $array );
 345  
 346          return key( $array );
 347      }
 348  }
 349  
 350  if ( ! function_exists( 'array_is_list' ) ) {
 351      /**
 352       * Polyfill for `array_is_list()` function added in PHP 8.1.
 353       *
 354       * Determines if the given array is a list.
 355       *
 356       * An array is considered a list if its keys consist of consecutive numbers from 0 to count($array)-1.
 357       *
 358       * @see https://github.com/symfony/polyfill-php81/tree/main
 359       *
 360       * @since 6.5.0
 361       *
 362       * @param array<mixed> $arr The array being evaluated.
 363       * @return bool True if array is a list, false otherwise.
 364       */
 365  	function array_is_list( $arr ) {
 366          if ( ( array() === $arr ) || ( array_values( $arr ) === $arr ) ) {
 367              return true;
 368          }
 369  
 370          $next_key = -1;
 371  
 372          foreach ( $arr as $k => $v ) {
 373              if ( ++$next_key !== $k ) {
 374                  return false;
 375              }
 376          }
 377  
 378          return true;
 379      }
 380  }
 381  
 382  if ( ! function_exists( 'str_contains' ) ) {
 383      /**
 384       * Polyfill for `str_contains()` function added in PHP 8.0.
 385       *
 386       * Performs a case-sensitive check indicating if needle is
 387       * contained in haystack.
 388       *
 389       * @since 5.9.0
 390       *
 391       * @param string $haystack The string to search in.
 392       * @param string $needle   The substring to search for in the `$haystack`.
 393       * @return bool True if `$needle` is in `$haystack`, otherwise false.
 394       */
 395  	function str_contains( $haystack, $needle ) {
 396          if ( '' === $needle ) {
 397              return true;
 398          }
 399  
 400          return false !== strpos( $haystack, $needle );
 401      }
 402  }
 403  
 404  if ( ! function_exists( 'str_starts_with' ) ) {
 405      /**
 406       * Polyfill for `str_starts_with()` function added in PHP 8.0.
 407       *
 408       * Performs a case-sensitive check indicating if
 409       * the haystack begins with needle.
 410       *
 411       * @since 5.9.0
 412       *
 413       * @param string $haystack The string to search in.
 414       * @param string $needle   The substring to search for in the `$haystack`.
 415       * @return bool True if `$haystack` starts with `$needle`, otherwise false.
 416       */
 417  	function str_starts_with( $haystack, $needle ) {
 418          if ( '' === $needle ) {
 419              return true;
 420          }
 421  
 422          return 0 === strpos( $haystack, $needle );
 423      }
 424  }
 425  
 426  if ( ! function_exists( 'str_ends_with' ) ) {
 427      /**
 428       * Polyfill for `str_ends_with()` function added in PHP 8.0.
 429       *
 430       * Performs a case-sensitive check indicating if
 431       * the haystack ends with needle.
 432       *
 433       * @since 5.9.0
 434       *
 435       * @param string $haystack The string to search in.
 436       * @param string $needle   The substring to search for in the `$haystack`.
 437       * @return bool True if `$haystack` ends with `$needle`, otherwise false.
 438       */
 439  	function str_ends_with( $haystack, $needle ) {
 440          if ( '' === $haystack ) {
 441              return '' === $needle;
 442          }
 443  
 444          $len = strlen( $needle );
 445  
 446          return substr( $haystack, -$len, $len ) === $needle;
 447      }
 448  }
 449  
 450  if ( ! function_exists( 'array_find' ) ) {
 451      /**
 452       * Polyfill for `array_find()` function added in PHP 8.4.
 453       *
 454       * Searches an array for the first element that passes a given callback.
 455       *
 456       * @since 6.8.0
 457       *
 458       * @param array    $array    The array to search.
 459       * @param callable $callback The callback to run for each element.
 460       * @return mixed|null The first element in the array that passes the `$callback`, otherwise null.
 461       */
 462  	function array_find( array $array, callable $callback ) { // phpcs:ignore Universal.NamingConventions.NoReservedKeywordParameterNames.arrayFound
 463          foreach ( $array as $key => $value ) {
 464              if ( $callback( $value, $key ) ) {
 465                  return $value;
 466              }
 467          }
 468  
 469          return null;
 470      }
 471  }
 472  
 473  if ( ! function_exists( 'array_find_key' ) ) {
 474      /**
 475       * Polyfill for `array_find_key()` function added in PHP 8.4.
 476       *
 477       * Searches an array for the first key that passes a given callback.
 478       *
 479       * @since 6.8.0
 480       *
 481       * @param array    $array    The array to search.
 482       * @param callable $callback The callback to run for each element.
 483       * @return int|string|null The first key in the array that passes the `$callback`, otherwise null.
 484       */
 485  	function array_find_key( array $array, callable $callback ) { // phpcs:ignore Universal.NamingConventions.NoReservedKeywordParameterNames.arrayFound
 486          foreach ( $array as $key => $value ) {
 487              if ( $callback( $value, $key ) ) {
 488                  return $key;
 489              }
 490          }
 491  
 492          return null;
 493      }
 494  }
 495  
 496  if ( ! function_exists( 'array_any' ) ) {
 497      /**
 498       * Polyfill for `array_any()` function added in PHP 8.4.
 499       *
 500       * Checks if any element of an array passes a given callback.
 501       *
 502       * @since 6.8.0
 503       *
 504       * @param array    $array    The array to check.
 505       * @param callable $callback The callback to run for each element.
 506       * @return bool True if any element in the array passes the `$callback`, otherwise false.
 507       */
 508  	function array_any( array $array, callable $callback ): bool { // phpcs:ignore Universal.NamingConventions.NoReservedKeywordParameterNames.arrayFound
 509          foreach ( $array as $key => $value ) {
 510              if ( $callback( $value, $key ) ) {
 511                  return true;
 512              }
 513          }
 514  
 515          return false;
 516      }
 517  }
 518  
 519  if ( ! function_exists( 'array_all' ) ) {
 520      /**
 521       * Polyfill for `array_all()` function added in PHP 8.4.
 522       *
 523       * Checks if all elements of an array pass a given callback.
 524       *
 525       * @since 6.8.0
 526       *
 527       * @param array    $array    The array to check.
 528       * @param callable $callback The callback to run for each element.
 529       * @return bool True if all elements in the array pass the `$callback`, otherwise false.
 530       */
 531  	function array_all( array $array, callable $callback ): bool { // phpcs:ignore Universal.NamingConventions.NoReservedKeywordParameterNames.arrayFound
 532          foreach ( $array as $key => $value ) {
 533              if ( ! $callback( $value, $key ) ) {
 534                  return false;
 535              }
 536          }
 537  
 538          return true;
 539      }
 540  }
 541  
 542  // IMAGETYPE_AVIF constant is only defined in PHP 8.x or later.
 543  if ( ! defined( 'IMAGETYPE_AVIF' ) ) {
 544      define( 'IMAGETYPE_AVIF', 19 );
 545  }
 546  
 547  // IMG_AVIF constant is only defined in PHP 8.x or later.
 548  if ( ! defined( 'IMG_AVIF' ) ) {
 549      define( 'IMG_AVIF', IMAGETYPE_AVIF );
 550  }
 551  
 552  // IMAGETYPE_HEIC constant is not yet defined in PHP as of PHP 8.3.
 553  if ( ! defined( 'IMAGETYPE_HEIC' ) ) {
 554      define( 'IMAGETYPE_HEIC', 99 );
 555  }


Generated : Fri Jun 27 08:20:01 2025 Cross-referenced by PHPXref