[ Index ]

PHP Cross Reference of WordPress Trunk (Updated Daily)

Search

title

Body

[close]

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

   1  <?php
   2  /**
   3   * Main WordPress API
   4   *
   5   * @package WordPress
   6   */
   7  
   8  require  ABSPATH . WPINC . '/option.php';
   9  
  10  /**
  11   * Convert given MySQL date string into a different format.
  12   *
  13   * `$format` should be a PHP date format string.
  14   * 'U' and 'G' formats will return a sum of timestamp with timezone offset.
  15   * `$date` is expected to be local time in MySQL format (`Y-m-d H:i:s`).
  16   *
  17   * Historically UTC time could be passed to the function to produce Unix timestamp.
  18   *
  19   * If `$translate` is true then the given date and format string will
  20   * be passed to `wp_date()` for translation.
  21   *
  22   * @since 0.71
  23   *
  24   * @param string $format    Format of the date to return.
  25   * @param string $date      Date string to convert.
  26   * @param bool   $translate Whether the return date should be translated. Default true.
  27   * @return string|int|false Formatted date string or sum of Unix timestamp and timezone offset.
  28   *                          False on failure.
  29   */
  30  function mysql2date( $format, $date, $translate = true ) {
  31      if ( empty( $date ) ) {
  32          return false;
  33      }
  34  
  35      $datetime = date_create( $date, wp_timezone() );
  36  
  37      if ( false === $datetime ) {
  38          return false;
  39      }
  40  
  41      // Returns a sum of timestamp with timezone offset. Ideally should never be used.
  42      if ( 'G' === $format || 'U' === $format ) {
  43          return $datetime->getTimestamp() + $datetime->getOffset();
  44      }
  45  
  46      if ( $translate ) {
  47          return wp_date( $format, $datetime->getTimestamp() );
  48      }
  49  
  50      return $datetime->format( $format );
  51  }
  52  
  53  /**
  54   * Retrieves the current time based on specified type.
  55   *
  56   * The 'mysql' type will return the time in the format for MySQL DATETIME field.
  57   * The 'timestamp' type will return the current timestamp or a sum of timestamp
  58   * and timezone offset, depending on `$gmt`.
  59   * Other strings will be interpreted as PHP date formats (e.g. 'Y-m-d').
  60   *
  61   * If $gmt is set to either '1' or 'true', then both types will use GMT time.
  62   * if $gmt is false, the output is adjusted with the GMT offset in the WordPress option.
  63   *
  64   * @since 1.0.0
  65   *
  66   * @param string   $type Type of time to retrieve. Accepts 'mysql', 'timestamp',
  67   *                       or PHP date format string (e.g. 'Y-m-d').
  68   * @param int|bool $gmt  Optional. Whether to use GMT timezone. Default false.
  69   * @return int|string Integer if $type is 'timestamp', string otherwise.
  70   */
  71  function current_time( $type, $gmt = 0 ) {
  72      // Don't use non-GMT timestamp, unless you know the difference and really need to.
  73      if ( 'timestamp' === $type || 'U' === $type ) {
  74          return $gmt ? time() : time() + (int) ( get_option( 'gmt_offset' ) * HOUR_IN_SECONDS );
  75      }
  76  
  77      if ( 'mysql' === $type ) {
  78          $type = 'Y-m-d H:i:s';
  79      }
  80  
  81      $timezone = $gmt ? new DateTimeZone( 'UTC' ) : wp_timezone();
  82      $datetime = new DateTime( 'now', $timezone );
  83  
  84      return $datetime->format( $type );
  85  }
  86  
  87  /**
  88   * Retrieves the current time as an object with the timezone from settings.
  89   *
  90   * @since 5.3.0
  91   *
  92   * @return DateTimeImmutable Date and time object.
  93   */
  94  function current_datetime() {
  95      return new DateTimeImmutable( 'now', wp_timezone() );
  96  }
  97  
  98  /**
  99   * Retrieves the timezone from site settings as a string.
 100   *
 101   * Uses the `timezone_string` option to get a proper timezone if available,
 102   * otherwise falls back to an offset.
 103   *
 104   * @since 5.3.0
 105   *
 106   * @return string PHP timezone string or a ±HH:MM offset.
 107   */
 108  function wp_timezone_string() {
 109      $timezone_string = get_option( 'timezone_string' );
 110  
 111      if ( $timezone_string ) {
 112          return $timezone_string;
 113      }
 114  
 115      $offset  = (float) get_option( 'gmt_offset' );
 116      $hours   = (int) $offset;
 117      $minutes = ( $offset - $hours );
 118  
 119      $sign      = ( $offset < 0 ) ? '-' : '+';
 120      $abs_hour  = abs( $hours );
 121      $abs_mins  = abs( $minutes * 60 );
 122      $tz_offset = sprintf( '%s%02d:%02d', $sign, $abs_hour, $abs_mins );
 123  
 124      return $tz_offset;
 125  }
 126  
 127  /**
 128   * Retrieves the timezone from site settings as a `DateTimeZone` object.
 129   *
 130   * Timezone can be based on a PHP timezone string or a ±HH:MM offset.
 131   *
 132   * @since 5.3.0
 133   *
 134   * @return DateTimeZone Timezone object.
 135   */
 136  function wp_timezone() {
 137      return new DateTimeZone( wp_timezone_string() );
 138  }
 139  
 140  /**
 141   * Retrieves the date in localized format, based on a sum of Unix timestamp and
 142   * timezone offset in seconds.
 143   *
 144   * If the locale specifies the locale month and weekday, then the locale will
 145   * take over the format for the date. If it isn't, then the date format string
 146   * will be used instead.
 147   *
 148   * Note that due to the way WP typically generates a sum of timestamp and offset
 149   * with `strtotime()`, it implies offset added at a _current_ time, not at the time
 150   * the timestamp represents. Storing such timestamps or calculating them differently
 151   * will lead to invalid output.
 152   *
 153   * @since 0.71
 154   * @since 5.3.0 Converted into a wrapper for wp_date().
 155   *
 156   * @global WP_Locale $wp_locale WordPress date and time locale object.
 157   *
 158   * @param string   $format                Format to display the date.
 159   * @param int|bool $timestamp_with_offset Optional. A sum of Unix timestamp and timezone offset
 160   *                                        in seconds. Default false.
 161   * @param bool     $gmt                   Optional. Whether to use GMT timezone. Only applies
 162   *                                        if timestamp is not provided. Default false.
 163   * @return string The date, translated if locale specifies it.
 164   */
 165  function date_i18n( $format, $timestamp_with_offset = false, $gmt = false ) {
 166      $timestamp = $timestamp_with_offset;
 167  
 168      // If timestamp is omitted it should be current time (summed with offset, unless `$gmt` is true).
 169      if ( ! is_numeric( $timestamp ) ) {
 170          $timestamp = current_time( 'timestamp', $gmt );
 171      }
 172  
 173      /*
 174       * This is a legacy implementation quirk that the returned timestamp is also with offset.
 175       * Ideally this function should never be used to produce a timestamp.
 176       */
 177      if ( 'U' === $format ) {
 178          $date = $timestamp;
 179      } elseif ( $gmt && false === $timestamp_with_offset ) { // Current time in UTC.
 180          $date = wp_date( $format, null, new DateTimeZone( 'UTC' ) );
 181      } elseif ( false === $timestamp_with_offset ) { // Current time in site's timezone.
 182          $date = wp_date( $format );
 183      } else {
 184          /*
 185           * Timestamp with offset is typically produced by a UTC `strtotime()` call on an input without timezone.
 186           * This is the best attempt to reverse that operation into a local time to use.
 187           */
 188          $local_time = gmdate( 'Y-m-d H:i:s', $timestamp );
 189          $timezone   = wp_timezone();
 190          $datetime   = date_create( $local_time, $timezone );
 191          $date       = wp_date( $format, $datetime->getTimestamp(), $timezone );
 192      }
 193  
 194      /**
 195       * Filters the date formatted based on the locale.
 196       *
 197       * @since 2.8.0
 198       *
 199       * @param string $date      Formatted date string.
 200       * @param string $format    Format to display the date.
 201       * @param int    $timestamp A sum of Unix timestamp and timezone offset in seconds.
 202       *                          Might be without offset if input omitted timestamp but requested GMT.
 203       * @param bool   $gmt       Whether to use GMT timezone. Only applies if timestamp was not provided.
 204       *                          Default false.
 205       */
 206      $date = apply_filters( 'date_i18n', $date, $format, $timestamp, $gmt );
 207  
 208      return $date;
 209  }
 210  
 211  /**
 212   * Retrieves the date, in localized format.
 213   *
 214   * This is a newer function, intended to replace `date_i18n()` without legacy quirks in it.
 215   *
 216   * Note that, unlike `date_i18n()`, this function accepts a true Unix timestamp, not summed
 217   * with timezone offset.
 218   *
 219   * @since 5.3.0
 220   *
 221   * @param string       $format    PHP date format.
 222   * @param int          $timestamp Optional. Unix timestamp. Defaults to current time.
 223   * @param DateTimeZone $timezone  Optional. Timezone to output result in. Defaults to timezone
 224   *                                from site settings.
 225   * @return string|false The date, translated if locale specifies it. False on invalid timestamp input.
 226   */
 227  function wp_date( $format, $timestamp = null, $timezone = null ) {
 228      global $wp_locale;
 229  
 230      if ( null === $timestamp ) {
 231          $timestamp = time();
 232      } elseif ( ! is_numeric( $timestamp ) ) {
 233          return false;
 234      }
 235  
 236      if ( ! $timezone ) {
 237          $timezone = wp_timezone();
 238      }
 239  
 240      $datetime = date_create( '@' . $timestamp );
 241      $datetime->setTimezone( $timezone );
 242  
 243      if ( empty( $wp_locale->month ) || empty( $wp_locale->weekday ) ) {
 244          $date = $datetime->format( $format );
 245      } else {
 246          // We need to unpack shorthand `r` format because it has parts that might be localized.
 247          $format = preg_replace( '/(?<!\\\\)r/', DATE_RFC2822, $format );
 248  
 249          $new_format    = '';
 250          $format_length = strlen( $format );
 251          $month         = $wp_locale->get_month( $datetime->format( 'm' ) );
 252          $weekday       = $wp_locale->get_weekday( $datetime->format( 'w' ) );
 253  
 254          for ( $i = 0; $i < $format_length; $i ++ ) {
 255              switch ( $format[ $i ] ) {
 256                  case 'D':
 257                      $new_format .= addcslashes( $wp_locale->get_weekday_abbrev( $weekday ), '\\A..Za..z' );
 258                      break;
 259                  case 'F':
 260                      $new_format .= addcslashes( $month, '\\A..Za..z' );
 261                      break;
 262                  case 'l':
 263                      $new_format .= addcslashes( $weekday, '\\A..Za..z' );
 264                      break;
 265                  case 'M':
 266                      $new_format .= addcslashes( $wp_locale->get_month_abbrev( $month ), '\\A..Za..z' );
 267                      break;
 268                  case 'a':
 269                      $new_format .= addcslashes( $wp_locale->get_meridiem( $datetime->format( 'a' ) ), '\\A..Za..z' );
 270                      break;
 271                  case 'A':
 272                      $new_format .= addcslashes( $wp_locale->get_meridiem( $datetime->format( 'A' ) ), '\\A..Za..z' );
 273                      break;
 274                  case '\\':
 275                      $new_format .= $format[ $i ];
 276  
 277                      // If character follows a slash, we add it without translating.
 278                      if ( $i < $format_length ) {
 279                          $new_format .= $format[ ++$i ];
 280                      }
 281                      break;
 282                  default:
 283                      $new_format .= $format[ $i ];
 284                      break;
 285              }
 286          }
 287  
 288          $date = $datetime->format( $new_format );
 289          $date = wp_maybe_decline_date( $date, $format );
 290      }
 291  
 292      /**
 293       * Filters the date formatted based on the locale.
 294       *
 295       * @since 5.3.0
 296       *
 297       * @param string       $date      Formatted date string.
 298       * @param string       $format    Format to display the date.
 299       * @param int          $timestamp Unix timestamp.
 300       * @param DateTimeZone $timezone  Timezone.
 301       */
 302      $date = apply_filters( 'wp_date', $date, $format, $timestamp, $timezone );
 303  
 304      return $date;
 305  }
 306  
 307  /**
 308   * Determines if the date should be declined.
 309   *
 310   * If the locale specifies that month names require a genitive case in certain
 311   * formats (like 'j F Y'), the month name will be replaced with a correct form.
 312   *
 313   * @since 4.4.0
 314   * @since 5.4.0 The `$format` parameter was added.
 315   *
 316   * @global WP_Locale $wp_locale WordPress date and time locale object.
 317   *
 318   * @param string $date   Formatted date string.
 319   * @param string $format Optional. Date format to check. Default empty string.
 320   * @return string The date, declined if locale specifies it.
 321   */
 322  function wp_maybe_decline_date( $date, $format = '' ) {
 323      global $wp_locale;
 324  
 325      // i18n functions are not available in SHORTINIT mode.
 326      if ( ! function_exists( '_x' ) ) {
 327          return $date;
 328      }
 329  
 330      /*
 331       * translators: If months in your language require a genitive case,
 332       * translate this to 'on'. Do not translate into your own language.
 333       */
 334      if ( 'on' === _x( 'off', 'decline months names: on or off' ) ) {
 335  
 336          $months          = $wp_locale->month;
 337          $months_genitive = $wp_locale->month_genitive;
 338  
 339          /*
 340           * Match a format like 'j F Y' or 'j. F' (day of the month, followed by month name)
 341           * and decline the month.
 342           */
 343          if ( $format ) {
 344              $decline = preg_match( '#[dj]\.? F#', $format );
 345          } else {
 346              // If the format is not passed, try to guess it from the date string.
 347              $decline = preg_match( '#\b\d{1,2}\.? [^\d ]+\b#u', $date );
 348          }
 349  
 350          if ( $decline ) {
 351              foreach ( $months as $key => $month ) {
 352                  $months[ $key ] = '# ' . preg_quote( $month, '#' ) . '\b#u';
 353              }
 354  
 355              foreach ( $months_genitive as $key => $month ) {
 356                  $months_genitive[ $key ] = ' ' . $month;
 357              }
 358  
 359              $date = preg_replace( $months, $months_genitive, $date );
 360          }
 361  
 362          /*
 363           * Match a format like 'F jS' or 'F j' (month name, followed by day with an optional ordinal suffix)
 364           * and change it to declined 'j F'.
 365           */
 366          if ( $format ) {
 367              $decline = preg_match( '#F [dj]#', $format );
 368          } else {
 369              // If the format is not passed, try to guess it from the date string.
 370              $decline = preg_match( '#\b[^\d ]+ \d{1,2}(st|nd|rd|th)?\b#u', trim( $date ) );
 371          }
 372  
 373          if ( $decline ) {
 374              foreach ( $months as $key => $month ) {
 375                  $months[ $key ] = '#\b' . preg_quote( $month, '#' ) . ' (\d{1,2})(st|nd|rd|th)?([-–]\d{1,2})?(st|nd|rd|th)?\b#u';
 376              }
 377  
 378              foreach ( $months_genitive as $key => $month ) {
 379                  $months_genitive[ $key ] = '$1$3 ' . $month;
 380              }
 381  
 382              $date = preg_replace( $months, $months_genitive, $date );
 383          }
 384      }
 385  
 386      // Used for locale-specific rules.
 387      $locale = get_locale();
 388  
 389      if ( 'ca' === $locale ) {
 390          // " de abril| de agost| de octubre..." -> " d'abril| d'agost| d'octubre..."
 391          $date = preg_replace( '# de ([ao])#i', " d'\\1", $date );
 392      }
 393  
 394      return $date;
 395  }
 396  
 397  /**
 398   * Convert float number to format based on the locale.
 399   *
 400   * @since 2.3.0
 401   *
 402   * @global WP_Locale $wp_locale WordPress date and time locale object.
 403   *
 404   * @param float $number   The number to convert based on locale.
 405   * @param int   $decimals Optional. Precision of the number of decimal places. Default 0.
 406   * @return string Converted number in string format.
 407   */
 408  function number_format_i18n( $number, $decimals = 0 ) {
 409      global $wp_locale;
 410  
 411      if ( isset( $wp_locale ) ) {
 412          $formatted = number_format( $number, absint( $decimals ), $wp_locale->number_format['decimal_point'], $wp_locale->number_format['thousands_sep'] );
 413      } else {
 414          $formatted = number_format( $number, absint( $decimals ) );
 415      }
 416  
 417      /**
 418       * Filters the number formatted based on the locale.
 419       *
 420       * @since 2.8.0
 421       * @since 4.9.0 The `$number` and `$decimals` parameters were added.
 422       *
 423       * @param string $formatted Converted number in string format.
 424       * @param float  $number    The number to convert based on locale.
 425       * @param int    $decimals  Precision of the number of decimal places.
 426       */
 427      return apply_filters( 'number_format_i18n', $formatted, $number, $decimals );
 428  }
 429  
 430  /**
 431   * Convert number of bytes largest unit bytes will fit into.
 432   *
 433   * It is easier to read 1 KB than 1024 bytes and 1 MB than 1048576 bytes. Converts
 434   * number of bytes to human readable number by taking the number of that unit
 435   * that the bytes will go into it. Supports TB value.
 436   *
 437   * Please note that integers in PHP are limited to 32 bits, unless they are on
 438   * 64 bit architecture, then they have 64 bit size. If you need to place the
 439   * larger size then what PHP integer type will hold, then use a string. It will
 440   * be converted to a double, which should always have 64 bit length.
 441   *
 442   * Technically the correct unit names for powers of 1024 are KiB, MiB etc.
 443   *
 444   * @since 2.3.0
 445   *
 446   * @param int|string $bytes    Number of bytes. Note max integer size for integers.
 447   * @param int        $decimals Optional. Precision of number of decimal places. Default 0.
 448   * @return string|false Number string on success, false on failure.
 449   */
 450  function size_format( $bytes, $decimals = 0 ) {
 451      $quant = array(
 452          /* translators: Unit symbol for terabyte. */
 453          _x( 'TB', 'unit symbol' ) => TB_IN_BYTES,
 454          /* translators: Unit symbol for gigabyte. */
 455          _x( 'GB', 'unit symbol' ) => GB_IN_BYTES,
 456          /* translators: Unit symbol for megabyte. */
 457          _x( 'MB', 'unit symbol' ) => MB_IN_BYTES,
 458          /* translators: Unit symbol for kilobyte. */
 459          _x( 'KB', 'unit symbol' ) => KB_IN_BYTES,
 460          /* translators: Unit symbol for byte. */
 461          _x( 'B', 'unit symbol' )  => 1,
 462      );
 463  
 464      if ( 0 === $bytes ) {
 465          /* translators: Unit symbol for byte. */
 466          return number_format_i18n( 0, $decimals ) . ' ' . _x( 'B', 'unit symbol' );
 467      }
 468  
 469      foreach ( $quant as $unit => $mag ) {
 470          if ( (float) $bytes >= $mag ) {
 471              return number_format_i18n( $bytes / $mag, $decimals ) . ' ' . $unit;
 472          }
 473      }
 474  
 475      return false;
 476  }
 477  
 478  /**
 479   * Convert a duration to human readable format.
 480   *
 481   * @since 5.1.0
 482   *
 483   * @param string $duration Duration will be in string format (HH:ii:ss) OR (ii:ss),
 484   *                         with a possible prepended negative sign (-).
 485   * @return string|false A human readable duration string, false on failure.
 486   */
 487  function human_readable_duration( $duration = '' ) {
 488      if ( ( empty( $duration ) || ! is_string( $duration ) ) ) {
 489          return false;
 490      }
 491  
 492      $duration = trim( $duration );
 493  
 494      // Remove prepended negative sign.
 495      if ( '-' === substr( $duration, 0, 1 ) ) {
 496          $duration = substr( $duration, 1 );
 497      }
 498  
 499      // Extract duration parts.
 500      $duration_parts = array_reverse( explode( ':', $duration ) );
 501      $duration_count = count( $duration_parts );
 502  
 503      $hour   = null;
 504      $minute = null;
 505      $second = null;
 506  
 507      if ( 3 === $duration_count ) {
 508          // Validate HH:ii:ss duration format.
 509          if ( ! ( (bool) preg_match( '/^([0-9]+):([0-5]?[0-9]):([0-5]?[0-9])$/', $duration ) ) ) {
 510              return false;
 511          }
 512          // Three parts: hours, minutes & seconds.
 513          list( $second, $minute, $hour ) = $duration_parts;
 514      } elseif ( 2 === $duration_count ) {
 515          // Validate ii:ss duration format.
 516          if ( ! ( (bool) preg_match( '/^([0-5]?[0-9]):([0-5]?[0-9])$/', $duration ) ) ) {
 517              return false;
 518          }
 519          // Two parts: minutes & seconds.
 520          list( $second, $minute ) = $duration_parts;
 521      } else {
 522          return false;
 523      }
 524  
 525      $human_readable_duration = array();
 526  
 527      // Add the hour part to the string.
 528      if ( is_numeric( $hour ) ) {
 529          /* translators: %s: Time duration in hour or hours. */
 530          $human_readable_duration[] = sprintf( _n( '%s hour', '%s hours', $hour ), (int) $hour );
 531      }
 532  
 533      // Add the minute part to the string.
 534      if ( is_numeric( $minute ) ) {
 535          /* translators: %s: Time duration in minute or minutes. */
 536          $human_readable_duration[] = sprintf( _n( '%s minute', '%s minutes', $minute ), (int) $minute );
 537      }
 538  
 539      // Add the second part to the string.
 540      if ( is_numeric( $second ) ) {
 541          /* translators: %s: Time duration in second or seconds. */
 542          $human_readable_duration[] = sprintf( _n( '%s second', '%s seconds', $second ), (int) $second );
 543      }
 544  
 545      return implode( ', ', $human_readable_duration );
 546  }
 547  
 548  /**
 549   * Get the week start and end from the datetime or date string from MySQL.
 550   *
 551   * @since 0.71
 552   *
 553   * @param string     $mysqlstring   Date or datetime field type from MySQL.
 554   * @param int|string $start_of_week Optional. Start of the week as an integer. Default empty string.
 555   * @return array Keys are 'start' and 'end'.
 556   */
 557  function get_weekstartend( $mysqlstring, $start_of_week = '' ) {
 558      // MySQL string year.
 559      $my = substr( $mysqlstring, 0, 4 );
 560  
 561      // MySQL string month.
 562      $mm = substr( $mysqlstring, 8, 2 );
 563  
 564      // MySQL string day.
 565      $md = substr( $mysqlstring, 5, 2 );
 566  
 567      // The timestamp for MySQL string day.
 568      $day = mktime( 0, 0, 0, $md, $mm, $my );
 569  
 570      // The day of the week from the timestamp.
 571      $weekday = gmdate( 'w', $day );
 572  
 573      if ( ! is_numeric( $start_of_week ) ) {
 574          $start_of_week = get_option( 'start_of_week' );
 575      }
 576  
 577      if ( $weekday < $start_of_week ) {
 578          $weekday += 7;
 579      }
 580  
 581      // The most recent week start day on or before $day.
 582      $start = $day - DAY_IN_SECONDS * ( $weekday - $start_of_week );
 583  
 584      // $start + 1 week - 1 second.
 585      $end = $start + WEEK_IN_SECONDS - 1;
 586      return compact( 'start', 'end' );
 587  }
 588  
 589  /**
 590   * Serialize data, if needed.
 591   *
 592   * @since 2.0.5
 593   *
 594   * @param string|array|object $data Data that might be serialized.
 595   * @return mixed A scalar data.
 596   */
 597  function maybe_serialize( $data ) {
 598      if ( is_array( $data ) || is_object( $data ) ) {
 599          return serialize( $data );
 600      }
 601  
 602      /*
 603       * Double serialization is required for backward compatibility.
 604       * See https://core.trac.wordpress.org/ticket/12930
 605       * Also the world will end. See WP 3.6.1.
 606       */
 607      if ( is_serialized( $data, false ) ) {
 608          return serialize( $data );
 609      }
 610  
 611      return $data;
 612  }
 613  
 614  /**
 615   * Unserialize data only if it was serialized.
 616   *
 617   * @since 2.0.0
 618   *
 619   * @param string $data Data that might be unserialized.
 620   * @return mixed Unserialized data can be any type.
 621   */
 622  function maybe_unserialize( $data ) {
 623      if ( is_serialized( $data ) ) { // Don't attempt to unserialize data that wasn't serialized going in.
 624          return @unserialize( trim( $data ) );
 625      }
 626  
 627      return $data;
 628  }
 629  
 630  /**
 631   * Check value to find if it was serialized.
 632   *
 633   * If $data is not an string, then returned value will always be false.
 634   * Serialized data is always a string.
 635   *
 636   * @since 2.0.5
 637   *
 638   * @param string $data   Value to check to see if was serialized.
 639   * @param bool   $strict Optional. Whether to be strict about the end of the string. Default true.
 640   * @return bool False if not serialized and true if it was.
 641   */
 642  function is_serialized( $data, $strict = true ) {
 643      // If it isn't a string, it isn't serialized.
 644      if ( ! is_string( $data ) ) {
 645          return false;
 646      }
 647      $data = trim( $data );
 648      if ( 'N;' === $data ) {
 649          return true;
 650      }
 651      if ( strlen( $data ) < 4 ) {
 652          return false;
 653      }
 654      if ( ':' !== $data[1] ) {
 655          return false;
 656      }
 657      if ( $strict ) {
 658          $lastc = substr( $data, -1 );
 659          if ( ';' !== $lastc && '}' !== $lastc ) {
 660              return false;
 661          }
 662      } else {
 663          $semicolon = strpos( $data, ';' );
 664          $brace     = strpos( $data, '}' );
 665          // Either ; or } must exist.
 666          if ( false === $semicolon && false === $brace ) {
 667              return false;
 668          }
 669          // But neither must be in the first X characters.
 670          if ( false !== $semicolon && $semicolon < 3 ) {
 671              return false;
 672          }
 673          if ( false !== $brace && $brace < 4 ) {
 674              return false;
 675          }
 676      }
 677      $token = $data[0];
 678      switch ( $token ) {
 679          case 's':
 680              if ( $strict ) {
 681                  if ( '"' !== substr( $data, -2, 1 ) ) {
 682                      return false;
 683                  }
 684              } elseif ( false === strpos( $data, '"' ) ) {
 685                  return false;
 686              }
 687              // Or else fall through.
 688          case 'a':
 689          case 'O':
 690              return (bool) preg_match( "/^{$token}:[0-9]+:/s", $data );
 691          case 'b':
 692          case 'i':
 693          case 'd':
 694              $end = $strict ? '$' : '';
 695              return (bool) preg_match( "/^{$token}:[0-9.E+-]+;$end/", $data );
 696      }
 697      return false;
 698  }
 699  
 700  /**
 701   * Check whether serialized data is of string type.
 702   *
 703   * @since 2.0.5
 704   *
 705   * @param string $data Serialized data.
 706   * @return bool False if not a serialized string, true if it is.
 707   */
 708  function is_serialized_string( $data ) {
 709      // if it isn't a string, it isn't a serialized string.
 710      if ( ! is_string( $data ) ) {
 711          return false;
 712      }
 713      $data = trim( $data );
 714      if ( strlen( $data ) < 4 ) {
 715          return false;
 716      } elseif ( ':' !== $data[1] ) {
 717          return false;
 718      } elseif ( ';' !== substr( $data, -1 ) ) {
 719          return false;
 720      } elseif ( 's' !== $data[0] ) {
 721          return false;
 722      } elseif ( '"' !== substr( $data, -2, 1 ) ) {
 723          return false;
 724      } else {
 725          return true;
 726      }
 727  }
 728  
 729  /**
 730   * Retrieve post title from XMLRPC XML.
 731   *
 732   * If the title element is not part of the XML, then the default post title from
 733   * the $post_default_title will be used instead.
 734   *
 735   * @since 0.71
 736   *
 737   * @global string $post_default_title Default XML-RPC post title.
 738   *
 739   * @param string $content XMLRPC XML Request content
 740   * @return string Post title
 741   */
 742  function xmlrpc_getposttitle( $content ) {
 743      global $post_default_title;
 744      if ( preg_match( '/<title>(.+?)<\/title>/is', $content, $matchtitle ) ) {
 745          $post_title = $matchtitle[1];
 746      } else {
 747          $post_title = $post_default_title;
 748      }
 749      return $post_title;
 750  }
 751  
 752  /**
 753   * Retrieve the post category or categories from XMLRPC XML.
 754   *
 755   * If the category element is not found, then the default post category will be
 756   * used. The return type then would be what $post_default_category. If the
 757   * category is found, then it will always be an array.
 758   *
 759   * @since 0.71
 760   *
 761   * @global string $post_default_category Default XML-RPC post category.
 762   *
 763   * @param string $content XMLRPC XML Request content
 764   * @return string|array List of categories or category name.
 765   */
 766  function xmlrpc_getpostcategory( $content ) {
 767      global $post_default_category;
 768      if ( preg_match( '/<category>(.+?)<\/category>/is', $content, $matchcat ) ) {
 769          $post_category = trim( $matchcat[1], ',' );
 770          $post_category = explode( ',', $post_category );
 771      } else {
 772          $post_category = $post_default_category;
 773      }
 774      return $post_category;
 775  }
 776  
 777  /**
 778   * XMLRPC XML content without title and category elements.
 779   *
 780   * @since 0.71
 781   *
 782   * @param string $content XML-RPC XML Request content.
 783   * @return string XMLRPC XML Request content without title and category elements.
 784   */
 785  function xmlrpc_removepostdata( $content ) {
 786      $content = preg_replace( '/<title>(.+?)<\/title>/si', '', $content );
 787      $content = preg_replace( '/<category>(.+?)<\/category>/si', '', $content );
 788      $content = trim( $content );
 789      return $content;
 790  }
 791  
 792  /**
 793   * Use RegEx to extract URLs from arbitrary content.
 794   *
 795   * @since 3.7.0
 796   *
 797   * @param string $content Content to extract URLs from.
 798   * @return string[] Array of URLs found in passed string.
 799   */
 800  function wp_extract_urls( $content ) {
 801      preg_match_all(
 802          "#([\"']?)("
 803              . '(?:([\w-]+:)?//?)'
 804              . '[^\s()<>]+'
 805              . '[.]'
 806              . '(?:'
 807                  . '\([\w\d]+\)|'
 808                  . '(?:'
 809                      . "[^`!()\[\]{};:'\".,<>«»“”‘’\s]|"
 810                      . '(?:[:]\d+)?/?'
 811                  . ')+'
 812              . ')'
 813          . ")\\1#",
 814          $content,
 815          $post_links
 816      );
 817  
 818      $post_links = array_unique( array_map( 'html_entity_decode', $post_links[2] ) );
 819  
 820      return array_values( $post_links );
 821  }
 822  
 823  /**
 824   * Check content for video and audio links to add as enclosures.
 825   *
 826   * Will not add enclosures that have already been added and will
 827   * remove enclosures that are no longer in the post. This is called as
 828   * pingbacks and trackbacks.
 829   *
 830   * @since 1.5.0
 831   * @since 5.3.0 The `$content` parameter was made optional, and the `$post` parameter was
 832   *              updated to accept a post ID or a WP_Post object.
 833   * @since 5.6.0 The `$content` parameter is no longer optional, but passing `null` to skip it
 834   *              is still supported.
 835   *
 836   * @global wpdb $wpdb WordPress database abstraction object.
 837   *
 838   * @param string|null $content Post content. If `null`, the `post_content` field from `$post` is used.
 839   * @param int|WP_Post $post    Post ID or post object.
 840   * @return void|false Void on success, false if the post is not found.
 841   */
 842  function do_enclose( $content, $post ) {
 843      global $wpdb;
 844  
 845      // @todo Tidy this code and make the debug code optional.
 846      include_once  ABSPATH . WPINC . '/class-IXR.php';
 847  
 848      $post = get_post( $post );
 849      if ( ! $post ) {
 850          return false;
 851      }
 852  
 853      if ( null === $content ) {
 854          $content = $post->post_content;
 855      }
 856  
 857      $post_links = array();
 858  
 859      $pung = get_enclosed( $post->ID );
 860  
 861      $post_links_temp = wp_extract_urls( $content );
 862  
 863      foreach ( $pung as $link_test ) {
 864          // Link is no longer in post.
 865          if ( ! in_array( $link_test, $post_links_temp, true ) ) {
 866              $mids = $wpdb->get_col( $wpdb->prepare( "SELECT meta_id FROM $wpdb->postmeta WHERE post_id = %d AND meta_key = 'enclosure' AND meta_value LIKE %s", $post->ID, $wpdb->esc_like( $link_test ) . '%' ) );
 867              foreach ( $mids as $mid ) {
 868                  delete_metadata_by_mid( 'post', $mid );
 869              }
 870          }
 871      }
 872  
 873      foreach ( (array) $post_links_temp as $link_test ) {
 874          // If we haven't pung it already.
 875          if ( ! in_array( $link_test, $pung, true ) ) {
 876              $test = parse_url( $link_test );
 877              if ( false === $test ) {
 878                  continue;
 879              }
 880              if ( isset( $test['query'] ) ) {
 881                  $post_links[] = $link_test;
 882              } elseif ( isset( $test['path'] ) && ( '/' !== $test['path'] ) && ( '' !== $test['path'] ) ) {
 883                  $post_links[] = $link_test;
 884              }
 885          }
 886      }
 887  
 888      /**
 889       * Filters the list of enclosure links before querying the database.
 890       *
 891       * Allows for the addition and/or removal of potential enclosures to save
 892       * to postmeta before checking the database for existing enclosures.
 893       *
 894       * @since 4.4.0
 895       *
 896       * @param string[] $post_links An array of enclosure links.
 897       * @param int      $post_ID    Post ID.
 898       */
 899      $post_links = apply_filters( 'enclosure_links', $post_links, $post->ID );
 900  
 901      foreach ( (array) $post_links as $url ) {
 902          $url = strip_fragment_from_url( $url );
 903  
 904          if ( '' !== $url && ! $wpdb->get_var( $wpdb->prepare( "SELECT post_id FROM $wpdb->postmeta WHERE post_id = %d AND meta_key = 'enclosure' AND meta_value LIKE %s", $post->ID, $wpdb->esc_like( $url ) . '%' ) ) ) {
 905  
 906              $headers = wp_get_http_headers( $url );
 907              if ( $headers ) {
 908                  $len           = isset( $headers['content-length'] ) ? (int) $headers['content-length'] : 0;
 909                  $type          = isset( $headers['content-type'] ) ? $headers['content-type'] : '';
 910                  $allowed_types = array( 'video', 'audio' );
 911  
 912                  // Check to see if we can figure out the mime type from the extension.
 913                  $url_parts = parse_url( $url );
 914                  if ( false !== $url_parts && ! empty( $url_parts['path'] ) ) {
 915                      $extension = pathinfo( $url_parts['path'], PATHINFO_EXTENSION );
 916                      if ( ! empty( $extension ) ) {
 917                          foreach ( wp_get_mime_types() as $exts => $mime ) {
 918                              if ( preg_match( '!^(' . $exts . ')$!i', $extension ) ) {
 919                                  $type = $mime;
 920                                  break;
 921                              }
 922                          }
 923                      }
 924                  }
 925  
 926                  if ( in_array( substr( $type, 0, strpos( $type, '/' ) ), $allowed_types, true ) ) {
 927                      add_post_meta( $post->ID, 'enclosure', "$url\n$len\n$mime\n" );
 928                  }
 929              }
 930          }
 931      }
 932  }
 933  
 934  /**
 935   * Retrieve HTTP Headers from URL.
 936   *
 937   * @since 1.5.1
 938   *
 939   * @param string $url        URL to retrieve HTTP headers from.
 940   * @param bool   $deprecated Not Used.
 941   * @return string|false Headers on success, false on failure.
 942   */
 943  function wp_get_http_headers( $url, $deprecated = false ) {
 944      if ( ! empty( $deprecated ) ) {
 945          _deprecated_argument( __FUNCTION__, '2.7.0' );
 946      }
 947  
 948      $response = wp_safe_remote_head( $url );
 949  
 950      if ( is_wp_error( $response ) ) {
 951          return false;
 952      }
 953  
 954      return wp_remote_retrieve_headers( $response );
 955  }
 956  
 957  /**
 958   * Determines whether the publish date of the current post in the loop is different
 959   * from the publish date of the previous post in the loop.
 960   *
 961   * For more information on this and similar theme functions, check out
 962   * the {@link https://developer.wordpress.org/themes/basics/conditional-tags/
 963   * Conditional Tags} article in the Theme Developer Handbook.
 964   *
 965   * @since 0.71
 966   *
 967   * @global string $currentday  The day of the current post in the loop.
 968   * @global string $previousday The day of the previous post in the loop.
 969   *
 970   * @return int 1 when new day, 0 if not a new day.
 971   */
 972  function is_new_day() {
 973      global $currentday, $previousday;
 974  
 975      if ( $currentday !== $previousday ) {
 976          return 1;
 977      } else {
 978          return 0;
 979      }
 980  }
 981  
 982  /**
 983   * Build URL query based on an associative and, or indexed array.
 984   *
 985   * This is a convenient function for easily building url queries. It sets the
 986   * separator to '&' and uses _http_build_query() function.
 987   *
 988   * @since 2.3.0
 989   *
 990   * @see _http_build_query() Used to build the query
 991   * @link https://www.php.net/manual/en/function.http-build-query.php for more on what
 992   *       http_build_query() does.
 993   *
 994   * @param array $data URL-encode key/value pairs.
 995   * @return string URL-encoded string.
 996   */
 997  function build_query( $data ) {
 998      return _http_build_query( $data, null, '&', '', false );
 999  }
