[ Index ]

PHP Cross Reference of WordPress Trunk (Updated Daily)

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      }
5354  
5355      $timezone_object = timezone_open( $timezone_string );
5356      $datetime_object = date_create();
5357      if ( false === $timezone_object || false === $datetime_object ) {
5358          return false;
5359      }
5360      return round( timezone_offset_get( $timezone_object, $datetime_object ) / HOUR_IN_SECONDS, 2 );
5361  }
5362  
5363  /**
5364   * Sort-helper for timezones.
5365   *
5366   * @since 2.9.0
5367   * @access private
5368   *
5369   * @param array $a
5370   * @param array $b
5371   * @return int
5372   */
5373  function _wp_timezone_choice_usort_callback( $a, $b ) {
5374      // Don't use translated versions of Etc
5375      if ( 'Etc' === $a['continent'] && 'Etc' === $b['continent'] ) {
5376          // Make the order of these more like the old dropdown
5377          if ( 'GMT+' === substr( $a['city'], 0, 4 ) && 'GMT+' === substr( $b['city'], 0, 4 ) ) {
5378              return -1 * ( strnatcasecmp( $a['city'], $b['city'] ) );
5379          }
5380          if ( 'UTC' === $a['city'] ) {
5381              if ( 'GMT+' === substr( $b['city'], 0, 4 ) ) {
5382                  return 1;
5383              }
5384              return -1;
5385          }
5386          if ( 'UTC' === $b['city'] ) {
5387              if ( 'GMT+' === substr( $a['city'], 0, 4 ) ) {
5388                  return -1;
5389              }
5390              return 1;
5391          }
5392          return strnatcasecmp( $a['city'], $b['city'] );
5393      }
5394      if ( $a['t_continent'] == $b['t_continent'] ) {
5395          if ( $a['t_city'] == $b['t_city'] ) {
5396              return strnatcasecmp( $a['t_subcity'], $b['t_subcity'] );
5397          }
5398          return strnatcasecmp( $a['t_city'], $b['t_city'] );
5399      } else {
5400          // Force Etc to the bottom of the list
5401          if ( 'Etc' === $a['continent'] ) {
5402              return 1;
5403          }
5404          if ( 'Etc' === $b['continent'] ) {
5405              return -1;
5406          }
5407          return strnatcasecmp( $a['t_continent'], $b['t_continent'] );
5408      }
5409  }
5410  
5411  /**
5412   * Gives a nicely-formatted list of timezone strings.
5413   *
5414   * @since 2.9.0
5415   * @since 4.7.0 Added the `$locale` parameter.
5416   *
5417   * @staticvar bool $mo_loaded
5418   * @staticvar string $locale_loaded
5419   *
5420   * @param string $selected_zone Selected timezone.
5421   * @param string $locale        Optional. Locale to load the timezones in. Default current site locale.
5422   * @return string
5423   */
5424  function wp_timezone_choice( $selected_zone, $locale = null ) {
5425      static $mo_loaded = false, $locale_loaded = null;
5426  
5427      $continents = array( 'Africa', 'America', 'Antarctica', 'Arctic', 'Asia', 'Atlantic', 'Australia', 'Europe', 'Indian', 'Pacific' );
5428  
5429      // Load translations for continents and cities.
5430      if ( ! $mo_loaded || $locale !== $locale_loaded ) {
5431          $locale_loaded = $locale ? $locale : get_locale();
5432          $mofile        = WP_LANG_DIR . '/continents-cities-' . $locale_loaded . '.mo';
5433          unload_textdomain( 'continents-cities' );
5434          load_textdomain( 'continents-cities', $mofile );
5435          $mo_loaded = true;
5436      }
5437  
5438      $zonen = array();
5439      foreach ( timezone_identifiers_list() as $zone ) {
5440          $zone = explode( '/', $zone );
5441          if ( ! in_array( $zone[0], $continents ) ) {
5442              continue;
5443          }
5444  
5445          // This determines what gets set and translated - we don't translate Etc/* strings here, they are done later
5446          $exists    = array(
5447              0 => ( isset( $zone[0] ) && $zone[0] ),
5448              1 => ( isset( $zone[1] ) && $zone[1] ),
5449              2 => ( isset( $zone[2] ) && $zone[2] ),
5450          );
5451          $exists[3] = ( $exists[0] && 'Etc' !== $zone[0] );
5452          $exists[4] = ( $exists[1] && $exists[3] );
5453          $exists[5] = ( $exists[2] && $exists[3] );
5454  
5455          // phpcs:disable WordPress.WP.I18n.LowLevelTranslationFunction,WordPress.WP.I18n.NonSingularStringLiteralText
5456          $zonen[] = array(
5457              'continent'   => ( $exists[0] ? $zone[0] : '' ),
5458              'city'        => ( $exists[1] ? $zone[1] : '' ),
5459              'subcity'     => ( $exists[2] ? $zone[2] : '' ),
5460              't_continent' => ( $exists[3] ? translate( str_replace( '_', ' ', $zone[0] ), 'continents-cities' ) : '' ),
5461              't_city'      => ( $exists[4] ? translate( str_replace( '_', ' ', $zone[1] ), 'continents-cities' ) : '' ),
5462              't_subcity'   => ( $exists[5] ? translate( str_replace( '_', ' ', $zone[2] ), 'continents-cities' ) : '' ),
5463          );
5464          // phpcs:enable
5465      }
5466      usort( $zonen, '_wp_timezone_choice_usort_callback' );
5467  
5468      $structure = array();
5469  
5470      if ( empty( $selected_zone ) ) {
5471          $structure[] = '<option selected="selected" value="">' . __( 'Select a city' ) . '</option>';
5472      }
5473  
5474      foreach ( $zonen as $key => $zone ) {
5475          // Build value in an array to join later
5476          $value = array( $zone['continent'] );
5477  
5478          if ( empty( $zone['city'] ) ) {
5479              // It's at the continent level (generally won't happen)
5480              $display = $zone['t_continent'];
5481          } else {
5482              // It's inside a continent group
5483  
5484              // Continent optgroup
5485              if ( ! isset( $zonen[ $key - 1 ] ) || $zonen[ $key - 1 ]['continent'] !== $zone['continent'] ) {
5486                  $label       = $zone['t_continent'];
5487                  $structure[] = '<optgroup label="' . esc_attr( $label ) . '">';
5488              }
5489  
5490              // Add the city to the value
5491              $value[] = $zone['city'];
5492  
5493              $display = $zone['t_city'];
5494              if ( ! empty( $zone['subcity'] ) ) {
5495                  // Add the subcity to the value
5496                  $value[]  = $zone['subcity'];
5497                  $display .= ' - ' . $zone['t_subcity'];
5498              }
5499          }
5500  
5501          // Build the value
5502          $value    = join( '/', $value );
5503          $selected = '';
5504          if ( $value === $selected_zone ) {
5505              $selected = 'selected="selected" ';
5506          }
5507          $structure[] = '<option ' . $selected . 'value="' . esc_attr( $value ) . '">' . esc_html( $display ) . '</option>';
5508  
5509          // Close continent optgroup
5510          if ( ! empty( $zone['city'] ) && ( ! isset( $zonen[ $key + 1 ] ) || ( isset( $zonen[ $key + 1 ] ) && $zonen[ $key + 1 ]['continent'] !== $zone['continent'] ) ) ) {
5511              $structure[] = '</optgroup>';
5512          }
5513      }
5514  
5515      // Do UTC
5516      $structure[] = '<optgroup label="' . esc_attr__( 'UTC' ) . '">';
5517      $selected    = '';
5518      if ( 'UTC' === $selected_zone ) {
5519          $selected = 'selected="selected" ';
5520      }
5521      $structure[] = '<option ' . $selected . 'value="' . esc_attr( 'UTC' ) . '">' . __( 'UTC' ) . '</option>';
5522      $structure[] = '</optgroup>';
5523  
5524      // Do manual UTC offsets
5525      $structure[]  = '<optgroup label="' . esc_attr__( 'Manual Offsets' ) . '">';
5526      $offset_range = array(
5527          -12,
5528          -11.5,
5529          -11,
5530          -10.5,
5531          -10,
5532          -9.5,
5533          -9,
5534          -8.5,
5535          -8,
5536          -7.5,
5537          -7,
5538          -6.5,
5539          -6,
5540          -5.5,
5541          -5,
5542          -4.5,
5543          -4,
5544          -3.5,
5545          -3,
5546          -2.5,
5547          -2,
5548          -1.5,
5549          -1,
5550          -0.5,
5551          0,
5552          0.5,
5553          1,
5554          1.5,
5555          2,
5556          2.5,
5557          3,
5558          3.5,
5559          4,
5560          4.5,
5561          5,
5562          5.5,
5563          5.75,
5564          6,
5565          6.5,
5566          7,
5567          7.5,