[ Index ]

PHP Cross Reference of WordPress Trunk (Updated Daily)

title

Body

[close]

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

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