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