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


Generated : Thu Sep 4 08:20:02 2025 Cross-referenced by PHPXref