[ Index ]

WordPress 5.4.1

[ Index ]     [ Classes ]     [ Functions ]     [ Variables ]     [ Constants ]     [ Statistics ]    

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