[ Index ]

PHP Cross Reference of WordPress

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