[ Index ]

PHP Cross Reference of WordPress

title

Body

[close]

/wp-includes/ -> functions.php (source)

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