home *** CD-ROM | disk | FTP | other *** search
/ HTML Examples / WP.iso / wordpress / wp-includes / compat.php < prev    next >
Encoding:
PHP Script  |  2017-07-27  |  14.9 KB  |  500 lines

  1. <?php
  2. /**
  3.  * WordPress implementation for PHP functions either missing from older PHP versions or not included by default.
  4.  *
  5.  * @package PHP
  6.  * @access private
  7.  */
  8.  
  9. // If gettext isn't available
  10. if ( !function_exists('_') ) {
  11.     function _($string) {
  12.         return $string;
  13.     }
  14. }
  15.  
  16. /**
  17.  * Returns whether PCRE/u (PCRE_UTF8 modifier) is available for use.
  18.  *
  19.  * @ignore
  20.  * @since 4.2.2
  21.  * @access private
  22.  *
  23.  * @staticvar string $utf8_pcre
  24.  *
  25.  * @param bool $set - Used for testing only
  26.  *             null   : default - get PCRE/u capability
  27.  *             false  : Used for testing - return false for future calls to this function
  28.  *             'reset': Used for testing - restore default behavior of this function
  29.  */
  30. function _wp_can_use_pcre_u( $set = null ) {
  31.     static $utf8_pcre = 'reset';
  32.  
  33.     if ( null !== $set ) {
  34.         $utf8_pcre = $set;
  35.     }
  36.  
  37.     if ( 'reset' === $utf8_pcre ) {
  38.         $utf8_pcre = @preg_match( '/^./u', 'a' );
  39.     }
  40.  
  41.     return $utf8_pcre;
  42. }
  43.  
  44. if ( ! function_exists( 'mb_substr' ) ) :
  45.     /**
  46.      * Compat function to mimic mb_substr().
  47.      *
  48.      * @ignore
  49.      * @since 3.2.0
  50.      *
  51.      * @see _mb_substr()
  52.      *
  53.      * @param string      $str      The string to extract the substring from.
  54.      * @param int         $start    Position to being extraction from in `$str`.
  55.      * @param int|null    $length   Optional. Maximum number of characters to extract from `$str`.
  56.      *                              Default null.
  57.      * @param string|null $encoding Optional. Character encoding to use. Default null.
  58.      * @return string Extracted substring.
  59.      */
  60.     function mb_substr( $str, $start, $length = null, $encoding = null ) {
  61.         return _mb_substr( $str, $start, $length, $encoding );
  62.     }
  63. endif;
  64.  
  65. /**
  66.  * Internal compat function to mimic mb_substr().
  67.  *
  68.  * Only understands UTF-8 and 8bit.  All other character sets will be treated as 8bit.
  69.  * For $encoding === UTF-8, the $str input is expected to be a valid UTF-8 byte sequence.
  70.  * The behavior of this function for invalid inputs is undefined.
  71.  *
  72.  * @ignore
  73.  * @since 3.2.0
  74.  *
  75.  * @param string      $str      The string to extract the substring from.
  76.  * @param int         $start    Position to being extraction from in `$str`.
  77.  * @param int|null    $length   Optional. Maximum number of characters to extract from `$str`.
  78.  *                              Default null.
  79.  * @param string|null $encoding Optional. Character encoding to use. Default null.
  80.  * @return string Extracted substring.
  81.  */
  82. function _mb_substr( $str, $start, $length = null, $encoding = null ) {
  83.     if ( null === $encoding ) {
  84.         $encoding = get_option( 'blog_charset' );
  85.     }
  86.  
  87.     /*
  88.      * The solution below works only for UTF-8, so in case of a different
  89.      * charset just use built-in substr().
  90.      */
  91.     if ( ! in_array( $encoding, array( 'utf8', 'utf-8', 'UTF8', 'UTF-8' ) ) ) {
  92.         return is_null( $length ) ? substr( $str, $start ) : substr( $str, $start, $length );
  93.     }
  94.  
  95.     if ( _wp_can_use_pcre_u() ) {
  96.         // Use the regex unicode support to separate the UTF-8 characters into an array.
  97.         preg_match_all( '/./us', $str, $match );
  98.         $chars = is_null( $length ) ? array_slice( $match[0], $start ) : array_slice( $match[0], $start, $length );
  99.         return implode( '', $chars );
  100.     }
  101.  
  102.     $regex = '/(
  103.           [\x00-\x7F]                  # single-byte sequences   0xxxxxxx
  104.         | [\xC2-\xDF][\x80-\xBF]       # double-byte sequences   110xxxxx 10xxxxxx
  105.         | \xE0[\xA0-\xBF][\x80-\xBF]   # triple-byte sequences   1110xxxx 10xxxxxx * 2
  106.         | [\xE1-\xEC][\x80-\xBF]{2}
  107.         | \xED[\x80-\x9F][\x80-\xBF]
  108.         | [\xEE-\xEF][\x80-\xBF]{2}
  109.         | \xF0[\x90-\xBF][\x80-\xBF]{2} # four-byte sequences   11110xxx 10xxxxxx * 3
  110.         | [\xF1-\xF3][\x80-\xBF]{3}
  111.         | \xF4[\x80-\x8F][\x80-\xBF]{2}
  112.     )/x';
  113.  
  114.     // Start with 1 element instead of 0 since the first thing we do is pop.
  115.     $chars = array( '' );
  116.     do {
  117.         // We had some string left over from the last round, but we counted it in that last round.
  118.         array_pop( $chars );
  119.  
  120.         /*
  121.          * Split by UTF-8 character, limit to 1000 characters (last array element will contain
  122.          * the rest of the string).
  123.          */
  124.         $pieces = preg_split( $regex, $str, 1000, PREG_SPLIT_DELIM_CAPTURE | PREG_SPLIT_NO_EMPTY );
  125.  
  126.         $chars = array_merge( $chars, $pieces );
  127.  
  128.     // If there's anything left over, repeat the loop.
  129.     } while ( count( $pieces ) > 1 && $str = array_pop( $pieces ) );
  130.  
  131.     return join( '', array_slice( $chars, $start, $length ) );
  132. }
  133.  
  134. if ( ! function_exists( 'mb_strlen' ) ) :
  135.     /**
  136.      * Compat function to mimic mb_strlen().
  137.      *
  138.      * @ignore
  139.      * @since 4.2.0
  140.      *
  141.      * @see _mb_strlen()
  142.      *
  143.      * @param string      $str      The string to retrieve the character length from.
  144.      * @param string|null $encoding Optional. Character encoding to use. Default null.
  145.      * @return int String length of `$str`.
  146.      */
  147.     function mb_strlen( $str, $encoding = null ) {
  148.         return _mb_strlen( $str, $encoding );
  149.     }
  150. endif;
  151.  
  152. /**
  153.  * Internal compat function to mimic mb_strlen().
  154.  *
  155.  * Only understands UTF-8 and 8bit.  All other character sets will be treated as 8bit.
  156.  * For $encoding === UTF-8, the `$str` input is expected to be a valid UTF-8 byte
  157.  * sequence. The behavior of this function for invalid inputs is undefined.
  158.  *
  159.  * @ignore
  160.  * @since 4.2.0
  161.  *
  162.  * @param string      $str      The string to retrieve the character length from.
  163.  * @param string|null $encoding Optional. Character encoding to use. Default null.
  164.  * @return int String length of `$str`.
  165.  */
  166. function _mb_strlen( $str, $encoding = null ) {
  167.     if ( null === $encoding ) {
  168.         $encoding = get_option( 'blog_charset' );
  169.     }
  170.  
  171.     /*
  172.      * The solution below works only for UTF-8, so in case of a different charset
  173.      * just use built-in strlen().
  174.      */
  175.     if ( ! in_array( $encoding, array( 'utf8', 'utf-8', 'UTF8', 'UTF-8' ) ) ) {
  176.         return strlen( $str );
  177.     }
  178.  
  179.     if ( _wp_can_use_pcre_u() ) {
  180.         // Use the regex unicode support to separate the UTF-8 characters into an array.
  181.         preg_match_all( '/./us', $str, $match );
  182.         return count( $match[0] );
  183.     }
  184.  
  185.     $regex = '/(?:
  186.           [\x00-\x7F]                  # single-byte sequences   0xxxxxxx
  187.         | [\xC2-\xDF][\x80-\xBF]       # double-byte sequences   110xxxxx 10xxxxxx
  188.         | \xE0[\xA0-\xBF][\x80-\xBF]   # triple-byte sequences   1110xxxx 10xxxxxx * 2
  189.         | [\xE1-\xEC][\x80-\xBF]{2}
  190.         | \xED[\x80-\x9F][\x80-\xBF]
  191.         | [\xEE-\xEF][\x80-\xBF]{2}
  192.         | \xF0[\x90-\xBF][\x80-\xBF]{2} # four-byte sequences   11110xxx 10xxxxxx * 3
  193.         | [\xF1-\xF3][\x80-\xBF]{3}
  194.         | \xF4[\x80-\x8F][\x80-\xBF]{2}
  195.     )/x';
  196.  
  197.     // Start at 1 instead of 0 since the first thing we do is decrement.
  198.     $count = 1;
  199.     do {
  200.         // We had some string left over from the last round, but we counted it in that last round.
  201.         $count--;
  202.  
  203.         /*
  204.          * Split by UTF-8 character, limit to 1000 characters (last array element will contain
  205.          * the rest of the string).
  206.          */
  207.         $pieces = preg_split( $regex, $str, 1000 );
  208.  
  209.         // Increment.
  210.         $count += count( $pieces );
  211.  
  212.     // If there's anything left over, repeat the loop.
  213.     } while ( $str = array_pop( $pieces ) );
  214.  
  215.     // Fencepost: preg_split() always returns one extra item in the array.
  216.     return --$count;
  217. }
  218.  
  219. if ( !function_exists('hash_hmac') ):
  220. /**
  221.  * Compat function to mimic hash_hmac().
  222.  *
  223.  * @ignore
  224.  * @since 3.2.0
  225.  *
  226.  * @see _hash_hmac()
  227.  *
  228.  * @param string $algo       Hash algorithm. Accepts 'md5' or 'sha1'.
  229.  * @param string $data       Data to be hashed.
  230.  * @param string $key        Secret key to use for generating the hash.
  231.  * @param bool   $raw_output Optional. Whether to output raw binary data (true),
  232.  *                           or lowercase hexits (false). Default false.
  233.  * @return string|false The hash in output determined by `$raw_output`. False if `$algo`
  234.  *                      is unknown or invalid.
  235.  */
  236. function hash_hmac($algo, $data, $key, $raw_output = false) {
  237.     return _hash_hmac($algo, $data, $key, $raw_output);
  238. }
  239. endif;
  240.  
  241. /**
  242.  * Internal compat function to mimic hash_hmac().
  243.  *
  244.  * @ignore
  245.  * @since 3.2.0
  246.  *
  247.  * @param string $algo       Hash algorithm. Accepts 'md5' or 'sha1'.
  248.  * @param string $data       Data to be hashed.
  249.  * @param string $key        Secret key to use for generating the hash.
  250.  * @param bool   $raw_output Optional. Whether to output raw binary data (true),
  251.  *                           or lowercase hexits (false). Default false.
  252.  * @return string|false The hash in output determined by `$raw_output`. False if `$algo`
  253.  *                      is unknown or invalid.
  254.  */
  255. function _hash_hmac($algo, $data, $key, $raw_output = false) {
  256.     $packs = array('md5' => 'H32', 'sha1' => 'H40');
  257.  
  258.     if ( !isset($packs[$algo]) )
  259.         return false;
  260.  
  261.     $pack = $packs[$algo];
  262.  
  263.     if (strlen($key) > 64)
  264.         $key = pack($pack, $algo($key));
  265.  
  266.     $key = str_pad($key, 64, chr(0));
  267.  
  268.     $ipad = (substr($key, 0, 64) ^ str_repeat(chr(0x36), 64));
  269.     $opad = (substr($key, 0, 64) ^ str_repeat(chr(0x5C), 64));
  270.  
  271.     $hmac = $algo($opad . pack($pack, $algo($ipad . $data)));
  272.  
  273.     if ( $raw_output )
  274.         return pack( $pack, $hmac );
  275.     return $hmac;
  276. }
  277.  
  278. if ( !function_exists('json_encode') ) {
  279.     function json_encode( $string ) {
  280.         global $wp_json;
  281.  
  282.         if ( ! ( $wp_json instanceof Services_JSON ) ) {
  283.             require_once( ABSPATH . WPINC . '/class-json.php' );
  284.             $wp_json = new Services_JSON();
  285.         }
  286.  
  287.         return $wp_json->encodeUnsafe( $string );
  288.     }
  289. }
  290.  
  291. if ( !function_exists('json_decode') ) {
  292.     /**
  293.      * @global Services_JSON $wp_json
  294.      * @param string $string
  295.      * @param bool   $assoc_array
  296.      * @return object|array
  297.      */
  298.     function json_decode( $string, $assoc_array = false ) {
  299.         global $wp_json;
  300.  
  301.         if ( ! ($wp_json instanceof Services_JSON ) ) {
  302.             require_once( ABSPATH . WPINC . '/class-json.php' );
  303.             $wp_json = new Services_JSON();
  304.         }
  305.  
  306.         $res = $wp_json->decode( $string );
  307.         if ( $assoc_array )
  308.             $res = _json_decode_object_helper( $res );
  309.         return $res;
  310.     }
  311.  
  312.     /**
  313.      * @param object $data
  314.      * @return array
  315.      */
  316.     function _json_decode_object_helper($data) {
  317.         if ( is_object($data) )
  318.             $data = get_object_vars($data);
  319.         return is_array($data) ? array_map(__FUNCTION__, $data) : $data;
  320.     }
  321. }
  322.  
  323. if ( ! function_exists( 'hash_equals' ) ) :
  324. /**
  325.  * Timing attack safe string comparison
  326.  *
  327.  * Compares two strings using the same time whether they're equal or not.
  328.  *
  329.  * This function was added in PHP 5.6.
  330.  *
  331.  * Note: It can leak the length of a string when arguments of differing length are supplied.
  332.  *
  333.  * @since 3.9.2
  334.  *
  335.  * @param string $a Expected string.
  336.  * @param string $b Actual, user supplied, string.
  337.  * @return bool Whether strings are equal.
  338.  */
  339. function hash_equals( $a, $b ) {
  340.     $a_length = strlen( $a );
  341.     if ( $a_length !== strlen( $b ) ) {
  342.         return false;
  343.     }
  344.     $result = 0;
  345.  
  346.     // Do not attempt to "optimize" this.
  347.     for ( $i = 0; $i < $a_length; $i++ ) {
  348.         $result |= ord( $a[ $i ] ) ^ ord( $b[ $i ] );
  349.     }
  350.  
  351.     return $result === 0;
  352. }
  353. endif;
  354.  
  355. // JSON_PRETTY_PRINT was introduced in PHP 5.4
  356. // Defined here to prevent a notice when using it with wp_json_encode()
  357. if ( ! defined( 'JSON_PRETTY_PRINT' ) ) {
  358.     define( 'JSON_PRETTY_PRINT', 128 );
  359. }
  360.  
  361. if ( ! function_exists( 'json_last_error_msg' ) ) :
  362.     /**
  363.      * Retrieves the error string of the last json_encode() or json_decode() call.
  364.      *
  365.      * @since 4.4.0
  366.      *
  367.      * @internal This is a compatibility function for PHP <5.5
  368.      *
  369.      * @return bool|string Returns the error message on success, "No Error" if no error has occurred,
  370.      *                     or false on failure.
  371.      */
  372.     function json_last_error_msg() {
  373.         // See https://core.trac.wordpress.org/ticket/27799.
  374.         if ( ! function_exists( 'json_last_error' ) ) {
  375.             return false;
  376.         }
  377.  
  378.         $last_error_code = json_last_error();
  379.  
  380.         // Just in case JSON_ERROR_NONE is not defined.
  381.         $error_code_none = defined( 'JSON_ERROR_NONE' ) ? JSON_ERROR_NONE : 0;
  382.  
  383.         switch ( true ) {
  384.             case $last_error_code === $error_code_none:
  385.                 return 'No error';
  386.  
  387.             case defined( 'JSON_ERROR_DEPTH' ) && JSON_ERROR_DEPTH === $last_error_code:
  388.                 return 'Maximum stack depth exceeded';
  389.  
  390.             case defined( 'JSON_ERROR_STATE_MISMATCH' ) && JSON_ERROR_STATE_MISMATCH === $last_error_code:
  391.                 return 'State mismatch (invalid or malformed JSON)';
  392.  
  393.             case defined( 'JSON_ERROR_CTRL_CHAR' ) && JSON_ERROR_CTRL_CHAR === $last_error_code:
  394.                 return 'Control character error, possibly incorrectly encoded';
  395.  
  396.             case defined( 'JSON_ERROR_SYNTAX' ) && JSON_ERROR_SYNTAX === $last_error_code:
  397.                 return 'Syntax error';
  398.  
  399.             case defined( 'JSON_ERROR_UTF8' ) && JSON_ERROR_UTF8 === $last_error_code:
  400.                 return 'Malformed UTF-8 characters, possibly incorrectly encoded';
  401.  
  402.             case defined( 'JSON_ERROR_RECURSION' ) && JSON_ERROR_RECURSION === $last_error_code:
  403.                 return 'Recursion detected';
  404.  
  405.             case defined( 'JSON_ERROR_INF_OR_NAN' ) && JSON_ERROR_INF_OR_NAN === $last_error_code:
  406.                 return 'Inf and NaN cannot be JSON encoded';
  407.  
  408.             case defined( 'JSON_ERROR_UNSUPPORTED_TYPE' ) && JSON_ERROR_UNSUPPORTED_TYPE === $last_error_code:
  409.                 return 'Type is not supported';
  410.  
  411.             default:
  412.                 return 'An unknown error occurred';
  413.         }
  414.     }
  415. endif;
  416.  
  417. if ( ! interface_exists( 'JsonSerializable' ) ) {
  418.     define( 'WP_JSON_SERIALIZE_COMPATIBLE', true );
  419.     /**
  420.      * JsonSerializable interface.
  421.      *
  422.      * Compatibility shim for PHP <5.4
  423.      *
  424.      * @link https://secure.php.net/jsonserializable
  425.      *
  426.      * @since 4.4.0
  427.      */
  428.     interface JsonSerializable {
  429.         public function jsonSerialize();
  430.     }
  431. }
  432.  
  433. // random_int was introduced in PHP 7.0
  434. if ( ! function_exists( 'random_int' ) ) {
  435.     require ABSPATH . WPINC . '/random_compat/random.php';
  436. }
  437.  
  438. if ( ! function_exists( 'array_replace_recursive' ) ) :
  439.     /**
  440.      * PHP-agnostic version of {@link array_replace_recursive()}.
  441.      *
  442.      * The array_replace_recursive() function is a PHP 5.3 function. WordPress
  443.      * currently supports down to PHP 5.2, so this method is a workaround
  444.      * for PHP 5.2.
  445.      *
  446.      * Note: array_replace_recursive() supports infinite arguments, but for our use-
  447.      * case, we only need to support two arguments.
  448.      *
  449.      * Subject to removal once WordPress makes PHP 5.3.0 the minimum requirement.
  450.      *
  451.      * @since 4.5.3
  452.      *
  453.      * @see https://secure.php.net/manual/en/function.array-replace-recursive.php#109390
  454.      *
  455.      * @param  array $base         Array with keys needing to be replaced.
  456.      * @param  array $replacements Array with the replaced keys.
  457.      *
  458.      * @return array
  459.      */
  460.     function array_replace_recursive( $base = array(), $replacements = array() ) {
  461.         foreach ( array_slice( func_get_args(), 1 ) as $replacements ) {
  462.             $bref_stack = array( &$base );
  463.             $head_stack = array( $replacements );
  464.  
  465.             do {
  466.                 end( $bref_stack );
  467.  
  468.                 $bref = &$bref_stack[ key( $bref_stack ) ];
  469.                 $head = array_pop( $head_stack );
  470.  
  471.                 unset( $bref_stack[ key( $bref_stack ) ] );
  472.  
  473.                 foreach ( array_keys( $head ) as $key ) {
  474.                     if ( isset( $key, $bref ) &&
  475.                          isset( $bref[ $key ] ) && is_array( $bref[ $key ] ) &&
  476.                          isset( $head[ $key ] ) && is_array( $head[ $key ] )
  477.                     ) {
  478.                         $bref_stack[] = &$bref[ $key ];
  479.                         $head_stack[] = $head[ $key ];
  480.                     } else {
  481.                         $bref[ $key ] = $head[ $key ];
  482.                     }
  483.                 }
  484.             } while ( count( $head_stack ) );
  485.         }
  486.  
  487.         return $base;
  488.     }
  489. endif;
  490.  
  491. /**
  492.  * Polyfill for the SPL autoloader. In PHP 5.2 (but not 5.3 and later), SPL can
  493.  * be disabled, and PHP 7.2 raises notices if the compiler finds an __autoload()
  494.  * function declaration. Function availability is checked here, and the
  495.  * autoloader is included only if necessary.
  496.  */
  497. if ( ! function_exists( 'spl_autoload_register' ) ) {
  498.     require_once ABSPATH . WPINC . '/spl-autoload-compat.php';
  499. }
  500.