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