1000  
1001  /**
1002   * From php.net (modified by Mark Jaquith to behave like the native PHP5 function).
1003   *
1004   * @since 3.2.0
1005   * @access private
1006   *
1007   * @see https://www.php.net/manual/en/function.http-build-query.php
1008   *
1009   * @param array|object $data      An array or object of data. Converted to array.
1010   * @param string       $prefix    Optional. Numeric index. If set, start parameter numbering with it.
1011   *                                Default null.
1012   * @param string       $sep       Optional. Argument separator; defaults to 'arg_separator.output'.
1013   *                                Default null.
1014   * @param string       $key       Optional. Used to prefix key name. Default empty.
1015   * @param bool         $urlencode Optional. Whether to use urlencode() in the result. Default true.
1016   * @return string The query string.
1017   */
1018  function _http_build_query( $data, $prefix = null, $sep = null, $key = '', $urlencode = true ) {
1019      $ret = array();
1020  
1021      foreach ( (array) $data as $k => $v ) {
1022          if ( $urlencode ) {
1023              $k = urlencode( $k );
1024          }
1025          if ( is_int( $k ) && null != $prefix ) {
1026              $k = $prefix . $k;
1027          }
1028          if ( ! empty( $key ) ) {
1029              $k = $key . '%5B' . $k . '%5D';
1030          }
1031          if ( null === $v ) {
1032              continue;
1033          } elseif ( false === $v ) {
1034              $v = '0';
1035          }
1036  
1037          if ( is_array( $v ) || is_object( $v ) ) {
1038              array_push( $ret, _http_build_query( $v, '', $sep, $k, $urlencode ) );
1039          } elseif ( $urlencode ) {
1040              array_push( $ret, $k . '=' . urlencode( $v ) );
1041          } else {
1042              array_push( $ret, $k . '=' . $v );
1043          }
1044      }
1045  
1046      if ( null === $sep ) {
1047          $sep = ini_get( 'arg_separator.output' );
1048      }
1049  
1050      return implode( $sep, $ret );
1051  }
1052  
1053  /**
1054   * Retrieves a modified URL query string.
1055   *
1056   * You can rebuild the URL and append query variables to the URL query by using this function.
1057   * There are two ways to use this function; either a single key and value, or an associative array.
1058   *
1059   * Using a single key and value:
1060   *
1061   *     add_query_arg( 'key', 'value', 'http://example.com' );
1062   *
1063   * Using an associative array:
1064   *
1065   *     add_query_arg( array(
1066   *         'key1' => 'value1',
1067   *         'key2' => 'value2',
1068   *     ), 'http://example.com' );
1069   *
1070   * Omitting the URL from either use results in the current URL being used
1071   * (the value of `$_SERVER['REQUEST_URI']`).
1072   *
1073   * Values are expected to be encoded appropriately with urlencode() or rawurlencode().
1074   *
1075   * Setting any query variable's value to boolean false removes the key (see remove_query_arg()).
1076   *
1077   * Important: The return value of add_query_arg() is not escaped by default. Output should be
1078   * late-escaped with esc_url() or similar to help prevent vulnerability to cross-site scripting
1079   * (XSS) attacks.
1080   *
1081   * @since 1.5.0
1082   * @since 5.3.0 Formalized the existing and already documented parameters
1083   *              by adding `...$args` to the function signature.
1084   *
1085   * @param string|array $key   Either a query variable key, or an associative array of query variables.
1086   * @param string       $value Optional. Either a query variable value, or a URL to act upon.
1087   * @param string       $url   Optional. A URL to act upon.
1088   * @return string New URL query string (unescaped).
1089   */
1090  function add_query_arg( ...$args ) {
1091      if ( is_array( $args[0] ) ) {
1092          if ( count( $args ) < 2 || false === $args[1] ) {
1093              $uri = $_SERVER['REQUEST_URI'];
1094          } else {
1095              $uri = $args[1];
1096          }
1097      } else {
1098          if ( count( $args ) < 3 || false === $args[2] ) {
1099              $uri = $_SERVER['REQUEST_URI'];
1100          } else {
1101              $uri = $args[2];
1102          }
1103      }
1104  
1105      $frag = strstr( $uri, '#' );
1106      if ( $frag ) {
1107          $uri = substr( $uri, 0, -strlen( $frag ) );
1108      } else {
1109          $frag = '';
1110      }
1111  
1112      if ( 0 === stripos( $uri, 'http://' ) ) {
1113          $protocol = 'http://';
1114          $uri      = substr( $uri, 7 );
1115      } elseif ( 0 === stripos( $uri, 'https://' ) ) {
1116          $protocol = 'https://';
1117          $uri      = substr( $uri, 8 );
1118      } else {
1119          $protocol = '';
1120      }
1121  
1122      if ( strpos( $uri, '?' ) !== false ) {
1123          list( $base, $query ) = explode( '?', $uri, 2 );
1124          $base                .= '?';
1125      } elseif ( $protocol || strpos( $uri, '=' ) === false ) {
1126          $base  = $uri . '?';
1127          $query = '';
1128      } else {
1129          $base  = '';
1130          $query = $uri;
1131      }
1132  
1133      wp_parse_str( $query, $qs );
1134      $qs = urlencode_deep( $qs ); // This re-URL-encodes things that were already in the query string.
1135      if ( is_array( $args[0] ) ) {
1136          foreach ( $args[0] as $k => $v ) {
1137              $qs[ $k ] = $v;
1138          }
1139      } else {
1140          $qs[ $args[0] ] = $args[1];
1141      }
1142  
1143      foreach ( $qs as $k => $v ) {
1144          if ( false === $v ) {
1145              unset( $qs[ $k ] );
1146          }
1147      }
1148  
1149      $ret = build_query( $qs );
1150      $ret = trim( $ret, '?' );
1151      $ret = preg_replace( '#=(&|$)#', '$1', $ret );
1152      $ret = $protocol . $base . $ret . $frag;
1153      $ret = rtrim( $ret, '?' );
1154      return $ret;
1155  }
1156  
1157  /**
1158   * Removes an item or items from a query string.
1159   *
1160   * @since 1.5.0
1161   *
1162   * @param string|string[] $key   Query key or keys to remove.
1163   * @param false|string    $query Optional. When false uses the current URL. Default false.
1164   * @return string New URL query string.
1165   */
1166  function remove_query_arg( $key, $query = false ) {
1167      if ( is_array( $key ) ) { // Removing multiple keys.
1168          foreach ( $key as $k ) {
1169              $query = add_query_arg( $k, false, $query );
1170          }
1171          return $query;
1172      }
1173      return add_query_arg( $key, false, $query );
1174  }
1175  
1176  /**
1177   * Returns an array of single-use query variable names that can be removed from a URL.
1178   *
1179   * @since 4.4.0
1180   *
1181   * @return string[] An array of query variable names to remove from the URL.
1182   */
1183  function wp_removable_query_args() {
1184      $removable_query_args = array(
1185          'activate',
1186          'activated',
1187          'admin_email_remind_later',
1188          'approved',
1189          'core-major-auto-updates-saved',
1190          'deactivate',
1191          'delete_count',
1192          'deleted',
1193          'disabled',
1194          'doing_wp_cron',
1195          'enabled',
1196          'error',
1197          'hotkeys_highlight_first',
1198          'hotkeys_highlight_last',
1199          'ids',
1200          'locked',
1201          'message',
1202          'same',
1203          'saved',
1204          'settings-updated',
1205          'skipped',
1206          'spammed',
1207          'trashed',
1208          'unspammed',
1209          'untrashed',
1210          'update',
1211          'updated',
1212          'wp-post-new-reload',
1213      );
1214  
1215      /**
1216       * Filters the list of query variable names to remove.
1217       *
1218       * @since 4.2.0
1219       *
1220       * @param string[] $removable_query_args An array of query variable names to remove from a URL.
1221       */
1222      return apply_filters( 'removable_query_args', $removable_query_args );
1223  }
1224  
1225  /**
1226   * Walks the array while sanitizing the contents.
1227   *
1228   * @since 0.71
1229   * @since 5.5.0 Non-string values are left untouched.
1230   *
1231   * @param array $array Array to walk while sanitizing contents.
1232   * @return array Sanitized $array.
1233   */
1234  function add_magic_quotes( $array ) {
1235      foreach ( (array) $array as $k => $v ) {
1236          if ( is_array( $v ) ) {
1237              $array[ $k ] = add_magic_quotes( $v );
1238          } elseif ( is_string( $v ) ) {
1239              $array[ $k ] = addslashes( $v );
1240          } else {
1241              continue;
1242          }
1243      }
1244  
1245      return $array;
1246  }
1247  
1248  /**
1249   * HTTP request for URI to retrieve content.
1250   *
1251   * @since 1.5.1
1252   *
1253   * @see wp_safe_remote_get()
1254   *
1255   * @param string $uri URI/URL of web page to retrieve.
1256   * @return string|false HTTP content. False on failure.
1257   */
1258  function wp_remote_fopen( $uri ) {
1259      $parsed_url = parse_url( $uri );
1260  
1261      if ( ! $parsed_url || ! is_array( $parsed_url ) ) {
1262          return false;
1263      }
1264  
1265      $options            = array();
1266      $options['timeout'] = 10;
1267  
1268      $response = wp_safe_remote_get( $uri, $options );
1269  
1270      if ( is_wp_error( $response ) ) {
1271          return false;
1272      }
1273  
1274      return wp_remote_retrieve_body( $response );
1275  }
1276  
1277  /**
1278   * Set up the WordPress query.
1279   *
1280   * @since 2.0.0
1281   *
1282   * @global WP       $wp           Current WordPress environment instance.
1283   * @global WP_Query $wp_query     WordPress Query object.
1284   * @global WP_Query $wp_the_query Copy of the WordPress Query object.
1285   *
1286   * @param string|array $query_vars Default WP_Query arguments.
1287   */
1288  function wp( $query_vars = '' ) {
1289      global $wp, $wp_query, $wp_the_query;
1290  
1291      $wp->main( $query_vars );
1292  
1293      if ( ! isset( $wp_the_query ) ) {
1294          $wp_the_query = $wp_query;
1295      }
1296  }
1297  
1298  /**
1299   * Retrieve the description for the HTTP status.
1300   *
1301   * @since 2.3.0
1302   * @since 3.9.0 Added status codes 418, 428, 429, 431, and 511.
1303   * @since 4.5.0 Added status codes 308, 421, and 451.
1304   * @since 5.1.0 Added status code 103.
1305   *
1306   * @global array $wp_header_to_desc
1307   *
1308   * @param int $code HTTP status code.
1309   * @return string Status description if found, an empty string otherwise.
1310   */
1311  function get_status_header_desc( $code ) {
1312      global $wp_header_to_desc;
1313  
1314      $code = absint( $code );
1315  
1316      if ( ! isset( $wp_header_to_desc ) ) {
1317          $wp_header_to_desc = array(
1318              100 => 'Continue',
1319              101 => 'Switching Protocols',
1320              102 => 'Processing',
1321              103 => 'Early Hints',
1322  
1323              200 => 'OK',
1324              201 => 'Created',
1325              202 => 'Accepted',
1326              203 => 'Non-Authoritative Information',
1327              204 => 'No Content',
1328              205 => 'Reset Content',
1329              206 => 'Partial Content',
1330              207 => 'Multi-Status',
1331              226 => 'IM Used',
1332  
1333              300 => 'Multiple Choices',
1334              301 => 'Moved Permanently',
1335              302 => 'Found',
1336              303 => 'See Other',
1337              304 => 'Not Modified',
1338              305 => 'Use Proxy',
1339              306 => 'Reserved',
1340              307 => 'Temporary Redirect',
1341              308 => 'Permanent Redirect',
1342  
1343              400 => 'Bad Request',
1344              401 => 'Unauthorized',
1345              402 => 'Payment Required',
1346              403 => 'Forbidden',
1347              404 => 'Not Found',
1348              405 => 'Method Not Allowed',
1349              406 => 'Not Acceptable',
1350              407 => 'Proxy Authentication Required',
1351              408 => 'Request Timeout',
1352              409 => 'Conflict',
1353              410 => 'Gone',
1354              411 => 'Length Required',
1355              412 => 'Precondition Failed',
1356              413 => 'Request Entity Too Large',
1357              414 => 'Request-URI Too Long',
1358              415 => 'Unsupported Media Type',
1359              416 => 'Requested Range Not Satisfiable',
1360              417 => 'Expectation Failed',
1361              418 => 'I\'m a teapot',
1362              421 => 'Misdirected Request',
1363              422 => 'Unprocessable Entity',
1364              423 => 'Locked',
1365              424 => 'Failed Dependency',
1366              426 => 'Upgrade Required',
1367              428 => 'Precondition Required',
1368              429 => 'Too Many Requests',
1369              431 => 'Request Header Fields Too Large',
1370              451 => 'Unavailable For Legal Reasons',
1371  
1372              500 => 'Internal Server Error',
1373              501 => 'Not Implemented',
1374              502 => 'Bad Gateway',
1375              503 => 'Service Unavailable',
1376              504 => 'Gateway Timeout',
1377              505 => 'HTTP Version Not Supported',
1378              506 => 'Variant Also Negotiates',
1379              507 => 'Insufficient Storage',
1380              510 => 'Not Extended',
1381              511 => 'Network Authentication Required',
1382          );
1383      }
1384  
1385      if ( isset( $wp_header_to_desc[ $code ] ) ) {
1386          return $wp_header_to_desc[ $code ];
1387      } else {
1388          return '';
1389      }
1390  }
1391  
1392  /**
1393   * Set HTTP status header.
1394   *
1395   * @since 2.0.0
1396   * @since 4.4.0 Added the `$description` parameter.
1397   *
1398   * @see get_status_header_desc()
1399   *
1400   * @param int    $code        HTTP status code.
1401   * @param string $description Optional. A custom description for the HTTP status.
1402   */
1403  function status_header( $code, $description = '' ) {
1404      if ( ! $description ) {
1405          $description = get_status_header_desc( $code );
1406      }
1407  
1408      if ( empty( $description ) ) {
1409          return;
1410      }
1411  
1412      $protocol      = wp_get_server_protocol();
1413      $status_header = "$protocol $code $description";
1414      if ( function_exists( 'apply_filters' ) ) {
1415  
1416          /**
1417           * Filters an HTTP status header.
1418           *
1419           * @since 2.2.0
1420           *
1421           * @param string $status_header HTTP status header.
1422           * @param int    $code          HTTP status code.
1423           * @param string $description   Description for the status code.
1424           * @param string $protocol      Server protocol.
1425           */
1426          $status_header = apply_filters( 'status_header', $status_header, $code, $description, $protocol );
1427      }
1428  
1429      if ( ! headers_sent() ) {
1430          header( $status_header, true, $code );
1431      }
1432  }
1433  
1434  /**
1435   * Get the header information to prevent caching.
1436   *
1437   * The several different headers cover the different ways cache prevention
1438   * is handled by different browsers
1439   *
1440   * @since 2.8.0
1441   *
1442   * @return array The associative array of header names and field values.
1443   */
1444  function wp_get_nocache_headers() {
1445      $headers = array(
1446          'Expires'       => 'Wed, 11 Jan 1984 05:00:00 GMT',
1447          'Cache-Control' => 'no-cache, must-revalidate, max-age=0',
1448      );
1449  
1450      if ( function_exists( 'apply_filters' ) ) {
1451          /**
1452           * Filters the cache-controlling headers.
1453           *
1454           * @since 2.8.0
1455           *
1456           * @see wp_get_nocache_headers()
1457           *
1458           * @param array $headers {
1459           *     Header names and field values.
1460           *
1461           *     @type string $Expires       Expires header.
1462           *     @type string $Cache-Control Cache-Control header.
1463           * }
1464           */
1465          $headers = (array) apply_filters( 'nocache_headers', $headers );
1466      }
1467      $headers['Last-Modified'] = false;
1468      return $headers;
1469  }
1470  
1471  /**
1472   * Set the headers to prevent caching for the different browsers.
1473   *
1474   * Different browsers support different nocache headers, so several
1475   * headers must be sent so that all of them get the point that no
1476   * caching should occur.
1477   *
1478   * @since 2.0.0
1479   *
1480   * @see wp_get_nocache_headers()
1481   */
1482  function nocache_headers() {
1483      if ( headers_sent() ) {
1484          return;
1485      }
1486  
1487      $headers = wp_get_nocache_headers();
1488  
1489      unset( $headers['Last-Modified'] );
1490  
1491      header_remove( 'Last-Modified' );
1492  
1493      foreach ( $headers as $name => $field_value ) {
1494          header( "{$name}: {$field_value}" );
1495      }
1496  }
1497  
1498  /**
1499   * Set the headers for caching for 10 days with JavaScript content type.
1500   *
1501   * @since 2.1.0
1502   */
1503  function cache_javascript_headers() {
1504      $expiresOffset = 10 * DAY_IN_SECONDS;
1505  
1506      header( 'Content-Type: text/javascript; charset=' . get_bloginfo( 'charset' ) );
1507      header( 'Vary: Accept-Encoding' ); // Handle proxies.
1508      header( 'Expires: ' . gmdate( 'D, d M Y H:i:s', time() + $expiresOffset ) . ' GMT' );
1509  }
1510  
1511  /**
1512   * Retrieve the number of database queries during the WordPress execution.
1513   *
1514   * @since 2.0.0
1515   *
1516   * @global wpdb $wpdb WordPress database abstraction object.
1517   *
1518   * @return int Number of database queries.
1519   */
1520  function get_num_queries() {
1521      global $wpdb;
1522      return $wpdb->num_queries;
1523  }
1524  
1525  /**
1526   * Whether input is yes or no.
1527   *
1528   * Must be 'y' to be true.
1529   *
1530   * @since 1.0.0
1531   *
1532   * @param string $yn Character string containing either 'y' (yes) or 'n' (no).
1533   * @return bool True if yes, false on anything else.
1534   */
1535  function bool_from_yn( $yn ) {
1536      return ( 'y' === strtolower( $yn ) );
1537  }
1538  
1539  /**
1540   * Load the feed template from the use of an action hook.
1541   *
1542   * If the feed action does not have a hook, then the function will die with a
1543   * message telling the visitor that the feed is not valid.
1544   *
1545   * It is better to only have one hook for each feed.
1546   *
1547   * @since 2.1.0
1548   *
1549   * @global WP_Query $wp_query WordPress Query object.
1550   */
1551  function do_feed() {
1552      global $wp_query;
1553  
1554      $feed = get_query_var( 'feed' );
1555  
1556      // Remove the pad, if present.
1557      $feed = preg_replace( '/^_+/', '', $feed );
1558  
1559      if ( '' === $feed || 'feed' === $feed ) {
1560          $feed = get_default_feed();
1561      }
1562  
1563      if ( ! has_action( "do_feed_{$feed}" ) ) {
1564          wp_die( __( 'Error: This is not a valid feed template.' ), '', array( 'response' => 404 ) );
1565      }
1566  
1567      /**
1568       * Fires once the given feed is loaded.
1569       *
1570       * The dynamic portion of the hook name, `$feed`, refers to the feed template name.
1571       *
1572       * Possible hook names include:
1573       *
1574       *  - `do_feed_atom`
1575       *  - `do_feed_rdf`
1576       *  - `do_feed_rss`
1577       *  - `do_feed_rss2`
1578       *
1579       * @since 2.1.0
1580       * @since 4.4.0 The `$feed` parameter was added.
1581       *
1582       * @param bool   $is_comment_feed Whether the feed is a comment feed.
1583       * @param string $feed            The feed name.
1584       */
1585      do_action( "do_feed_{$feed}", $wp_query->is_comment_feed, $feed );
1586  }
1587  
1588  /**
1589   * Load the RDF RSS 0.91 Feed template.
1590   *
1591   * @since 2.1.0
1592   *
1593   * @see load_template()
1594   */
1595  function do_feed_rdf() {
1596      load_template( ABSPATH . WPINC . '/feed-rdf.php' );
1597  }
1598  
1599  /**
1600   * Load the RSS 1.0 Feed Template.
1601   *
1602   * @since 2.1.0
1603   *
1604   * @see load_template()
1605   */
1606  function do_feed_rss() {
1607      load_template( ABSPATH . WPINC . '/feed-rss.php' );
1608  }
1609  
1610  /**
1611   * Load either the RSS2 comment feed or the RSS2 posts feed.
1612   *
1613   * @since 2.1.0
1614   *
1615   * @see load_template()
1616   *
1617   * @param bool $for_comments True for the comment feed, false for normal feed.
1618   */
1619  function do_feed_rss2( $for_comments ) {
1620      if ( $for_comments ) {
1621          load_template( ABSPATH . WPINC . '/feed-rss2-comments.php' );
1622      } else {
1623          load_template( ABSPATH . WPINC . '/feed-rss2.php' );
1624      }
1625  }
1626  
1627  /**
1628   * Load either Atom comment feed or Atom posts feed.
1629   *
1630   * @since 2.1.0
1631   *
1632   * @see load_template()
1633   *
1634   * @param bool $for_comments True for the comment feed, false for normal feed.
1635   */
1636  function do_feed_atom( $for_comments ) {
1637      if ( $for_comments ) {
1638          load_template( ABSPATH . WPINC . '/feed-atom-comments.php' );
1639      } else {
1640          load_template( ABSPATH . WPINC . '/feed-atom.php' );
1641      }
1642  }
1643  
1644  /**
1645   * Displays the default robots.txt file content.
1646   *
1647   * @since 2.1.0
1648   * @since 5.3.0 Remove the "Disallow: /" output if search engine visiblity is
1649   *              discouraged in favor of robots meta HTML tag via wp_robots_no_robots()
1650   *              filter callback.
1651   */
1652  function do_robots() {
1653      header( 'Content-Type: text/plain; charset=utf-8' );
1654  
1655      /**
1656       * Fires when displaying the robots.txt file.
1657       *
1658       * @since 2.1.0
1659       */
1660      do_action( 'do_robotstxt' );
1661  
1662      $output = "User-agent: *\n";
1663      $public = get_option( 'blog_public' );
1664  
1665      $site_url = parse_url( site_url() );
1666      $path     = ( ! empty( $site_url['path'] ) ) ? $site_url['path'] : '';
1667      $output  .= "Disallow: $path/wp-admin/\n";
1668      $output  .= "Allow: $path/wp-admin/admin-ajax.php\n";
1669  
1670      /**
1671       * Filters the robots.txt output.
1672       *
1673       * @since 3.0.0
1674       *
1675       * @param string $output The robots.txt output.
1676       * @param bool   $public Whether the site is considered "public".
1677       */
1678      echo apply_filters( 'robots_txt', $output, $public );
1679  }
1680  
1681  /**
1682   * Display the favicon.ico file content.
1683   *
1684   * @since 5.4.0
1685   */
1686  function do_favicon() {
1687      /**
1688       * Fires when serving the favicon.ico file.
1689       *
1690       * @since 5.4.0
1691       */
1692      do_action( 'do_faviconico' );
1693  
1694      wp_redirect( get_site_icon_url( 32, includes_url( 'images/w-logo-blue-white-bg.png' ) ) );
1695      exit;
1696  }
1697  
1698  /**
1699   * Determines whether WordPress is already installed.
1700   *
1701   * The cache will be checked first. If you have a cache plugin, which saves
1702   * the cache values, then this will work. If you use the default WordPress
1703   * cache, and the database goes away, then you might have problems.
1704   *
1705   * Checks for the 'siteurl' option for whether WordPress is installed.
1706   *
1707   * For more information on this and similar theme functions, check out
1708   * the {@link https://developer.wordpress.org/themes/basics/conditional-tags/
1709   * Conditional Tags} article in the Theme Developer Handbook.
1710   *
1711   * @since 2.1.0
1712   *
1713   * @global wpdb $wpdb WordPress database abstraction object.
1714   *
1715   * @return bool Whether the site is already installed.
1716   */
1717  function is_blog_installed() {
1718      global $wpdb;
1719  
1720      /*
1721       * Check cache first. If options table goes away and we have true
1722       * cached, oh well.
1723       */
1724      if ( wp_cache_get( 'is_blog_installed' ) ) {
1725          return true;
1726      }
1727  
1728      $suppress = $wpdb->suppress_errors();
1729      if ( ! wp_installing() ) {
1730          $alloptions = wp_load_alloptions();
1731      }
1732      // If siteurl is not set to autoload, check it specifically.
1733      if ( ! isset( $alloptions['siteurl'] ) ) {
1734          $installed = $wpdb->get_var( "SELECT option_value FROM $wpdb->options WHERE option_name = 'siteurl'" );
1735      } else {
1736          $installed = $alloptions['siteurl'];
1737      }
1738      $wpdb->suppress_errors( $suppress );
1739  
1740      $installed = ! empty( $installed );
1741      wp_cache_set( 'is_blog_installed', $installed );
1742  
1743      if ( $installed ) {
1744          return true;
1745      }
1746  
1747      // If visiting repair.php, return true and let it take over.
1748      if ( defined( 'WP_REPAIRING' ) ) {
1749          return true;
1750      }
1751  
1752      $suppress = $wpdb->suppress_errors();
1753  
1754      /*
1755       * Loop over the WP tables. If none exist, then scratch installation is allowed.
1756       * If one or more exist, suggest table repair since we got here because the
1757       * options table could not be accessed.
1758       */
1759      $wp_tables = $wpdb->tables();
1760      foreach ( $wp_tables as $table ) {
1761          // The existence of custom user tables shouldn't suggest an unwise state or prevent a clean installation.
1762          if ( defined( 'CUSTOM_USER_TABLE' ) && CUSTOM_USER_TABLE == $table ) {
1763              continue;
1764          }
1765          if ( defined( 'CUSTOM_USER_META_TABLE' ) && CUSTOM_USER_META_TABLE == $table ) {
1766              continue;
1767          }
1768  
1769          $described_table = $wpdb->get_results( "DESCRIBE $table;" );
1770          if (
1771              ( ! $described_table && empty( $wpdb->last_error ) ) ||
1772              ( is_array( $described_table ) && 0 === count( $described_table ) )
1773          ) {
1774              continue;
1775          }
1776  
1777          // One or more tables exist. This is not good.
1778  
1779          wp_load_translations_early();
1780  
1781          // Die with a DB error.
1782          $wpdb->error = sprintf(
1783              /* translators: %s: Database repair URL. */
1784              __( 'One or more database tables are unavailable. The database may need to be <a href="%s">repaired</a>.' ),
1785              'maint/repair.php?referrer=is_blog_installed'
1786          );
1787  
1788          dead_db();
1789      }
1790  
1791      $wpdb->suppress_errors( $suppress );
1792  
1793      wp_cache_set( 'is_blog_installed', false );
1794  
1795      return false;
1796  }
1797  
1798  /**
1799   * Retrieve URL with nonce added to URL query.
1800   *
1801   * @since 2.0.4
1802   *
1803   * @param string     $actionurl URL to add nonce action.
1804   * @param int|string $action    Optional. Nonce action name. Default -1.
1805   * @param string     $name      Optional. Nonce name. Default '_wpnonce'.
1806   * @return string Escaped URL with nonce action added.
1807   */
1808  function wp_nonce_url( $actionurl, $action = -1, $name = '_wpnonce' ) {
1809      $actionurl = str_replace( '&amp;', '&', $actionurl );
1810      return esc_html( add_query_arg( $name, wp_create_nonce( $action ), $actionurl ) );
1811  }
1812  
1813  /**
1814   * Retrieve or display nonce hidden field for forms.
1815   *
1816   * The nonce field is used to validate that the contents of the form came from
1817   * the location on the current site and not somewhere else. The nonce does not
1818   * offer absolute protection, but should protect against most cases. It is very
1819   * important to use nonce field in forms.
1820   *
1821   * The $action and $name are optional, but if you want to have better security,
1822   * it is strongly suggested to set those two parameters. It is easier to just
1823   * call the function without any parameters, because validation of the nonce
1824   * doesn't require any parameters, but since crackers know what the default is
1825   * it won't be difficult for them to find a way around your nonce and cause
1826   * damage.
1827   *
1828   * The input name will be whatever $name value you gave. The input value will be
1829   * the nonce creation value.
1830   *
1831   * @since 2.0.4
1832   *
1833   * @param int|string $action  Optional. Action name. Default -1.
1834   * @param string     $name    Optional. Nonce name. Default '_wpnonce'.
1835   * @param bool       $referer Optional. Whether to set the referer field for validation. Default true.
1836   * @param bool       $echo    Optional. Whether to display or return hidden form field. Default true.
1837   * @return string Nonce field HTML markup.
1838   */
1839  function wp_nonce_field( $action = -1, $name = '_wpnonce', $referer = true, $echo = true ) {
1840      $name        = esc_attr( $name );
1841      $nonce_field = '<input type="hidden" id="' . $name . '" name="' . $name . '" value="' . wp_create_nonce( $action ) . '" />';
1842  
1843      if ( $referer ) {
1844          $nonce_field .= wp_referer_field( false );
1845      }
1846  
1847      if ( $echo ) {
1848          echo $nonce_field;
1849      }
1850  
1851      return $nonce_field;
1852  }
1853  
1854  /**
1855   * Retrieve or display referer hidden field for forms.
1856   *
1857   * The referer link is the current Request URI from the server super global. The
1858   * input name is '_wp_http_referer', in case you wanted to check manually.
1859   *
1860   * @since 2.0.4
1861   *
1862   * @param bool $echo Optional. Whether to echo or return the referer field. Default true.
1863   * @return string Referer field HTML markup.
1864   */
1865  function wp_referer_field( $echo = true ) {
1866      $referer_field = '<input type="hidden" name="_wp_http_referer" value="' . esc_attr( wp_unslash( $_SERVER['REQUEST_URI'] ) ) . '" />';
1867  
1868      if ( $echo ) {
1869          echo $referer_field;
1870      }
1871  
1872      return $referer_field;
1873  }
1874  
1875  /**
1876   * Retrieve or display original referer hidden field for forms.
1877   *
1878   * The input name is '_wp_original_http_referer' and will be either the same
1879   * value of wp_referer_field(), if that was posted already or it will be the
1880   * current page, if it doesn't exist.
1881   *
1882   * @since 2.0.4
1883   *
1884   * @param bool   $echo         Optional. Whether to echo the original http referer. Default true.
1885   * @param string $jump_back_to Optional. Can be 'previous' or page you want to jump back to.
1886   *                             Default 'current'.
1887   * @return string Original referer field.
1888   */
1889  function wp_original_referer_field( $echo = true, $jump_back_to = 'current' ) {
1890      $ref = wp_get_original_referer();
1891  
1892      if ( ! $ref ) {
1893          $ref = ( 'previous' === $jump_back_to ) ? wp_get_referer() : wp_unslash( $_SERVER['REQUEST_URI'] );
1894      }
1895  
1896      $orig_referer_field = '<input type="hidden" name="_wp_original_http_referer" value="' . esc_attr( $ref ) . '" />';
1897  
1898      if ( $echo ) {
1899          echo $orig_referer_field;
1900      }
1901  
1902      return $orig_referer_field;
1903  }
1904  
1905  /**
1906   * Retrieve referer from '_wp_http_referer' or HTTP referer.
1907   *
1908   * If it's the same as the current request URL, will return false.
1909   *
1910   * @since 2.0.4
1911   *
1912   * @return string|false Referer URL on success, false on failure.
1913   */
1914  function wp_get_referer() {
1915      if ( ! function_exists( 'wp_validate_redirect' ) ) {
1916          return false;
1917      }
1918  
1919      $ref = wp_get_raw_referer();
1920  
1921      if ( $ref && wp_unslash( $_SERVER['REQUEST_URI'] ) !== $ref && home_url() . wp_unslash( $_SERVER['REQUEST_URI'] ) !== $ref ) {
1922          return wp_validate_redirect( $ref, false );
1923      }
1924  
1925      return false;
1926  }
1927  
1928  /**
1929   * Retrieves unvalidated referer from '_wp_http_referer' or HTTP referer.
1930   *
1931   * Do not use for redirects, use wp_get_referer() instead.
1932   *
1933   * @since 4.5.0
1934   *
1935   * @return string|false Referer URL on success, false on failure.
1936   */
1937  function wp_get_raw_referer() {
1938      if ( ! empty( $_REQUEST['_wp_http_referer'] ) ) {
1939          return wp_unslash( $_REQUEST['_wp_http_referer'] );
1940      } elseif ( ! empty( $_SERVER['HTTP_REFERER'] ) ) {
1941          return wp_unslash( $_SERVER['HTTP_REFERER'] );
1942      }
1943  
1944      return false;
1945  }
1946  
1947  /**
1948   * Retrieve original referer that was posted, if it exists.
1949   *
1950   * @since 2.0.4
1951   *
1952   * @return string|false Original referer URL on success, false on failure.
1953   */
1954  function wp_get_original_referer() {
1955      if ( ! empty( $_REQUEST['_wp_original_http_referer'] ) && function_exists( 'wp_validate_redirect' ) ) {
1956          return wp_validate_redirect( wp_unslash( $_REQUEST['_wp_original_http_referer'] ), false );
1957      }
1958  
1959      return false;
1960  }
1961  
1962  /**
1963   * Recursive directory creation based on full path.
1964   *
1965   * Will attempt to set permissions on folders.
1966   *
1967   * @since 2.0.1
1968   *
1969   * @param string $target Full path to attempt to create.
1970   * @return bool Whether the path was created. True if path already exists.
1971   */
1972  function wp_mkdir_p( $target ) {
1973      $wrapper = null;
1974  
1975      // Strip the protocol.
1976      if ( wp_is_stream( $target ) ) {
1977          list( $wrapper, $target ) = explode( '://', $target, 2 );
1978      }
1979  
1980      // From php.net/mkdir user contributed notes.
1981      $target = str_replace( '//', '/', $target );
1982  
1983      // Put the wrapper back on the target.
1984      if ( null !== $wrapper ) {
1985          $target = $wrapper . '://' . $target;
1986      }
1987  
1988      /*
1989       * Safe mode fails with a trailing slash under certain PHP versions.
1990       * Use rtrim() instead of untrailingslashit to avoid formatting.php dependency.
1991       */
1992      $target = rtrim( $target, '/' );
1993      if ( empty( $target ) ) {
1994          $target = '/';
1995      }
1996  
1997      if ( file_exists( $target ) ) {
1998          return @is_dir( $target );
1999      }
2000  
2001      // Do not allow path traversals.
2002      if ( false !== strpos( $target, '../' ) || false !== strpos( $target, '..' . DIRECTORY_SEPARATOR ) ) {
2003          return false;
2004      }
2005  
2006      // We need to find the permissions of the parent folder that exists and inherit that.
2007      $target_parent = dirname( $target );
2008      while ( '.' !== $target_parent && ! is_dir( $target_parent ) && dirname( $target_parent ) !== $target_parent ) {
2009          $target_parent = dirname( $target_parent );
2010      }
2011  
2012      // Get the permission bits.
2013      $stat = @stat( $target_parent );
2014      if ( $stat ) {
2015          $dir_perms = $stat['mode'] & 0007777;
2016      } else {
2017          $dir_perms = 0777;
2018      }
2019  
2020      if ( @mkdir( $target, $dir_perms, true ) ) {
2021  
2022          /*
2023           * If a umask is set that modifies $dir_perms, we'll have to re-set
2024           * the $dir_perms correctly with chmod()
2025           */
2026          if ( ( $dir_perms & ~umask() ) != $dir_perms ) {
2027              $folder_parts = explode( '/', substr( $target, strlen( $target_parent ) + 1 ) );
2028              for ( $i = 1, $c = count( $folder_parts ); $i <= $c; $i++ ) {
2029                  chmod( $target_parent . '/' . implode( '/', array_slice( $folder_parts, 0, $i ) ), $dir_perms );
2030              }
2031          }
2032  
2033          return true;
2034      }
2035  
2036      return false;
2037  }
2038  
2039  /**
2040   * Test if a given filesystem path is absolute.
2041   *
2042   * For example, '/foo/bar', or 'c:\windows'.
2043   *
2044   * @since 2.5.0
2045   *
2046   * @param string $path File path.
2047   * @return bool True if path is absolute, false is not absolute.
2048   */
2049  function path_is_absolute( $path ) {
2050      /*
2051       * Check to see if the path is a stream and check to see if its an actual
2052       * path or file as realpath() does not support stream wrappers.
2053       */
2054      if ( wp_is_stream( $path ) && ( is_dir( $path ) || is_file( $path ) ) ) {
2055          return true;
2056      }
2057  
2058      /*
2059       * This is definitive if true but fails if $path does not exist or contains
2060       * a symbolic link.
2061       */
2062      if ( realpath( $path ) == $path ) {
2063          return true;
2064      }
2065  
2066      if ( strlen( $path ) == 0 || '.' === $path[0] ) {
2067          return false;
2068      }
2069  
2070      // Windows allows absolute paths like this.
2071      if ( preg_match( '#^[a-zA-Z]:\\\\#', $path ) ) {
2072          return true;
2073      }
2074  
2075      // A path starting with / or \ is absolute; anything else is relative.
2076      return ( '/' === $path[0] || '\\' === $path[0] );
2077  }
2078  
2079  /**
2080   * Join two filesystem paths together.
2081   *
2082   * For example, 'give me $path relative to $base'. If the $path is absolute,
2083   * then it the full path is returned.
2084   *
2085   * @since 2.5.0
2086   *
2087   * @param string $base Base path.
2088   * @param string $path Path relative to $base.
2089   * @return string The path with the base or absolute path.
2090   */
2091  function path_join( $base, $path ) {
2092      if ( path_is_absolute( $path ) ) {
2093          return $path;
2094      }
2095  
2096      return rtrim( $base, '/' ) . '/' . ltrim( $path, '/' );
2097  }
2098  
2099  /**
2100   * Normalize a filesystem path.
2101   *
2102   * On windows systems, replaces backslashes with forward slashes
2103   * and forces upper-case drive letters.
2104   * Allows for two leading slashes for Windows network shares, but
2105   * ensures that all other duplicate slashes are reduced to a single.
2106   *
2107   * @since 3.9.0
2108   * @since 4.4.0 Ensures upper-case drive letters on Windows systems.
2109   * @since 4.5.0 Allows for Windows network shares.
2110   * @since 4.9.7 Allows for PHP file wrappers.
2111   *
2112   * @param string $path Path to normalize.
2113   * @return string Normalized path.
2114   */
2115  function wp_normalize_path( $path ) {
2116      $wrapper = '';
2117  
2118      if ( wp_is_stream( $path ) ) {
2119          list( $wrapper, $path ) = explode( '://', $path, 2 );
2120  
2121          $wrapper .= '://';
2122      }
2123  
2124      // Standardise all paths to use '/'.
2125      $path = str_replace( '\\', '/', $path );
2126  
2127      // Replace multiple slashes down to a singular, allowing for network shares having two slashes.
2128      $path = preg_replace( '|(?<=.)/+|', '/', $path );
2129  
2130      // Windows paths should uppercase the drive letter.
2131      if ( ':' === substr( $path, 1, 1 ) ) {
2132          $path = ucfirst( $path );
2133      }
2134  
2135      return $wrapper . $path;
2136  }
2137  
2138  /**
2139   * Determine a writable directory for temporary files.
2140   *
2141   * Function's preference is the return value of sys_get_temp_dir(),
2142   * followed by your PHP temporary upload directory, followed by WP_CONTENT_DIR,
2143   * before finally defaulting to /tmp/
2144   *
2145   * In the event that this function does not find a writable location,
2146   * It may be overridden by the WP_TEMP_DIR constant in your wp-config.php file.
2147   *
2148   * @since 2.5.0
2149   *
2150   * @return string Writable temporary directory.
2151   */
2152  function get_temp_dir() {
2153      static $temp = '';
2154      if ( defined( 'WP_TEMP_DIR' ) ) {
2155          return trailingslashit( WP_TEMP_DIR );
2156      }
2157  
2158      if ( $temp ) {
2159          return trailingslashit( $temp );
2160      }
2161  
2162      if ( function_exists( 'sys_get_temp_dir' ) ) {
2163          $temp = sys_get_temp_dir();
2164          if ( @is_dir( $temp ) && wp_is_writable( $temp ) ) {
2165              return trailingslashit( $temp );
2166          }
2167      }
2168  
2169      $temp = ini_get( 'upload_tmp_dir' );
2170      if ( @is_dir( $temp ) && wp_is_writable( $temp ) ) {
2171          return trailingslashit( $temp );
2172      }
2173  
2174      $temp = WP_CONTENT_DIR . '/';
2175      if ( is_dir( $temp ) && wp_is_writable( $temp ) ) {
2176          return $temp;
2177      }
2178  
2179      return '/tmp/';
2180  }
2181  
2182  /**
2183   * Determine if a directory is writable.
2184   *
2185   * This function is used to work around certain ACL issues in PHP primarily
2186   * affecting Windows Servers.
2187   *
2188   * @since 3.6.0
2189   *
2190   * @see win_is_writable()
2191   *
2192   * @param string $path Path to check for write-ability.
2193   * @return bool Whether the path is writable.
2194   */
2195  function wp_is_writable( $path ) {
2196      if ( 'WIN' === strtoupper( substr( PHP_OS, 0, 3 ) ) ) {
2197          return win_is_writable( $path );
2198      } else {
2199          return @is_writable( $path );
2200      }
2201  }
2202  
2203  /**
2204   * Workaround for Windows bug in is_writable() function
2205   *
2206   * PHP has issues with Windows ACL's for determine if a
2207   * directory is writable or not, this works around them by
2208   * checking the ability to open files rather than relying
2209   * upon PHP to interprate the OS ACL.
2210   *
2211   * @since 2.8.0
2212   *
2213   * @see https://bugs.php.net/bug.php?id=27609
2214   * @see https://bugs.php.net/bug.php?id=30931
2215   *
2216   * @param string $path Windows path to check for write-ability.
2217   * @return bool Whether the path is writable.
2218   */
2219  function win_is_writable( $path ) {
2220      if ( '/' === $path[ strlen( $path ) - 1 ] ) {
2221          // If it looks like a directory, check a random file within the directory.
2222          return win_is_writable( $path . uniqid( mt_rand() ) . '.tmp' );
2223      } elseif ( is_dir( $path ) ) {
2224          // If it's a directory (and not a file), check a random file within the directory.
2225          return win_is_writable( $path . '/' . uniqid( mt_rand() ) . '.tmp' );
2226      }
2227  
2228      // Check tmp file for read/write capabilities.
2229      $should_delete_tmp_file = ! file_exists( $path );
2230  
2231      $f = @fopen( $path, 'a' );
2232      if ( false === $f ) {
2233          return false;
2234      }
2235      fclose( $f );
2236  
2237      if ( $should_delete_tmp_file ) {
2238          unlink( $path );
2239      }
2240  
2241      return true;
2242  }
2243  
2244  /**
2245   * Retrieves uploads directory information.
2246   *
2247   * Same as wp_upload_dir() but "light weight" as it doesn't attempt to create the uploads directory.
2248   * Intended for use in themes, when only 'basedir' and 'baseurl' are needed, generally in all cases
2249   * when not uploading files.
2250   *
2251   * @since 4.5.0
2252   *
2253   * @see wp_upload_dir()
2254   *
2255   * @return array See wp_upload_dir() for description.
2256   */
2257  function wp_get_upload_dir() {
2258      return wp_upload_dir( null, false );
2259  }
2260  
2261  /**
2262   * Returns an array containing the current upload directory's path and URL.
2263   *
2264   * Checks the 'upload_path' option, which should be from the web root folder,
2265   * and if it isn't empty it will be used. If it is empty, then the path will be
2266   * 'WP_CONTENT_DIR/uploads'. If the 'UPLOADS' constant is defined, then it will
2267   * override the 'upload_path' option and 'WP_CONTENT_DIR/uploads' path.
2268   *
2269   * The upload URL path is set either by the 'upload_url_path' option or by using
2270   * the 'WP_CONTENT_URL' constant and appending '/uploads' to the path.
2271   *
2272   * If the 'uploads_use_yearmonth_folders' is set to true (checkbox if checked in
2273   * the administration settings panel), then the time will be used. The format
2274   * will be year first and then month.
2275   *
2276   * If the path couldn't be created, then an error will be returned with the key
2277   * 'error' containing the error message. The error suggests that the parent
2278   * directory is not writable by the server.
2279   *
2280   * @since 2.0.0
2281   * @uses _wp_upload_dir()
2282   *
2283   * @param string $time Optional. Time formatted in 'yyyy/mm'. Default null.
2284   * @param bool   $create_dir Optional. Whether to check and create the uploads directory.
2285   *                           Default true for backward compatibility.
2286   * @param bool   $refresh_cache Optional. Whether to refresh the cache. Default false.
2287   * @return array {
2288   *     Array of information about the upload directory.
2289   *
2290   *     @type string       $path    Base directory and subdirectory or full path to upload directory.
2291   *     @type string       $url     Base URL and subdirectory or absolute URL to upload directory.
2292   *     @type string       $subdir  Subdirectory if uploads use year/month folders option is on.
2293   *     @type string       $basedir Path without subdir.
2294   *     @type string       $baseurl URL path without subdir.
2295   *     @type string|false $error   False or error message.
2296   * }
2297   */
2298  function wp_upload_dir( $time = null, $create_dir = true, $refresh_cache = false ) {
2299      static $cache = array(), $tested_paths = array();
2300  
2301      $key = sprintf( '%d-%s', get_current_blog_id(), (string) $time );
2302  
2303      if ( $refresh_cache || empty( $cache[ $key ] ) ) {
2304          $cache[ $key ] = _wp_upload_dir( $time );
2305      }
2306  
2307      /**
2308       * Filters the uploads directory data.
2309       *
2310       * @since 2.0.0
2311       *
2312       * @param array $uploads {
2313       *     Array of information about the upload directory.
2314       *
2315       *     @type string       $path    Base directory and subdirectory or full path to upload directory.
2316       *     @type string       $url     Base URL and subdirectory or absolute URL to upload directory.
2317       *     @type string       $subdir  Subdirectory if uploads use year/month folders option is on.
2318       *     @type string       $basedir Path without subdir.
2319       *     @type string       $baseurl URL path without subdir.
2320       *     @type string|false $error   False or error message.
2321       * }
2322       */
2323      $uploads = apply_filters( 'upload_dir', $cache[ $key ] );
2324  
2325      if ( $create_dir ) {
2326          $path = $uploads['path'];
2327  
2328          if ( array_key_exists( $path, $tested_paths ) ) {
2329              $uploads['error'] = $tested_paths[ $path ];
2330          } else {
2331              if ( ! wp_mkdir_p( $path ) ) {
2332                  if ( 0 === strpos( $uploads['basedir'], ABSPATH ) ) {
2333                      $error_path = str_replace( ABSPATH, '', $uploads['basedir'] ) . $uploads['subdir'];
2334                  } else {
2335                      $error_path = wp_basename( $uploads['basedir'] ) . $uploads['subdir'];
2336                  }
2337  
2338                  $uploads['error'] = sprintf(
2339                      /* translators: %s: Directory path. */
2340                      __( 'Unable to create directory %s. Is its parent directory writable by the server?' ),
2341                      esc_html( $error_path )
2342                  );
2343              }
2344  
2345              $tested_paths[ $path ] = $uploads['error'];
2346          }
2347      }
2348  
2349      return $uploads;
2350  }
2351  
2352  /**
2353   * A non-filtered, non-cached version of wp_upload_dir() that doesn't check the path.
2354   *
2355   * @since 4.5.0
2356   * @access private
2357   *
2358   * @param string $time Optional. Time formatted in 'yyyy/mm'. Default null.
2359   * @return array See wp_upload_dir()
2360   */
2361  function _wp_upload_dir( $time = null ) {
2362      $siteurl     = get_option( 'siteurl' );
2363      $upload_path = trim( get_option( 'upload_path' ) );
2364  
2365      if ( empty( $upload_path ) || 'wp-content/uploads' === $upload_path ) {
2366          $dir = WP_CONTENT_DIR . '/uploads';
2367      } elseif ( 0 !== strpos( $upload_path, ABSPATH ) ) {
2368          // $dir is absolute, $upload_path is (maybe) relative to ABSPATH.
2369          $dir = path_join( ABSPATH, $upload_path );
2370      } else {
2371          $dir = $upload_path;
2372      }
2373  
2374      $url = get_option( 'upload_url_path' );
2375      if ( ! $url ) {
2376          if ( empty( $upload_path ) || ( 'wp-content/uploads' === $upload_path ) || ( $upload_path == $dir ) ) {
2377              $url = WP_CONTENT_URL . '/uploads';
2378          } else {
2379              $url = trailingslashit( $siteurl ) . $upload_path;
2380          }
2381      }
2382  
2383      /*
2384       * Honor the value of UPLOADS. This happens as long as ms-files rewriting is disabled.
2385       * We also sometimes obey UPLOADS when rewriting is enabled -- see the next block.
2386       */
2387      if ( defined( 'UPLOADS' ) && ! ( is_multisite() && get_site_option( 'ms_files_rewriting' ) ) ) {
2388          $dir = ABSPATH . UPLOADS;
2389          $url = trailingslashit( $siteurl ) . UPLOADS;
2390      }
2391  
2392      // If multisite (and if not the main site in a post-MU network).
2393      if ( is_multisite() && ! ( is_main_network() && is_main_site() && defined( 'MULTISITE' ) ) ) {
2394  
2395          if ( ! get_site_option( 'ms_files_rewriting' ) ) {
2396              /*
2397               * If ms-files rewriting is disabled (networks created post-3.5), it is fairly
2398               * straightforward: Append sites/%d if we're not on the main site (for post-MU
2399               * networks). (The extra directory prevents a four-digit ID from conflicting with
2400               * a year-based directory for the main site. But if a MU-era network has disabled
2401               * ms-files rewriting manually, they don't need the extra directory, as they never
2402               * had wp-content/uploads for the main site.)
2403               */
2404  
2405              if ( defined( 'MULTISITE' ) ) {
2406                  $ms_dir = '/sites/' . get_current_blog_id();
2407              } else {
2408                  $ms_dir = '/' . get_current_blog_id();
2409              }
2410  
2411              $dir .= $ms_dir;
2412              $url .= $ms_dir;
2413  
2414          } elseif ( defined( 'UPLOADS' ) && ! ms_is_switched() ) {
2415              /*
2416               * Handle the old-form ms-files.php rewriting if the network still has that enabled.
2417               * When ms-files rewriting is enabled, then we only listen to UPLOADS when:
2418               * 1) We are not on the main site in a post-MU network, as wp-content/uploads is used
2419               *    there, and
2420               * 2) We are not switched, as ms_upload_constants() hardcodes these constants to reflect
2421               *    the original blog ID.
2422               *
2423               * Rather than UPLOADS, we actually use BLOGUPLOADDIR if it is set, as it is absolute.
2424               * (And it will be set, see ms_upload_constants().) Otherwise, UPLOADS can be used, as
2425               * as it is relative to ABSPATH. For the final piece: when UPLOADS is used with ms-files
2426               * rewriting in multisite, the resulting URL is /files. (#WP22702 for background.)
2427               */
2428  
2429              if ( defined( 'BLOGUPLOADDIR' ) ) {
2430                  $dir = untrailingslashit( BLOGUPLOADDIR );
2431              } else {
2432                  $dir = ABSPATH . UPLOADS;
2433              }
2434              $url = trailingslashit( $siteurl ) . 'files';
2435          }
2436      }
2437  
2438      $basedir = $dir;
2439      $baseurl = $url;
2440  
2441      $subdir = '';
2442      if ( get_option( 'uploads_use_yearmonth_folders' ) ) {
2443          // Generate the yearly and monthly directories.
2444          if ( ! $time ) {
2445              $time = current_time( 'mysql' );
2446          }
2447          $y      = substr( $time, 0, 4 );
2448          $m      = substr( $time, 5, 2 );
2449          $subdir = "/$y/$m";
2450      }
2451  
2452      $dir .= $subdir;
2453      $url .= $subdir;
2454  
2455      return array(
2456          'path'    => $dir,
2457          'url'     => $url,
2458          'subdir'  => $subdir,
2459          'basedir' => $basedir,
2460          'baseurl' => $baseurl,
2461          'error'   => false,
2462      );
2463  }
2464  
2465  /**
2466   * Get a filename that is sanitized and unique for the given directory.
2467   *
2468   * If the filename is not unique, then a number will be added to the filename
2469   * before the extension, and will continue adding numbers until the filename
2470   * is unique.
2471   *
2472   * The callback function allows the caller to use their own method to create
2473   * unique file names. If defined, the callback should take three arguments:
2474   * - directory, base filename, and extension - and return a unique filename.
2475   *
2476   * @since 2.5.0
2477   *
2478   * @param string   $dir                      Directory.
2479   * @param string   $filename                 File name.
2480   * @param callable $unique_filename_callback Callback. Default null.
2481   * @return string New filename, if given wasn't unique.
2482   */
2483  function wp_unique_filename( $dir, $filename, $unique_filename_callback = null ) {
2484      // Sanitize the file name before we begin processing.
2485      $filename = sanitize_file_name( $filename );
2486      $ext2     = null;
2487  
2488      // Separate the filename into a name and extension.
2489      $ext  = pathinfo( $filename, PATHINFO_EXTENSION );
2490      $name = pathinfo( $filename, PATHINFO_BASENAME );
2491  
2492      if ( $ext ) {
2493          $ext = '.' . $ext;
2494      }
2495  
2496      // Edge case: if file is named '.ext', treat as an empty name.
2497      if ( $name === $ext ) {
2498          $name = '';
2499      }
2500  
2501      /*
2502       * Increment the file number until we have a unique file to save in $dir.
2503       * Use callback if supplied.
2504       */
2505      if ( $unique_filename_callback && is_callable( $unique_filename_callback ) ) {
2506          $filename = call_user_func( $unique_filename_callback, $dir, $name, $ext );
2507      } else {
2508          $number = '';
2509          $fname  = pathinfo( $filename, PATHINFO_FILENAME );
2510  
2511          // Always append a number to file names that can potentially match image sub-size file names.
2512          if ( $fname && preg_match( '/-(?:\d+x\d+|scaled|rotated)$/', $fname ) ) {
2513              $number = 1;
2514  
2515              // At this point the file name may not be unique. This is tested below and the $number is incremented.
2516              $filename = str_replace( "{$fname}{$ext}", "{$fname}-{$number}{$ext}", $filename );
2517          }
2518  
2519          // Change '.ext' to lower case.
2520          if ( $ext && strtolower( $ext ) != $ext ) {
2521              $ext2      = strtolower( $ext );
2522              $filename2 = preg_replace( '|' . preg_quote( $ext ) . '$|', $ext2, $filename );
2523  
2524              // Check for both lower and upper case extension or image sub-sizes may be overwritten.
2525              while ( file_exists( $dir . "/{$filename}" ) || file_exists( $dir . "/{$filename2}" ) ) {
2526                  $new_number = (int) $number + 1;
2527                  $filename   = str_replace( array( "-{$number}{$ext}", "{$number}{$ext}" ), "-{$new_number}{$ext}", $filename );
2528                  $filename2  = str_replace( array( "-{$number}{$ext2}", "{$number}{$ext2}" ), "-{$new_number}{$ext2}", $filename2 );
2529                  $number     = $new_number;
2530              }
2531  
2532              $filename = $filename2;
2533          } else {
2534              while ( file_exists( $dir . "/{$filename}" ) ) {
2535                  $new_number = (int) $number + 1;
2536  
2537                  if ( '' === "{$number}{$ext}" ) {
2538                      $filename = "{$filename}-{$new_number}";
2539                  } else {
2540                      $filename = str_replace( array( "-{$number}{$ext}", "{$number}{$ext}" ), "-{$new_number}{$ext}", $filename );
2541                  }
2542  
2543                  $number = $new_number;
2544              }
2545          }
2546  
2547          // Prevent collisions with existing file names that contain dimension-like strings
2548          // (whether they are subsizes or originals uploaded prior to #42437).
2549          $upload_dir = wp_get_upload_dir();
2550  
2551          // The (resized) image files would have name and extension, and will be in the uploads dir.
2552          if ( $name && $ext && @is_dir( $dir ) && false !== strpos( $dir, $upload_dir['basedir'] ) ) {
2553              /**
2554               * Filters the file list used for calculating a unique filename for a newly added file.
2555               *
2556               * Returning an array from the filter will effectively short-circuit retrieval
2557               * from the filesystem and return the passed value instead.
2558               *
2559               * @since 5.5.0
2560               *
2561               * @param array|null $files    The list of files to use for filename comparisons.
2562               *                             Default null (to retrieve the list from the filesystem).
2563               * @param string     $dir      The directory for the new file.
2564               * @param string     $filename The proposed filename for the new file.
2565               */
2566              $files = apply_filters( 'pre_wp_unique_filename_file_list', null, $dir, $filename );
2567  
2568              if ( null === $files ) {
2569                  // List of all files and directories contained in $dir.
2570                  $files = @scandir( $dir );
2571              }
2572  
2573              if ( ! empty( $files ) ) {
2574                  // Remove "dot" dirs.
2575                  $files = array_diff( $files, array( '.', '..' ) );
2576              }
2577  
2578              if ( ! empty( $files ) ) {
2579                  // The extension case may have changed above.
2580                  $new_ext = ! empty( $ext2 ) ? $ext2 : $ext;
2581  
2582                  // Ensure this never goes into infinite loop
2583                  // as it uses pathinfo() and regex in the check, but string replacement for the changes.
2584                  $count = count( $files );
2585                  $i     = 0;
2586  
2587                  while ( $i <= $count && _wp_check_existing_file_names( $filename, $files ) ) {
2588                      $new_number = (int) $number + 1;
2589                      $filename   = str_replace( array( "-{$number}{$new_ext}", "{$number}{$new_ext}" ), "-{$new_number}{$new_ext}", $filename );
2590                      $number     = $new_number;
2591                      $i++;
2592                  }
2593              }
2594          }
2595      }
2596  
2597      /**
2598       * Filters the result when generating a unique file name.
2599       *
2600       * @since 4.5.0
2601       *
2602       * @param string        $filename                 Unique file name.
2603       * @param string        $ext                      File extension, eg. ".png".
2604       * @param string        $dir                      Directory path.
2605       * @param callable|null $unique_filename_callback Callback function that generates the unique file name.
2606       */
2607      return apply_filters( 'wp_unique_filename', $filename, $ext, $dir, $unique_filename_callback );
2608  }
2609  
2610  /**
2611   * Helper function to check if a file name could match an existing image sub-size file name.
2612   *
2613   * @since 5.3.1
2614   * @access private
2615   *
2616   * @param string $filename The file name to check.
2617   * @param array  $files    An array of existing files in the directory.
2618   * @return bool True if the tested file name could match an existing file, false otherwise.
2619   */
2620  function _wp_check_existing_file_names( $filename, $files ) {
2621      $fname = pathinfo( $filename, PATHINFO_FILENAME );
2622      $ext   = pathinfo( $filename, PATHINFO_EXTENSION );
2623  
2624      // Edge case, file names like `.ext`.
2625      if ( empty( $fname ) ) {
2626          return false;
2627      }
2628  
2629      if ( $ext ) {
2630          $ext = ".$ext";
2631      }
2632  
2633      $regex = '/^' . preg_quote( $fname ) . '-(?:\d+x\d+|scaled|rotated)' . preg_quote( $ext ) . '$/i';
2634  
2635      foreach ( $files as $file ) {
2636          if ( preg_match( $regex, $file ) ) {
2637              return true;
2638          }
2639      }
2640  
2641      return false;
2642  }
2643  
2644  /**
2645   * Create a file in the upload folder with given content.
2646   *
2647   * If there is an error, then the key 'error' will exist with the error message.
2648   * If success, then the key 'file' will have the unique file path, the 'url' key
2649   * will have the link to the new file. and the 'error' key will be set to false.
2650   *
2651   * This function will not move an uploaded file to the upload folder. It will
2652   * create a new file with the content in $bits parameter. If you move the upload
2653   * file, read the content of the uploaded file, and then you can give the
2654   * filename and content to this function, which will add it to the upload
2655   * folder.
2656   *
2657   * The permissions will be set on the new file automatically by this function.
2658   *
2659   * @since 2.0.0
2660   *
2661   * @param string      $name       Filename.
2662   * @param null|string $deprecated Never used. Set to null.
2663   * @param string      $bits       File content
2664   * @param string      $time       Optional. Time formatted in 'yyyy/mm'. Default null.
2665   * @return array {
2666   *     Information about the newly-uploaded file.
2667   *
2668   *     @type string       $file  Filename of the newly-uploaded file.
2669   *     @type string       $url   URL of the uploaded file.
2670   *     @type string       $type  File type.
2671   *     @type string|false $error Error message, if there has been an error.
2672   * }
2673   */
2674  function wp_upload_bits( $name, $deprecated, $bits, $time = null ) {
2675      if ( ! empty( $deprecated ) ) {
2676          _deprecated_argument( __FUNCTION__, '2.0.0' );
2677      }
2678  
2679      if ( empty( $name ) ) {
2680          return array( 'error' => __( 'Empty filename' ) );
2681      }
2682  
2683      $wp_filetype = wp_check_filetype( $name );
2684      if ( ! $wp_filetype['ext'] && ! current_user_can( 'unfiltered_upload' ) ) {
2685          return array( 'error' => __( 'Sorry, this file type is not permitted for security reasons.' ) );
2686      }
2687  
2688      $upload = wp_upload_dir( $time );
2689  
2690      if ( false !== $upload['error'] ) {
2691          return $upload;
2692      }
2693  
2694      /**
2695       * Filters whether to treat the upload bits as an error.
2696       *
2697       * Returning a non-array from the filter will effectively short-circuit preparing the upload bits
2698       * and return that value instead. An error message should be returned as a string.
2699       *
2700       * @since 3.0.0
2701       *
2702       * @param array|string $upload_bits_error An array of upload bits data, or error message to return.
2703       */
2704      $upload_bits_error = apply_filters(
2705          'wp_upload_bits',
2706          array(
2707              'name' => $name,
2708              'bits' => $bits,
2709              'time' => $time,
2710          )
2711      );
2712      if ( ! is_array( $upload_bits_error ) ) {
2713          $upload['error'] = $upload_bits_error;
2714          return $upload;
2715      }
2716  
2717      $filename = wp_unique_filename( $upload['path'], $name );
2718  
2719      $new_file = $upload['path'] . "/$filename";
2720      if ( ! wp_mkdir_p( dirname( $new_file ) ) ) {
2721          if ( 0 === strpos( $upload['basedir'], ABSPATH ) ) {
2722              $error_path = str_replace( ABSPATH, '', $upload['basedir'] ) . $upload['subdir'];
2723          } else {
2724              $error_path = wp_basename( $upload['basedir'] ) . $upload['subdir'];
2725          }
2726  
2727          $message = sprintf(
2728              /* translators: %s: Directory path. */
2729              __( 'Unable to create directory %s. Is its parent directory writable by the server?' ),
2730              $error_path
2731          );
2732          return array( 'error' => $message );
2733      }
2734  
2735      $ifp = @fopen( $new_file, 'wb' );
2736      if ( ! $ifp ) {
2737          return array(
2738              /* translators: %s: File name. */
2739              'error' => sprintf( __( 'Could not write file %s' ), $new_file ),
2740          );
2741      }
2742  
2743      fwrite( $ifp, $bits );
2744      fclose( $ifp );
2745      clearstatcache();
2746  
2747      // Set correct file permissions.
2748      $stat  = @ stat( dirname( $new_file ) );
2749      $perms = $stat['mode'] & 0007777;
2750      $perms = $perms & 0000666;
2751      chmod( $new_file, $perms );
2752      clearstatcache();
2753  
2754      // Compute the URL.
2755      $url = $upload['url'] . "/$filename";
2756  
2757      if ( is_multisite() ) {
2758          clean_dirsize_cache( $new_file );
2759      }
2760  
2761      /** This filter is documented in wp-admin/includes/file.php */
2762      return apply_filters(
2763          'wp_handle_upload',
2764          array(
2765              'file'  => $new_file,
2766              'url'   => $url,
2767              'type'  => $wp_filetype['type'],
2768              'error' => false,
2769          ),
2770          'sideload'
2771      );
2772  }
2773  
2774  /**
2775   * Retrieve the file type based on the extension name.
2776   *
2777   * @since 2.5.0
2778   *
2779   * @param string $ext The extension to search.
2780   * @return string|void The file type, example: audio, video, document, spreadsheet, etc.
2781   */
2782  function wp_ext2type( $ext ) {
2783      $ext = strtolower( $ext );
2784  
2785      $ext2type = wp_get_ext_types();
2786      foreach ( $ext2type as $type => $exts ) {
2787          if ( in_array( $ext, $exts, true ) ) {
2788              return $type;
2789          }
2790      }
2791  }
2792  
2793  /**
2794   * Retrieve the file type from the file name.
2795   *
2796   * You can optionally define the mime array, if needed.
2797   *
2798   * @since 2.0.4
2799   *
2800   * @param string   $filename File name or path.
2801   * @param string[] $mimes    Optional. Array of allowed mime types keyed by their file extension regex.
2802   * @return array {
2803   *     Values for the extension and mime type.
2804   *
2805   *     @type string|false $ext  File extension, or false if the file doesn't match a mime type.
2806   *     @type string|false $type File mime type, or false if the file doesn't match a mime type.
2807   * }
2808   */
2809  function wp_check_filetype( $filename, $mimes = null ) {
2810      if ( empty( $mimes ) ) {
2811          $mimes = get_allowed_mime_types();
2812      }
2813      $type = false;
2814      $ext  = false;
2815  
2816      foreach ( $mimes as $ext_preg => $mime_match ) {
2817          $ext_preg = '!\.(' . $ext_preg . ')$!i';
2818          if ( preg_match( $ext_preg, $filename, $ext_matches ) ) {
2819              $type = $mime_match;
2820              $ext  = $ext_matches[1];
2821              break;
2822          }
2823      }
2824  
2825      return compact( 'ext', 'type' );
2826  }
2827  
2828  /**
2829   * Attempt to determine the real file type of a file.
2830   *
2831   * If unable to, the file name extension will be used to determine type.
2832   *
2833   * If it's determined that the extension does not match the file's real type,
2834   * then the "proper_filename" value will be set with a proper filename and extension.
2835   *
2836   * Currently this function only supports renaming images validated via wp_get_image_mime().
2837   *
2838   * @since 3.0.0
2839   *
2840   * @param string   $file     Full path to the file.
2841   * @param string   $filename The name of the file (may differ from $file due to $file being
2842   *                           in a tmp directory).
2843   * @param string[] $mimes    Optional. Array of allowed mime types keyed by their file extension regex.
2844   * @return array {
2845   *     Values for the extension, mime type, and corrected filename.
2846   *
2847   *     @type string|false $ext             File extension, or false if the file doesn't match a mime type.
2848   *     @type string|false $type            File mime type, or false if the file doesn't match a mime type.
2849   *     @type string|false $proper_filename File name with its correct extension, or false if it cannot be determined.
2850   * }
2851   */
2852  function wp_check_filetype_and_ext( $file, $filename, $mimes = null ) {
2853      $proper_filename = false;
2854  
2855      // Do basic extension validation and MIME mapping.
2856      $wp_filetype = wp_check_filetype( $filename, $mimes );
2857      $ext         = $wp_filetype['ext'];
2858      $type        = $wp_filetype['type'];
2859  
2860      // We can't do any further validation without a file to work with.
2861      if ( ! file_exists( $file ) ) {
2862          return compact( 'ext', 'type', 'proper_filename' );
2863      }
2864  
2865      $real_mime = false;
2866  
2867      // Validate image types.
2868      if ( $type && 0 === strpos( $type, 'image/' ) ) {
2869  
2870          // Attempt to figure out what type of image it actually is.
2871          $real_mime = wp_get_image_mime( $file );
2872  
2873          if ( $real_mime && $real_mime != $type ) {
2874              /**
2875               * Filters the list mapping image mime types to their respective extensions.
2876               *
2877               * @since 3.0.0
2878               *
2879               * @param array $mime_to_ext Array of image mime types and their matching extensions.
2880               */
2881              $mime_to_ext = apply_filters(
2882                  'getimagesize_mimes_to_exts',
2883                  array(
2884                      'image/jpeg' => 'jpg',
2885                      'image/png'  => 'png',
2886                      'image/gif'  => 'gif',
2887                      'image/bmp'  => 'bmp',
2888                      'image/tiff' => 'tif',
2889                      'image/webp' => 'webp',
2890                  )
2891              );
2892  
2893              // Replace whatever is after the last period in the filename with the correct extension.
2894              if ( ! empty( $mime_to_ext[ $real_mime ] ) ) {
2895                  $filename_parts = explode( '.', $filename );
2896                  array_pop( $filename_parts );
2897                  $filename_parts[] = $mime_to_ext[ $real_mime ];
2898                  $new_filename     = implode( '.', $filename_parts );
2899  
2900                  if ( $new_filename != $filename ) {
2901                      $proper_filename = $new_filename; // Mark that it changed.
2902                  }
2903                  // Redefine the extension / MIME.
2904                  $wp_filetype = wp_check_filetype( $new_filename, $mimes );
2905                  $ext         = $wp_filetype['ext'];
2906                  $type        = $wp_filetype['type'];
2907              } else {
2908                  // Reset $real_mime and try validating again.
2909                  $real_mime = false;
2910              }
2911          }
2912      }
2913  
2914      // Validate files that didn't get validated during previous checks.
2915      if ( $type && ! $real_mime && extension_loaded( 'fileinfo' ) ) {
2916          $finfo     = finfo_open( FILEINFO_MIME_TYPE );
2917          $real_mime = finfo_file( $finfo, $file );
2918          finfo_close( $finfo );
2919  
2920          // fileinfo often misidentifies obscure files as one of these types.
2921          $nonspecific_types = array(
2922              'application/octet-stream',
2923              'application/encrypted',
2924              'application/CDFV2-encrypted',
2925              'application/zip',
2926          );
2927  
2928          /*
2929           * If $real_mime doesn't match the content type we're expecting from the file's extension,
2930           * we need to do some additional vetting. Media types and those listed in $nonspecific_types are
2931           * allowed some leeway, but anything else must exactly match the real content type.
2932           */
2933          if ( in_array( $real_mime, $nonspecific_types, true ) ) {
2934              // File is a non-specific binary type. That's ok if it's a type that generally tends to be binary.
2935              if ( ! in_array( substr( $type, 0, strcspn( $type, '/' ) ), array( 'application', 'video', 'audio' ), true ) ) {
2936                  $type = false;
2937                  $ext  = false;
2938              }
2939          } elseif ( 0 === strpos( $real_mime, 'video/' ) || 0 === strpos( $real_mime, 'audio/' ) ) {
2940              /*
2941               * For these types, only the major type must match the real value.
2942               * This means that common mismatches are forgiven: application/vnd.apple.numbers is often misidentified as application/zip,
2943               * and some media files are commonly named with the wrong extension (.mov instead of .mp4)
2944               */
2945              if ( substr( $real_mime, 0, strcspn( $real_mime, '/' ) ) !== substr( $type, 0, strcspn( $type, '/' ) ) ) {
2946                  $type = false;
2947                  $ext  = false;
2948              }
2949          } elseif ( 'text/plain' === $real_mime ) {
2950              // A few common file types are occasionally detected as text/plain; allow those.
2951              if ( ! in_array(
2952                  $type,
2953                  array(
2954                      'text/plain',
2955                      'text/csv',
2956                      'application/csv',
2957                      'text/richtext',
2958                      'text/tsv',
2959                      'text/vtt',
2960                  ),
2961                  true
2962              )
2963              ) {
2964                  $type = false;
2965                  $ext  = false;
2966              }
2967          } elseif ( 'application/csv' === $real_mime ) {
2968              // Special casing for CSV files.
2969              if ( ! in_array(
2970                  $type,
2971                  array(
2972                      'text/csv',
2973                      'text/plain',
2974                      'application/csv',
2975                  ),
2976                  true
2977              )
2978              ) {
2979                  $type = false;
2980                  $ext  = false;
2981              }
2982          } elseif ( 'text/rtf' === $real_mime ) {
2983              // Special casing for RTF files.
2984              if ( ! in_array(
2985                  $type,
2986                  array(
2987                      'text/rtf',
2988                      'text/plain',
2989                      'application/rtf',
2990                  ),
2991                  true
2992              )
2993              ) {
2994                  $type = false;
2995                  $ext  = false;
2996              }
2997          } else {
2998              if ( $type !== $real_mime ) {
2999                  /*
3000                   * Everything else including image/* and application/*:
3001                   * If the real content type doesn't match the file extension, assume it's dangerous.
3002                   */
3003                  $type = false;
3004                  $ext  = false;
3005              }
3006          }
3007      }
3008  
3009      // The mime type must be allowed.
3010      if ( $type ) {
3011          $allowed = get_allowed_mime_types();
3012  
3013          if ( ! in_array( $type, $allowed, true ) ) {
3014              $type = false;
3015              $ext  = false;
3016          }
3017      }
3018  
3019      /**
3020       * Filters the "real" file type of the given file.
3021       *
3022       * @since 3.0.0
3023       * @since 5.1.0 The $real_mime parameter was added.
3024       *
3025       * @param array        $wp_check_filetype_and_ext {
3026       *     Values for the extension, mime type, and corrected filename.
3027       *
3028       *     @type string|false $ext             File extension, or false if the file doesn't match a mime type.
3029       *     @type string|false $type            File mime type, or false if the file doesn't match a mime type.
3030       *     @type string|false $proper_filename File name with its correct extension, or false if it cannot be determined.
3031       * }
3032       * @param string       $file                      Full path to the file.
3033       * @param string       $filename                  The name of the file (may differ from $file due to
3034       *                                                $file being in a tmp directory).
3035       * @param string[]     $mimes                     Array of mime types keyed by their file extension regex.
3036       * @param string|false $real_mime                 The actual mime type or false if the type cannot be determined.
3037       */
3038      return apply_filters( 'wp_check_filetype_and_ext', compact( 'ext', 'type', 'proper_filename' ), $file, $filename, $mimes, $real_mime );
3039  }
3040  
3041  /**
3042   * Returns the real mime type of an image file.
3043   *
3044   * This depends on exif_imagetype() or getimagesize() to determine real mime types.
3045   *
3046   * @since 4.7.1
3047   * @since 5.8.0 Added support for WebP images.
3048   *
3049   * @param string $file Full path to the file.
3050   * @return string|false The actual mime type or false if the type cannot be determined.
3051   */
3052  function wp_get_image_mime( $file ) {
3053      /*
3054       * Use exif_imagetype() to check the mimetype if available or fall back to
3055       * getimagesize() if exif isn't avaialbe. If either function throws an Exception
3056       * we assume the file could not be validated.
3057       */
3058      try {
3059          if ( is_callable( 'exif_imagetype' ) ) {
3060              $imagetype = exif_imagetype( $file );
3061              $mime      = ( $imagetype ) ? image_type_to_mime_type( $imagetype ) : false;
3062          } elseif ( function_exists( 'getimagesize' ) ) {
3063              // Don't silence errors when in debug mode, unless running unit tests.
3064              if ( defined( 'WP_DEBUG' ) && WP_DEBUG
3065                  && ! defined( 'WP_RUN_CORE_TESTS' )
3066              ) {
3067                  // Not using wp_getimagesize() here to avoid an infinite loop.
3068                  $imagesize = getimagesize( $file );
3069              } else {
3070                  // phpcs:ignore WordPress.PHP.NoSilencedErrors
3071                  $imagesize = @getimagesize( $file );
3072              }
3073  
3074              $mime = ( isset( $imagesize['mime'] ) ) ? $imagesize['mime'] : false;
3075          } else {
3076              $mime = false;
3077          }
3078  
3079          if ( false !== $mime ) {
3080              return $mime;
3081          }
3082  
3083          $handle = fopen( $file, 'rb' );
3084          if ( false === $handle ) {
3085              return false;
3086          }
3087  
3088          $magic = fread( $handle, 12 );
3089          if ( false === $magic ) {
3090              return false;
3091          }
3092  
3093          /*
3094           * Add WebP fallback detection when image library doesn't support WebP.
3095           * Note: detection values come from LibWebP, see
3096           * https://github.com/webmproject/libwebp/blob/master/imageio/image_dec.c#L30
3097           */
3098          $magic = bin2hex( $magic );
3099          if (
3100              // RIFF.
3101              ( 0 === strpos( $magic, '52494646' ) ) &&
3102              // WEBP.
3103              ( 16 === strpos( $magic, '57454250' ) )
3104          ) {
3105              $mime = 'image/webp';
3106          }
3107  
3108          fclose( $handle );
3109      } catch ( Exception $e ) {
3110          $mime = false;
3111      }
3112  
3113      return $mime;
3114  }
3115  
3116  /**
3117   * Retrieve list of mime types and file extensions.
3118   *
3119   * @since 3.5.0
3120   * @since 4.2.0 Support was added for GIMP (.xcf) files.
3121   * @since 4.9.2 Support was added for Flac (.flac) files.
3122   * @since 4.9.6 Support was added for AAC (.aac) files.
3123   *
3124   * @return string[] Array of mime types keyed by the file extension regex corresponding to those types.
3125   */
3126  function wp_get_mime_types() {
3127      /**
3128       * Filters the list of mime types and file extensions.
3129       *
3130       * This filter should be used to add, not remove, mime types. To remove
3131       * mime types, use the {@see 'upload_mimes'} filter.
3132       *
3133       * @since 3.5.0
3134       *
3135       * @param string[] $wp_get_mime_types Mime types keyed by the file extension regex
3136       *                                 corresponding to those types.
3137       */
3138      return apply_filters(
3139          'mime_types',
3140          array(
3141              // Image formats.
3142              'jpg|jpeg|jpe'                 => 'image/jpeg',
3143              'gif'                          => 'image/gif',
3144              'png'                          => 'image/png',
3145              'bmp'                          => 'image/bmp',
3146              'tiff|tif'                     => 'image/tiff',
3147              'webp'                         => 'image/webp',
3148              'ico'                          => 'image/x-icon',
3149              'heic'                         => 'image/heic',
3150              // Video formats.
3151              'asf|asx'                      => 'video/x-ms-asf',
3152              'wmv'                          => 'video/x-ms-wmv',
3153              'wmx'                          => 'video/x-ms-wmx',
3154              'wm'                           => 'video/x-ms-wm',
3155              'avi'                          => 'video/avi',
3156              'divx'                         => 'video/divx',
3157              'flv'                          => 'video/x-flv',
3158              'mov|qt'                       => 'video/quicktime',
3159              'mpeg|mpg|mpe'                 => 'video/mpeg',
3160              'mp4|m4v'                      => 'video/mp4',
3161              'ogv'                          => 'video/ogg',
3162              'webm'                         => 'video/webm',
3163              'mkv'                          => 'video/x-matroska',
3164              '3gp|3gpp'                     => 'video/3gpp',  // Can also be audio.
3165              '3g2|3gp2'                     => 'video/3gpp2', // Can also be audio.
3166              // Text formats.
3167              'txt|asc|c|cc|h|srt'           => 'text/plain',
3168              'csv'                          => 'text/csv',
3169              'tsv'                          => 'text/tab-separated-values',
3170              'ics'                          => 'text/calendar',
3171              'rtx'                          => 'text/richtext',
3172              'css'                          => 'text/css',
3173              'htm|html'                     => 'text/html',
3174              'vtt'                          => 'text/vtt',
3175              'dfxp'                         => 'application/ttaf+xml',
3176              // Audio formats.
3177              'mp3|m4a|m4b'                  => 'audio/mpeg',
3178              'aac'                          => 'audio/aac',
3179              'ra|ram'                       => 'audio/x-realaudio',
3180              'wav'                          => 'audio/wav',
3181              'ogg|oga'                      => 'audio/ogg',
3182              'flac'                         => 'audio/flac',
3183              'mid|midi'                     => 'audio/midi',
3184              'wma'                          => 'audio/x-ms-wma',
3185              'wax'                          => 'audio/x-ms-wax',
3186              'mka'                          => 'audio/x-matroska',
3187              // Misc application formats.
3188              'rtf'                          => 'application/rtf',
3189              'js'                           => 'application/javascript',
3190              'pdf'                          => 'application/pdf',
3191              'swf'                          => 'application/x-shockwave-flash',
3192              'class'                        => 'application/java',
3193              'tar'                          => 'application/x-tar',
3194              'zip'                          => 'application/zip',
3195              'gz|gzip'                      => 'application/x-gzip',
3196              'rar'                          => 'application/rar',
3197              '7z'                           => 'application/x-7z-compressed',
3198              'exe'                          => 'application/x-msdownload',
3199              'psd'                          => 'application/octet-stream',
3200              'xcf'                          => 'application/octet-stream',
3201              // MS Office formats.
3202              'doc'                          => 'application/msword',
3203              'pot|pps|ppt'                  => 'application/vnd.ms-powerpoint',
3204              'wri'                          => 'application/vnd.ms-write',
3205              'xla|xls|xlt|xlw'              => 'application/vnd.ms-excel',
3206              'mdb'                          => 'application/vnd.ms-access',
3207              'mpp'                          => 'application/vnd.ms-project',
3208              'docx'                         => 'application/vnd.openxmlformats-officedocument.wordprocessingml.document',
3209              'docm'                         => 'application/vnd.ms-word.document.macroEnabled.12',
3210              'dotx'                         => 'application/vnd.openxmlformats-officedocument.wordprocessingml.template',
3211              'dotm'                         => 'application/vnd.ms-word.template.macroEnabled.12',
3212              'xlsx'                         => 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet',
3213              'xlsm'                         => 'application/vnd.ms-excel.sheet.macroEnabled.12',
3214              'xlsb'                         => 'application/vnd.ms-excel.sheet.binary.macroEnabled.12',
3215              'xltx'                         => 'application/vnd.openxmlformats-officedocument.spreadsheetml.template',
3216              'xltm'                         => 'application/vnd.ms-excel.template.macroEnabled.12',
3217              'xlam'                         => 'application/vnd.ms-excel.addin.macroEnabled.12',
3218              'pptx'                         => 'application/vnd.openxmlformats-officedocument.presentationml.presentation',
3219              'pptm'                         => 'application/vnd.ms-powerpoint.presentation.macroEnabled.12',
3220              'ppsx'                         => 'application/vnd.openxmlformats-officedocument.presentationml.slideshow',
3221              'ppsm'                         => 'application/vnd.ms-powerpoint.slideshow.macroEnabled.12',
3222              'potx'                         => 'application/vnd.openxmlformats-officedocument.presentationml.template',
3223              'potm'                         => 'application/vnd.ms-powerpoint.template.macroEnabled.12',
3224              'ppam'                         => 'application/vnd.ms-powerpoint.addin.macroEnabled.12',
3225              'sldx'                         => 'application/vnd.openxmlformats-officedocument.presentationml.slide',
3226              'sldm'                         => 'application/vnd.ms-powerpoint.slide.macroEnabled.12',
3227              'onetoc|onetoc2|onetmp|onepkg' => 'application/onenote',
3228              'oxps'                         => 'application/oxps',
3229              'xps'                          => 'application/vnd.ms-xpsdocument',
3230              // OpenOffice formats.
3231              'odt'                          => 'application/vnd.oasis.opendocument.text',
3232              'odp'                          => 'application/vnd.oasis.opendocument.presentation',
3233              'ods'                          => 'application/vnd.oasis.opendocument.spreadsheet',
3234              'odg'                          => 'application/vnd.oasis.opendocument.graphics',
3235              'odc'                          => 'application/vnd.oasis.opendocument.chart',
3236              'odb'                          => 'application/vnd.oasis.opendocument.database',
3237              'odf'                          => 'application/vnd.oasis.opendocument.formula',
3238              // WordPerfect formats.
3239              'wp|wpd'                       => 'application/wordperfect',
3240              // iWork formats.
3241              'key'                          => 'application/vnd.apple.keynote',
3242              'numbers'                      => 'application/vnd.apple.numbers',
3243              'pages'                        => 'application/vnd.apple.pages',
3244          )
3245      );
3246  }
3247  
3248  /**
3249   * Retrieves the list of common file extensions and their types.
3250   *
3251   * @since 4.6.0
3252   *
3253   * @return array[] Multi-dimensional array of file extensions types keyed by the type of file.
3254   */
3255  function wp_get_ext_types() {
3256  
3257      /**
3258       * Filters file type based on the extension name.
3259       *
3260       * @since 2.5.0
3261       *
3262       * @see wp_ext2type()
3263       *
3264       * @param array[] $ext2type Multi-dimensional array of file extensions types keyed by the type of file.
3265       */
3266      return apply_filters(
3267          'ext2type',
3268          array(
3269              'image'       => array( 'jpg', 'jpeg', 'jpe', 'gif', 'png', 'bmp', 'tif', 'tiff', 'ico', 'heic', 'webp' ),
3270              'audio'       => array( 'aac', 'ac3', 'aif', 'aiff', 'flac', 'm3a', 'm4a', 'm4b', 'mka', 'mp1', 'mp2', 'mp3', 'ogg', 'oga', 'ram', 'wav', 'wma' ),
3271              'video'       => array( '3g2', '3gp', '3gpp', 'asf', 'avi', 'divx', 'dv', 'flv', 'm4v', 'mkv', 'mov', 'mp4', 'mpeg', 'mpg', 'mpv', 'ogm', 'ogv', 'qt', 'rm', 'vob', 'wmv' ),
3272              'document'    => array( 'doc', 'docx', 'docm', 'dotm', 'odt', 'pages', 'pdf', 'xps', 'oxps', 'rtf', 'wp', 'wpd', 'psd', 'xcf' ),
3273              'spreadsheet' => array( 'numbers', 'ods', 'xls', 'xlsx', 'xlsm', 'xlsb' ),
3274              'interactive' => array( 'swf', 'key', 'ppt', 'pptx', 'pptm', 'pps', 'ppsx', 'ppsm', 'sldx', 'sldm', 'odp' ),
3275              'text'        => array( 'asc', 'csv', 'tsv', 'txt' ),
3276              'archive'     => array( 'bz2', 'cab', 'dmg', 'gz', 'rar', 'sea', 'sit', 'sqx', 'tar', 'tgz', 'zip', '7z' ),
3277              'code'        => array( 'css', 'htm', 'html', 'php', 'js' ),
3278          )
3279      );
3280  }
3281  
3282  /**
3283   * Retrieve list of allowed mime types and file extensions.
3284   *
3285   * @since 2.8.6
3286   *
3287   * @param int|WP_User $user Optional. User to check. Defaults to current user.
3288   * @return string[] Array of mime types keyed by the file extension regex corresponding
3289   *                  to those types.
3290   */
3291  function get_allowed_mime_types( $user = null ) {
3292      $t = wp_get_mime_types();
3293  
3294      unset( $t['swf'], $t['exe'] );
3295      if ( function_exists( 'current_user_can' ) ) {
3296          $unfiltered = $user ? user_can( $user, 'unfiltered_html' ) : current_user_can( 'unfiltered_html' );
3297      }
3298  
3299      if ( empty( $unfiltered ) ) {
3300          unset( $t['htm|html'], $t['js'] );
3301      }
3302  
3303      /**
3304       * Filters list of allowed mime types and file extensions.
3305       *
3306       * @since 2.0.0
3307       *
3308       * @param array            $t    Mime types keyed by the file extension regex corresponding to those types.
3309       * @param int|WP_User|null $user User ID, User object or null if not provided (indicates current user).
3310       */
3311      return apply_filters( 'upload_mimes', $t, $user );
3312  }
3313  
3314  /**
3315   * Display "Are You Sure" message to confirm the action being taken.
3316   *
3317   * If the action has the nonce explain message, then it will be displayed
3318   * along with the "Are you sure?" message.
3319   *
3320   * @since 2.0.4
3321   *
3322   * @param string $action The nonce action.
3323   */
3324  function wp_nonce_ays( $action ) {
3325      if ( 'log-out' === $action ) {
3326          $html = sprintf(
3327              /* translators: %s: Site title. */
3328              __( 'You are attempting to log out of %s' ),
3329              get_bloginfo( 'name' )
3330          );
3331          $html       .= '</p><p>';
3332          $redirect_to = isset( $_REQUEST['redirect_to'] ) ? $_REQUEST['redirect_to'] : '';
3333          $html       .= sprintf(
3334              /* translators: %s: Logout URL. */
3335              __( 'Do you really want to <a href="%s">log out</a>?' ),
3336              wp_logout_url( $redirect_to )
3337          );
3338      } else {
3339          $html = __( 'The link you followed has expired.' );
3340          if ( wp_get_referer() ) {
3341              $html .= '</p><p>';
3342              $html .= sprintf(
3343                  '<a href="%s">%s</a>',
3344                  esc_url( remove_query_arg( 'updated', wp_get_referer() ) ),
3345                  __( 'Please try again.' )
3346              );
3347          }
3348      }
3349  
3350      wp_die( $html, __( 'Something went wrong.' ), 403 );
3351  }
3352  
3353  /**
3354   * Kills WordPress execution and displays HTML page with an error message.
3355   *
3356   * This function complements the `die()` PHP function. The difference is that
3357   * HTML will be displayed to the user. It is recommended to use this function
3358   * only when the execution should not continue any further. It is not recommended
3359   * to call this function very often, and try to handle as many errors as possible
3360   * silently or more gracefully.
3361   *
3362   * As a shorthand, the desired HTTP response code may be passed as an integer to
3363   * the `$title` parameter (the default title would apply) or the `$args` parameter.
3364   *
3365   * @since 2.0.4
3366   * @since 4.1.0 The `$title` and `$args` parameters were changed to optionally accept
3367   *              an integer to be used as the response code.
3368   * @since 5.1.0 The `$link_url`, `$link_text`, and `$exit` arguments were added.
3369   * @since 5.3.0 The `$charset` argument was added.
3370   * @since 5.5.0 The `$text_direction` argument has a priority over get_language_attributes()
3371   *              in the default handler.
3372   *
3373   * @global WP_Query $wp_query WordPress Query object.
3374   *
3375   * @param string|WP_Error  $message Optional. Error message. If this is a WP_Error object,
3376   *                                  and not an Ajax or XML-RPC request, the error's messages are used.
3377   *                                  Default empty.
3378   * @param string|int       $title   Optional. Error title. If `$message` is a `WP_Error` object,
3379   *                                  error data with the key 'title' may be used to specify the title.
3380   *                                  If `$title` is an integer, then it is treated as the response
3381   *                                  code. Default empty.
3382   * @param string|array|int $args {
3383   *     Optional. Arguments to control behavior. If `$args` is an integer, then it is treated
3384   *     as the response code. Default empty array.
3385   *
3386   *     @type int    $response       The HTTP response code. Default 200 for Ajax requests, 500 otherwise.
3387   *     @type string $link_url       A URL to include a link to. Only works in combination with $link_text.
3388   *                                  Default empty string.
3389   *     @type string $link_text      A label for the link to include. Only works in combination with $link_url.
3390   *                                  Default empty string.
3391   *     @type bool   $back_link      Whether to include a link to go back. Default false.
3392   *     @type string $text_direction The text direction. This is only useful internally, when WordPress is still
3393   *                                  loading and the site's locale is not set up yet. Accepts 'rtl' and 'ltr'.
3394   *                                  Default is the value of is_rtl().
3395   *     @type string $charset        Character set of the HTML output. Default 'utf-8'.
3396   *     @type string $code           Error code to use. Default is 'wp_die', or the main error code if $message
3397   *                                  is a WP_Error.
3398   *     @type bool   $exit           Whether to exit the process after completion. Default true.
3399   * }
3400   */
3401  function wp_die( $message = '', $title = '', $args = array() ) {
3402      global $wp_query;
3403  
3404      if ( is_int( $args ) ) {
3405          $args = array( 'response' => $args );
3406      } elseif ( is_int( $title ) ) {
3407          $args  = array( 'response' => $title );
3408          $title = '';
3409      }
3410  
3411      if ( wp_doing_ajax() ) {
3412          /**
3413           * Filters the callback for killing WordPress execution for Ajax requests.
3414           *
3415           * @since 3.4.0
3416           *
3417           * @param callable $function Callback function name.
3418           */
3419          $function = apply_filters( 'wp_die_ajax_handler', '_ajax_wp_die_handler' );
3420      } elseif ( wp_is_json_request() ) {
3421          /**
3422           * Filters the callback for killing WordPress execution for JSON requests.
3423           *
3424           * @since 5.1.0
3425           *
3426           * @param callable $function Callback function name.
3427           */
3428          $function = apply_filters( 'wp_die_json_handler', '_json_wp_die_handler' );
3429      } elseif ( wp_is_jsonp_request() ) {
3430          /**
3431           * Filters the callback for killing WordPress execution for JSONP requests.
3432           *
3433           * @since 5.2.0
3434           *
3435           * @param callable $function Callback function name.
3436           */
3437          $function = apply_filters( 'wp_die_jsonp_handler', '_jsonp_wp_die_handler' );
3438      } elseif ( defined( 'XMLRPC_REQUEST' ) && XMLRPC_REQUEST ) {
3439          /**
3440           * Filters the callback for killing WordPress execution for XML-RPC requests.
3441           *
3442           * @since 3.4.0
3443           *
3444           * @param callable $function Callback function name.
3445           */
3446          $function = apply_filters( 'wp_die_xmlrpc_handler', '_xmlrpc_wp_die_handler' );
3447      } elseif ( wp_is_xml_request()
3448          || isset( $wp_query ) &&
3449              ( function_exists( 'is_feed' ) && is_feed()
3450              || function_exists( 'is_comment_feed' ) && is_comment_feed()
3451              || function_exists( 'is_trackback' ) && is_trackback() ) ) {
3452          /**
3453           * Filters the callback for killing WordPress execution for XML requests.
3454           *
3455           * @since 5.2.0
3456           *
3457           * @param callable $function Callback function name.
3458           */
3459          $function = apply_filters( 'wp_die_xml_handler', '_xml_wp_die_handler' );
3460      } else {
3461          /**
3462           * Filters the callback for killing WordPress execution for all non-Ajax, non-JSON, non-XML requests.
3463           *
3464           * @since 3.0.0
3465           *
3466           * @param callable $function Callback function name.
3467           */
3468          $function = apply_filters( 'wp_die_handler', '_default_wp_die_handler' );
3469      }
3470  
3471      call_user_func( $function, $message, $title, $args );
3472  }
3473  
3474  /**
3475   * Kills WordPress execution and displays HTML page with an error message.
3476   *
3477   * This is the default handler for wp_die(). If you want a custom one,
3478   * you can override this using the {@see 'wp_die_handler'} filter in wp_die().
3479   *
3480   * @since 3.0.0
3481   * @access private
3482   *
3483   * @param string|WP_Error $message Error message or WP_Error object.
3484   * @param string          $title   Optional. Error title. Default empty.
3485   * @param string|array    $args    Optional. Arguments to control behavior. Default empty array.
3486   */
3487  function _default_wp_die_handler( $message, $title = '', $args = array() ) {
3488      list( $message, $title, $parsed_args ) = _wp_die_process_input( $message, $title, $args );
3489  
3490      if ( is_string( $message ) ) {
3491          if ( ! empty( $parsed_args['additional_errors'] ) ) {
3492              $message = array_merge(
3493                  array( $message ),
3494                  wp_list_pluck( $parsed_args['additional_errors'], 'message' )
3495              );
3496              $message = "<ul>\n\t\t<li>" . implode( "</li>\n\t\t<li>", $message ) . "</li>\n\t</ul>";
3497          }
3498  
3499          $message = sprintf(
3500              '<div class="wp-die-message">%s</div>',
3501              $message
3502          );
3503      }
3504  
3505      $have_gettext = function_exists( '__' );
3506  
3507      if ( ! empty( $parsed_args['link_url'] ) && ! empty( $parsed_args['link_text'] ) ) {
3508          $link_url = $parsed_args['link_url'];
3509          if ( function_exists( 'esc_url' ) ) {
3510              $link_url = esc_url( $link_url );
3511          }
3512          $link_text = $parsed_args['link_text'];
3513          $message  .= "\n<p><a href='{$link_url}'>{$link_text}</a></p>";
3514      }
3515  
3516      if ( isset( $parsed_args['back_link'] ) && $parsed_args['back_link'] ) {
3517          $back_text = $have_gettext ? __( '&laquo; Back' ) : '&laquo; Back';
3518          $message  .= "\n<p><a href='javascript:history.back()'>$back_text</a></p>";
3519      }
3520  
3521      if ( ! did_action( 'admin_head' ) ) :
3522          if ( ! headers_sent() ) {
3523              header( "Content-Type: text/html; charset={$parsed_args['charset']}" );
3524              status_header( $parsed_args['response'] );
3525              nocache_headers();
3526          }
3527  
3528          $text_direction = $parsed_args['text_direction'];
3529          $dir_attr       = "dir='$text_direction'";
3530  
3531          // If `text_direction` was not explicitly passed,
3532          // use get_language_attributes() if available.
3533          if ( empty( $args['text_direction'] )
3534              && function_exists( 'language_attributes' ) && function_exists( 'is_rtl' )
3535          ) {
3536              $dir_attr = get_language_attributes();
3537          }
3538          ?>
3539  <!DOCTYPE html>
3540  <html <?php echo $dir_attr; ?>>
3541  <head>
3542      <meta http-equiv="Content-Type" content="text/html; charset=<?php echo $parsed_args['charset']; ?>" />
3543      <meta name="viewport" content="width=device-width">
3544          <?php
3545          if ( function_exists( 'wp_robots' ) && function_exists( 'wp_robots_no_robots' ) && function_exists( 'add_filter' ) ) {
3546              add_filter( 'wp_robots', 'wp_robots_no_robots' );
3547              wp_robots();
3548          }
3549          ?>
3550      <title><?php echo $title; ?></title>
3551      <style type="text/css">
3552          html {
3553              background: #f1f1f1;
3554          }
3555          body {
3556              background: #fff;
3557              border: 1px solid #ccd0d4;
3558              color: #444;
3559              font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Oxygen-Sans, Ubuntu, Cantarell, "Helvetica Neue", sans-serif;
3560              margin: 2em auto;
3561              padding: 1em 2em;
3562              max-width: 700px;
3563              -webkit-box-shadow: 0 1px 1px rgba(0, 0, 0, .04);
3564              box-shadow: 0 1px 1px rgba(0, 0, 0, .04);
3565          }
3566          h1 {
3567              border-bottom: 1px solid #dadada;
3568              clear: both;
3569              color: #666;
3570              font-size: 24px;
3571              margin: 30px 0 0 0;
3572              padding: 0;
3573              padding-bottom: 7px;
3574          }
3575          #error-page {
3576              margin-top: 50px;
3577          }
3578          #error-page p,
3579          #error-page .wp-die-message {
3580              font-size: 14px;
3581              line-height: 1.5;
3582              margin: 25px 0 20px;
3583          }
3584          #error-page code {
3585              font-family: Consolas, Monaco, monospace;
3586          }
3587          ul li {
3588              margin-bottom: 10px;
3589              font-size: 14px ;
3590          }
3591          a {
3592              color: #0073aa;
3593          }
3594          a:hover,
3595          a:active {
3596              color: #006799;
3597          }
3598          a:focus {
3599              color: #124964;
3600              -webkit-box-shadow:
3601                  0 0 0 1px #5b9dd9,
3602                  0 0 2px 1px rgba(30, 140, 190, 0.8);
3603              box-shadow:
3604                  0 0 0 1px #5b9dd9,
3605                  0 0 2px 1px rgba(30, 140, 190, 0.8);
3606              outline: none;
3607          }
3608          .button {
3609              background: #f3f5f6;
3610              border: 1px solid #016087;
3611              color: #016087;
3612              display: inline-block;
3613              text-decoration: none;
3614              font-size: 13px;
3615              line-height: 2;
3616              height: 28px;
3617              margin: 0;
3618              padding: 0 10px 1px;
3619              cursor: pointer;
3620              -webkit-border-radius: 3px;
3621              -webkit-appearance: none;
3622              border-radius: 3px;
3623              white-space: nowrap;
3624              -webkit-box-sizing: border-box;
3625              -moz-box-sizing:    border-box;
3626              box-sizing:         border-box;
3627  
3628              vertical-align: top;
3629          }
3630  
3631          .button.button-large {
3632              line-height: 2.30769231;
3633              min-height: 32px;
3634              padding: 0 12px;
3635          }
3636  
3637          .button:hover,
3638          .button:focus {
3639              background: #f1f1f1;
3640          }
3641  
3642          .button:focus {
3643              background: #f3f5f6;
3644              border-color: #007cba;
3645              -webkit-box-shadow: 0 0 0 1px #007cba;
3646              box-shadow: 0 0 0 1px #007cba;
3647              color: #016087;
3648              outline: 2px solid transparent;
3649              outline-offset: 0;
3650          }
3651  
3652          .button:active {
3653              background: #f3f5f6;
3654              border-color: #7e8993;
3655              -webkit-box-shadow: none;
3656              box-shadow: none;
3657          }
3658  
3659          <?php
3660          if ( 'rtl' === $text_direction ) {
3661              echo 'body { font-family: Tahoma, Arial; }';
3662          }
3663          ?>
3664      </style>
3665  </head>
3666  <body id="error-page">
3667  <?php endif; // ! did_action( 'admin_head' ) ?>
3668      <?php echo $message; ?>
3669  </body>
3670  </html>
3671      <?php
3672      if ( $parsed_args['exit'] ) {
3673          die();
3674      }
3675  }
3676  
3677  /**
3678   * Kills WordPress execution and displays Ajax response with an error message.
3679   *
3680   * This is the handler for wp_die() when processing Ajax requests.
3681   *
3682   * @since 3.4.0
3683   * @access private
3684   *
3685   * @param string       $message Error message.
3686   * @param string       $title   Optional. Error title (unused). Default empty.
3687   * @param string|array $args    Optional. Arguments to control behavior. Default empty array.
3688   */
3689  function _ajax_wp_die_handler( $message, $title = '', $args = array() ) {
3690      // Set default 'response' to 200 for Ajax requests.
3691      $args = wp_parse_args(
3692          $args,
3693          array( 'response' => 200 )
3694      );
3695  
3696      list( $message, $title, $parsed_args ) = _wp_die_process_input( $message, $title, $args );
3697  
3698      if ( ! headers_sent() ) {
3699          // This is intentional. For backward-compatibility, support passing null here.
3700          if ( null !== $args['response'] ) {
3701              status_header( $parsed_args['response'] );
3702          }
3703          nocache_headers();
3704      }
3705  
3706      if ( is_scalar( $message ) ) {
3707          $message = (string) $message;
3708      } else {
3709          $message = '0';
3710      }
3711  
3712      if ( $parsed_args['exit'] ) {
3713          die( $message );
3714      }
3715  
3716      echo $message;
3717  }
3718  
3719  /**
3720   * Kills WordPress execution and displays JSON response with an error message.
3721   *
3722   * This is the handler for wp_die() when processing JSON requests.
3723   *
3724   * @since 5.1.0
3725   * @access private
3726   *
3727   * @param string       $message Error message.
3728   * @param string       $title   Optional. Error title. Default empty.
3729   * @param string|array $args    Optional. Arguments to control behavior. Default empty array.
3730   */
3731  function _json_wp_die_handler( $message, $title = '', $args = array() ) {
3732      list( $message, $title, $parsed_args ) = _wp_die_process_input( $message, $title, $args );
3733  
3734      $data = array(
3735          'code'              => $parsed_args['code'],
3736          'message'           => $message,
3737          'data'              => array(
3738              'status' => $parsed_args['response'],
3739          ),
3740          'additional_errors' => $parsed_args['additional_errors'],
3741      );
3742  
3743      if ( ! headers_sent() ) {
3744          header( "Content-Type: application/json; charset={$parsed_args['charset']}" );
3745          if ( null !== $parsed_args['response'] ) {
3746              status_header( $parsed_args['response'] );
3747          }
3748          nocache_headers();
3749      }
3750  
3751      echo wp_json_encode( $data );
3752      if ( $parsed_args['exit'] ) {
3753          die();
3754      }
3755  }
3756  
3757  /**
3758   * Kills WordPress execution and displays JSONP response with an error message.
3759   *
3760   * This is the handler for wp_die() when processing JSONP requests.
3761   *
3762   * @since 5.2.0
3763   * @access private
3764   *
3765   * @param string       $message Error message.
3766   * @param string       $title   Optional. Error title. Default empty.
3767   * @param string|array $args    Optional. Arguments to control behavior. Default empty array.
3768   */
3769  function _jsonp_wp_die_handler( $message, $title = '', $args = array() ) {
3770      list( $message, $title, $parsed_args ) = _wp_die_process_input( $message, $title, $args );
3771  
3772      $data = array(
3773          'code'              => $parsed_args['code'],
3774          'message'           => $message,
3775          'data'              => array(
3776              'status' => $parsed_args['response'],
3777          ),
3778          'additional_errors' => $parsed_args['additional_errors'],
3779      );
3780  
3781      if ( ! headers_sent() ) {
3782          header( "Content-Type: application/javascript; charset={$parsed_args['charset']}" );
3783          header( 'X-Content-Type-Options: nosniff' );
3784          header( 'X-Robots-Tag: noindex' );
3785          if ( null !== $parsed_args['response'] ) {
3786              status_header( $parsed_args['response'] );
3787          }
3788          nocache_headers();
3789      }
3790  
3791      $result         = wp_json_encode( $data );
3792      $jsonp_callback = $_GET['_jsonp'];
3793      echo '/**/' . $jsonp_callback . '(' . $result . ')';
3794      if ( $parsed_args['exit'] ) {
3795          die();
3796      }
3797  }
3798  
3799  /**
3800   * Kills WordPress execution and displays XML response with an error message.
3801   *
3802   * This is the handler for wp_die() when processing XMLRPC requests.
3803   *
3804   * @since 3.2.0
3805   * @access private
3806   *
3807   * @global wp_xmlrpc_server $wp_xmlrpc_server
3808   *
3809   * @param string       $message Error message.
3810   * @param string       $title   Optional. Error title. Default empty.
3811   * @param string|array $args    Optional. Arguments to control behavior. Default empty array.
3812   */
3813  function _xmlrpc_wp_die_handler( $message, $title = '', $args = array() ) {
3814      global $wp_xmlrpc_server;
3815  
3816      list( $message, $title, $parsed_args ) = _wp_die_process_input( $message, $title, $args );
3817  
3818      if ( ! headers_sent() ) {
3819          nocache_headers();
3820      }
3821  
3822      if ( $wp_xmlrpc_server ) {
3823          $error = new IXR_Error( $parsed_args['response'], $message );
3824          $wp_xmlrpc_server->output( $error->getXml() );
3825      }
3826      if ( $parsed_args['exit'] ) {
3827          die();
3828      }
3829  }
3830  
3831  /**
3832   * Kills WordPress execution and displays XML response with an error message.
3833   *
3834   * This is the handler for wp_die() when processing XML requests.
3835   *
3836   * @since 5.2.0
3837   * @access private
3838   *
3839   * @param string       $message Error message.
3840   * @param string       $title   Optional. Error title. Default empty.
3841   * @param string|array $args    Optional. Arguments to control behavior. Default empty array.
3842   */
3843  function _xml_wp_die_handler( $message, $title = '', $args = array() ) {
3844      list( $message, $title, $parsed_args ) = _wp_die_process_input( $message, $title, $args );
3845  
3846      $message = htmlspecialchars( $message );
3847      $title   = htmlspecialchars( $title );
3848  
3849      $xml = <<<EOD
3850  <error>
3851      <code>{$parsed_args['code']}</code>
3852      <title><![CDATA[{$title}]]></title>
3853      <message><![CDATA[{$message}]]></message>
3854      <data>
3855          <status>{$parsed_args['response']}</status>
3856      </data>
3857  </error>
3858  
3859  EOD;
3860  
3861      if ( ! headers_sent() ) {
3862          header( "Content-Type: text/xml; charset={$parsed_args['charset']}" );
3863          if ( null !== $parsed_args['response'] ) {
3864              status_header( $parsed_args['response'] );
3865          }
3866          nocache_headers();
3867      }
3868  
3869      echo $xml;
3870      if ( $parsed_args['exit'] ) {
3871          die();
3872      }
3873  }
3874  
3875  /**
3876   * Kills WordPress execution and displays an error message.
3877   *
3878   * This is the handler for wp_die() when processing APP requests.
3879   *
3880   * @since 3.4.0
3881   * @since 5.1.0 Added the $title and $args parameters.
3882   * @access private
3883   *
3884   * @param string       $message Optional. Response to print. Default empty.
3885   * @param string       $title   Optional. Error title (unused). Default empty.
3886   * @param string|array $args    Optional. Arguments to control behavior. Default empty array.
3887   */
3888  function _scalar_wp_die_handler( $message = '', $title = '', $args = array() ) {
3889      list( $message, $title, $parsed_args ) = _wp_die_process_input( $message, $title, $args );
3890  
3891      if ( $parsed_args['exit'] ) {
3892          if ( is_scalar( $message ) ) {
3893              die( (string) $message );
3894          }
3895          die();
3896      }
3897  
3898      if ( is_scalar( $message ) ) {
3899          echo (string) $message;
3900      }
3901  }
3902  
3903  /**
3904   * Processes arguments passed to wp_die() consistently for its handlers.
3905   *
3906   * @since 5.1.0
3907   * @access private
3908   *
3909   * @param string|WP_Error $message Error message or WP_Error object.
3910   * @param string          $title   Optional. Error title. Default empty.
3911   * @param string|array    $args    Optional. Arguments to control behavior. Default empty array.
3912   * @return array {
3913   *     Processed arguments.
3914   *
3915   *     @type string $0 Error message.
3916   *     @type string $1 Error title.
3917   *     @type array  $2 Arguments to control behavior.
3918   * }
3919   */
3920  function _wp_die_process_input( $message, $title = '', $args = array() ) {
3921      $defaults = array(
3922          'response'          => 0,
3923          'code'              => '',
3924          'exit'              => true,
3925          'back_link'         => false,
3926          'link_url'          => '',
3927          'link_text'         => '',
3928          'text_direction'    => '',
3929          'charset'           => 'utf-8',
3930          'additional_errors' => array(),
3931      );
3932  
3933      $args = wp_parse_args( $args, $defaults );
3934  
3935      if ( function_exists( 'is_wp_error' ) && is_wp_error( $message ) ) {
3936          if ( ! empty( $message->errors ) ) {
3937              $errors = array();
3938              foreach ( (array) $message->errors as $error_code => $error_messages ) {
3939                  foreach ( (array) $error_messages as $error_message ) {
3940                      $errors[] = array(
3941                          'code'    => $error_code,
3942                          'message' => $error_message,
3943                          'data'    => $message->get_error_data( $error_code ),
3944                      );
3945                  }
3946              }
3947  
3948              $message = $errors[0]['message'];
3949              if ( empty( $args['code'] ) ) {
3950                  $args['code'] = $errors[0]['code'];
3951              }
3952              if ( empty( $args['response'] ) && is_array( $errors[0]['data'] ) && ! empty( $errors[0]['data']['status'] ) ) {
3953                  $args['response'] = $errors[0]['data']['status'];
3954              }
3955              if ( empty( $title ) && is_array( $errors[0]['data'] ) && ! empty( $errors[0]['data']['title'] ) ) {
3956                  $title = $errors[0]['data']['title'];
3957              }
3958  
3959              unset( $errors[0] );
3960              $args['additional_errors'] = array_values( $errors );
3961          } else {
3962              $message = '';
3963          }
3964      }
3965  
3966      $have_gettext = function_exists( '__' );
3967  
3968      // The $title and these specific $args must always have a non-empty value.
3969      if ( empty( $args['code'] ) ) {
3970          $args['code'] = 'wp_die';
3971      }
3972      if ( empty( $args['response'] ) ) {
3973          $args['response'] = 500;
3974      }
3975      if ( empty( $title ) ) {
3976          $title = $have_gettext ? __( 'WordPress &rsaquo; Error' ) : 'WordPress &rsaquo; Error';
3977      }
3978      if ( empty( $args['text_direction'] ) || ! in_array( $args['text_direction'], array( 'ltr', 'rtl' ), true ) ) {
3979          $args['text_direction'] = 'ltr';
3980          if ( function_exists( 'is_rtl' ) && is_rtl() ) {
3981              $args['text_direction'] = 'rtl';
3982          }
3983      }
3984  
3985      if ( ! empty( $args['charset'] ) ) {
3986          $args['charset'] = _canonical_charset( $args['charset'] );
3987      }
3988  
3989      return array( $message, $title, $args );
3990  }
3991  
3992  /**
3993   * Encode a variable into JSON, with some sanity checks.
3994   *
3995   * @since 4.1.0
3996   * @since 5.3.0 No longer handles support for PHP < 5.6.
3997   *
3998   * @param mixed $data    Variable (usually an array or object) to encode as JSON.
3999   * @param int   $options Optional. Options to be passed to json_encode(). Default 0.
4000   * @param int   $depth   Optional. Maximum depth to walk through $data. Must be
4001   *                       greater than 0. Default 512.
4002   * @return string|false The JSON encoded string, or false if it cannot be encoded.
4003   */
4004  function wp_json_encode( $data, $options = 0, $depth = 512 ) {
4005      $json = json_encode( $data, $options, $depth );
4006  
4007      // If json_encode() was successful, no need to do more sanity checking.
4008      if ( false !== $json ) {
4009          return $json;
4010      }
4011  
4012      try {
4013          $data = _wp_json_sanity_check( $data, $depth );
4014      } catch ( Exception $e ) {
4015          return false;
4016      }
4017  
4018      return json_encode( $data, $options, $depth );
4019  }
4020  
4021  /**
4022   * Perform sanity checks on data that shall be encoded to JSON.
4023   *
4024   * @ignore
4025   * @since 4.1.0
4026   * @access private
4027   *
4028   * @see wp_json_encode()
4029   *
4030   * @throws Exception If depth limit is reached.
4031   *
4032   * @param mixed $data  Variable (usually an array or object) to encode as JSON.
4033   * @param int   $depth Maximum depth to walk through $data. Must be greater than 0.
4034   * @return mixed The sanitized data that shall be encoded to JSON.
4035   */
4036  function _wp_json_sanity_check( $data, $depth ) {
4037      if ( $depth < 0 ) {
4038          throw new Exception( 'Reached depth limit' );
4039      }
4040  
4041      if ( is_array( $data ) ) {
4042          $output = array();
4043          foreach ( $data as $id => $el ) {
4044              // Don't forget to sanitize the ID!
4045              if ( is_string( $id ) ) {
4046                  $clean_id = _wp_json_convert_string( $id );
4047              } else {
4048                  $clean_id = $id;
4049              }
4050  
4051              // Check the element type, so that we're only recursing if we really have to.
4052              if ( is_array( $el ) || is_object( $el ) ) {
4053                  $output[ $clean_id ] = _wp_json_sanity_check( $el, $depth - 1 );
4054              } elseif ( is_string( $el ) ) {
4055                  $output[ $clean_id ] = _wp_json_convert_string( $el );
4056              } else {
4057                  $output[ $clean_id ] = $el;
4058              }
4059          }
4060      } elseif ( is_object( $data ) ) {
4061          $output = new stdClass;
4062          foreach ( $data as $id => $el ) {
4063              if ( is_string( $id ) ) {
4064                  $clean_id = _wp_json_convert_string( $id );
4065              } else {
4066                  $clean_id = $id;
4067              }
4068  
4069              if ( is_array( $el ) || is_object( $el ) ) {
4070                  $output->$clean_id = _wp_json_sanity_check( $el, $depth - 1 );
4071              } elseif ( is_string( $el ) ) {
4072                  $output->$clean_id = _wp_json_convert_string( $el );
4073              } else {
4074                  $output->$clean_id = $el;
4075              }
4076          }
4077      } elseif ( is_string( $data ) ) {
4078          return _wp_json_convert_string( $data );
4079      } else {
4080          return $data;
4081      }
4082  
4083      return $output;
4084  }
4085  
4086  /**
4087   * Convert a string to UTF-8, so that it can be safely encoded to JSON.
4088   *
4089   * @ignore
4090   * @since 4.1.0
4091   * @access private
4092   *
4093   * @see _wp_json_sanity_check()
4094   *
4095   * @param string $string The string which is to be converted.
4096   * @return string The checked string.
4097   */
4098  function _wp_json_convert_string( $string ) {
4099      static $use_mb = null;
4100      if ( is_null( $use_mb ) ) {
4101          $use_mb = function_exists( 'mb_convert_encoding' );
4102      }
4103  
4104      if ( $use_mb ) {
4105          $encoding = mb_detect_encoding( $string, mb_detect_order(), true );
4106          if ( $encoding ) {
4107              return mb_convert_encoding( $string, 'UTF-8', $encoding );
4108          } else {
4109              return mb_convert_encoding( $string, 'UTF-8', 'UTF-8' );
4110          }
4111      } else {
4112          return wp_check_invalid_utf8( $string, true );
4113      }
4114  }
4115  
4116  /**
4117   * Prepares response data to be serialized to JSON.
4118   *
4119   * This supports the JsonSerializable interface for PHP 5.2-5.3 as well.
4120   *
4121   * @ignore
4122   * @since 4.4.0
4123   * @deprecated 5.3.0 This function is no longer needed as support for PHP 5.2-5.3
4124   *                   has been dropped.
4125   * @access private
4126   *
4127   * @param mixed $data Native representation.
4128   * @return bool|int|float|null|string|array Data ready for `json_encode()`.
4129   */
4130  function _wp_json_prepare_data( $data ) {
4131      _deprecated_function( __FUNCTION__, '5.3.0' );
4132      return $data;
4133  }
4134  
4135  /**
4136   * Send a JSON response back to an Ajax request.
4137   *
4138   * @since 3.5.0
4139   * @since 4.7.0 The `$status_code` parameter was added.
4140   * @since 5.6.0 The `$options` parameter was added.
4141   *
4142   * @param mixed $response    Variable (usually an array or object) to encode as JSON,
4143   *                           then print and die.
4144   * @param int   $status_code Optional. The HTTP status code to output. Default null.
4145   * @param int   $options     Optional. Options to be passed to json_encode(). Default 0.
4146   */
4147  function wp_send_json( $response, $status_code = null, $options = 0 ) {
4148      if ( defined( 'REST_REQUEST' ) && REST_REQUEST ) {
4149          _doing_it_wrong(
4150              __FUNCTION__,
4151              sprintf(
4152                  /* translators: 1: WP_REST_Response, 2: WP_Error */
4153                  __( 'Return a %1$s or %2$s object from your callback when using the REST API.' ),
4154                  'WP_REST_Response',
4155                  'WP_Error'
4156              ),
4157              '5.5.0'
4158          );
4159      }
4160  
4161      if ( ! headers_sent() ) {
4162          header( 'Content-Type: application/json; charset=' . get_option( 'blog_charset' ) );
4163          if ( null !== $status_code ) {
4164              status_header( $status_code );
4165          }
4166      }
4167  
4168      echo wp_json_encode( $response, $options );
4169  
4170      if ( wp_doing_ajax() ) {
4171          wp_die(
4172              '',
4173              '',
4174              array(
4175                  'response' => null,
4176              )
4177          );
4178      } else {
4179          die;
4180      }
4181  }
4182  
4183  /**
4184   * Send a JSON response back to an Ajax request, indicating success.
4185   *
4186   * @since 3.5.0
4187   * @since 4.7.0 The `$status_code` parameter was added.
4188   * @since 5.6.0 The `$options` parameter was added.
4189   *
4190   * @param mixed $data        Optional. Data to encode as JSON, then print and die. Default null.
4191   * @param int   $status_code Optional. The HTTP status code to output. Default null.
4192   * @param int   $options     Optional. Options to be passed to json_encode(). Default 0.
4193   */
4194  function wp_send_json_success( $data = null, $status_code = null, $options = 0 ) {
4195      $response = array( 'success' => true );
4196  
4197      if ( isset( $data ) ) {
4198          $response['data'] = $data;
4199      }
4200  
4201      wp_send_json( $response, $status_code, $options );
4202  }
4203  
4204  /**
4205   * Send a JSON response back to an Ajax request, indicating failure.
4206   *
4207   * If the `$data` parameter is a WP_Error object, the errors
4208   * within the object are processed and output as an array of error
4209   * codes and corresponding messages. All other types are output
4210   * without further processing.
4211   *
4212   * @since 3.5.0
4213   * @since 4.1.0 The `$data` parameter is now processed if a WP_Error object is passed in.
4214   * @since 4.7.0 The `$status_code` parameter was added.
4215   * @since 5.6.0 The `$options` parameter was added.
4216   *
4217   * @param mixed $data        Optional. Data to encode as JSON, then print and die. Default null.
4218   * @param int   $status_code Optional. The HTTP status code to output. Default null.
4219   * @param int   $options     Optional. Options to be passed to json_encode(). Default 0.
4220   */
4221  function wp_send_json_error( $data = null, $status_code = null, $options = 0 ) {
4222      $response = array( 'success' => false );
4223  
4224      if ( isset( $data ) ) {
4225          if ( is_wp_error( $data ) ) {
4226              $result = array();
4227              foreach ( $data->errors as $code => $messages ) {
4228                  foreach ( $messages as $message ) {
4229                      $result[] = array(
4230                          'code'    => $code,
4231                          'message' => $message,
4232                      );
4233                  }
4234              }
4235  
4236              $response['data'] = $result;
4237          } else {
4238              $response['data'] = $data;
4239          }
4240      }
4241  
4242      wp_send_json( $response, $status_code, $options );
4243  }
4244  
4245  /**
4246   * Checks that a JSONP callback is a valid JavaScript callback name.
4247   *
4248   * Only allows alphanumeric characters and the dot character in callback
4249   * function names. This helps to mitigate XSS attacks caused by directly
4250   * outputting user input.
4251   *
4252   * @since 4.6.0
4253   *
4254   * @param string $callback Supplied JSONP callback function name.
4255   * @return bool Whether the callback function name is valid.
4256   */
4257  function wp_check_jsonp_callback( $callback ) {
4258      if ( ! is_string( $callback ) ) {
4259          return false;
4260      }
4261  
4262      preg_replace( '/[^\w\.]/', '', $callback, -1, $illegal_char_count );
4263  
4264      return 0 === $illegal_char_count;
4265  }
4266  
4267  /**
4268   * Retrieve the WordPress home page URL.
4269   *
4270   * If the constant named 'WP_HOME' exists, then it will be used and returned
4271   * by the function. This can be used to counter the redirection on your local
4272   * development environment.
4273   *
4274   * @since 2.2.0
4275   * @access private
4276   *
4277   * @see WP_HOME
4278   *
4279   * @param string $url URL for the home location.
4280   * @return string Homepage location.
4281   */
4282  function _config_wp_home( $url = '' ) {
4283      if ( defined( 'WP_HOME' ) ) {
4284          return untrailingslashit( WP_HOME );
4285      }
4286      return $url;
4287  }
4288  
4289  /**
4290   * Retrieve the WordPress site URL.
4291   *
4292   * If the constant named 'WP_SITEURL' is defined, then the value in that
4293   * constant will always be returned. This can be used for debugging a site
4294   * on your localhost while not having to change the database to your URL.
4295   *
4296   * @since 2.2.0
4297   * @access private
4298   *
4299   * @see WP_SITEURL
4300   *
4301   * @param string $url URL to set the WordPress site location.
4302   * @return string The WordPress Site URL.
4303   */
4304  function _config_wp_siteurl( $url = '' ) {
4305      if ( defined( 'WP_SITEURL' ) ) {
4306          return untrailingslashit( WP_SITEURL );
4307      }
4308      return $url;
4309  }
4310  
4311  /**
4312   * Delete the fresh site option.
4313   *
4314   * @since 4.7.0
4315   * @access private
4316   */
4317  function _delete_option_fresh_site() {
4318      update_option( 'fresh_site', '0' );
4319  }
4320  
4321  /**
4322   * Set the localized direction for MCE plugin.
4323   *
4324   * Will only set the direction to 'rtl', if the WordPress locale has
4325   * the text direction set to 'rtl'.
4326   *
4327   * Fills in the 'directionality' setting, enables the 'directionality'
4328   * plugin, and adds the 'ltr' button to 'toolbar1', formerly
4329   * 'theme_advanced_buttons1' array keys. These keys are then returned
4330   * in the $mce_init (TinyMCE settings) array.
4331   *
4332   * @since 2.1.0
4333   * @access private
4334   *
4335   * @param array $mce_init MCE settings array.
4336   * @return array Direction set for 'rtl', if needed by locale.
4337   */
4338  function _mce_set_direction( $mce_init ) {
4339      if ( is_rtl() ) {
4340          $mce_init['directionality'] = 'rtl';
4341          $mce_init['rtl_ui']         = true;
4342  
4343          if ( ! empty( $mce_init['plugins'] ) && strpos( $mce_init['plugins'], 'directionality' ) === false ) {
4344              $mce_init['plugins'] .= ',directionality';
4345          }
4346  
4347          if ( ! empty( $mce_init['toolbar1'] ) && ! preg_match( '/\bltr\b/', $mce_init['toolbar1'] ) ) {
4348              $mce_init['toolbar1'] .= ',ltr';
4349          }
4350      }
4351  
4352      return $mce_init;
4353  }
4354  
4355  
4356  /**
4357   * Convert smiley code to the icon graphic file equivalent.
4358   *
4359   * You can turn off smilies, by going to the write setting screen and unchecking
4360   * the box, or by setting 'use_smilies' option to false or removing the option.
4361   *
4362   * Plugins may override the default smiley list by setting the $wpsmiliestrans
4363   * to an array, with the key the code the blogger types in and the value the
4364   * image file.
4365   *
4366   * The $wp_smiliessearch global is for the regular expression and is set each
4367   * time the function is called.
4368   *
4369   * The full list of smilies can be found in the function and won't be listed in
4370   * the description. Probably should create a Codex page for it, so that it is
4371   * available.
4372   *
4373   * @global array $wpsmiliestrans
4374   * @global array $wp_smiliessearch
4375   *
4376   * @since 2.2.0
4377   */
4378  function smilies_init() {
4379      global $wpsmiliestrans, $wp_smiliessearch;
4380  
4381      // Don't bother setting up smilies if they are disabled.
4382      if ( ! get_option( 'use_smilies' ) ) {
4383          return;
4384      }
4385  
4386      if ( ! isset( $wpsmiliestrans ) ) {
4387          $wpsmiliestrans = array(
4388              ':mrgreen:' => 'mrgreen.png',
4389              ':neutral:' => "\xf0\x9f\x98\x90",
4390              ':twisted:' => "\xf0\x9f\x98\x88",
4391              ':arrow:'   => "\xe2\x9e\xa1",
4392              ':shock:'   => "\xf0\x9f\x98\xaf",
4393              ':smile:'   => "\xf0\x9f\x99\x82",
4394              ':???:'     => "\xf0\x9f\x98\x95",
4395              ':cool:'    => "\xf0\x9f\x98\x8e",
4396              ':evil:'    => "\xf0\x9f\x91\xbf",
4397              ':grin:'    => "\xf0\x9f\x98\x80",
4398              ':idea:'    => "\xf0\x9f\x92\xa1",
4399              ':oops:'    => "\xf0\x9f\x98\xb3",
4400              ':razz:'    => "\xf0\x9f\x98\x9b",
4401              ':roll:'    => "\xf0\x9f\x99\x84",
4402              ':wink:'    => "\xf0\x9f\x98\x89",
4403              ':cry:'     => "\xf0\x9f\x98\xa5",
4404              ':eek:'     => "\xf0\x9f\x98\xae",
4405              ':lol:'     => "\xf0\x9f\x98\x86",
4406              ':mad:'     => "\xf0\x9f\x98\xa1",
4407              ':sad:'     => "\xf0\x9f\x99\x81",
4408              '8-)'       => "\xf0\x9f\x98\x8e",
4409              '8-O'       => "\xf0\x9f\x98\xaf",
4410              ':-('       => "\xf0\x9f\x99\x81",
4411              ':-)'       => "\xf0\x9f\x99\x82",
4412              ':-?'       => "\xf0\x9f\x98\x95",
4413              ':-D'       => "\xf0\x9f\x98\x80",
4414              ':-P'       => "\xf0\x9f\x98\x9b",
4415              ':-o'       => "\xf0\x9f\x98\xae",
4416              ':-x'       => "\xf0\x9f\x98\xa1",
4417              ':-|'       => "\xf0\x9f\x98\x90",
4418              ';-)'       => "\xf0\x9f\x98\x89",
4419              // This one transformation breaks regular text with frequency.
4420              //     '8)' => "\xf0\x9f\x98\x8e",
4421              '8O'        => "\xf0\x9f\x98\xaf",
4422              ':('        => "\xf0\x9f\x99\x81",
4423              ':)'        => "\xf0\x9f\x99\x82",
4424              ':?'        => "\xf0\x9f\x98\x95",
4425              ':D'        => "\xf0\x9f\x98\x80",
4426              ':P'        => "\xf0\x9f\x98\x9b",
4427              ':o'        => "\xf0\x9f\x98\xae",
4428              ':x'        => "\xf0\x9f\x98\xa1",
4429              ':|'        => "\xf0\x9f\x98\x90",
4430              ';)'        => "\xf0\x9f\x98\x89",
4431              ':!:'       => "\xe2\x9d\x97",
4432              ':?:'       => "\xe2\x9d\x93",
4433          );
4434      }
4435  
4436      /**
4437       * Filters all the smilies.
4438       *
4439       * This filter must be added before `smilies_init` is run, as
4440       * it is normally only run once to setup the smilies regex.
4441       *
4442       * @since 4.7.0
4443       *
4444       * @param string[] $wpsmiliestrans List of the smilies' hexadecimal representations, keyed by their smily code.
4445       */
4446      $wpsmiliestrans = apply_filters( 'smilies', $wpsmiliestrans );
4447  
4448      if ( count( $wpsmiliestrans ) == 0 ) {
4449          return;
4450      }
4451  
4452      /*
4453       * NOTE: we sort the smilies in reverse key order. This is to make sure
4454       * we match the longest possible smilie (:???: vs :?) as the regular
4455       * expression used below is first-match
4456       */
4457      krsort( $wpsmiliestrans );
4458  
4459      $spaces = wp_spaces_regexp();
4460  
4461      // Begin first "subpattern".
4462      $wp_smiliessearch = '/(?<=' . $spaces . '|^)';
4463  
4464      $subchar = '';
4465      foreach ( (array) $wpsmiliestrans as $smiley => $img ) {
4466          $firstchar = substr( $smiley, 0, 1 );
4467          $rest      = substr( $smiley, 1 );
4468  
4469          // New subpattern?
4470          if ( $firstchar != $subchar ) {
4471              if ( '' !== $subchar ) {
4472                  $wp_smiliessearch .= ')(?=' . $spaces . '|$)';  // End previous "subpattern".
4473                  $wp_smiliessearch .= '|(?<=' . $spaces . '|^)'; // Begin another "subpattern".
4474              }
4475              $subchar           = $firstchar;
4476              $wp_smiliessearch .= preg_quote( $firstchar, '/' ) . '(?:';
4477          } else {
4478              $wp_smiliessearch .= '|';
4479          }
4480          $wp_smiliessearch .= preg_quote( $rest, '/' );
4481      }
4482  
4483      $wp_smiliessearch .= ')(?=' . $spaces . '|$)/m';
4484  
4485  }
4486  
4487  /**
4488   * Merges user defined arguments into defaults array.
4489   *
4490   * This function is used throughout WordPress to allow for both string or array
4491   * to be merged into another array.
4492   *
4493   * @since 2.2.0
4494   * @since 2.3.0 `$args` can now also be an object.
4495   *
4496   * @param string|array|object $args     Value to merge with $defaults.
4497   * @param array               $defaults Optional. Array that serves as the defaults.
4498   *                                      Default empty array.
4499   * @return array Merged user defined values with defaults.
4500   */
4501  function wp_parse_args( $args, $defaults = array() ) {
4502      if ( is_object( $args ) ) {
4503          $parsed_args = get_object_vars( $args );
4504      } elseif ( is_array( $args ) ) {
4505          $parsed_args =& $args;
4506      } else {
4507          wp_parse_str( $args, $parsed_args );
4508      }
4509  
4510      if ( is_array( $defaults ) && $defaults ) {
4511          return array_merge( $defaults, $parsed_args );
4512      }
4513      return $parsed_args;
4514  }
4515  
4516  /**
4517   * Converts a comma- or space-separated list of scalar values to an array.
4518   *
4519   * @since 5.1.0
4520   *
4521   * @param array|string $list List of values.
4522   * @return array Array of values.
4523   */
4524  function wp_parse_list( $list ) {
4525      if ( ! is_array( $list ) ) {
4526          return preg_split( '/[\s,]+/', $list, -1, PREG_SPLIT_NO_EMPTY );
4527      }
4528  
4529      return $list;
4530  }
4531  
4532  /**
4533   * Cleans up an array, comma- or space-separated list of IDs.
4534   *
4535   * @since 3.0.0
4536   *
4537   * @param array|string $list List of IDs.
4538   * @return int[] Sanitized array of IDs.
4539   */
4540  function wp_parse_id_list( $list ) {
4541      $list = wp_parse_list( $list );
4542  
4543      return array_unique( array_map( 'absint', $list ) );
4544  }
4545  
4546  /**
4547   * Cleans up an array, comma- or space-separated list of slugs.
4548   *
4549   * @since 4.7.0
4550   *
4551   * @param array|string $list List of slugs.
4552   * @return string[] Sanitized array of slugs.
4553   */
4554  function wp_parse_slug_list( $list ) {
4555      $list = wp_parse_list( $list );
4556  
4557      return array_unique( array_map( 'sanitize_title', $list ) );
4558  }
4559  
4560  /**
4561   * Extract a slice of an array, given a list of keys.
4562   *
4563   * @since 3.1.0
4564   *
4565   * @param array $array The original array.
4566   * @param array $keys  The list of keys.
4567   * @return array The array slice.
4568   */
4569  function wp_array_slice_assoc( $array, $keys ) {
4570      $slice = array();
4571  
4572      foreach ( $keys as $key ) {
4573          if ( isset( $array[ $key ] ) ) {
4574              $slice[ $key ] = $array[ $key ];
4575          }
4576      }
4577  
4578      return $slice;
4579  }
4580  
4581  /**
4582   * Accesses an array in depth based on a path of keys.
4583   *
4584   * It is the PHP equivalent of JavaScript's `lodash.get()` and mirroring it may help other components
4585   * retain some symmetry between client and server implementations.
4586   *
4587   * Example usage:
4588   *
4589   *     $array = array(
4590   *         'a' => array(
4591   *             'b' => array(
4592   *                 'c' => 1,
4593   *             ),
4594   *         ),
4595   *     );
4596   *     _wp_array_get( $array, array( 'a', 'b', 'c' );
4597   *
4598   * @internal
4599   *
4600   * @since 5.6.0
4601   * @access private
4602   *
4603   * @param array $array   An array from which we want to retrieve some information.
4604   * @param array $path    An array of keys describing the path with which to retrieve information.
4605   * @param mixed $default The return value if the path does not exist within the array,
4606   *                       or if `$array` or `$path` are not arrays.
4607   * @return mixed The value from the path specified.
4608   */
4609  function _wp_array_get( $array, $path, $default = null ) {
4610      // Confirm $path is valid.
4611      if ( ! is_array( $path ) || 0 === count( $path ) ) {
4612          return $default;
4613      }
4614  
4615      foreach ( $path as $path_element ) {
4616          if (
4617              ! is_array( $array ) ||
4618              ( ! is_string( $path_element ) && ! is_integer( $path_element ) && ! is_null( $path_element ) ) ||
4619              ! array_key_exists( $path_element, $array )
4620          ) {
4621              return $default;
4622          }
4623          $array = $array[ $path_element ];
4624      }
4625  
4626      return $array;
4627  }
4628  
4629  /**
4630   * Determines if the variable is a numeric-indexed array.
4631   *
4632   * @since 4.4.0
4633   *
4634   * @param mixed $data Variable to check.
4635   * @return bool Whether the variable is a list.
4636   */
4637  function wp_is_numeric_array( $data ) {
4638      if ( ! is_array( $data ) ) {
4639          return false;
4640      }
4641  
4642      $keys        = array_keys( $data );
4643      $string_keys = array_filter( $keys, 'is_string' );
4644  
4645      return count( $string_keys ) === 0;
4646  }
4647  
4648  /**
4649   * Filters a list of objects, based on a set of key => value arguments.
4650   *
4651   * @since 3.0.0
4652   * @since 4.7.0 Uses `WP_List_Util` class.
4653   *
4654   * @param array       $list     An array of objects to filter
4655   * @param array       $args     Optional. An array of key => value arguments to match
4656   *                              against each object. Default empty array.
4657   * @param string      $operator Optional. The logical operation to perform. 'or' means
4658   *                              only one element from the array needs to match; 'and'
4659   *                              means all elements must match; 'not' means no elements may
4660   *                              match. Default 'and'.
4661   * @param bool|string $field    A field from the object to place instead of the entire object.
4662   *                              Default false.
4663   * @return array A list of objects or object fields.
4664   */
4665  function wp_filter_object_list( $list, $args = array(), $operator = 'and', $field = false ) {
4666      if ( ! is_array( $list ) ) {
4667          return array();
4668      }
4669  
4670      $util = new WP_List_Util( $list );
4671  
4672      $util->filter( $args, $operator );
4673  
4674      if ( $field ) {
4675          $util->pluck( $field );
4676      }
4677  
4678      return $util->get_output();
4679  }
4680  
4681  /**
4682   * Filters a list of objects, based on a set of key => value arguments.
4683   *
4684   * @since 3.1.0
4685   * @since 4.7.0 Uses `WP_List_Util` class.
4686   *
4687   * @param array  $list     An array of objects to filter.
4688   * @param array  $args     Optional. An array of key => value arguments to match
4689   *                         against each object. Default empty array.
4690   * @param string $operator Optional. The logical operation to perform. 'AND' means
4691   *                         all elements from the array must match. 'OR' means only
4692   *                         one element needs to match. 'NOT' means no elements may
4693   *                         match. Default 'AND'.
4694   * @return array Array of found values.
4695   */
4696  function wp_list_filter( $list, $args = array(), $operator = 'AND' ) {
4697      if ( ! is_array( $list ) ) {
4698          return array();
4699      }
4700  
4701      $util = new WP_List_Util( $list );
4702      return $util->filter( $args, $operator );
4703  }
4704  
4705  /**
4706   * Pluck a certain field out of each object in a list.
4707   *
4708   * This has the same functionality and prototype of
4709   * array_column() (PHP 5.5) but also supports objects.
4710   *
4711   * @since 3.1.0
4712   * @since 4.0.0 $index_key parameter added.
4713   * @since 4.7.0 Uses `WP_List_Util` class.
4714   *
4715   * @param array      $list      List of objects or arrays
4716   * @param int|string $field     Field from the object to place instead of the entire object
4717   * @param int|string $index_key Optional. Field from the object to use as keys for the new array.
4718   *                              Default null.
4719   * @return array Array of found values. If `$index_key` is set, an array of found values with keys
4720   *               corresponding to `$index_key`. If `$index_key` is null, array keys from the original
4721   *               `$list` will be preserved in the results.
4722   */
4723  function wp_list_pluck( $list, $field, $index_key = null ) {
4724      $util = new WP_List_Util( $list );
4725      return $util->pluck( $field, $index_key );
4726  }
4727  
4728  /**
4729   * Sorts a list of objects, based on one or more orderby arguments.
4730   *
4731   * @since 4.7.0
4732   *
4733   * @param array        $list          An array of objects to sort.
4734   * @param string|array $orderby       Optional. Either the field name to order by or an array
4735   *                                    of multiple orderby fields as $orderby => $order.
4736   * @param string       $order         Optional. Either 'ASC' or 'DESC'. Only used if $orderby
4737   *                                    is a string.
4738   * @param bool         $preserve_keys Optional. Whether to preserve keys. Default false.
4739   * @return array The sorted array.
4740   */
4741  function wp_list_sort( $list, $orderby = array(), $order = 'ASC', $preserve_keys = false ) {
4742      if ( ! is_array( $list ) ) {
4743          return array();
4744      }
4745  
4746      $util = new WP_List_Util( $list );
4747      return $util->sort( $orderby, $order, $preserve_keys );
4748  }
4749  
4750  /**
4751   * Determines if Widgets library should be loaded.
4752   *
4753   * Checks to make sure that the widgets library hasn't already been loaded.
4754   * If it hasn't, then it will load the widgets library and run an action hook.
4755   *
4756   * @since 2.2.0
4757   */
4758  function wp_maybe_load_widgets() {
4759      /**
4760       * Filters whether to load the Widgets library.
4761       *
4762       * Returning a falsey value from the filter will effectively short-circuit
4763       * the Widgets library from loading.
4764       *
4765       * @since 2.8.0
4766       *
4767       * @param bool $wp_maybe_load_widgets Whether to load the Widgets library.
4768       *                                    Default true.
4769       */
4770      if ( ! apply_filters( 'load_default_widgets', true ) ) {
4771          return;
4772      }
4773  
4774      require_once  ABSPATH . WPINC . '/default-widgets.php';
4775  
4776      add_action( '_admin_menu', 'wp_widgets_add_menu' );
4777  }
4778  
4779  /**
4780   * Append the Widgets menu to the themes main menu.
4781   *
4782   * @since 2.2.0
4783   *
4784   * @global array $submenu
4785   */
4786  function wp_widgets_add_menu() {
4787      global $submenu;
4788  
4789      if ( ! current_theme_supports( 'widgets' ) ) {
4790          return;
4791      }
4792  
4793      $submenu['themes.php'][7] = array( __( 'Widgets' ), 'edit_theme_options', 'widgets.php' );
4794      ksort( $submenu['themes.php'], SORT_NUMERIC );
4795  }
4796  
4797  /**
4798   * Flush all output buffers for PHP 5.2.
4799   *
4800   * Make sure all output buffers are flushed before our singletons are destroyed.
4801   *
4802   * @since 2.2.0
4803   */
4804  function wp_ob_end_flush_all() {
4805      $levels = ob_get_level();
4806      for ( $i = 0; $i < $levels; $i++ ) {
4807          ob_end_flush();
4808      }
4809  }
4810  
4811  /**
4812   * Load custom DB error or display WordPress DB error.
4813   *
4814   * If a file exists in the wp-content directory named db-error.php, then it will
4815   * be loaded instead of displaying the WordPress DB error. If it is not found,
4816   * then the WordPress DB error will be displayed instead.
4817   *
4818   * The WordPress DB error sets the HTTP status header to 500 to try to prevent
4819   * search engines from caching the message. Custom DB messages should do the
4820   * same.
4821   *
4822   * This function was backported to WordPress 2.3.2, but originally was added
4823   * in WordPress 2.5.0.
4824   *
4825   * @since 2.3.2
4826   *
4827   * @global wpdb $wpdb WordPress database abstraction object.
4828   */
4829  function dead_db() {
4830      global $wpdb;
4831  
4832      wp_load_translations_early();
4833  
4834      // Load custom DB error template, if present.
4835      if ( file_exists( WP_CONTENT_DIR . '/db-error.php' ) ) {
4836          require_once WP_CONTENT_DIR . '/db-error.php';
4837          die();
4838      }
4839  
4840      // If installing or in the admin, provide the verbose message.
4841      if ( wp_installing() || defined( 'WP_ADMIN' ) ) {
4842          wp_die( $wpdb->error );
4843      }
4844  
4845      // Otherwise, be terse.
4846      wp_die( '<h1>' . __( 'Error establishing a database connection' ) . '</h1>', __( 'Database Error' ) );
4847  }
4848  
4849  /**
4850   * Convert a value to non-negative integer.
4851   *
4852   * @since 2.5.0
4853   *
4854   * @param mixed $maybeint Data you wish to have converted to a non-negative integer.
4855   * @return int A non-negative integer.
4856   */
4857  function absint( $maybeint ) {
4858      return abs( (int) $maybeint );
4859  }
4860  
4861  /**
4862   * Mark a function as deprecated and inform when it has been used.
4863   *
4864   * There is a {@see 'hook deprecated_function_run'} that will be called that can be used
4865   * to get the backtrace up to what file and function called the deprecated
4866   * function.
4867   *
4868   * The current behavior is to trigger a user error if `WP_DEBUG` is true.
4869   *
4870   * This function is to be used in every function that is deprecated.
4871   *
4872   * @since 2.5.0
4873   * @since 5.4.0 This function is no longer marked as "private".
4874   * @since 5.4.0 The error type is now classified as E_USER_DEPRECATED (used to default to E_USER_NOTICE).
4875   *
4876   * @param string $function    The function that was called.
4877   * @param string $version     The version of WordPress that deprecated the function.
4878   * @param string $replacement Optional. The function that should have been called. Default empty.
4879   */
4880  function _deprecated_function( $function, $version, $replacement = '' ) {
4881  
4882      /**
4883       * Fires when a deprecated function is called.
4884       *
4885       * @since 2.5.0
4886       *
4887       * @param string $function    The function that was called.
4888       * @param string $replacement The function that should have been called.
4889       * @param string $version     The version of WordPress that deprecated the function.
4890       */
4891      do_action( 'deprecated_function_run', $function, $replacement, $version );
4892  
4893      /**
4894       * Filters whether to trigger an error for deprecated functions.
4895       *
4896       * @since 2.5.0
4897       *
4898       * @param bool $trigger Whether to trigger the error for deprecated functions. Default true.
4899       */
4900      if ( WP_DEBUG && apply_filters( 'deprecated_function_trigger_error', true ) ) {
4901          if ( function_exists( '__' ) ) {
4902              if ( $replacement ) {
4903                  trigger_error(
4904                      sprintf(
4905                          /* translators: 1: PHP function name, 2: Version number, 3: Alternative function name. */
4906                          __( '%1$s is <strong>deprecated</strong> since version %2$s! Use %3$s instead.' ),
4907                          $function,
4908                          $version,
4909                          $replacement
4910                      ),
4911                      E_USER_DEPRECATED
4912                  );
4913              } else {
4914                  trigger_error(
4915                      sprintf(
4916                          /* translators: 1: PHP function name, 2: Version number. */
4917                          __( '%1$s is <strong>deprecated</strong> since version %2$s with no alternative available.' ),
4918                          $function,
4919                          $version
4920                      ),
4921                      E_USER_DEPRECATED
4922                  );
4923              }
4924          } else {
4925              if ( $replacement ) {
4926                  trigger_error(
4927                      sprintf(
4928                          '%1$s is <strong>deprecated</strong> since version %2$s! Use %3$s instead.',
4929                          $function,
4930                          $version,
4931                          $replacement
4932                      ),
4933                      E_USER_DEPRECATED
4934                  );
4935              } else {
4936                  trigger_error(
4937                      sprintf(
4938                          '%1$s is <strong>deprecated</strong> since version %2$s with no alternative available.',
4939                          $function,
4940                          $version
4941                      ),
4942                      E_USER_DEPRECATED
4943                  );
4944              }
4945          }
4946      }
4947  }
4948  
4949  /**
4950   * Marks a constructor as deprecated and informs when it has been used.
4951   *
4952   * Similar to _deprecated_function(), but with different strings. Used to
4953   * remove PHP4 style constructors.
4954   *
4955   * The current behavior is to trigger a user error if `WP_DEBUG` is true.
4956   *
4957   * This function is to be used in every PHP4 style constructor method that is deprecated.
4958   *
4959   * @since 4.3.0
4960   * @since 4.5.0 Added the `$parent_class` parameter.
4961   * @since 5.4.0 This function is no longer marked as "private".
4962   * @since 5.4.0 The error type is now classified as E_USER_DEPRECATED (used to default to E_USER_NOTICE).
4963   *
4964   * @param string $class        The class containing the deprecated constructor.
4965   * @param string $version      The version of WordPress that deprecated the function.
4966   * @param string $parent_class Optional. The parent class calling the deprecated constructor.
4967   *                             Default empty string.
4968   */
4969  function _deprecated_constructor( $class, $version, $parent_class = '' ) {
4970  
4971      /**
4972       * Fires when a deprecated constructor is called.
4973       *
4974       * @since 4.3.0
4975       * @since 4.5.0 Added the `$parent_class` parameter.
4976       *
4977       * @param string $class        The class containing the deprecated constructor.
4978       * @param string $version      The version of WordPress that deprecated the function.
4979       * @param string $parent_class The parent class calling the deprecated constructor.
4980       */
4981      do_action( 'deprecated_constructor_run', $class, $version, $parent_class );
4982  
4983      /**
4984       * Filters whether to trigger an error for deprecated functions.
4985       *
4986       * `WP_DEBUG` must be true in addition to the filter evaluating to true.
4987       *
4988       * @since 4.3.0
4989       *
4990       * @param bool $trigger Whether to trigger the error for deprecated functions. Default true.
4991       */
4992      if ( WP_DEBUG && apply_filters( 'deprecated_constructor_trigger_error', true ) ) {
4993          if ( function_exists( '__' ) ) {
4994              if ( $parent_class ) {
4995                  trigger_error(
4996                      sprintf(
4997                          /* translators: 1: PHP class name, 2: PHP parent class name, 3: Version number, 4: __construct() method. */
4998                          __( 'The called constructor method for %1$s in %2$s is <strong>deprecated</strong> since version %3$s! Use %4$s instead.' ),
4999                          $class,
5000                          $parent_class,
5001                          $version,
5002                          '<code>__construct()</code>'
5003                      ),
5004                      E_USER_DEPRECATED
5005                  );
5006              } else {
5007                  trigger_error(
5008                      sprintf(
5009                          /* translators: 1: PHP class name, 2: Version number, 3: __construct() method. */
5010                          __( 'The called constructor method for %1$s is <strong>deprecated</strong> since version %2$s! Use %3$s instead.' ),
5011                          $class,
5012                          $version,
5013                          '<code>__construct()</code>'
5014                      ),
5015                      E_USER_DEPRECATED
5016                  );
5017              }
5018          } else {
5019              if ( $parent_class ) {
5020                  trigger_error(
5021                      sprintf(
5022                          'The called constructor method for %1$s in %2$s is <strong>deprecated</strong> since version %3$s! Use %4$s instead.',
5023                          $class,
5024                          $parent_class,
5025                          $version,
5026                          '<code>__construct()</code>'
5027                      ),
5028                      E_USER_DEPRECATED
5029                  );
5030              } else {
5031                  trigger_error(
5032                      sprintf(
5033                          'The called constructor method for %1$s is <strong>deprecated</strong> since version %2$s! Use %3$s instead.',
5034                          $class,
5035                          $version,
5036                          '<code>__construct()</code>'
5037                      ),
5038                      E_USER_DEPRECATED
5039                  );
5040              }
5041          }
5042      }
5043  
5044  }
5045  
5046  /**
5047   * Mark a file as deprecated and inform when it has been used.
5048   *
5049   * There is a hook {@see 'deprecated_file_included'} that will be called that can be used
5050   * to get the backtrace up to what file and function included the deprecated
5051   * file.
5052   *
5053   * The current behavior is to trigger a user error if `WP_DEBUG` is true.
5054   *
5055   * This function is to be used in every file that is deprecated.
5056   *
5057   * @since 2.5.0
5058   * @since 5.4.0 This function is no longer marked as "private".
5059   * @since 5.4.0 The error type is now classified as E_USER_DEPRECATED (used to default to E_USER_NOTICE).
5060   *
5061   * @param string $file        The file that was included.
5062   * @param string $version     The version of WordPress that deprecated the file.
5063   * @param string $replacement Optional. The file that should have been included based on ABSPATH.
5064   *                            Default empty.
5065   * @param string $message     Optional. A message regarding the change. Default empty.
5066   */
5067  function _deprecated_file( $file, $version, $replacement = '', $message = '' ) {
5068  
5069      /**
5070       * Fires when a deprecated file is called.
5071       *
5072       * @since 2.5.0
5073       *
5074       * @param string $file        The file that was called.
5075       * @param string $replacement The file that should have been included based on ABSPATH.
5076       * @param string $version     The version of WordPress that deprecated the file.
5077       * @param string $message     A message regarding the change.
5078       */
5079      do_action( 'deprecated_file_included', $file, $replacement, $version, $message );
5080  
5081      /**
5082       * Filters whether to trigger an error for deprecated files.
5083       *
5084       * @since 2.5.0
5085       *
5086       * @param bool $trigger Whether to trigger the error for deprecated files. Default true.
5087       */
5088      if ( WP_DEBUG && apply_filters( 'deprecated_file_trigger_error', true ) ) {
5089          $message = empty( $message ) ? '' : ' ' . $message;
5090  
5091          if ( function_exists( '__' ) ) {
5092              if ( $replacement ) {
5093                  trigger_error(
5094                      sprintf(
5095                          /* translators: 1: PHP file name, 2: Version number, 3: Alternative file name. */
5096                          __( '%1$s is <strong>deprecated</strong> since version %2$s! Use %3$s instead.' ),
5097                          $file,
5098                          $version,
5099                          $replacement
5100                      ) . $message,
5101                      E_USER_DEPRECATED
5102                  );
5103              } else {
5104                  trigger_error(
5105                      sprintf(
5106                          /* translators: 1: PHP file name, 2: Version number. */
5107                          __( '%1$s is <strong>deprecated</strong> since version %2$s with no alternative available.' ),
5108                          $file,
5109                          $version
5110                      ) . $message,
5111                      E_USER_DEPRECATED
5112                  );
5113              }
5114          } else {
5115              if ( $replacement ) {
5116                  trigger_error(
5117                      sprintf(
5118                          '%1$s is <strong>deprecated</strong> since version %2$s! Use %3$s instead.',
5119                          $file,
5120                          $version,
5121                          $replacement
5122                      ) . $message,
5123                      E_USER_DEPRECATED
5124                  );
5125              } else {
5126                  trigger_error(
5127                      sprintf(
5128                          '%1$s is <strong>deprecated</strong> since version %2$s with no alternative available.',
5129                          $file,
5130                          $version
5131                      ) . $message,
5132                      E_USER_DEPRECATED
5133                  );
5134              }
5135          }
5136      }
5137  }
5138  /**
5139   * Mark a function argument as deprecated and inform when it has been used.
5140   *
5141   * This function is to be used whenever a deprecated function argument is used.
5142   * Before this function is called, the argument must be checked for whether it was
5143   * used by comparing it to its default value or evaluating whether it is empty.
5144   * For example:
5145   *
5146   *     if ( ! empty( $deprecated ) ) {
5147   *         _deprecated_argument( __FUNCTION__, '3.0.0' );
5148   *     }
5149   *
5150   * There is a hook deprecated_argument_run that will be called that can be used
5151   * to get the backtrace up to what file and function used the deprecated
5152   * argument.
5153   *
5154   * The current behavior is to trigger a user error if WP_DEBUG is true.
5155   *
5156   * @since 3.0.0
5157   * @since 5.4.0 This function is no longer marked as "private".
5158   * @since 5.4.0 The error type is now classified as E_USER_DEPRECATED (used to default to E_USER_NOTICE).
5159   *
5160   * @param string $function The function that was called.
5161   * @param string $version  The version of WordPress that deprecated the argument used.
5162   * @param string $message  Optional. A message regarding the change. Default empty.
5163   */
5164  function _deprecated_argument( $function, $version, $message = '' ) {
5165  
5166      /**
5167       * Fires when a deprecated argument is called.
5168       *
5169       * @since 3.0.0
5170       *
5171       * @param string $function The function that was called.
5172       * @param string $message  A message regarding the change.
5173       * @param string $version  The version of WordPress that deprecated the argument used.
5174       */
5175      do_action( 'deprecated_argument_run', $function, $message, $version );
5176  
5177      /**
5178       * Filters whether to trigger an error for deprecated arguments.
5179       *
5180       * @since 3.0.0
5181       *
5182       * @param bool $trigger Whether to trigger the error for deprecated arguments. Default true.
5183       */
5184      if ( WP_DEBUG && apply_filters( 'deprecated_argument_trigger_error', true ) ) {
5185          if ( function_exists( '__' ) ) {
5186              if ( $message ) {
5187                  trigger_error(
5188                      sprintf(
5189                          /* translators: 1: PHP function name, 2: Version number, 3: Optional message regarding the change. */
5190                          __( '%1$s was called with an argument that is <strong>deprecated</strong> since version %2$s! %3$s' ),
5191                          $function,
5192                          $version,
5193                          $message
5194                      ),
5195                      E_USER_DEPRECATED
5196                  );
5197              } else {
5198                  trigger_error(
5199                      sprintf(
5200                          /* translators: 1: PHP function name, 2: Version number. */
5201                          __( '%1$s was called with an argument that is <strong>deprecated</strong> since version %2$s with no alternative available.' ),
5202                          $function,
5203                          $version
5204                      ),
5205                      E_USER_DEPRECATED
5206                  );
5207              }
5208          } else {
5209              if ( $message ) {
5210                  trigger_error(
5211                      sprintf(
5212                          '%1$s was called with an argument that is <strong>deprecated</strong> since version %2$s! %3$s',
5213                          $function,
5214                          $version,
5215                          $message
5216                      ),
5217                      E_USER_DEPRECATED
5218                  );
5219              } else {
5220                  trigger_error(
5221                      sprintf(
5222                          '%1$s was called with an argument that is <strong>deprecated</strong> since version %2$s with no alternative available.',
5223                          $function,
5224                          $version
5225                      ),
5226                      E_USER_DEPRECATED
5227                  );
5228              }
5229          }
5230      }
5231  }
5232  
5233  /**
5234   * Marks a deprecated action or filter hook as deprecated and throws a notice.
5235   *
5236   * Use the {@see 'deprecated_hook_run'} action to get the backtrace describing where
5237   * the deprecated hook was called.
5238   *
5239   * Default behavior is to trigger a user error if `WP_DEBUG` is true.
5240   *
5241   * This function is called by the do_action_deprecated() and apply_filters_deprecated()
5242   * functions, and so generally does not need to be called directly.
5243   *
5244   * @since 4.6.0
5245   * @since 5.4.0 The error type is now classified as E_USER_DEPRECATED (used to default to E_USER_NOTICE).
5246   * @access private
5247   *
5248   * @param string $hook        The hook that was used.
5249   * @param string $version     The version of WordPress that deprecated the hook.
5250   * @param string $replacement Optional. The hook that should have been used. Default empty.
5251   * @param string $message     Optional. A message regarding the change. Default empty.
5252   */
5253  function _deprecated_hook( $hook, $version, $replacement = '', $message = '' ) {
5254      /**
5255       * Fires when a deprecated hook is called.
5256       *
5257       * @since 4.6.0
5258       *
5259       * @param string $hook        The hook that was called.
5260       * @param string $replacement The hook that should be used as a replacement.
5261       * @param string $version     The version of WordPress that deprecated the argument used.
5262       * @param string $message     A message regarding the change.
5263       */
5264      do_action( 'deprecated_hook_run', $hook, $replacement, $version, $message );
5265  
5266      /**
5267       * Filters whether to trigger deprecated hook errors.
5268       *
5269       * @since 4.6.0
5270       *
5271       * @param bool $trigger Whether to trigger deprecated hook errors. Requires
5272       *                      `WP_DEBUG` to be defined true.
5273       */
5274      if ( WP_DEBUG && apply_filters( 'deprecated_hook_trigger_error', true ) ) {
5275          $message = empty( $message ) ? '' : ' ' . $message;
5276  
5277          if ( $replacement ) {
5278              trigger_error(
5279                  sprintf(
5280                      /* translators: 1: WordPress hook name, 2: Version number, 3: Alternative hook name. */
5281                      __( '%1$s is <strong>deprecated</strong> since version %2$s! Use %3$s instead.' ),
5282                      $hook,
5283                      $version,
5284                      $replacement
5285                  ) . $message,
5286                  E_USER_DEPRECATED
5287              );
5288          } else {
5289              trigger_error(
5290                  sprintf(
5291                      /* translators: 1: WordPress hook name, 2: Version number. */
5292                      __( '%1$s is <strong>deprecated</strong> since version %2$s with no alternative available.' ),
5293                      $hook,
5294                      $version
5295                  ) . $message,
5296                  E_USER_DEPRECATED
5297              );
5298          }
5299      }
5300  }
5301  
5302  /**
5303   * Mark something as being incorrectly called.
5304   *
5305   * There is a hook {@see 'doing_it_wrong_run'} that will be called that can be used
5306   * to get the backtrace up to what file and function called the deprecated
5307   * function.
5308   *
5309   * The current behavior is to trigger a user error if `WP_DEBUG` is true.
5310   *
5311   * @since 3.1.0
5312   * @since 5.4.0 This function is no longer marked as "private".
5313   *
5314   * @param string $function The function that was called.
5315   * @param string $message  A message explaining what has been done incorrectly.
5316   * @param string $version  The version of WordPress where the message was added.
5317   */
5318  function _doing_it_wrong( $function, $message, $version ) {
5319  
5320      /**
5321       * Fires when the given function is being used incorrectly.
5322       *
5323       * @since 3.1.0
5324       *
5325       * @param string $function The function that was called.
5326       * @param string $message  A message explaining what has been done incorrectly.
5327       * @param string $version  The version of WordPress where the message was added.
5328       */
5329      do_action( 'doing_it_wrong_run', $function, $message, $version );
5330  
5331      /**
5332       * Filters whether to trigger an error for _doing_it_wrong() calls.
5333       *
5334       * @since 3.1.0
5335       * @since 5.1.0 Added the $function, $message and $version parameters.
5336       *
5337       * @param bool   $trigger  Whether to trigger the error for _doing_it_wrong() calls. Default true.
5338       * @param string $function The function that was called.
5339       * @param string $message  A message explaining what has been done incorrectly.
5340       * @param string $version  The version of WordPress where the message was added.
5341       */
5342      if ( WP_DEBUG && apply_filters( 'doing_it_wrong_trigger_error', true, $function, $message, $version ) ) {
5343          if ( function_exists( '__' ) ) {
5344              if ( $version ) {
5345                  /* translators: %s: Version number. */
5346                  $version = sprintf( __( '(This message was added in version %s.)' ), $version );
5347              }
5348  
5349              $message .= ' ' . sprintf(
5350                  /* translators: %s: Documentation URL. */
5351                  __( 'Please see <a href="%s">Debugging in WordPress</a> for more information.' ),
5352                  __( 'https://wordpress.org/support/article/debugging-in-wordpress/' )
5353              );
5354  
5355              trigger_error(
5356                  sprintf(
5357                      /* translators: Developer debugging message. 1: PHP function name, 2: Explanatory message, 3: WordPress version number. */
5358                      __( '%1$s was called <strong>incorrectly</strong>. %2$s %3$s' ),
5359                      $function,
5360                      $message,
5361                      $version
5362                  ),
5363                  E_USER_NOTICE
5364              );
5365          } else {
5366              if ( $version ) {
5367                  $version = sprintf( '(This message was added in version %s.)', $version );
5368              }
5369  
5370              $message .= sprintf(
5371                  ' Please see <a href="%s">Debugging in WordPress</a> for more information.',
5372                  'https://wordpress.org/support/article/debugging-in-wordpress/'
5373              );
5374  
5375              trigger_error(
5376                  sprintf(
5377                      '%1$s was called <strong>incorrectly</strong>. %2$s %3$s',
5378                      $function,
5379                      $message,
5380                      $version
5381                  ),
5382                  E_USER_NOTICE
5383              );
5384          }
5385      }
5386  }
5387  
5388  /**
5389   * Is the server running earlier than 1.5.0 version of lighttpd?
5390   *
5391   * @since 2.5.0
5392   *
5393   * @return bool Whether the server is running lighttpd < 1.5.0.
5394   */
5395  function is_lighttpd_before_150() {
5396      $server_parts    = explode( '/', isset( $_SERVER['SERVER_SOFTWARE'] ) ? $_SERVER['SERVER_SOFTWARE'] : '' );
5397      $server_parts[1] = isset( $server_parts[1] ) ? $server_parts[1] : '';
5398  
5399      return ( 'lighttpd' === $server_parts[0] && -1 == version_compare( $server_parts[1], '1.5.0' ) );
5400  }
5401  
5402  /**
5403   * Does the specified module exist in the Apache config?
5404   *
5405   * @since 2.5.0
5406   *
5407   * @global bool $is_apache
5408   *
5409   * @param string $mod     The module, e.g. mod_rewrite.
5410   * @param bool   $default Optional. The default return value if the module is not found. Default false.
5411   * @return bool Whether the specified module is loaded.
5412   */
5413  function apache_mod_loaded( $mod, $default = false ) {
5414      global $is_apache;
5415  
5416      if ( ! $is_apache ) {
5417          return false;
5418      }
5419  
5420      if ( function_exists( 'apache_get_modules' ) ) {
5421          $mods = apache_get_modules();
5422          if ( in_array( $mod, $mods, true ) ) {
5423              return true;
5424          }
5425      } elseif ( function_exists( 'phpinfo' ) && false === strpos( ini_get( 'disable_functions' ), 'phpinfo' ) ) {
5426              ob_start();
5427              phpinfo( 8 );
5428              $phpinfo = ob_get_clean();
5429          if ( false !== strpos( $phpinfo, $mod ) ) {
5430              return true;
5431          }
5432      }
5433  
5434      return $default;
5435  }
5436  
5437  /**
5438   * Check if IIS 7+ supports pretty permalinks.
5439   *
5440   * @since 2.8.0
5441   *
5442   * @global bool $is_iis7
5443   *
5444   * @return bool Whether IIS7 supports permalinks.
5445   */
5446  function iis7_supports_permalinks() {
5447      global $is_iis7;
5448  
5449      $supports_permalinks = false;
5450      if ( $is_iis7 ) {
5451          /* First we check if the DOMDocument class exists. If it does not exist, then we cannot
5452           * easily update the xml configuration file, hence we just bail out and tell user that
5453           * pretty permalinks cannot be used.
5454           *
5455           * Next we check if the URL Rewrite Module 1.1 is loaded and enabled for the web site. When
5456           * URL Rewrite 1.1 is loaded it always sets a server variable called 'IIS_UrlRewriteModule'.
5457           * Lastly we make sure that PHP is running via FastCGI. This is important because if it runs
5458           * via ISAPI then pretty permalinks will not work.
5459           */
5460          $supports_permalinks = class_exists( 'DOMDocument', false ) && isset( $_SERVER['IIS_UrlRewriteModule'] ) && ( 'cgi-fcgi' === PHP_SAPI );
5461      }
5462  
5463      /**
5464       * Filters whether IIS 7+ supports pretty permalinks.
5465       *
5466       * @since 2.8.0
5467       *
5468       * @param bool $supports_permalinks Whether IIS7 supports permalinks. Default false.
5469       */
5470      return apply_filters( 'iis7_supports_permalinks', $supports_permalinks );
5471  }
5472  
5473  /**
5474   * Validates a file name and path against an allowed set of rules.
5475   *
5476   * A return value of `1` means the file path contains directory traversal.
5477   *
5478   * A return value of `2` means the file path contains a Windows drive path.
5479   *
5480   * A return value of `3` means the file is not in the allowed files list.
5481   *
5482   * @since 1.2.0
5483   *
5484   * @param string   $file          File path.
5485   * @param string[] $allowed_files Optional. Array of allowed files.
5486   * @return int 0 means nothing is wrong, greater than 0 means something was wrong.
5487   */
5488  function validate_file( $file, $allowed_files = array() ) {
5489      // `../` on its own is not allowed:
5490      if ( '../' === $file ) {
5491          return 1;
5492      }
5493  
5494      // More than one occurence of `../` is not allowed:
5495      if ( preg_match_all( '#\.\./#', $file, $matches, PREG_SET_ORDER ) && ( count( $matches ) > 1 ) ) {
5496          return 1;
5497      }
5498  
5499      // `../` which does not occur at the end of the path is not allowed:
5500      if ( false !== strpos( $file, '../' ) && '../' !== mb_substr( $file, -3, 3 ) ) {
5501          return 1;
5502      }
5503  
5504      // Files not in the allowed file list are not allowed:
5505      if ( ! empty( $allowed_files ) && ! in_array( $file, $allowed_files, true ) ) {
5506          return 3;
5507      }
5508  
5509      // Absolute Windows drive paths are not allowed:
5510      if ( ':' === substr( $file, 1, 1 ) ) {
5511          return 2;
5512      }
5513  
5514      return 0;
5515  }
5516  
5517  /**
5518   * Whether to force SSL used for the Administration Screens.
5519   *
5520   * @since 2.6.0
5521   *
5522   * @param string|bool $force Optional. Whether to force SSL in admin screens. Default null.
5523   * @return bool True if forced, false if not forced.
5524   */
5525  function force_ssl_admin( $force = null ) {
5526      static $forced = false;
5527  
5528      if ( ! is_null( $force ) ) {
5529          $old_forced = $forced;
5530          $forced     = $force;
5531          return $old_forced;
5532      }
5533  
5534      return $forced;
5535  }
5536  
5537  /**
5538   * Guess the URL for the site.
5539   *
5540   * Will remove wp-admin links to retrieve only return URLs not in the wp-admin
5541   * directory.
5542   *
5543   * @since 2.6.0
5544   *
5545   * @return string The guessed URL.
5546   */
5547  function wp_guess_url() {
5548      if ( defined( 'WP_SITEURL' ) && '' !== WP_SITEURL ) {
5549          $url = WP_SITEURL;
5550      } else {
5551          $abspath_fix         = str_replace( '\\', '/', ABSPATH );
5552          $script_filename_dir = dirname( $_SERVER['SCRIPT_FILENAME'] );
5553  
5554          // The request is for the admin.
5555          if ( strpos( $_SERVER['REQUEST_URI'], 'wp-admin' ) !== false || strpos( $_SERVER['REQUEST_URI'], 'wp-login.php' ) !== false ) {
5556              $path = preg_replace( '#/(wp-admin/.*|wp-login.php)#i', '', $_SERVER['REQUEST_URI'] );
5557  
5558              // The request is for a file in ABSPATH.
5559          } elseif ( $script_filename_dir . '/' === $abspath_fix ) {
5560              // Strip off any file/query params in the path.
5561              $path = preg_replace( '#/[^/]*$#i', '', $_SERVER['PHP_SELF'] );
5562  
5563          } else {
5564              if ( false !== strpos( $_SERVER['SCRIPT_FILENAME'], $abspath_fix ) ) {
5565                  // Request is hitting a file inside ABSPATH.
5566                  $directory = str_replace( ABSPATH, '', $script_filename_dir );
5567                  // Strip off the subdirectory, and any file/query params.
5568                  $path = preg_replace( '#/' . preg_quote( $directory, '#' ) . '/[^/]*$#i', '', $_SERVER['REQUEST_URI'] );
5569              } elseif ( false !== strpos( $abspath_fix, $script_filename_dir ) ) {
5570                  // Request is hitting a file above ABSPATH.
5571                  $subdirectory = substr( $abspath_fix, strpos( $abspath_fix, $script_filename_dir ) + strlen( $script_filename_dir ) );
5572                  // Strip off any file/query params from the path, appending the subdirectory to the installation.
5573                  $path = preg_replace( '#/[^/]*$#i', '', $_SERVER['REQUEST_URI'] ) . $subdirectory;
5574              } else {
5575                  $path = $_SERVER['REQUEST_URI'];
5576              }
5577          }
5578  
5579          $schema = is_ssl() ? 'https://' : 'http://'; // set_url_scheme() is not defined yet.
5580          $url    = $schema . $_SERVER['HTTP_HOST'] . $path;
5581      }
5582  
5583      return rtrim( $url, '/' );
5584  }
5585  
5586  /**
5587   * Temporarily suspend cache additions.
5588   *
5589   * Stops more data being added to the cache, but still allows cache retrieval.
5590   * This is useful for actions, such as imports, when a lot of data would otherwise
5591   * be almost uselessly added to the cache.
5592   *
5593   * Suspension lasts for a single page load at most. Remember to call this
5594   * function again if you wish to re-enable cache adds earlier.
5595   *
5596   * @since 3.3.0
5597   *
5598   * @param bool $suspend Optional. Suspends additions if true, re-enables them if false.
5599   * @return bool The current suspend setting
5600   */
5601  function wp_suspend_cache_addition( $suspend = null ) {
5602      static $_suspend = false;
5603  
5604      if ( is_bool( $suspend ) ) {
5605          $_suspend = $suspend;
5606      }
5607  
5608      return $_suspend;
5609  }
5610  
5611  /**
5612   * Suspend cache invalidation.
5613   *
5614   * Turns cache invalidation on and off. Useful during imports where you don't want to do
5615   * invalidations every time a post is inserted. Callers must be sure that what they are
5616   * doing won't lead to an inconsistent cache when invalidation is suspended.
5617   *
5618   * @since 2.7.0
5619   *
5620   * @global bool $_wp_suspend_cache_invalidation
5621   *
5622   * @param bool $suspend Optional. Whether to suspend or enable cache invalidation. Default true.
5623   * @return bool The current suspend setting.
5624   */
5625  function wp_suspend_cache_invalidation( $suspend = true ) {
5626      global $_wp_suspend_cache_invalidation;
5627  
5628      $current_suspend                = $_wp_suspend_cache_invalidation;
5629      $_wp_suspend_cache_invalidation = $suspend;
5630      return $current_suspend;
5631  }
5632  
5633  /**
5634   * Determine whether a site is the main site of the current network.
5635   *
5636   * @since 3.0.0
5637   * @since 4.9.0 The `$network_id` parameter was added.
5638   *
5639   * @param int $site_id    Optional. Site ID to test. Defaults to current site.
5640   * @param int $network_id Optional. Network ID of the network to check for.
5641   *                        Defaults to current network.
5642   * @return bool True if $site_id is the main site of the network, or if not
5643   *              running Multisite.
5644   */
5645  function is_main_site( $site_id = null, $network_id = null ) {
5646      if ( ! is_multisite() ) {
5647          return true;
5648      }
5649  
5650      if ( ! $site_id ) {
5651          $site_id = get_current_blog_id();
5652      }
5653  
5654      $site_id = (int) $site_id;
5655  
5656      return get_main_site_id( $network_id ) === $site_id;
5657  }
5658  
5659  /**
5660   * Gets the main site ID.
5661   *
5662   * @since 4.9.0
5663   *
5664   * @param int $network_id Optional. The ID of the network for which to get the main site.
5665   *                        Defaults to the current network.
5666   * @return int The ID of the main site.
5667   */
5668  function get_main_site_id( $network_id = null ) {
5669      if ( ! is_multisite() ) {
5670          return get_current_blog_id();
5671      }
5672  
5673      $network = get_network( $network_id );
5674      if ( ! $network ) {
5675          return 0;
5676      }
5677  
5678      return $network->site_id;
5679  }
5680  
5681  /**
5682   * Determine whether a network is the main network of the Multisite installation.
5683   *
5684   * @since 3.7.0
5685   *
5686   * @param int $network_id Optional. Network ID to test. Defaults to current network.
5687   * @return bool True if $network_id is the main network, or if not running Multisite.
5688   */
5689  function is_main_network( $network_id = null ) {
5690      if ( ! is_multisite() ) {
5691          return true;
5692      }
5693  
5694      if ( null === $network_id ) {
5695          $network_id = get_current_network_id();
5696      }
5697  
5698      $network_id = (int) $network_id;
5699  
5700      return ( get_main_network_id() === $network_id );
5701  }
5702  
5703  /**
5704   * Get the main network ID.
5705   *
5706   * @since 4.3.0
5707   *
5708   * @return int The ID of the main network.
5709   */
5710  function get_main_network_id() {
5711      if ( ! is_multisite() ) {
5712          return 1;
5713      }
5714  
5715      $current_network = get_network();
5716  
5717      if ( defined( 'PRIMARY_NETWORK_ID' ) ) {
5718          $main_network_id = PRIMARY_NETWORK_ID;
5719      } elseif ( isset( $current_network->id ) && 1 === (int) $current_network->id ) {
5720          // If the current network has an ID of 1, assume it is the main network.
5721          $main_network_id = 1;
5722      } else {
5723          $_networks       = get_networks(
5724              array(
5725                  'fields' => 'ids',
5726                  'number' => 1,
5727              )
5728          );
5729          $main_network_id = array_shift( $_networks );
5730      }
5731  
5732      /**
5733       * Filters the main network ID.
5734       *
5735       * @since 4.3.0
5736       *
5737       * @param int $main_network_id The ID of the main network.
5738       */
5739      return (int) apply_filters( 'get_main_network_id', $main_network_id );
5740  }
5741  
5742  /**
5743   * Determine whether global terms are enabled.
5744   *
5745   * @since 3.0.0
5746   *
5747   * @return bool True if multisite and global terms enabled.
5748   */
5749  function global_terms_enabled() {
5750      if ( ! is_multisite() ) {
5751          return false;
5752      }
5753  
5754      static $global_terms = null;
5755      if ( is_null( $global_terms ) ) {
5756  
5757          /**
5758           * Filters whether global terms are enabled.
5759           *
5760           * Returning a non-null value from the filter will effectively short-circuit the function
5761           * and return the value of the 'global_terms_enabled' site option instead.
5762           *
5763           * @since 3.0.0
5764           *
5765           * @param null $enabled Whether global terms are enabled.
5766           */
5767          $filter = apply_filters( 'global_terms_enabled', null );
5768          if ( ! is_null( $filter ) ) {
5769              $global_terms = (bool) $filter;
5770          } else {
5771              $global_terms = (bool) get_site_option( 'global_terms_enabled', false );
5772          }
5773      }
5774      return $global_terms;
5775  }
5776  
5777  /**
5778   * Determines whether site meta is enabled.
5779   *
5780   * This function checks whether the 'blogmeta' database table exists. The result is saved as
5781   * a setting for the main network, making it essentially a global setting. Subsequent requests
5782   * will refer to this setting instead of running the query.
5783   *
5784   * @since 5.1.0
5785   *
5786   * @global wpdb $wpdb WordPress database abstraction object.
5787   *
5788   * @return bool True if site meta is supported, false otherwise.
5789   */
5790  function is_site_meta_supported() {
5791      global $wpdb;
5792  
5793      if ( ! is_multisite() ) {
5794          return false;
5795      }
5796  
5797      $network_id = get_main_network_id();
5798  
5799      $supported = get_network_option( $network_id, 'site_meta_supported', false );
5800      if ( false === $supported ) {
5801          $supported = $wpdb->get_var( "SHOW TABLES LIKE '{$wpdb->blogmeta}'" ) ? 1 : 0;
5802  
5803          update_network_option( $network_id, 'site_meta_supported', $supported );
5804      }
5805  
5806      return (bool) $supported;
5807  }
5808  
5809  /**
5810   * gmt_offset modification for smart timezone handling.
5811   *
5812   * Overrides the gmt_offset option if we have a timezone_string available.
5813   *
5814   * @since 2.8.0
5815   *
5816   * @return float|false Timezone GMT offset, false otherwise.
5817   */
5818  function wp_timezone_override_offset() {
5819      $timezone_string = get_option( 'timezone_string' );
5820      if ( ! $timezone_string ) {
5821          return false;
5822      }
5823  
5824      $timezone_object = timezone_open( $timezone_string );
5825      $datetime_object = date_create();
5826      if ( false === $timezone_object || false === $datetime_object ) {
5827          return false;
5828      }
5829      return round( timezone_offset_get( $timezone_object, $datetime_object ) / HOUR_IN_SECONDS, 2 );
5830  }
5831  
5832  /**
5833   * Sort-helper for timezones.
5834   *
5835   * @since 2.9.0
5836   * @access private
5837   *
5838   * @param array $a
5839   * @param array $b
5840   * @return int
5841   */
5842  function _wp_timezone_choice_usort_callback( $a, $b ) {
5843      // Don't use translated versions of Etc.
5844      if ( 'Etc' === $a['continent'] && 'Etc' === $b['continent'] ) {
5845          // Make the order of these more like the old dropdown.
5846          if ( 'GMT+' === substr( $a['city'], 0, 4 ) && 'GMT+' === substr( $b['city'], 0, 4 ) ) {
5847              return -1 * ( strnatcasecmp( $a['city'], $b['city'] ) );
5848          }
5849          if ( 'UTC' === $a['city'] ) {
5850              if ( 'GMT+' === substr( $b['city'], 0, 4 ) ) {
5851                  return 1;
5852              }
5853              return -1;
5854          }
5855          if ( 'UTC' === $b['city'] ) {
5856              if ( 'GMT+' === substr( $a['city'], 0, 4 ) ) {
5857                  return -1;
5858              }
5859              return 1;
5860          }
5861          return strnatcasecmp( $a['city'], $b['city'] );
5862      }
5863      if ( $a['t_continent'] == $b['t_continent'] ) {
5864          if ( $a['t_city'] == $b['t_city'] ) {
5865              return strnatcasecmp( $a['t_subcity'], $b['t_subcity'] );
5866          }
5867          return strnatcasecmp( $a['t_city'], $b['t_city'] );
5868      } else {
5869          // Force Etc to the bottom of the list.
5870          if ( 'Etc' === $a['continent'] ) {
5871              return 1;
5872          }
5873          if ( 'Etc' === $b['continent'] ) {
5874              return -1;
5875          }
5876          return strnatcasecmp( $a['t_continent'], $b['t_continent'] );
5877<