[ Index ]

PHP Cross Reference of WordPress Trunk (Updated Daily)

Search

title

Body

[close]

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

   1  <?php
   2  /**
   3   * These functions can be replaced via plugins. If plugins do not redefine these
   4   * functions, then these will be used instead.
   5   *
   6   * @package WordPress
   7   */
   8  
   9  if ( ! function_exists( 'wp_set_current_user' ) ) :
  10      /**
  11       * Changes the current user by ID or name.
  12       *
  13       * Set $id to null and specify a name if you do not know a user's ID.
  14       *
  15       * Some WordPress functionality is based on the current user and not based on
  16       * the signed in user. Therefore, it opens the ability to edit and perform
  17       * actions on users who aren't signed in.
  18       *
  19       * @since 2.0.3
  20       *
  21       * @global WP_User $current_user The current user object which holds the user data.
  22       *
  23       * @param int|null $id   User ID.
  24       * @param string   $name User's username.
  25       * @return WP_User Current user User object.
  26       */
  27  	function wp_set_current_user( $id, $name = '' ) {
  28          global $current_user;
  29  
  30          // If `$id` matches the current user, there is nothing to do.
  31          if ( isset( $current_user )
  32          && ( $current_user instanceof WP_User )
  33          && ( $id === $current_user->ID )
  34          && ( null !== $id )
  35          ) {
  36              return $current_user;
  37          }
  38  
  39          $current_user = new WP_User( $id, $name );
  40  
  41          setup_userdata( $current_user->ID );
  42  
  43          /**
  44           * Fires after the current user is set.
  45           *
  46           * @since 2.0.1
  47           */
  48          do_action( 'set_current_user' );
  49  
  50          return $current_user;
  51      }
  52  endif;
  53  
  54  if ( ! function_exists( 'wp_get_current_user' ) ) :
  55      /**
  56       * Retrieves the current user object.
  57       *
  58       * Will set the current user, if the current user is not set. The current user
  59       * will be set to the logged-in person. If no user is logged-in, then it will
  60       * set the current user to 0, which is invalid and won't have any permissions.
  61       *
  62       * @since 2.0.3
  63       *
  64       * @see _wp_get_current_user()
  65       * @global WP_User $current_user Checks if the current user is set.
  66       *
  67       * @return WP_User Current WP_User instance.
  68       */
  69  	function wp_get_current_user() {
  70          return _wp_get_current_user();
  71      }
  72  endif;
  73  
  74  if ( ! function_exists( 'get_userdata' ) ) :
  75      /**
  76       * Retrieves user info by user ID.
  77       *
  78       * @since 0.71
  79       *
  80       * @param int $user_id User ID
  81       * @return WP_User|false WP_User object on success, false on failure.
  82       */
  83  	function get_userdata( $user_id ) {
  84          return get_user_by( 'id', $user_id );
  85      }
  86  endif;
  87  
  88  if ( ! function_exists( 'get_user_by' ) ) :
  89      /**
  90       * Retrieves user info by a given field.
  91       *
  92       * @since 2.8.0
  93       * @since 4.4.0 Added 'ID' as an alias of 'id' for the `$field` parameter.
  94       *
  95       * @global WP_User $current_user The current user object which holds the user data.
  96       *
  97       * @param string     $field The field to retrieve the user with. id | ID | slug | email | login.
  98       * @param int|string $value A value for $field. A user ID, slug, email address, or login name.
  99       * @return WP_User|false WP_User object on success, false on failure.
 100       */
 101  	function get_user_by( $field, $value ) {
 102          $userdata = WP_User::get_data_by( $field, $value );
 103  
 104          if ( ! $userdata ) {
 105              return false;
 106          }
 107  
 108          $user = new WP_User();
 109          $user->init( $userdata );
 110  
 111          return $user;
 112      }
 113  endif;
 114  
 115  if ( ! function_exists( 'cache_users' ) ) :
 116      /**
 117       * Retrieves info for user lists to prevent multiple queries by get_userdata().
 118       *
 119       * @since 3.0.0
 120       *
 121       * @global wpdb $wpdb WordPress database abstraction object.
 122       *
 123       * @param int[] $user_ids User ID numbers list
 124       */
 125  	function cache_users( $user_ids ) {
 126          global $wpdb;
 127  
 128          update_meta_cache( 'user', $user_ids );
 129  
 130          $clean = _get_non_cached_ids( $user_ids, 'users' );
 131  
 132          if ( empty( $clean ) ) {
 133              return;
 134          }
 135  
 136          $list = implode( ',', $clean );
 137  
 138          $users = $wpdb->get_results( "SELECT * FROM $wpdb->users WHERE ID IN ($list)" );
 139  
 140          foreach ( $users as $user ) {
 141              update_user_caches( $user );
 142          }
 143      }
 144  endif;
 145  
 146  if ( ! function_exists( 'wp_mail' ) ) :
 147      /**
 148       * Sends an email, similar to PHP's mail function.
 149       *
 150       * A true return value does not automatically mean that the user received the
 151       * email successfully. It just only means that the method used was able to
 152       * process the request without any errors.
 153       *
 154       * The default content type is `text/plain` which does not allow using HTML.
 155       * However, you can set the content type of the email by using the
 156       * {@see 'wp_mail_content_type'} filter.
 157       *
 158       * The default charset is based on the charset used on the blog. The charset can
 159       * be set using the {@see 'wp_mail_charset'} filter.
 160       *
 161       * When using the `$embeds` parameter to embed images for use in HTML emails,
 162       * reference the embedded file in your HTML with a `cid:` URL whose value
 163       * matches the file's Content-ID. By default, the Content-ID (`cid`) used for
 164       * each embedded file is the key in the embeds array, unless modified via the
 165       * {@see 'wp_mail_embed_args'} filter. For example:
 166       *
 167       * `<img src="cid:0" alt="Logo">`
 168       * `<img src="cid:my-image" alt="Image">`
 169       *
 170       * You may also customize the Content-ID for each file by using the
 171       * {@see 'wp_mail_embed_args'} filter and setting the `cid` value.
 172       *
 173       * @since 1.2.1
 174       * @since 5.5.0 is_email() is used for email validation,
 175       *              instead of PHPMailer's default validator.
 176       * @since 6.9.0 Added $embeds parameter.
 177       *
 178       * @global PHPMailer\PHPMailer\PHPMailer $phpmailer
 179       *
 180       * @param string|string[] $to          Array or comma-separated list of email addresses to send message.
 181       * @param string          $subject     Email subject.
 182       * @param string          $message     Message contents.
 183       * @param string|string[] $headers     Optional. Additional headers.
 184       * @param string|string[] $attachments Optional. Paths to files to attach.
 185       * @param string|string[] $embeds      Optional. Paths to files to embed.
 186       * @return bool Whether the email was sent successfully.
 187       */
 188  	function wp_mail( $to, $subject, $message, $headers = '', $attachments = array(), $embeds = array() ) {
 189          // Compact the input, apply the filters, and extract them back out.
 190  
 191          /**
 192           * Filters the wp_mail() arguments.
 193           *
 194           * @since 2.2.0
 195           *
 196           * @param array $args {
 197           *     Array of the `wp_mail()` arguments.
 198           *
 199           *     @type string|string[] $to          Array or comma-separated list of email addresses to send message.
 200           *     @type string          $subject     Email subject.
 201           *     @type string          $message     Message contents.
 202           *     @type string|string[] $headers     Additional headers.
 203           *     @type string|string[] $attachments Paths to files to attach.
 204           *     @type string|string[] $embeds      Paths to files to embed.
 205           * }
 206           */
 207          $atts = apply_filters( 'wp_mail', compact( 'to', 'subject', 'message', 'headers', 'attachments', 'embeds' ) );
 208  
 209          /**
 210           * Filters whether to preempt sending an email.
 211           *
 212           * Returning a non-null value will short-circuit {@see wp_mail()}, returning
 213           * that value instead. A boolean return value should be used to indicate whether
 214           * the email was successfully sent.
 215           *
 216           * @since 5.7.0
 217           *
 218           * @param null|bool $return Short-circuit return value.
 219           * @param array     $atts {
 220           *     Array of the `wp_mail()` arguments.
 221           *
 222           *     @type string|string[] $to          Array or comma-separated list of email addresses to send message.
 223           *     @type string          $subject     Email subject.
 224           *     @type string          $message     Message contents.
 225           *     @type string|string[] $headers     Additional headers.
 226           *     @type string|string[] $attachments Paths to files to attach.
 227           *     @type string|string[] $embeds      Paths to files to embed.
 228           * }
 229           */
 230          $pre_wp_mail = apply_filters( 'pre_wp_mail', null, $atts );
 231  
 232          if ( null !== $pre_wp_mail ) {
 233              return $pre_wp_mail;
 234          }
 235  
 236          if ( isset( $atts['to'] ) ) {
 237              $to = $atts['to'];
 238          }
 239  
 240          if ( ! is_array( $to ) ) {
 241              $to = explode( ',', $to );
 242          }
 243  
 244          if ( isset( $atts['subject'] ) ) {
 245              $subject = $atts['subject'];
 246          }
 247  
 248          if ( isset( $atts['message'] ) ) {
 249              $message = $atts['message'];
 250          }
 251  
 252          if ( isset( $atts['headers'] ) ) {
 253              $headers = $atts['headers'];
 254          }
 255  
 256          if ( isset( $atts['attachments'] ) ) {
 257              $attachments = $atts['attachments'];
 258          }
 259  
 260          if ( ! is_array( $attachments ) ) {
 261              $attachments = explode( "\n", str_replace( "\r\n", "\n", $attachments ) );
 262          }
 263  
 264          if ( isset( $atts['embeds'] ) ) {
 265              $embeds = $atts['embeds'];
 266          }
 267  
 268          if ( ! is_array( $embeds ) ) {
 269              $embeds = explode( "\n", str_replace( "\r\n", "\n", $embeds ) );
 270          }
 271  
 272          global $phpmailer;
 273  
 274          // (Re)create it, if it's gone missing.
 275          if ( ! ( $phpmailer instanceof PHPMailer\PHPMailer\PHPMailer ) ) {
 276              require_once  ABSPATH . WPINC . '/PHPMailer/PHPMailer.php';
 277              require_once  ABSPATH . WPINC . '/PHPMailer/SMTP.php';
 278              require_once  ABSPATH . WPINC . '/PHPMailer/Exception.php';
 279              require_once  ABSPATH . WPINC . '/class-wp-phpmailer.php';
 280              $phpmailer = new WP_PHPMailer( true );
 281  
 282              $phpmailer::$validator = static function ( $email ) {
 283                  return (bool) is_email( $email );
 284              };
 285          }
 286  
 287          // Headers.
 288          $cc       = array();
 289          $bcc      = array();
 290          $reply_to = array();
 291  
 292          if ( empty( $headers ) ) {
 293              $headers = array();
 294          } else {
 295              if ( ! is_array( $headers ) ) {
 296                  /*
 297                   * Explode the headers out, so this function can take
 298                   * both string headers and an array of headers.
 299                   */
 300                  $tempheaders = explode( "\n", str_replace( "\r\n", "\n", $headers ) );
 301              } else {
 302                  $tempheaders = $headers;
 303              }
 304              $headers = array();
 305  
 306              // If it's actually got contents.
 307              if ( ! empty( $tempheaders ) ) {
 308                  // Iterate through the raw headers.
 309                  foreach ( (array) $tempheaders as $header ) {
 310                      if ( ! str_contains( $header, ':' ) ) {
 311                          if ( false !== stripos( $header, 'boundary=' ) ) {
 312                              $parts    = preg_split( '/boundary=/i', trim( $header ) );
 313                              $boundary = trim( str_replace( array( "'", '"' ), '', $parts[1] ) );
 314                          }
 315                          continue;
 316                      }
 317                      // Explode them out.
 318                      list( $name, $content ) = explode( ':', trim( $header ), 2 );
 319  
 320                      // Cleanup crew.
 321                      $name    = trim( $name );
 322                      $content = trim( $content );
 323  
 324                      switch ( strtolower( $name ) ) {
 325                          // Mainly for legacy -- process a "From:" header if it's there.
 326                          case 'from':
 327                              $bracket_pos = strpos( $content, '<' );
 328                              if ( false !== $bracket_pos ) {
 329                                  // Text before the bracketed email is the "From" name.
 330                                  if ( $bracket_pos > 0 ) {
 331                                      $from_name = substr( $content, 0, $bracket_pos );
 332                                      $from_name = str_replace( '"', '', $from_name );
 333                                      $from_name = trim( $from_name );
 334                                  }
 335  
 336                                  $from_email = substr( $content, $bracket_pos + 1 );
 337                                  $from_email = str_replace( '>', '', $from_email );
 338                                  $from_email = trim( $from_email );
 339  
 340                                  // Avoid setting an empty $from_email.
 341                              } elseif ( '' !== trim( $content ) ) {
 342                                  $from_email = trim( $content );
 343                              }
 344                              break;
 345                          case 'content-type':
 346                              if ( str_contains( $content, ';' ) ) {
 347                                  list( $type, $charset_content ) = explode( ';', $content );
 348                                  $content_type                   = trim( $type );
 349                                  if ( false !== stripos( $charset_content, 'charset=' ) ) {
 350                                      $charset = trim( str_replace( array( 'charset=', '"' ), '', $charset_content ) );
 351                                  } elseif ( false !== stripos( $charset_content, 'boundary=' ) ) {
 352                                      $boundary = trim( str_replace( array( 'BOUNDARY=', 'boundary=', '"' ), '', $charset_content ) );
 353                                      $charset  = '';
 354                                  }
 355  
 356                                  // Avoid setting an empty $content_type.
 357                              } elseif ( '' !== trim( $content ) ) {
 358                                  $content_type = trim( $content );
 359                              }
 360                              break;
 361                          case 'cc':
 362                              $cc = array_merge( (array) $cc, explode( ',', $content ) );
 363                              break;
 364                          case 'bcc':
 365                              $bcc = array_merge( (array) $bcc, explode( ',', $content ) );
 366                              break;
 367                          case 'reply-to':
 368                              $reply_to = array_merge( (array) $reply_to, explode( ',', $content ) );
 369                              break;
 370                          default:
 371                              // Add it to our grand headers array.
 372                              $headers[ trim( $name ) ] = trim( $content );
 373                              break;
 374                      }
 375                  }
 376              }
 377          }
 378  
 379          // Empty out the values that may be set.
 380          $phpmailer->clearAllRecipients();
 381          $phpmailer->clearAttachments();
 382          $phpmailer->clearCustomHeaders();
 383          $phpmailer->clearReplyTos();
 384          $phpmailer->Body    = '';
 385          $phpmailer->AltBody = '';
 386  
 387          // Set "From" name and email.
 388  
 389          // If we don't have a name from the input headers.
 390          if ( ! isset( $from_name ) ) {
 391              $from_name = 'WordPress';
 392          }
 393  
 394          /*
 395           * If we don't have an email from the input headers, default to wordpress@$sitename
 396           * Some hosts will block outgoing mail from this address if it doesn't exist,
 397           * but there's no easy alternative. Defaulting to admin_email might appear to be
 398           * another option, but some hosts may refuse to relay mail from an unknown domain.
 399           * See https://core.trac.wordpress.org/ticket/5007.
 400           */
 401          if ( ! isset( $from_email ) ) {
 402              // Get the site domain and get rid of www.
 403              $sitename   = wp_parse_url( network_home_url(), PHP_URL_HOST );
 404              $from_email = 'wordpress@';
 405  
 406              if ( null !== $sitename ) {
 407                  if ( str_starts_with( $sitename, 'www.' ) ) {
 408                      $sitename = substr( $sitename, 4 );
 409                  }
 410  
 411                  $from_email .= $sitename;
 412              }
 413          }
 414  
 415          /**
 416           * Filters the email address to send from.
 417           *
 418           * @since 2.2.0
 419           *
 420           * @param string $from_email Email address to send from.
 421           */
 422          $from_email = apply_filters( 'wp_mail_from', $from_email );
 423  
 424          /**
 425           * Filters the name to associate with the "from" email address.
 426           *
 427           * @since 2.3.0
 428           *
 429           * @param string $from_name Name associated with the "from" email address.
 430           */
 431          $from_name = apply_filters( 'wp_mail_from_name', $from_name );
 432  
 433          try {
 434              $phpmailer->setFrom( $from_email, $from_name, false );
 435          } catch ( PHPMailer\PHPMailer\Exception $e ) {
 436              $mail_error_data                             = compact( 'to', 'subject', 'message', 'headers', 'attachments' );
 437              $mail_error_data['phpmailer_exception_code'] = $e->getCode();
 438  
 439              /** This filter is documented in wp-includes/pluggable.php */
 440              do_action( 'wp_mail_failed', new WP_Error( 'wp_mail_failed', $e->getMessage(), $mail_error_data ) );
 441  
 442              return false;
 443          }
 444  
 445          // Set mail's subject and body.
 446          $phpmailer->Subject = $subject;
 447          $phpmailer->Body    = $message;
 448  
 449          // Set destination addresses, using appropriate methods for handling addresses.
 450          $address_headers = compact( 'to', 'cc', 'bcc', 'reply_to' );
 451  
 452          foreach ( $address_headers as $address_header => $addresses ) {
 453              if ( empty( $addresses ) ) {
 454                  continue;
 455              }
 456  
 457              foreach ( (array) $addresses as $address ) {
 458                  try {
 459                      // Break $recipient into name and address parts if in the format "Foo <bar@baz.com>".
 460                      $recipient_name = '';
 461  
 462                      if ( preg_match( '/(.*)<(.+)>/', $address, $matches ) ) {
 463                          if ( count( $matches ) === 3 ) {
 464                              $recipient_name = $matches[1];
 465                              $address        = $matches[2];
 466                          }
 467                      }
 468  
 469                      switch ( $address_header ) {
 470                          case 'to':
 471                              $phpmailer->addAddress( $address, $recipient_name );
 472                              break;
 473                          case 'cc':
 474                              $phpmailer->addCC( $address, $recipient_name );
 475                              break;
 476                          case 'bcc':
 477                              $phpmailer->addBCC( $address, $recipient_name );
 478                              break;
 479                          case 'reply_to':
 480                              $phpmailer->addReplyTo( $address, $recipient_name );
 481                              break;
 482                      }
 483                  } catch ( PHPMailer\PHPMailer\Exception $e ) {
 484                      continue;
 485                  }
 486              }
 487          }
 488  
 489          // Set to use PHP's mail().
 490          $phpmailer->isMail();
 491  
 492          // Set Content-Type and charset.
 493  
 494          // If we don't have a Content-Type from the input headers.
 495          if ( ! isset( $content_type ) ) {
 496              $content_type = 'text/plain';
 497          }
 498  
 499          /**
 500           * Filters the wp_mail() content type.
 501           *
 502           * @since 2.3.0
 503           *
 504           * @param string $content_type Default wp_mail() content type.
 505           */
 506          $content_type = apply_filters( 'wp_mail_content_type', $content_type );
 507  
 508          $phpmailer->ContentType = $content_type;
 509  
 510          // Set whether it's plaintext, depending on $content_type.
 511          if ( 'text/html' === $content_type ) {
 512              $phpmailer->isHTML( true );
 513          }
 514  
 515          // If we don't have a charset from the input headers.
 516          if ( ! isset( $charset ) ) {
 517              $charset = get_bloginfo( 'charset' );
 518          }
 519  
 520          /**
 521           * Filters the default wp_mail() charset.
 522           *
 523           * @since 2.3.0
 524           *
 525           * @param string $charset Default email charset.
 526           */
 527          $phpmailer->CharSet = apply_filters( 'wp_mail_charset', $charset );
 528  
 529          // Set custom headers.
 530          if ( ! empty( $headers ) ) {
 531              foreach ( (array) $headers as $name => $content ) {
 532                  // Only add custom headers not added automatically by PHPMailer.
 533                  if ( ! in_array( $name, array( 'MIME-Version', 'X-Mailer' ), true ) ) {
 534                      try {
 535                          $phpmailer->addCustomHeader( sprintf( '%1$s: %2$s', $name, $content ) );
 536                      } catch ( PHPMailer\PHPMailer\Exception $e ) {
 537                          continue;
 538                      }
 539                  }
 540              }
 541  
 542              if ( false !== stripos( $content_type, 'multipart' ) && ! empty( $boundary ) ) {
 543                  $phpmailer->addCustomHeader( sprintf( 'Content-Type: %s; boundary="%s"', $content_type, $boundary ) );
 544              }
 545          }
 546  
 547          if ( ! empty( $attachments ) ) {
 548              foreach ( $attachments as $filename => $attachment ) {
 549                  $filename = is_string( $filename ) ? $filename : '';
 550  
 551                  try {
 552                      $phpmailer->addAttachment( $attachment, $filename );
 553                  } catch ( PHPMailer\PHPMailer\Exception $e ) {
 554                      continue;
 555                  }
 556              }
 557          }
 558  
 559          if ( ! empty( $embeds ) ) {
 560              foreach ( $embeds as $key => $embed_path ) {
 561                  /**
 562                   * Filters the arguments for PHPMailer's addEmbeddedImage() method.
 563                   *
 564                   * @since 6.9.0
 565                   *
 566                   * @param array $args {
 567                   *     An array of arguments for `addEmbeddedImage()`.
 568                   *     @type string $path        The path to the file.
 569                   *     @type string $cid         The Content-ID of the image. Default: The key in the embeds array.
 570                   *     @type string $name        The filename of the image.
 571                   *     @type string $encoding    The encoding of the image. Default: 'base64'.
 572                   *     @type string $type        The MIME type of the image. Default: empty string, which lets PHPMailer auto-detect.
 573                   *     @type string $disposition The disposition of the image. Default: 'inline'.
 574                   * }
 575                   */
 576                  $embed_args = apply_filters(
 577                      'wp_mail_embed_args',
 578                      array(
 579                          'path'        => $embed_path,
 580                          'cid'         => (string) $key,
 581                          'name'        => basename( $embed_path ),
 582                          'encoding'    => 'base64',
 583                          'type'        => '',
 584                          'disposition' => 'inline',
 585                      )
 586                  );
 587  
 588                  try {
 589                      $phpmailer->addEmbeddedImage(
 590                          $embed_args['path'],
 591                          $embed_args['cid'],
 592                          $embed_args['name'],
 593                          $embed_args['encoding'],
 594                          $embed_args['type'],
 595                          $embed_args['disposition']
 596                      );
 597                  } catch ( PHPMailer\PHPMailer\Exception $e ) {
 598                      continue;
 599                  }
 600              }
 601          }
 602  
 603          /**
 604           * Fires after PHPMailer is initialized.
 605           *
 606           * @since 2.2.0
 607           *
 608           * @param PHPMailer $phpmailer The PHPMailer instance (passed by reference).
 609           */
 610          do_action_ref_array( 'phpmailer_init', array( &$phpmailer ) );
 611  
 612          $mail_data = compact( 'to', 'subject', 'message', 'headers', 'attachments' );
 613  
 614          // Send!
 615          try {
 616              $send = $phpmailer->send();
 617  
 618              /**
 619               * Fires after PHPMailer has successfully sent an email.
 620               *
 621               * The firing of this action does not necessarily mean that the recipient(s) received the
 622               * email successfully. It only means that the `send` method above was able to
 623               * process the request without any errors.
 624               *
 625               * @since 5.9.0
 626               *
 627               * @param array $mail_data {
 628               *     An array containing the email recipient(s), subject, message, headers, and attachments.
 629               *
 630               *     @type string[] $to          Email addresses to send message.
 631               *     @type string   $subject     Email subject.
 632               *     @type string   $message     Message contents.
 633               *     @type string[] $headers     Additional headers.
 634               *     @type string[] $attachments Paths to files to attach.
 635               *     @type string[] $embeds      Paths to files to embed.
 636               * }
 637               */
 638              do_action( 'wp_mail_succeeded', $mail_data );
 639  
 640              return $send;
 641          } catch ( PHPMailer\PHPMailer\Exception $e ) {
 642              $mail_data['phpmailer_exception_code'] = $e->getCode();
 643  
 644              /**
 645               * Fires after a PHPMailer\PHPMailer\Exception is caught.
 646               *
 647               * @since 4.4.0
 648               *
 649               * @param WP_Error $error A WP_Error object with the PHPMailer\PHPMailer\Exception message, and an array
 650               *                        containing the mail recipient, subject, message, headers, and attachments.
 651               */
 652              do_action( 'wp_mail_failed', new WP_Error( 'wp_mail_failed', $e->getMessage(), $mail_data ) );
 653  
 654              return false;
 655          }
 656      }
 657  endif;
 658  
 659  if ( ! function_exists( 'wp_authenticate' ) ) :
 660      /**
 661       * Authenticates a user, confirming the login credentials are valid.
 662       *
 663       * @since 2.5.0
 664       * @since 4.5.0 `$username` now accepts an email address.
 665       *
 666       * @param string $username User's username or email address.
 667       * @param string $password User's password.
 668       * @return WP_User|WP_Error WP_User object if the credentials are valid,
 669       *                          otherwise WP_Error.
 670       */
 671  	function wp_authenticate(
 672          $username,
 673          #[\SensitiveParameter]
 674          $password
 675      ) {
 676          $username = sanitize_user( $username );
 677          $password = trim( $password );
 678  
 679          /**
 680           * Filters whether a set of user login credentials are valid.
 681           *
 682           * A WP_User object is returned if the credentials authenticate a user.
 683           * WP_Error or null otherwise.
 684           *
 685           * @since 2.8.0
 686           * @since 4.5.0 `$username` now accepts an email address.
 687           *
 688           * @param null|WP_User|WP_Error $user     WP_User if the user is authenticated.
 689           *                                        WP_Error or null otherwise.
 690           * @param string                $username Username or email address.
 691           * @param string                $password User password.
 692           */
 693          $user = apply_filters( 'authenticate', null, $username, $password );
 694  
 695          if ( null === $user || false === $user ) {
 696              /*
 697               * TODO: What should the error message be? (Or would these even happen?)
 698               * Only needed if all authentication handlers fail to return anything.
 699               */
 700              $user = new WP_Error( 'authentication_failed', __( '<strong>Error:</strong> Invalid username, email address or incorrect password.' ) );
 701          }
 702  
 703          $ignore_codes = array( 'empty_username', 'empty_password' );
 704  
 705          if ( is_wp_error( $user ) && ! in_array( $user->get_error_code(), $ignore_codes, true ) ) {
 706              $error = $user;
 707  
 708              /**
 709               * Fires after a user login has failed.
 710               *
 711               * @since 2.5.0
 712               * @since 4.5.0 The value of `$username` can now be an email address.
 713               * @since 5.4.0 The `$error` parameter was added.
 714               *
 715               * @param string   $username Username or email address.
 716               * @param WP_Error $error    A WP_Error object with the authentication failure details.
 717               */
 718              do_action( 'wp_login_failed', $username, $error );
 719          }
 720  
 721          return $user;
 722      }
 723  endif;
 724  
 725  if ( ! function_exists( 'wp_logout' ) ) :
 726      /**
 727       * Logs the current user out.
 728       *
 729       * @since 2.5.0
 730       */
 731  	function wp_logout() {
 732          $user_id = get_current_user_id();
 733  
 734          wp_destroy_current_session();
 735          wp_clear_auth_cookie();
 736          wp_set_current_user( 0 );
 737  
 738          /**
 739           * Fires after a user is logged out.
 740           *
 741           * @since 1.5.0
 742           * @since 5.5.0 Added the `$user_id` parameter.
 743           *
 744           * @param int $user_id ID of the user that was logged out.
 745           */
 746          do_action( 'wp_logout', $user_id );
 747      }
 748  endif;
 749  
 750  if ( ! function_exists( 'wp_validate_auth_cookie' ) ) :
 751      /**
 752       * Validates authentication cookie.
 753       *
 754       * The checks include making sure that the authentication cookie is set and
 755       * pulling in the contents (if $cookie is not used).
 756       *
 757       * Makes sure the cookie is not expired. Verifies the hash in cookie is what is
 758       * should be and compares the two.
 759       *
 760       * @since 2.5.0
 761       *
 762       * @global int $login_grace_period
 763       *
 764       * @param string $cookie Optional. If used, will validate contents instead of cookie's.
 765       * @param string $scheme Optional. The cookie scheme to use: 'auth', 'secure_auth', or 'logged_in'.
 766       *                       Note: This does *not* default to 'auth' like other cookie functions.
 767       * @return int|false User ID if valid cookie, false if invalid.
 768       */
 769  	function wp_validate_auth_cookie( $cookie = '', $scheme = '' ) {
 770          $cookie_elements = wp_parse_auth_cookie( $cookie, $scheme );
 771          if ( ! $cookie_elements ) {
 772              /**
 773               * Fires if an authentication cookie is malformed.
 774               *
 775               * @since 2.7.0
 776               *
 777               * @param string $cookie Malformed auth cookie.
 778               * @param string $scheme Authentication scheme. Values include 'auth', 'secure_auth',
 779               *                       or 'logged_in'.
 780               */
 781              do_action( 'auth_cookie_malformed', $cookie, $scheme );
 782              return false;
 783          }
 784  
 785          $scheme     = $cookie_elements['scheme'];
 786          $username   = $cookie_elements['username'];
 787          $hmac       = $cookie_elements['hmac'];
 788          $token      = $cookie_elements['token'];
 789          $expiration = $cookie_elements['expiration'];
 790  
 791          $expired = (int) $expiration;
 792  
 793          // Allow a grace period for POST and Ajax requests.
 794          if ( wp_doing_ajax() || 'POST' === $_SERVER['REQUEST_METHOD'] ) {
 795              $expired += HOUR_IN_SECONDS;
 796          }
 797  
 798          // Quick check to see if an honest cookie has expired.
 799          if ( $expired < time() ) {
 800              /**
 801               * Fires once an authentication cookie has expired.
 802               *
 803               * @since 2.7.0
 804               *
 805               * @param string[] $cookie_elements {
 806               *     Authentication cookie components. None of the components should be assumed
 807               *     to be valid as they come directly from a client-provided cookie value.
 808               *
 809               *     @type string $username   User's username.
 810               *     @type string $expiration The time the cookie expires as a UNIX timestamp.
 811               *     @type string $token      User's session token used.
 812               *     @type string $hmac       The security hash for the cookie.
 813               *     @type string $scheme     The cookie scheme to use.
 814               * }
 815               */
 816              do_action( 'auth_cookie_expired', $cookie_elements );
 817              return false;
 818          }
 819  
 820          $user = get_user_by( 'login', $username );
 821          if ( ! $user ) {
 822              /**
 823               * Fires if a bad username is entered in the user authentication process.
 824               *
 825               * @since 2.7.0
 826               *
 827               * @param string[] $cookie_elements {
 828               *     Authentication cookie components. None of the components should be assumed
 829               *     to be valid as they come directly from a client-provided cookie value.
 830               *
 831               *     @type string $username   User's username.
 832               *     @type string $expiration The time the cookie expires as a UNIX timestamp.
 833               *     @type string $token      User's session token used.
 834               *     @type string $hmac       The security hash for the cookie.
 835               *     @type string $scheme     The cookie scheme to use.
 836               * }
 837               */
 838              do_action( 'auth_cookie_bad_username', $cookie_elements );
 839              return false;
 840          }
 841  
 842          if ( str_starts_with( $user->user_pass, '$P$' ) || str_starts_with( $user->user_pass, '$2y$' ) ) {
 843              // Retain previous behaviour of phpass or vanilla bcrypt hashed passwords.
 844              $pass_frag = substr( $user->user_pass, 8, 4 );
 845          } else {
 846              // Otherwise, use a substring from the end of the hash to avoid dealing with potentially long hash prefixes.
 847              $pass_frag = substr( $user->user_pass, -4 );
 848          }
 849  
 850          $key = wp_hash( $username . '|' . $pass_frag . '|' . $expiration . '|' . $token, $scheme );
 851  
 852          $hash = hash_hmac( 'sha256', $username . '|' . $expiration . '|' . $token, $key );
 853  
 854          if ( ! hash_equals( $hash, $hmac ) ) {
 855              /**
 856               * Fires if a bad authentication cookie hash is encountered.
 857               *
 858               * @since 2.7.0
 859               *
 860               * @param string[] $cookie_elements {
 861               *     Authentication cookie components. None of the components should be assumed
 862               *     to be valid as they come directly from a client-provided cookie value.
 863               *
 864               *     @type string $username   User's username.
 865               *     @type string $expiration The time the cookie expires as a UNIX timestamp.
 866               *     @type string $token      User's session token used.
 867               *     @type string $hmac       The security hash for the cookie.
 868               *     @type string $scheme     The cookie scheme to use.
 869               * }
 870               */
 871              do_action( 'auth_cookie_bad_hash', $cookie_elements );
 872              return false;
 873          }
 874  
 875          $manager = WP_Session_Tokens::get_instance( $user->ID );
 876          if ( ! $manager->verify( $token ) ) {
 877              /**
 878               * Fires if a bad session token is encountered.
 879               *
 880               * @since 4.0.0
 881               *
 882               * @param string[] $cookie_elements {
 883               *     Authentication cookie components. None of the components should be assumed
 884               *     to be valid as they come directly from a client-provided cookie value.
 885               *
 886               *     @type string $username   User's username.
 887               *     @type string $expiration The time the cookie expires as a UNIX timestamp.
 888               *     @type string $token      User's session token used.
 889               *     @type string $hmac       The security hash for the cookie.
 890               *     @type string $scheme     The cookie scheme to use.
 891               * }
 892               */
 893              do_action( 'auth_cookie_bad_session_token', $cookie_elements );
 894              return false;
 895          }
 896  
 897          // Ajax/POST grace period set above.
 898          if ( $expiration < time() ) {
 899              $GLOBALS['login_grace_period'] = 1;
 900          }
 901  
 902          /**
 903           * Fires once an authentication cookie has been validated.
 904           *
 905           * @since 2.7.0
 906           *
 907           * @param string[] $cookie_elements {
 908           *     Authentication cookie components.
 909           *
 910           *     @type string $username   User's username.
 911           *     @type string $expiration The time the cookie expires as a UNIX timestamp.
 912           *     @type string $token      User's session token used.
 913           *     @type string $hmac       The security hash for the cookie.
 914           *     @type string $scheme     The cookie scheme to use.
 915           * }
 916           * @param WP_User  $user            User object.
 917           */
 918          do_action( 'auth_cookie_valid', $cookie_elements, $user );
 919  
 920          return $user->ID;
 921      }
 922  endif;
 923  
 924  if ( ! function_exists( 'wp_generate_auth_cookie' ) ) :
 925      /**
 926       * Generates authentication cookie contents.
 927       *
 928       * @since 2.5.0
 929       * @since 4.0.0 The `$token` parameter was added.
 930       *
 931       * @param int    $user_id    User ID.
 932       * @param int    $expiration The time the cookie expires as a UNIX timestamp.
 933       * @param string $scheme     Optional. The cookie scheme to use: 'auth', 'secure_auth', or 'logged_in'.
 934       *                           Default 'auth'.
 935       * @param string $token      User's session token to use for this cookie.
 936       * @return string Authentication cookie contents. Empty string if user does not exist.
 937       */
 938  	function wp_generate_auth_cookie( $user_id, $expiration, $scheme = 'auth', $token = '' ) {
 939          $user = get_userdata( $user_id );
 940          if ( ! $user ) {
 941              return '';
 942          }
 943  
 944          if ( ! $token ) {
 945              $manager = WP_Session_Tokens::get_instance( $user_id );
 946              $token   = $manager->create( $expiration );
 947          }
 948  
 949          if ( str_starts_with( $user->user_pass, '$P$' ) || str_starts_with( $user->user_pass, '$2y$' ) ) {
 950              // Retain previous behaviour of phpass or vanilla bcrypt hashed passwords.
 951              $pass_frag = substr( $user->user_pass, 8, 4 );
 952          } else {
 953              // Otherwise, use a substring from the end of the hash to avoid dealing with potentially long hash prefixes.
 954              $pass_frag = substr( $user->user_pass, -4 );
 955          }
 956  
 957          $key = wp_hash( $user->user_login . '|' . $pass_frag . '|' . $expiration . '|' . $token, $scheme );
 958  
 959          $hash = hash_hmac( 'sha256', $user->user_login . '|' . $expiration . '|' . $token, $key );
 960  
 961          $cookie = $user->user_login . '|' . $expiration . '|' . $token . '|' . $hash;
 962  
 963          /**
 964           * Filters the authentication cookie.
 965           *
 966           * @since 2.5.0
 967           * @since 4.0.0 The `$token` parameter was added.
 968           *
 969           * @param string $cookie     Authentication cookie.
 970           * @param int    $user_id    User ID.
 971           * @param int    $expiration The time the cookie expires as a UNIX timestamp.
 972           * @param string $scheme     Cookie scheme used. Accepts 'auth', 'secure_auth', or 'logged_in'.
 973           * @param string $token      User's session token used.
 974           */
 975          return apply_filters( 'auth_cookie', $cookie, $user_id, $expiration, $scheme, $token );
 976      }
 977  endif;
 978  
 979  if ( ! function_exists( 'wp_parse_auth_cookie' ) ) :
 980      /**
 981       * Parses a cookie into its components.
 982       *
 983       * @since 2.7.0
 984       * @since 4.0.0 The `$token` element was added to the return value.
 985       *
 986       * @param string $cookie Authentication cookie.
 987       * @param string $scheme Optional. The cookie scheme to use: 'auth', 'secure_auth', or 'logged_in'.
 988       * @return string[]|false {
 989       *     Authentication cookie components. None of the components should be assumed
 990       *     to be valid as they come directly from a client-provided cookie value. If
 991       *     the cookie value is malformed, false is returned.
 992       *
 993       *     @type string $username   User's username.
 994       *     @type string $expiration The time the cookie expires as a UNIX timestamp.
 995       *     @type string $token      User's session token used.
 996       *     @type string $hmac       The security hash for the cookie.
 997       *     @type string $scheme     The cookie scheme to use.
 998       * }
 999       */
