true, ); foreach ( $queries as $key => $query ) { if ( 'relation' === $key ) { $cleaned_query['relation'] = $this->sanitize_relation( $query ); // First-order clause. } elseif ( self::is_first_order_clause( $query ) ) { $cleaned_clause = array_merge( $defaults, $query ); $cleaned_clause['terms'] = (array) $cleaned_clause['terms']; $cleaned_query[] = $cleaned_clause; /* * Keep a copy of the clause in the flate * $queried_terms array, for use in WP_Query. */ if ( ! empty( $cleaned_clause['taxonomy'] ) && 'NOT IN' !== $cleaned_clause['operator'] ) { $taxonomy = $cleaned_clause['taxonomy']; if ( ! isset( $this->queried_terms[ $taxonomy ] ) ) { $this->queried_terms[ $taxonomy ] = array(); } /* * Backward compatibility: Only store the first * 'terms' and 'field' found for a given taxonomy. */ if ( ! empty( $cleaned_clause['terms'] ) && ! isset( $this->queried_terms[ $taxonomy ]['terms'] ) ) { $this->queried_terms[ $taxonomy ]['terms'] = $cleaned_clause['terms']; } if ( ! empty( $cleaned_clause['field'] ) && ! isset( $this->queried_terms[ $taxonomy ]['field'] ) ) { $this->queried_terms[ $taxonomy ]['field'] = $cleaned_clause['field']; } } // Otherwise, it's a nested query, so we recurse. } elseif ( is_array( $query ) ) { $cleaned_subquery = $this->sanitize_query( $query ); if ( ! empty( $cleaned_subquery ) ) { // All queries with children must have a relation. if ( ! isset( $cleaned_subquery['relation'] ) ) { $cleaned_subquery['relation'] = 'AND'; } $cleaned_query[] = $cleaned_subquery; } } } return $cleaned_query; } /** * Sanitizes a 'relation' operator. * * @since 4.1.0 * * @param string $relation Raw relation key from the query argument. * @return string Sanitized relation ('AND' or 'OR'). */ public function sanitize_relation( $relation ) { if ( 'OR' === strtoupper( $relation ) ) { return 'OR'; } else { return 'AND'; } } /** * Determines whether a clause is first-order. * * A "first-order" clause is one that contains any of the first-order * clause keys ('terms', 'taxonomy', 'include_children', 'field', * 'operator'). An empty clause also counts as a first-order clause, * for backward compatibility. Any clause that doesn't meet this is * determined, by process of elimination, to be a higher-order query. * * @since 4.1.0 * * @param array $query Tax query arguments. * @return bool Whether the query clause is a first-order clause. */ protected static function is_first_order_clause( $query ) { return is_array( $query ) && ( empty( $query ) || array_key_exists( 'terms', $query ) || array_key_exists( 'taxonomy', $query ) || array_key_exists( 'include_children', $query ) || array_key_exists( 'field', $query ) || array_key_exists( 'operator', $query ) ); } /** * Generates SQL clauses to be appended to a main query. * * @since 3.1.0 * * @param string $primary_table Database table where the object being filtered is stored (eg wp_users). * @param string $primary_id_column ID column for the filtered object in $primary_table. * @return string[] { * Array containing JOIN and WHERE SQL clauses to append to the main query. * * @type string $join SQL fragment to append to the main JOIN clause. * @type string $where SQL fragment to append to the main WHERE clause. * } */ public function get_sql( $primary_table, $primary_id_column ) { $this->primary_table = $primary_table; $this->primary_id_column = $primary_id_column; return $this->get_sql_clauses(); } /** * Generates SQL clauses to be appended to a main query. * * Called by the public WP_Tax_Query::get_sql(), this method * is abstracted out to maintain parity with the other Query classes. * * @since 4.1.0 * * @return string[] { * Array containing JOIN and WHERE SQL clauses to append to the main query. * * @type string $join SQL fragment to append to the main JOIN clause. * @type string $where SQL fragment to append to the main WHERE clause. * } */ protected function get_sql_clauses() { /* * $queries are passed by reference to get_sql_for_query() for recursion. * To keep $this->queries unaltered, pass a copy. */ $queries = $this->queries; $sql = $this->get_sql_for_query( $queries ); if ( ! empty( $sql['where'] ) ) { $sql['where'] = ' AND ' . $sql['where']; } return $sql; } /** * Generates SQL clauses for a single query array. * * If nested subqueries are found, this method recurses the tree to * produce the properly nested SQL. * * @since 4.1.0 * * @param array $query Query to parse (passed by reference). * @param int $depth Optional. Number of tree levels deep we currently are. * Used to calculate indentation. Default 0. * @return string[] { * Array containing JOIN and WHERE SQL clauses to append to a single query array. * * @type string $join SQL fragment to append to the main JOIN clause. * @type string $where SQL fragment to append to the main WHERE clause. * } */ protected function get_sql_for_query( &$query, $depth = 0 ) { $sql_chunks = array( 'join' => array(), 'where' => array(), ); $sql = array( 'join' => '', 'where' => '', ); $indent = ''; for ( $i = 0; $i < $depth; $i++ ) { $indent .= ' '; } foreach ( $query as $key => &$clause ) { if ( 'relation' === $key ) { $relation = $query['relation']; } elseif ( is_array( $clause ) ) { // This is a first-order clause. if ( $this->is_first_order_clause( $clause ) ) { $clause_sql = $this->get_sql_for_clause( $clause, $query ); $where_count = count( $clause_sql['where'] ); if ( ! $where_count ) { $sql_chunks['where'][] = ''; } elseif ( 1 === $where_count ) { $sql_chunks['where'][] = $clause_sql['where'][0]; } else { $sql_chunks['where'][] = '( ' . implode( ' AND ', $clause_sql['where'] ) . ' )'; } $sql_chunks['join'] = array_merge( $sql_chunks['join'], $clause_sql['join'] ); // This is a subquery, so we recurse. } else { $clause_sql = $this->get_sql_for_query( $clause, $depth + 1 ); $sql_chunks['where'][] = $clause_sql['where']; $sql_chunks['join'][] = $clause_sql['join']; } } } // Filter to remove empties. $sql_chunks['join'] = array_filter( $sql_chunks['join'] ); $sql_chunks['where'] = array_filter( $sql_chunks['where'] ); if ( empty( $relation ) ) { $relation = 'AND'; } // Filter duplicate JOIN clauses and combine into a single string. if ( ! empty( $sql_chunks['join'] ) ) { $sql['join'] = implode( ' ', array_unique( $sql_chunks['join'] ) ); } // Generate a single WHERE clause with proper brackets and indentation. if ( ! empty( $sql_chunks['where'] ) ) { $sql['where'] = '( ' . "\n " . $indent . implode( ' ' . "\n " . $indent . $relation . ' ' . "\n " . $indent, $sql_chunks['where'] ) . "\n" . $indent . ')'; } return $sql; } /** * Generates SQL JOIN and WHERE clauses for a "first-order" query clause. * * @since 4.1.0 * * @global wpdb $wpdb The WordPress database abstraction object. * * @param array $clause Query clause (passed by reference). * @param array $parent_query Parent query array. * @return array { * Array containing JOIN and WHERE SQL clauses to append to a first-order query. * * @type string[] $join Array of SQL fragments to append to the main JOIN clause. * @type string[] $where Array of SQL fragments to append to the main WHERE clause. * } */ public function get_sql_for_clause( &$clause, $parent_query ) { global $wpdb; $sql = array( 'where' => array(), 'join' => array(), ); $join = ''; $where = ''; $this->clean_query( $clause ); if ( is_wp_error( $clause ) ) { return self::$no_results; } $terms = $clause['terms']; $operator = strtoupper( $clause['operator'] ); if ( 'IN' === $operator ) { if ( empty( $terms ) ) { return self::$no_results; } $terms = implode( ',', $terms ); /* * Before creating another table join, see if this clause has a * sibling with an existing join that can be shared. */ $alias = $this->find_compatible_table_alias( $clause, $parent_query ); if ( false === $alias ) { $i = count( $this->table_aliases ); $alias = $i ? 'tt' . $i : $wpdb->term_relationships; // Store the alias as part of a flat array to build future iterators. $this->table_aliases[] = $alias; // Store the alias with this clause, so later siblings can use it. $clause['alias'] = $alias; $join .= " LEFT JOIN $wpdb->term_relationships"; $join .= $i ? " AS $alias" : ''; $join .= " ON ($this->primary_table.$this->primary_id_column = $alias.object_id)"; } $where = "$alias.term_taxonomy_id $operator ($terms)"; } elseif ( 'NOT IN' === $operator ) { if ( empty( $terms ) ) { return $sql; } $terms = implode( ',', $terms ); $where = "$this->primary_table.$this->primary_id_column NOT IN ( SELECT object_id FROM $wpdb->term_relationships WHERE term_taxonomy_id IN ($terms) )"; } elseif ( 'AND' === $operator ) { if ( empty( $terms ) ) { return $sql; } $num_terms = count( $terms ); $terms = implode( ',', $terms ); $where = "( SELECT COUNT(1) FROM $wpdb->term_relationships WHERE term_taxonomy_id IN ($terms) AND object_id = $this->primary_table.$this->primary_id_column ) = $num_terms"; } elseif ( 'NOT EXISTS' === $operator || 'EXISTS' === $operator ) { $where = $wpdb->prepare( "$operator ( SELECT 1 FROM $wpdb->term_relationships INNER JOIN $wpdb->term_taxonomy ON $wpdb->term_taxonomy.term_taxonomy_id = $wpdb->term_relationships.term_taxonomy_id WHERE $wpdb->term_taxonomy.taxonomy = %s AND $wpdb->term_relationships.object_id = $this->primary_table.$this->primary_id_column )", $clause['taxonomy'] ); } $sql['join'][] = $join; $sql['where'][] = $where; return $sql; } /** * Identifies an existing table alias that is compatible with the current query clause. * * We avoid unnecessary table joins by allowing each clause to look for * an existing table alias that is compatible with the query that it * needs to perform. * * An existing alias is compatible if (a) it is a sibling of `$clause` * (ie, it's under the scope of the same relation), and (b) the combination * of operator and relation between the clauses allows for a shared table * join. In the case of WP_Tax_Query, this only applies to 'IN' * clauses that are connected by the relation 'OR'. * * @since 4.1.0 * * @param array $clause Query clause. * @param array $parent_query Parent query of $clause. * @return string|false Table alias if found, otherwise false. */ protected function find_compatible_table_alias( $clause, $parent_query ) { $alias = false; // Sanity check. Only IN queries use the JOIN syntax. if ( ! isset( $clause['operator'] ) || 'IN' !== $clause['operator'] ) { return $alias; } // Since we're only checking IN queries, we're only concerned with OR relations. if ( ! isset( $parent_query['relation'] ) || 'OR' !== $parent_query['relation'] ) { return $alias; } $compatible_operators = array( 'IN' ); foreach ( $parent_query as $sibling ) { if ( ! is_array( $sibling ) || ! $this->is_first_order_clause( $sibling ) ) { continue; } if ( empty( $sibling['alias'] ) || empty( $sibling['operator'] ) ) { continue; } // The sibling must both have compatible operator to share its alias. if ( in_array( strtoupper( $sibling['operator'] ), $compatible_operators, true ) ) { $alias = preg_replace( '/\W/', '_', $sibling['alias'] ); break; } } return $alias; } /** * Validates a single query. * * @since 3.2.0 * * @param array $query The single query. Passed by reference. */ private function clean_query( &$query ) { if ( empty( $query['taxonomy'] ) ) { if ( 'term_taxonomy_id' !== $query['field'] ) { $query = new WP_Error( 'invalid_taxonomy', __( 'Invalid taxonomy.' ) ); return; } // So long as there are shared terms, 'include_children' requires that a taxonomy is set. $query['include_children'] = false; } elseif ( ! taxonomy_exists( $query['taxonomy'] ) ) { $query = new WP_Error( 'invalid_taxonomy', __( 'Invalid taxonomy.' ) ); return; } if ( 'slug' === $query['field'] || 'name' === $query['field'] ) { $query['terms'] = array_unique( (array) $query['terms'] ); } else { $query['terms'] = wp_parse_id_list( $query['terms'] ); } if ( is_taxonomy_hierarchical( $query['taxonomy'] ) && $query['include_children'] ) { $this->transform_query( $query, 'term_id' ); if ( is_wp_error( $query ) ) { return; } $children = array(); foreach ( $query['terms'] as $term ) { $children = array_merge( $children, get_term_children( $term, $query['taxonomy'] ) ); $children[] = $term; } $query['terms'] = $children; } $this->transform_query( $query, 'term_taxonomy_id' ); } /** * Transforms a single query, from one field to another. * * Operates on the `$query` object by reference. In the case of error, * `$query` is converted to a WP_Error object. * * @since 3.2.0 * * @param array $query The single query. Passed by reference. * @param string $resulting_field The resulting field. Accepts 'slug', 'name', 'term_taxonomy_id', * or 'term_id'. Default 'term_id'. */ public function transform_query( &$query, $resulting_field ) { if ( empty( $query['terms'] ) ) { return; } if ( $query['field'] === $resulting_field ) { return; } $resulting_field = sanitize_key( $resulting_field ); // Empty 'terms' always results in a null transformation. $terms = array_filter( $query['terms'] ); if ( empty( $terms ) ) { $query['terms'] = array(); $query['field'] = $resulting_field; return; } $args = array( 'get' => 'all', 'number' => 0, 'taxonomy' => $query['taxonomy'], 'update_term_meta_cache' => false, 'orderby' => 'none', ); // Term query parameter name depends on the 'field' being searched on. switch ( $query['field'] ) { case 'slug': $args['slug'] = $terms; break; case 'name': $args['name'] = $terms; break; case 'term_taxonomy_id': $args['term_taxonomy_id'] = $terms; break; default: $args['include'] = wp_parse_id_list( $terms ); break; } if ( ! is_taxonomy_hierarchical( $query['taxonomy'] ) ) { $args['number'] = count( $terms ); } $term_query = new WP_Term_Query(); $term_list = $term_query->query( $args ); if ( is_wp_error( $term_list ) ) { $query = $term_list; return; } if ( 'AND' === $query['operator'] && count( $term_list ) < count( $query['terms'] ) ) { $query = new WP_Error( 'inexistent_terms', __( 'Inexistent terms.' ) ); return; } $query['terms'] = wp_list_pluck( $term_list, $resulting_field ); $query['field'] = $resulting_field; } } fication_email, $user, $blogname ); wp_mail( $wp_new_user_notification_email['to'], wp_specialchars_decode( sprintf( $wp_new_user_notification_email['subject'], $blogname ) ), $wp_new_user_notification_email['message'], $wp_new_user_notification_email['headers'] ); if ( $switched_locale ) { restore_previous_locale(); } } endif; if ( ! function_exists( 'wp_nonce_tick' ) ) : /** * Returns the time-dependent variable for nonce creation. * * A nonce has a lifespan of two ticks. Nonces in their second tick may be * updated, e.g. by autosave. * * @since 2.5.0 * @since 6.1.0 Added `$action` argument. * * @param string|int $action Optional. The nonce action. Default -1. * @return float Float value rounded up to the next highest integer. */ function wp_nonce_tick( $action = -1 ) { /** * Filters the lifespan of nonces in seconds. * * @since 2.5.0 * @since 6.1.0 Added `$action` argument to allow for more targeted filters. * * @param int $lifespan Lifespan of nonces in seconds. Default 86,400 seconds, or one day. * @param string|int $action The nonce action, or -1 if none was provided. */ $nonce_life = apply_filters( 'nonce_life', DAY_IN_SECONDS, $action ); return ceil( time() / ( $nonce_life / 2 ) ); } endif; if ( ! function_exists( 'wp_verify_nonce' ) ) : /** * Verifies that a correct security nonce was used with time limit. * * A nonce is valid for 24 hours (by default). * * @since 2.0.3 * * @param string $nonce Nonce value that was used for verification, usually via a form field. * @param string|int $action Should give context to what is taking place and be the same when nonce was created. * @return int|false 1 if the nonce is valid and generated between 0-12 hours ago, * 2 if the nonce is valid and generated between 12-24 hours ago. * False if the nonce is invalid. */ function wp_verify_nonce( $nonce, $action = -1 ) { $nonce = (string) $nonce; $user = wp_get_current_user(); $uid = (int) $user->ID; if ( ! $uid ) { /** * Filters whether the user who generated the nonce is logged out. * * @since 3.5.0 * * @param int $uid ID of the nonce-owning user. * @param string|int $action The nonce action, or -1 if none was provided. */ $uid = apply_filters( 'nonce_user_logged_out', $uid, $action ); } if ( empty( $nonce ) ) { return false; } $token = wp_get_session_token(); $i = wp_nonce_tick( $action ); // Nonce generated 0-12 hours ago. $expected = substr( wp_hash( $i . '|' . $action . '|' . $uid . '|' . $token, 'nonce' ), -12, 10 ); if ( hash_equals( $expected, $nonce ) ) { return 1; } // Nonce generated 12-24 hours ago. $expected = substr( wp_hash( ( $i - 1 ) . '|' . $action . '|' . $uid . '|' . $token, 'nonce' ), -12, 10 ); if ( hash_equals( $expected, $nonce ) ) { return 2; } /** * Fires when nonce verification fails. * * @since 4.4.0 * * @param string $nonce The invalid nonce. * @param string|int $action The nonce action. * @param WP_User $user The current user object. * @param string $token The user's session token. */ do_action( 'wp_verify_nonce_failed', $nonce, $action, $user, $token ); // Invalid nonce. return false; } endif; if ( ! function_exists( 'wp_create_nonce' ) ) : /** * Creates a cryptographic token tied to a specific action, user, user session, * and window of time. * * @since 2.0.3 * @since 4.0.0 Session tokens were integrated with nonce creation. * * @param string|int $action Scalar value to add context to the nonce. * @return string The token. */ function wp_create_nonce( $action = -1 ) { $user = wp_get_current_user(); $uid = (int) $user->ID; if ( ! $uid ) { /** This filter is documented in wp-includes/pluggable.php */ $uid = apply_filters( 'nonce_user_logged_out', $uid, $action ); } $token = wp_get_session_token(); $i = wp_nonce_tick( $action ); return substr( wp_hash( $i . '|' . $action . '|' . $uid . '|' . $token, 'nonce' ), -12, 10 ); } endif; if ( ! function_exists( 'wp_salt' ) ) : /** * Returns a salt to add to hashes. * * Salts are created using secret keys. Secret keys are located in two places: * in the database and in the wp-config.php file. The secret key in the database * is randomly generated and will be appended to the secret keys in wp-config.php. * * The secret keys in wp-config.php should be updated to strong, random keys to maximize * security. Below is an example of how the secret key constants are defined. * Do not paste this example directly into wp-config.php. Instead, have a * {@link https://api.wordpress.org/secret-key/1.1/salt/ secret key created} just * for you. * * define('AUTH_KEY', ' XakmM%G4Yt>f`z]MON'); * define('SECURE_AUTH_KEY', 'LzJ}op]mr|6+![P}Ak:uNdJCJZd>(Hx.-Mh#Tz)pCIU#uGEnfFz|f ;;eU%/U^O~'); * define('LOGGED_IN_KEY', '|i|Ux`9z7X>QYR0Z_XnZ@|'); * define('AUTH_SALT', 'eZyT)-Naw]F8CwA*VaW#q*|.)g@o}||wf~@C-YSt}(dh_r6EbI#A,y|nU2{B#JBW'); * define('SECURE_AUTH_SALT', '!=oLUTXh,QW=H `}`L|9/^4-3 STz},T(w}W*c(u`g~EJBf#8u#R{mUEZrozmm'); * define('NONCE_SALT', 'h`GXHhD>SLWVfg1(1(N{;.V!MoE(SfbA_ksP@&`+AycHcAV$+?@3q+rxV{%^VyKT'); * * Salting passwords helps against tools which has stored hashed values of * common dictionary strings. The added values makes it harder to crack. * * @since 2.5.0 * * @link https://api.wordpress.org/secret-key/1.1/salt/ Create secrets for wp-config.php * * @param string $scheme Authentication scheme (auth, secure_auth, logged_in, nonce). * @return string Salt value */ function wp_salt( $scheme = 'auth' ) { static $cached_salts = array(); if ( isset( $cached_salts[ $scheme ] ) ) { /** * Filters the WordPress salt. * * @since 2.5.0 * * @param string $cached_salt Cached salt for the given scheme. * @param string $scheme Authentication scheme. Values include 'auth', * 'secure_auth', 'logged_in', and 'nonce'. */ return apply_filters( 'salt', $cached_salts[ $scheme ], $scheme ); } static $duplicated_keys; if ( null === $duplicated_keys ) { $duplicated_keys = array( 'put your unique phrase here' => true, ); /* * translators: This string should only be translated if wp-config-sample.php is localized. * You can check the localized release package or * https://i18n.svn.wordpress.org//branches//dist/wp-config-sample.php */ $duplicated_keys[ __( 'put your unique phrase here' ) ] = true; foreach ( array( 'AUTH', 'SECURE_AUTH', 'LOGGED_IN', 'NONCE', 'SECRET' ) as $first ) { foreach ( array( 'KEY', 'SALT' ) as $second ) { if ( ! defined( "{$first}_{$second}" ) ) { continue; } $value = constant( "{$first}_{$second}" ); $duplicated_keys[ $value ] = isset( $duplicated_keys[ $value ] ); } } } $values = array( 'key' => '', 'salt' => '', ); if ( defined( 'SECRET_KEY' ) && SECRET_KEY && empty( $duplicated_keys[ SECRET_KEY ] ) ) { $values['key'] = SECRET_KEY; } if ( 'auth' === $scheme && defined( 'SECRET_SALT' ) && SECRET_SALT && empty( $duplicated_keys[ SECRET_SALT ] ) ) { $values['salt'] = SECRET_SALT; } if ( in_array( $scheme, array( 'auth', 'secure_auth', 'logged_in', 'nonce' ), true ) ) { foreach ( array( 'key', 'salt' ) as $type ) { $const = strtoupper( "{$scheme}_{$type}" ); if ( defined( $const ) && constant( $const ) && empty( $duplicated_keys[ constant( $const ) ] ) ) { $values[ $type ] = constant( $const ); } elseif ( ! $values[ $type ] ) { $values[ $type ] = get_site_option( "{$scheme}_{$type}" ); if ( ! $values[ $type ] ) { $values[ $type ] = wp_generate_password( 64, true, true ); update_site_option( "{$scheme}_{$type}", $values[ $type ] ); } } } } else { if ( ! $values['key'] ) { $values['key'] = get_site_option( 'secret_key' ); if ( ! $values['key'] ) { $values['key'] = wp_generate_password( 64, true, true ); update_site_option( 'secret_key', $values['key'] ); } } $values['salt'] = hash_hmac( 'md5', $scheme, $values['key'] ); } $cached_salts[ $scheme ] = $values['key'] . $values['salt']; /** This filter is documented in wp-includes/pluggable.php */ return apply_filters( 'salt', $cached_salts[ $scheme ], $scheme ); } endif; if ( ! function_exists( 'wp_hash' ) ) : /** * Gets hash of given string. * * @since 2.0.3 * * @param string $data Plain text to hash. * @param string $scheme Authentication scheme (auth, secure_auth, logged_in, nonce). * @return string Hash of $data. */ function wp_hash( $data, $scheme = 'auth' ) { $salt = wp_salt( $scheme ); return hash_hmac( 'md5', $data, $salt ); } endif; if ( ! function_exists( 'wp_hash_password' ) ) : /** * Creates a hash (encrypt) of a plain text password. * * For integration with other applications, this function can be overwritten to * instead use the other package password checking algorithm. * * @since 2.5.0 * * @global PasswordHash $wp_hasher PHPass object * * @param string $password Plain text user password to hash. * @return string The hash string of the password. */ function wp_hash_password( $password ) { global $wp_hasher; if ( empty( $wp_hasher ) ) { require_once ABSPATH . WPINC . '/class-phpass.php'; // By default, use the portable hash from phpass. $wp_hasher = new PasswordHash( 8, true ); } return $wp_hasher->HashPassword( trim( $password ) ); } endif; if ( ! function_exists( 'wp_check_password' ) ) : /** * Checks the plaintext password against the encrypted Password. * * Maintains compatibility between old version and the new cookie authentication * protocol using PHPass library. The $hash parameter is the encrypted password * and the function compares the plain text password when encrypted similarly * against the already encrypted password to see if they match. * * For integration with other applications, this function can be overwritten to * instead use the other package password checking algorithm. * * @since 2.5.0 * * @global PasswordHash $wp_hasher PHPass object used for checking the password * against the $hash + $password. * @uses PasswordHash::CheckPassword * * @param string $password Plaintext user's password. * @param string $hash Hash of the user's password to check against. * @param string|int $user_id Optional. User ID. * @return bool False, if the $password does not match the hashed password. */ function wp_check_password( $password, $hash, $user_id = '' ) { global $wp_hasher; // If the hash is still md5... if ( strlen( $hash ) <= 32 ) { $check = hash_equals( $hash, md5( $password ) ); if ( $check && $user_id ) { // Rehash using new hash. wp_set_password( $password, $user_id ); $hash = wp_hash_password( $password ); } /** * Filters whether the plaintext password matches the encrypted password. * * @since 2.5.0 * * @param bool $check Whether the passwords match. * @param string $password The plaintext password. * @param string $hash The hashed password. * @param string|int $user_id User ID. Can be empty. */ return apply_filters( 'check_password', $check, $password, $hash, $user_id ); } /* * If the stored hash is longer than an MD5, * presume the new style phpass portable hash. */ if ( empty( $wp_hasher ) ) { require_once ABSPATH . WPINC . '/class-phpass.php'; // By default, use the portable hash from phpass. $wp_hasher = new PasswordHash( 8, true ); } $check = $wp_hasher->CheckPassword( $password, $hash ); /** This filter is documented in wp-includes/pluggable.php */ return apply_filters( 'check_password', $check, $password, $hash, $user_id ); } endif; if ( ! function_exists( 'wp_generate_password' ) ) : /** * Generates a random password drawn from the defined set of characters. * * Uses wp_rand() to create passwords with far less predictability * than similar native PHP functions like `rand()` or `mt_rand()`. * * @since 2.5.0 * * @param int $length Optional. The length of password to generate. Default 12. * @param bool $special_chars Optional. Whether to include standard special characters. * Default true. * @param bool $extra_special_chars Optional. Whether to include other special characters. * Used when generating secret keys and salts. Default false. * @return string The random password. */ function wp_generate_password( $length = 12, $special_chars = true, $extra_special_chars = false ) { $chars = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789'; if ( $special_chars ) { $chars .= '!@#$%^&*()'; } if ( $extra_special_chars ) { $chars .= '-_ []{}<>~`+=,.;:/?|'; } $password = ''; for ( $i = 0; $i < $length; $i++ ) { $password .= substr( $chars, wp_rand( 0, strlen( $chars ) - 1 ), 1 ); } /** * Filters the randomly-generated password. * * @since 3.0.0 * @since 5.3.0 Added the `$length`, `$special_chars`, and `$extra_special_chars` parameters. * * @param string $password The generated password. * @param int $length The length of password to generate. * @param bool $special_chars Whether to include standard special characters. * @param bool $extra_special_chars Whether to include other special characters. */ return apply_filters( 'random_password', $password, $length, $special_chars, $extra_special_chars ); } endif; if ( ! function_exists( 'wp_rand' ) ) : /** * Generates a random non-negative number. * * @since 2.6.2 * @since 4.4.0 Uses PHP7 random_int() or the random_compat library if available. * @since 6.1.0 Returns zero instead of a random number if both `$min` and `$max` are zero. * * @global string $rnd_value * * @param int $min Optional. Lower limit for the generated number. * Accepts positive integers or zero. Defaults to 0. * @param int $max Optional. Upper limit for the generated number. * Accepts positive integers. Defaults to 4294967295. * @return int A random non-negative number between min and max. */ function wp_rand( $min = null, $max = null ) { global $rnd_value; /* * Some misconfigured 32-bit environments (Entropy PHP, for example) * truncate integers larger than PHP_INT_MAX to PHP_INT_MAX rather than overflowing them to floats. */ $max_random_number = 3000000000 === 2147483647 ? (float) '4294967295' : 4294967295; // 4294967295 = 0xffffffff if ( null === $min ) { $min = 0; } if ( null === $max ) { $max = $max_random_number; } // We only handle ints, floats are truncated to their integer value. $min = (int) $min; $max = (int) $max; // Use PHP's CSPRNG, or a compatible method. static $use_random_int_functionality = true; if ( $use_random_int_functionality ) { try { // wp_rand() can accept arguments in either order, PHP cannot. $_max = max( $min, $max ); $_min = min( $min, $max ); $val = random_int( $_min, $_max ); if ( false !== $val ) { return absint( $val ); } else { $use_random_int_functionality = false; } } catch ( Error $e ) { $use_random_int_functionality = false; } catch ( Exception $e ) { $use_random_int_functionality = false; } } /* * Reset $rnd_value after 14 uses. * 32 (md5) + 40 (sha1) + 40 (sha1) / 8 = 14 random numbers from $rnd_value. */ if ( strlen( $rnd_value ) < 8 ) { if ( defined( 'WP_SETUP_CONFIG' ) ) { static $seed = ''; } else { $seed = get_transient( 'random_seed' ); } $rnd_value = md5( uniqid( microtime() . mt_rand(), true ) . $seed ); $rnd_value .= sha1( $rnd_value ); $rnd_value .= sha1( $rnd_value . $seed ); $seed = md5( $seed . $rnd_value ); if ( ! defined( 'WP_SETUP_CONFIG' ) && ! defined( 'WP_INSTALLING' ) ) { set_transient( 'random_seed', $seed ); } } // Take the first 8 digits for our value. $value = substr( $rnd_value, 0, 8 ); // Strip the first eight, leaving the remainder for the next call to wp_rand(). $rnd_value = substr( $rnd_value, 8 ); $value = abs( hexdec( $value ) ); // Reduce the value to be within the min - max range. $value = $min + ( $max - $min + 1 ) * $value / ( $max_random_number + 1 ); return abs( (int) $value ); } endif; if ( ! function_exists( 'wp_set_password' ) ) : /** * Updates the user's password with a new encrypted one. * * For integration with other applications, this function can be overwritten to * instead use the other package password checking algorithm. * * Please note: This function should be used sparingly and is really only meant for single-time * application. Leveraging this improperly in a plugin or theme could result in an endless loop * of password resets if precautions are not taken to ensure it does not execute on every page load. * * @since 2.5.0 * * @global wpdb $wpdb WordPress database abstraction object. * * @param string $password The plaintext new user password. * @param int $user_id User ID. */ function wp_set_password( $password, $user_id ) { global $wpdb; $hash = wp_hash_password( $password ); $wpdb->update( $wpdb->users, array( 'user_pass' => $hash, 'user_activation_key' => '', ), array( 'ID' => $user_id ) ); clean_user_cache( $user_id ); /** * Fires after the user password is set. * * @since 6.2.0 * * @param string $password The plaintext password just set. * @param int $user_id The ID of the user whose password was just set. */ do_action( 'wp_set_password', $password, $user_id ); } endif; if ( ! function_exists( 'get_avatar' ) ) : /** * Retrieves the avatar `` tag for a user, email address, MD5 hash, comment, or post. * * @since 2.5.0 * @since 4.2.0 Optional `$args` parameter added. * * @param mixed $id_or_email The Gravatar to retrieve. Accepts a user_id, gravatar md5 hash, * user email, WP_User object, WP_Post object, or WP_Comment object. * @param int $size Optional. Height and width of the avatar image file in pixels. Default 96. * @param string $default_value URL for the default image or a default type. Accepts '404' (return * a 404 instead of a default image), 'retro' (8bit), 'RoboHash' (robohash), * 'monsterid' (monster), 'wavatar' (cartoon face), 'indenticon' (the "quilt"), * 'mystery', 'mm', or 'mysteryman' (The Oyster Man), 'blank' (transparent GIF), * or 'gravatar_default' (the Gravatar logo). Default is the value of the * 'avatar_default' option, with a fallback of 'mystery'. * @param string $alt Optional. Alternative text to use in img tag. Default empty. * @param array $args { * Optional. Extra arguments to retrieve the avatar. * * @type int $height Display height of the avatar in pixels. Defaults to $size. * @type int $width Display width of the avatar in pixels. Defaults to $size. * @type bool $force_default Whether to always show the default image, never the Gravatar. Default false. * @type string $rating What rating to display avatars up to. Accepts 'G', 'PG', 'R', 'X', and are * judged in that order. Default is the value of the 'avatar_rating' option. * @type string $scheme URL scheme to use. See set_url_scheme() for accepted values. * Default null. * @type array|string $class Array or string of additional classes to add to the img element. * Default null. * @type bool $force_display Whether to always show the avatar - ignores the show_avatars option. * Default false. * @type string $loading Value for the `loading` attribute. * Default null. * @type string $extra_attr HTML attributes to insert in the IMG element. Is not sanitized. Default empty. * } * @return string|false `` tag for the user's avatar. False on failure. */ function get_avatar( $id_or_email, $size = 96, $default_value = '', $alt = '', $args = null ) { $defaults = array( // get_avatar_data() args. 'size' => 96, 'height' => null, 'width' => null, 'default' => get_option( 'avatar_default', 'mystery' ), 'force_default' => false, 'rating' => get_option( 'avatar_rating' ), 'scheme' => null, 'alt' => '', 'class' => null, 'force_display' => false, 'loading' => null, 'fetchpriority' => null, 'extra_attr' => '', 'decoding' => 'async', ); if ( empty( $args ) ) { $args = array(); } $args['size'] = (int) $size; $args['default'] = $default_value; $args['alt'] = $alt; $args = wp_parse_args( $args, $defaults ); if ( empty( $args['height'] ) ) { $args['height'] = $args['size']; } if ( empty( $args['width'] ) ) { $args['width'] = $args['size']; } // Update args with loading optimized attributes. $loading_optimization_attr = wp_get_loading_optimization_attributes( 'img', $args, 'get_avatar' ); $args = array_merge( $args, $loading_optimization_attr ); if ( is_object( $id_or_email ) && isset( $id_or_email->comment_ID ) ) { $id_or_email = get_comment( $id_or_email ); } /** * Allows the HTML for a user's avatar to be returned early. * * Returning a non-null value will effectively short-circuit get_avatar(), passing * the value through the {@see 'get_avatar'} filter and returning early. * * @since 4.2.0 * * @param string|null $avatar HTML for the user's avatar. Default null. * @param mixed $id_or_email The avatar to retrieve. Accepts a user_id, Gravatar MD5 hash, * user email, WP_User object, WP_Post object, or WP_Comment object. * @param array $args Arguments passed to get_avatar_url(), after processing. */ $avatar = apply_filters( 'pre_get_avatar', null, $id_or_email, $args ); if ( ! is_null( $avatar ) ) { /** This filter is documented in wp-includes/pluggable.php */ return apply_filters( 'get_avatar', $avatar, $id_or_email, $args['size'], $args['default'], $args['alt'], $args ); } if ( ! $args['force_display'] && ! get_option( 'show_avatars' ) ) { return false; } $url2x = get_avatar_url( $id_or_email, array_merge( $args, array( 'size' => $args['size'] * 2 ) ) ); $args = get_avatar_data( $id_or_email, $args ); $url = $args['url']; if ( ! $url || is_wp_error( $url ) ) { return false; } $class = array( 'avatar', 'avatar-' . (int) $args['size'], 'photo' ); if ( ! $args['found_avatar'] || $args['force_default'] ) { $class[] = 'avatar-default'; } if ( $args['class'] ) { if ( is_array( $args['class'] ) ) { $class = array_merge( $class, $args['class'] ); } else { $class[] = $args['class']; } } // Add `loading`, `fetchpriority` and `decoding` attributes. $extra_attr = $args['extra_attr']; if ( in_array( $args['loading'], array( 'lazy', 'eager' ), true ) && ! preg_match( '/\bloading\s*=/', $extra_attr ) ) { if ( ! empty( $extra_attr ) ) { $extra_attr .= ' '; } $extra_attr .= "loading='{$args['loading']}'"; } if ( in_array( $args['decoding'], array( 'async', 'sync', 'auto' ), true ) && ! preg_match( '/\bdecoding\s*=/', $extra_attr ) ) { if ( ! empty( $extra_attr ) ) { $extra_attr .= ' '; } $extra_attr .= "decoding='{$args['decoding']}'"; } // Add support for `fetchpriority`. if ( in_array( $args['fetchpriority'], array( 'high', 'low', 'auto' ), true ) && ! preg_match( '/\bfetchpriority\s*=/', $extra_attr ) ) { if ( ! empty( $extra_attr ) ) { $extra_attr .= ' '; } $extra_attr .= "fetchpriority='{$args['fetchpriority']}'"; } $avatar = sprintf( "%s", esc_attr( $args['alt'] ), esc_url( $url ), esc_url( $url2x ) . ' 2x', esc_attr( implode( ' ', $class ) ), (int) $args['height'], (int) $args['width'], $extra_attr ); /** * Filters the HTML for a user's avatar. * * @since 2.5.0 * @since 4.2.0 The `$args` parameter was added. * * @param string $avatar HTML for the user's avatar. * @param mixed $id_or_email The avatar to retrieve. Accepts a user_id, Gravatar MD5 hash, * user email, WP_User object, WP_Post object, or WP_Comment object. * @param int $size Square avatar width and height in pixels to retrieve. * @param string $default_value URL for the default image or a default type. Accepts '404', 'retro', 'monsterid', * 'wavatar', 'indenticon', 'mystery', 'mm', 'mysteryman', 'blank', or 'gravatar_default'. * @param string $alt Alternative text to use in the avatar image tag. * @param array $args Arguments passed to get_avatar_data(), after processing. */ return apply_filters( 'get_avatar', $avatar, $id_or_email, $args['size'], $args['default'], $args['alt'], $args ); } endif; if ( ! function_exists( 'wp_text_diff' ) ) : /** * Displays a human readable HTML representation of the difference between two strings. * * The Diff is available for getting the changes between versions. The output is * HTML, so the primary use is for displaying the changes. If the two strings * are equivalent, then an empty string will be returned. * * @since 2.6.0 * * @see wp_parse_args() Used to change defaults to user defined settings. * @uses Text_Diff * @uses WP_Text_Diff_Renderer_Table * * @param string $left_string "old" (left) version of string. * @param string $right_string "new" (right) version of string. * @param string|array $args { * Associative array of options to pass to WP_Text_Diff_Renderer_Table(). * * @type string $title Titles the diff in a manner compatible * with the output. Default empty. * @type string $title_left Change the HTML to the left of the title. * Default empty. * @type string $title_right Change the HTML to the right of the title. * Default empty. * @type bool $show_split_view True for split view (two columns), false for * un-split view (single column). Default true. * } * @return string Empty string if strings are equivalent or HTML with differences. */ function wp_text_diff( $left_string, $right_string, $args = null ) { $defaults = array( 'title' => '', 'title_left' => '', 'title_right' => '', 'show_split_view' => true, ); $args = wp_parse_args( $args, $defaults ); if ( ! class_exists( 'WP_Text_Diff_Renderer_Table', false ) ) { require ABSPATH . WPINC . '/wp-diff.php'; } $left_string = normalize_whitespace( $left_string ); $right_string = normalize_whitespace( $right_string ); $left_lines = explode( "\n", $left_string ); $right_lines = explode( "\n", $right_string ); $text_diff = new Text_Diff( $left_lines, $right_lines ); $renderer = new WP_Text_Diff_Renderer_Table( $args ); $diff = $renderer->render( $text_diff ); if ( ! $diff ) { return ''; } $is_split_view = ! empty( $args['show_split_view'] ); $is_split_view_class = $is_split_view ? ' is-split-view' : ''; $r = "\n"; if ( $args['title'] ) { $r .= "\n"; } if ( $args['title_left'] || $args['title_right'] ) { $r .= ''; } if ( $args['title_left'] || $args['title_right'] ) { $th_or_td_left = empty( $args['title_left'] ) ? 'td' : 'th'; $th_or_td_right = empty( $args['title_right'] ) ? 'td' : 'th'; $r .= "\n"; $r .= "\t<$th_or_td_left>$args[title_left]\n"; if ( $is_split_view ) { $r .= "\t<$th_or_td_right>$args[title_right]\n"; } $r .= "\n"; } if ( $args['title_left'] || $args['title_right'] ) { $r .= "\n"; } $r .= "\n$diff\n\n"; $r .= '
$args[title]
'; return $r; } endif;