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