1000  	function wp_parse_auth_cookie( $cookie = '', $scheme = '' ) {
1001          if ( empty( $cookie ) ) {
1002              switch ( $scheme ) {
1003                  case 'auth':
1004                      $cookie_name = AUTH_COOKIE;
1005                      break;
1006                  case 'secure_auth':
1007                      $cookie_name = SECURE_AUTH_COOKIE;
1008                      break;
1009                  case 'logged_in':
1010                      $cookie_name = LOGGED_IN_COOKIE;
1011                      break;
1012                  default:
1013                      if ( is_ssl() ) {
1014                          $cookie_name = SECURE_AUTH_COOKIE;
1015                          $scheme      = 'secure_auth';
1016                      } else {
1017                          $cookie_name = AUTH_COOKIE;
1018                          $scheme      = 'auth';
1019                      }
1020              }
1021  
1022              if ( empty( $_COOKIE[ $cookie_name ] ) ) {
1023                  return false;
1024              }
1025              $cookie = $_COOKIE[ $cookie_name ];
1026          }
1027  
1028          $cookie_elements = explode( '|', $cookie );
1029          if ( count( $cookie_elements ) !== 4 ) {
1030              return false;
1031          }
1032  
1033          list( $username, $expiration, $token, $hmac ) = $cookie_elements;
1034  
1035          return compact( 'username', 'expiration', 'token', 'hmac', 'scheme' );
1036      }
1037  endif;
1038  
1039  if ( ! function_exists( 'wp_set_auth_cookie' ) ) :
1040      /**
1041       * Sets the authentication cookies based on user ID.
1042       *
1043       * The $remember parameter increases the time that the cookie will be kept. The
1044       * default the cookie is kept without remembering is two days. When $remember is
1045       * set, the cookies will be kept for 14 days or two weeks.
1046       *
1047       * @since 2.5.0
1048       * @since 4.3.0 Added the `$token` parameter.
1049       *
1050       * @param int         $user_id  User ID.
1051       * @param bool        $remember Whether to remember the user.
1052       * @param bool|string $secure   Whether the auth cookie should only be sent over HTTPS. Default is an empty
1053       *                              string which means the value of `is_ssl()` will be used.
1054       * @param string      $token    Optional. User's session token to use for this cookie.
1055       */
1056  	function wp_set_auth_cookie( $user_id, $remember = false, $secure = '', $token = '' ) {
1057          if ( $remember ) {
1058              /**
1059               * Filters the duration of the authentication cookie expiration period.
1060               *
1061               * @since 2.8.0
1062               *
1063               * @param int  $length   Duration of the expiration period in seconds.
1064               * @param int  $user_id  User ID.
1065               * @param bool $remember Whether to remember the user login. Default false.
1066               */
1067              $expiration = time() + apply_filters( 'auth_cookie_expiration', 14 * DAY_IN_SECONDS, $user_id, $remember );
1068  
1069              /*
1070               * Ensure the browser will continue to send the cookie after the expiration time is reached.
1071               * Needed for the login grace period in wp_validate_auth_cookie().
1072               */
1073              $expire = $expiration + ( 12 * HOUR_IN_SECONDS );
1074          } else {
1075              /** This filter is documented in wp-includes/pluggable.php */
1076              $expiration = time() + apply_filters( 'auth_cookie_expiration', 2 * DAY_IN_SECONDS, $user_id, $remember );
1077              $expire     = 0;
1078          }
1079  
1080          if ( '' === $secure ) {
1081              $secure = is_ssl();
1082          }
1083  
1084          // Front-end cookie is secure when the auth cookie is secure and the site's home URL uses HTTPS.
1085          $secure_logged_in_cookie = $secure && 'https' === parse_url( get_option( 'home' ), PHP_URL_SCHEME );
1086  
1087          /**
1088           * Filters whether the auth cookie should only be sent over HTTPS.
1089           *
1090           * @since 3.1.0
1091           *
1092           * @param bool $secure  Whether the cookie should only be sent over HTTPS.
1093           * @param int  $user_id User ID.
1094           */
1095          $secure = apply_filters( 'secure_auth_cookie', $secure, $user_id );
1096  
1097          /**
1098           * Filters whether the logged in cookie should only be sent over HTTPS.
1099           *
1100           * @since 3.1.0
1101           *
1102           * @param bool $secure_logged_in_cookie Whether the logged in cookie should only be sent over HTTPS.
1103           * @param int  $user_id                 User ID.
1104           * @param bool $secure                  Whether the auth cookie should only be sent over HTTPS.
1105           */
1106          $secure_logged_in_cookie = apply_filters( 'secure_logged_in_cookie', $secure_logged_in_cookie, $user_id, $secure );
1107  
1108          if ( $secure ) {
1109              $auth_cookie_name = SECURE_AUTH_COOKIE;
1110              $scheme           = 'secure_auth';
1111          } else {
1112              $auth_cookie_name = AUTH_COOKIE;
1113              $scheme           = 'auth';
1114          }
1115  
1116          if ( '' === $token ) {
1117              $manager = WP_Session_Tokens::get_instance( $user_id );
1118              $token   = $manager->create( $expiration );
1119          }
1120  
1121          $auth_cookie      = wp_generate_auth_cookie( $user_id, $expiration, $scheme, $token );
1122          $logged_in_cookie = wp_generate_auth_cookie( $user_id, $expiration, 'logged_in', $token );
1123  
1124          /**
1125           * Fires immediately before the authentication cookie is set.
1126           *
1127           * @since 2.5.0
1128           * @since 4.9.0 The `$token` parameter was added.
1129           *
1130           * @param string $auth_cookie Authentication cookie value.
1131           * @param int    $expire      The time the login grace period expires as a UNIX timestamp.
1132           *                            Default is 12 hours past the cookie's expiration time.
1133           * @param int    $expiration  The time when the authentication cookie expires as a UNIX timestamp.
1134           *                            Default is 14 days from now.
1135           * @param int    $user_id     User ID.
1136           * @param string $scheme      Authentication scheme. Values include 'auth' or 'secure_auth'.
1137           * @param string $token       User's session token to use for this cookie.
1138           */
1139          do_action( 'set_auth_cookie', $auth_cookie, $expire, $expiration, $user_id, $scheme, $token );
1140  
1141          /**
1142           * Fires immediately before the logged-in authentication cookie is set.
1143           *
1144           * @since 2.6.0
1145           * @since 4.9.0 The `$token` parameter was added.
1146           *
1147           * @param string $logged_in_cookie The logged-in cookie value.
1148           * @param int    $expire           The time the login grace period expires as a UNIX timestamp.
1149           *                                 Default is 12 hours past the cookie's expiration time.
1150           * @param int    $expiration       The time when the logged-in authentication cookie expires as a UNIX timestamp.
1151           *                                 Default is 14 days from now.
1152           * @param int    $user_id          User ID.
1153           * @param string $scheme           Authentication scheme. Default 'logged_in'.
1154           * @param string $token            User's session token to use for this cookie.
1155           */
1156          do_action( 'set_logged_in_cookie', $logged_in_cookie, $expire, $expiration, $user_id, 'logged_in', $token );
1157  
1158          /**
1159           * Allows preventing auth cookies from actually being sent to the client.
1160           *
1161           * @since 4.7.4
1162           * @since 6.2.0 The `$expire`, `$expiration`, `$user_id`, `$scheme`, and `$token` parameters were added.
1163           *
1164           * @param bool   $send       Whether to send auth cookies to the client. Default true.
1165           * @param int    $expire     The time the login grace period expires as a UNIX timestamp.
1166           *                           Default is 12 hours past the cookie's expiration time. Zero when clearing cookies.
1167           * @param int    $expiration The time when the logged-in authentication cookie expires as a UNIX timestamp.
1168           *                           Default is 14 days from now. Zero when clearing cookies.
1169           * @param int    $user_id    User ID. Zero when clearing cookies.
1170           * @param string $scheme     Authentication scheme. Values include 'auth' or 'secure_auth'.
1171           *                           Empty string when clearing cookies.
1172           * @param string $token      User's session token to use for this cookie. Empty string when clearing cookies.
1173           */
1174          if ( ! apply_filters( 'send_auth_cookies', true, $expire, $expiration, $user_id, $scheme, $token ) ) {
1175              return;
1176          }
1177  
1178          setcookie( $auth_cookie_name, $auth_cookie, $expire, PLUGINS_COOKIE_PATH, COOKIE_DOMAIN, $secure, true );
1179          setcookie( $auth_cookie_name, $auth_cookie, $expire, ADMIN_COOKIE_PATH, COOKIE_DOMAIN, $secure, true );
1180          setcookie( LOGGED_IN_COOKIE, $logged_in_cookie, $expire, COOKIEPATH, COOKIE_DOMAIN, $secure_logged_in_cookie, true );
1181          if ( COOKIEPATH !== SITECOOKIEPATH ) {
1182              setcookie( LOGGED_IN_COOKIE, $logged_in_cookie, $expire, SITECOOKIEPATH, COOKIE_DOMAIN, $secure_logged_in_cookie, true );
1183          }
1184      }
1185  endif;
1186  
1187  if ( ! function_exists( 'wp_clear_auth_cookie' ) ) :
1188      /**
1189       * Removes all of the cookies associated with authentication.
1190       *
1191       * @since 2.5.0
1192       */
1193  	function wp_clear_auth_cookie() {
1194          /**
1195           * Fires just before the authentication cookies are cleared.
1196           *
1197           * @since 2.7.0
1198           */
1199          do_action( 'clear_auth_cookie' );
1200  
1201          /** This filter is documented in wp-includes/pluggable.php */
1202          if ( ! apply_filters( 'send_auth_cookies', true, 0, 0, 0, '', '' ) ) {
1203              return;
1204          }
1205  
1206          // Auth cookies.
1207          setcookie( AUTH_COOKIE, ' ', time() - YEAR_IN_SECONDS, ADMIN_COOKIE_PATH, COOKIE_DOMAIN );
1208          setcookie( SECURE_AUTH_COOKIE, ' ', time() - YEAR_IN_SECONDS, ADMIN_COOKIE_PATH, COOKIE_DOMAIN );
1209          setcookie( AUTH_COOKIE, ' ', time() - YEAR_IN_SECONDS, PLUGINS_COOKIE_PATH, COOKIE_DOMAIN );
1210          setcookie( SECURE_AUTH_COOKIE, ' ', time() - YEAR_IN_SECONDS, PLUGINS_COOKIE_PATH, COOKIE_DOMAIN );
1211          setcookie( LOGGED_IN_COOKIE, ' ', time() - YEAR_IN_SECONDS, COOKIEPATH, COOKIE_DOMAIN );
1212          setcookie( LOGGED_IN_COOKIE, ' ', time() - YEAR_IN_SECONDS, SITECOOKIEPATH, COOKIE_DOMAIN );
1213  
1214          // Settings cookies.
1215          setcookie( 'wp-settings-' . get_current_user_id(), ' ', time() - YEAR_IN_SECONDS, SITECOOKIEPATH );
1216          setcookie( 'wp-settings-time-' . get_current_user_id(), ' ', time() - YEAR_IN_SECONDS, SITECOOKIEPATH );
1217  
1218          // Old cookies.
1219          setcookie( AUTH_COOKIE, ' ', time() - YEAR_IN_SECONDS, COOKIEPATH, COOKIE_DOMAIN );
1220          setcookie( AUTH_COOKIE, ' ', time() - YEAR_IN_SECONDS, SITECOOKIEPATH, COOKIE_DOMAIN );
1221          setcookie( SECURE_AUTH_COOKIE, ' ', time() - YEAR_IN_SECONDS, COOKIEPATH, COOKIE_DOMAIN );
1222          setcookie( SECURE_AUTH_COOKIE, ' ', time() - YEAR_IN_SECONDS, SITECOOKIEPATH, COOKIE_DOMAIN );
1223  
1224          // Even older cookies.
1225          setcookie( USER_COOKIE, ' ', time() - YEAR_IN_SECONDS, COOKIEPATH, COOKIE_DOMAIN );
1226          setcookie( PASS_COOKIE, ' ', time() - YEAR_IN_SECONDS, COOKIEPATH, COOKIE_DOMAIN );
1227          setcookie( USER_COOKIE, ' ', time() - YEAR_IN_SECONDS, SITECOOKIEPATH, COOKIE_DOMAIN );
1228          setcookie( PASS_COOKIE, ' ', time() - YEAR_IN_SECONDS, SITECOOKIEPATH, COOKIE_DOMAIN );
1229  
1230          // Post password cookie.
1231          setcookie( 'wp-postpass_' . COOKIEHASH, ' ', time() - YEAR_IN_SECONDS, COOKIEPATH, COOKIE_DOMAIN );
1232      }
1233  endif;
1234  
1235  if ( ! function_exists( 'is_user_logged_in' ) ) :
1236      /**
1237       * Determines whether the current visitor is a logged in user.
1238       *
1239       * For more information on this and similar theme functions, check out
1240       * the {@link https://developer.wordpress.org/themes/basics/conditional-tags/
1241       * Conditional Tags} article in the Theme Developer Handbook.
1242       *
1243       * @since 2.0.0
1244       *
1245       * @return bool True if user is logged in, false if not logged in.
1246       */
1247  	function is_user_logged_in() {
1248          $user = wp_get_current_user();
1249  
1250          return $user->exists();
1251      }
1252  endif;
1253  
1254  if ( ! function_exists( 'auth_redirect' ) ) :
1255      /**
1256       * Checks if a user is logged in, if not it redirects them to the login page.
1257       *
1258       * When this code is called from a page, it checks to see if the user viewing the page is logged in.
1259       * If the user is not logged in, they are redirected to the login page. The user is redirected
1260       * in such a way that, upon logging in, they will be sent directly to the page they were originally
1261       * trying to access.
1262       *
1263       * @since 1.5.0
1264       */
1265  	function auth_redirect() {
1266          $secure = ( is_ssl() || force_ssl_admin() );
1267  
1268          /**
1269           * Filters whether to use a secure authentication redirect.
1270           *
1271           * @since 3.1.0
1272           *
1273           * @param bool $secure Whether to use a secure authentication redirect. Default false.
1274           */
1275          $secure = apply_filters( 'secure_auth_redirect', $secure );
1276  
1277          // If https is required and request is http, redirect.
1278          if ( $secure && ! is_ssl() && str_contains( $_SERVER['REQUEST_URI'], 'wp-admin' ) ) {
1279              if ( str_starts_with( $_SERVER['REQUEST_URI'], 'http' ) ) {
1280                  wp_redirect( set_url_scheme( $_SERVER['REQUEST_URI'], 'https' ) );
1281                  exit;
1282              } else {
1283                  wp_redirect( 'https://' . $_SERVER['HTTP_HOST'] . $_SERVER['REQUEST_URI'] );
1284                  exit;
1285              }
1286          }
1287  
1288          /**
1289           * Filters the authentication redirect scheme.
1290           *
1291           * @since 2.9.0
1292           *
1293           * @param string $scheme Authentication redirect scheme. Default empty.
1294           */
1295          $scheme = apply_filters( 'auth_redirect_scheme', '' );
1296  
1297          $user_id = wp_validate_auth_cookie( '', $scheme );
1298          if ( $user_id ) {
1299              /**
1300               * Fires before the authentication redirect.
1301               *
1302               * @since 2.8.0
1303               *
1304               * @param int $user_id User ID.
1305               */
1306              do_action( 'auth_redirect', $user_id );
1307  
1308              // If the user wants ssl but the session is not ssl, redirect.
1309              if ( ! $secure && get_user_option( 'use_ssl', $user_id ) && str_contains( $_SERVER['REQUEST_URI'], 'wp-admin' ) ) {
1310                  if ( str_starts_with( $_SERVER['REQUEST_URI'], 'http' ) ) {
1311                      wp_redirect( set_url_scheme( $_SERVER['REQUEST_URI'], 'https' ) );
1312                      exit;
1313                  } else {
1314                      wp_redirect( 'https://' . $_SERVER['HTTP_HOST'] . $_SERVER['REQUEST_URI'] );
1315                      exit;
1316                  }
1317              }
1318  
1319              return; // The cookie is good, so we're done.
1320          }
1321  
1322          // The cookie is no good, so force login.
1323          nocache_headers();
1324  
1325          if ( str_contains( $_SERVER['REQUEST_URI'], '/options.php' ) && wp_get_referer() ) {
1326              $redirect = wp_get_referer();
1327          } else {
1328              $redirect = set_url_scheme( 'http://' . $_SERVER['HTTP_HOST'] . $_SERVER['REQUEST_URI'] );
1329          }
1330  
1331          $login_url = wp_login_url( $redirect, true );
1332  
1333          wp_redirect( $login_url );
1334          exit;
1335      }
1336  endif;
1337  
1338  if ( ! function_exists( 'check_admin_referer' ) ) :
1339      /**
1340       * Ensures intent by verifying that a user was referred from another admin page with the correct security nonce.
1341       *
1342       * This function ensures the user intends to perform a given action, which helps protect against clickjacking style
1343       * attacks. It verifies intent, not authorization, therefore it does not verify the user's capabilities. This should
1344       * be performed with `current_user_can()` or similar.
1345       *
1346       * If the nonce value is invalid, the function will exit with an "Are You Sure?" style message.
1347       *
1348       * @since 1.2.0
1349       * @since 2.5.0 The `$query_arg` parameter was added.
1350       *
1351       * @param int|string $action    The nonce action.
1352       * @param string     $query_arg Optional. Key to check for nonce in `$_REQUEST`. Default '_wpnonce'.
1353       * @return int|false 1 if the nonce is valid and generated between 0-12 hours ago,
1354       *                   2 if the nonce is valid and generated between 12-24 hours ago.
1355       *                   False if the nonce is invalid.
1356       */
1357  	function check_admin_referer( $action = -1, $query_arg = '_wpnonce' ) {
1358          if ( -1 === $action ) {
1359              _doing_it_wrong( __FUNCTION__, __( 'You should specify an action to be verified by using the first parameter.' ), '3.2.0' );
1360          }
1361  
1362          $adminurl = strtolower( admin_url() );
1363          $referer  = strtolower( wp_get_referer() );
1364          $result   = isset( $_REQUEST[ $query_arg ] ) ? wp_verify_nonce( $_REQUEST[ $query_arg ], $action ) : false;
1365  
1366          /**
1367           * Fires once the admin request has been validated or not.
1368           *
1369           * @since 1.5.1
1370           *
1371           * @param string    $action The nonce action.
1372           * @param false|int $result False if the nonce is invalid, 1 if the nonce is valid and generated between
1373           *                          0-12 hours ago, 2 if the nonce is valid and generated between 12-24 hours ago.
1374           */
1375          do_action( 'check_admin_referer', $action, $result );
1376  
1377          if ( ! $result && ! ( -1 === $action && str_starts_with( $referer, $adminurl ) ) ) {
1378              wp_nonce_ays( $action );
1379              die();
1380          }
1381  
1382          return $result;
1383      }
1384  endif;
1385  
1386  if ( ! function_exists( 'check_ajax_referer' ) ) :
1387      /**
1388       * Verifies the Ajax request to prevent processing requests external of the blog.
1389       *
1390       * @since 2.0.3
1391       *
1392       * @param int|string   $action    Action nonce.
1393       * @param false|string $query_arg Optional. Key to check for the nonce in `$_REQUEST` (since 2.5). If false,
1394       *                                `$_REQUEST` values will be evaluated for '_ajax_nonce', and '_wpnonce'
1395       *                                (in that order). Default false.
1396       * @param bool         $stop      Optional. Whether to stop early when the nonce cannot be verified.
1397       *                                Default true.
1398       * @return int|false 1 if the nonce is valid and generated between 0-12 hours ago,
1399       *                   2 if the nonce is valid and generated between 12-24 hours ago.
1400       *                   False if the nonce is invalid.
1401       */
1402  	function check_ajax_referer( $action = -1, $query_arg = false, $stop = true ) {
1403          if ( -1 === $action ) {
1404              _doing_it_wrong( __FUNCTION__, __( 'You should specify an action to be verified by using the first parameter.' ), '4.7.0' );
1405          }
1406  
1407          $nonce = '';
1408  
1409          if ( $query_arg && isset( $_REQUEST[ $query_arg ] ) ) {
1410              $nonce = $_REQUEST[ $query_arg ];
1411          } elseif ( isset( $_REQUEST['_ajax_nonce'] ) ) {
1412              $nonce = $_REQUEST['_ajax_nonce'];
1413          } elseif ( isset( $_REQUEST['_wpnonce'] ) ) {
1414              $nonce = $_REQUEST['_wpnonce'];
1415          }
1416  
1417          $result = wp_verify_nonce( $nonce, $action );
1418  
1419          /**
1420           * Fires once the Ajax request has been validated or not.
1421           *
1422           * @since 2.1.0
1423           *
1424           * @param string    $action The Ajax nonce action.
1425           * @param false|int $result False if the nonce is invalid, 1 if the nonce is valid and generated between
1426           *                          0-12 hours ago, 2 if the nonce is valid and generated between 12-24 hours ago.
1427           */
1428          do_action( 'check_ajax_referer', $action, $result );
1429  
1430          if ( $stop && false === $result ) {
1431              if ( wp_doing_ajax() ) {
1432                  wp_die( -1, 403 );
1433              } else {
1434                  die( '-1' );
1435              }
1436          }
1437  
1438          return $result;
1439      }
1440  endif;
1441  
1442  if ( ! function_exists( 'wp_redirect' ) ) :
1443      /**
1444       * Redirects to another page.
1445       *
1446       * Note: wp_redirect() does not exit automatically, and should almost always be
1447       * followed by a call to `exit;`:
1448       *
1449       *     wp_redirect( $url );
1450       *     exit;
1451       *
1452       * Exiting can also be selectively manipulated by using wp_redirect() as a conditional
1453       * in conjunction with the {@see 'wp_redirect'} and {@see 'wp_redirect_status'} filters:
1454       *
1455       *     if ( wp_redirect( $url ) ) {
1456       *         exit;
1457       *     }
1458       *
1459       * @since 1.5.1
1460       * @since 5.1.0 The `$x_redirect_by` parameter was added.
1461       * @since 5.4.0 On invalid status codes, wp_die() is called.
1462       *
1463       * @global bool $is_IIS
1464       *
1465       * @param string       $location      The path or URL to redirect to.
1466       * @param int          $status        Optional. HTTP response status code to use. Default '302' (Moved Temporarily).
1467       * @param string|false $x_redirect_by Optional. The application doing the redirect or false to omit. Default 'WordPress'.
1468       * @return bool False if the redirect was canceled, true otherwise.
1469       */
1470  	function wp_redirect( $location, $status = 302, $x_redirect_by = 'WordPress' ) {
1471          global $is_IIS;
1472  
1473          /**
1474           * Filters the redirect location.
1475           *
1476           * @since 2.1.0
1477           *
1478           * @param string $location The path or URL to redirect to.
1479           * @param int    $status   The HTTP response status code to use.
1480           */
1481          $location = apply_filters( 'wp_redirect', $location, $status );
1482  
1483          /**
1484           * Filters the redirect HTTP response status code to use.
1485           *
1486           * @since 2.3.0
1487           *
1488           * @param int    $status   The HTTP response status code to use.
1489           * @param string $location The path or URL to redirect to.
1490           */
1491          $status = apply_filters( 'wp_redirect_status', $status, $location );
1492  
1493          if ( ! $location ) {
1494              return false;
1495          }
1496  
1497          if ( $status < 300 || 399 < $status ) {
1498              wp_die( __( 'HTTP redirect status code must be a redirection code, 3xx.' ) );
1499          }
1500  
1501          $location = wp_sanitize_redirect( $location );
1502  
1503          if ( ! $is_IIS && 'cgi-fcgi' !== PHP_SAPI ) {
1504              status_header( $status ); // This causes problems on IIS and some FastCGI setups.
1505          }
1506  
1507          /**
1508           * Filters the X-Redirect-By header.
1509           *
1510           * Allows applications to identify themselves when they're doing a redirect.
1511           *
1512           * @since 5.1.0
1513           *
1514           * @param string|false $x_redirect_by The application doing the redirect or false to omit the header.
1515           * @param int          $status        Status code to use.
1516           * @param string       $location      The path to redirect to.
1517           */
1518          $x_redirect_by = apply_filters( 'x_redirect_by', $x_redirect_by, $status, $location );
1519          if ( is_string( $x_redirect_by ) ) {
1520              header( "X-Redirect-By: $x_redirect_by" );
1521          }
1522  
1523          header( "Location: $location", true, $status );
1524  
1525          return true;
1526      }
1527  endif;
1528  
1529  if ( ! function_exists( 'wp_sanitize_redirect' ) ) :
1530      /**
1531       * Sanitizes a URL for use in a redirect.
1532       *
1533       * @since 2.3.0
1534       *
1535       * @param string $location The path to redirect to.
1536       * @return string Redirect-sanitized URL.
1537       */
1538  	function wp_sanitize_redirect( $location ) {
1539          // Encode spaces.
1540          $location = str_replace( ' ', '%20', $location );
1541  
1542          $regex    = '/
1543          (
1544              (?: [\xC2-\xDF][\x80-\xBF]        # double-byte sequences   110xxxxx 10xxxxxx
1545              |   \xE0[\xA0-\xBF][\x80-\xBF]    # triple-byte sequences   1110xxxx 10xxxxxx * 2
1546              |   [\xE1-\xEC][\x80-\xBF]{2}
1547              |   \xED[\x80-\x9F][\x80-\xBF]
1548              |   [\xEE-\xEF][\x80-\xBF]{2}
1549              |   \xF0[\x90-\xBF][\x80-\xBF]{2} # four-byte sequences   11110xxx 10xxxxxx * 3
1550              |   [\xF1-\xF3][\x80-\xBF]{3}
1551              |   \xF4[\x80-\x8F][\x80-\xBF]{2}
1552          ){1,40}                              # ...one or more times
1553          )/x';
1554          $location = preg_replace_callback( $regex, '_wp_sanitize_utf8_in_redirect', $location );
1555          $location = preg_replace( '|[^a-z0-9-~+_.?#=&;,/:%!*\[\]()@]|i', '', $location );
1556          $location = wp_kses_no_null( $location );
1557  
1558          // Remove %0D and %0A from location.
1559          $strip = array( '%0d', '%0a', '%0D', '%0A' );
1560          return _deep_replace( $strip, $location );
1561      }
1562  
1563      /**
1564       * URL encodes UTF-8 characters in a URL.
1565       *
1566       * @ignore
1567       * @since 4.2.0
1568       * @access private
1569       *
1570       * @see wp_sanitize_redirect()
1571       *
1572       * @param array $matches RegEx matches against the redirect location.
1573       * @return string URL-encoded version of the first RegEx match.
1574       */
1575  	function _wp_sanitize_utf8_in_redirect( $matches ) {
1576          return urlencode( $matches[0] );
1577      }
1578  endif;
1579  
1580  if ( ! function_exists( 'wp_safe_redirect' ) ) :
1581      /**
1582       * Performs a safe (local) redirect, using wp_redirect().
1583       *
1584       * Checks whether the $location is using an allowed host, if it has an absolute
1585       * path. A plugin can therefore set or remove allowed host(s) to or from the
1586       * list.
1587       *
1588       * If the host is not allowed, then the redirect defaults to wp-admin on the siteurl
1589       * instead. This prevents malicious redirects which redirect to another host,
1590       * but only used in a few places.
1591       *
1592       * Note: wp_safe_redirect() does not exit automatically, and should almost always be
1593       * followed by a call to `exit;`:
1594       *
1595       *     wp_safe_redirect( $url );
1596       *     exit;
1597       *
1598       * Exiting can also be selectively manipulated by using wp_safe_redirect() as a conditional
1599       * in conjunction with the {@see 'wp_redirect'} and {@see 'wp_redirect_status'} filters:
1600       *
1601       *     if ( wp_safe_redirect( $url ) ) {
1602       *         exit;
1603       *     }
1604       *
1605       * @since 2.3.0
1606       * @since 5.1.0 The return value from wp_redirect() is now passed on, and the `$x_redirect_by` parameter was added.
1607       *
1608       * @param string       $location      The path or URL to redirect to.
1609       * @param int          $status        Optional. HTTP response status code to use. Default '302' (Moved Temporarily).
1610       * @param string|false $x_redirect_by Optional. The application doing the redirect or false to omit. Default 'WordPress'.
1611       * @return bool False if the redirect was canceled, true otherwise.
1612       */
1613  	function wp_safe_redirect( $location, $status = 302, $x_redirect_by = 'WordPress' ) {
1614  
1615          // Need to look at the URL the way it will end up in wp_redirect().
1616          $location = wp_sanitize_redirect( $location );
1617  
1618          /**
1619           * Filters the redirect fallback URL for when the provided redirect is not safe (local).
1620           *
1621           * @since 4.3.0
1622           *
1623           * @param string $fallback_url The fallback URL to use by default.
1624           * @param int    $status       The HTTP response status code to use.
1625           */
1626          $fallback_url = apply_filters( 'wp_safe_redirect_fallback', admin_url(), $status );
1627  
1628          $location = wp_validate_redirect( $location, $fallback_url );
1629  
1630          return wp_redirect( $location, $status, $x_redirect_by );
1631      }
1632  endif;
1633  
1634  if ( ! function_exists( 'wp_validate_redirect' ) ) :
1635      /**
1636       * Validates a URL for use in a redirect.
1637       *
1638       * Checks whether the $location is using an allowed host, if it has an absolute
1639       * path. A plugin can therefore set or remove allowed host(s) to or from the
1640       * list.
1641       *
1642       * If the host is not allowed, then the redirect is to $fallback_url supplied.
1643       *
1644       * @since 2.8.1
1645       *
1646       * @param string $location     The redirect to validate.
1647       * @param string $fallback_url The value to return if $location is not allowed.
1648       * @return string Redirect-sanitized URL.
1649       */
1650  	function wp_validate_redirect( $location, $fallback_url = '' ) {
1651          $location = wp_sanitize_redirect( trim( $location, " \t\n\r\0\x08\x0B" ) );
1652          // Browsers will assume 'http' is your protocol, and will obey a redirect to a URL starting with '//'.
1653          if ( str_starts_with( $location, '//' ) ) {
1654              $location = 'http:' . $location;
1655          }
1656  
1657          /*
1658           * In PHP 5 parse_url() may fail if the URL query part contains 'http://'.
1659           * See https://bugs.php.net/bug.php?id=38143
1660           */
1661          $cut  = strpos( $location, '?' );
1662          $test = $cut ? substr( $location, 0, $cut ) : $location;
1663  
1664          $lp = parse_url( $test );
1665  
1666          // Give up if malformed URL.
1667          if ( false === $lp ) {
1668              return $fallback_url;
1669          }
1670  
1671          // Allow only 'http' and 'https' schemes. No 'data:', etc.
1672          if ( isset( $lp['scheme'] ) && ! ( 'http' === $lp['scheme'] || 'https' === $lp['scheme'] ) ) {
1673              return $fallback_url;
1674          }
1675  
1676          if ( ! isset( $lp['host'] ) && ! empty( $lp['path'] ) && '/' !== $lp['path'][0] ) {
1677              $path = '';
1678              if ( ! empty( $_SERVER['REQUEST_URI'] ) ) {
1679                  $path = dirname( parse_url( 'http://placeholder' . $_SERVER['REQUEST_URI'], PHP_URL_PATH ) . '?' );
1680                  $path = wp_normalize_path( $path );
1681              }
1682              $location = '/' . ltrim( $path . '/', '/' ) . $location;
1683          }
1684  
1685          /*
1686           * Reject if certain components are set but host is not.
1687           * This catches URLs like https:host.com for which parse_url() does not set the host field.
1688           */
1689          if ( ! isset( $lp['host'] ) && ( isset( $lp['scheme'] ) || isset( $lp['user'] ) || isset( $lp['pass'] ) || isset( $lp['port'] ) ) ) {
1690              return $fallback_url;
1691          }
1692  
1693          // Reject malformed components parse_url() can return on odd inputs.
1694          foreach ( array( 'user', 'pass', 'host' ) as $component ) {
1695              if ( isset( $lp[ $component ] ) && strpbrk( $lp[ $component ], ':/?#@' ) ) {
1696                  return $fallback_url;
1697              }
1698          }
1699  
1700          $wpp = parse_url( home_url() );
1701  
1702          /**
1703           * Filters the list of allowed hosts to redirect to.
1704           *
1705           * @since 2.3.0
1706           *
1707           * @param string[] $hosts An array of allowed host names.
1708           * @param string   $host  The host name of the redirect destination; empty string if not set.
1709           */
1710          $allowed_hosts = (array) apply_filters( 'allowed_redirect_hosts', array( $wpp['host'] ), isset( $lp['host'] ) ? $lp['host'] : '' );
1711  
1712          if ( isset( $lp['host'] ) && ( ! in_array( $lp['host'], $allowed_hosts, true ) && strtolower( $wpp['host'] ) !== $lp['host'] ) ) {
1713              $location = $fallback_url;
1714          }
1715  
1716          return $location;
1717      }
1718  endif;
1719  
1720  if ( ! function_exists( 'wp_notify_postauthor' ) ) :
1721      /**
1722       * Notifies an author (and/or others) of a comment/trackback/pingback on a post.
1723       *
1724       * @since 1.0.0
1725       *
1726       * @param int|WP_Comment $comment_id Comment ID or WP_Comment object.
1727       * @param string         $deprecated Not used.
1728       * @return bool True on completion. False if no email addresses were specified.
1729       */
1730  	function wp_notify_postauthor( $comment_id, $deprecated = null ) {
1731          if ( null !== $deprecated ) {
1732              _deprecated_argument( __FUNCTION__, '3.8.0' );
1733          }
1734  
1735          $comment = get_comment( $comment_id );
1736          if ( empty( $comment ) || empty( $comment->comment_post_ID ) ) {
1737              return false;
1738          }
1739  
1740          $post   = get_post( $comment->comment_post_ID );
1741          $author = get_userdata( $post->post_author );
1742  
1743          // Who to notify? By default, just the post author, but others can be added.
1744          $emails = array();
1745          if ( $author ) {
1746              $emails[] = $author->user_email;
1747          }
1748  
1749          /**
1750           * Filters the list of email addresses to receive a comment notification.
1751           *
1752           * By default, only post authors are notified of comments. This filter allows
1753           * others to be added.
1754           *
1755           * @since 3.7.0
1756           *
1757           * @param string[] $emails     An array of email addresses to receive a comment notification.
1758           * @param string   $comment_id The comment ID as a numeric string.
1759           */
1760          $emails = apply_filters( 'comment_notification_recipients', $emails, $comment->comment_ID );
1761          $emails = array_filter( $emails );
1762  
1763          // If there are no addresses to send the comment to, bail.
1764          if ( ! count( $emails ) ) {
1765              return false;
1766          }
1767  
1768          // Facilitate unsetting below without knowing the keys.
1769          $emails = array_flip( $emails );
1770  
1771          /**
1772           * Filters whether to notify comment authors of their comments on their own posts.
1773           *
1774           * By default, comment authors aren't notified of their comments on their own
1775           * posts. This filter allows you to override that.
1776           *
1777           * @since 3.8.0
1778           *
1779           * @param bool   $notify     Whether to notify the post author of their own comment.
1780           *                           Default false.
1781           * @param string $comment_id The comment ID as a numeric string.
1782           */
1783          $notify_author = apply_filters( 'comment_notification_notify_author', false, $comment->comment_ID );
1784  
1785          // The comment was left by the author.
1786          if ( $author && ! $notify_author && (int) $comment->user_id === (int) $post->post_author ) {
1787              unset( $emails[ $author->user_email ] );
1788          }
1789  
1790          // The author moderated a comment on their own post.
1791          if ( $author && ! $notify_author && get_current_user_id() === (int) $post->post_author ) {
1792              unset( $emails[ $author->user_email ] );
1793          }
1794  
1795          // The post author is no longer a member of the blog.
1796          if ( $author && ! $notify_author && ! user_can( $post->post_author, 'read_post', $post->ID ) ) {
1797              unset( $emails[ $author->user_email ] );
1798          }
1799  
1800          // If there's no email to send the comment to, bail, otherwise flip array back around for use below.
1801          if ( ! count( $emails ) ) {
1802              return false;
1803          } else {
1804              $emails = array_flip( $emails );
1805          }
1806  
1807          $comment_author_domain = '';
1808          if ( WP_Http::is_ip_address( $comment->comment_author_IP ) ) {
1809              $comment_author_domain = gethostbyaddr( $comment->comment_author_IP );
1810          }
1811  
1812          /*
1813           * The blogname option is escaped with esc_html() on the way into the database in sanitize_option().
1814           * We want to reverse this for the plain text arena of emails.
1815           */
1816          $blogname        = wp_specialchars_decode( get_option( 'blogname' ), ENT_QUOTES );
1817          $comment_content = wp_specialchars_decode( $comment->comment_content );
1818  
1819          $wp_email = 'wordpress@' . preg_replace( '#^www\.#', '', wp_parse_url( network_home_url(), PHP_URL_HOST ) );
1820  
1821          if ( '' === $comment->comment_author ) {
1822              $from = "From: \"$blogname\" <$wp_email>";
1823              if ( '' !== $comment->comment_author_email ) {
1824                  $reply_to = "Reply-To: $comment->comment_author_email";
1825              }
1826          } else {
1827              $from = "From: \"$comment->comment_author\" <$wp_email>";
1828              if ( '' !== $comment->comment_author_email ) {
1829                  $reply_to = "Reply-To: \"$comment->comment_author_email\" <$comment->comment_author_email>";
1830              }
1831          }
1832  
1833          $message_headers = "$from\n"
1834          . 'Content-Type: text/plain; charset="' . get_option( 'blog_charset' ) . "\"\n";
1835  
1836          if ( isset( $reply_to ) ) {
1837              $message_headers .= $reply_to . "\n";
1838          }
1839  
1840          /**
1841           * Filters the comment notification email headers.
1842           *
1843           * @since 1.5.2
1844           *
1845           * @param string $message_headers Headers for the comment notification email.
1846           * @param string $comment_id      Comment ID as a numeric string.
1847           */
1848          $message_headers = apply_filters( 'comment_notification_headers', $message_headers, $comment->comment_ID );
1849  
1850          foreach ( $emails as $email ) {
1851              $user = get_user_by( 'email', $email );
1852  
1853              if ( $user ) {
1854                  $switched_locale = switch_to_user_locale( $user->ID );
1855              } else {
1856                  $switched_locale = switch_to_locale( get_locale() );
1857              }
1858  
1859              switch ( $comment->comment_type ) {
1860                  case 'trackback':
1861                      /* translators: %s: Post title. */
1862                      $notify_message = sprintf( __( 'New trackback on your post "%s"' ), $post->post_title ) . "\r\n";
1863                      /* translators: 1: Trackback/pingback website name, 2: Website IP address, 3: Website hostname. */
1864                      $notify_message .= sprintf( __( 'Website: %1$s (IP address: %2$s, %3$s)' ), $comment->comment_author, $comment->comment_author_IP, $comment_author_domain ) . "\r\n";
1865                      /* translators: %s: Trackback/pingback/comment author URL. */
1866                      $notify_message .= sprintf( __( 'URL: %s' ), $comment->comment_author_url ) . "\r\n";
1867                      /* translators: %s: Comment text. */
1868                      $notify_message .= sprintf( __( 'Comment: %s' ), "\r\n" . $comment_content ) . "\r\n\r\n";
1869                      $notify_message .= __( 'You can see all trackbacks on this post here:' ) . "\r\n";
1870                      /* translators: Trackback notification email subject. 1: Site title, 2: Post title. */
1871                      $subject = sprintf( __( '[%1$s] Trackback: "%2$s"' ), $blogname, $post->post_title );
1872                      break;
1873  
1874                  case 'pingback':
1875                      /* translators: %s: Post title. */
1876                      $notify_message = sprintf( __( 'New pingback on your post "%s"' ), $post->post_title ) . "\r\n";
1877                      /* translators: 1: Trackback/pingback website name, 2: Website IP address, 3: Website hostname. */
1878                      $notify_message .= sprintf( __( 'Website: %1$s (IP address: %2$s, %3$s)' ), $comment->comment_author, $comment->comment_author_IP, $comment_author_domain ) . "\r\n";
1879                      /* translators: %s: Trackback/pingback/comment author URL. */
1880                      $notify_message .= sprintf( __( 'URL: %s' ), $comment->comment_author_url ) . "\r\n";
1881                      /* translators: %s: Comment text. */
1882                      $notify_message .= sprintf( __( 'Comment: %s' ), "\r\n" . $comment_content ) . "\r\n\r\n";
1883                      $notify_message .= __( 'You can see all pingbacks on this post here:' ) . "\r\n";
1884                      /* translators: Pingback notification email subject. 1: Site title, 2: Post title. */
1885                      $subject = sprintf( __( '[%1$s] Pingback: "%2$s"' ), $blogname, $post->post_title );
1886                      break;
1887  
1888                  default: // Comments.
1889                      /* translators: %s: Post title. */
1890                      $notify_message = sprintf( __( 'New comment on your post "%s"' ), $post->post_title ) . "\r\n";
1891                      /* translators: 1: Comment author's name, 2: Comment author's IP address, 3: Comment author's hostname. */
1892                      $notify_message .= sprintf( __( 'Author: %1$s (IP address: %2$s, %3$s)' ), $comment->comment_author, $comment->comment_author_IP, $comment_author_domain ) . "\r\n";
1893                      /* translators: %s: Comment author email. */
1894                      $notify_message .= sprintf( __( 'Email: %s' ), $comment->comment_author_email ) . "\r\n";
1895                      /* translators: %s: Trackback/pingback/comment author URL. */
1896                      $notify_message .= sprintf( __( 'URL: %s' ), $comment->comment_author_url ) . "\r\n";
1897  
1898                      if ( $comment->comment_parent && user_can( $post->post_author, 'edit_comment', $comment->comment_parent ) ) {
1899                          /* translators: Comment moderation. %s: Parent comment edit URL. */
1900                          $notify_message .= sprintf( __( 'In reply to: %s' ), admin_url( "comment.php?action=editcomment&c={$comment->comment_parent}#wpbody-content" ) ) . "\r\n";
1901                      }
1902  
1903                      /* translators: %s: Comment text. */
1904                      $notify_message .= sprintf( __( 'Comment: %s' ), "\r\n" . $comment_content ) . "\r\n\r\n";
1905                      $notify_message .= __( 'You can see all comments on this post here:' ) . "\r\n";
1906                      /* translators: Comment notification email subject. 1: Site title, 2: Post title. */
1907                      $subject = sprintf( __( '[%1$s] Comment: "%2$s"' ), $blogname, $post->post_title );
1908                      break;
1909              }
1910  
1911              $notify_message .= get_permalink( $comment->comment_post_ID ) . "#comments\r\n\r\n";
1912              /* translators: %s: Comment URL. */
1913              $notify_message .= sprintf( __( 'Permalink: %s' ), get_comment_link( $comment ) ) . "\r\n";
1914  
1915              if ( user_can( $post->post_author, 'edit_comment', $comment->comment_ID ) ) {
1916                  if ( EMPTY_TRASH_DAYS ) {
1917                      /* translators: Comment moderation. %s: Comment action URL. */
1918                      $notify_message .= sprintf( __( 'Trash it: %s' ), admin_url( "comment.php?action=trash&c={$comment->comment_ID}#wpbody-content" ) ) . "\r\n";
1919                  } else {
1920                      /* translators: Comment moderation. %s: Comment action URL. */
1921                      $notify_message .= sprintf( __( 'Delete it: %s' ), admin_url( "comment.php?action=delete&c={$comment->comment_ID}#wpbody-content" ) ) . "\r\n";
1922                  }
1923                  /* translators: Comment moderation. %s: Comment action URL. */
1924                  $notify_message .= sprintf( __( 'Spam it: %s' ), admin_url( "comment.php?action=spam&c={$comment->comment_ID}#wpbody-content" ) ) . "\r\n";
1925              }
1926  
1927              /**
1928               * Filters the comment notification email text.
1929               *
1930               * @since 1.5.2
1931               *
1932               * @param string $notify_message The comment notification email text.
1933               * @param string $comment_id     Comment ID as a numeric string.
1934               */
1935              $notify_message = apply_filters( 'comment_notification_text', $notify_message, $comment->comment_ID );
1936  
1937              /**
1938               * Filters the comment notification email subject.
1939               *
1940               * @since 1.5.2
1941               *
1942               * @param string $subject    The comment notification email subject.
1943               * @param string $comment_id Comment ID as a numeric string.
1944               */
1945              $subject = apply_filters( 'comment_notification_subject', $subject, $comment->comment_ID );
1946  
1947              wp_mail( $email, wp_specialchars_decode( $subject ), $notify_message, $message_headers );
1948  
1949              if ( $switched_locale ) {
1950                  restore_previous_locale();
1951              }
1952          }
1953  
1954          return true;
1955      }
1956  endif;
1957  
1958  if ( ! function_exists( 'wp_notify_moderator' ) ) :
1959      /**
1960       * Notifies the moderator of the site about a new comment that is awaiting approval.
1961       *
1962       * @since 1.0.0
1963       *
1964       * @global wpdb $wpdb WordPress database abstraction object.
1965       *
1966       * Uses the {@see 'notify_moderator'} filter to determine whether the site moderator
1967       * should be notified, overriding the site setting.
1968       *
1969       * @param int $comment_id Comment ID.
1970       * @return true Always returns true.
1971       */
1972  	function wp_notify_moderator( $comment_id ) {
1973          global $wpdb;
1974  
1975          $maybe_notify = get_option( 'moderation_notify' );
1976  
1977          /**
1978           * Filters whether to send the site moderator email notifications, overriding the site setting.
1979           *
1980           * @since 4.4.0
1981           *
1982           * @param bool $maybe_notify Whether to notify blog moderator.
1983           * @param int  $comment_id   The ID of the comment for the notification.
1984           */
1985          $maybe_notify = apply_filters( 'notify_moderator', $maybe_notify, $comment_id );
1986  
1987          if ( ! $maybe_notify ) {
1988              return true;
1989          }
1990  
1991          $comment = get_comment( $comment_id );
1992          $post    = get_post( $comment->comment_post_ID );
1993          $user    = get_userdata( $post->post_author );
1994          // Send to the administration and to the post author if the author can modify the comment.
1995          $emails = array( get_option( 'admin_email' ) );
1996          if ( $user && user_can( $user->ID, 'edit_comment', $comment_id ) && ! empty( $user->user_email ) ) {
1997              if ( 0 !== strcasecmp( $user->user_email, get_option( 'admin_email' ) ) ) {
1998                  $emails[] = $user->user_email;
1999              }
2000          }
2001  
2002          $comment_author_domain = '';
2003          if ( WP_Http::is_ip_address( $comment->comment_author_IP ) ) {
2004              $comment_author_domain = gethostbyaddr( $comment->comment_author_IP );
2005          }
2006  
2007          $comments_waiting = $wpdb->get_var( "SELECT COUNT(*) FROM $wpdb->comments WHERE comment_approved = '0'" );
2008  
2009          /*
2010           * The blogname option is escaped with esc_html() on the way into the database in sanitize_option().
2011           * We want to reverse this for the plain text arena of emails.
2012           */
2013          $blogname        = wp_specialchars_decode( get_option( 'blogname' ), ENT_QUOTES );
2014          $comment_content = wp_specialchars_decode( $comment->comment_content );
2015  
2016          $message_headers = '';
2017  
2018          /**
2019           * Filters the list of recipients for comment moderation emails.
2020           *
2021           * @since 3.7.0
2022           *
2023           * @param string[] $emails     List of email addresses to notify for comment moderation.
2024           * @param int      $comment_id Comment ID.
2025           */
2026          $emails = apply_filters( 'comment_moderation_recipients', $emails, $comment_id );
2027  
2028          /**
2029           * Filters the comment moderation email headers.
2030           *
2031           * @since 2.8.0
2032           *
2033           * @param string $message_headers Headers for the comment moderation email.
2034           * @param int    $comment_id      Comment ID.
2035           */
2036          $message_headers = apply_filters( 'comment_moderation_headers', $message_headers, $comment_id );
2037  
2038          foreach ( $emails as $email ) {
2039              $user = get_user_by( 'email', $email );
2040  
2041              if ( $user ) {
2042                  $switched_locale = switch_to_user_locale( $user->ID );
2043              } else {
2044                  $switched_locale = switch_to_locale( get_locale() );
2045              }
2046  
2047              switch ( $comment->comment_type ) {
2048                  case 'trackback':
2049                      /* translators: %s: Post title. */
2050                      $notify_message  = sprintf( __( 'A new trackback on the post "%s" is waiting for your approval' ), $post->post_title ) . "\r\n";
2051                      $notify_message .= get_permalink( $comment->comment_post_ID ) . "\r\n\r\n";
2052                      /* translators: 1: Trackback/pingback website name, 2: Website IP address, 3: Website hostname. */
2053                      $notify_message .= sprintf( __( 'Website: %1$s (IP address: %2$s, %3$s)' ), $comment->comment_author, $comment->comment_author_IP, $comment_author_domain ) . "\r\n";
2054                      /* translators: %s: Trackback/pingback/comment author URL. */
2055                      $notify_message .= sprintf( __( 'URL: %s' ), $comment->comment_author_url ) . "\r\n";
2056                      $notify_message .= __( 'Trackback excerpt: ' ) . "\r\n" . $comment_content . "\r\n\r\n";
2057                      break;
2058  
2059                  case 'pingback':
2060                      /* translators: %s: Post title. */
2061                      $notify_message  = sprintf( __( 'A new pingback on the post "%s" is waiting for your approval' ), $post->post_title ) . "\r\n";
2062                      $notify_message .= get_permalink( $comment->comment_post_ID ) . "\r\n\r\n";
2063                      /* translators: 1: Trackback/pingback website name, 2: Website IP address, 3: Website hostname. */
2064                      $notify_message .= sprintf( __( 'Website: %1$s (IP address: %2$s, %3$s)' ), $comment->comment_author, $comment->comment_author_IP, $comment_author_domain ) . "\r\n";
2065                      /* translators: %s: Trackback/pingback/comment author URL. */
2066                      $notify_message .= sprintf( __( 'URL: %s' ), $comment->comment_author_url ) . "\r\n";
2067                      $notify_message .= __( 'Pingback excerpt: ' ) . "\r\n" . $comment_content . "\r\n\r\n";
2068                      break;
2069  
2070                  default: // Comments.
2071                      /* translators: %s: Post title. */
2072                      $notify_message  = sprintf( __( 'A new comment on the post "%s" is waiting for your approval' ), $post->post_title ) . "\r\n";
2073                      $notify_message .= get_permalink( $comment->comment_post_ID ) . "\r\n\r\n";
2074                      /* translators: 1: Comment author's name, 2: Comment author's IP address, 3: Comment author's hostname. */
2075                      $notify_message .= sprintf( __( 'Author: %1$s (IP address: %2$s, %3$s)' ), $comment->comment_author, $comment->comment_author_IP, $comment_author_domain ) . "\r\n";
2076                      /* translators: %s: Comment author email. */
2077                      $notify_message .= sprintf( __( 'Email: %s' ), $comment->comment_author_email ) . "\r\n";
2078                      /* translators: %s: Trackback/pingback/comment author URL. */
2079                      $notify_message .= sprintf( __( 'URL: %s' ), $comment->comment_author_url ) . "\r\n";
2080  
2081                      if ( $comment->comment_parent ) {
2082                          /* translators: Comment moderation. %s: Parent comment edit URL. */
2083                          $notify_message .= sprintf( __( 'In reply to: %s' ), admin_url( "comment.php?action=editcomment&c={$comment->comment_parent}#wpbody-content" ) ) . "\r\n";
2084                      }
2085  
2086                      /* translators: %s: Comment text. */
2087                      $notify_message .= sprintf( __( 'Comment: %s' ), "\r\n" . $comment_content ) . "\r\n\r\n";
2088                      break;
2089              }
2090  
2091              /* translators: Comment moderation. %s: Comment action URL. */
2092              $notify_message .= sprintf( __( 'Approve it: %s' ), admin_url( "comment.php?action=approve&c={$comment_id}#wpbody-content" ) ) . "\r\n";
2093  
2094              if ( EMPTY_TRASH_DAYS ) {
2095                  /* translators: Comment moderation. %s: Comment action URL. */
2096                  $notify_message .= sprintf( __( 'Trash it: %s' ), admin_url( "comment.php?action=trash&c={$comment_id}#wpbody-content" ) ) . "\r\n";
2097              } else {
2098                  /* translators: Comment moderation. %s: Comment action URL. */
2099                  $notify_message .= sprintf( __( 'Delete it: %s' ), admin_url( "comment.php?action=delete&c={$comment_id}#wpbody-content" ) ) . "\r\n";
2100              }
2101  
2102              /* translators: Comment moderation. %s: Comment action URL. */
2103              $notify_message .= sprintf( __( 'Spam it: %s' ), admin_url( "comment.php?action=spam&c={$comment_id}#wpbody-content" ) ) . "\r\n";
2104  
2105              $notify_message .= sprintf(
2106                  /* translators: Comment moderation. %s: Number of comments awaiting approval. */
2107                  _n(
2108                      'Currently %s comment is waiting for approval. Please visit the moderation panel:',
2109                      'Currently %s comments are waiting for approval. Please visit the moderation panel:',
2110                      $comments_waiting
2111                  ),
2112                  number_format_i18n( $comments_waiting )
2113              ) . "\r\n";
2114              $notify_message .= admin_url( 'edit-comments.php?comment_status=moderated#wpbody-content' ) . "\r\n";
2115  
2116              /* translators: Comment moderation notification email subject. 1: Site title, 2: Post title. */
2117              $subject = sprintf( __( '[%1$s] Please moderate: "%2$s"' ), $blogname, $post->post_title );
2118  
2119              /**
2120               * Filters the comment moderation email text.
2121               *
2122               * @since 1.5.2
2123               *
2124               * @param string $notify_message Text of the comment moderation email.
2125               * @param int    $comment_id     Comment ID.
2126               */
2127              $notify_message = apply_filters( 'comment_moderation_text', $notify_message, $comment_id );
2128  
2129              /**
2130               * Filters the comment moderation email subject.
2131               *
2132               * @since 1.5.2
2133               *
2134               * @param string $subject    Subject of the comment moderation email.
2135               * @param int    $comment_id Comment ID.
2136               */
2137              $subject = apply_filters( 'comment_moderation_subject', $subject, $comment_id );
2138  
2139              wp_mail( $email, wp_specialchars_decode( $subject ), $notify_message, $message_headers );
2140  
2141              if ( $switched_locale ) {
2142                  restore_previous_locale();
2143              }
2144          }
2145  
2146          return true;
2147      }
2148  endif;
2149  
2150  if ( ! function_exists( 'wp_password_change_notification' ) ) :
2151      /**
2152       * Notifies the blog admin of a user changing password, normally via email.
2153       *
2154       * @since 2.7.0
2155       *
2156       * @param WP_User $user User object.
2157       */
2158  	function wp_password_change_notification( $user ) {
2159          /*
2160           * Send a copy of password change notification to the admin,
2161           * but check to see if it's the admin whose password we're changing, and skip this.
2162           */
2163          if ( 0 !== strcasecmp( $user->user_email, get_option( 'admin_email' ) ) ) {
2164  
2165              $admin_user = get_user_by( 'email', get_option( 'admin_email' ) );
2166  
2167              if ( $admin_user ) {
2168                  $switched_locale = switch_to_user_locale( $admin_user->ID );
2169              } else {
2170                  $switched_locale = switch_to_locale( get_locale() );
2171              }
2172  
2173              /* translators: %s: User name. */
2174              $message = sprintf( __( 'Password changed for user: %s' ), $user->user_login ) . "\r\n";
2175              /*
2176               * The blogname option is escaped with esc_html() on the way into the database in sanitize_option().
2177               * We want to reverse this for the plain text arena of emails.
2178               */
2179              $blogname = wp_specialchars_decode( get_option( 'blogname' ), ENT_QUOTES );
2180  
2181              $wp_password_change_notification_email = array(
2182                  'to'      => get_option( 'admin_email' ),
2183                  /* translators: Password change notification email subject. %s: Site title. */
2184                  'subject' => __( '[%s] Password Changed' ),
2185                  'message' => $message,
2186                  'headers' => '',
2187              );
2188  
2189              /**
2190               * Filters the contents of the password change notification email sent to the site admin.
2191               *
2192               * @since 4.9.0
2193               *
2194               * @param array   $wp_password_change_notification_email {
2195               *     Used to build wp_mail().
2196               *
2197               *     @type string $to      The intended recipient - site admin email address.
2198               *     @type string $subject The subject of the email.
2199               *     @type string $message The body of the email.
2200               *     @type string $headers The headers of the email.
2201               * }
2202               * @param WP_User $user     User object for user whose password was changed.
2203               * @param string  $blogname The site title.
2204               */
2205              $wp_password_change_notification_email = apply_filters( 'wp_password_change_notification_email', $wp_password_change_notification_email, $user, $blogname );
2206  
2207              wp_mail(
2208                  $wp_password_change_notification_email['to'],
2209                  wp_specialchars_decode( sprintf( $wp_password_change_notification_email['subject'], $blogname ) ),
2210                  $wp_password_change_notification_email['message'],
2211                  $wp_password_change_notification_email['headers']
2212              );
2213  
2214              if ( $switched_locale ) {
2215                  restore_previous_locale();
2216              }
2217          }
2218      }
2219  endif;
2220  
2221  if ( ! function_exists( 'wp_new_user_notification' ) ) :
2222      /**
2223       * Emails login credentials to a newly-registered user.
2224       *
2225       * A new user registration notification is also sent to admin email.
2226       *
2227       * @since 2.0.0
2228       * @since 4.3.0 The `$plaintext_pass` parameter was changed to `$notify`.
2229       * @since 4.3.1 The `$plaintext_pass` parameter was deprecated. `$notify` added as a third parameter.
2230       * @since 4.6.0 The `$notify` parameter accepts 'user' for sending notification only to the user created.
2231       *
2232       * @param int    $user_id    User ID.
2233       * @param null   $deprecated Not used (argument deprecated).
2234       * @param string $notify     Optional. Type of notification that should happen. Accepts 'admin' or an empty
2235       *                           string (admin only), 'user', or 'both' (admin and user). Default empty.
2236       */
2237  	function wp_new_user_notification( $user_id, $deprecated = null, $notify = '' ) {
2238          if ( null !== $deprecated ) {
2239              _deprecated_argument( __FUNCTION__, '4.3.1' );
2240          }
2241  
2242          // Accepts only 'user', 'admin' , 'both' or default '' as $notify.
2243          if ( ! in_array( $notify, array( 'user', 'admin', 'both', '' ), true ) ) {
2244              return;
2245          }
2246  
2247          $user = get_userdata( $user_id );
2248  
2249          /*
2250           * The blogname option is escaped with esc_html() on the way into the database in sanitize_option().
2251           * We want to reverse this for the plain text arena of emails.
2252           */
2253          $blogname = wp_specialchars_decode( get_option( 'blogname' ), ENT_QUOTES );
2254  
2255          /**
2256           * Filters whether the admin is notified of a new user registration.
2257           *
2258           * @since 6.1.0
2259           *
2260           * @param bool    $send Whether to send the email. Default true.
2261           * @param WP_User $user User object for new user.
2262           */
2263          $send_notification_to_admin = apply_filters( 'wp_send_new_user_notification_to_admin', true, $user );
2264  
2265          if ( 'user' !== $notify && true === $send_notification_to_admin ) {
2266  
2267              $admin_user = get_user_by( 'email', get_option( 'admin_email' ) );
2268  
2269              if ( $admin_user ) {
2270                  $switched_locale = switch_to_user_locale( $admin_user->ID );
2271              } else {
2272                  $switched_locale = switch_to_locale( get_locale() );
2273              }
2274  
2275              /* translators: %s: Site title. */
2276              $message = sprintf( __( 'New user registration on your site %s:' ), $blogname ) . "\r\n\r\n";
2277              /* translators: %s: User login. */
2278              $message .= sprintf( __( 'Username: %s' ), $user->user_login ) . "\r\n\r\n";
2279              /* translators: %s: User email address. */
2280              $message .= sprintf( __( 'Email: %s' ), $user->user_email ) . "\r\n";
2281  
2282              $wp_new_user_notification_email_admin = array(
2283                  'to'      => get_option( 'admin_email' ),
2284                  /* translators: New user registration notification email subject. %s: Site title. */
2285                  'subject' => __( '[%s] New User Registration' ),
2286                  'message' => $message,
2287                  'headers' => '',
2288              );
2289  
2290              /**
2291               * Filters the contents of the new user notification email sent to the site admin.
2292               *
2293               * @since 4.9.0
2294               *
2295               * @param array   $wp_new_user_notification_email_admin {
2296               *     Used to build wp_mail().
2297               *
2298               *     @type string $to      The intended recipient - site admin email address.
2299               *     @type string $subject The subject of the email.
2300               *     @type string $message The body of the email.
2301               *     @type string $headers The headers of the email.
2302               * }
2303               * @param WP_User $user     User object for new user.
2304               * @param string  $blogname The site title.
2305               */
2306              $wp_new_user_notification_email_admin = apply_filters( 'wp_new_user_notification_email_admin', $wp_new_user_notification_email_admin, $user, $blogname );
2307  
2308              wp_mail(
2309                  $wp_new_user_notification_email_admin['to'],
2310                  wp_specialchars_decode( sprintf( $wp_new_user_notification_email_admin['subject'], $blogname ) ),
2311                  $wp_new_user_notification_email_admin['message'],
2312                  $wp_new_user_notification_email_admin['headers']
2313              );
2314  
2315              if ( $switched_locale ) {
2316                  restore_previous_locale();
2317              }
2318          }
2319  
2320          /**
2321           * Filters whether the user is notified of their new user registration.
2322           *
2323           * @since 6.1.0
2324           *
2325           * @param bool    $send Whether to send the email. Default true.
2326           * @param WP_User $user User object for new user.
2327           */
2328          $send_notification_to_user = apply_filters( 'wp_send_new_user_notification_to_user', true, $user );
2329  
2330          // `$deprecated` was pre-4.3 `$plaintext_pass`. An empty `$plaintext_pass` didn't sent a user notification.
2331          if ( 'admin' === $notify || true !== $send_notification_to_user || ( empty( $deprecated ) && empty( $notify ) ) ) {
2332              return;
2333          }
2334  
2335          $key = get_password_reset_key( $user );
2336          if ( is_wp_error( $key ) ) {
2337              return;
2338          }
2339  
2340          $switched_locale = switch_to_user_locale( $user_id );
2341  
2342          /* translators: %s: User login. */
2343          $message  = sprintf( __( 'Username: %s' ), $user->user_login ) . "\r\n\r\n";
2344          $message .= __( 'To set your password, visit the following address:' ) . "\r\n\r\n";
2345  
2346          /*
2347           * Since some user login names end in a period, this could produce ambiguous URLs that
2348           * end in a period. To avoid the ambiguity, ensure that the login is not the last query
2349           * arg in the URL. If moving it to the end, a trailing period will need to be escaped.
2350           *
2351           * @see https://core.trac.wordpress.org/tickets/42957
2352           */
2353          $message .= network_site_url( 'wp-login.php?login=' . rawurlencode( $user->user_login ) . "&key=$key&action=rp", 'login' ) . "\r\n\r\n";
2354  
2355          $message .= wp_login_url() . "\r\n";
2356  
2357          $wp_new_user_notification_email = array(
2358              'to'      => $user->user_email,
2359              /* translators: Login details notification email subject. %s: Site title. */
2360              'subject' => __( '[%s] Login Details' ),
2361              'message' => $message,
2362              'headers' => '',
2363          );
2364  
2365          /**
2366           * Filters the contents of the new user notification email sent to the new user.
2367           *
2368           * @since 4.9.0
2369           *
2370           * @param array   $wp_new_user_notification_email {
2371           *     Used to build wp_mail().
2372           *
2373           *     @type string $to      The intended recipient - New user email address.
2374           *     @type string $subject The subject of the email.
2375           *     @type string $message The body of the email.
2376           *     @type string $headers The headers of the email.
2377           * }
2378           * @param WP_User $user     User object for new user.
2379           * @param string  $blogname The site title.
2380           */
2381          $wp_new_user_notification_email = apply_filters( 'wp_new_user_notification_email', $wp_new_user_notification_email, $user, $blogname );
2382  
2383          wp_mail(
2384              $wp_new_user_notification_email['to'],
2385              wp_specialchars_decode( sprintf( $wp_new_user_notification_email['subject'], $blogname ) ),
2386              $wp_new_user_notification_email['message'],
2387              $wp_new_user_notification_email['headers']
2388          );
2389  
2390          if ( $switched_locale ) {
2391              restore_previous_locale();
2392          }
2393      }
2394  endif;
2395  
2396  if ( ! function_exists( 'wp_nonce_tick' ) ) :
2397      /**
2398       * Returns the time-dependent variable for nonce creation.
2399       *
2400       * A nonce has a lifespan of two ticks. Nonces in their second tick may be
2401       * updated, e.g. by autosave.
2402       *
2403       * @since 2.5.0
2404       * @since 6.1.0 Added `$action` argument.
2405       *
2406       * @param string|int $action Optional. The nonce action. Default -1.
2407       * @return float Float value rounded up to the next highest integer.
2408       */
2409  	function wp_nonce_tick( $action = -1 ) {
2410          /**
2411           * Filters the lifespan of nonces in seconds.
2412           *
2413           * @since 2.5.0
2414           * @since 6.1.0 Added `$action` argument to allow for more targeted filters.
2415           *
2416           * @param int        $lifespan Lifespan of nonces in seconds. Default 86,400 seconds, or one day.
2417           * @param string|int $action   The nonce action, or -1 if none was provided.
2418           */
2419          $nonce_life = apply_filters( 'nonce_life', DAY_IN_SECONDS, $action );
2420  
2421          return ceil( time() / ( $nonce_life / 2 ) );
2422      }
2423  endif;
2424  
2425  if ( ! function_exists( 'wp_verify_nonce' ) ) :
2426      /**
2427       * Verifies that a correct security nonce was used with time limit.
2428       *
2429       * A nonce is valid for between 12 and 24 hours (by default).
2430       *
2431       * @since 2.0.3
2432       *
2433       * @param string     $nonce  Nonce value that was used for verification, usually via a form field.
2434       * @param string|int $action Should give context to what is taking place and be the same when nonce was created.
2435       * @return int|false 1 if the nonce is valid and generated between 0-12 hours ago,
2436       *                   2 if the nonce is valid and generated between 12-24 hours ago.
2437       *                   False if the nonce is invalid.
2438       */
2439  	function wp_verify_nonce( $nonce, $action = -1 ) {
2440          $nonce = (string) $nonce;
2441          $user  = wp_get_current_user();
2442          $uid   = (int) $user->ID;
2443          if ( ! $uid ) {
2444              /**
2445               * Filters whether the user who generated the nonce is logged out.
2446               *
2447               * @since 3.5.0
2448               *
2449               * @param int        $uid    ID of the nonce-owning user.
2450               * @param string|int $action The nonce action, or -1 if none was provided.
2451               */
2452              $uid = apply_filters( 'nonce_user_logged_out', $uid, $action );
2453          }
2454  
2455          if ( empty( $nonce ) ) {
2456              return false;
2457          }
2458  
2459          $token = wp_get_session_token();
2460          $i     = wp_nonce_tick( $action );
2461  
2462          // Nonce generated 0-12 hours ago.
2463          $expected = substr( wp_hash( $i . '|' . $action . '|' . $uid . '|' . $token, 'nonce' ), -12, 10 );
2464          if ( hash_equals( $expected, $nonce ) ) {
2465              return 1;
2466          }
2467  
2468          // Nonce generated 12-24 hours ago.
2469          $expected = substr( wp_hash( ( $i - 1 ) . '|' . $action . '|' . $uid . '|' . $token, 'nonce' ), -12, 10 );
2470          if ( hash_equals( $expected, $nonce ) ) {
2471              return 2;
2472          }
2473  
2474          /**
2475           * Fires when nonce verification fails.
2476           *
2477           * @since 4.4.0
2478           *
2479           * @param string     $nonce  The invalid nonce.
2480           * @param string|int $action The nonce action.
2481           * @param WP_User    $user   The current user object.
2482           * @param string     $token  The user's session token.
2483           */
2484          do_action( 'wp_verify_nonce_failed', $nonce, $action, $user, $token );
2485  
2486          // Invalid nonce.
2487          return false;
2488      }
2489  endif;
2490  
2491  if ( ! function_exists( 'wp_create_nonce' ) ) :
2492      /**
2493       * Creates a cryptographic token tied to a specific action, user, user session,
2494       * and window of time.
2495       *
2496       * @since 2.0.3
2497       * @since 4.0.0 Session tokens were integrated with nonce creation.
2498       *
2499       * @param string|int $action Scalar value to add context to the nonce.
2500       * @return string The token.
2501       */
2502  	function wp_create_nonce( $action = -1 ) {
2503          $user = wp_get_current_user();
2504          $uid  = (int) $user->ID;
2505          if ( ! $uid ) {
2506              /** This filter is documented in wp-includes/pluggable.php */
2507              $uid = apply_filters( 'nonce_user_logged_out', $uid, $action );
2508          }
2509  
2510          $token = wp_get_session_token();
2511          $i     = wp_nonce_tick( $action );
2512  
2513          return substr( wp_hash( $i . '|' . $action . '|' . $uid . '|' . $token, 'nonce' ), -12, 10 );
2514      }
2515  endif;
2516  
2517  if ( ! function_exists( 'wp_salt' ) ) :
2518      /**
2519       * Returns a salt to add to hashes.
2520       *
2521       * Salts are created using secret keys. Secret keys are located in two places:
2522       * in the database and in the wp-config.php file. The secret key in the database
2523       * is randomly generated and will be appended to the secret keys in wp-config.php.
2524       *
2525       * The secret keys in wp-config.php should be updated to strong, random keys to maximize
2526       * security. Below is an example of how the secret key constants are defined.
2527       * Do not paste this example directly into wp-config.php. Instead, have a
2528       * {@link https://api.wordpress.org/secret-key/1.1/salt/ secret key created} just
2529       * for you.
2530       *
2531       *     define('AUTH_KEY',         ' Xakm<o xQy rw4EMsLKM-?!T+,PFF})H4lzcW57AF0U@N@< >M%G4Yt>f`z]MON');
2532       *     define('SECURE_AUTH_KEY',  'LzJ}op]mr|6+![P}Ak:uNdJCJZd>(Hx.-Mh#Tz)pCIU#uGEnfFz|f ;;eU%/U^O~');
2533       *     define('LOGGED_IN_KEY',    '|i|Ux`9<p-h$aFf(qnT:sDO:D1P^wZ$$/Ra@miTJi9G;ddp_<q}6H1)o|a +&JCM');
2534       *     define('NONCE_KEY',        '%:R{[P|,s.KuMltH5}cI;/k<Gx~j!f0I)m_sIyu+&NJZ)-iO>z7X>QYR0Z_XnZ@|');
2535       *     define('AUTH_SALT',        'eZyT)-Naw]F8CwA*VaW#q*|.)g@o}||wf~@C-YSt}(dh_r6EbI#A,y|nU2{B#JBW');
2536       *     define('SECURE_AUTH_SALT', '!=oLUTXh,QW=H `}`L|9/^4-3 STz},T(w}W<I`.JjPi)<Bmf1v,HpGe}T1:Xt7n');
2537       *     define('LOGGED_IN_SALT',   '+XSqHc;@Q*K_b|Z?NC[3H!!EONbh.n<+=uKR:>*c(u`g~EJBf#8u#R{mUEZrozmm');
2538       *     define('NONCE_SALT',       'h`GXHhD>SLWVfg1(1(N{;.V!MoE(SfbA_ksP@&`+AycHcAV$+?@3q+rxV{%^VyKT');
2539       *
2540       * Salting passwords helps against tools which has stored hashed values of
2541       * common dictionary strings. The added values makes it harder to crack.
2542       *
2543       * @since 2.5.0
2544       *
2545       * @link https://api.wordpress.org/secret-key/1.1/salt/ Create secrets for wp-config.php
2546       *
2547       * @param string $scheme Authentication scheme (auth, secure_auth, logged_in, nonce).
2548       * @return string Salt value
2549       */
2550  	function wp_salt( $scheme = 'auth' ) {
2551          static $cached_salts = array();
2552          if ( isset( $cached_salts[ $scheme ] ) ) {
2553              /**
2554               * Filters the WordPress salt.
2555               *
2556               * @since 2.5.0
2557               *
2558               * @param string $cached_salt Cached salt for the given scheme.
2559               * @param string $scheme      Authentication scheme. Values include 'auth',
2560               *                            'secure_auth', 'logged_in', and 'nonce'.
2561               */
2562              return apply_filters( 'salt', $cached_salts[ $scheme ], $scheme );
2563          }
2564  
2565          static $duplicated_keys;
2566          if ( null === $duplicated_keys ) {
2567              $duplicated_keys = array();
2568  
2569              foreach ( array( 'AUTH', 'SECURE_AUTH', 'LOGGED_IN', 'NONCE', 'SECRET' ) as $first ) {
2570                  foreach ( array( 'KEY', 'SALT' ) as $second ) {
2571                      if ( ! defined( "{$first}_{$second}" ) ) {
2572                          continue;
2573                      }
2574                      $value                     = constant( "{$first}_{$second}" );
2575                      $duplicated_keys[ $value ] = isset( $duplicated_keys[ $value ] );
2576                  }
2577              }
2578  
2579              $duplicated_keys['put your unique phrase here'] = true;
2580  
2581              /*
2582               * translators: This string should only be translated if wp-config-sample.php is localized.
2583               * You can check the localized release package or
2584               * https://i18n.svn.wordpress.org/<locale code>/branches/<wp version>/dist/wp-config-sample.php
2585               */
2586              $duplicated_keys[ __( 'put your unique phrase here' ) ] = true;
2587          }
2588  
2589          /*
2590           * Determine which options to prime.
2591           *
2592           * If the salt keys are undefined, use a duplicate value or the
2593           * default `put your unique phrase here` value the salt will be
2594           * generated via `wp_generate_password()` and stored as a site
2595           * option. These options will be primed to avoid repeated
2596           * database requests for undefined salts.
2597           */
2598          $options_to_prime = array();
2599          foreach ( array( 'auth', 'secure_auth', 'logged_in', 'nonce' ) as $key ) {
2600              foreach ( array( 'key', 'salt' ) as $second ) {
2601                  $const = strtoupper( "{$key}_{$second}" );
2602                  if ( ! defined( $const ) || true === $duplicated_keys[ constant( $const ) ] ) {
2603                      $options_to_prime[] = "{$key}_{$second}";
2604                  }
2605              }
2606          }
2607  
2608          if ( ! empty( $options_to_prime ) ) {
2609              /*
2610               * Also prime `secret_key` used for undefined salting schemes.
2611               *
2612               * If the scheme is unknown, the default value for `secret_key` will be
2613               * used too for the salt. This should rarely happen, so the option is only
2614               * primed if other salts are undefined.
2615               *
2616               * At this point of execution it is known that a database call will be made
2617               * to prime salts, so the `secret_key` option can be primed regardless of the
2618               * constants status.
2619               */
2620              $options_to_prime[] = 'secret_key';
2621              wp_prime_site_option_caches( $options_to_prime );
2622          }
2623  
2624          $values = array(
2625              'key'  => '',
2626              'salt' => '',
2627          );
2628          if ( defined( 'SECRET_KEY' ) && SECRET_KEY && empty( $duplicated_keys[ SECRET_KEY ] ) ) {
2629              $values['key'] = SECRET_KEY;
2630          }
2631          if ( 'auth' === $scheme && defined( 'SECRET_SALT' ) && SECRET_SALT && empty( $duplicated_keys[ SECRET_SALT ] ) ) {
2632              $values['salt'] = SECRET_SALT;
2633          }
2634  
2635          if ( in_array( $scheme, array( 'auth', 'secure_auth', 'logged_in', 'nonce' ), true ) ) {
2636              foreach ( array( 'key', 'salt' ) as $type ) {
2637                  $const = strtoupper( "{$scheme}_{$type}" );
2638                  if ( defined( $const ) && constant( $const ) && empty( $duplicated_keys[ constant( $const ) ] ) ) {
2639                      $values[ $type ] = constant( $const );
2640                  } elseif ( ! $values[ $type ] ) {
2641                      $values[ $type ] = get_site_option( "{$scheme}_{$type}" );
2642                      if ( ! $values[ $type ] ) {
2643                          $values[ $type ] = wp_generate_password( 64, true, true );
2644                          update_site_option( "{$scheme}_{$type}", $values[ $type ] );
2645                      }
2646                  }
2647              }
2648          } else {
2649              if ( ! $values['key'] ) {
2650                  $values['key'] = get_site_option( 'secret_key' );
2651                  if ( ! $values['key'] ) {
2652                      $values['key'] = wp_generate_password( 64, true, true );
2653                      update_site_option( 'secret_key', $values['key'] );
2654                  }
2655              }
2656              $values['salt'] = hash_hmac( 'md5', $scheme, $values['key'] );
2657          }
2658  
2659          $cached_salts[ $scheme ] = $values['key'] . $values['salt'];
2660  
2661          /** This filter is documented in wp-includes/pluggable.php */
2662          return apply_filters( 'salt', $cached_salts[ $scheme ], $scheme );
2663      }
2664  endif;
2665  
2666  if ( ! function_exists( 'wp_hash' ) ) :
2667      /**
2668       * Gets the hash of the given string.
2669       *
2670       * The default algorithm is md5 but can be changed to any algorithm supported by
2671       * `hash_hmac()`. Use the `hash_hmac_algos()` function to check the supported
2672       * algorithms.
2673       *
2674       * @since 2.0.3
2675       * @since 6.8.0 The `$algo` parameter was added.
2676       *
2677       * @throws InvalidArgumentException if the hashing algorithm is not supported.
2678       *
2679       * @param string $data   Plain text to hash.
2680       * @param string $scheme Authentication scheme (auth, secure_auth, logged_in, nonce).
2681       * @param string $algo   Hashing algorithm to use. Default: 'md5'.
2682       * @return string Hash of $data.
2683       */
2684  	function wp_hash( $data, $scheme = 'auth', $algo = 'md5' ) {
2685          $salt = wp_salt( $scheme );
2686  
2687          // Ensure the algorithm is supported by the hash_hmac function.
2688          if ( ! in_array( $algo, hash_hmac_algos(), true ) ) {
2689              throw new InvalidArgumentException(
2690                  sprintf(
2691                      /* translators: 1: Name of a cryptographic hash algorithm. 2: List of supported algorithms. */
2692                      __( 'Unsupported hashing algorithm: %1$s. Supported algorithms are: %2$s' ),
2693                      $algo,
2694                      implode( ', ', hash_hmac_algos() )
2695                  )
2696              );
2697          }
2698  
2699          return hash_hmac( $algo, $data, $salt );
2700      }
2701  endif;
2702  
2703  if ( ! function_exists( 'wp_hash_password' ) ) :
2704      /**
2705       * Creates a hash of a plain text password.
2706       *
2707       * For integration with other applications, this function can be overwritten to
2708       * instead use the other package password hashing algorithm.
2709       *
2710       * @since 2.5.0
2711       * @since 6.8.0 The password is now hashed using bcrypt by default instead of phpass.
2712       *
2713       * @global PasswordHash $wp_hasher phpass object.
2714       *
2715       * @param string $password Plain text user password to hash.
2716       * @return string The hash string of the password.
2717       */
2718  	function wp_hash_password(
2719          #[\SensitiveParameter]
2720          $password
2721      ) {
2722          global $wp_hasher;
2723  
2724          if ( ! empty( $wp_hasher ) ) {
2725              return $wp_hasher->HashPassword( trim( $password ) );
2726          }
2727  
2728          if ( strlen( $password ) > 4096 ) {
2729              return '*';
2730          }
2731  
2732          /**
2733           * Filters the hashing algorithm to use in the password_hash() and password_needs_rehash() functions.
2734           *
2735           * The default is the value of the `PASSWORD_BCRYPT` constant which means bcrypt is used.
2736           *
2737           * **Important:** The only password hashing algorithm that is guaranteed to be available across PHP
2738           * installations is bcrypt. If you use any other algorithm you must make sure that it is available on
2739           * the server. The `password_algos()` function can be used to check which hashing algorithms are available.
2740           *
2741           * The hashing options can be controlled via the {@see 'wp_hash_password_options'} filter.
2742           *
2743           * Other available constants include:
2744           *
2745           * - `PASSWORD_ARGON2I`
2746           * - `PASSWORD_ARGON2ID`
2747           * - `PASSWORD_DEFAULT`
2748           *
2749           * The values of the algorithm constants are strings in PHP 7.4+ and integers in PHP 7.3 and earlier.
2750           *
2751           * @since 6.8.0
2752           *
2753           * @param string|int $algorithm The hashing algorithm. Default is the value of the `PASSWORD_BCRYPT` constant.
2754           */
2755          $algorithm = apply_filters( 'wp_hash_password_algorithm', PASSWORD_BCRYPT );
2756  
2757          /**
2758           * Filters the options passed to the password_hash() and password_needs_rehash() functions.
2759           *
2760           * The default hashing algorithm is bcrypt, but this can be changed via the {@see 'wp_hash_password_algorithm'}
2761           * filter. You must ensure that the options are appropriate for the algorithm in use.
2762           *
2763           * The values of the algorithm constants are strings in PHP 7.4+ and integers in PHP 7.3 and earlier.
2764           *
2765           * @since 6.8.0
2766           *
2767           * @param array      $options   Array of options to pass to the password hashing functions.
2768           *                              By default this is an empty array which means the default
2769           *                              options will be used.
2770           * @param string|int $algorithm The hashing algorithm in use.
2771           */
2772          $options = apply_filters( 'wp_hash_password_options', array(), $algorithm );
2773  
2774          // Algorithms other than bcrypt don't need to use pre-hashing.
2775          if ( PASSWORD_BCRYPT !== $algorithm ) {
2776              return password_hash( $password, $algorithm, $options );
2777          }
2778  
2779          // Use SHA-384 to retain entropy from a password that's longer than 72 bytes, and a `wp-sha384` key for domain separation.
2780          $password_to_hash = base64_encode( hash_hmac( 'sha384', trim( $password ), 'wp-sha384', true ) );
2781  
2782          // Add a prefix to facilitate distinguishing vanilla bcrypt hashes.
2783          return '$wp' . password_hash( $password_to_hash, $algorithm, $options );
2784      }
2785  endif;
2786  
2787  if ( ! function_exists( 'wp_check_password' ) ) :
2788      /**
2789       * Checks a plaintext password against a hashed password.
2790       *
2791       * Note that this function may be used to check a value that is not a user password.
2792       * A plugin may use this function to check a password of a different type, and there
2793       * may not always be a user ID associated with the password.
2794       *
2795       * For integration with other applications, this function can be overwritten to
2796       * instead use the other package password hashing algorithm.
2797       *
2798       * @since 2.5.0
2799       * @since 6.8.0 Passwords in WordPress are now hashed with bcrypt by default. A
2800       *              password that wasn't hashed with bcrypt will be checked with phpass.
2801       *
2802       * @global PasswordHash $wp_hasher phpass object. Used as a fallback for verifying
2803       *                                 passwords that were hashed with phpass.
2804       *
2805       * @param string     $password Plaintext password.
2806       * @param string     $hash     Hash of the password to check against.
2807       * @param string|int $user_id  Optional. ID of a user associated with the password.
2808       * @return bool False, if the $password does not match the hashed password.
2809       */
2810  	function wp_check_password(
2811          #[\SensitiveParameter]
2812          $password,
2813          $hash,
2814          $user_id = ''
2815      ) {
2816          global $wp_hasher;
2817  
2818          if ( strlen( $hash ) <= 32 ) {
2819              // Check the hash using md5 regardless of the current hashing mechanism.
2820              $check = hash_equals( $hash, md5( $password ) );
2821          } elseif ( ! empty( $wp_hasher ) ) {
2822              // Check the password using the overridden hasher.
2823              $check = $wp_hasher->CheckPassword( $password, $hash );
2824          } elseif ( strlen( $password ) > 4096 ) {
2825              // Passwords longer than 4096 characters are not supported.
2826              $check = false;
2827          } elseif ( str_starts_with( $hash, '$wp' ) ) {
2828              // Check the password using the current prefixed hash.
2829              $password_to_verify = base64_encode( hash_hmac( 'sha384', $password, 'wp-sha384', true ) );
2830              $check              = password_verify( $password_to_verify, substr( $hash, 3 ) );
2831          } elseif ( str_starts_with( $hash, '$P$' ) ) {
2832              // Check the password using phpass.
2833              require_once  ABSPATH . WPINC . '/class-phpass.php';
2834              $check = ( new PasswordHash( 8, true ) )->CheckPassword( $password, $hash );
2835          } else {
2836              // Check the password using compat support for any non-prefixed hash.
2837              $check = password_verify( $password, $hash );
2838          }
2839  
2840          /**
2841           * Filters whether the plaintext password matches the hashed password.
2842           *
2843           * @since 2.5.0
2844           * @since 6.8.0 Passwords are now hashed with bcrypt by default.
2845           *              Old passwords may still be hashed with phpass or md5.
2846           *
2847           * @param bool       $check    Whether the passwords match.
2848           * @param string     $password The plaintext password.
2849           * @param string     $hash     The hashed password.
2850           * @param string|int $user_id  Optional ID of a user associated with the password.
2851           *                             Can be empty.
2852           */
2853          return apply_filters( 'check_password', $check, $password, $hash, $user_id );
2854      }
2855  endif;
2856  
2857  if ( ! function_exists( 'wp_password_needs_rehash' ) ) :
2858      /**
2859       * Checks whether a password hash needs to be rehashed.
2860       *
2861       * Passwords are hashed with bcrypt using the default cost. A password hashed in a prior version
2862       * of WordPress may still be hashed with phpass and will need to be rehashed. If the default cost
2863       * or algorithm is changed in PHP or WordPress then a password hashed in a previous version will
2864       * need to be rehashed.
2865       *
2866       * Note that, just like wp_check_password(), this function may be used to check a value that is
2867       * not a user password. A plugin may use this function to check a password of a different type,
2868       * and there may not always be a user ID associated with the password.
2869       *
2870       * @since 6.8.0
2871       *
2872       * @global PasswordHash $wp_hasher phpass object.
2873       *
2874       * @param string     $hash    Hash of a password to check.
2875       * @param string|int $user_id Optional. ID of a user associated with the password.
2876       * @return bool Whether the hash needs to be rehashed.
2877       */
2878  	function wp_password_needs_rehash( $hash, $user_id = '' ) {
2879          global $wp_hasher;
2880  
2881          if ( ! empty( $wp_hasher ) ) {
2882              return false;
2883          }
2884  
2885          /** This filter is documented in wp-includes/pluggable.php */
2886          $algorithm = apply_filters( 'wp_hash_password_algorithm', PASSWORD_BCRYPT );
2887  
2888          /** This filter is documented in wp-includes/pluggable.php */
2889          $options = apply_filters( 'wp_hash_password_options', array(), $algorithm );
2890  
2891          $prefixed = str_starts_with( $hash, '$wp' );
2892  
2893          if ( ( PASSWORD_BCRYPT === $algorithm ) && ! $prefixed ) {
2894              // If bcrypt is in use and the hash is not prefixed then it needs to be rehashed.
2895              $needs_rehash = true;
2896          } else {
2897              // Otherwise check the hash minus its prefix if necessary.
2898              $hash_to_check = $prefixed ? substr( $hash, 3 ) : $hash;
2899              $needs_rehash  = password_needs_rehash( $hash_to_check, $algorithm, $options );
2900          }
2901  
2902          /**
2903           * Filters whether the password hash needs to be rehashed.
2904           *
2905           * @since 6.8.0
2906           *
2907           * @param bool       $needs_rehash Whether the password hash needs to be rehashed.
2908           * @param string     $hash         The password hash.
2909           * @param string|int $user_id      Optional. ID of a user associated with the password.
2910           */
2911          return apply_filters( 'password_needs_rehash', $needs_rehash, $hash, $user_id );
2912      }
2913  endif;
2914  
2915  if ( ! function_exists( 'wp_generate_password' ) ) :
2916      /**
2917       * Generates a random password drawn from the defined set of characters.
2918       *
2919       * Uses wp_rand() to create passwords with far less predictability
2920       * than similar native PHP functions like `rand()` or `mt_rand()`.
2921       *
2922       * @since 2.5.0
2923       *
2924       * @param int  $length              Optional. The length of password to generate. Default 12.
2925       * @param bool $special_chars       Optional. Whether to include standard special characters.
2926       *                                  Default true.
2927       * @param bool $extra_special_chars Optional. Whether to include other special characters.
2928       *                                  Used when generating secret keys and salts. Default false.
2929       * @return string The random password.
2930       */
2931  	function wp_generate_password( $length = 12, $special_chars = true, $extra_special_chars = false ) {
2932          $chars = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789';
2933          if ( $special_chars ) {
2934              $chars .= '!@#$%^&*()';
2935          }
2936          if ( $extra_special_chars ) {
2937              $chars .= '-_ []{}<>~`+=,.;:/?|';
2938          }
2939  
2940          $password = '';
2941          for ( $i = 0; $i < $length; $i++ ) {
2942              $password .= substr( $chars, wp_rand( 0, strlen( $chars ) - 1 ), 1 );
2943          }
2944  
2945          /**
2946           * Filters the randomly-generated password.
2947           *
2948           * @since 3.0.0
2949           * @since 5.3.0 Added the `$length`, `$special_chars`, and `$extra_special_chars` parameters.
2950           *
2951           * @param string $password            The generated password.
2952           * @param int    $length              The length of password to generate.
2953           * @param bool   $special_chars       Whether to include standard special characters.
2954           * @param bool   $extra_special_chars Whether to include other special characters.
2955           */
2956          return apply_filters( 'random_password', $password, $length, $special_chars, $extra_special_chars );
2957      }
2958  endif;
2959  
2960  if ( ! function_exists( 'wp_rand' ) ) :
2961      /**
2962       * Generates a random non-negative number.
2963       *
2964       * @since 2.6.2
2965       * @since 4.4.0 Uses PHP7 random_int() or the random_compat library if available.
2966       * @since 6.1.0 Returns zero instead of a random number if both `$min` and `$max` are zero.
2967       *
2968       * @global string $rnd_value
2969       *
2970       * @param int $min Optional. Lower limit for the generated number.
2971       *                 Accepts positive integers or zero. Defaults to 0.
2972       * @param int $max Optional. Upper limit for the generated number.
2973       *                 Accepts positive integers. Defaults to 4294967295.
2974       * @return int A random non-negative number between min and max.
2975       */
2976  	function wp_rand( $min = null, $max = null ) {
2977          global $rnd_value;
2978  
2979          /*
2980           * Some misconfigured 32-bit environments (Entropy PHP, for example)
2981           * truncate integers larger than PHP_INT_MAX to PHP_INT_MAX rather than overflowing them to floats.
2982           */
2983          $max_random_number = 3000000000 === 2147483647 ? (float) '4294967295' : 4294967295; // 4294967295 = 0xffffffff
2984  
2985          if ( null === $min ) {
2986              $min = 0;
2987          }
2988  
2989          if ( null === $max ) {
2990              $max = $max_random_number;
2991          }
2992  
2993          // We only handle ints, floats are truncated to their integer value.
2994          $min = (int) $min;
2995          $max = (int) $max;
2996  
2997          // Use PHP's CSPRNG, or a compatible method.
2998          static $use_random_int_functionality = true;
2999          if ( $use_random_int_functionality ) {
3000              try {
3001                  // wp_rand() can accept arguments in either order, PHP cannot.
3002                  $_max = max( $min, $max );
3003                  $_min = min( $min, $max );
3004                  $val  = random_int( $_min, $_max );
3005                  if ( false !== $val ) {
3006                      return absint( $val );
3007                  } else {
3008                      $use_random_int_functionality = false;
3009                  }
3010              } catch ( Error $e ) {
3011                  $use_random_int_functionality = false;
3012              } catch ( Exception $e ) {
3013                  $use_random_int_functionality = false;
3014              }
3015          }
3016  
3017          /*
3018           * Reset $rnd_value after 14 uses.
3019           * 32 (md5) + 40 (sha1) + 40 (sha1) / 8 = 14 random numbers from $rnd_value.
3020           */
3021          if ( strlen( $rnd_value ) < 8 ) {
3022              if ( defined( 'WP_SETUP_CONFIG' ) ) {
3023                  static $seed = '';
3024              } else {
3025                  $seed = get_transient( 'random_seed' );
3026              }
3027              $rnd_value  = md5( uniqid( microtime() . mt_rand(), true ) . $seed );
3028              $rnd_value .= sha1( $rnd_value );
3029              $rnd_value .= sha1( $rnd_value . $seed );
3030              $seed       = md5( $seed . $rnd_value );
3031              if ( ! defined( 'WP_SETUP_CONFIG' ) && ! defined( 'WP_INSTALLING' ) ) {
3032                  set_transient( 'random_seed', $seed );
3033              }
3034          }
3035  
3036          // Take the first 8 digits for our value.
3037          $value = substr( $rnd_value, 0, 8 );
3038  
3039          // Strip the first eight, leaving the remainder for the next call to wp_rand().
3040          $rnd_value = substr( $rnd_value, 8 );
3041  
3042          $value = abs( hexdec( $value ) );
3043  
3044          // Reduce the value to be within the min - max range.
3045          $value = $min + ( $max - $min + 1 ) * $value / ( $max_random_number + 1 );
3046  
3047          return abs( (int) $value );
3048      }
3049  endif;
3050  
3051  if ( ! function_exists( 'wp_set_password' ) ) :
3052      /**
3053       * Updates the user's password with a new hashed one.
3054       *
3055       * For integration with other applications, this function can be overwritten to
3056       * instead use the other package password checking algorithm.
3057       *
3058       * Please note: This function should be used sparingly and is really only meant for single-time
3059       * application. Leveraging this improperly in a plugin or theme could result in an endless loop
3060       * of password resets if precautions are not taken to ensure it does not execute on every page load.
3061       *
3062       * @since 2.5.0
3063       * @since 6.8.0 The password is now hashed using bcrypt by default instead of phpass.
3064       *
3065       * @global wpdb $wpdb WordPress database abstraction object.
3066       *
3067       * @param string $password The plaintext new user password.
3068       * @param int    $user_id  User ID.
3069       */
3070  	function wp_set_password(
3071          #[\SensitiveParameter]
3072          $password,
3073          $user_id
3074      ) {
3075          global $wpdb;
3076  
3077          $old_user_data = get_userdata( $user_id );
3078  
3079          $hash = wp_hash_password( $password );
3080          $wpdb->update(
3081              $wpdb->users,
3082              array(
3083                  'user_pass'           => $hash,
3084                  'user_activation_key' => '',
3085              ),
3086              array( 'ID' => $user_id )
3087          );
3088  
3089          clean_user_cache( $user_id );
3090  
3091          /**
3092           * Fires after the user password is set.
3093           *
3094           * @since 6.2.0
3095           * @since 6.7.0 The `$old_user_data` parameter was added.
3096           *
3097           * @param string  $password      The plaintext password just set.
3098           * @param int     $user_id       The ID of the user whose password was just set.
3099           * @param WP_User $old_user_data Object containing user's data prior to update.
3100           */
3101          do_action( 'wp_set_password', $password, $user_id, $old_user_data );
3102      }
3103  endif;
3104  
3105  if ( ! function_exists( 'get_avatar' ) ) :
3106      /**
3107       * Retrieves the avatar `<img>` tag for a user, email address, MD5 hash, comment, or post.
3108       *
3109       * @since 2.5.0
3110       * @since 4.2.0 Added the optional `$args` parameter.
3111       * @since 5.5.0 Added the `loading` argument.
3112       * @since 6.1.0 Added the `decoding` argument.
3113       * @since 6.3.0 Added the `fetchpriority` argument.
3114       *
3115       * @param mixed  $id_or_email   The avatar to retrieve. Accepts a user ID, Gravatar MD5 hash,
3116       *                              user email, WP_User object, WP_Post object, or WP_Comment object.
3117       * @param int    $size          Optional. Height and width of the avatar in pixels. Default 96.
3118       * @param string $default_value URL for the default image or a default type. Accepts:
3119       *                              - '404' (return a 404 instead of a default image)
3120       *                              - 'retro' (a 8-bit arcade-style pixelated face)
3121       *                              - 'robohash' (a robot)
3122       *                              - 'monsterid' (a monster)
3123       *                              - 'wavatar' (a cartoon face)
3124       *                              - 'identicon' (the "quilt", a geometric pattern)
3125       *                              - 'initials' (initials based avatar with background color)
3126       *                              - 'color' (generated background color)
3127       *                              - 'mystery', 'mm', or 'mysteryman' (The Oyster Man)
3128       *                              - 'blank' (transparent GIF)
3129       *                              - 'gravatar_default' (the Gravatar logo)
3130       *                              Default is the value of the 'avatar_default' option,
3131       *                              with a fallback of 'mystery'.
3132       * @param string $alt           Optional. Alternative text to use in the avatar image tag.
3133       *                              Default empty.
3134       * @param array  $args {
3135       *     Optional. Extra arguments to retrieve the avatar.
3136       *
3137       *     @type int          $height        Display height of the avatar in pixels. Defaults to $size.
3138       *     @type int          $width         Display width of the avatar in pixels. Defaults to $size.
3139       *     @type bool         $force_default Whether to always show the default image, never the Gravatar.
3140       *                                       Default false.
3141       *     @type string       $rating        What rating to display avatars up to. Accepts:
3142       *                                       - 'G' (suitable for all audiences)
3143       *                                       - 'PG' (possibly offensive, usually for audiences 13 and above)
3144       *                                       - 'R' (intended for adult audiences above 17)
3145       *                                       - 'X' (even more mature than above)
3146       *                                       Default is the value of the 'avatar_rating' option.
3147       *     @type string       $scheme        URL scheme to use. See set_url_scheme() for accepted values.
3148       *                                       Default null.
3149       *     @type array|string $class         Array or string of additional classes to add to the img element.
3150       *                                       Default null.
3151       *     @type bool         $force_display Whether to always show the avatar - ignores the show_avatars option.
3152       *                                       Default false.
3153       *     @type string       $loading       Value for the `loading` attribute.
3154       *                                       Default null.
3155       *     @type string       $fetchpriority Value for the `fetchpriority` attribute.
3156       *                                       Default null.
3157       *     @type string       $decoding      Value for the `decoding` attribute.
3158       *                                       Default null.
3159       *     @type string       $extra_attr    HTML attributes to insert in the IMG element. Is not sanitized.
3160       *                                       Default empty.
3161       * }
3162       * @return string|false `<img>` tag for the user's avatar. False on failure.
3163       */
3164  	function get_avatar( $id_or_email, $size = 96, $default_value = '', $alt = '', $args = null ) {
3165          $defaults = array(
3166              // get_avatar_data() args.
3167              'size'          => 96,
3168              'height'        => null,
3169              'width'         => null,
3170              'default'       => get_option( 'avatar_default', 'mystery' ),
3171              'force_default' => false,
3172              'rating'        => get_option( 'avatar_rating' ),
3173              'scheme'        => null,
3174              'alt'           => '',
3175              'class'         => null,
3176              'force_display' => false,
3177              'loading'       => null,
3178              'fetchpriority' => null,
3179              'decoding'      => null,
3180              'extra_attr'    => '',
3181          );
3182  
3183          if ( empty( $args ) ) {
3184              $args = array();
3185          }
3186  
3187          $args['size']    = (int) $size;
3188          $args['default'] = $default_value;
3189          $args['alt']     = $alt;
3190  
3191          $args = wp_parse_args( $args, $defaults );
3192  
3193          if ( empty( $args['height'] ) ) {
3194              $args['height'] = $args['size'];
3195          }
3196          if ( empty( $args['width'] ) ) {
3197              $args['width'] = $args['size'];
3198          }
3199  
3200          // Update args with loading optimized attributes.
3201          $loading_optimization_attr = wp_get_loading_optimization_attributes( 'img', $args, 'get_avatar' );
3202  
3203          $args = array_merge( $args, $loading_optimization_attr );
3204  
3205          if ( is_object( $id_or_email ) && isset( $id_or_email->comment_ID ) ) {
3206              $id_or_email = get_comment( $id_or_email );
3207          }
3208  
3209          /**
3210           * Allows the HTML for a user's avatar to be returned early.
3211           *
3212           * Returning a non-null value will effectively short-circuit get_avatar(), passing
3213           * the value through the {@see 'get_avatar'} filter and returning early.
3214           *
3215           * @since 4.2.0
3216           *
3217           * @param string|null $avatar      HTML for the user's avatar. Default null.
3218           * @param mixed       $id_or_email The avatar to retrieve. Accepts a user ID, Gravatar MD5 hash,
3219           *                                 user email, WP_User object, WP_Post object, or WP_Comment object.
3220           * @param array       $args        Arguments passed to get_avatar_url(), after processing.
3221           */
3222          $avatar = apply_filters( 'pre_get_avatar', null, $id_or_email, $args );
3223  
3224          if ( ! is_null( $avatar ) ) {
3225              /** This filter is documented in wp-includes/pluggable.php */
3226              return apply_filters( 'get_avatar', $avatar, $id_or_email, $args['size'], $args['default'], $args['alt'], $args );
3227          }
3228  
3229          if ( ! $args['force_display'] && ! get_option( 'show_avatars' ) ) {
3230              return false;
3231          }
3232  
3233          $url2x = get_avatar_url( $id_or_email, array_merge( $args, array( 'size' => $args['size'] * 2 ) ) );
3234  
3235          $args = get_avatar_data( $id_or_email, $args );
3236  
3237          $url = $args['url'];
3238  
3239          if ( ! $url || is_wp_error( $url ) ) {
3240              return false;
3241          }
3242  
3243          $class = array( 'avatar', 'avatar-' . (int) $args['size'], 'photo' );
3244  
3245          if ( ! $args['found_avatar'] || $args['force_default'] ) {
3246              $class[] = 'avatar-default';
3247          }
3248  
3249          if ( $args['class'] ) {
3250              if ( is_array( $args['class'] ) ) {
3251                  $class = array_merge( $class, $args['class'] );
3252              } else {
3253                  $class[] = $args['class'];
3254              }
3255          }
3256  
3257          // Add `loading`, `fetchpriority`, and `decoding` attributes.
3258          $extra_attr = $args['extra_attr'];
3259  
3260          if ( in_array( $args['loading'], array( 'lazy', 'eager' ), true )
3261              && ! preg_match( '/\bloading\s*=/', $extra_attr )
3262          ) {
3263              if ( ! empty( $extra_attr ) ) {
3264                  $extra_attr .= ' ';
3265              }
3266  
3267              $extra_attr .= "loading='{$args['loading']}'";
3268          }
3269  
3270          if ( in_array( $args['fetchpriority'], array( 'high', 'low', 'auto' ), true )
3271              && ! preg_match( '/\bfetchpriority\s*=/', $extra_attr )
3272          ) {
3273              if ( ! empty( $extra_attr ) ) {
3274                  $extra_attr .= ' ';
3275              }
3276  
3277              $extra_attr .= "fetchpriority='{$args['fetchpriority']}'";
3278          }
3279  
3280          if ( in_array( $args['decoding'], array( 'async', 'sync', 'auto' ), true )
3281              && ! preg_match( '/\bdecoding\s*=/', $extra_attr )
3282          ) {
3283              if ( ! empty( $extra_attr ) ) {
3284                  $extra_attr .= ' ';
3285              }
3286  
3287              $extra_attr .= "decoding='{$args['decoding']}'";
3288          }
3289  
3290          $avatar = sprintf(
3291              "<img alt='%s' src='%s' srcset='%s' class='%s' height='%d' width='%d' %s/>",
3292              esc_attr( $args['alt'] ),
3293              esc_url( $url ),
3294              esc_url( $url2x ) . ' 2x',
3295              esc_attr( implode( ' ', $class ) ),
3296              (int) $args['height'],
3297              (int) $args['width'],
3298              $extra_attr
3299          );
3300  
3301          /**
3302           * Filters the HTML for a user's avatar.
3303           *
3304           * @since 2.5.0
3305           * @since 4.2.0 Added the `$args` parameter.
3306           *
3307           * @param string $avatar        HTML for the user's avatar.
3308           * @param mixed  $id_or_email   The avatar to retrieve. Accepts a user ID, Gravatar MD5 hash,
3309           *                              user email, WP_User object, WP_Post object, or WP_Comment object.
3310           * @param int    $size          Height and width of the avatar in pixels.
3311           * @param string $default_value URL for the default image or a default type. Accepts:
3312           *                              - '404' (return a 404 instead of a default image)
3313           *                              - 'retro' (a 8-bit arcade-style pixelated face)
3314           *                              - 'robohash' (a robot)
3315           *                              - 'monsterid' (a monster)
3316           *                              - 'wavatar' (a cartoon face)
3317           *                              - 'identicon' (the "quilt", a geometric pattern)
3318           *                              - 'mystery', 'mm', or 'mysteryman' (The Oyster Man)
3319           *                              - 'blank' (transparent GIF)
3320           *                              - 'gravatar_default' (the Gravatar logo)
3321           * @param string $alt           Alternative text to use in the avatar image tag.
3322           * @param array  $args          Arguments passed to get_avatar_data(), after processing.
3323           */
3324          return apply_filters( 'get_avatar', $avatar, $id_or_email, $args['size'], $args['default'], $args['alt'], $args );
3325      }
3326  endif;
3327  
3328  if ( ! function_exists( 'wp_text_diff' ) ) :
3329      /**
3330       * Displays a human readable HTML representation of the difference between two strings.
3331       *
3332       * The Diff is available for getting the changes between versions. The output is
3333       * HTML, so the primary use is for displaying the changes. If the two strings
3334       * are equivalent, then an empty string will be returned.
3335       *
3336       * @since 2.6.0
3337       *
3338       * @see wp_parse_args() Used to change defaults to user defined settings.
3339       * @uses Text_Diff
3340       * @uses WP_Text_Diff_Renderer_Table
3341       *
3342       * @param string       $left_string  "old" (left) version of string.
3343       * @param string       $right_string "new" (right) version of string.
3344       * @param string|array $args {
3345       *     Associative array of options to pass to WP_Text_Diff_Renderer_Table().
3346       *
3347       *     @type string $title           Titles the diff in a manner compatible
3348       *                                   with the output. Default empty.
3349       *     @type string $title_left      Change the HTML to the left of the title.
3350       *                                   Default empty.
3351       *     @type string $title_right     Change the HTML to the right of the title.
3352       *                                   Default empty.
3353       *     @type bool   $show_split_view True for split view (two columns), false for
3354       *                                   un-split view (single column). Default true.
3355       * }
3356       * @return string Empty string if strings are equivalent or HTML with differences.
3357       */
3358  	function wp_text_diff( $left_string, $right_string, $args = null ) {
3359          $defaults = array(
3360              'title'           => '',
3361              'title_left'      => '',
3362              'title_right'     => '',
3363              'show_split_view' => true,
3364          );
3365          $args     = wp_parse_args( $args, $defaults );
3366  
3367          if ( ! class_exists( 'WP_Text_Diff_Renderer_Table', false ) ) {
3368              require  ABSPATH . WPINC . '/wp-diff.php';
3369          }
3370  
3371          $left_string  = normalize_whitespace( $left_string );
3372          $right_string = normalize_whitespace( $right_string );
3373  
3374          $left_lines  = explode( "\n", $left_string );
3375          $right_lines = explode( "\n", $right_string );
3376          $text_diff   = new Text_Diff( $left_lines, $right_lines );
3377          $renderer    = new WP_Text_Diff_Renderer_Table( $args );
3378          $diff        = $renderer->render( $text_diff );
3379  
3380          if ( ! $diff ) {
3381              return '';
3382          }
3383  
3384          $is_split_view       = ! empty( $args['show_split_view'] );
3385          $is_split_view_class = $is_split_view ? ' is-split-view' : '';
3386  
3387          $r = "<table class='diff$is_split_view_class'>\n";
3388  
3389          if ( $args['title'] ) {
3390              $r .= "<caption class='diff-title'>$args[title]</caption>\n";
3391          }
3392  
3393          if ( $args['title_left'] || $args['title_right'] ) {
3394              $r .= '<thead>';
3395          }
3396  
3397          if ( $args['title_left'] || $args['title_right'] ) {
3398              $th_or_td_left  = empty( $args['title_left'] ) ? 'td' : 'th';
3399              $th_or_td_right = empty( $args['title_right'] ) ? 'td' : 'th';
3400  
3401              $r .= "<tr class='diff-sub-title'>\n";
3402              $r .= "\t<$th_or_td_left>$args[title_left]</$th_or_td_left>\n";
3403              if ( $is_split_view ) {
3404                  $r .= "\t<$th_or_td_right>$args[title_right]</$th_or_td_right>\n";
3405              }
3406              $r .= "</tr>\n";
3407          }
3408  
3409          if ( $args['title_left'] || $args['title_right'] ) {
3410              $r .= "</thead>\n";
3411          }
3412  
3413          $r .= "<tbody>\n$diff\n</tbody>\n";
3414          $r .= '</table>';
3415  
3416          return $r;
3417      }
3418  endif;


Generated : Thu Sep 4 08:20:02 2025 Cross-referenced by PHPXref