ausmalbilder-malbuch.de Open in urlscan Pro
137.74.202.223  Public Scan

Submitted URL: http://ausmalbilder-malbuch.de/ausmalbilder-big-mouth
Effective URL: https://ausmalbilder-malbuch.de/ausmalbilder-big-mouth
Submission: On August 16 via api from US — Scanned from DE

Form analysis 0 forms found in the DOM

Text Content

/** * Error Protection API: WP_Recovery_Mode_Email_Link class * * @package
WordPress * @since 5.2.0 */ /** * Core class used to send an email with a link
to begin Recovery Mode. * * @since 5.2.0 */ #[AllowDynamicProperties] final
class WP_Recovery_Mode_Email_Service { const RATE_LIMIT_OPTION =
'recovery_mode_email_last_sent'; /** * Service to generate recovery mode URLs. *
* @since 5.2.0 * @var WP_Recovery_Mode_Link_Service */ private $link_service;
/** * WP_Recovery_Mode_Email_Service constructor. * * @since 5.2.0 * * @param
WP_Recovery_Mode_Link_Service $link_service */ public function __construct(
WP_Recovery_Mode_Link_Service $link_service ) { $this->link_service =
$link_service; } /** * Sends the recovery mode email if the rate limit has not
been sent. * * @since 5.2.0 * * @param int $rate_limit Number of seconds before
another email can be sent. * @param array $error Error details from
`error_get_last()`. * @param array $extension { * The extension that caused the
error. * * @type string $slug The extension slug. The plugin or theme's
directory. * @type string $type The extension type. Either 'plugin' or 'theme'.
* } * @return true|WP_Error True if email sent, WP_Error otherwise. */ public
function maybe_send_recovery_mode_email( $rate_limit, $error, $extension ) {
$last_sent = get_option( self::RATE_LIMIT_OPTION ); if ( ! $last_sent || time()
> $last_sent + $rate_limit ) { if ( ! update_option( self::RATE_LIMIT_OPTION,
time() ) ) { return new WP_Error( 'storage_error', __( 'Could not update the
email last sent time.' ) ); } $sent = $this->send_recovery_mode_email(
$rate_limit, $error, $extension ); if ( $sent ) { return true; } return new
WP_Error( 'email_failed', sprintf( /* translators: %s: mail() */ __( 'The email
could not be sent. Possible reason: your host may have disabled the %s
function.' ), 'mail()' ) ); } $err_message = sprintf( /* translators: 1: Last
sent as a human time diff, 2: Wait time as a human time diff. */ __( 'A recovery
link was already sent %1$s ago. Please wait another %2$s before requesting a new
email.' ), human_time_diff( $last_sent ), human_time_diff( $last_sent +
$rate_limit ) ); return new WP_Error( 'email_sent_already', $err_message ); }
/** * Clears the rate limit, allowing a new recovery mode email to be sent
immediately. * * @since 5.2.0 * * @return bool True on success, false on
failure. */ public function clear_rate_limit() { return delete_option(
self::RATE_LIMIT_OPTION ); } /** * Sends the Recovery Mode email to the site
admin email address. * * @since 5.2.0 * * @param int $rate_limit Number of
seconds before another email can be sent. * @param array $error Error details
from `error_get_last()`. * @param array $extension { * The extension that caused
the error. * * @type string $slug The extension slug. The directory of the
plugin or theme. * @type string $type The extension type. Either 'plugin' or
'theme'. * } * @return bool Whether the email was sent successfully. */ private
function send_recovery_mode_email( $rate_limit, $error, $extension ) { $url =
$this->link_service->generate_url(); $blogname = wp_specialchars_decode(
get_option( 'blogname' ), ENT_QUOTES ); $switched_locale = switch_to_locale(
get_locale() ); if ( $extension ) { $cause = $this->get_cause( $extension );
$details = wp_strip_all_tags( wp_get_extension_error_description( $error ) ); if
( $details ) { $header = __( 'Error Details' ); $details = "\n\n" . $header .
"\n" . str_pad( '', strlen( $header ), '=' ) . "\n" . $details; } } else {
$cause = ''; $details = ''; } /** * Filters the support message sent with the
the fatal error protection email. * * @since 5.2.0 * * @param string $message
The Message to include in the email. */ $support = apply_filters(
'recovery_email_support_info', __( 'Please contact your host for assistance with
investigating this issue further.' ) ); /** * Filters the debug information
included in the fatal error protection email. * * @since 5.3.0 * * @param array
$message An associative array of debug information. */ $debug = apply_filters(
'recovery_email_debug_info', $this->get_debug( $extension ) ); /* translators:
Do not translate LINK, EXPIRES, CAUSE, DETAILS, SITEURL, PAGEURL, SUPPORT.
DEBUG: those are placeholders. */ $message = __( 'Howdy! WordPress has a
built-in feature that detects when a plugin or theme causes a fatal error on
your site, and notifies you with this automated email. ###CAUSE### First, visit
your website (###SITEURL###) and check for any visible issues. Next, visit the
page where the error was caught (###PAGEURL###) and check for any visible
issues. ###SUPPORT### If your site appears broken and you can\'t access your
dashboard normally, WordPress now has a special "recovery mode". This lets you
safely login to your dashboard and investigate further. ###LINK### To keep your
site safe, this link will expire in ###EXPIRES###. Don\'t worry about that,
though: a new link will be emailed to you if the error occurs again after it
expires. When seeking help with this issue, you may be asked for some of the
following information: ###DEBUG### ###DETAILS###' ); $message = str_replace(
array( '###LINK###', '###EXPIRES###', '###CAUSE###', '###DETAILS###',
'###SITEURL###', '###PAGEURL###', '###SUPPORT###', '###DEBUG###', ), array(
$url, human_time_diff( time() + $rate_limit ), $cause ? "\n{$cause}\n" : "\n",
$details, home_url( '/' ), home_url( $_SERVER['REQUEST_URI'] ), $support,
implode( "\r\n", $debug ), ), $message ); $email = array( 'to' =>
$this->get_recovery_mode_email_address(), /* translators: %s: Site title. */
'subject' => __( '[%s] Your Site is Experiencing a Technical Issue' ), 'message'
=> $message, 'headers' => '', 'attachments' => '', ); /** * Filters the contents
of the Recovery Mode email. * * @since 5.2.0 * @since 5.6.0 The `$email`
argument includes the `attachments` key. * * @param array $email { * Used to
build a call to wp_mail(). * * @type string|array $to Array or comma-separated
list of email addresses to send message. * @type string $subject Email subject *
@type string $message Message contents * @type string|array $headers Optional.
Additional headers. * @type string|array $attachments Optional. Files to attach.
* } * @param string $url URL to enter recovery mode. */ $email = apply_filters(
'recovery_mode_email', $email, $url ); $sent = wp_mail( $email['to'],
wp_specialchars_decode( sprintf( $email['subject'], $blogname ) ),
$email['message'], $email['headers'], $email['attachments'] ); if (
$switched_locale ) { restore_previous_locale(); } return $sent; } /** * Gets the
email address to send the recovery mode link to. * * @since 5.2.0 * * @return
string Email address to send recovery mode link to. */ private function
get_recovery_mode_email_address() { if ( defined( 'RECOVERY_MODE_EMAIL' ) &&
is_email( RECOVERY_MODE_EMAIL ) ) { return RECOVERY_MODE_EMAIL; } return
get_option( 'admin_email' ); } /** * Gets the description indicating the
possible cause for the error. * * @since 5.2.0 * * @param array $extension { *
The extension that caused the error. * * @type string $slug The extension slug.
The directory of the plugin or theme. * @type string $type The extension type.
Either 'plugin' or 'theme'. * } * @return string Message about which extension
caused the error. */ private function get_cause( $extension ) { if ( 'plugin'
=== $extension['type'] ) { $plugin = $this->get_plugin( $extension ); if ( false
=== $plugin ) { $name = $extension['slug']; } else { $name = $plugin['Name']; }
/* translators: %s: Plugin name. */ $cause = sprintf( __( 'In this case,
WordPress caught an error with one of your plugins, %s.' ), $name ); } else {
$theme = wp_get_theme( $extension['slug'] ); $name = $theme->exists() ?
$theme->display( 'Name' ) : $extension['slug']; /* translators: %s: Theme name.
*/ $cause = sprintf( __( 'In this case, WordPress caught an error with your
theme, %s.' ), $name ); } return $cause; } /** * Return the details for a single
plugin based on the extension data from an error. * * @since 5.3.0 * * @param
array $extension { * The extension that caused the error. * * @type string $slug
The extension slug. The directory of the plugin or theme. * @type string $type
The extension type. Either 'plugin' or 'theme'. * } * @return array|false A
plugin array {@see get_plugins()} or `false` if no plugin was found. */ private
function get_plugin( $extension ) { if ( ! function_exists( 'get_plugins' ) ) {
require_once ABSPATH . 'wp-admin/includes/plugin.php'; } $plugins =
get_plugins(); // Assume plugin main file name first since it is a common
convention. if ( isset( $plugins[
"{$extension['slug']}/{$extension['slug']}.php" ] ) ) { return $plugins[
"{$extension['slug']}/{$extension['slug']}.php" ]; } else { foreach ( $plugins
as $file => $plugin_data ) { if ( str_starts_with( $file,
"{$extension['slug']}/" ) || $file === $extension['slug'] ) { return
$plugin_data; } } } return false; } /** * Return debug information in an easy to
manipulate format. * * @since 5.3.0 * * @param array $extension { * The
extension that caused the error. * * @type string $slug The extension slug. The
directory of the plugin or theme. * @type string $type The extension type.
Either 'plugin' or 'theme'. * } * @return array An associative array of debug
information. */ private function get_debug( $extension ) { $theme =
wp_get_theme(); $wp_version = get_bloginfo( 'version' ); if ( $extension ) {
$plugin = $this->get_plugin( $extension ); } else { $plugin = null; } $debug =
array( 'wp' => sprintf( /* translators: %s: Current WordPress version number. */
__( 'WordPress version %s' ), $wp_version ), 'theme' => sprintf( /* translators:
1: Current active theme name. 2: Current active theme version. */ __( 'Active
theme: %1$s (version %2$s)' ), $theme->get( 'Name' ), $theme->get( 'Version' )
), ); if ( null !== $plugin ) { $debug['plugin'] = sprintf( /* translators: 1:
The failing plugins name. 2: The failing plugins version. */ __( 'Current
plugin: %1$s (version %2$s)' ), $plugin['Name'], $plugin['Version'] ); }
$debug['php'] = sprintf( /* translators: %s: The currently used PHP version. */
__( 'PHP version %s' ), PHP_VERSION ); return $debug; } } /** * Error Protection
API: WP_Recovery_Mode class * * @package WordPress * @since 5.2.0 */ /** * Core
class used to implement Recovery Mode. * * @since 5.2.0 */
#[AllowDynamicProperties] class WP_Recovery_Mode { const EXIT_ACTION =
'exit_recovery_mode'; /** * Service to handle cookies. * * @since 5.2.0 * @var
WP_Recovery_Mode_Cookie_Service */ private $cookie_service; /** * Service to
generate a recovery mode key. * * @since 5.2.0 * @var
WP_Recovery_Mode_Key_Service */ private $key_service; /** * Service to generate
and validate recovery mode links. * * @since 5.2.0 * @var
WP_Recovery_Mode_Link_Service */ private $link_service; /** * Service to handle
sending an email with a recovery mode link. * * @since 5.2.0 * @var
WP_Recovery_Mode_Email_Service */ private $email_service; /** * Is recovery mode
initialized. * * @since 5.2.0 * @var bool */ private $is_initialized = false;
/** * Is recovery mode active in this session. * * @since 5.2.0 * @var bool */
private $is_active = false; /** * Get an ID representing the current recovery
mode session. * * @since 5.2.0 * @var string */ private $session_id = ''; /** *
WP_Recovery_Mode constructor. * * @since 5.2.0 */ public function __construct()
{ $this->cookie_service = new WP_Recovery_Mode_Cookie_Service();
$this->key_service = new WP_Recovery_Mode_Key_Service(); $this->link_service =
new WP_Recovery_Mode_Link_Service( $this->cookie_service, $this->key_service );
$this->email_service = new WP_Recovery_Mode_Email_Service( $this->link_service
); } /** * Initialize recovery mode for the current request. * * @since 5.2.0 */
public function initialize() { $this->is_initialized = true; add_action(
'wp_logout', array( $this, 'exit_recovery_mode' ) ); add_action( 'login_form_' .
self::EXIT_ACTION, array( $this, 'handle_exit_recovery_mode' ) ); add_action(
'recovery_mode_clean_expired_keys', array( $this, 'clean_expired_keys' ) ); if (
! wp_next_scheduled( 'recovery_mode_clean_expired_keys' ) && ! wp_installing() )
{ wp_schedule_event( time(), 'daily', 'recovery_mode_clean_expired_keys' ); } if
( defined( 'WP_RECOVERY_MODE_SESSION_ID' ) ) { $this->is_active = true;
$this->session_id = WP_RECOVERY_MODE_SESSION_ID; return; } if (
$this->cookie_service->is_cookie_set() ) { $this->handle_cookie(); return; }
$this->link_service->handle_begin_link( $this->get_link_ttl() ); } /** * Checks
whether recovery mode is active. * * This will not change after recovery mode
has been initialized. {@see WP_Recovery_Mode::run()}. * * @since 5.2.0 * *
@return bool True if recovery mode is active, false otherwise. */ public
function is_active() { return $this->is_active; } /** * Gets the recovery mode
session ID. * * @since 5.2.0 * * @return string The session ID if recovery mode
is active, empty string otherwise. */ public function get_session_id() { return
$this->session_id; } /** * Checks whether recovery mode has been initialized. *
* Recovery mode should not be used until this point. Initialization happens
immediately before loading plugins. * * @since 5.2.0 * * @return bool */ public
function is_initialized() { return $this->is_initialized; } /** * Handles a
fatal error occurring. * * The calling API should immediately die() after
calling this function. * * @since 5.2.0 * * @param array $error Error details
from `error_get_last()`. * @return true|WP_Error True if the error was handled
and headers have already been sent. * Or the request will exit to try and catch
multiple errors at once. * WP_Error if an error occurred preventing it from
being handled. */ public function handle_error( array $error ) { $extension =
$this->get_extension_for_error( $error ); if ( ! $extension ||
$this->is_network_plugin( $extension ) ) { return new WP_Error(
'invalid_source', __( 'Error not caused by a plugin or theme.' ) ); } if ( !
$this->is_active() ) { if ( ! is_protected_endpoint() ) { return new WP_Error(
'non_protected_endpoint', __( 'Error occurred on a non-protected endpoint.' ) );
} if ( ! function_exists( 'wp_generate_password' ) ) { require_once ABSPATH .
WPINC . '/pluggable.php'; } return
$this->email_service->maybe_send_recovery_mode_email(
$this->get_email_rate_limit(), $error, $extension ); } if ( !
$this->store_error( $error ) ) { return new WP_Error( 'storage_error', __(
'Failed to store the error.' ) ); } if ( headers_sent() ) { return true; }
$this->redirect_protected(); } /** * Ends the current recovery mode session. * *
@since 5.2.0 * * @return bool True on success, false on failure. */ public
function exit_recovery_mode() { if ( ! $this->is_active() ) { return false; }
$this->email_service->clear_rate_limit(); $this->cookie_service->clear_cookie();
wp_paused_plugins()->delete_all(); wp_paused_themes()->delete_all(); return
true; } /** * Handles a request to exit Recovery Mode. * * @since 5.2.0 */
public function handle_exit_recovery_mode() { $redirect_to = wp_get_referer();
// Safety check in case referrer returns false. if ( ! $redirect_to ) {
$redirect_to = is_user_logged_in() ? admin_url() : home_url(); } if ( !
$this->is_active() ) { wp_safe_redirect( $redirect_to ); die; } if ( ! isset(
$_GET['action'] ) || self::EXIT_ACTION !== $_GET['action'] ) { return; } if ( !
isset( $_GET['_wpnonce'] ) || ! wp_verify_nonce( $_GET['_wpnonce'],
self::EXIT_ACTION ) ) { wp_die( __( 'Exit recovery mode link expired.' ), 403 );
} if ( ! $this->exit_recovery_mode() ) { wp_die( __( 'Failed to exit recovery
mode. Please try again later.' ) ); } wp_safe_redirect( $redirect_to ); die; }
/** * Cleans any recovery mode keys that have expired according to the link TTL.
* * Executes on a daily cron schedule. * * @since 5.2.0 */ public function
clean_expired_keys() { $this->key_service->clean_expired_keys(
$this->get_link_ttl() ); } /** * Handles checking for the recovery mode cookie
and validating it. * * @since 5.2.0 */ protected function handle_cookie() {
$validated = $this->cookie_service->validate_cookie(); if ( is_wp_error(
$validated ) ) { $this->cookie_service->clear_cookie(); $validated->add_data(
array( 'status' => 403 ) ); wp_die( $validated ); } $session_id =
$this->cookie_service->get_session_id_from_cookie(); if ( is_wp_error(
$session_id ) ) { $this->cookie_service->clear_cookie(); $session_id->add_data(
array( 'status' => 403 ) ); wp_die( $session_id ); } $this->is_active = true;
$this->session_id = $session_id; } /** * Gets the rate limit between sending new
recovery mode email links. * * @since 5.2.0 * * @return int Rate limit in
seconds. */ protected function get_email_rate_limit() { /** * Filters the rate
limit between sending new recovery mode email links. * * @since 5.2.0 * * @param
int $rate_limit Time to wait in seconds. Defaults to 1 day. */ return
apply_filters( 'recovery_mode_email_rate_limit', DAY_IN_SECONDS ); } /** * Gets
the number of seconds the recovery mode link is valid for. * * @since 5.2.0 * *
@return int Interval in seconds. */ protected function get_link_ttl() {
$rate_limit = $this->get_email_rate_limit(); $valid_for = $rate_limit; /** *
Filters the amount of time the recovery mode email link is valid for. * * The
ttl must be at least as long as the email rate limit. * * @since 5.2.0 * *
@param int $valid_for The number of seconds the link is valid for. */ $valid_for
= apply_filters( 'recovery_mode_email_link_ttl', $valid_for ); return max(
$valid_for, $rate_limit ); } /** * Gets the extension that the error occurred
in. * * @since 5.2.0 * * @global array $wp_theme_directories * * @param array
$error Error details from `error_get_last()`. * @return array|false { *
Extension details. * * @type string $slug The extension slug. This is the plugin
or theme's directory. * @type string $type The extension type. Either 'plugin'
or 'theme'. * } */ protected function get_extension_for_error( $error ) { global
$wp_theme_directories; if ( ! isset( $error['file'] ) ) { return false; } if ( !
defined( 'WP_PLUGIN_DIR' ) ) { return false; } $error_file = wp_normalize_path(
$error['file'] ); $wp_plugin_dir = wp_normalize_path( WP_PLUGIN_DIR ); if (
str_starts_with( $error_file, $wp_plugin_dir ) ) { $path = str_replace(
$wp_plugin_dir . '/', '', $error_file ); $parts = explode( '/', $path ); return
array( 'type' => 'plugin', 'slug' => $parts[0], ); } if ( empty(
$wp_theme_directories ) ) { return false; } foreach ( $wp_theme_directories as
$theme_directory ) { $theme_directory = wp_normalize_path( $theme_directory );
if ( str_starts_with( $error_file, $theme_directory ) ) { $path = str_replace(
$theme_directory . '/', '', $error_file ); $parts = explode( '/', $path );
return array( 'type' => 'theme', 'slug' => $parts[0], ); } } return false; } /**
* Checks whether the given extension a network activated plugin. * * @since
5.2.0 * * @param array $extension Extension data. * @return bool True if network
plugin, false otherwise. */ protected function is_network_plugin( $extension ) {
if ( 'plugin' !== $extension['type'] ) { return false; } if ( ! is_multisite() )
{ return false; } $network_plugins = wp_get_active_network_plugins(); foreach (
$network_plugins as $plugin ) { if ( str_starts_with( $plugin,
$extension['slug'] . '/' ) ) { return true; } } return false; } /** * Stores the
given error so that the extension causing it is paused. * * @since 5.2.0 * *
@param array $error Error details from `error_get_last()`. * @return bool True
if the error was stored successfully, false otherwise. */ protected function
store_error( $error ) { $extension = $this->get_extension_for_error( $error );
if ( ! $extension ) { return false; } switch ( $extension['type'] ) { case
'plugin': return wp_paused_plugins()->set( $extension['slug'], $error ); case
'theme': return wp_paused_themes()->set( $extension['slug'], $error ); default:
return false; } } /** * Redirects the current request to allow recovering
multiple errors in one go. * * The redirection will only happen when on a
protected endpoint. * * It must be ensured that this method is only called when
an error actually occurred and will not occur on the * next request again.
Otherwise it will create a redirect loop. * * @since 5.2.0 */ protected function
redirect_protected() { // Pluggable is usually loaded after plugins, so we
manually include it here for redirection functionality. if ( ! function_exists(
'wp_safe_redirect' ) ) { require_once ABSPATH . WPINC . '/pluggable.php'; }
$scheme = is_ssl() ? 'https://' : 'http://'; $url =
"{$scheme}{$_SERVER['HTTP_HOST']}{$_SERVER['REQUEST_URI']}"; wp_safe_redirect(
$url ); exit; } } /** * Core Metadata API * * Functions for retrieving and
manipulating metadata of various WordPress object types. Metadata * for an
object is a represented by a simple key-value pair. Objects may contain multiple
* metadata entries that share the same key and differ only in their value. * *
@package WordPress * @subpackage Meta */ require ABSPATH . WPINC .
'/class-wp-metadata-lazyloader.php'; /** * Adds metadata for the specified
object. * * @since 2.9.0 * * @global wpdb $wpdb WordPress database abstraction
object. * * @param string $meta_type Type of object metadata is for. Accepts
'post', 'comment', 'term', 'user', * or any other object type with an associated
meta table. * @param int $object_id ID of the object metadata is for. * @param
string $meta_key Metadata key. * @param mixed $meta_value Metadata value. Must
be serializable if non-scalar. * @param bool $unique Optional. Whether the
specified metadata key should be unique for the object. * If true, and the
object already has a value for the specified metadata key, * no change will be
made. Default false. * @return int|false The meta ID on success, false on
failure. */ function add_metadata( $meta_type, $object_id, $meta_key,
$meta_value, $unique = false ) { global $wpdb; if ( ! $meta_type || ! $meta_key
|| ! is_numeric( $object_id ) ) { return false; } $object_id = absint(
$object_id ); if ( ! $object_id ) { return false; } $table = _get_meta_table(
$meta_type ); if ( ! $table ) { return false; } $meta_subtype =
get_object_subtype( $meta_type, $object_id ); $column = sanitize_key( $meta_type
. '_id' ); // expected_slashed ($meta_key) $meta_key = wp_unslash( $meta_key );
$meta_value = wp_unslash( $meta_value ); $meta_value = sanitize_meta( $meta_key,
$meta_value, $meta_type, $meta_subtype ); /** * Short-circuits adding metadata
of a specific type. * * The dynamic portion of the hook name, `$meta_type`,
refers to the meta object type * (post, comment, term, user, or any other type
with an associated meta table). * Returning a non-null value will effectively
short-circuit the function. * * Possible hook names include: * * -
`add_post_metadata` * - `add_comment_metadata` * - `add_term_metadata` * -
`add_user_metadata` * * @since 3.1.0 * * @param null|bool $check Whether to
allow adding metadata for the given type. * @param int $object_id ID of the
object metadata is for. * @param string $meta_key Metadata key. * @param mixed
$meta_value Metadata value. Must be serializable if non-scalar. * @param bool
$unique Whether the specified meta key should be unique for the object. */
$check = apply_filters( "add_{$meta_type}_metadata", null, $object_id,
$meta_key, $meta_value, $unique ); if ( null !== $check ) { return $check; } if
( $unique && $wpdb->get_var( $wpdb->prepare( "SELECT COUNT(*) FROM $table WHERE
meta_key = %s AND $column = %d", $meta_key, $object_id ) ) ) { return false; }
$_meta_value = $meta_value; $meta_value = maybe_serialize( $meta_value ); /** *
Fires immediately before meta of a specific type is added. * * The dynamic
portion of the hook name, `$meta_type`, refers to the meta object type * (post,
comment, term, user, or any other type with an associated meta table). * *
Possible hook names include: * * - `add_post_meta` * - `add_comment_meta` * -
`add_term_meta` * - `add_user_meta` * * @since 3.1.0 * * @param int $object_id
ID of the object metadata is for. * @param string $meta_key Metadata key. *
@param mixed $_meta_value Metadata value. */ do_action( "add_{$meta_type}_meta",
$object_id, $meta_key, $_meta_value ); $result = $wpdb->insert( $table, array(
$column => $object_id, 'meta_key' => $meta_key, 'meta_value' => $meta_value, )
); if ( ! $result ) { return false; } $mid = (int) $wpdb->insert_id;
wp_cache_delete( $object_id, $meta_type . '_meta' ); /** * Fires immediately
after meta of a specific type is added. * * The dynamic portion of the hook
name, `$meta_type`, refers to the meta object type * (post, comment, term, user,
or any other type with an associated meta table). * * Possible hook names
include: * * - `added_post_meta` * - `added_comment_meta` * - `added_term_meta`
* - `added_user_meta` * * @since 2.9.0 * * @param int $mid The meta ID after
successful update. * @param int $object_id ID of the object metadata is for. *
@param string $meta_key Metadata key. * @param mixed $_meta_value Metadata
value. */ do_action( "added_{$meta_type}_meta", $mid, $object_id, $meta_key,
$_meta_value ); return $mid; } /** * Updates metadata for the specified object.
If no value already exists for the specified object * ID and metadata key, the
metadata will be added. * * @since 2.9.0 * * @global wpdb $wpdb WordPress
database abstraction object. * * @param string $meta_type Type of object
metadata is for. Accepts 'post', 'comment', 'term', 'user', * or any other
object type with an associated meta table. * @param int $object_id ID of the
object metadata is for. * @param string $meta_key Metadata key. * @param mixed
$meta_value Metadata value. Must be serializable if non-scalar. * @param mixed
$prev_value Optional. Previous value to check before updating. * If specified,
only update existing metadata entries with * this value. Otherwise, update all
entries. Default empty string. * @return int|bool The new meta field ID if a
field with the given key didn't exist * and was therefore added, true on
successful update, * false on failure or if the value passed to the function *
is the same as the one that is already in the database. */ function
update_metadata( $meta_type, $object_id, $meta_key, $meta_value, $prev_value =
'' ) { global $wpdb; if ( ! $meta_type || ! $meta_key || ! is_numeric(
$object_id ) ) { return false; } $object_id = absint( $object_id ); if ( !
$object_id ) { return false; } $table = _get_meta_table( $meta_type ); if ( !
$table ) { return false; } $meta_subtype = get_object_subtype( $meta_type,
$object_id ); $column = sanitize_key( $meta_type . '_id' ); $id_column = (
'user' === $meta_type ) ? 'umeta_id' : 'meta_id'; // expected_slashed
($meta_key) $raw_meta_key = $meta_key; $meta_key = wp_unslash( $meta_key );
$passed_value = $meta_value; $meta_value = wp_unslash( $meta_value );
$meta_value = sanitize_meta( $meta_key, $meta_value, $meta_type, $meta_subtype
); /** * Short-circuits updating metadata of a specific type. * * The dynamic
portion of the hook name, `$meta_type`, refers to the meta object type * (post,
comment, term, user, or any other type with an associated meta table). *
Returning a non-null value will effectively short-circuit the function. * *
Possible hook names include: * * - `update_post_metadata` * -
`update_comment_metadata` * - `update_term_metadata` * - `update_user_metadata`
* * @since 3.1.0 * * @param null|bool $check Whether to allow updating metadata
for the given type. * @param int $object_id ID of the object metadata is for. *
@param string $meta_key Metadata key. * @param mixed $meta_value Metadata value.
Must be serializable if non-scalar. * @param mixed $prev_value Optional.
Previous value to check before updating. * If specified, only update existing
metadata entries with * this value. Otherwise, update all entries. */ $check =
apply_filters( "update_{$meta_type}_metadata", null, $object_id, $meta_key,
$meta_value, $prev_value ); if ( null !== $check ) { return (bool) $check; } //
Compare existing value to new value if no prev value given and the key exists
only once. if ( empty( $prev_value ) ) { $old_value = get_metadata_raw(
$meta_type, $object_id, $meta_key ); if ( is_countable( $old_value ) && count(
$old_value ) === 1 ) { if ( $old_value[0] === $meta_value ) { return false; } }
} $meta_ids = $wpdb->get_col( $wpdb->prepare( "SELECT $id_column FROM $table
WHERE meta_key = %s AND $column = %d", $meta_key, $object_id ) ); if ( empty(
$meta_ids ) ) { return add_metadata( $meta_type, $object_id, $raw_meta_key,
$passed_value ); } $_meta_value = $meta_value; $meta_value = maybe_serialize(
$meta_value ); $data = compact( 'meta_value' ); $where = array( $column =>
$object_id, 'meta_key' => $meta_key, ); if ( ! empty( $prev_value ) ) {
$prev_value = maybe_serialize( $prev_value ); $where['meta_value'] =
$prev_value; } foreach ( $meta_ids as $meta_id ) { /** * Fires immediately
before updating metadata of a specific type. * * The dynamic portion of the hook
name, `$meta_type`, refers to the meta object type * (post, comment, term, user,
or any other type with an associated meta table). * * Possible hook names
include: * * - `update_post_meta` * - `update_comment_meta` * -
`update_term_meta` * - `update_user_meta` * * @since 2.9.0 * * @param int
$meta_id ID of the metadata entry to update. * @param int $object_id ID of the
object metadata is for. * @param string $meta_key Metadata key. * @param mixed
$_meta_value Metadata value. */ do_action( "update_{$meta_type}_meta", $meta_id,
$object_id, $meta_key, $_meta_value ); if ( 'post' === $meta_type ) { /** *
Fires immediately before updating a post's metadata. * * @since 2.9.0 * * @param
int $meta_id ID of metadata entry to update. * @param int $object_id Post ID. *
@param string $meta_key Metadata key. * @param mixed $meta_value Metadata value.
This will be a PHP-serialized string representation of the value * if the value
is an array, an object, or itself a PHP-serialized string. */ do_action(
'update_postmeta', $meta_id, $object_id, $meta_key, $meta_value ); } } $result =
$wpdb->update( $table, $data, $where ); if ( ! $result ) { return false; }
wp_cache_delete( $object_id, $meta_type . '_meta' ); foreach ( $meta_ids as
$meta_id ) { /** * Fires immediately after updating metadata of a specific type.
* * The dynamic portion of the hook name, `$meta_type`, refers to the meta
object type * (post, comment, term, user, or any other type with an associated
meta table). * * Possible hook names include: * * - `updated_post_meta` * -
`updated_comment_meta` * - `updated_term_meta` * - `updated_user_meta` * *
@since 2.9.0 * * @param int $meta_id ID of updated metadata entry. * @param int
$object_id ID of the object metadata is for. * @param string $meta_key Metadata
key. * @param mixed $_meta_value Metadata value. */ do_action(
"updated_{$meta_type}_meta", $meta_id, $object_id, $meta_key, $_meta_value ); if
( 'post' === $meta_type ) { /** * Fires immediately after updating a post's
metadata. * * @since 2.9.0 * * @param int $meta_id ID of updated metadata entry.
* @param int $object_id Post ID. * @param string $meta_key Metadata key. *
@param mixed $meta_value Metadata value. This will be a PHP-serialized string
representation of the value * if the value is an array, an object, or itself a
PHP-serialized string. */ do_action( 'updated_postmeta', $meta_id, $object_id,
$meta_key, $meta_value ); } } return true; } /** * Deletes metadata for the
specified object. * * @since 2.9.0 * * @global wpdb $wpdb WordPress database
abstraction object. * * @param string $meta_type Type of object metadata is for.
Accepts 'post', 'comment', 'term', 'user', * or any other object type with an
associated meta table. * @param int $object_id ID of the object metadata is for.
* @param string $meta_key Metadata key. * @param mixed $meta_value Optional.
Metadata value. Must be serializable if non-scalar. * If specified, only delete
metadata entries with this value. * Otherwise, delete all entries with the
specified meta_key. * Pass `null`, `false`, or an empty string to skip this
check. * (For backward compatibility, it is not possible to pass an empty string
* to delete those entries with an empty string for a value.) * Default empty
string. * @param bool $delete_all Optional. If true, delete matching metadata
entries for all objects, * ignoring the specified object_id. Otherwise, only
delete * matching metadata entries for the specified object_id. Default false. *
@return bool True on successful delete, false on failure. */ function
delete_metadata( $meta_type, $object_id, $meta_key, $meta_value = '',
$delete_all = false ) { global $wpdb; if ( ! $meta_type || ! $meta_key || !
is_numeric( $object_id ) && ! $delete_all ) { return false; } $object_id =
absint( $object_id ); if ( ! $object_id && ! $delete_all ) { return false; }
$table = _get_meta_table( $meta_type ); if ( ! $table ) { return false; }
$type_column = sanitize_key( $meta_type . '_id' ); $id_column = ( 'user' ===
$meta_type ) ? 'umeta_id' : 'meta_id'; // expected_slashed ($meta_key) $meta_key
= wp_unslash( $meta_key ); $meta_value = wp_unslash( $meta_value ); /** *
Short-circuits deleting metadata of a specific type. * * The dynamic portion of
the hook name, `$meta_type`, refers to the meta object type * (post, comment,
term, user, or any other type with an associated meta table). * Returning a
non-null value will effectively short-circuit the function. * * Possible hook
names include: * * - `delete_post_metadata` * - `delete_comment_metadata` * -
`delete_term_metadata` * - `delete_user_metadata` * * @since 3.1.0 * * @param
null|bool $delete Whether to allow metadata deletion of the given type. * @param
int $object_id ID of the object metadata is for. * @param string $meta_key
Metadata key. * @param mixed $meta_value Metadata value. Must be serializable if
non-scalar. * @param bool $delete_all Whether to delete the matching metadata
entries * for all objects, ignoring the specified $object_id. * Default false.
*/ $check = apply_filters( "delete_{$meta_type}_metadata", null, $object_id,
$meta_key, $meta_value, $delete_all ); if ( null !== $check ) { return (bool)
$check; } $_meta_value = $meta_value; $meta_value = maybe_serialize( $meta_value
); $query = $wpdb->prepare( "SELECT $id_column FROM $table WHERE meta_key = %s",
$meta_key ); if ( ! $delete_all ) { $query .= $wpdb->prepare( " AND $type_column
= %d", $object_id ); } if ( '' !== $meta_value && null !== $meta_value && false
!== $meta_value ) { $query .= $wpdb->prepare( ' AND meta_value = %s',
$meta_value ); } $meta_ids = $wpdb->get_col( $query ); if ( ! count( $meta_ids )
) { return false; } if ( $delete_all ) { if ( '' !== $meta_value && null !==
$meta_value && false !== $meta_value ) { $object_ids = $wpdb->get_col(
$wpdb->prepare( "SELECT $type_column FROM $table WHERE meta_key = %s AND
meta_value = %s", $meta_key, $meta_value ) ); } else { $object_ids =
$wpdb->get_col( $wpdb->prepare( "SELECT $type_column FROM $table WHERE meta_key
= %s", $meta_key ) ); } } /** * Fires immediately before deleting metadata of a
specific type. * * The dynamic portion of the hook name, `$meta_type`, refers to
the meta object type * (post, comment, term, user, or any other type with an
associated meta table). * * Possible hook names include: * * -
`delete_post_meta` * - `delete_comment_meta` * - `delete_term_meta` * -
`delete_user_meta` * * @since 3.1.0 * * @param string[] $meta_ids An array of
metadata entry IDs to delete. * @param int $object_id ID of the object metadata
is for. * @param string $meta_key Metadata key. * @param mixed $_meta_value
Metadata value. */ do_action( "delete_{$meta_type}_meta", $meta_ids, $object_id,
$meta_key, $_meta_value ); // Old-style action. if ( 'post' === $meta_type ) {
/** * Fires immediately before deleting metadata for a post. * * @since 2.9.0 *
* @param string[] $meta_ids An array of metadata entry IDs to delete. */
do_action( 'delete_postmeta', $meta_ids ); } $query = "DELETE FROM $table WHERE
$id_column IN( " . implode( ',', $meta_ids ) . ' )'; $count = $wpdb->query(
$query ); if ( ! $count ) { return false; } if ( $delete_all ) { $data = (array)
$object_ids; } else { $data = array( $object_id ); } wp_cache_delete_multiple(
$data, $meta_type . '_meta' ); /** * Fires immediately after deleting metadata
of a specific type. * * The dynamic portion of the hook name, `$meta_type`,
refers to the meta object type * (post, comment, term, user, or any other type
with an associated meta table). * * Possible hook names include: * * -
`deleted_post_meta` * - `deleted_comment_meta` * - `deleted_term_meta` * -
`deleted_user_meta` * * @since 2.9.0 * * @param string[] $meta_ids An array of
metadata entry IDs to delete. * @param int $object_id ID of the object metadata
is for. * @param string $meta_key Metadata key. * @param mixed $_meta_value
Metadata value. */ do_action( "deleted_{$meta_type}_meta", $meta_ids,
$object_id, $meta_key, $_meta_value ); // Old-style action. if ( 'post' ===
$meta_type ) { /** * Fires immediately after deleting metadata for a post. * *
@since 2.9.0 * * @param string[] $meta_ids An array of metadata entry IDs to
delete. */ do_action( 'deleted_postmeta', $meta_ids ); } return true; } /** *
Retrieves the value of a metadata field for the specified object type and ID. *
* If the meta field exists, a single value is returned if `$single` is true, *
or an array of values if it's false. * * If the meta field does not exist, the
result depends on get_metadata_default(). * By default, an empty string is
returned if `$single` is true, or an empty array * if it's false. * * @since
2.9.0 * * @see get_metadata_raw() * @see get_metadata_default() * * @param
string $meta_type Type of object metadata is for. Accepts 'post', 'comment',
'term', 'user', * or any other object type with an associated meta table. *
@param int $object_id ID of the object metadata is for. * @param string
$meta_key Optional. Metadata key. If not specified, retrieve all metadata for *
the specified object. Default empty string. * @param bool $single Optional. If
true, return only the first value of the specified `$meta_key`. * This parameter
has no effect if `$meta_key` is not specified. Default false. * @return mixed An
array of values if `$single` is false. * The value of the meta field if
`$single` is true. * False for an invalid `$object_id` (non-numeric, zero, or
negative value), * or if `$meta_type` is not specified. * An empty string if a
valid but non-existing object ID is passed. */ function get_metadata(
$meta_type, $object_id, $meta_key = '', $single = false ) { $value =
get_metadata_raw( $meta_type, $object_id, $meta_key, $single ); if ( ! is_null(
$value ) ) { return $value; } return get_metadata_default( $meta_type,
$object_id, $meta_key, $single ); } /** * Retrieves raw metadata value for the
specified object. * * @since 5.5.0 * * @param string $meta_type Type of object
metadata is for. Accepts 'post', 'comment', 'term', 'user', * or any other
object type with an associated meta table. * @param int $object_id ID of the
object metadata is for. * @param string $meta_key Optional. Metadata key. If not
specified, retrieve all metadata for * the specified object. Default empty
string. * @param bool $single Optional. If true, return only the first value of
the specified `$meta_key`. * This parameter has no effect if `$meta_key` is not
specified. Default false. * @return mixed An array of values if `$single` is
false. * The value of the meta field if `$single` is true. * False for an
invalid `$object_id` (non-numeric, zero, or negative value), * or if
`$meta_type` is not specified. * Null if the value does not exist. */ function
get_metadata_raw( $meta_type, $object_id, $meta_key = '', $single = false ) { if
( ! $meta_type || ! is_numeric( $object_id ) ) { return false; } $object_id =
absint( $object_id ); if ( ! $object_id ) { return false; } /** * Short-circuits
the return value of a meta field. * * The dynamic portion of the hook name,
`$meta_type`, refers to the meta object type * (post, comment, term, user, or
any other type with an associated meta table). * Returning a non-null value will
effectively short-circuit the function. * * Possible filter names include: * * -
`get_post_metadata` * - `get_comment_metadata` * - `get_term_metadata` * -
`get_user_metadata` * * @since 3.1.0 * @since 5.5.0 Added the `$meta_type`
parameter. * * @param mixed $value The value to return, either a single metadata
value or an array * of values depending on the value of `$single`. Default null.
* @param int $object_id ID of the object metadata is for. * @param string
$meta_key Metadata key. * @param bool $single Whether to return only the first
value of the specified `$meta_key`. * @param string $meta_type Type of object
metadata is for. Accepts 'post', 'comment', 'term', 'user', * or any other
object type with an associated meta table. */ $check = apply_filters(
"get_{$meta_type}_metadata", null, $object_id, $meta_key, $single, $meta_type );
if ( null !== $check ) { if ( $single && is_array( $check ) ) { return
$check[0]; } else { return $check; } } $meta_cache = wp_cache_get( $object_id,
$meta_type . '_meta' ); if ( ! $meta_cache ) { $meta_cache = update_meta_cache(
$meta_type, array( $object_id ) ); if ( isset( $meta_cache[ $object_id ] ) ) {
$meta_cache = $meta_cache[ $object_id ]; } else { $meta_cache = null; } } if ( !
$meta_key ) { return $meta_cache; } if ( isset( $meta_cache[ $meta_key ] ) ) {
if ( $single ) { return maybe_unserialize( $meta_cache[ $meta_key ][0] ); } else
{ return array_map( 'maybe_unserialize', $meta_cache[ $meta_key ] ); } } return
null; } /** * Retrieves default metadata value for the specified meta key and
object. * * By default, an empty string is returned if `$single` is true, or an
empty array * if it's false. * * @since 5.5.0 * * @param string $meta_type Type
of object metadata is for. Accepts 'post', 'comment', 'term', 'user', * or any
other object type with an associated meta table. * @param int $object_id ID of
the object metadata is for. * @param string $meta_key Metadata key. * @param
bool $single Optional. If true, return only the first value of the specified
`$meta_key`. * This parameter has no effect if `$meta_key` is not specified.
Default false. * @return mixed An array of default values if `$single` is false.
* The default value of the meta field if `$single` is true. */ function
get_metadata_default( $meta_type, $object_id, $meta_key, $single = false ) { if
( $single ) { $value = ''; } else { $value = array(); } /** * Filters the
default metadata value for a specified meta key and object. * * The dynamic
portion of the hook name, `$meta_type`, refers to the meta object type * (post,
comment, term, user, or any other type with an associated meta table). * *
Possible filter names include: * * - `default_post_metadata` * -
`default_comment_metadata` * - `default_term_metadata` * -
`default_user_metadata` * * @since 5.5.0 * * @param mixed $value The value to
return, either a single metadata value or an array * of values depending on the
value of `$single`. * @param int $object_id ID of the object metadata is for. *
@param string $meta_key Metadata key. * @param bool $single Whether to return
only the first value of the specified `$meta_key`. * @param string $meta_type
Type of object metadata is for. Accepts 'post', 'comment', 'term', 'user', * or
any other object type with an associated meta table. */ $value = apply_filters(
"default_{$meta_type}_metadata", $value, $object_id, $meta_key, $single,
$meta_type ); if ( ! $single && ! wp_is_numeric_array( $value ) ) { $value =
array( $value ); } return $value; } /** * Determines if a meta field with the
given key exists for the given object ID. * * @since 3.3.0 * * @param string
$meta_type Type of object metadata is for. Accepts 'post', 'comment', 'term',
'user', * or any other object type with an associated meta table. * @param int
$object_id ID of the object metadata is for. * @param string $meta_key Metadata
key. * @return bool Whether a meta field with the given key exists. */ function
metadata_exists( $meta_type, $object_id, $meta_key ) { if ( ! $meta_type || !
is_numeric( $object_id ) ) { return false; } $object_id = absint( $object_id );
if ( ! $object_id ) { return false; } /** This filter is documented in
wp-includes/meta.php */ $check = apply_filters( "get_{$meta_type}_metadata",
null, $object_id, $meta_key, true, $meta_type ); if ( null !== $check ) { return
(bool) $check; } $meta_cache = wp_cache_get( $object_id, $meta_type . '_meta' );
if ( ! $meta_cache ) { $meta_cache = update_meta_cache( $meta_type, array(
$object_id ) ); $meta_cache = $meta_cache[ $object_id ]; } if ( isset(
$meta_cache[ $meta_key ] ) ) { return true; } return false; } /** * Retrieves
metadata by meta ID. * * @since 3.3.0 * * @global wpdb $wpdb WordPress database
abstraction object. * * @param string $meta_type Type of object metadata is for.
Accepts 'post', 'comment', 'term', 'user', * or any other object type with an
associated meta table. * @param int $meta_id ID for a specific meta row. *
@return stdClass|false { * Metadata object, or boolean `false` if the metadata
doesn't exist. * * @type string $meta_key The meta key. * @type mixed
$meta_value The unserialized meta value. * @type string $meta_id Optional. The
meta ID when the meta type is any value except 'user'. * @type string $umeta_id
Optional. The meta ID when the meta type is 'user'. * @type string $post_id
Optional. The object ID when the meta type is 'post'. * @type string $comment_id
Optional. The object ID when the meta type is 'comment'. * @type string $term_id
Optional. The object ID when the meta type is 'term'. * @type string $user_id
Optional. The object ID when the meta type is 'user'. * } */ function
get_metadata_by_mid( $meta_type, $meta_id ) { global $wpdb; if ( ! $meta_type ||
! is_numeric( $meta_id ) || floor( $meta_id ) != $meta_id ) { return false; }
$meta_id = (int) $meta_id; if ( $meta_id <= 0 ) { return false; } $table =
_get_meta_table( $meta_type ); if ( ! $table ) { return false; } /** *
Short-circuits the return value when fetching a meta field by meta ID. * * The
dynamic portion of the hook name, `$meta_type`, refers to the meta object type *
(post, comment, term, user, or any other type with an associated meta table). *
Returning a non-null value will effectively short-circuit the function. * *
Possible hook names include: * * - `get_post_metadata_by_mid` * -
`get_comment_metadata_by_mid` * - `get_term_metadata_by_mid` * -
`get_user_metadata_by_mid` * * @since 5.0.0 * * @param stdClass|null $value The
value to return. * @param int $meta_id Meta ID. */ $check = apply_filters(
"get_{$meta_type}_metadata_by_mid", null, $meta_id ); if ( null !== $check ) {
return $check; } $id_column = ( 'user' === $meta_type ) ? 'umeta_id' :
'meta_id'; $meta = $wpdb->get_row( $wpdb->prepare( "SELECT * FROM $table WHERE
$id_column = %d", $meta_id ) ); if ( empty( $meta ) ) { return false; } if (
isset( $meta->meta_value ) ) { $meta->meta_value = maybe_unserialize(
$meta->meta_value ); } return $meta; } /** * Updates metadata by meta ID. * *
@since 3.3.0 * * @global wpdb $wpdb WordPress database abstraction object. * *
@param string $meta_type Type of object metadata is for. Accepts 'post',
'comment', 'term', 'user', * or any other object type with an associated meta
table. * @param int $meta_id ID for a specific meta row. * @param string
$meta_value Metadata value. Must be serializable if non-scalar. * @param
string|false $meta_key Optional. You can provide a meta key to update it.
Default false. * @return bool True on successful update, false on failure. */
function update_metadata_by_mid( $meta_type, $meta_id, $meta_value, $meta_key =
false ) { global $wpdb; // Make sure everything is valid. if ( ! $meta_type || !
is_numeric( $meta_id ) || floor( $meta_id ) != $meta_id ) { return false; }
$meta_id = (int) $meta_id; if ( $meta_id <= 0 ) { return false; } $table =
_get_meta_table( $meta_type ); if ( ! $table ) { return false; } $column =
sanitize_key( $meta_type . '_id' ); $id_column = ( 'user' === $meta_type ) ?
'umeta_id' : 'meta_id'; /** * Short-circuits updating metadata of a specific
type by meta ID. * * The dynamic portion of the hook name, `$meta_type`, refers
to the meta object type * (post, comment, term, user, or any other type with an
associated meta table). * Returning a non-null value will effectively
short-circuit the function. * * Possible hook names include: * * -
`update_post_metadata_by_mid` * - `update_comment_metadata_by_mid` * -
`update_term_metadata_by_mid` * - `update_user_metadata_by_mid` * * @since 5.0.0
* * @param null|bool $check Whether to allow updating metadata for the given
type. * @param int $meta_id Meta ID. * @param mixed $meta_value Meta value. Must
be serializable if non-scalar. * @param string|false $meta_key Meta key, if
provided. */ $check = apply_filters( "update_{$meta_type}_metadata_by_mid",
null, $meta_id, $meta_value, $meta_key ); if ( null !== $check ) { return (bool)
$check; } // Fetch the meta and go on if it's found. $meta =
get_metadata_by_mid( $meta_type, $meta_id ); if ( $meta ) { $original_key =
$meta->meta_key; $object_id = $meta->{$column}; /* * If a new meta_key (last
parameter) was specified, change the meta key, * otherwise use the original key
in the update statement. */ if ( false === $meta_key ) { $meta_key =
$original_key; } elseif ( ! is_string( $meta_key ) ) { return false; }
$meta_subtype = get_object_subtype( $meta_type, $object_id ); // Sanitize the
meta. $_meta_value = $meta_value; $meta_value = sanitize_meta( $meta_key,
$meta_value, $meta_type, $meta_subtype ); $meta_value = maybe_serialize(
$meta_value ); // Format the data query arguments. $data = array( 'meta_key' =>
$meta_key, 'meta_value' => $meta_value, ); // Format the where query arguments.
$where = array(); $where[ $id_column ] = $meta_id; /** This action is documented
in wp-includes/meta.php */ do_action( "update_{$meta_type}_meta", $meta_id,
$object_id, $meta_key, $_meta_value ); if ( 'post' === $meta_type ) { /** This
action is documented in wp-includes/meta.php */ do_action( 'update_postmeta',
$meta_id, $object_id, $meta_key, $meta_value ); } // Run the update query, all
fields in $data are %s, $where is a %d. $result = $wpdb->update( $table, $data,
$where, '%s', '%d' ); if ( ! $result ) { return false; } // Clear the caches.
wp_cache_delete( $object_id, $meta_type . '_meta' ); /** This action is
documented in wp-includes/meta.php */ do_action( "updated_{$meta_type}_meta",
$meta_id, $object_id, $meta_key, $_meta_value ); if ( 'post' === $meta_type ) {
/** This action is documented in wp-includes/meta.php */ do_action(
'updated_postmeta', $meta_id, $object_id, $meta_key, $meta_value ); } return
true; } // And if the meta was not found. return false; } /** * Deletes metadata
by meta ID. * * @since 3.3.0 * * @global wpdb $wpdb WordPress database
abstraction object. * * @param string $meta_type Type of object metadata is for.
Accepts 'post', 'comment', 'term', 'user', * or any other object type with an
associated meta table. * @param int $meta_id ID for a specific meta row. *
@return bool True on successful delete, false on failure. */ function
delete_metadata_by_mid( $meta_type, $meta_id ) { global $wpdb; // Make sure
everything is valid. if ( ! $meta_type || ! is_numeric( $meta_id ) || floor(
$meta_id ) != $meta_id ) { return false; } $meta_id = (int) $meta_id; if (
$meta_id <= 0 ) { return false; } $table = _get_meta_table( $meta_type ); if ( !
$table ) { return false; } // Object and ID columns. $column = sanitize_key(
$meta_type . '_id' ); $id_column = ( 'user' === $meta_type ) ? 'umeta_id' :
'meta_id'; /** * Short-circuits deleting metadata of a specific type by meta ID.
* * The dynamic portion of the hook name, `$meta_type`, refers to the meta
object type * (post, comment, term, user, or any other type with an associated
meta table). * Returning a non-null value will effectively short-circuit the
function. * * Possible hook names include: * * - `delete_post_metadata_by_mid` *
- `delete_comment_metadata_by_mid` * - `delete_term_metadata_by_mid` * -
`delete_user_metadata_by_mid` * * @since 5.0.0 * * @param null|bool $delete
Whether to allow metadata deletion of the given type. * @param int $meta_id Meta
ID. */ $check = apply_filters( "delete_{$meta_type}_metadata_by_mid", null,
$meta_id ); if ( null !== $check ) { return (bool) $check; } // Fetch the meta
and go on if it's found. $meta = get_metadata_by_mid( $meta_type, $meta_id ); if
( $meta ) { $object_id = (int) $meta->{$column}; /** This action is documented
in wp-includes/meta.php */ do_action( "delete_{$meta_type}_meta", (array)
$meta_id, $object_id, $meta->meta_key, $meta->meta_value ); // Old-style action.
if ( 'post' === $meta_type || 'comment' === $meta_type ) { /** * Fires
immediately before deleting post or comment metadata of a specific type. * * The
dynamic portion of the hook name, `$meta_type`, refers to the meta * object type
(post or comment). * * Possible hook names include: * * - `delete_postmeta` * -
`delete_commentmeta` * - `delete_termmeta` * - `delete_usermeta` * * @since
3.4.0 * * @param int $meta_id ID of the metadata entry to delete. */ do_action(
"delete_{$meta_type}meta", $meta_id ); } // Run the query, will return true if
deleted, false otherwise. $result = (bool) $wpdb->delete( $table, array(
$id_column => $meta_id ) ); // Clear the caches. wp_cache_delete( $object_id,
$meta_type . '_meta' ); /** This action is documented in wp-includes/meta.php */
do_action( "deleted_{$meta_type}_meta", (array) $meta_id, $object_id,
$meta->meta_key, $meta->meta_value ); // Old-style action. if ( 'post' ===
$meta_type || 'comment' === $meta_type ) { /** * Fires immediately after
deleting post or comment metadata of a specific type. * * The dynamic portion of
the hook name, `$meta_type`, refers to the meta * object type (post or comment).
* * Possible hook names include: * * - `deleted_postmeta` * -
`deleted_commentmeta` * - `deleted_termmeta` * - `deleted_usermeta` * * @since
3.4.0 * * @param int $meta_id Deleted metadata entry ID. */ do_action(
"deleted_{$meta_type}meta", $meta_id ); } return $result; } // Meta ID was not
found. return false; } /** * Updates the metadata cache for the specified
objects. * * @since 2.9.0 * * @global wpdb $wpdb WordPress database abstraction
object. * * @param string $meta_type Type of object metadata is for. Accepts
'post', 'comment', 'term', 'user', * or any other object type with an associated
meta table. * @param string|int[] $object_ids Array or comma delimited list of
object IDs to update cache for. * @return array|false Metadata cache for the
specified objects, or false on failure. */ function update_meta_cache(
$meta_type, $object_ids ) { global $wpdb; if ( ! $meta_type || ! $object_ids ) {
return false; } $table = _get_meta_table( $meta_type ); if ( ! $table ) { return
false; } $column = sanitize_key( $meta_type . '_id' ); if ( ! is_array(
$object_ids ) ) { $object_ids = preg_replace( '|[^0-9,]|', '', $object_ids );
$object_ids = explode( ',', $object_ids ); } $object_ids = array_map( 'intval',
$object_ids ); /** * Short-circuits updating the metadata cache of a specific
type. * * The dynamic portion of the hook name, `$meta_type`, refers to the meta
object type * (post, comment, term, user, or any other type with an associated
meta table). * Returning a non-null value will effectively short-circuit the
function. * * Possible hook names include: * * - `update_post_metadata_cache` *
- `update_comment_metadata_cache` * - `update_term_metadata_cache` * -
`update_user_metadata_cache` * * @since 5.0.0 * * @param mixed $check Whether to
allow updating the meta cache of the given type. * @param int[] $object_ids
Array of object IDs to update the meta cache for. */ $check = apply_filters(
"update_{$meta_type}_metadata_cache", null, $object_ids ); if ( null !== $check
) { return (bool) $check; } $cache_key = $meta_type . '_meta'; $non_cached_ids =
array(); $cache = array(); $cache_values = wp_cache_get_multiple( $object_ids,
$cache_key ); foreach ( $cache_values as $id => $cached_object ) { if ( false
=== $cached_object ) { $non_cached_ids[] = $id; } else { $cache[ $id ] =
$cached_object; } } if ( empty( $non_cached_ids ) ) { return $cache; } // Get
meta info. $id_list = implode( ',', $non_cached_ids ); $id_column = ( 'user' ===
$meta_type ) ? 'umeta_id' : 'meta_id'; $meta_list = $wpdb->get_results( "SELECT
$column, meta_key, meta_value FROM $table WHERE $column IN ($id_list) ORDER BY
$id_column ASC", ARRAY_A ); if ( ! empty( $meta_list ) ) { foreach ( $meta_list
as $metarow ) { $mpid = (int) $metarow[ $column ]; $mkey = $metarow['meta_key'];
$mval = $metarow['meta_value']; // Force subkeys to be array type. if ( ! isset(
$cache[ $mpid ] ) || ! is_array( $cache[ $mpid ] ) ) { $cache[ $mpid ] =
array(); } if ( ! isset( $cache[ $mpid ][ $mkey ] ) || ! is_array( $cache[ $mpid
][ $mkey ] ) ) { $cache[ $mpid ][ $mkey ] = array(); } // Add a value to the
current pid/key. $cache[ $mpid ][ $mkey ][] = $mval; } } $data = array();
foreach ( $non_cached_ids as $id ) { if ( ! isset( $cache[ $id ] ) ) { $cache[
$id ] = array(); } $data[ $id ] = $cache[ $id ]; } wp_cache_add_multiple( $data,
$cache_key ); return $cache; } /** * Retrieves the queue for lazy-loading
metadata. * * @since 4.5.0 * * @return WP_Metadata_Lazyloader Metadata
lazyloader queue. */ function wp_metadata_lazyloader() { static
$wp_metadata_lazyloader; if ( null === $wp_metadata_lazyloader ) {
$wp_metadata_lazyloader = new WP_Metadata_Lazyloader(); } return
$wp_metadata_lazyloader; } /** * Given a meta query, generates SQL clauses to be
appended to a main query. * * @since 3.2.0 * * @see WP_Meta_Query * * @param
array $meta_query A meta query. * @param string $type Type of meta. * @param
string $primary_table Primary database table name. * @param string
$primary_id_column Primary ID column name. * @param object $context Optional.
The main query object. Default null. * @return string[]|false { * Array
containing JOIN and WHERE SQL clauses to append to the main query, * or false if
no table exists for the requested meta type. * * @type string $join SQL fragment
to append to the main JOIN clause. * @type string $where SQL fragment to append
to the main WHERE clause. * } */ function get_meta_sql( $meta_query, $type,
$primary_table, $primary_id_column, $context = null ) { $meta_query_obj = new
WP_Meta_Query( $meta_query ); return $meta_query_obj->get_sql( $type,
$primary_table, $primary_id_column, $context ); } /** * Retrieves the name of
the metadata table for the specified object type. * * @since 2.9.0 * * @global
wpdb $wpdb WordPress database abstraction object. * * @param string $type Type
of object metadata is for. Accepts 'post', 'comment', 'term', 'user', * or any
other object type with an associated meta table. * @return string|false Metadata
table name, or false if no metadata table exists */ function _get_meta_table(
$type ) { global $wpdb; $table_name = $type . 'meta'; if ( empty(
$wpdb->$table_name ) ) { return false; } return $wpdb->$table_name; } /** *
Determines whether a meta key is considered protected. * * @since 3.1.3 * *
@param string $meta_key Metadata key. * @param string $meta_type Optional. Type
of object metadata is for. Accepts 'post', 'comment', 'term', 'user', * or any
other object type with an associated meta table. Default empty string. * @return
bool Whether the meta key is considered protected. */ function
is_protected_meta( $meta_key, $meta_type = '' ) { $sanitized_key = preg_replace(
"/[^\x20-\x7E\p{L}]/", '', $meta_key ); $protected = strlen( $sanitized_key ) >
0 && ( '_' === $sanitized_key[0] ); /** * Filters whether a meta key is
considered protected. * * @since 3.2.0 * * @param bool $protected Whether the
key is considered protected. * @param string $meta_key Metadata key. * @param
string $meta_type Type of object metadata is for. Accepts 'post', 'comment',
'term', 'user', * or any other object type with an associated meta table. */
return apply_filters( 'is_protected_meta', $protected, $meta_key, $meta_type );
} /** * Sanitizes meta value. * * @since 3.1.3 * @since 4.9.8 The
`$object_subtype` parameter was added. * * @param string $meta_key Metadata key.
* @param mixed $meta_value Metadata value to sanitize. * @param string
$object_type Type of object metadata is for. Accepts 'post', 'comment', 'term',
'user', * or any other object type with an associated meta table. * @param
string $object_subtype Optional. The subtype of the object type. Default empty
string. * @return mixed Sanitized $meta_value. */ function sanitize_meta(
$meta_key, $meta_value, $object_type, $object_subtype = '' ) { if ( ! empty(
$object_subtype ) && has_filter(
"sanitize_{$object_type}_meta_{$meta_key}_for_{$object_subtype}" ) ) { /** *
Filters the sanitization of a specific meta key of a specific meta type and
subtype. * * The dynamic portions of the hook name, `$object_type`, `$meta_key`,
* and `$object_subtype`, refer to the metadata object type (comment, post, term,
or user), * the meta key value, and the object subtype respectively. * * @since
4.9.8 * * @param mixed $meta_value Metadata value to sanitize. * @param string
$meta_key Metadata key. * @param string $object_type Type of object metadata is
for. Accepts 'post', 'comment', 'term', 'user', * or any other object type with
an associated meta table. * @param string $object_subtype Object subtype. */
return apply_filters(
"sanitize_{$object_type}_meta_{$meta_key}_for_{$object_subtype}", $meta_value,
$meta_key, $object_type, $object_subtype ); } /** * Filters the sanitization of
a specific meta key of a specific meta type. * * The dynamic portions of the
hook name, `$meta_type`, and `$meta_key`, * refer to the metadata object type
(comment, post, term, or user) and the meta * key value, respectively. * *
@since 3.3.0 * * @param mixed $meta_value Metadata value to sanitize. * @param
string $meta_key Metadata key. * @param string $object_type Type of object
metadata is for. Accepts 'post', 'comment', 'term', 'user', * or any other
object type with an associated meta table. */ return apply_filters(
"sanitize_{$object_type}_meta_{$meta_key}", $meta_value, $meta_key, $object_type
); } /** * Registers a meta key. * * It is recommended to register meta keys for
a specific combination of object type and object subtype. If passing * an object
subtype is omitted, the meta key will be registered for the entire object type,
however it can be partly * overridden in case a more specific meta key of the
same name exists for the same object type and a subtype. * * If an object type
does not support any subtypes, such as users or comments, you should commonly
call this function * without passing a subtype. * * @since 3.3.0 * @since 4.6.0
{@link https://core.trac.wordpress.org/ticket/35658 Modified * to support an
array of data to attach to registered meta keys}. Previous arguments for *
`$sanitize_callback` and `$auth_callback` have been folded into this array. *
@since 4.9.8 The `$object_subtype` argument was added to the arguments array. *
@since 5.3.0 Valid meta types expanded to include "array" and "object". * @since
5.5.0 The `$default` argument was added to the arguments array. * @since 6.4.0
The `$revisions_enabled` argument was added to the arguments array. * * @param
string $object_type Type of object metadata is for. Accepts 'post', 'comment',
'term', 'user', * or any other object type with an associated meta table. *
@param string $meta_key Meta key to register. * @param array $args { * Data used
to describe the meta key when registered. * * @type string $object_subtype A
subtype; e.g. if the object type is "post", the post type. If left empty, * the
meta key will be registered on the entire object type. Default empty. * @type
string $type The type of data associated with this meta key. * Valid values are
'string', 'boolean', 'integer', 'number', 'array', and 'object'. * @type string
$description A description of the data attached to this meta key. * @type bool
$single Whether the meta key has one value per object, or an array of values per
object. * @type mixed $default The default value returned from get_metadata() if
no value has been set yet. * When using a non-single meta key, the default value
is for the first entry. * In other words, when calling get_metadata() with
`$single` set to `false`, * the default value given here will be wrapped in an
array. * @type callable $sanitize_callback A function or method to call when
sanitizing `$meta_key` data. * @type callable $auth_callback Optional. A
function or method to call when performing edit_post_meta, * add_post_meta, and
delete_post_meta capability checks. * @type bool|array $show_in_rest Whether
data associated with this meta key can be considered public and * should be
accessible via the REST API. A custom post type must also declare * support for
custom fields for registered meta to be accessible via REST. * When registering
complex meta values this argument may optionally be an * array with 'schema' or
'prepare_callback' keys instead of a boolean. * @type bool $revisions_enabled
Whether to enable revisions support for this meta_key. Can only be used when the
* object type is 'post'. * } * @param string|array $deprecated Deprecated. Use
`$args` instead. * @return bool True if the meta key was successfully registered
in the global array, false if not. * Registering a meta key with distinct
sanitize and auth callbacks will fire those callbacks, * but will not add to the
global registry. */ function register_meta( $object_type, $meta_key, $args,
$deprecated = null ) { global $wp_meta_keys; if ( ! is_array( $wp_meta_keys ) )
{ $wp_meta_keys = array(); } $defaults = array( 'object_subtype' => '', 'type'
=> 'string', 'description' => '', 'default' => '', 'single' => false,
'sanitize_callback' => null, 'auth_callback' => null, 'show_in_rest' => false,
'revisions_enabled' => false, ); // There used to be individual args for
sanitize and auth callbacks. $has_old_sanitize_cb = false; $has_old_auth_cb =
false; if ( is_callable( $args ) ) { $args = array( 'sanitize_callback' =>
$args, ); $has_old_sanitize_cb = true; } else { $args = (array) $args; } if (
is_callable( $deprecated ) ) { $args['auth_callback'] = $deprecated;
$has_old_auth_cb = true; } /** * Filters the registration arguments when
registering meta. * * @since 4.6.0 * * @param array $args Array of meta
registration arguments. * @param array $defaults Array of default arguments. *
@param string $object_type Type of object metadata is for. Accepts 'post',
'comment', 'term', 'user', * or any other object type with an associated meta
table. * @param string $meta_key Meta key. */ $args = apply_filters(
'register_meta_args', $args, $defaults, $object_type, $meta_key ); unset(
$defaults['default'] ); $args = wp_parse_args( $args, $defaults ); // Require an
item schema when registering array meta. if ( false !== $args['show_in_rest'] &&
'array' === $args['type'] ) { if ( ! is_array( $args['show_in_rest'] ) || !
isset( $args['show_in_rest']['schema']['items'] ) ) { _doing_it_wrong(
__FUNCTION__, __( 'When registering an "array" meta type to show in the REST
API, you must specify the schema for each array item in
"show_in_rest.schema.items".' ), '5.3.0' ); return false; } } $object_subtype =
! empty( $args['object_subtype'] ) ? $args['object_subtype'] : ''; if (
$args['revisions_enabled'] ) { if ( 'post' !== $object_type ) { _doing_it_wrong(
__FUNCTION__, __( 'Meta keys cannot enable revisions support unless the object
type supports revisions.' ), '6.4.0' ); return false; } elseif ( ! empty(
$object_subtype ) && ! post_type_supports( $object_subtype, 'revisions' ) ) {
_doing_it_wrong( __FUNCTION__, __( 'Meta keys cannot enable revisions support
unless the object subtype supports revisions.' ), '6.4.0' ); return false; } }
// If `auth_callback` is not provided, fall back to `is_protected_meta()`. if (
empty( $args['auth_callback'] ) ) { if ( is_protected_meta( $meta_key,
$object_type ) ) { $args['auth_callback'] = '__return_false'; } else {
$args['auth_callback'] = '__return_true'; } } // Back-compat: old sanitize and
auth callbacks are applied to all of an object type. if ( is_callable(
$args['sanitize_callback'] ) ) { if ( ! empty( $object_subtype ) ) { add_filter(
"sanitize_{$object_type}_meta_{$meta_key}_for_{$object_subtype}",
$args['sanitize_callback'], 10, 4 ); } else { add_filter(
"sanitize_{$object_type}_meta_{$meta_key}", $args['sanitize_callback'], 10, 3 );
} } if ( is_callable( $args['auth_callback'] ) ) { if ( ! empty( $object_subtype
) ) { add_filter( "auth_{$object_type}_meta_{$meta_key}_for_{$object_subtype}",
$args['auth_callback'], 10, 6 ); } else { add_filter(
"auth_{$object_type}_meta_{$meta_key}", $args['auth_callback'], 10, 6 ); } } if
( array_key_exists( 'default', $args ) ) { $schema = $args; if ( is_array(
$args['show_in_rest'] ) && isset( $args['show_in_rest']['schema'] ) ) { $schema
= array_merge( $schema, $args['show_in_rest']['schema'] ); } $check =
rest_validate_value_from_schema( $args['default'], $schema ); if ( is_wp_error(
$check ) ) { _doing_it_wrong( __FUNCTION__, __( 'When registering a default meta
value the data must match the type provided.' ), '5.5.0' ); return false; } if (
! has_filter( "default_{$object_type}_metadata", 'filter_default_metadata' ) ) {
add_filter( "default_{$object_type}_metadata", 'filter_default_metadata', 10, 5
); } } // Global registry only contains meta keys registered with the array of
arguments added in 4.6.0. if ( ! $has_old_auth_cb && ! $has_old_sanitize_cb ) {
unset( $args['object_subtype'] ); $wp_meta_keys[ $object_type ][ $object_subtype
][ $meta_key ] = $args; return true; } return false; } /** * Filters into
default_{$object_type}_metadata and adds in default value. * * @since 5.5.0 * *
@param mixed $value Current value passed to filter. * @param int $object_id ID
of the object metadata is for. * @param string $meta_key Metadata key. * @param
bool $single If true, return only the first value of the specified `$meta_key`.
* This parameter has no effect if `$meta_key` is not specified. * @param string
$meta_type Type of object metadata is for. Accepts 'post', 'comment', 'term',
'user', * or any other object type with an associated meta table. * @return
mixed An array of default values if `$single` is false. * The default value of
the meta field if `$single` is true. */ function filter_default_metadata(
$value, $object_id, $meta_key, $single, $meta_type ) { global $wp_meta_keys; if
( wp_installing() ) { return $value; } if ( ! is_array( $wp_meta_keys ) || !
isset( $wp_meta_keys[ $meta_type ] ) ) { return $value; } $defaults = array();
foreach ( $wp_meta_keys[ $meta_type ] as $sub_type => $meta_data ) { foreach (
$meta_data as $_meta_key => $args ) { if ( $_meta_key === $meta_key &&
array_key_exists( 'default', $args ) ) { $defaults[ $sub_type ] = $args; } } }
if ( ! $defaults ) { return $value; } // If this meta type does not have
subtypes, then the default is keyed as an empty string. if ( isset(
$defaults[''] ) ) { $metadata = $defaults['']; } else { $sub_type =
get_object_subtype( $meta_type, $object_id ); if ( ! isset( $defaults[ $sub_type
] ) ) { return $value; } $metadata = $defaults[ $sub_type ]; } if ( $single ) {
$value = $metadata['default']; } else { $value = array( $metadata['default'] );
} return $value; } /** * Checks if a meta key is registered. * * @since 4.6.0 *
@since 4.9.8 The `$object_subtype` parameter was added. * * @param string
$object_type Type of object metadata is for. Accepts 'post', 'comment', 'term',
'user', * or any other object type with an associated meta table. * @param
string $meta_key Metadata key. * @param string $object_subtype Optional. The
subtype of the object type. Default empty string. * @return bool True if the
meta key is registered to the object type and, if provided, * the object
subtype. False if not. */ function registered_meta_key_exists( $object_type,
$meta_key, $object_subtype = '' ) { $meta_keys = get_registered_meta_keys(
$object_type, $object_subtype ); return isset( $meta_keys[ $meta_key ] ); } /**
* Unregisters a meta key from the list of registered keys. * * @since 4.6.0 *
@since 4.9.8 The `$object_subtype` parameter was added. * * @param string
$object_type Type of object metadata is for. Accepts 'post', 'comment', 'term',
'user', * or any other object type with an associated meta table. * @param
string $meta_key Metadata key. * @param string $object_subtype Optional. The
subtype of the object type. Default empty string. * @return bool True if
successful. False if the meta key was not registered. */ function
unregister_meta_key( $object_type, $meta_key, $object_subtype = '' ) { global
$wp_meta_keys; if ( ! registered_meta_key_exists( $object_type, $meta_key,
$object_subtype ) ) { return false; } $args = $wp_meta_keys[ $object_type ][
$object_subtype ][ $meta_key ]; if ( isset( $args['sanitize_callback'] ) &&
is_callable( $args['sanitize_callback'] ) ) { if ( ! empty( $object_subtype ) )
{ remove_filter(
"sanitize_{$object_type}_meta_{$meta_key}_for_{$object_subtype}",
$args['sanitize_callback'] ); } else { remove_filter(
"sanitize_{$object_type}_meta_{$meta_key}", $args['sanitize_callback'] ); } } if
( isset( $args['auth_callback'] ) && is_callable( $args['auth_callback'] ) ) {
if ( ! empty( $object_subtype ) ) { remove_filter(
"auth_{$object_type}_meta_{$meta_key}_for_{$object_subtype}",
$args['auth_callback'] ); } else { remove_filter(
"auth_{$object_type}_meta_{$meta_key}", $args['auth_callback'] ); } } unset(
$wp_meta_keys[ $object_type ][ $object_subtype ][ $meta_key ] ); // Do some
clean up. if ( empty( $wp_meta_keys[ $object_type ][ $object_subtype ] ) ) {
unset( $wp_meta_keys[ $object_type ][ $object_subtype ] ); } if ( empty(
$wp_meta_keys[ $object_type ] ) ) { unset( $wp_meta_keys[ $object_type ] ); }
return true; } /** * Retrieves a list of registered metadata args for an object
type, keyed by their meta keys. * * @since 4.6.0 * @since 4.9.8 The
`$object_subtype` parameter was added. * * @param string $object_type Type of
object metadata is for. Accepts 'post', 'comment', 'term', 'user', * or any
other object type with an associated meta table. * @param string $object_subtype
Optional. The subtype of the object type. Default empty string. * @return
array[] List of registered metadata args, keyed by their meta keys. */ function
get_registered_meta_keys( $object_type, $object_subtype = '' ) { global
$wp_meta_keys; if ( ! is_array( $wp_meta_keys ) || ! isset( $wp_meta_keys[
$object_type ] ) || ! isset( $wp_meta_keys[ $object_type ][ $object_subtype ] )
) { return array(); } return $wp_meta_keys[ $object_type ][ $object_subtype ]; }
/** * Retrieves registered metadata for a specified object. * * The results
include both meta that is registered specifically for the * object's subtype and
meta that is registered for the entire object type. * * @since 4.6.0 * * @param
string $object_type Type of object metadata is for. Accepts 'post', 'comment',
'term', 'user', * or any other object type with an associated meta table. *
@param int $object_id ID of the object the metadata is for. * @param string
$meta_key Optional. Registered metadata key. If not specified, retrieve all
registered * metadata for the specified object. * @return mixed A single value
or array of values for a key if specified. An array of all registered keys * and
values for an object ID if not. False if a given $meta_key is not registered. */
function get_registered_metadata( $object_type, $object_id, $meta_key = '' ) {
$object_subtype = get_object_subtype( $object_type, $object_id ); if ( ! empty(
$meta_key ) ) { if ( ! empty( $object_subtype ) && ! registered_meta_key_exists(
$object_type, $meta_key, $object_subtype ) ) { $object_subtype = ''; } if ( !
registered_meta_key_exists( $object_type, $meta_key, $object_subtype ) ) {
return false; } $meta_keys = get_registered_meta_keys( $object_type,
$object_subtype ); $meta_key_data = $meta_keys[ $meta_key ]; $data =
get_metadata( $object_type, $object_id, $meta_key, $meta_key_data['single'] );
return $data; } $data = get_metadata( $object_type, $object_id ); if ( ! $data )
{ return array(); } $meta_keys = get_registered_meta_keys( $object_type ); if (
! empty( $object_subtype ) ) { $meta_keys = array_merge( $meta_keys,
get_registered_meta_keys( $object_type, $object_subtype ) ); } return
array_intersect_key( $data, $meta_keys ); } /** * Filters out `register_meta()`
args based on an allowed list. * * `register_meta()` args may change over time,
so requiring the allowed list * to be explicitly turned off is a warranty seal
of sorts. * * @access private * @since 5.5.0 * * @param array $args Arguments
from `register_meta()`. * @param array $default_args Default arguments for
`register_meta()`. * @return array Filtered arguments. */ function
_wp_register_meta_args_allowed_list( $args, $default_args ) { return
array_intersect_key( $args, $default_args ); } /** * Returns the object subtype
for a given object ID of a specific type. * * @since 4.9.8 * * @param string
$object_type Type of object metadata is for. Accepts 'post', 'comment', 'term',
'user', * or any other object type with an associated meta table. * @param int
$object_id ID of the object to retrieve its subtype. * @return string The object
subtype or an empty string if unspecified subtype. */ function
get_object_subtype( $object_type, $object_id ) { $object_id = (int) $object_id;
$object_subtype = ''; switch ( $object_type ) { case 'post': $post_type =
get_post_type( $object_id ); if ( ! empty( $post_type ) ) { $object_subtype =
$post_type; } break; case 'term': $term = get_term( $object_id ); if ( ! $term
instanceof WP_Term ) { break; } $object_subtype = $term->taxonomy; break; case
'comment': $comment = get_comment( $object_id ); if ( ! $comment ) { break; }
$object_subtype = 'comment'; break; case 'user': $user = get_user_by( 'id',
$object_id ); if ( ! $user ) { break; } $object_subtype = 'user'; break; } /** *
Filters the object subtype identifier for a non-standard object type. * * The
dynamic portion of the hook name, `$object_type`, refers to the meta object type
* (post, comment, term, user, or any other type with an associated meta table).
* * Possible hook names include: * * - `get_object_subtype_post` * -
`get_object_subtype_comment` * - `get_object_subtype_term` * -
`get_object_subtype_user` * * @since 4.9.8 * * @param string $object_subtype
Empty string to override. * @param int $object_id ID of the object to get the
subtype for. */ return apply_filters( "get_object_subtype_{$object_type}",
$object_subtype, $object_id ); } /** * Meta API: WP_Meta_Query class * *
@package WordPress * @subpackage Meta * @since 4.4.0 */ /** * Core class used to
implement meta queries for the Meta API. * * Used for generating SQL clauses
that filter a primary query according to metadata keys and values. * *
WP_Meta_Query is a helper that allows primary query classes, such as WP_Query
and WP_User_Query, * * to filter their results by object metadata, by generating
`JOIN` and `WHERE` subclauses to be attached * to the primary SQL query string.
* * @since 3.2.0 */ #[AllowDynamicProperties] class WP_Meta_Query { /** * Array
of metadata queries. * * See WP_Meta_Query::__construct() for information on
meta query arguments. * * @since 3.2.0 * @var array */ public $queries =
array(); /** * The relation between the queries. Can be one of 'AND' or 'OR'. *
* @since 3.2.0 * @var string */ public $relation; /** * Database table to query
for the metadata. * * @since 4.1.0 * @var string */ public $meta_table; /** *
Column in meta_table that represents the ID of the object the metadata belongs
to. * * @since 4.1.0 * @var string */ public $meta_id_column; /** * Database
table that where the metadata's objects are stored (eg $wpdb->users). * * @since
4.1.0 * @var string */ public $primary_table; /** * Column in primary_table that
represents the ID of the object. * * @since 4.1.0 * @var string */ public
$primary_id_column; /** * A flat list of table aliases used in JOIN clauses. * *
@since 4.1.0 * @var array */ protected $table_aliases = array(); /** * A flat
list of clauses, keyed by clause 'name'. * * @since 4.2.0 * @var array */
protected $clauses = array(); /** * Whether the query contains any OR relations.
* * @since 4.3.0 * @var bool */ protected $has_or_relation = false; /** *
Constructor. * * @since 3.2.0 * @since 4.2.0 Introduced support for naming query
clauses by associative array keys. * @since 5.1.0 Introduced `$compare_key`
clause parameter, which enables LIKE key matches. * @since 5.3.0 Increased the
number of operators available to `$compare_key`. Introduced `$type_key`, * which
enables the `$key` to be cast to a new data type for comparisons. * * @param
array $meta_query { * Array of meta query clauses. When first-order clauses or
sub-clauses use strings as * their array keys, they may be referenced in the
'orderby' parameter of the parent query. * * @type string $relation Optional.
The MySQL keyword used to join the clauses of the query. * Accepts 'AND' or
'OR'. Default 'AND'. * @type array ...$0 { * Optional. An array of first-order
clause parameters, or another fully-formed meta query. * * @type string|string[]
$key Meta key or keys to filter by. * @type string $compare_key MySQL operator
used for comparing the $key. Accepts: * - '=' * - '!=' * - 'LIKE' * - 'NOT LIKE'
* - 'IN' * - 'NOT IN' * - 'REGEXP' * - 'NOT REGEXP' * - 'RLIKE', * - 'EXISTS'
(alias of '=') * - 'NOT EXISTS' (alias of '!=') * Default is 'IN' when `$key` is
an array, '=' otherwise. * @type string $type_key MySQL data type that the
meta_key column will be CAST to for * comparisons. Accepts 'BINARY' for
case-sensitive regular expression * comparisons. Default is ''. * @type
string|string[] $value Meta value or values to filter by. * @type string
$compare MySQL operator used for comparing the $value. Accepts: * - '=', * -
'!=' * - '>' * - '>=' * - '<' * - '<=' * - 'LIKE' * - 'NOT LIKE' * - 'IN' * -
'NOT IN' * - 'BETWEEN' * - 'NOT BETWEEN' * - 'REGEXP' * - 'NOT REGEXP' * -
'RLIKE' * - 'EXISTS' * - 'NOT EXISTS' * Default is 'IN' when `$value` is an
array, '=' otherwise. * @type string $type MySQL data type that the meta_value
column will be CAST to for * comparisons. Accepts: * - 'NUMERIC' * - 'BINARY' *
- 'CHAR' * - 'DATE' * - 'DATETIME' * - 'DECIMAL' * - 'SIGNED' * - 'TIME' * -
'UNSIGNED' * Default is 'CHAR'. * } * } */ public function __construct(
$meta_query = false ) { if ( ! $meta_query ) { return; } if ( isset(
$meta_query['relation'] ) && 'OR' === strtoupper( $meta_query['relation'] ) ) {
$this->relation = 'OR'; } else { $this->relation = 'AND'; } $this->queries =
$this->sanitize_query( $meta_query ); } /** * Ensures the 'meta_query' argument
passed to the class constructor is well-formed. * * Eliminates empty items and
ensures that a 'relation' is set. * * @since 4.1.0 * * @param array $queries
Array of query clauses. * @return array Sanitized array of query clauses. */
public function sanitize_query( $queries ) { $clean_queries = array(); if ( !
is_array( $queries ) ) { return $clean_queries; } foreach ( $queries as $key =>
$query ) { if ( 'relation' === $key ) { $relation = $query; } elseif ( !
is_array( $query ) ) { continue; // First-order clause. } elseif (
$this->is_first_order_clause( $query ) ) { if ( isset( $query['value'] ) &&
array() === $query['value'] ) { unset( $query['value'] ); } $clean_queries[ $key
] = $query; // Otherwise, it's a nested query, so we recurse. } else {
$cleaned_query = $this->sanitize_query( $query ); if ( ! empty( $cleaned_query )
) { $clean_queries[ $key ] = $cleaned_query; } } } if ( empty( $clean_queries )
) { return $clean_queries; } // Sanitize the 'relation' key provided in the
query. if ( isset( $relation ) && 'OR' === strtoupper( $relation ) ) {
$clean_queries['relation'] = 'OR'; $this->has_or_relation = true; /* * If there
is only a single clause, call the relation 'OR'. * This value will not actually
be used to join clauses, but it * simplifies the logic around combining key-only
queries. */ } elseif ( 1 === count( $clean_queries ) ) {
$clean_queries['relation'] = 'OR'; // Default to AND. } else {
$clean_queries['relation'] = 'AND'; } return $clean_queries; } /** * Determines
whether a query clause is first-order. * * A first-order meta query clause is
one that has either a 'key' or * a 'value' array key. * * @since 4.1.0 * *
@param array $query Meta query arguments. * @return bool Whether the query
clause is a first-order clause. */ protected function is_first_order_clause(
$query ) { return isset( $query['key'] ) || isset( $query['value'] ); } /** *
Constructs a meta query based on 'meta_*' query vars * * @since 3.2.0 * * @param
array $qv The query variables. */ public function parse_query_vars( $qv ) {
$meta_query = array(); /* * For orderby=meta_value to work correctly, simple
query needs to be * first (so that its table join is against an unaliased meta
table) and * needs to be its own clause (so it doesn't interfere with the logic
of * the rest of the meta_query). */ $primary_meta_query = array(); foreach (
array( 'key', 'compare', 'type', 'compare_key', 'type_key' ) as $key ) { if ( !
empty( $qv[ "meta_$key" ] ) ) { $primary_meta_query[ $key ] = $qv[ "meta_$key"
]; } } // WP_Query sets 'meta_value' = '' by default. if ( isset(
$qv['meta_value'] ) && '' !== $qv['meta_value'] && ( ! is_array(
$qv['meta_value'] ) || $qv['meta_value'] ) ) { $primary_meta_query['value'] =
$qv['meta_value']; } $existing_meta_query = isset( $qv['meta_query'] ) &&
is_array( $qv['meta_query'] ) ? $qv['meta_query'] : array(); if ( ! empty(
$primary_meta_query ) && ! empty( $existing_meta_query ) ) { $meta_query =
array( 'relation' => 'AND', $primary_meta_query, $existing_meta_query, ); }
elseif ( ! empty( $primary_meta_query ) ) { $meta_query = array(
$primary_meta_query, ); } elseif ( ! empty( $existing_meta_query ) ) {
$meta_query = $existing_meta_query; } $this->__construct( $meta_query ); } /** *
Returns the appropriate alias for the given meta type if applicable. * * @since
3.7.0 * * @param string $type MySQL type to cast meta_value. * @return string
MySQL type. */ public function get_cast_for_type( $type = '' ) { if ( empty(
$type ) ) { return 'CHAR'; } $meta_type = strtoupper( $type ); if ( !
preg_match(
'/^(?:BINARY|CHAR|DATE|DATETIME|SIGNED|UNSIGNED|TIME|NUMERIC(?:\(\d+(?:,\s?\d+)?\))?|DECIMAL(?:\(\d+(?:,\s?\d+)?\))?)$/',
$meta_type ) ) { return 'CHAR'; } if ( 'NUMERIC' === $meta_type ) { $meta_type =
'SIGNED'; } return $meta_type; } /** * Generates SQL clauses to be appended to a
main query. * * @since 3.2.0 * * @param string $type Type of meta. Possible
values include but are not limited * to 'post', 'comment', 'blog', 'term', and
'user'. * @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. * @param object $context Optional.
The main query object that corresponds to the type, for * example a `WP_Query`,
`WP_User_Query`, or `WP_Site_Query`. * Default null. * @return string[]|false {
* Array containing JOIN and WHERE SQL clauses to append to the main query, * or
false if no table exists for the requested meta type. * * @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( $type,
$primary_table, $primary_id_column, $context = null ) { $meta_table =
_get_meta_table( $type ); if ( ! $meta_table ) { return false; }
$this->table_aliases = array(); $this->meta_table = $meta_table;
$this->meta_id_column = sanitize_key( $type . '_id' ); $this->primary_table =
$primary_table; $this->primary_id_column = $primary_id_column; $sql =
$this->get_sql_clauses(); /* * If any JOINs are LEFT JOINs (as in the case of
NOT EXISTS), then all JOINs should * be LEFT. Otherwise posts with no metadata
will be excluded from results. */ if ( str_contains( $sql['join'], 'LEFT JOIN' )
) { $sql['join'] = str_replace( 'INNER JOIN', 'LEFT JOIN', $sql['join'] ); } /**
* Filters the meta query's generated SQL. * * @since 3.1.0 * * @param string[]
$sql Array containing the query's JOIN and WHERE clauses. * @param array
$queries Array of meta queries. * @param string $type Type of meta. Possible
values include but are not limited * to 'post', 'comment', 'blog', 'term', and
'user'. * @param string $primary_table Primary table. * @param string
$primary_id_column Primary column ID. * @param object $context The main query
object that corresponds to the type, for * example a `WP_Query`,
`WP_User_Query`, or `WP_Site_Query`. */ return apply_filters_ref_array(
'get_meta_sql', array( $sql, $this->queries, $type, $primary_table,
$primary_id_column, $context ) ); } /** * Generates SQL clauses to be appended
to a main query. * * Called by the public WP_Meta_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, $key ); $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. * *
"First-order" means that it's an array with a 'key' or 'value'. * * @since 4.1.0
* * @global wpdb $wpdb WordPress database abstraction object. * * @param array
$clause Query clause (passed by reference). * @param array $parent_query Parent
query array. * @param string $clause_key Optional. The array key used to name
the clause in the original `$meta_query` * parameters. If not provided, a key
will be generated automatically. * Default empty string. * @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,
$clause_key = '' ) { global $wpdb; $sql_chunks = array( 'where' => array(),
'join' => array(), ); if ( isset( $clause['compare'] ) ) { $clause['compare'] =
strtoupper( $clause['compare'] ); } else { $clause['compare'] = isset(
$clause['value'] ) && is_array( $clause['value'] ) ? 'IN' : '='; }
$non_numeric_operators = array( '=', '!=', 'LIKE', 'NOT LIKE', 'IN', 'NOT IN',
'EXISTS', 'NOT EXISTS', 'RLIKE', 'REGEXP', 'NOT REGEXP', ); $numeric_operators =
array( '>', '>=', '<', '<=', 'BETWEEN', 'NOT BETWEEN', ); if ( ! in_array(
$clause['compare'], $non_numeric_operators, true ) && ! in_array(
$clause['compare'], $numeric_operators, true ) ) { $clause['compare'] = '='; }
if ( isset( $clause['compare_key'] ) ) { $clause['compare_key'] = strtoupper(
$clause['compare_key'] ); } else { $clause['compare_key'] = isset(
$clause['key'] ) && is_array( $clause['key'] ) ? 'IN' : '='; } if ( ! in_array(
$clause['compare_key'], $non_numeric_operators, true ) ) {
$clause['compare_key'] = '='; } $meta_compare = $clause['compare'];
$meta_compare_key = $clause['compare_key']; // First build the JOIN clause, if
one is required. $join = ''; // We prefer to avoid joins if possible. Look for
an existing join compatible with this clause. $alias =
$this->find_compatible_table_alias( $clause, $parent_query ); if ( false ===
$alias ) { $i = count( $this->table_aliases ); $alias = $i ? 'mt' . $i :
$this->meta_table; // JOIN clauses for NOT EXISTS have their own syntax. if (
'NOT EXISTS' === $meta_compare ) { $join .= " LEFT JOIN $this->meta_table";
$join .= $i ? " AS $alias" : ''; if ( 'LIKE' === $meta_compare_key ) { $join .=
$wpdb->prepare( " ON ( $this->primary_table.$this->primary_id_column =
$alias.$this->meta_id_column AND $alias.meta_key LIKE %s )", '%' .
$wpdb->esc_like( $clause['key'] ) . '%' ); } else { $join .= $wpdb->prepare( "
ON ( $this->primary_table.$this->primary_id_column =
$alias.$this->meta_id_column AND $alias.meta_key = %s )", $clause['key'] ); } //
All other JOIN clauses. } else { $join .= " INNER JOIN $this->meta_table"; $join
.= $i ? " AS $alias" : ''; $join .= " ON (
$this->primary_table.$this->primary_id_column = $alias.$this->meta_id_column )";
} $this->table_aliases[] = $alias; $sql_chunks['join'][] = $join; } // Save the
alias to this clause, for future siblings to find. $clause['alias'] = $alias; //
Determine the data type. $_meta_type = isset( $clause['type'] ) ?
$clause['type'] : ''; $meta_type = $this->get_cast_for_type( $_meta_type );
$clause['cast'] = $meta_type; // Fallback for clause keys is the table alias.
Key must be a string. if ( is_int( $clause_key ) || ! $clause_key ) {
$clause_key = $clause['alias']; } // Ensure unique clause keys, so none are
overwritten. $iterator = 1; $clause_key_base = $clause_key; while ( isset(
$this->clauses[ $clause_key ] ) ) { $clause_key = $clause_key_base . '-' .
$iterator; ++$iterator; } // Store the clause in our flat array. $this->clauses[
$clause_key ] =& $clause; // Next, build the WHERE clause. // meta_key. if (
array_key_exists( 'key', $clause ) ) { if ( 'NOT EXISTS' === $meta_compare ) {
$sql_chunks['where'][] = $alias . '.' . $this->meta_id_column . ' IS NULL'; }
else { /** * In joined clauses negative operators have to be nested into a * NOT
EXISTS clause and flipped, to avoid returning records with * matching post IDs
but different meta keys. Here we prepare the * nested clause. */ if ( in_array(
$meta_compare_key, array( '!=', 'NOT IN', 'NOT LIKE', 'NOT EXISTS', 'NOT REGEXP'
), true ) ) { // Negative clauses may be reused. $i = count(
$this->table_aliases ); $subquery_alias = $i ? 'mt' . $i : $this->meta_table;
$this->table_aliases[] = $subquery_alias; $meta_compare_string_start = 'NOT
EXISTS ('; $meta_compare_string_start .= "SELECT 1 FROM $wpdb->postmeta
$subquery_alias "; $meta_compare_string_start .= "WHERE $subquery_alias.post_ID
= $alias.post_ID "; $meta_compare_string_end = 'LIMIT 1';
$meta_compare_string_end .= ')'; } switch ( $meta_compare_key ) { case '=': case
'EXISTS': $where = $wpdb->prepare( "$alias.meta_key = %s", trim( $clause['key']
) ); // phpcs:ignore WordPress.DB.PreparedSQL.InterpolatedNotPrepared break;
case 'LIKE': $meta_compare_value = '%' . $wpdb->esc_like( trim( $clause['key'] )
) . '%'; $where = $wpdb->prepare( "$alias.meta_key LIKE %s", $meta_compare_value
); // phpcs:ignore WordPress.DB.PreparedSQL.InterpolatedNotPrepared break; case
'IN': $meta_compare_string = "$alias.meta_key IN (" . substr( str_repeat( ',%s',
count( $clause['key'] ) ), 1 ) . ')'; $where = $wpdb->prepare(
$meta_compare_string, $clause['key'] ); // phpcs:ignore
WordPress.DB.PreparedSQL.NotPrepared break; case 'RLIKE': case 'REGEXP':
$operator = $meta_compare_key; if ( isset( $clause['type_key'] ) && 'BINARY' ===
strtoupper( $clause['type_key'] ) ) { $cast = 'BINARY'; $meta_key =
"CAST($alias.meta_key AS BINARY)"; } else { $cast = ''; $meta_key =
"$alias.meta_key"; } $where = $wpdb->prepare( "$meta_key $operator $cast %s",
trim( $clause['key'] ) ); // phpcs:ignore
WordPress.DB.PreparedSQL.InterpolatedNotPrepared break; case '!=': case 'NOT
EXISTS': $meta_compare_string = $meta_compare_string_start . "AND
$subquery_alias.meta_key = %s " . $meta_compare_string_end; $where =
$wpdb->prepare( $meta_compare_string, $clause['key'] ); // phpcs:ignore
WordPress.DB.PreparedSQL.NotPrepared break; case 'NOT LIKE':
$meta_compare_string = $meta_compare_string_start . "AND
$subquery_alias.meta_key LIKE %s " . $meta_compare_string_end;
$meta_compare_value = '%' . $wpdb->esc_like( trim( $clause['key'] ) ) . '%';
$where = $wpdb->prepare( $meta_compare_string, $meta_compare_value ); //
phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared break; case 'NOT IN':
$array_subclause = '(' . substr( str_repeat( ',%s', count( $clause['key'] ) ), 1
) . ') '; $meta_compare_string = $meta_compare_string_start . "AND
$subquery_alias.meta_key IN " . $array_subclause . $meta_compare_string_end;
$where = $wpdb->prepare( $meta_compare_string, $clause['key'] ); // phpcs:ignore
WordPress.DB.PreparedSQL.NotPrepared break; case 'NOT REGEXP': $operator =
$meta_compare_key; if ( isset( $clause['type_key'] ) && 'BINARY' === strtoupper(
$clause['type_key'] ) ) { $cast = 'BINARY'; $meta_key =
"CAST($subquery_alias.meta_key AS BINARY)"; } else { $cast = ''; $meta_key =
"$subquery_alias.meta_key"; } $meta_compare_string = $meta_compare_string_start
. "AND $meta_key REGEXP $cast %s " . $meta_compare_string_end; $where =
$wpdb->prepare( $meta_compare_string, $clause['key'] ); // phpcs:ignore
WordPress.DB.PreparedSQL.NotPrepared break; } $sql_chunks['where'][] = $where; }
} // meta_value. if ( array_key_exists( 'value', $clause ) ) { $meta_value =
$clause['value']; if ( in_array( $meta_compare, array( 'IN', 'NOT IN',
'BETWEEN', 'NOT BETWEEN' ), true ) ) { if ( ! is_array( $meta_value ) ) {
$meta_value = preg_split( '/[,\s]+/', $meta_value ); } } elseif ( is_string(
$meta_value ) ) { $meta_value = trim( $meta_value ); } switch ( $meta_compare )
{ case 'IN': case 'NOT IN': $meta_compare_string = '(' . substr( str_repeat(
',%s', count( $meta_value ) ), 1 ) . ')'; $where = $wpdb->prepare(
$meta_compare_string, $meta_value ); break; case 'BETWEEN': case 'NOT BETWEEN':
$where = $wpdb->prepare( '%s AND %s', $meta_value[0], $meta_value[1] ); break;
case 'LIKE': case 'NOT LIKE': $meta_value = '%' . $wpdb->esc_like( $meta_value )
. '%'; $where = $wpdb->prepare( '%s', $meta_value ); break; // EXISTS with a
value is interpreted as '='. case 'EXISTS': $meta_compare = '='; $where =
$wpdb->prepare( '%s', $meta_value ); break; // 'value' is ignored for NOT
EXISTS. case 'NOT EXISTS': $where = ''; break; default: $where = $wpdb->prepare(
'%s', $meta_value ); break; } if ( $where ) { if ( 'CHAR' === $meta_type ) {
$sql_chunks['where'][] = "$alias.meta_value {$meta_compare} {$where}"; } else {
$sql_chunks['where'][] = "CAST($alias.meta_value AS {$meta_type})
{$meta_compare} {$where}"; } } } /* * Multiple WHERE clauses (for meta_key and
meta_value) should * be joined in parentheses. */ if ( 1 < count(
$sql_chunks['where'] ) ) { $sql_chunks['where'] = array( '( ' . implode( ' AND
', $sql_chunks['where'] ) . ' )' ); } return $sql_chunks; } /** * Gets a
flattened list of sanitized meta clauses. * * This array should be used for
clause lookup, as when the table alias and CAST type must be determined for * a
value of 'orderby' corresponding to a meta clause. * * @since 4.2.0 * * @return
array Meta clauses. */ public function get_clauses() { return $this->clauses; }
/** * 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_Meta_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;
foreach ( $parent_query as $sibling ) { // If the sibling has no alias yet,
there's nothing to check. if ( empty( $sibling['alias'] ) ) { continue; } //
We're only interested in siblings that are first-order clauses. if ( ! is_array(
$sibling ) || ! $this->is_first_order_clause( $sibling ) ) { continue; }
$compatible_compares = array(); // Clauses connected by OR can share joins as
long as they have "positive" operators. if ( 'OR' === $parent_query['relation']
) { $compatible_compares = array( '=', 'IN', 'BETWEEN', 'LIKE', 'REGEXP',
'RLIKE', '>', '>=', '<', '<=' ); // Clauses joined by AND with "negative"
operators share a join only if they also share a key. } elseif ( isset(
$sibling['key'] ) && isset( $clause['key'] ) && $sibling['key'] ===
$clause['key'] ) { $compatible_compares = array( '!=', 'NOT IN', 'NOT LIKE' ); }
$clause_compare = strtoupper( $clause['compare'] ); $sibling_compare =
strtoupper( $sibling['compare'] ); if ( in_array( $clause_compare,
$compatible_compares, true ) && in_array( $sibling_compare,
$compatible_compares, true ) ) { $alias = preg_replace( '/\W/', '_',
$sibling['alias'] ); break; } } /** * Filters the table alias identified as
compatible with the current clause. * * @since 4.1.0 * * @param string|false
$alias Table alias, or false if none was found. * @param array $clause
First-order query clause. * @param array $parent_query Parent of $clause. *
@param WP_Meta_Query $query WP_Meta_Query object. */ return apply_filters(
'meta_query_find_compatible_table_alias', $alias, $clause, $parent_query, $this
); } /** * Checks whether the current query has any OR relations. * * In some
cases, the presence of an OR relation somewhere in the query will require * the
use of a `DISTINCT` or `GROUP BY` keyword in the `SELECT` clause. The current *
method can be used in these cases to determine whether such a clause is
necessary. * * @since 4.3.0 * * @return bool True if the query contains any `OR`
relations, otherwise false. */ public function has_or_relation() { return
$this->has_or_relation; } } /** * I18N: WP_Translation_Controller class. * *
@package WordPress * @subpackage I18N * @since 6.5.0 */ /** * Class
WP_Translation_Controller. * * @since 6.5.0 */ final class
WP_Translation_Controller { /** * Current locale. * * @since 6.5.0 * @var string
*/ protected $current_locale = 'en_US'; /** * Map of loaded translations per
locale and text domain. * * [ Locale => [ Textdomain => [ ..., ... ] ] ] * *
@since 6.5.0 * @var array> */ protected $loaded_translations = array(); /** *
List of loaded translation files. * * [ Filename => [ Locale => [ Textdomain =>
WP_Translation_File ] ] ] * * @since 6.5.0 * @var array>> */ protected
$loaded_files = array(); /** * Container for the main instance of the class. * *
@since 6.5.0 * @var WP_Translation_Controller|null */ private static $instance =
null; /** * Utility method to retrieve the main instance of the class. * * The
instance will be created if it does not exist yet. * * @since 6.5.0 * * @return
WP_Translation_Controller */ public static function get_instance():
WP_Translation_Controller { if ( null === self::$instance ) { self::$instance =
new self(); } return self::$instance; } /** * Returns the current locale. * *
@since 6.5.0 * * @return string Locale. */ public function get_locale(): string
{ return $this->current_locale; } /** * Sets the current locale. * * @since
6.5.0 * * @param string $locale Locale. */ public function set_locale( string
$locale ) { $this->current_locale = $locale; } /** * Loads a translation file
for a given text domain. * * @since 6.5.0 * * @param string $translation_file
Translation file. * @param string $textdomain Optional. Text domain. Default
'default'. * @param string $locale Optional. Locale. Default current locale. *
@return bool True on success, false otherwise. */ public function load_file(
string $translation_file, string $textdomain = 'default', string $locale = null
): bool { if ( null === $locale ) { $locale = $this->current_locale; }
$translation_file = realpath( $translation_file ); if ( false ===
$translation_file ) { return false; } if ( isset( $this->loaded_files[
$translation_file ][ $locale ][ $textdomain ] ) && false !==
$this->loaded_files[ $translation_file ][ $locale ][ $textdomain ] ) { return
null === $this->loaded_files[ $translation_file ][ $locale ][ $textdomain
]->error(); } if ( isset( $this->loaded_files[ $translation_file ][ $locale ] )
&& array() !== $this->loaded_files[ $translation_file ][ $locale ] ) { $moe =
reset( $this->loaded_files[ $translation_file ][ $locale ] ); } else { $moe =
WP_Translation_File::create( $translation_file ); if ( false === $moe || null
!== $moe->error() ) { $moe = false; } } $this->loaded_files[ $translation_file
][ $locale ][ $textdomain ] = $moe; if ( ! $moe instanceof WP_Translation_File )
{ return false; } if ( ! isset( $this->loaded_translations[ $locale ][
$textdomain ] ) ) { $this->loaded_translations[ $locale ][ $textdomain ] =
array(); } $this->loaded_translations[ $locale ][ $textdomain ][] = $moe; return
true; } /** * Unloads a translation file for a given text domain. * * @since
6.5.0 * * @param WP_Translation_File|string $file Translation file instance or
file name. * @param string $textdomain Optional. Text domain. Default 'default'.
* @param string $locale Optional. Locale. Defaults to all locales. * @return
bool True on success, false otherwise. */ public function unload_file( $file,
string $textdomain = 'default', string $locale = null ): bool { if ( is_string(
$file ) ) { $file = realpath( $file ); } if ( null !== $locale ) { if ( isset(
$this->loaded_translations[ $locale ][ $textdomain ] ) ) { foreach (
$this->loaded_translations[ $locale ][ $textdomain ] as $i => $moe ) { if (
$file === $moe || $file === $moe->get_file() ) { unset(
$this->loaded_translations[ $locale ][ $textdomain ][ $i ] ); unset(
$this->loaded_files[ $moe->get_file() ][ $locale ][ $textdomain ] ); return
true; } } } return true; } foreach ( $this->loaded_translations as $l =>
$domains ) { if ( ! isset( $domains[ $textdomain ] ) ) { continue; } foreach (
$domains[ $textdomain ] as $i => $moe ) { if ( $file === $moe || $file ===
$moe->get_file() ) { unset( $this->loaded_translations[ $l ][ $textdomain ][ $i
] ); unset( $this->loaded_files[ $moe->get_file() ][ $l ][ $textdomain ] );
return true; } } } return false; } /** * Unloads all translation files for a
given text domain. * * @since 6.5.0 * * @param string $textdomain Optional. Text
domain. Default 'default'. * @param string $locale Optional. Locale. Defaults to
all locales. * @return bool True on success, false otherwise. */ public function
unload_textdomain( string $textdomain = 'default', string $locale = null ): bool
{ $unloaded = false; if ( null !== $locale ) { if ( isset(
$this->loaded_translations[ $locale ][ $textdomain ] ) ) { $unloaded = true;
foreach ( $this->loaded_translations[ $locale ][ $textdomain ] as $moe ) {
unset( $this->loaded_files[ $moe->get_file() ][ $locale ][ $textdomain ] ); } }
unset( $this->loaded_translations[ $locale ][ $textdomain ] ); return $unloaded;
} foreach ( $this->loaded_translations as $l => $domains ) { if ( ! isset(
$domains[ $textdomain ] ) ) { continue; } $unloaded = true; foreach ( $domains[
$textdomain ] as $moe ) { unset( $this->loaded_files[ $moe->get_file() ][ $l ][
$textdomain ] ); } unset( $this->loaded_translations[ $l ][ $textdomain ] ); }
return $unloaded; } /** * Determines whether translations are loaded for a given
text domain. * * @since 6.5.0 * * @param string $textdomain Optional. Text
domain. Default 'default'. * @param string $locale Optional. Locale. Default
current locale. * @return bool True if there are any loaded translations, false
otherwise. */ public function is_textdomain_loaded( string $textdomain =
'default', string $locale = null ): bool { if ( null === $locale ) { $locale =
$this->current_locale; } return isset( $this->loaded_translations[ $locale ][
$textdomain ] ) && array() !== $this->loaded_translations[ $locale ][
$textdomain ]; } /** * Translates a singular string. * * @since 6.5.0 * * @param
string $text Text to translate. * @param string $context Optional. Context for
the string. Default empty string. * @param string $textdomain Optional. Text
domain. Default 'default'. * @param string $locale Optional. Locale. Default
current locale. * @return string|false Translation on success, false otherwise.
*/ public function translate( string $text, string $context = '', string
$textdomain = 'default', string $locale = null ) { if ( '' !== $context ) {
$context .= "\4"; } $translation = $this->locate_translation(
"{$context}{$text}", $textdomain, $locale ); if ( false === $translation ) {
return false; } return $translation['entries'][0]; } /** * Translates plurals. *
* Checks both singular+plural combinations as well as just singulars, * in case
the translation file does not store the plural. * * @since 6.5.0 * * @param
array{0: string, 1: string} $plurals { * Pair of singular and plural
translations. * * @type string $0 Singular translation. * @type string $1 Plural
translation. * } * @param int $number Number of items. * @param string $context
Optional. Context for the string. Default empty string. * @param string
$textdomain Optional. Text domain. Default 'default'. * @param string $locale
Optional. Locale. Default current locale. * @return string|false Translation on
success, false otherwise. */ public function translate_plural( array $plurals,
int $number, string $context = '', string $textdomain = 'default', string
$locale = null ) { if ( '' !== $context ) { $context .= "\4"; } $text = implode(
"\0", $plurals ); $translation = $this->locate_translation( "{$context}{$text}",
$textdomain, $locale ); if ( false === $translation ) { $text = $plurals[0];
$translation = $this->locate_translation( "{$context}{$text}", $textdomain,
$locale ); if ( false === $translation ) { return false; } } /** @var
WP_Translation_File $source */ $source = $translation['source']; $num =
$source->get_plural_form( $number ); // See \Translations::translate_plural().
return $translation['entries'][ $num ] ?? $translation['entries'][0]; } /** *
Returns all existing headers for a given text domain. * * @since 6.5.0 * *
@param string $textdomain Optional. Text domain. Default 'default'. * @return
array Headers. */ public function get_headers( string $textdomain = 'default' ):
array { if ( array() === $this->loaded_translations ) { return array(); }
$headers = array(); foreach ( $this->get_files( $textdomain ) as $moe ) {
foreach ( $moe->headers() as $header => $value ) { $headers[
$this->normalize_header( $header ) ] = $value; } } return $headers; } /** *
Normalizes header names to be capitalized. * * @since 6.5.0 * * @param string
$header Header name. * @return string Normalized header name. */ protected
function normalize_header( string $header ): string { $parts = explode( '-',
$header ); $parts = array_map( 'ucfirst', $parts ); return implode( '-', $parts
); } /** * Returns all entries for a given text domain. * * @since 6.5.0 * *
@param string $textdomain Optional. Text domain. Default 'default'. * @return
array Entries. */ public function get_entries( string $textdomain = 'default' ):
array { if ( array() === $this->loaded_translations ) { return array(); }
$entries = array(); foreach ( $this->get_files( $textdomain ) as $moe ) {
$entries = array_merge( $entries, $moe->entries() ); } return $entries; } /** *
Locates translation for a given string and text domain. * * @since 6.5.0 * *
@param string $singular Singular translation. * @param string $textdomain
Optional. Text domain. Default 'default'. * @param string $locale Optional.
Locale. Default current locale. * @return array{source: WP_Translation_File,
entries: string[]}|false { * Translations on success, false otherwise. * * @type
WP_Translation_File $source Translation file instance. * @type string[] $entries
Array of translation entries. * } */ protected function locate_translation(
string $singular, string $textdomain = 'default', string $locale = null ) { if (
array() === $this->loaded_translations ) { return false; } // Find the
translation in all loaded files for this text domain. foreach (
$this->get_files( $textdomain, $locale ) as $moe ) { $translation =
$moe->translate( $singular ); if ( false !== $translation ) { return array(
'entries' => explode( "\0", $translation ), 'source' => $moe, ); } if ( null !==
$moe->error() ) { // Unload this file, something is wrong. $this->unload_file(
$moe, $textdomain, $locale ); } } // Nothing could be found. return false; } /**
* Returns all translation files for a given text domain. * * @since 6.5.0 * *
@param string $textdomain Optional. Text domain. Default 'default'. * @param
string $locale Optional. Locale. Default current locale. * @return
WP_Translation_File[] List of translation files. */ protected function
get_files( string $textdomain = 'default', string $locale = null ): array { if (
null === $locale ) { $locale = $this->current_locale; } return
$this->loaded_translations[ $locale ][ $textdomain ] ?? array(); } } /** * Sets
up the default filters and actions for most * of the WordPress hooks. * * If you
need to remove a default hook, this file will * give you the priority to use for
removing the hook. * * Not all of the default hooks are found in this file. *
For instance, administration-related hooks are located in *
wp-admin/includes/admin-filters.php. * * If a hook should only be called from a
specific context * (admin area, multisite environment…), please move it * to a
more appropriate file instead. * * @package WordPress */ // Strip, trim, kses,
special chars for string saves. foreach ( array( 'pre_term_name',
'pre_comment_author_name', 'pre_link_name', 'pre_link_target', 'pre_link_rel',
'pre_user_display_name', 'pre_user_first_name', 'pre_user_last_name',
'pre_user_nickname' ) as $filter ) { add_filter( $filter, 'sanitize_text_field'
); add_filter( $filter, 'wp_filter_kses' ); add_filter( $filter,
'_wp_specialchars', 30 ); } // Strip, kses, special chars for string display.
foreach ( array( 'term_name', 'comment_author_name', 'link_name', 'link_target',
'link_rel', 'user_display_name', 'user_first_name', 'user_last_name',
'user_nickname' ) as $filter ) { if ( is_admin() ) { // These are expensive. Run
only on admin pages for defense in depth. add_filter( $filter,
'sanitize_text_field' ); add_filter( $filter, 'wp_kses_data' ); } add_filter(
$filter, '_wp_specialchars', 30 ); } // Kses only for textarea saves. foreach (
array( 'pre_term_description', 'pre_link_description', 'pre_link_notes',
'pre_user_description' ) as $filter ) { add_filter( $filter, 'wp_filter_kses' );
} // Kses only for textarea admin displays. if ( is_admin() ) { foreach ( array(
'term_description', 'link_description', 'link_notes', 'user_description' ) as
$filter ) { add_filter( $filter, 'wp_kses_data' ); } add_filter( 'comment_text',
'wp_kses_post' ); } // Email saves. foreach ( array( 'pre_comment_author_email',
'pre_user_email' ) as $filter ) { add_filter( $filter, 'trim' ); add_filter(
$filter, 'sanitize_email' ); add_filter( $filter, 'wp_filter_kses' ); } // Email
admin display. foreach ( array( 'comment_author_email', 'user_email' ) as
$filter ) { add_filter( $filter, 'sanitize_email' ); if ( is_admin() ) {
add_filter( $filter, 'wp_kses_data' ); } } // Save URL. foreach ( array(
'pre_comment_author_url', 'pre_user_url', 'pre_link_url', 'pre_link_image',
'pre_link_rss', 'pre_post_guid', ) as $filter ) { add_filter( $filter,
'wp_strip_all_tags' ); add_filter( $filter, 'sanitize_url' ); add_filter(
$filter, 'wp_filter_kses' ); } // Display URL. foreach ( array( 'user_url',
'link_url', 'link_image', 'link_rss', 'comment_url', 'post_guid' ) as $filter )
{ if ( is_admin() ) { add_filter( $filter, 'wp_strip_all_tags' ); } add_filter(
$filter, 'esc_url' ); if ( is_admin() ) { add_filter( $filter, 'wp_kses_data' );
} } // Slugs. add_filter( 'pre_term_slug', 'sanitize_title' ); add_filter(
'wp_insert_post_data', '_wp_customize_changeset_filter_insert_post_data', 10, 2
); // Keys. foreach ( array( 'pre_post_type', 'pre_post_status',
'pre_post_comment_status', 'pre_post_ping_status' ) as $filter ) { add_filter(
$filter, 'sanitize_key' ); } // Mime types. add_filter( 'pre_post_mime_type',
'sanitize_mime_type' ); add_filter( 'post_mime_type', 'sanitize_mime_type' ); //
Meta. add_filter( 'register_meta_args', '_wp_register_meta_args_allowed_list',
10, 2 ); // Counts. add_action( 'admin_init', 'wp_schedule_update_user_counts'
); add_action( 'wp_update_user_counts', 'wp_schedule_update_user_counts', 10, 0
); foreach ( array( 'user_register', 'deleted_user' ) as $action ) { add_action(
$action, 'wp_maybe_update_user_counts', 10, 0 ); } // Post meta. add_action(
'added_post_meta', 'wp_cache_set_posts_last_changed' ); add_action(
'updated_post_meta', 'wp_cache_set_posts_last_changed' ); add_action(
'deleted_post_meta', 'wp_cache_set_posts_last_changed' ); // User meta.
add_action( 'added_user_meta', 'wp_cache_set_users_last_changed' ); add_action(
'updated_user_meta', 'wp_cache_set_users_last_changed' ); add_action(
'deleted_user_meta', 'wp_cache_set_users_last_changed' ); add_action(
'add_user_role', 'wp_cache_set_users_last_changed' ); add_action(
'set_user_role', 'wp_cache_set_users_last_changed' ); add_action(
'remove_user_role', 'wp_cache_set_users_last_changed' ); // Term meta.
add_action( 'added_term_meta', 'wp_cache_set_terms_last_changed' ); add_action(
'updated_term_meta', 'wp_cache_set_terms_last_changed' ); add_action(
'deleted_term_meta', 'wp_cache_set_terms_last_changed' ); add_filter(
'get_term_metadata', 'wp_check_term_meta_support_prefilter' ); add_filter(
'add_term_metadata', 'wp_check_term_meta_support_prefilter' ); add_filter(
'update_term_metadata', 'wp_check_term_meta_support_prefilter' ); add_filter(
'delete_term_metadata', 'wp_check_term_meta_support_prefilter' ); add_filter(
'get_term_metadata_by_mid', 'wp_check_term_meta_support_prefilter' );
add_filter( 'update_term_metadata_by_mid',
'wp_check_term_meta_support_prefilter' ); add_filter(
'delete_term_metadata_by_mid', 'wp_check_term_meta_support_prefilter' );
add_filter( 'update_term_metadata_cache', 'wp_check_term_meta_support_prefilter'
); // Comment meta. add_action( 'added_comment_meta',
'wp_cache_set_comments_last_changed' ); add_action( 'updated_comment_meta',
'wp_cache_set_comments_last_changed' ); add_action( 'deleted_comment_meta',
'wp_cache_set_comments_last_changed' ); // Places to balance tags on input.
foreach ( array( 'content_save_pre', 'excerpt_save_pre', 'comment_save_pre',
'pre_comment_content' ) as $filter ) { add_filter( $filter,
'convert_invalid_entities' ); add_filter( $filter, 'balanceTags', 50 ); } // Add
proper rel values for links with target. add_action( 'init',
'wp_init_targeted_link_rel_filters' ); // Format strings for display. foreach (
array( 'comment_author', 'term_name', 'link_name', 'link_description',
'link_notes', 'bloginfo', 'wp_title', 'document_title', 'widget_title' ) as
$filter ) { add_filter( $filter, 'wptexturize' ); add_filter( $filter,
'convert_chars' ); add_filter( $filter, 'esc_html' ); } // Format WordPress.
foreach ( array( 'the_content', 'the_title', 'wp_title', 'document_title' ) as
$filter ) { add_filter( $filter, 'capital_P_dangit', 11 ); } add_filter(
'comment_text', 'capital_P_dangit', 31 ); // Format titles. foreach ( array(
'single_post_title', 'single_cat_title', 'single_tag_title',
'single_month_title', 'nav_menu_attr_title', 'nav_menu_description' ) as $filter
) { add_filter( $filter, 'wptexturize' ); add_filter( $filter, 'strip_tags' ); }
// Format text area for display. foreach ( array( 'term_description',
'get_the_post_type_description' ) as $filter ) { add_filter( $filter,
'wptexturize' ); add_filter( $filter, 'convert_chars' ); add_filter( $filter,
'wpautop' ); add_filter( $filter, 'shortcode_unautop' ); } // Format for RSS.
add_filter( 'term_name_rss', 'convert_chars' ); // Pre save hierarchy.
add_filter( 'wp_insert_post_parent', 'wp_check_post_hierarchy_for_loops', 10, 2
); add_filter( 'wp_update_term_parent', 'wp_check_term_hierarchy_for_loops', 10,
3 ); // Display filters. add_filter( 'the_title', 'wptexturize' ); add_filter(
'the_title', 'convert_chars' ); add_filter( 'the_title', 'trim' ); add_filter(
'the_content', 'do_blocks', 9 ); add_filter( 'the_content', 'wptexturize' );
add_filter( 'the_content', 'convert_smilies', 20 ); add_filter( 'the_content',
'wpautop' ); add_filter( 'the_content', 'shortcode_unautop' ); add_filter(
'the_content', 'prepend_attachment' ); add_filter( 'the_content',
'wp_replace_insecure_home_url' ); add_filter( 'the_content', 'do_shortcode', 11
); // AFTER wpautop(). add_filter( 'the_content', 'wp_filter_content_tags', 12
); // Runs after do_shortcode(). add_filter( 'the_excerpt', 'wptexturize' );
add_filter( 'the_excerpt', 'convert_smilies' ); add_filter( 'the_excerpt',
'convert_chars' ); add_filter( 'the_excerpt', 'wpautop' ); add_filter(
'the_excerpt', 'shortcode_unautop' ); add_filter( 'the_excerpt',
'wp_replace_insecure_home_url' ); add_filter( 'the_excerpt',
'wp_filter_content_tags', 12 ); add_filter( 'get_the_excerpt',
'wp_trim_excerpt', 10, 2 ); add_filter( 'the_post_thumbnail_caption',
'wptexturize' ); add_filter( 'the_post_thumbnail_caption', 'convert_smilies' );
add_filter( 'the_post_thumbnail_caption', 'convert_chars' ); add_filter(
'comment_text', 'wptexturize' ); add_filter( 'comment_text', 'convert_chars' );
add_filter( 'comment_text', 'make_clickable', 9 ); add_filter( 'comment_text',
'force_balance_tags', 25 ); add_filter( 'comment_text', 'convert_smilies', 20 );
add_filter( 'comment_text', 'wpautop', 30 ); add_filter( 'comment_excerpt',
'convert_chars' ); add_filter( 'list_cats', 'wptexturize' ); add_filter(
'wp_sprintf', 'wp_sprintf_l', 10, 2 ); add_filter( 'widget_text', 'balanceTags'
); add_filter( 'widget_text_content', 'capital_P_dangit', 11 ); add_filter(
'widget_text_content', 'wptexturize' ); add_filter( 'widget_text_content',
'convert_smilies', 20 ); add_filter( 'widget_text_content', 'wpautop' );
add_filter( 'widget_text_content', 'shortcode_unautop' ); add_filter(
'widget_text_content', 'wp_replace_insecure_home_url' ); add_filter(
'widget_text_content', 'do_shortcode', 11 ); // Runs after wpautop(); note that
$post global will be null when shortcodes run. add_filter(
'widget_text_content', 'wp_filter_content_tags', 12 ); // Runs after
do_shortcode(). add_filter( 'widget_block_content', 'do_blocks', 9 );
add_filter( 'widget_block_content', 'do_shortcode', 11 ); add_filter(
'widget_block_content', 'wp_filter_content_tags', 12 ); // Runs after
do_shortcode(). add_filter( 'block_type_metadata',
'wp_migrate_old_typography_shape' ); add_filter( 'wp_get_custom_css',
'wp_replace_insecure_home_url' ); // RSS filters. add_filter( 'the_title_rss',
'strip_tags' ); add_filter( 'the_title_rss', 'ent2ncr', 8 ); add_filter(
'the_title_rss', 'esc_html' ); add_filter( 'the_content_rss', 'ent2ncr', 8 );
add_filter( 'the_content_feed', 'wp_staticize_emoji' ); add_filter(
'the_content_feed', '_oembed_filter_feed_content' ); add_filter(
'the_excerpt_rss', 'convert_chars' ); add_filter( 'the_excerpt_rss', 'ent2ncr',
8 ); add_filter( 'comment_author_rss', 'ent2ncr', 8 ); add_filter(
'comment_text_rss', 'ent2ncr', 8 ); add_filter( 'comment_text_rss', 'esc_html'
); add_filter( 'comment_text_rss', 'wp_staticize_emoji' ); add_filter(
'bloginfo_rss', 'ent2ncr', 8 ); add_filter( 'the_author', 'ent2ncr', 8 );
add_filter( 'the_guid', 'esc_url' ); // Email filters. add_filter( 'wp_mail',
'wp_staticize_emoji_for_email' ); // Robots filters. add_filter( 'wp_robots',
'wp_robots_noindex' ); add_filter( 'wp_robots', 'wp_robots_noindex_embeds' );
add_filter( 'wp_robots', 'wp_robots_noindex_search' ); add_filter( 'wp_robots',
'wp_robots_max_image_preview_large' ); // Mark site as no longer fresh. foreach
( array( 'publish_post', 'publish_page', 'wp_ajax_save-widget',
'wp_ajax_widgets-order', 'customize_save_after', 'rest_after_save_widget',
'rest_delete_widget', 'rest_save_sidebar', ) as $action ) { add_action( $action,
'_delete_option_fresh_site', 0 ); } // Misc filters. add_filter(
'option_ping_sites', 'privacy_ping_filter' ); add_filter( 'option_blog_charset',
'_wp_specialchars' ); // IMPORTANT: This must not be wp_specialchars() or
esc_html() or it'll cause an infinite loop. add_filter( 'option_blog_charset',
'_canonical_charset' ); add_filter( 'option_home', '_config_wp_home' );
add_filter( 'option_siteurl', '_config_wp_siteurl' ); add_filter(
'tiny_mce_before_init', '_mce_set_direction' ); add_filter(
'teeny_mce_before_init', '_mce_set_direction' ); add_filter( 'pre_kses',
'wp_pre_kses_less_than' ); add_filter( 'pre_kses',
'wp_pre_kses_block_attributes', 10, 3 ); add_filter( 'sanitize_title',
'sanitize_title_with_dashes', 10, 3 ); add_action( 'check_comment_flood',
'check_comment_flood_db', 10, 4 ); add_filter( 'comment_flood_filter',
'wp_throttle_comment_flood', 10, 3 ); add_filter( 'pre_comment_content',
'wp_rel_ugc', 15 ); add_filter( 'comment_email', 'antispambot' ); add_filter(
'option_tag_base', '_wp_filter_taxonomy_base' ); add_filter(
'option_category_base', '_wp_filter_taxonomy_base' ); add_filter( 'the_posts',
'_close_comments_for_old_posts', 10, 2 ); add_filter( 'comments_open',
'_close_comments_for_old_post', 10, 2 ); add_filter( 'pings_open',
'_close_comments_for_old_post', 10, 2 ); add_filter( 'editable_slug',
'urldecode' ); add_filter( 'editable_slug', 'esc_textarea' ); add_filter(
'pingback_ping_source_uri', 'pingback_ping_source_uri' ); add_filter(
'xmlrpc_pingback_error', 'xmlrpc_pingback_error' ); add_filter(
'title_save_pre', 'trim' ); add_action( 'transition_comment_status',
'_clear_modified_cache_on_transition_comment_status', 10, 2 ); add_filter(
'http_request_host_is_external', 'allowed_http_request_hosts', 10, 2 ); // REST
API filters. add_action( 'xmlrpc_rsd_apis', 'rest_output_rsd' ); add_action(
'wp_head', 'rest_output_link_wp_head', 10, 0 ); add_action( 'template_redirect',
'rest_output_link_header', 11, 0 ); add_action( 'auth_cookie_malformed',
'rest_cookie_collect_status' ); add_action( 'auth_cookie_expired',
'rest_cookie_collect_status' ); add_action( 'auth_cookie_bad_username',
'rest_cookie_collect_status' ); add_action( 'auth_cookie_bad_hash',
'rest_cookie_collect_status' ); add_action( 'auth_cookie_valid',
'rest_cookie_collect_status' ); add_action(
'application_password_failed_authentication',
'rest_application_password_collect_status' ); add_action(
'application_password_did_authenticate',
'rest_application_password_collect_status', 10, 2 ); add_filter(
'rest_authentication_errors', 'rest_application_password_check_errors', 90 );
add_filter( 'rest_authentication_errors', 'rest_cookie_check_errors', 100 ); //
Actions. add_action( 'wp_head', '_wp_render_title_tag', 1 ); add_action(
'wp_head', 'wp_enqueue_scripts', 1 ); add_action( 'wp_head',
'wp_resource_hints', 2 ); add_action( 'wp_head', 'wp_preload_resources', 1 );
add_action( 'wp_head', 'feed_links', 2 ); add_action( 'wp_head',
'feed_links_extra', 3 ); add_action( 'wp_head', 'rsd_link' ); add_action(
'wp_head', 'locale_stylesheet' ); add_action( 'publish_future_post',
'check_and_publish_future_post', 10, 1 ); add_action( 'wp_head', 'wp_robots', 1
); add_action( 'wp_head', 'print_emoji_detection_script', 7 ); add_action(
'wp_head', 'wp_print_styles', 8 ); add_action( 'wp_head',
'wp_print_head_scripts', 9 ); add_action( 'wp_head', 'wp_generator' );
add_action( 'wp_head', 'rel_canonical' ); add_action( 'wp_head',
'wp_shortlink_wp_head', 10, 0 ); add_action( 'wp_head', 'wp_custom_css_cb', 101
); add_action( 'wp_head', 'wp_site_icon', 99 ); add_action( 'wp_footer',
'wp_print_footer_scripts', 20 ); add_action( 'template_redirect',
'wp_shortlink_header', 11, 0 ); add_action( 'wp_print_footer_scripts',
'_wp_footer_scripts' ); add_action( 'init',
'_register_core_block_patterns_and_categories' ); add_action( 'init',
'check_theme_switched', 99 ); add_action( 'init', array( 'WP_Block_Supports',
'init' ), 22 ); add_action( 'switch_theme', 'wp_clean_theme_json_cache' );
add_action( 'start_previewing_theme', 'wp_clean_theme_json_cache' ); add_action(
'after_switch_theme', '_wp_menus_changed' ); add_action( 'after_switch_theme',
'_wp_sidebars_changed' ); add_action( 'wp_enqueue_scripts',
'wp_enqueue_emoji_styles' ); add_action( 'wp_print_styles', 'print_emoji_styles'
); // Retained for backwards-compatibility. Unhooked by
wp_enqueue_emoji_styles(). if ( isset( $_GET['replytocom'] ) ) { add_filter(
'wp_robots', 'wp_robots_no_robots' ); } // Login actions. add_action(
'login_head', 'wp_robots', 1 ); add_filter( 'login_head', 'wp_resource_hints', 8
); add_action( 'login_head', 'wp_print_head_scripts', 9 ); add_action(
'login_head', 'print_admin_styles', 9 ); add_action( 'login_head',
'wp_site_icon', 99 ); add_action( 'login_footer', 'wp_print_footer_scripts', 20
); add_action( 'login_init', 'send_frame_options_header', 10, 0 ); // Feed
generator tags. foreach ( array( 'rss2_head', 'commentsrss2_head', 'rss_head',
'rdf_header', 'atom_head', 'comments_atom_head', 'opml_head', 'app_head' ) as
$action ) { add_action( $action, 'the_generator' ); } // Feed Site Icon.
add_action( 'atom_head', 'atom_site_icon' ); add_action( 'rss2_head',
'rss2_site_icon' ); // WP Cron. if ( ! defined( 'DOING_CRON' ) ) { add_action(
'init', 'wp_cron' ); } // HTTPS migration. add_action( 'update_option_home',
'wp_update_https_migration_required', 10, 2 ); // 2 Actions 2 Furious.
add_action( 'do_feed_rdf', 'do_feed_rdf', 10, 0 ); add_action( 'do_feed_rss',
'do_feed_rss', 10, 0 ); add_action( 'do_feed_rss2', 'do_feed_rss2', 10, 1 );
add_action( 'do_feed_atom', 'do_feed_atom', 10, 1 ); add_action( 'do_pings',
'do_all_pings', 10, 0 ); add_action( 'do_all_pings', 'do_all_pingbacks', 10, 0
); add_action( 'do_all_pings', 'do_all_enclosures', 10, 0 ); add_action(
'do_all_pings', 'do_all_trackbacks', 10, 0 ); add_action( 'do_all_pings',
'generic_ping', 10, 0 ); add_action( 'do_robots', 'do_robots' ); add_action(
'do_favicon', 'do_favicon' ); add_action( 'set_comment_cookies',
'wp_set_comment_cookies', 10, 3 ); add_action( 'sanitize_comment_cookies',
'sanitize_comment_cookies' ); add_action( 'init', 'smilies_init', 5 );
add_action( 'plugins_loaded', 'wp_maybe_load_widgets', 0 ); add_action(
'plugins_loaded', 'wp_maybe_load_embeds', 0 ); add_action( 'shutdown',
'wp_ob_end_flush_all', 1 ); // Create a revision whenever a post is updated.
add_action( 'wp_after_insert_post', 'wp_save_post_revision_on_insert', 9, 3 );
add_action( 'post_updated', 'wp_save_post_revision', 10, 1 ); add_action(
'publish_post', '_publish_post_hook', 5, 1 ); add_action(
'transition_post_status', '_transition_post_status', 5, 3 ); add_action(
'transition_post_status', '_update_term_count_on_transition_post_status', 10, 3
); add_action( 'comment_form', 'wp_comment_form_unfiltered_html_nonce' ); //
Privacy. add_action( 'user_request_action_confirmed',
'_wp_privacy_account_request_confirmed' ); add_action(
'user_request_action_confirmed',
'_wp_privacy_send_request_confirmation_notification', 12 ); // After request
marked as completed. add_filter( 'wp_privacy_personal_data_exporters',
'wp_register_comment_personal_data_exporter' ); add_filter(
'wp_privacy_personal_data_exporters', 'wp_register_media_personal_data_exporter'
); add_filter( 'wp_privacy_personal_data_exporters',
'wp_register_user_personal_data_exporter', 1 ); add_filter(
'wp_privacy_personal_data_erasers', 'wp_register_comment_personal_data_eraser'
); add_action( 'init', 'wp_schedule_delete_old_privacy_export_files' );
add_action( 'wp_privacy_delete_old_export_files',
'wp_privacy_delete_old_export_files' ); // Cron tasks. add_action(
'wp_scheduled_delete', 'wp_scheduled_delete' ); add_action(
'wp_scheduled_auto_draft_delete', 'wp_delete_auto_drafts' ); add_action(
'importer_scheduled_cleanup', 'wp_delete_attachment' ); add_action(
'upgrader_scheduled_cleanup', 'wp_delete_attachment' ); add_action(
'delete_expired_transients', 'delete_expired_transients' ); // Navigation menu
actions. add_action( 'delete_post', '_wp_delete_post_menu_item' ); add_action(
'delete_term', '_wp_delete_tax_menu_item', 10, 3 ); add_action(
'transition_post_status', '_wp_auto_add_pages_to_menu', 10, 3 ); add_action(
'delete_post', '_wp_delete_customize_changeset_dependent_auto_drafts' ); // Post
Thumbnail specific image filtering. add_action(
'begin_fetch_post_thumbnail_html', '_wp_post_thumbnail_class_filter_add' );
add_action( 'end_fetch_post_thumbnail_html',
'_wp_post_thumbnail_class_filter_remove' ); add_action(
'begin_fetch_post_thumbnail_html', '_wp_post_thumbnail_context_filter_add' );
add_action( 'end_fetch_post_thumbnail_html',
'_wp_post_thumbnail_context_filter_remove' ); // Redirect old slugs. add_action(
'template_redirect', 'wp_old_slug_redirect' ); add_action( 'post_updated',
'wp_check_for_changed_slugs', 12, 3 ); add_action( 'attachment_updated',
'wp_check_for_changed_slugs', 12, 3 ); // Redirect old dates. add_action(
'post_updated', 'wp_check_for_changed_dates', 12, 3 ); add_action(
'attachment_updated', 'wp_check_for_changed_dates', 12, 3 ); // Nonce check for
post previews. add_action( 'init', '_show_post_preview' ); // Output JS to reset
window.name for previews. add_action( 'wp_head', 'wp_post_preview_js', 1 ); //
Timezone. add_filter( 'pre_option_gmt_offset', 'wp_timezone_override_offset' );
// If the upgrade hasn't run yet, assume link manager is used. add_filter(
'default_option_link_manager_enabled', '__return_true' ); // This option no
longer exists; tell plugins we always support auto-embedding. add_filter(
'pre_option_embed_autourls', '__return_true' ); // Default settings for
heartbeat. add_filter( 'heartbeat_settings', 'wp_heartbeat_settings' ); // Check
if the user is logged out. add_action( 'admin_enqueue_scripts',
'wp_auth_check_load' ); add_filter( 'heartbeat_send', 'wp_auth_check' );
add_filter( 'heartbeat_nopriv_send', 'wp_auth_check' ); // Default
authentication filters. add_filter( 'authenticate',
'wp_authenticate_username_password', 20, 3 ); add_filter( 'authenticate',
'wp_authenticate_email_password', 20, 3 ); add_filter( 'authenticate',
'wp_authenticate_application_password', 20, 3 ); add_filter( 'authenticate',
'wp_authenticate_spam_check', 99 ); add_filter( 'determine_current_user',
'wp_validate_auth_cookie' ); add_filter( 'determine_current_user',
'wp_validate_logged_in_cookie', 20 ); add_filter( 'determine_current_user',
'wp_validate_application_password', 20 ); // Split term updates. add_action(
'admin_init', '_wp_check_for_scheduled_split_terms' ); add_action(
'split_shared_term', '_wp_check_split_default_terms', 10, 4 ); add_action(
'split_shared_term', '_wp_check_split_terms_in_menus', 10, 4 ); add_action(
'split_shared_term', '_wp_check_split_nav_menu_terms', 10, 4 ); add_action(
'wp_split_shared_term_batch', '_wp_batch_split_terms' ); // Comment type
updates. add_action( 'admin_init', '_wp_check_for_scheduled_update_comment_type'
); add_action( 'wp_update_comment_type_batch', '_wp_batch_update_comment_type'
); // Email notifications. add_action( 'comment_post',
'wp_new_comment_notify_moderator' ); add_action( 'comment_post',
'wp_new_comment_notify_postauthor' ); add_action( 'after_password_reset',
'wp_password_change_notification' ); add_action( 'register_new_user',
'wp_send_new_user_notifications' ); add_action( 'edit_user_created_user',
'wp_send_new_user_notifications', 10, 2 ); // REST API actions. add_action(
'init', 'rest_api_init' ); add_action( 'rest_api_init',
'rest_api_default_filters', 10, 1 ); add_action( 'rest_api_init',
'register_initial_settings', 10 ); add_action( 'rest_api_init',
'create_initial_rest_routes', 99 ); add_action( 'parse_request',
'rest_api_loaded' ); // Sitemaps actions. add_action( 'init',
'wp_sitemaps_get_server' ); /** * Filters formerly mixed into wp-includes. */ //
Theme. add_action( 'setup_theme', 'create_initial_theme_features', 0 );
add_action( 'after_setup_theme', '_add_default_theme_supports', 1 ); add_action(
'wp_loaded', '_custom_header_background_just_in_time' ); add_action( 'wp_head',
'_custom_logo_header_styles' ); add_action( 'plugins_loaded',
'_wp_customize_include' ); add_action( 'transition_post_status',
'_wp_customize_publish_changeset', 10, 3 ); add_action( 'admin_enqueue_scripts',
'_wp_customize_loader_settings' ); add_action( 'delete_attachment',
'_delete_attachment_theme_mod' ); add_action( 'transition_post_status',
'_wp_keep_alive_customize_changeset_dependent_auto_drafts', 20, 3 ); // Block
Theme Previews. add_action( 'plugins_loaded',
'wp_initialize_theme_preview_hooks', 1 ); // Calendar widget cache. add_action(
'save_post', 'delete_get_calendar_cache' ); add_action( 'delete_post',
'delete_get_calendar_cache' ); add_action( 'update_option_start_of_week',
'delete_get_calendar_cache' ); add_action( 'update_option_gmt_offset',
'delete_get_calendar_cache' ); // Author. add_action( 'transition_post_status',
'__clear_multi_author_cache' ); // Post. add_action( 'init',
'create_initial_post_types', 0 ); // Highest priority. add_action( 'admin_menu',
'_add_post_type_submenus' ); add_action( 'before_delete_post',
'_reset_front_page_settings_for_post' ); add_action( 'wp_trash_post',
'_reset_front_page_settings_for_post' ); add_action( 'change_locale',
'create_initial_post_types' ); // Post Formats. add_filter( 'request',
'_post_format_request' ); add_filter( 'term_link', '_post_format_link', 10, 3 );
add_filter( 'get_post_format', '_post_format_get_term' ); add_filter(
'get_terms', '_post_format_get_terms', 10, 3 ); add_filter(
'wp_get_object_terms', '_post_format_wp_get_object_terms' ); // KSES.
add_action( 'init', 'kses_init' ); add_action( 'set_current_user', 'kses_init'
); // Script Loader. add_action( 'wp_default_scripts', 'wp_default_scripts' );
add_action( 'wp_default_scripts', 'wp_default_packages' ); add_action(
'wp_enqueue_scripts', 'wp_localize_jquery_ui_datepicker', 1000 ); add_action(
'wp_enqueue_scripts', 'wp_common_block_scripts_and_styles' ); add_action(
'wp_enqueue_scripts', 'wp_enqueue_classic_theme_styles' ); add_action(
'admin_enqueue_scripts', 'wp_localize_jquery_ui_datepicker', 1000 ); add_action(
'admin_enqueue_scripts', 'wp_common_block_scripts_and_styles' ); add_action(
'enqueue_block_assets', 'wp_enqueue_registered_block_scripts_and_styles' );
add_action( 'enqueue_block_assets', 'enqueue_block_styles_assets', 30 ); /* *
`wp_enqueue_registered_block_scripts_and_styles` is bound to both *
`enqueue_block_editor_assets` and `enqueue_block_assets` hooks * since the
introduction of the block editor in WordPress 5.0. * * The way this works is
that the block assets are loaded before any other assets. * For example, this is
the order of styles for the editor: * * - front styles registered for blocks,
via `styles` handle (block.json) * - editor styles registered for blocks, via
`editorStyles` handle (block.json) * - editor styles enqueued via
`enqueue_block_editor_assets` hook * - front styles enqueued via
`enqueue_block_assets` hook */ add_action( 'enqueue_block_editor_assets',
'wp_enqueue_registered_block_scripts_and_styles' ); add_action(
'enqueue_block_editor_assets', 'enqueue_editor_block_styles_assets' );
add_action( 'enqueue_block_editor_assets',
'wp_enqueue_editor_block_directory_assets' ); add_action(
'enqueue_block_editor_assets', 'wp_enqueue_editor_format_library_assets' );
add_action( 'enqueue_block_editor_assets',
'wp_enqueue_global_styles_css_custom_properties' ); add_action(
'wp_print_scripts', 'wp_just_in_time_script_localization' ); add_filter(
'print_scripts_array', 'wp_prototype_before_jquery' ); add_action(
'customize_controls_print_styles', 'wp_resource_hints', 1 ); add_action(
'admin_head', 'wp_check_widget_editor_deps' ); add_filter(
'block_editor_settings_all', 'wp_add_editor_classic_theme_styles' ); // Global
styles can be enqueued in both the header and the footer. See
https://core.trac.wordpress.org/ticket/53494. add_action( 'wp_enqueue_scripts',
'wp_enqueue_global_styles' ); add_action( 'wp_footer',
'wp_enqueue_global_styles', 1 ); // Global styles custom CSS. add_action(
'wp_enqueue_scripts', 'wp_enqueue_global_styles_custom_css' ); // Block
supports, and other styles parsed and stored in the Style Engine. add_action(
'wp_enqueue_scripts', 'wp_enqueue_stored_styles' ); add_action( 'wp_footer',
'wp_enqueue_stored_styles', 1 ); add_action( 'wp_default_styles',
'wp_default_styles' ); add_filter( 'style_loader_src', 'wp_style_loader_src',
10, 2 ); add_action( 'wp_head', 'wp_maybe_inline_styles', 1 ); // Run for styles
enqueued in . add_action( 'wp_footer', 'wp_maybe_inline_styles', 1 ); // Run for
late-loaded styles in the footer. /* * Block specific actions and filters. */ //
Footnotes Block. add_action( 'init', '_wp_footnotes_kses_init' ); add_action(
'set_current_user', '_wp_footnotes_kses_init' ); add_filter(
'force_filtered_html_on_import',
'_wp_footnotes_force_filtered_html_on_import_filter', 999 ); /* * Disable "Post
Attributes" for wp_navigation post type. The attributes are * also conditionally
enabled when a site has custom templates. Block Theme * templates can be
available for every post type. */ add_filter( 'theme_wp_navigation_templates',
'__return_empty_array' ); // Taxonomy. add_action( 'init',
'create_initial_taxonomies', 0 ); // Highest priority. add_action(
'change_locale', 'create_initial_taxonomies' ); // Canonical. add_action(
'template_redirect', 'redirect_canonical' ); add_action( 'template_redirect',
'wp_redirect_admin_locations', 1000 ); // Media. add_action(
'wp_playlist_scripts', 'wp_playlist_scripts' ); add_action(
'customize_controls_enqueue_scripts', 'wp_plupload_default_settings' );
add_action( 'plugins_loaded', '_wp_add_additional_image_sizes', 0 ); add_filter(
'plupload_default_settings', 'wp_show_heic_upload_error' ); // Nav menu.
add_filter( 'nav_menu_item_id', '_nav_menu_item_id_use_once', 10, 2 );
add_filter( 'nav_menu_css_class',
'wp_nav_menu_remove_menu_item_has_children_class', 10, 4 ); // Widgets.
add_action( 'after_setup_theme', 'wp_setup_widgets_block_editor', 1 );
add_action( 'init', 'wp_widgets_init', 1 ); add_action( 'change_locale', array(
'WP_Widget_Media', 'reset_default_labels' ) ); add_action( 'widgets_init',
'_wp_block_theme_register_classic_sidebars', 1 ); // Admin Bar. // Don't remove.
Wrong way to disable. add_action( 'template_redirect', '_wp_admin_bar_init', 0
); add_action( 'admin_init', '_wp_admin_bar_init' ); add_action(
'wp_enqueue_scripts', 'wp_enqueue_admin_bar_bump_styles' ); add_action(
'wp_enqueue_scripts', 'wp_enqueue_admin_bar_header_styles' ); add_action(
'admin_enqueue_scripts', 'wp_enqueue_admin_bar_header_styles' ); add_action(
'before_signup_header', '_wp_admin_bar_init' ); add_action( 'activate_header',
'_wp_admin_bar_init' ); add_action( 'wp_body_open', 'wp_admin_bar_render', 0 );
add_action( 'wp_footer', 'wp_admin_bar_render', 1000 ); // Back-compat for
themes not using `wp_body_open`. add_action( 'in_admin_header',
'wp_admin_bar_render', 0 ); // Former admin filters that can also be hooked on
the front end. add_action( 'media_buttons', 'media_buttons' ); add_filter(
'image_send_to_editor', 'image_add_caption', 20, 8 ); add_filter(
'media_send_to_editor', 'image_media_send_to_editor', 10, 3 ); // Embeds.
add_action( 'rest_api_init', 'wp_oembed_register_route' ); add_filter(
'rest_pre_serve_request', '_oembed_rest_pre_serve_request', 10, 4 ); add_action(
'wp_head', 'wp_oembed_add_discovery_links' ); add_action( 'wp_head',
'wp_oembed_add_host_js' ); // Back-compat for sites disabling oEmbed host JS by
removing action. add_filter( 'embed_oembed_html',
'wp_maybe_enqueue_oembed_host_js' ); add_action( 'embed_head',
'enqueue_embed_scripts', 1 ); add_action( 'embed_head',
'print_emoji_detection_script' ); add_action( 'embed_head',
'wp_enqueue_embed_styles', 9 ); add_action( 'embed_head', 'print_embed_styles'
); // Retained for backwards-compatibility. Unhooked by
wp_enqueue_embed_styles(). add_action( 'embed_head', 'wp_print_head_scripts', 20
); add_action( 'embed_head', 'wp_print_styles', 20 ); add_action( 'embed_head',
'wp_robots' ); add_action( 'embed_head', 'rel_canonical' ); add_action(
'embed_head', 'locale_stylesheet', 30 ); add_action( 'enqueue_embed_scripts',
'wp_enqueue_emoji_styles' ); add_action( 'embed_content_meta',
'print_embed_comments_button' ); add_action( 'embed_content_meta',
'print_embed_sharing_button' ); add_action( 'embed_footer',
'print_embed_sharing_dialog' ); add_action( 'embed_footer',
'print_embed_scripts' ); add_action( 'embed_footer', 'wp_print_footer_scripts',
20 ); add_filter( 'excerpt_more', 'wp_embed_excerpt_more', 20 ); add_filter(
'the_excerpt_embed', 'wptexturize' ); add_filter( 'the_excerpt_embed',
'convert_chars' ); add_filter( 'the_excerpt_embed', 'wpautop' ); add_filter(
'the_excerpt_embed', 'shortcode_unautop' ); add_filter( 'the_excerpt_embed',
'wp_embed_excerpt_attachment' ); add_filter( 'oembed_dataparse',
'wp_filter_oembed_iframe_title_attribute', 5, 3 ); add_filter(
'oembed_dataparse', 'wp_filter_oembed_result', 10, 3 ); add_filter(
'oembed_response_data', 'get_oembed_response_data_rich', 10, 4 ); add_filter(
'pre_oembed_result', 'wp_filter_pre_oembed_result', 10, 3 ); // Capabilities.
add_filter( 'user_has_cap', 'wp_maybe_grant_install_languages_cap', 1 );
add_filter( 'user_has_cap', 'wp_maybe_grant_resume_extensions_caps', 1 );
add_filter( 'user_has_cap', 'wp_maybe_grant_site_health_caps', 1, 4 ); // Block
templates post type and rendering. add_filter( 'render_block_context',
'_block_template_render_without_post_block_context' ); add_filter(
'pre_wp_unique_post_slug', 'wp_filter_wp_template_unique_post_slug', 10, 5 );
add_action( 'save_post_wp_template_part',
'wp_set_unique_slug_on_create_template_part' ); add_action(
'wp_enqueue_scripts', 'wp_enqueue_block_template_skip_link' ); add_action(
'wp_footer', 'the_block_template_skip_link' ); // Retained for
backwards-compatibility. Unhooked by wp_enqueue_block_template_skip_link().
add_action( 'after_setup_theme', 'wp_enable_block_templates', 1 ); add_action(
'wp_loaded', '_add_template_loader_filters' ); // wp_navigation post type.
add_filter( 'rest_wp_navigation_item_schema', array( 'WP_Navigation_Fallback',
'update_wp_navigation_post_schema' ) ); // Fluid typography. add_filter(
'render_block', 'wp_render_typography_support', 10, 2 ); // User preferences.
add_action( 'init', 'wp_register_persisted_preferences_meta' ); // CPT wp_block
custom postmeta field. add_action( 'init', 'wp_create_initial_post_meta' ); //
Include revisioned meta when considering whether a post revision has changed.
add_filter( 'wp_save_post_revision_post_has_changed',
'wp_check_revisioned_meta_fields_have_changed', 10, 3 ); // Save revisioned post
meta immediately after a revision is saved add_action( '_wp_put_post_revision',
'wp_save_revisioned_meta_fields', 10, 2 ); // Include revisioned meta when
creating or updating an autosave revision. add_action( 'wp_creating_autosave',
'wp_autosave_post_revisioned_meta_fields' ); // When restoring revisions, also
restore revisioned meta. add_action( 'wp_restore_post_revision',
'wp_restore_post_revision_meta', 10, 2 ); // Font management. add_action(
'wp_head', 'wp_print_font_faces', 50 ); add_action( 'deleted_post',
'_wp_after_delete_font_family', 10, 2 ); add_action( 'before_delete_post',
'_wp_before_delete_font_face', 10, 2 ); add_action( 'init',
'_wp_register_default_font_collections' ); // Add ignoredHookedBlocks metadata
attribute to the template and template part post types. add_filter(
'rest_pre_insert_wp_template',
'inject_ignored_hooked_blocks_metadata_attributes' ); add_filter(
'rest_pre_insert_wp_template_part',
'inject_ignored_hooked_blocks_metadata_attributes' ); unset( $filter, $action );
/** * Locale API: WP_Locale class * * @package WordPress * @subpackage i18n *
@since 4.6.0 */ /** * Core class used to store translated data for a locale. * *
@since 2.1.0 * @since 4.6.0 Moved to its own file from wp-includes/locale.php.
*/ #[AllowDynamicProperties] class WP_Locale { /** * Stores the translated
strings for the full weekday names. * * @since 2.1.0 * @since 6.2.0 Initialized
to an empty array. * @var string[] */ public $weekday = array(); /** * Stores
the translated strings for the one character weekday names. * * There is a hack
to make sure that Tuesday and Thursday, as well * as Sunday and Saturday, don't
conflict. See init() method for more. * * @see WP_Locale::init() for how to
handle the hack. * * @since 2.1.0 * @since 6.2.0 Initialized to an empty array.
* @var string[] */ public $weekday_initial = array(); /** * Stores the
translated strings for the abbreviated weekday names. * * @since 2.1.0 * @since
6.2.0 Initialized to an empty array. * @var string[] */ public $weekday_abbrev =
array(); /** * Stores the translated strings for the full month names. * *
@since 2.1.0 * @since 6.2.0 Initialized to an empty array. * @var string[] */
public $month = array(); /** * Stores the translated strings for the month names
in genitive case, if the locale specifies. * * @since 4.4.0 * @since 6.2.0
Initialized to an empty array. * @var string[] */ public $month_genitive =
array(); /** * Stores the translated strings for the abbreviated month names. *
* @since 2.1.0 * @since 6.2.0 Initialized to an empty array. * @var string[] */
public $month_abbrev = array(); /** * Stores the translated strings for 'am' and
'pm'. * * Also the capitalized versions. * * @since 2.1.0 * @since 6.2.0
Initialized to an empty array. * @var string[] */ public $meridiem = array();
/** * The text direction of the locale language. * * Default is left to right
'ltr'. * * @since 2.1.0 * @var string */ public $text_direction = 'ltr'; /** *
The thousands separator and decimal point values used for localizing numbers. *
* @since 2.3.0 * @since 6.2.0 Initialized to an empty array. * @var array */
public $number_format = array(); /** * The separator string used for localizing
list item separator. * * @since 6.0.0 * @var string */ public
$list_item_separator; /** * The word count type of the locale language. * *
Default is 'words'. * * @since 6.2.0 * @var string */ public $word_count_type;
/** * Constructor which calls helper methods to set up object variables. * *
@since 2.1.0 */ public function __construct() { $this->init();
$this->register_globals(); } /** * Sets up the translated strings and object
properties. * * The method creates the translatable strings for various *
calendar elements. Which allows for specifying locale * specific calendar names
and text direction. * * @since 2.1.0 * * @global string $text_direction */
public function init() { // The weekdays. $this->weekday[0] = /* translators:
Weekday. */ __( 'Sunday' ); $this->weekday[1] = /* translators: Weekday. */ __(
'Monday' ); $this->weekday[2] = /* translators: Weekday. */ __( 'Tuesday' );
$this->weekday[3] = /* translators: Weekday. */ __( 'Wednesday' );
$this->weekday[4] = /* translators: Weekday. */ __( 'Thursday' );
$this->weekday[5] = /* translators: Weekday. */ __( 'Friday' );
$this->weekday[6] = /* translators: Weekday. */ __( 'Saturday' ); // The first
letter of each day. $this->weekday_initial[ $this->weekday[0] ] = /*
translators: One-letter abbreviation of the weekday. */ _x( 'S', 'Sunday
initial' ); $this->weekday_initial[ $this->weekday[1] ] = /* translators:
One-letter abbreviation of the weekday. */ _x( 'M', 'Monday initial' );
$this->weekday_initial[ $this->weekday[2] ] = /* translators: One-letter
abbreviation of the weekday. */ _x( 'T', 'Tuesday initial' );
$this->weekday_initial[ $this->weekday[3] ] = /* translators: One-letter
abbreviation of the weekday. */ _x( 'W', 'Wednesday initial' );
$this->weekday_initial[ $this->weekday[4] ] = /* translators: One-letter
abbreviation of the weekday. */ _x( 'T', 'Thursday initial' );
$this->weekday_initial[ $this->weekday[5] ] = /* translators: One-letter
abbreviation of the weekday. */ _x( 'F', 'Friday initial' );
$this->weekday_initial[ $this->weekday[6] ] = /* translators: One-letter
abbreviation of the weekday. */ _x( 'S', 'Saturday initial' ); // Abbreviations
for each day. $this->weekday_abbrev[ $this->weekday[0] ] = /* translators:
Three-letter abbreviation of the weekday. */ __( 'Sun' ); $this->weekday_abbrev[
$this->weekday[1] ] = /* translators: Three-letter abbreviation of the weekday.
*/ __( 'Mon' ); $this->weekday_abbrev[ $this->weekday[2] ] = /* translators:
Three-letter abbreviation of the weekday. */ __( 'Tue' ); $this->weekday_abbrev[
$this->weekday[3] ] = /* translators: Three-letter abbreviation of the weekday.
*/ __( 'Wed' ); $this->weekday_abbrev[ $this->weekday[4] ] = /* translators:
Three-letter abbreviation of the weekday. */ __( 'Thu' ); $this->weekday_abbrev[
$this->weekday[5] ] = /* translators: Three-letter abbreviation of the weekday.
*/ __( 'Fri' ); $this->weekday_abbrev[ $this->weekday[6] ] = /* translators:
Three-letter abbreviation of the weekday. */ __( 'Sat' ); // The months.
$this->month['01'] = /* translators: Month name. */ __( 'January' );
$this->month['02'] = /* translators: Month name. */ __( 'February' );
$this->month['03'] = /* translators: Month name. */ __( 'March' );
$this->month['04'] = /* translators: Month name. */ __( 'April' );
$this->month['05'] = /* translators: Month name. */ __( 'May' );
$this->month['06'] = /* translators: Month name. */ __( 'June' );
$this->month['07'] = /* translators: Month name. */ __( 'July' );
$this->month['08'] = /* translators: Month name. */ __( 'August' );
$this->month['09'] = /* translators: Month name. */ __( 'September' );
$this->month['10'] = /* translators: Month name. */ __( 'October' );
$this->month['11'] = /* translators: Month name. */ __( 'November' );
$this->month['12'] = /* translators: Month name. */ __( 'December' ); // The
months, genitive. $this->month_genitive['01'] = /* translators: Month name,
genitive. */ _x( 'January', 'genitive' ); $this->month_genitive['02'] = /*
translators: Month name, genitive. */ _x( 'February', 'genitive' );
$this->month_genitive['03'] = /* translators: Month name, genitive. */ _x(
'March', 'genitive' ); $this->month_genitive['04'] = /* translators: Month name,
genitive. */ _x( 'April', 'genitive' ); $this->month_genitive['05'] = /*
translators: Month name, genitive. */ _x( 'May', 'genitive' );
$this->month_genitive['06'] = /* translators: Month name, genitive. */ _x(
'June', 'genitive' ); $this->month_genitive['07'] = /* translators: Month name,
genitive. */ _x( 'July', 'genitive' ); $this->month_genitive['08'] = /*
translators: Month name, genitive. */ _x( 'August', 'genitive' );
$this->month_genitive['09'] = /* translators: Month name, genitive. */ _x(
'September', 'genitive' ); $this->month_genitive['10'] = /* translators: Month
name, genitive. */ _x( 'October', 'genitive' ); $this->month_genitive['11'] = /*
translators: Month name, genitive. */ _x( 'November', 'genitive' );
$this->month_genitive['12'] = /* translators: Month name, genitive. */ _x(
'December', 'genitive' ); // Abbreviations for each month. $this->month_abbrev[
$this->month['01'] ] = /* translators: Three-letter abbreviation of the month.
*/ _x( 'Jan', 'January abbreviation' ); $this->month_abbrev[ $this->month['02']
] = /* translators: Three-letter abbreviation of the month. */ _x( 'Feb',
'February abbreviation' ); $this->month_abbrev[ $this->month['03'] ] = /*
translators: Three-letter abbreviation of the month. */ _x( 'Mar', 'March
abbreviation' ); $this->month_abbrev[ $this->month['04'] ] = /* translators:
Three-letter abbreviation of the month. */ _x( 'Apr', 'April abbreviation' );
$this->month_abbrev[ $this->month['05'] ] = /* translators: Three-letter
abbreviation of the month. */ _x( 'May', 'May abbreviation' );
$this->month_abbrev[ $this->month['06'] ] = /* translators: Three-letter
abbreviation of the month. */ _x( 'Jun', 'June abbreviation' );
$this->month_abbrev[ $this->month['07'] ] = /* translators: Three-letter
abbreviation of the month. */ _x( 'Jul', 'July abbreviation' );
$this->month_abbrev[ $this->month['08'] ] = /* translators: Three-letter
abbreviation of the month. */ _x( 'Aug', 'August abbreviation' );
$this->month_abbrev[ $this->month['09'] ] = /* translators: Three-letter
abbreviation of the month. */ _x( 'Sep', 'September abbreviation' );
$this->month_abbrev[ $this->month['10'] ] = /* translators: Three-letter
abbreviation of the month. */ _x( 'Oct', 'October abbreviation' );
$this->month_abbrev[ $this->month['11'] ] = /* translators: Three-letter
abbreviation of the month. */ _x( 'Nov', 'November abbreviation' );
$this->month_abbrev[ $this->month['12'] ] = /* translators: Three-letter
abbreviation of the month. */ _x( 'Dec', 'December abbreviation' ); // The
meridiems. $this->meridiem['am'] = __( 'am' ); $this->meridiem['pm'] = __( 'pm'
); $this->meridiem['AM'] = __( 'AM' ); $this->meridiem['PM'] = __( 'PM' ); /* *
Numbers formatting. * See https://www.php.net/number_format */ /* translators:
$thousands_sep argument for https://www.php.net/number_format, default is ',' */
$thousands_sep = __( 'number_format_thousands_sep' ); // Replace space with a
non-breaking space to avoid wrapping. $thousands_sep = str_replace( ' ', ' ',
$thousands_sep ); $this->number_format['thousands_sep'] = (
'number_format_thousands_sep' === $thousands_sep ) ? ',' : $thousands_sep; /*
translators: $dec_point argument for https://www.php.net/number_format, default
is '.' */ $decimal_point = __( 'number_format_decimal_point' );
$this->number_format['decimal_point'] = ( 'number_format_decimal_point' ===
$decimal_point ) ? '.' : $decimal_point; /* translators: Used between list
items, there is a space after the comma. */ $this->list_item_separator = __( ',
' ); // Set text direction. if ( isset( $GLOBALS['text_direction'] ) ) {
$this->text_direction = $GLOBALS['text_direction']; /* translators: 'rtl' or
'ltr'. This sets the text direction for WordPress. */ } elseif ( 'rtl' === _x(
'ltr', 'text direction' ) ) { $this->text_direction = 'rtl'; } // Set the word
count type. $this->word_count_type = $this->get_word_count_type(); } /** *
Retrieves the full translated weekday word. * * Week starts on translated Sunday
and can be fetched * by using 0 (zero). So the week starts with 0 (zero) * and
ends on Saturday with is fetched by using 6 (six). * * @since 2.1.0 * * @param
int $weekday_number 0 for Sunday through 6 Saturday. * @return string Full
translated weekday. */ public function get_weekday( $weekday_number ) { return
$this->weekday[ $weekday_number ]; } /** * Retrieves the translated weekday
initial. * * The weekday initial is retrieved by the translated * full weekday
word. When translating the weekday initial * pay attention to make sure that the
starting letter does * not conflict. * * @since 2.1.0 * * @param string
$weekday_name Full translated weekday word. * @return string Translated weekday
initial. */ public function get_weekday_initial( $weekday_name ) { return
$this->weekday_initial[ $weekday_name ]; } /** * Retrieves the translated
weekday abbreviation. * * The weekday abbreviation is retrieved by the
translated * full weekday word. * * @since 2.1.0 * * @param string $weekday_name
Full translated weekday word. * @return string Translated weekday abbreviation.
*/ public function get_weekday_abbrev( $weekday_name ) { return
$this->weekday_abbrev[ $weekday_name ]; } /** * Retrieves the full translated
month by month number. * * The $month_number parameter has to be a string *
because it must have the '0' in front of any number * that is less than 10.
Starts from '01' and ends at * '12'. * * You can use an integer instead and it
will add the * '0' before the numbers less than 10 for you. * * @since 2.1.0 * *
@param string|int $month_number '01' through '12'. * @return string Translated
full month name. */ public function get_month( $month_number ) { return
$this->month[ zeroise( $month_number, 2 ) ]; } /** * Retrieves translated
version of month abbreviation string. * * The $month_name parameter is expected
to be the translated or * translatable version of the month. * * @since 2.1.0 *
* @param string $month_name Translated month to get abbreviated version. *
@return string Translated abbreviated month. */ public function
get_month_abbrev( $month_name ) { return $this->month_abbrev[ $month_name ]; }
/** * Retrieves translated version of meridiem string. * * The $meridiem
parameter is expected to not be translated. * * @since 2.1.0 * * @param string
$meridiem Either 'am', 'pm', 'AM', or 'PM'. Not translated version. * @return
string Translated version */ public function get_meridiem( $meridiem ) { return
$this->meridiem[ $meridiem ]; } /** * Global variables are deprecated. * * For
backward compatibility only. * * @deprecated For backward compatibility only. *
* @global array $weekday * @global array $weekday_initial * @global array
$weekday_abbrev * @global array $month * @global array $month_abbrev * * @since
2.1.0 */ public function register_globals() { $GLOBALS['weekday'] =
$this->weekday; $GLOBALS['weekday_initial'] = $this->weekday_initial;
$GLOBALS['weekday_abbrev'] = $this->weekday_abbrev; $GLOBALS['month'] =
$this->month; $GLOBALS['month_abbrev'] = $this->month_abbrev; } /** * Checks if
current locale is RTL. * * @since 3.0.0 * @return bool Whether locale is RTL. */
public function is_rtl() { return 'rtl' === $this->text_direction; } /** *
Registers date/time format strings for general POT. * * Private, unused method
to add some date/time formats translated * on wp-admin/options-general.php to
the general POT that would * otherwise be added to the admin POT. * * @since
3.6.0 */ public function _strings_for_pot() { /* translators: Localized date
format, see https://www.php.net/manual/datetime.format.php */ __( 'F j, Y' ); /*
translators: Localized time format, see
https://www.php.net/manual/datetime.format.php */ __( 'g:i a' ); /* translators:
Localized date and time format, see
https://www.php.net/manual/datetime.format.php */ __( 'F j, Y g:i a' ); } /** *
Retrieves the localized list item separator. * * @since 6.0.0 * * @return string
Localized list item separator. */ public function get_list_item_separator() {
return $this->list_item_separator; } /** * Retrieves the localized word count
type. * * @since 6.2.0 * * @return string Localized word count type. Possible
values are `characters_excluding_spaces`, * `characters_including_spaces`, or
`words`. Defaults to `words`. */ public function get_word_count_type() { /* *
translators: If your word count is based on single characters (e.g. East Asian
characters), * enter 'characters_excluding_spaces' or
'characters_including_spaces'. Otherwise, enter 'words'. * Do not translate into
your own language. */ $word_count_type = is_null( $this->word_count_type ) ? _x(
'words', 'Word count type. Do not translate!' ) : $this->word_count_type; //
Check for valid types. if ( 'characters_excluding_spaces' !== $word_count_type
&& 'characters_including_spaces' !== $word_count_type ) { // Defaults to
'words'. $word_count_type = 'words'; } return $word_count_type; } } /** * A
class for displaying various tree-like structures. * * Extend the Walker class
to use it, see examples below. Child classes * do not need to implement all of
the abstract methods in the class. The child * only needs to implement the
methods that are needed. * * @since 2.1.0 * * @package WordPress * @abstract */
#[AllowDynamicProperties] class Walker { /** * What the class handles. * *
@since 2.1.0 * @var string */ public $tree_type; /** * DB fields to use. * *
@since 2.1.0 * @var string[] */ public $db_fields; /** * Max number of pages
walked by the paged walker. * * @since 2.7.0 * @var int */ public $max_pages =
1; /** * Whether the current element has children or not. * * To be used in
start_el(). * * @since 4.0.0 * @var bool */ public $has_children; /** * Starts
the list before the elements are added. * * The $args parameter holds additional
values that may be used with the child * class methods. This method is called at
the start of the output list. * * @since 2.1.0 * @abstract * * @param string
$output Used to append additional content (passed by reference). * @param int
$depth Depth of the item. * @param array $args An array of additional arguments.
*/ public function start_lvl( &$output, $depth = 0, $args = array() ) {} /** *
Ends the list of after the elements are added. * * The $args parameter holds
additional values that may be used with the child * class methods. This method
finishes the list at the end of output of the elements. * * @since 2.1.0 *
@abstract * * @param string $output Used to append additional content (passed by
reference). * @param int $depth Depth of the item. * @param array $args An array
of additional arguments. */ public function end_lvl( &$output, $depth = 0, $args
= array() ) {} /** * Starts the element output. * * The $args parameter holds
additional values that may be used with the child * class methods. Also includes
the element output. * * @since 2.1.0 * @since 5.9.0 Renamed `$object` (a PHP
reserved keyword) to `$data_object` for PHP 8 named parameter support. *
@abstract * * @param string $output Used to append additional content (passed by
reference). * @param object $data_object The data object. * @param int $depth
Depth of the item. * @param array $args An array of additional arguments. *
@param int $current_object_id Optional. ID of the current item. Default 0. */
public function start_el( &$output, $data_object, $depth = 0, $args = array(),
$current_object_id = 0 ) {} /** * Ends the element output, if needed. * * The
$args parameter holds additional values that may be used with the child class
methods. * * @since 2.1.0 * @since 5.9.0 Renamed `$object` (a PHP reserved
keyword) to `$data_object` for PHP 8 named parameter support. * @abstract * *
@param string $output Used to append additional content (passed by reference). *
@param object $data_object The data object. * @param int $depth Depth of the
item. * @param array $args An array of additional arguments. */ public function
end_el( &$output, $data_object, $depth = 0, $args = array() ) {} /** * Traverses
elements to create list from elements. * * Display one element if the element
doesn't have any children otherwise, * display the element and its children.
Will only traverse up to the max * depth and no ignore elements under that
depth. It is possible to set the * max depth to include all depths, see walk()
method. * * This method should not be called directly, use the walk() method
instead. * * @since 2.5.0 * * @param object $element Data object. * @param array
$children_elements List of elements to continue traversing (passed by
reference). * @param int $max_depth Max depth to traverse. * @param int $depth
Depth of current element. * @param array $args An array of arguments. * @param
string $output Used to append additional content (passed by reference). */
public function display_element( $element, &$children_elements, $max_depth,
$depth, $args, &$output ) { if ( ! $element ) { return; } $id_field =
$this->db_fields['id']; $id = $element->$id_field; // Display this element.
$this->has_children = ! empty( $children_elements[ $id ] ); if ( isset( $args[0]
) && is_array( $args[0] ) ) { $args[0]['has_children'] = $this->has_children; //
Back-compat. } $this->start_el( $output, $element, $depth, ...array_values(
$args ) ); // Descend only when the depth is right and there are children for
this element. if ( ( 0 == $max_depth || $max_depth > $depth + 1 ) && isset(
$children_elements[ $id ] ) ) { foreach ( $children_elements[ $id ] as $child )
{ if ( ! isset( $newlevel ) ) { $newlevel = true; // Start the child delimiter.
$this->start_lvl( $output, $depth, ...array_values( $args ) ); }
$this->display_element( $child, $children_elements, $max_depth, $depth + 1,
$args, $output ); } unset( $children_elements[ $id ] ); } if ( isset( $newlevel
) && $newlevel ) { // End the child delimiter. $this->end_lvl( $output, $depth,
...array_values( $args ) ); } // End this element. $this->end_el( $output,
$element, $depth, ...array_values( $args ) ); } /** * Displays array of elements
hierarchically. * * Does not assume any existing order of elements. * *
$max_depth = -1 means flatly display every element. * $max_depth = 0 means
display all levels. * $max_depth > 0 specifies the number of display levels. * *
@since 2.1.0 * @since 5.3.0 Formalized the existing `...$args` parameter by
adding it * to the function signature. * * @param array $elements An array of
elements. * @param int $max_depth The maximum hierarchical depth. * @param mixed
...$args Optional additional arguments. * @return string The hierarchical item
output. */ public function walk( $elements, $max_depth, ...$args ) { $output =
''; // Invalid parameter or nothing to walk. if ( $max_depth < -1 || empty(
$elements ) ) { return $output; } $parent_field = $this->db_fields['parent']; //
Flat display. if ( -1 == $max_depth ) { $empty_array = array(); foreach (
$elements as $e ) { $this->display_element( $e, $empty_array, 1, 0, $args,
$output ); } return $output; } /* * Need to display in hierarchical order. *
Separate elements into two buckets: top level and children elements. *
Children_elements is two dimensional array. Example: * Children_elements[10][]
contains all sub-elements whose parent is 10. */ $top_level_elements = array();
$children_elements = array(); foreach ( $elements as $e ) { if ( empty(
$e->$parent_field ) ) { $top_level_elements[] = $e; } else { $children_elements[
$e->$parent_field ][] = $e; } } /* * When none of the elements is top level. *
Assume the first one must be root of the sub elements. */ if ( empty(
$top_level_elements ) ) { $first = array_slice( $elements, 0, 1 ); $root =
$first[0]; $top_level_elements = array(); $children_elements = array(); foreach
( $elements as $e ) { if ( $root->$parent_field == $e->$parent_field ) {
$top_level_elements[] = $e; } else { $children_elements[ $e->$parent_field ][] =
$e; } } } foreach ( $top_level_elements as $e ) { $this->display_element( $e,
$children_elements, $max_depth, 0, $args, $output ); } /* * If we are displaying
all levels, and remaining children_elements is not empty, * then we got orphans,
which should be displayed regardless. */ if ( ( 0 == $max_depth ) && count(
$children_elements ) > 0 ) { $empty_array = array(); foreach (
$children_elements as $orphans ) { foreach ( $orphans as $op ) {
$this->display_element( $op, $empty_array, 1, 0, $args, $output ); } } } return
$output; } /** * Produces a page of nested elements. * * Given an array of
hierarchical elements, the maximum depth, a specific page number, * and number
of elements per page, this function first determines all top level root elements
* belonging to that page, then lists them and all of their children in
hierarchical order. * * $max_depth = 0 means display all levels. * $max_depth >
0 specifies the number of display levels. * * @since 2.7.0 * @since 5.3.0
Formalized the existing `...$args` parameter by adding it * to the function
signature. * * @param array $elements An array of elements. * @param int
$max_depth The maximum hierarchical depth. * @param int $page_num The specific
page number, beginning with 1. * @param int $per_page Number of elements per
page. * @param mixed ...$args Optional additional arguments. * @return string
XHTML of the specified page of elements. */ public function paged_walk(
$elements, $max_depth, $page_num, $per_page, ...$args ) { if ( empty( $elements
) || $max_depth < -1 ) { return ''; } $output = ''; $parent_field =
$this->db_fields['parent']; $count = -1; if ( -1 == $max_depth ) { $total_top =
count( $elements ); } if ( $page_num < 1 || $per_page < 0 ) { // No paging.
$paging = false; $start = 0; if ( -1 == $max_depth ) { $end = $total_top; }
$this->max_pages = 1; } else { $paging = true; $start = ( (int) $page_num - 1 )
* (int) $per_page; $end = $start + $per_page; if ( -1 == $max_depth ) {
$this->max_pages = (int) ceil( $total_top / $per_page ); } } // Flat display. if
( -1 == $max_depth ) { if ( ! empty( $args[0]['reverse_top_level'] ) ) {
$elements = array_reverse( $elements ); $oldstart = $start; $start = $total_top
- $end; $end = $total_top - $oldstart; } $empty_array = array(); foreach (
$elements as $e ) { ++$count; if ( $count < $start ) { continue; } if ( $count
>= $end ) { break; } $this->display_element( $e, $empty_array, 1, 0, $args,
$output ); } return $output; } /* * Separate elements into two buckets: top
level and children elements. * Children_elements is two dimensional array, e.g.
* $children_elements[10][] contains all sub-elements whose parent is 10. */
$top_level_elements = array(); $children_elements = array(); foreach ( $elements
as $e ) { if ( empty( $e->$parent_field ) ) { $top_level_elements[] = $e; } else
{ $children_elements[ $e->$parent_field ][] = $e; } } $total_top = count(
$top_level_elements ); if ( $paging ) { $this->max_pages = (int) ceil(
$total_top / $per_page ); } else { $end = $total_top; } if ( ! empty(
$args[0]['reverse_top_level'] ) ) { $top_level_elements = array_reverse(
$top_level_elements ); $oldstart = $start; $start = $total_top - $end; $end =
$total_top - $oldstart; } if ( ! empty( $args[0]['reverse_children'] ) ) {
foreach ( $children_elements as $parent => $children ) { $children_elements[
$parent ] = array_reverse( $children ); } } foreach ( $top_level_elements as $e
) { ++$count; // For the last page, need to unset earlier children in order to
keep track of orphans. if ( $end >= $total_top && $count < $start ) {
$this->unset_children( $e, $children_elements ); } if ( $count < $start ) {
continue; } if ( $count >= $end ) { break; } $this->display_element( $e,
$children_elements, $max_depth, 0, $args, $output ); } if ( $end >= $total_top
&& count( $children_elements ) > 0 ) { $empty_array = array(); foreach (
$children_elements as $orphans ) { foreach ( $orphans as $op ) {
$this->display_element( $op, $empty_array, 1, 0, $args, $output ); } } } return
$output; } /** * Calculates the total number of root elements. * * @since 2.7.0
* * @param array $elements Elements to list. * @return int Number of root
elements. */ public function get_number_of_root_elements( $elements ) { $num =
0; $parent_field = $this->db_fields['parent']; foreach ( $elements as $e ) { if
( empty( $e->$parent_field ) ) { ++$num; } } return $num; } /** * Unsets all the
children for a given top level element. * * @since 2.7.0 * * @param object
$element The top level element. * @param array $children_elements The children
elements. */ public function unset_children( $element, &$children_elements ) {
if ( ! $element || ! $children_elements ) { return; } $id_field =
$this->db_fields['id']; $id = $element->$id_field; if ( ! empty(
$children_elements[ $id ] ) && is_array( $children_elements[ $id ] ) ) { foreach
( (array) $children_elements[ $id ] as $child ) { $this->unset_children( $child,
$children_elements ); } } unset( $children_elements[ $id ] ); } } /** * Core
User Role & Capabilities API * * @package WordPress * @subpackage Users */ /** *
Maps a capability to the primitive capabilities required of the given user to *
satisfy the capability being checked. * * This function also accepts an ID of an
object to map against if the capability is a meta capability. Meta *
capabilities such as `edit_post` and `edit_user` are capabilities used by this
function to map to primitive * capabilities that a user or role requires, such
as `edit_posts` and `edit_others_posts`. * * Example usage: * * map_meta_cap(
'edit_posts', $user->ID ); * map_meta_cap( 'edit_post', $user->ID, $post->ID );
* map_meta_cap( 'edit_post_meta', $user->ID, $post->ID, $meta_key ); * * This
function does not check whether the user has the required capabilities, * it
just returns what the required capabilities are. * * @since 2.0.0 * @since 4.9.6
Added the `export_others_personal_data`, `erase_others_personal_data`, * and
`manage_privacy_options` capabilities. * @since 5.1.0 Added the `update_php`
capability. * @since 5.2.0 Added the `resume_plugin` and `resume_theme`
capabilities. * @since 5.3.0 Formalized the existing and already documented
`...$args` parameter * by adding it to the function signature. * @since 5.7.0
Added the `create_app_password`, `list_app_passwords`, `read_app_password`, *
`edit_app_password`, `delete_app_passwords`, `delete_app_password`, * and
`update_https` capabilities. * * @global array $post_type_meta_caps Used to get
post type meta capabilities. * * @param string $cap Capability being checked. *
@param int $user_id User ID. * @param mixed ...$args Optional further
parameters, typically starting with an object ID. * @return string[] Primitive
capabilities required of the user. */ function map_meta_cap( $cap, $user_id,
...$args ) { $caps = array(); switch ( $cap ) { case 'remove_user': // In
multisite the user must be a super admin to remove themselves. if ( isset(
$args[0] ) && $user_id == $args[0] && ! is_super_admin( $user_id ) ) { $caps[] =
'do_not_allow'; } else { $caps[] = 'remove_users'; } break; case 'promote_user':
case 'add_users': $caps[] = 'promote_users'; break; case 'edit_user': case
'edit_users': // Allow user to edit themselves. if ( 'edit_user' === $cap &&
isset( $args[0] ) && $user_id == $args[0] ) { break; } // In multisite the user
must have manage_network_users caps. If editing a super admin, the user must be
a super admin. if ( is_multisite() && ( ( ! is_super_admin( $user_id ) &&
'edit_user' === $cap && is_super_admin( $args[0] ) ) || ! user_can( $user_id,
'manage_network_users' ) ) ) { $caps[] = 'do_not_allow'; } else { $caps[] =
'edit_users'; // edit_user maps to edit_users. } break; case 'delete_post': case
'delete_page': if ( ! isset( $args[0] ) ) { if ( 'delete_post' === $cap ) { /*
translators: %s: Capability name. */ $message = __( 'When checking for the %s
capability, you must always check it against a specific post.' ); } else { /*
translators: %s: Capability name. */ $message = __( 'When checking for the %s
capability, you must always check it against a specific page.' ); }
_doing_it_wrong( __FUNCTION__, sprintf( $message, '' . $cap . '' ), '6.1.0' );
$caps[] = 'do_not_allow'; break; } $post = get_post( $args[0] ); if ( ! $post )
{ $caps[] = 'do_not_allow'; break; } if ( 'revision' === $post->post_type ) {
$caps[] = 'do_not_allow'; break; } if ( ( get_option( 'page_for_posts' ) ==
$post->ID ) || ( get_option( 'page_on_front' ) == $post->ID ) ) { $caps[] =
'manage_options'; break; } $post_type = get_post_type_object( $post->post_type
); if ( ! $post_type ) { /* translators: 1: Post type, 2: Capability name. */
$message = __( 'The post type %1$s is not registered, so it may not be reliable
to check the capability %2$s against a post of that type.' ); _doing_it_wrong(
__FUNCTION__, sprintf( $message, '' . $post->post_type . '', '' . $cap . '' ),
'4.4.0' ); $caps[] = 'edit_others_posts'; break; } if ( !
$post_type->map_meta_cap ) { $caps[] = $post_type->cap->$cap; // Prior to 3.1 we
would re-call map_meta_cap here. if ( 'delete_post' === $cap ) { $cap =
$post_type->cap->$cap; } break; } // If the post author is set and the user is
the author... if ( $post->post_author && $user_id == $post->post_author ) { //
If the post is published or scheduled... if ( in_array( $post->post_status,
array( 'publish', 'future' ), true ) ) { $caps[] =
$post_type->cap->delete_published_posts; } elseif ( 'trash' ===
$post->post_status ) { $status = get_post_meta( $post->ID,
'_wp_trash_meta_status', true ); if ( in_array( $status, array( 'publish',
'future' ), true ) ) { $caps[] = $post_type->cap->delete_published_posts; } else
{ $caps[] = $post_type->cap->delete_posts; } } else { // If the post is draft...
$caps[] = $post_type->cap->delete_posts; } } else { // The user is trying to
edit someone else's post. $caps[] = $post_type->cap->delete_others_posts; // The
post is published or scheduled, extra cap required. if ( in_array(
$post->post_status, array( 'publish', 'future' ), true ) ) { $caps[] =
$post_type->cap->delete_published_posts; } elseif ( 'private' ===
$post->post_status ) { $caps[] = $post_type->cap->delete_private_posts; } } /* *
Setting the privacy policy page requires `manage_privacy_options`, * so deleting
it should require that too. */ if ( (int) get_option(
'wp_page_for_privacy_policy' ) === $post->ID ) { $caps = array_merge( $caps,
map_meta_cap( 'manage_privacy_options', $user_id ) ); } break; /* * edit_post
breaks down to edit_posts, edit_published_posts, or * edit_others_posts. */ case
'edit_post': case 'edit_page': if ( ! isset( $args[0] ) ) { if ( 'edit_post' ===
$cap ) { /* translators: %s: Capability name. */ $message = __( 'When checking
for the %s capability, you must always check it against a specific post.' ); }
else { /* translators: %s: Capability name. */ $message = __( 'When checking for
the %s capability, you must always check it against a specific page.' ); }
_doing_it_wrong( __FUNCTION__, sprintf( $message, '' . $cap . '' ), '6.1.0' );
$caps[] = 'do_not_allow'; break; } $post = get_post( $args[0] ); if ( ! $post )
{ $caps[] = 'do_not_allow'; break; } if ( 'revision' === $post->post_type ) {
$post = get_post( $post->post_parent ); if ( ! $post ) { $caps[] =
'do_not_allow'; break; } } $post_type = get_post_type_object( $post->post_type
); if ( ! $post_type ) { /* translators: 1: Post type, 2: Capability name. */
$message = __( 'The post type %1$s is not registered, so it may not be reliable
to check the capability %2$s against a post of that type.' ); _doing_it_wrong(
__FUNCTION__, sprintf( $message, '' . $post->post_type . '', '' . $cap . '' ),
'4.4.0' ); $caps[] = 'edit_others_posts'; break; } if ( !
$post_type->map_meta_cap ) { $caps[] = $post_type->cap->$cap; // Prior to 3.1 we
would re-call map_meta_cap here. if ( 'edit_post' === $cap ) { $cap =
$post_type->cap->$cap; } break; } // If the post author is set and the user is
the author... if ( $post->post_author && $user_id == $post->post_author ) { //
If the post is published or scheduled... if ( in_array( $post->post_status,
array( 'publish', 'future' ), true ) ) { $caps[] =
$post_type->cap->edit_published_posts; } elseif ( 'trash' === $post->post_status
) { $status = get_post_meta( $post->ID, '_wp_trash_meta_status', true ); if (
in_array( $status, array( 'publish', 'future' ), true ) ) { $caps[] =
$post_type->cap->edit_published_posts; } else { $caps[] =
$post_type->cap->edit_posts; } } else { // If the post is draft... $caps[] =
$post_type->cap->edit_posts; } } else { // The user is trying to edit someone
else's post. $caps[] = $post_type->cap->edit_others_posts; // The post is
published or scheduled, extra cap required. if ( in_array( $post->post_status,
array( 'publish', 'future' ), true ) ) { $caps[] =
$post_type->cap->edit_published_posts; } elseif ( 'private' ===
$post->post_status ) { $caps[] = $post_type->cap->edit_private_posts; } } /* *
Setting the privacy policy page requires `manage_privacy_options`, * so editing
it should require that too. */ if ( (int) get_option(
'wp_page_for_privacy_policy' ) === $post->ID ) { $caps = array_merge( $caps,
map_meta_cap( 'manage_privacy_options', $user_id ) ); } break; case 'read_post':
case 'read_page': if ( ! isset( $args[0] ) ) { if ( 'read_post' === $cap ) { /*
translators: %s: Capability name. */ $message = __( 'When checking for the %s
capability, you must always check it against a specific post.' ); } else { /*
translators: %s: Capability name. */ $message = __( 'When checking for the %s
capability, you must always check it against a specific page.' ); }
_doing_it_wrong( __FUNCTION__, sprintf( $message, '' . $cap . '' ), '6.1.0' );
$caps[] = 'do_not_allow'; break; } $post = get_post( $args[0] ); if ( ! $post )
{ $caps[] = 'do_not_allow'; break; } if ( 'revision' === $post->post_type ) {
$post = get_post( $post->post_parent ); if ( ! $post ) { $caps[] =
'do_not_allow'; break; } } $post_type = get_post_type_object( $post->post_type
); if ( ! $post_type ) { /* translators: 1: Post type, 2: Capability name. */
$message = __( 'The post type %1$s is not registered, so it may not be reliable
to check the capability %2$s against a post of that type.' ); _doing_it_wrong(
__FUNCTION__, sprintf( $message, '' . $post->post_type . '', '' . $cap . '' ),
'4.4.0' ); $caps[] = 'edit_others_posts'; break; } if ( !
$post_type->map_meta_cap ) { $caps[] = $post_type->cap->$cap; // Prior to 3.1 we
would re-call map_meta_cap here. if ( 'read_post' === $cap ) { $cap =
$post_type->cap->$cap; } break; } $status_obj = get_post_status_object(
get_post_status( $post ) ); if ( ! $status_obj ) { /* translators: 1: Post
status, 2: Capability name. */ $message = __( 'The post status %1$s is not
registered, so it may not be reliable to check the capability %2$s against a
post with that status.' ); _doing_it_wrong( __FUNCTION__, sprintf( $message, ''
. get_post_status( $post ) . '', '' . $cap . '' ), '5.4.0' ); $caps[] =
'edit_others_posts'; break; } if ( $status_obj->public ) { $caps[] =
$post_type->cap->read; break; } if ( $post->post_author && $user_id ==
$post->post_author ) { $caps[] = $post_type->cap->read; } elseif (
$status_obj->private ) { $caps[] = $post_type->cap->read_private_posts; } else {
$caps = map_meta_cap( 'edit_post', $user_id, $post->ID ); } break; case
'publish_post': if ( ! isset( $args[0] ) ) { /* translators: %s: Capability
name. */ $message = __( 'When checking for the %s capability, you must always
check it against a specific post.' ); _doing_it_wrong( __FUNCTION__, sprintf(
$message, '' . $cap . '' ), '6.1.0' ); $caps[] = 'do_not_allow'; break; } $post
= get_post( $args[0] ); if ( ! $post ) { $caps[] = 'do_not_allow'; break; }
$post_type = get_post_type_object( $post->post_type ); if ( ! $post_type ) { /*
translators: 1: Post type, 2: Capability name. */ $message = __( 'The post type
%1$s is not registered, so it may not be reliable to check the capability %2$s
against a post of that type.' ); _doing_it_wrong( __FUNCTION__, sprintf(
$message, '' . $post->post_type . '', '' . $cap . '' ), '4.4.0' ); $caps[] =
'edit_others_posts'; break; } $caps[] = $post_type->cap->publish_posts; break;
case 'edit_post_meta': case 'delete_post_meta': case 'add_post_meta': case
'edit_comment_meta': case 'delete_comment_meta': case 'add_comment_meta': case
'edit_term_meta': case 'delete_term_meta': case 'add_term_meta': case
'edit_user_meta': case 'delete_user_meta': case 'add_user_meta': $object_type =
explode( '_', $cap )[1]; if ( ! isset( $args[0] ) ) { if ( 'post' ===
$object_type ) { /* translators: %s: Capability name. */ $message = __( 'When
checking for the %s capability, you must always check it against a specific
post.' ); } elseif ( 'comment' === $object_type ) { /* translators: %s:
Capability name. */ $message = __( 'When checking for the %s capability, you
must always check it against a specific comment.' ); } elseif ( 'term' ===
$object_type ) { /* translators: %s: Capability name. */ $message = __( 'When
checking for the %s capability, you must always check it against a specific
term.' ); } else { /* translators: %s: Capability name. */ $message = __( 'When
checking for the %s capability, you must always check it against a specific
user.' ); } _doing_it_wrong( __FUNCTION__, sprintf( $message, '' . $cap . '' ),
'6.1.0' ); $caps[] = 'do_not_allow'; break; } $object_id = (int) $args[0];
$object_subtype = get_object_subtype( $object_type, $object_id ); if ( empty(
$object_subtype ) ) { $caps[] = 'do_not_allow'; break; } $caps = map_meta_cap(
"edit_{$object_type}", $user_id, $object_id ); $meta_key = isset( $args[1] ) ?
$args[1] : false; if ( $meta_key ) { $allowed = ! is_protected_meta( $meta_key,
$object_type ); if ( ! empty( $object_subtype ) && has_filter(
"auth_{$object_type}_meta_{$meta_key}_for_{$object_subtype}" ) ) { /** * Filters
whether the user is allowed to edit a specific meta key of a specific object
type and subtype. * * The dynamic portions of the hook name, `$object_type`,
`$meta_key`, * and `$object_subtype`, refer to the metadata object type
(comment, post, term or user), * the meta key value, and the object subtype
respectively. * * @since 4.9.8 * * @param bool $allowed Whether the user can add
the object meta. Default false. * @param string $meta_key The meta key. * @param
int $object_id Object ID. * @param int $user_id User ID. * @param string $cap
Capability name. * @param string[] $caps Array of the user's capabilities. */
$allowed = apply_filters(
"auth_{$object_type}_meta_{$meta_key}_for_{$object_subtype}", $allowed,
$meta_key, $object_id, $user_id, $cap, $caps ); } else { /** * Filters whether
the user is allowed to edit a specific meta key of a specific object type. * *
Return true to have the mapped meta caps from `edit_{$object_type}` apply. * *
The dynamic portion of the hook name, `$object_type` refers to the object type
being filtered. * The dynamic portion of the hook name, `$meta_key`, refers to
the meta key passed to map_meta_cap(). * * @since 3.3.0 As
`auth_post_meta_{$meta_key}`. * @since 4.6.0 * * @param bool $allowed Whether
the user can add the object meta. Default false. * @param string $meta_key The
meta key. * @param int $object_id Object ID. * @param int $user_id User ID. *
@param string $cap Capability name. * @param string[] $caps Array of the user's
capabilities. */ $allowed = apply_filters(
"auth_{$object_type}_meta_{$meta_key}", $allowed, $meta_key, $object_id,
$user_id, $cap, $caps ); } if ( ! empty( $object_subtype ) ) { /** * Filters
whether the user is allowed to edit meta for specific object types/subtypes. * *
Return true to have the mapped meta caps from `edit_{$object_type}` apply. * *
The dynamic portion of the hook name, `$object_type` refers to the object type
being filtered. * The dynamic portion of the hook name, `$object_subtype` refers
to the object subtype being filtered. * The dynamic portion of the hook name,
`$meta_key`, refers to the meta key passed to map_meta_cap(). * * @since 4.6.0
As `auth_post_{$post_type}_meta_{$meta_key}`. * @since 4.7.0 Renamed from
`auth_post_{$post_type}_meta_{$meta_key}` to *
`auth_{$object_type}_{$object_subtype}_meta_{$meta_key}`. * @deprecated 4.9.8
Use {@see 'auth_{$object_type}_meta_{$meta_key}_for_{$object_subtype}'} instead.
* * @param bool $allowed Whether the user can add the object meta. Default
false. * @param string $meta_key The meta key. * @param int $object_id Object
ID. * @param int $user_id User ID. * @param string $cap Capability name. *
@param string[] $caps Array of the user's capabilities. */ $allowed =
apply_filters_deprecated(
"auth_{$object_type}_{$object_subtype}_meta_{$meta_key}", array( $allowed,
$meta_key, $object_id, $user_id, $cap, $caps ), '4.9.8',
"auth_{$object_type}_meta_{$meta_key}_for_{$object_subtype}" ); } if ( !
$allowed ) { $caps[] = $cap; } } break; case 'edit_comment': if ( ! isset(
$args[0] ) ) { /* translators: %s: Capability name. */ $message = __( 'When
checking for the %s capability, you must always check it against a specific
comment.' ); _doing_it_wrong( __FUNCTION__, sprintf( $message, '' . $cap . '' ),
'6.1.0' ); $caps[] = 'do_not_allow'; break; } $comment = get_comment( $args[0]
); if ( ! $comment ) { $caps[] = 'do_not_allow'; break; } $post = get_post(
$comment->comment_post_ID ); /* * If the post doesn't exist, we have an orphaned
comment. * Fall back to the edit_posts capability, instead. */ if ( $post ) {
$caps = map_meta_cap( 'edit_post', $user_id, $post->ID ); } else { $caps =
map_meta_cap( 'edit_posts', $user_id ); } break; case 'unfiltered_upload': if (
defined( 'ALLOW_UNFILTERED_UPLOADS' ) && ALLOW_UNFILTERED_UPLOADS && ( !
is_multisite() || is_super_admin( $user_id ) ) ) { $caps[] = $cap; } else {
$caps[] = 'do_not_allow'; } break; case 'edit_css': case 'unfiltered_html': //
Disallow unfiltered_html for all users, even admins and super admins. if (
defined( 'DISALLOW_UNFILTERED_HTML' ) && DISALLOW_UNFILTERED_HTML ) { $caps[] =
'do_not_allow'; } elseif ( is_multisite() && ! is_super_admin( $user_id ) ) {
$caps[] = 'do_not_allow'; } else { $caps[] = 'unfiltered_html'; } break; case
'edit_files': case 'edit_plugins': case 'edit_themes': // Disallow the file
editors. if ( defined( 'DISALLOW_FILE_EDIT' ) && DISALLOW_FILE_EDIT ) { $caps[]
= 'do_not_allow'; } elseif ( ! wp_is_file_mod_allowed( 'capability_edit_themes'
) ) { $caps[] = 'do_not_allow'; } elseif ( is_multisite() && ! is_super_admin(
$user_id ) ) { $caps[] = 'do_not_allow'; } else { $caps[] = $cap; } break; case
'update_plugins': case 'delete_plugins': case 'install_plugins': case
'upload_plugins': case 'update_themes': case 'delete_themes': case
'install_themes': case 'upload_themes': case 'update_core': /* * Disallow
anything that creates, deletes, or updates core, plugin, or theme files. * Files
in uploads are excepted. */ if ( ! wp_is_file_mod_allowed(
'capability_update_core' ) ) { $caps[] = 'do_not_allow'; } elseif (
is_multisite() && ! is_super_admin( $user_id ) ) { $caps[] = 'do_not_allow'; }
elseif ( 'upload_themes' === $cap ) { $caps[] = 'install_themes'; } elseif (
'upload_plugins' === $cap ) { $caps[] = 'install_plugins'; } else { $caps[] =
$cap; } break; case 'install_languages': case 'update_languages': if ( !
wp_is_file_mod_allowed( 'can_install_language_pack' ) ) { $caps[] =
'do_not_allow'; } elseif ( is_multisite() && ! is_super_admin( $user_id ) ) {
$caps[] = 'do_not_allow'; } else { $caps[] = 'install_languages'; } break; case
'activate_plugins': case 'deactivate_plugins': case 'activate_plugin': case
'deactivate_plugin': $caps[] = 'activate_plugins'; if ( is_multisite() ) { //
update_, install_, and delete_ are handled above with is_super_admin().
$menu_perms = get_site_option( 'menu_items', array() ); if ( empty(
$menu_perms['plugins'] ) ) { $caps[] = 'manage_network_plugins'; } } break; case
'resume_plugin': $caps[] = 'resume_plugins'; break; case 'resume_theme': $caps[]
= 'resume_themes'; break; case 'delete_user': case 'delete_users': // If
multisite only super admins can delete users. if ( is_multisite() && !
is_super_admin( $user_id ) ) { $caps[] = 'do_not_allow'; } else { $caps[] =
'delete_users'; // delete_user maps to delete_users. } break; case
'create_users': if ( ! is_multisite() ) { $caps[] = $cap; } elseif (
is_super_admin( $user_id ) || get_site_option( 'add_new_users' ) ) { $caps[] =
$cap; } else { $caps[] = 'do_not_allow'; } break; case 'manage_links': if (
get_option( 'link_manager_enabled' ) ) { $caps[] = $cap; } else { $caps[] =
'do_not_allow'; } break; case 'customize': $caps[] = 'edit_theme_options';
break; case 'delete_site': if ( is_multisite() ) { $caps[] = 'manage_options'; }
else { $caps[] = 'do_not_allow'; } break; case 'edit_term': case 'delete_term':
case 'assign_term': if ( ! isset( $args[0] ) ) { /* translators: %s: Capability
name. */ $message = __( 'When checking for the %s capability, you must always
check it against a specific term.' ); _doing_it_wrong( __FUNCTION__, sprintf(
$message, '' . $cap . '' ), '6.1.0' ); $caps[] = 'do_not_allow'; break; }
$term_id = (int) $args[0]; $term = get_term( $term_id ); if ( ! $term ||
is_wp_error( $term ) ) { $caps[] = 'do_not_allow'; break; } $tax = get_taxonomy(
$term->taxonomy ); if ( ! $tax ) { $caps[] = 'do_not_allow'; break; } if (
'delete_term' === $cap && ( get_option( 'default_' . $term->taxonomy ) ==
$term->term_id || get_option( 'default_term_' . $term->taxonomy ) ==
$term->term_id ) ) { $caps[] = 'do_not_allow'; break; } $taxo_cap = $cap . 's';
$caps = map_meta_cap( $tax->cap->$taxo_cap, $user_id, $term_id ); break; case
'manage_post_tags': case 'edit_categories': case 'edit_post_tags': case
'delete_categories': case 'delete_post_tags': $caps[] = 'manage_categories';
break; case 'assign_categories': case 'assign_post_tags': $caps[] =
'edit_posts'; break; case 'create_sites': case 'delete_sites': case
'manage_network': case 'manage_sites': case 'manage_network_users': case
'manage_network_plugins': case 'manage_network_themes': case
'manage_network_options': case 'upgrade_network': $caps[] = $cap; break; case
'setup_network': if ( is_multisite() ) { $caps[] = 'manage_network_options'; }
else { $caps[] = 'manage_options'; } break; case 'update_php': if (
is_multisite() && ! is_super_admin( $user_id ) ) { $caps[] = 'do_not_allow'; }
else { $caps[] = 'update_core'; } break; case 'update_https': if (
is_multisite() && ! is_super_admin( $user_id ) ) { $caps[] = 'do_not_allow'; }
else { $caps[] = 'manage_options'; $caps[] = 'update_core'; } break; case
'export_others_personal_data': case 'erase_others_personal_data': case
'manage_privacy_options': $caps[] = is_multisite() ? 'manage_network' :
'manage_options'; break; case 'create_app_password': case 'list_app_passwords':
case 'read_app_password': case 'edit_app_password': case 'delete_app_passwords':
case 'delete_app_password': $caps = map_meta_cap( 'edit_user', $user_id,
$args[0] ); break; default: // Handle meta capabilities for custom post types.
global $post_type_meta_caps; if ( isset( $post_type_meta_caps[ $cap ] ) ) {
return map_meta_cap( $post_type_meta_caps[ $cap ], $user_id, ...$args ); } //
Block capabilities map to their post equivalent. $block_caps = array(
'edit_blocks', 'edit_others_blocks', 'publish_blocks', 'read_private_blocks',
'delete_blocks', 'delete_private_blocks', 'delete_published_blocks',
'delete_others_blocks', 'edit_private_blocks', 'edit_published_blocks', ); if (
in_array( $cap, $block_caps, true ) ) { $cap = str_replace( '_blocks', '_posts',
$cap ); } // If no meta caps match, return the original cap. $caps[] = $cap; }
/** * Filters the primitive capabilities required of the given user to satisfy
the * capability being checked. * * @since 2.8.0 * * @param string[] $caps
Primitive capabilities required of the user. * @param string $cap Capability
being checked. * @param int $user_id The user ID. * @param array $args Adds
context to the capability check, typically * starting with an object ID. */
return apply_filters( 'map_meta_cap', $caps, $cap, $user_id, $args ); } /** *
Returns whether the current user has the specified capability. * * This function
also accepts an ID of an object to check against if the capability is a meta
capability. Meta * capabilities such as `edit_post` and `edit_user` are
capabilities used by the `map_meta_cap()` function to * map to primitive
capabilities that a user or role has, such as `edit_posts` and
`edit_others_posts`. * * Example usage: * * current_user_can( 'edit_posts' ); *
current_user_can( 'edit_post', $post->ID ); * current_user_can(
'edit_post_meta', $post->ID, $meta_key ); * * While checking against particular
roles in place of a capability is supported * in part, this practice is
discouraged as it may produce unreliable results. * * Note: Will always return
true if the current user is a super admin, unless specifically denied. * *
@since 2.0.0 * @since 5.3.0 Formalized the existing and already documented
`...$args` parameter * by adding it to the function signature. * @since 5.8.0
Converted to wrapper for the user_can() function. * * @see WP_User::has_cap() *
@see map_meta_cap() * * @param string $capability Capability name. * @param
mixed ...$args Optional further parameters, typically starting with an object
ID. * @return bool Whether the current user has the given capability. If
`$capability` is a meta cap and `$object_id` is * passed, whether the current
user has the given meta capability for the given object. */ function
current_user_can( $capability, ...$args ) { return user_can(
wp_get_current_user(), $capability, ...$args ); } /** * Returns whether the
current user has the specified capability for a given site. * * This function
also accepts an ID of an object to check against if the capability is a meta
capability. Meta * capabilities such as `edit_post` and `edit_user` are
capabilities used by the `map_meta_cap()` function to * map to primitive
capabilities that a user or role has, such as `edit_posts` and
`edit_others_posts`. * * Example usage: * * current_user_can_for_blog( $blog_id,
'edit_posts' ); * current_user_can_for_blog( $blog_id, 'edit_post', $post->ID );
* current_user_can_for_blog( $blog_id, 'edit_post_meta', $post->ID, $meta_key );
* * @since 3.0.0 * @since 5.3.0 Formalized the existing and already documented
`...$args` parameter * by adding it to the function signature. * @since 5.8.0
Wraps current_user_can() after switching to blog. * * @param int $blog_id Site
ID. * @param string $capability Capability name. * @param mixed ...$args
Optional further parameters, typically starting with an object ID. * @return
bool Whether the user has the given capability. */ function
current_user_can_for_blog( $blog_id, $capability, ...$args ) { $switched =
is_multisite() ? switch_to_blog( $blog_id ) : false; $can = current_user_can(
$capability, ...$args ); if ( $switched ) { restore_current_blog(); } return
$can; } /** * Returns whether the author of the supplied post has the specified
capability. * * This function also accepts an ID of an object to check against
if the capability is a meta capability. Meta * capabilities such as `edit_post`
and `edit_user` are capabilities used by the `map_meta_cap()` function to * map
to primitive capabilities that a user or role has, such as `edit_posts` and
`edit_others_posts`. * * Example usage: * * author_can( $post, 'edit_posts' ); *
author_can( $post, 'edit_post', $post->ID ); * author_can( $post,
'edit_post_meta', $post->ID, $meta_key ); * * @since 2.9.0 * @since 5.3.0
Formalized the existing and already documented `...$args` parameter * by adding
it to the function signature. * * @param int|WP_Post $post Post ID or post
object. * @param string $capability Capability name. * @param mixed ...$args
Optional further parameters, typically starting with an object ID. * @return
bool Whether the post author has the given capability. */ function author_can(
$post, $capability, ...$args ) { $post = get_post( $post ); if ( ! $post ) {
return false; } $author = get_userdata( $post->post_author ); if ( ! $author ) {
return false; } return $author->has_cap( $capability, ...$args ); } /** *
Returns whether a particular user has the specified capability. * * This
function also accepts an ID of an object to check against if the capability is a
meta capability. Meta * capabilities such as `edit_post` and `edit_user` are
capabilities used by the `map_meta_cap()` function to * map to primitive
capabilities that a user or role has, such as `edit_posts` and
`edit_others_posts`. * * Example usage: * * user_can( $user->ID, 'edit_posts' );
* user_can( $user->ID, 'edit_post', $post->ID ); * user_can( $user->ID,
'edit_post_meta', $post->ID, $meta_key ); * * @since 3.1.0 * @since 5.3.0
Formalized the existing and already documented `...$args` parameter * by adding
it to the function signature. * * @param int|WP_User $user User ID or object. *
@param string $capability Capability name. * @param mixed ...$args Optional
further parameters, typically starting with an object ID. * @return bool Whether
the user has the given capability. */ function user_can( $user, $capability,
...$args ) { if ( ! is_object( $user ) ) { $user = get_userdata( $user ); } if (
empty( $user ) ) { // User is logged out, create anonymous user object. $user =
new WP_User( 0 ); $user->init( new stdClass() ); } return $user->has_cap(
$capability, ...$args ); } /** * Retrieves the global WP_Roles instance and
instantiates it if necessary. * * @since 4.3.0 * * @global WP_Roles $wp_roles
WordPress role management object. * * @return WP_Roles WP_Roles global instance
if not already instantiated. */ function wp_roles() { global $wp_roles; if ( !
isset( $wp_roles ) ) { $wp_roles = new WP_Roles(); } return $wp_roles; } /** *
Retrieves role object. * * @since 2.0.0 * * @param string $role Role name. *
@return WP_Role|null WP_Role object if found, null if the role does not exist.
*/ function get_role( $role ) { return wp_roles()->get_role( $role ); } /** *
Adds a role, if it does not exist. * * @since 2.0.0 * * @param string $role Role
name. * @param string $display_name Display name for role. * @param bool[]
$capabilities List of capabilities keyed by the capability name, * e.g. array(
'edit_posts' => true, 'delete_posts' => false ). * @return WP_Role|void WP_Role
object, if the role is added. */ function add_role( $role, $display_name,
$capabilities = array() ) { if ( empty( $role ) ) { return; } return
wp_roles()->add_role( $role, $display_name, $capabilities ); } /** * Removes a
role, if it exists. * * @since 2.0.0 * * @param string $role Role name. */
function remove_role( $role ) { wp_roles()->remove_role( $role ); } /** *
Retrieves a list of super admins. * * @since 3.0.0 * * @global array
$super_admins * * @return string[] List of super admin logins. */ function
get_super_admins() { global $super_admins; if ( isset( $super_admins ) ) {
return $super_admins; } else { return get_site_option( 'site_admins', array(
'admin' ) ); } } /** * Determines whether user is a site admin. * * @since 3.0.0
* * @param int|false $user_id Optional. The ID of a user. Defaults to false, to
check the current user. * @return bool Whether the user is a site admin. */
function is_super_admin( $user_id = false ) { if ( ! $user_id ) { $user =
wp_get_current_user(); } else { $user = get_userdata( $user_id ); } if ( ! $user
|| ! $user->exists() ) { return false; } if ( is_multisite() ) { $super_admins =
get_super_admins(); if ( is_array( $super_admins ) && in_array(
$user->user_login, $super_admins, true ) ) { return true; } } else { if (
$user->has_cap( 'delete_users' ) ) { return true; } } return false; } /** *
Grants Super Admin privileges. * * @since 3.0.0 * * @global array $super_admins
* * @param int $user_id ID of the user to be granted Super Admin privileges. *
@return bool True on success, false on failure. This can fail when the user is *
already a super admin or when the `$super_admins` global is defined. */ function
grant_super_admin( $user_id ) { // If global super_admins override is defined,
there is nothing to do here. if ( isset( $GLOBALS['super_admins'] ) || !
is_multisite() ) { return false; } /** * Fires before the user is granted Super
Admin privileges. * * @since 3.0.0 * * @param int $user_id ID of the user that
is about to be granted Super Admin privileges. */ do_action(
'grant_super_admin', $user_id ); // Directly fetch site_admins instead of using
get_super_admins(). $super_admins = get_site_option( 'site_admins', array(
'admin' ) ); $user = get_userdata( $user_id ); if ( $user && ! in_array(
$user->user_login, $super_admins, true ) ) { $super_admins[] =
$user->user_login; update_site_option( 'site_admins', $super_admins ); /** *
Fires after the user is granted Super Admin privileges. * * @since 3.0.0 * *
@param int $user_id ID of the user that was granted Super Admin privileges. */
do_action( 'granted_super_admin', $user_id ); return true; } return false; } /**
* Revokes Super Admin privileges. * * @since 3.0.0 * * @global array
$super_admins * * @param int $user_id ID of the user Super Admin privileges to
be revoked from. * @return bool True on success, false on failure. This can fail
when the user's email * is the network admin email or when the `$super_admins`
global is defined. */ function revoke_super_admin( $user_id ) { // If global
super_admins override is defined, there is nothing to do here. if ( isset(
$GLOBALS['super_admins'] ) || ! is_multisite() ) { return false; } /** * Fires
before the user's Super Admin privileges are revoked. * * @since 3.0.0 * *
@param int $user_id ID of the user Super Admin privileges are being revoked
from. */ do_action( 'revoke_super_admin', $user_id ); // Directly fetch
site_admins instead of using get_super_admins(). $super_admins =
get_site_option( 'site_admins', array( 'admin' ) ); $user = get_userdata(
$user_id ); if ( $user && 0 !== strcasecmp( $user->user_email, get_site_option(
'admin_email' ) ) ) { $key = array_search( $user->user_login, $super_admins,
true ); if ( false !== $key ) { unset( $super_admins[ $key ] );
update_site_option( 'site_admins', $super_admins ); /** * Fires after the user's
Super Admin privileges are revoked. * * @since 3.0.0 * * @param int $user_id ID
of the user Super Admin privileges were revoked from. */ do_action(
'revoked_super_admin', $user_id ); return true; } } return false; } /** *
Filters the user capabilities to grant the 'install_languages' capability as
necessary. * * A user must have at least one out of the 'update_core',
'install_plugins', and * 'install_themes' capabilities to qualify for
'install_languages'. * * @since 4.9.0 * * @param bool[] $allcaps An array of all
the user's capabilities. * @return bool[] Filtered array of the user's
capabilities. */ function wp_maybe_grant_install_languages_cap( $allcaps ) { if
( ! empty( $allcaps['update_core'] ) || ! empty( $allcaps['install_plugins'] )
|| ! empty( $allcaps['install_themes'] ) ) { $allcaps['install_languages'] =
true; } return $allcaps; } /** * Filters the user capabilities to grant the
'resume_plugins' and 'resume_themes' capabilities as necessary. * * @since 5.2.0
* * @param bool[] $allcaps An array of all the user's capabilities. * @return
bool[] Filtered array of the user's capabilities. */ function
wp_maybe_grant_resume_extensions_caps( $allcaps ) { // Even in a multisite,
regular administrators should be able to resume plugins. if ( ! empty(
$allcaps['activate_plugins'] ) ) { $allcaps['resume_plugins'] = true; } // Even
in a multisite, regular administrators should be able to resume themes. if ( !
empty( $allcaps['switch_themes'] ) ) { $allcaps['resume_themes'] = true; }
return $allcaps; } /** * Filters the user capabilities to grant the
'view_site_health_checks' capabilities as necessary. * * @since 5.2.2 * * @param
bool[] $allcaps An array of all the user's capabilities. * @param string[] $caps
Required primitive capabilities for the requested capability. * @param array
$args { * Arguments that accompany the requested capability check. * * @type
string $0 Requested capability. * @type int $1 Concerned user ID. * @type mixed
...$2 Optional second and further parameters, typically object ID. * } * @param
WP_User $user The user object. * @return bool[] Filtered array of the user's
capabilities. */ function wp_maybe_grant_site_health_caps( $allcaps, $caps,
$args, $user ) { if ( ! empty( $allcaps['install_plugins'] ) && ( !
is_multisite() || is_super_admin( $user->ID ) ) ) {
$allcaps['view_site_health_checks'] = true; } return $allcaps; } return; //
Dummy gettext calls to get strings in the catalog. /* translators: User role for
administrators. */ _x( 'Administrator', 'User role' ); /* translators: User role
for editors. */ _x( 'Editor', 'User role' ); /* translators: User role for
authors. */ _x( 'Author', 'User role' ); /* translators: User role for
contributors. */ _x( 'Contributor', 'User role' ); /* translators: User role for
subscribers. */ _x( 'Subscriber', 'User role' ); /** * User API: WP_User class *
* @package WordPress * @subpackage Users * @since 4.4.0 */ /** * Core class used
to implement the WP_User object. * * @since 2.0.0 * * @property string $nickname
* @property string $description * @property string $user_description * @property
string $first_name * @property string $user_firstname * @property string
$last_name * @property string $user_lastname * @property string $user_login *
@property string $user_pass * @property string $user_nicename * @property string
$user_email * @property string $user_url * @property string $user_registered *
@property string $user_activation_key * @property string $user_status *
@property int $user_level * @property string $display_name * @property string
$spam * @property string $deleted * @property string $locale * @property string
$rich_editing * @property string $syntax_highlighting * @property string
$use_ssl */ #[AllowDynamicProperties] class WP_User { /** * User data container.
* * @since 2.0.0 * @var stdClass */ public $data; /** * The user's ID. * *
@since 2.1.0 * @var int */ public $ID = 0; /** * Capabilities that the
individual user has been granted outside of those inherited from their role. * *
@since 2.0.0 * @var bool[] Array of key/value pairs where keys represent a
capability name * and boolean values represent whether the user has that
capability. */ public $caps = array(); /** * User metadata option name. * *
@since 2.0.0 * @var string */ public $cap_key; /** * The roles the user is part
of. * * @since 2.0.0 * @var string[] */ public $roles = array(); /** * All
capabilities the user has, including individual and role based. * * @since 2.0.0
* @var bool[] Array of key/value pairs where keys represent a capability name *
and boolean values represent whether the user has that capability. */ public
$allcaps = array(); /** * The filter context applied to user data fields. * *
@since 2.9.0 * @var string */ public $filter = null; /** * The site ID the
capabilities of this user are initialized for. * * @since 4.9.0 * @var int */
private $site_id = 0; /** * @since 3.3.0 * @var array */ private static
$back_compat_keys; /** * Constructor. * * Retrieves the userdata and passes it
to WP_User::init(). * * @since 2.0.0 * * @global wpdb $wpdb WordPress database
abstraction object. * * @param int|string|stdClass|WP_User $id User's ID, a
WP_User object, or a user object from the DB. * @param string $name Optional.
User's username * @param int $site_id Optional Site ID, defaults to current
site. */ public function __construct( $id = 0, $name = '', $site_id = '' ) {
global $wpdb; if ( ! isset( self::$back_compat_keys ) ) { $prefix =
$wpdb->prefix; self::$back_compat_keys = array( 'user_firstname' =>
'first_name', 'user_lastname' => 'last_name', 'user_description' =>
'description', 'user_level' => $prefix . 'user_level', $prefix . 'usersettings'
=> $prefix . 'user-settings', $prefix . 'usersettingstime' => $prefix .
'user-settings-time', ); } if ( $id instanceof WP_User ) { $this->init(
$id->data, $site_id ); return; } elseif ( is_object( $id ) ) { $this->init( $id,
$site_id ); return; } if ( ! empty( $id ) && ! is_numeric( $id ) ) { $name =
$id; $id = 0; } if ( $id ) { $data = self::get_data_by( 'id', $id ); } else {
$data = self::get_data_by( 'login', $name ); } if ( $data ) { $this->init(
$data, $site_id ); } else { $this->data = new stdClass(); } } /** * Sets up
object properties, including capabilities. * * @since 3.3.0 * * @param object
$data User DB row object. * @param int $site_id Optional. The site ID to
initialize for. */ public function init( $data, $site_id = '' ) { if ( ! isset(
$data->ID ) ) { $data->ID = 0; } $this->data = $data; $this->ID = (int)
$data->ID; $this->for_site( $site_id ); } /** * Returns only the main user
fields. * * @since 3.3.0 * @since 4.4.0 Added 'ID' as an alias of 'id' for the
`$field` parameter. * * @global wpdb $wpdb WordPress database abstraction
object. * * @param string $field The field to query against: Accepts 'id', 'ID',
'slug', 'email' or 'login'. * @param string|int $value The field value. *
@return object|false Raw user object. */ public static function get_data_by(
$field, $value ) { global $wpdb; // 'ID' is an alias of 'id'. if ( 'ID' ===
$field ) { $field = 'id'; } if ( 'id' === $field ) { // Make sure the value is
numeric to avoid casting objects, for example, to int 1. if ( ! is_numeric(
$value ) ) { return false; } $value = (int) $value; if ( $value < 1 ) { return
false; } } else { $value = trim( $value ); } if ( ! $value ) { return false; }
switch ( $field ) { case 'id': $user_id = $value; $db_field = 'ID'; break; case
'slug': $user_id = wp_cache_get( $value, 'userslugs' ); $db_field =
'user_nicename'; break; case 'email': $user_id = wp_cache_get( $value,
'useremail' ); $db_field = 'user_email'; break; case 'login': $value =
sanitize_user( $value ); $user_id = wp_cache_get( $value, 'userlogins' );
$db_field = 'user_login'; break; default: return false; } if ( false !==
$user_id ) { $user = wp_cache_get( $user_id, 'users' ); if ( $user ) { return
$user; } } $user = $wpdb->get_row( $wpdb->prepare( "SELECT * FROM $wpdb->users
WHERE $db_field = %s LIMIT 1", $value ) ); if ( ! $user ) { return false; }
update_user_caches( $user ); return $user; } /** * Magic method for checking the
existence of a certain custom field. * * @since 3.3.0 * * @param string $key
User meta key to check if set. * @return bool Whether the given user meta key is
set. */ public function __isset( $key ) { if ( 'id' === $key ) {
_deprecated_argument( 'WP_User->id', '2.1.0', sprintf( /* translators: %s:
WP_User->ID */ __( 'Use %s instead.' ), 'WP_User->ID' ) ); $key = 'ID'; } if (
isset( $this->data->$key ) ) { return true; } if ( isset(
self::$back_compat_keys[ $key ] ) ) { $key = self::$back_compat_keys[ $key ]; }
return metadata_exists( 'user', $this->ID, $key ); } /** * Magic method for
accessing custom fields. * * @since 3.3.0 * * @param string $key User meta key
to retrieve. * @return mixed Value of the given user meta key (if set). If
`$key` is 'id', the user ID. */ public function __get( $key ) { if ( 'id' ===
$key ) { _deprecated_argument( 'WP_User->id', '2.1.0', sprintf( /* translators:
%s: WP_User->ID */ __( 'Use %s instead.' ), 'WP_User->ID' ) ); return $this->ID;
} if ( isset( $this->data->$key ) ) { $value = $this->data->$key; } else { if (
isset( self::$back_compat_keys[ $key ] ) ) { $key = self::$back_compat_keys[
$key ]; } $value = get_user_meta( $this->ID, $key, true ); } if ( $this->filter
) { $value = sanitize_user_field( $key, $value, $this->ID, $this->filter ); }
return $value; } /** * Magic method for setting custom user fields. * * This
method does not update custom fields in the database. It only stores * the value
on the WP_User instance. * * @since 3.3.0 * * @param string $key User meta key.
* @param mixed $value User meta value. */ public function __set( $key, $value )
{ if ( 'id' === $key ) { _deprecated_argument( 'WP_User->id', '2.1.0', sprintf(
/* translators: %s: WP_User->ID */ __( 'Use %s instead.' ), 'WP_User->ID' ) );
$this->ID = $value; return; } $this->data->$key = $value; } /** * Magic method
for unsetting a certain custom field. * * @since 4.4.0 * * @param string $key
User meta key to unset. */ public function __unset( $key ) { if ( 'id' === $key
) { _deprecated_argument( 'WP_User->id', '2.1.0', sprintf( /* translators: %s:
WP_User->ID */ __( 'Use %s instead.' ), 'WP_User->ID' ) ); } if ( isset(
$this->data->$key ) ) { unset( $this->data->$key ); } if ( isset(
self::$back_compat_keys[ $key ] ) ) { unset( self::$back_compat_keys[ $key ] );
} } /** * Determines whether the user exists in the database. * * @since 3.4.0 *
* @return bool True if user exists in the database, false if not. */ public
function exists() { return ! empty( $this->ID ); } /** * Retrieves the value of
a property or meta key. * * Retrieves from the users and usermeta table. * *
@since 3.3.0 * * @param string $key Property * @return mixed */ public function
get( $key ) { return $this->__get( $key ); } /** * Determines whether a property
or meta key is set. * * Consults the users and usermeta tables. * * @since 3.3.0
* * @param string $key Property. * @return bool */ public function has_prop(
$key ) { return $this->__isset( $key ); } /** * Returns an array representation.
* * @since 3.5.0 * * @return array Array representation. */ public function
to_array() { return get_object_vars( $this->data ); } /** * Makes
private/protected methods readable for backward compatibility. * * @since 4.3.0
* * @param string $name Method to call. * @param array $arguments Arguments to
pass when calling. * @return mixed|false Return value of the callback, false
otherwise. */ public function __call( $name, $arguments ) { if ( '_init_caps'
=== $name ) { return $this->_init_caps( ...$arguments ); } return false; } /** *
Sets up capability object properties. * * Will set the value for the 'cap_key'
property to current database table * prefix, followed by 'capabilities'. Will
then check to see if the * property matching the 'cap_key' exists and is an
array. If so, it will be * used. * * @since 2.1.0 * @deprecated 4.9.0 Use
WP_User::for_site() * * @global wpdb $wpdb WordPress database abstraction
object. * * @param string $cap_key Optional capability key */ protected function
_init_caps( $cap_key = '' ) { global $wpdb; _deprecated_function( __METHOD__,
'4.9.0', 'WP_User::for_site()' ); if ( empty( $cap_key ) ) { $this->cap_key =
$wpdb->get_blog_prefix( $this->site_id ) . 'capabilities'; } else {
$this->cap_key = $cap_key; } $this->caps = $this->get_caps_data();
$this->get_role_caps(); } /** * Retrieves all of the capabilities of the user's
roles, and merges them with * individual user capabilities. * * All of the
capabilities of the user's roles are merged with the user's individual *
capabilities. This means that the user can be denied specific capabilities that
* their role might have, but the user is specifically denied. * * @since 2.0.0 *
* @return bool[] Array of key/value pairs where keys represent a capability name
* and boolean values represent whether the user has that capability. */ public
function get_role_caps() { $switch_site = false; if ( is_multisite() &&
get_current_blog_id() !== $this->site_id ) { $switch_site = true;
switch_to_blog( $this->site_id ); } $wp_roles = wp_roles(); // Filter out caps
that are not role names and assign to $this->roles. if ( is_array( $this->caps )
) { $this->roles = array_filter( array_keys( $this->caps ), array( $wp_roles,
'is_role' ) ); } // Build $allcaps from role caps, overlay user's $caps.
$this->allcaps = array(); foreach ( (array) $this->roles as $role ) { $the_role
= $wp_roles->get_role( $role ); $this->allcaps = array_merge( (array)
$this->allcaps, (array) $the_role->capabilities ); } $this->allcaps =
array_merge( (array) $this->allcaps, (array) $this->caps ); if ( $switch_site )
{ restore_current_blog(); } return $this->allcaps; } /** * Adds role to user. *
* Updates the user's meta data option with capabilities and roles. * * @since
2.0.0 * * @param string $role Role name. */ public function add_role( $role ) {
if ( empty( $role ) ) { return; } if ( in_array( $role, $this->roles, true ) ) {
return; } $this->caps[ $role ] = true; update_user_meta( $this->ID,
$this->cap_key, $this->caps ); $this->get_role_caps();
$this->update_user_level_from_caps(); /** * Fires immediately after the user has
been given a new role. * * @since 4.3.0 * * @param int $user_id The user ID. *
@param string $role The new role. */ do_action( 'add_user_role', $this->ID,
$role ); } /** * Removes role from user. * * @since 2.0.0 * * @param string
$role Role name. */ public function remove_role( $role ) { if ( ! in_array(
$role, $this->roles, true ) ) { return; } unset( $this->caps[ $role ] );
update_user_meta( $this->ID, $this->cap_key, $this->caps );
$this->get_role_caps(); $this->update_user_level_from_caps(); /** * Fires
immediately after a role as been removed from a user. * * @since 4.3.0 * *
@param int $user_id The user ID. * @param string $role The removed role. */
do_action( 'remove_user_role', $this->ID, $role ); } /** * Sets the role of the
user. * * This will remove the previous roles of the user and assign the user
the * new one. You can set the role to an empty string and it will remove all *
of the roles from the user. * * @since 2.0.0 * * @param string $role Role name.
*/ public function set_role( $role ) { if ( 1 === count( $this->roles ) &&
current( $this->roles ) === $role ) { return; } foreach ( (array) $this->roles
as $oldrole ) { unset( $this->caps[ $oldrole ] ); } $old_roles = $this->roles;
if ( ! empty( $role ) ) { $this->caps[ $role ] = true; $this->roles = array(
$role => true ); } else { $this->roles = array(); } update_user_meta( $this->ID,
$this->cap_key, $this->caps ); $this->get_role_caps();
$this->update_user_level_from_caps(); foreach ( $old_roles as $old_role ) { if (
! $old_role || $old_role === $role ) { continue; } /** This action is documented
in wp-includes/class-wp-user.php */ do_action( 'remove_user_role', $this->ID,
$old_role ); } if ( $role && ! in_array( $role, $old_roles, true ) ) { /** This
action is documented in wp-includes/class-wp-user.php */ do_action(
'add_user_role', $this->ID, $role ); } /** * Fires after the user's role has
changed. * * @since 2.9.0 * @since 3.6.0 Added $old_roles to include an array of
the user's previous roles. * * @param int $user_id The user ID. * @param string
$role The new role. * @param string[] $old_roles An array of the user's previous
roles. */ do_action( 'set_user_role', $this->ID, $role, $old_roles ); } /** *
Chooses the maximum level the user has. * * Will compare the level from the
$item parameter against the $max * parameter. If the item is incorrect, then
just the $max parameter value * will be returned. * * Used to get the max level
based on the capabilities the user has. This * is also based on roles, so if the
user is assigned the Administrator role * then the capability 'level_10' will
exist and the user will get that * value. * * @since 2.0.0 * * @param int $max
Max level of user. * @param string $item Level capability name. * @return int
Max Level. */ public function level_reduction( $max, $item ) { if ( preg_match(
'/^level_(10|[0-9])$/i', $item, $matches ) ) { $level = (int) $matches[1];
return max( $max, $level ); } else { return $max; } } /** * Updates the maximum
user level for the user. * * Updates the 'user_level' user metadata (includes
prefix that is the * database table prefix) with the maximum user level. Gets
the value from * the all of the capabilities that the user has. * * @since 2.0.0
* * @global wpdb $wpdb WordPress database abstraction object. */ public function
update_user_level_from_caps() { global $wpdb; $this->user_level = array_reduce(
array_keys( $this->allcaps ), array( $this, 'level_reduction' ), 0 );
update_user_meta( $this->ID, $wpdb->get_blog_prefix() . 'user_level',
$this->user_level ); } /** * Adds capability and grant or deny access to
capability. * * @since 2.0.0 * * @param string $cap Capability name. * @param
bool $grant Whether to grant capability to user. */ public function add_cap(
$cap, $grant = true ) { $this->caps[ $cap ] = $grant; update_user_meta(
$this->ID, $this->cap_key, $this->caps ); $this->get_role_caps();
$this->update_user_level_from_caps(); } /** * Removes capability from user. * *
@since 2.0.0 * * @param string $cap Capability name. */ public function
remove_cap( $cap ) { if ( ! isset( $this->caps[ $cap ] ) ) { return; } unset(
$this->caps[ $cap ] ); update_user_meta( $this->ID, $this->cap_key, $this->caps
); $this->get_role_caps(); $this->update_user_level_from_caps(); } /** * Removes
all of the capabilities of the user. * * @since 2.1.0 * * @global wpdb $wpdb
WordPress database abstraction object. */ public function remove_all_caps() {
global $wpdb; $this->caps = array(); delete_user_meta( $this->ID, $this->cap_key
); delete_user_meta( $this->ID, $wpdb->get_blog_prefix() . 'user_level' );
$this->get_role_caps(); } /** * Returns whether the user has the specified
capability. * * This function also accepts an ID of an object to check against
if the capability is a meta capability. Meta * capabilities such as `edit_post`
and `edit_user` are capabilities used by the `map_meta_cap()` function to * map
to primitive capabilities that a user or role has, such as `edit_posts` and
`edit_others_posts`. * * Example usage: * * $user->has_cap( 'edit_posts' ); *
$user->has_cap( 'edit_post', $post->ID ); * $user->has_cap( 'edit_post_meta',
$post->ID, $meta_key ); * * While checking against a role in place of a
capability is supported in part, this practice is discouraged as it * may
produce unreliable results. * * @since 2.0.0 * @since 5.3.0 Formalized the
existing and already documented `...$args` parameter * by adding it to the
function signature. * * @see map_meta_cap() * * @param string $cap Capability
name. * @param mixed ...$args Optional further parameters, typically starting
with an object ID. * @return bool Whether the user has the given capability, or,
if an object ID is passed, whether the user has * the given capability for that
object. */ public function has_cap( $cap, ...$args ) { if ( is_numeric( $cap ) )
{ _deprecated_argument( __FUNCTION__, '2.0.0', __( 'Usage of user levels is
deprecated. Use capabilities instead.' ) ); $cap =
$this->translate_level_to_cap( $cap ); } $caps = map_meta_cap( $cap, $this->ID,
...$args ); // Multisite super admin has all caps by definition, Unless
specifically denied. if ( is_multisite() && is_super_admin( $this->ID ) ) { if (
in_array( 'do_not_allow', $caps, true ) ) { return false; } return true; } //
Maintain BC for the argument passed to the "user_has_cap" filter. $args =
array_merge( array( $cap, $this->ID ), $args ); /** * Dynamically filter a
user's capabilities. * * @since 2.0.0 * @since 3.7.0 Added the `$user`
parameter. * * @param bool[] $allcaps Array of key/value pairs where keys
represent a capability name * and boolean values represent whether the user has
that capability. * @param string[] $caps Required primitive capabilities for the
requested capability. * @param array $args { * Arguments that accompany the
requested capability check. * * @type string $0 Requested capability. * @type
int $1 Concerned user ID. * @type mixed ...$2 Optional second and further
parameters, typically object ID. * } * @param WP_User $user The user object. */
$capabilities = apply_filters( 'user_has_cap', $this->allcaps, $caps, $args,
$this ); // Everyone is allowed to exist. $capabilities['exist'] = true; //
Nobody is allowed to do things they are not allowed to do. unset(
$capabilities['do_not_allow'] ); // Must have ALL requested caps. foreach (
(array) $caps as $cap ) { if ( empty( $capabilities[ $cap ] ) ) { return false;
} } return true; } /** * Converts numeric level to level capability name. * *
Prepends 'level_' to level number. * * @since 2.0.0 * * @param int $level Level
number, 1 to 10. * @return string */ public function translate_level_to_cap(
$level ) { return 'level_' . $level; } /** * Sets the site to operate on.
Defaults to the current site. * * @since 3.0.0 * @deprecated 4.9.0 Use
WP_User::for_site() * * @param int $blog_id Optional. Site ID, defaults to
current site. */ public function for_blog( $blog_id = '' ) {
_deprecated_function( __METHOD__, '4.9.0', 'WP_User::for_site()' );
$this->for_site( $blog_id ); } /** * Sets the site to operate on. Defaults to
the current site. * * @since 4.9.0 * * @global wpdb $wpdb WordPress database
abstraction object. * * @param int $site_id Site ID to initialize user
capabilities for. Default is the current site. */ public function for_site(
$site_id = '' ) { global $wpdb; if ( ! empty( $site_id ) ) { $this->site_id =
absint( $site_id ); } else { $this->site_id = get_current_blog_id(); }
$this->cap_key = $wpdb->get_blog_prefix( $this->site_id ) . 'capabilities';
$this->caps = $this->get_caps_data(); $this->get_role_caps(); } /** * Gets the
ID of the site for which the user's capabilities are currently initialized. * *
@since 4.9.0 * * @return int Site ID. */ public function get_site_id() { return
$this->site_id; } /** * Gets the available user capabilities data. * * @since
4.9.0 * * @return bool[] List of capabilities keyed by the capability name, *
e.g. `array( 'edit_posts' => true, 'delete_posts' => false )`. */ private
function get_caps_data() { $caps = get_user_meta( $this->ID, $this->cap_key,
true ); if ( ! is_array( $caps ) ) { return array(); } return $caps; } } /** *
WordPress Query API * * The query API attempts to get which part of WordPress
the user is on. It * also provides functionality for getting URL query
information. * * @link https://developer.wordpress.org/themes/basics/the-loop/
More information on The Loop. * * @package WordPress * @subpackage Query */ /**
* Retrieves the value of a query variable in the WP_Query class. * * @since
1.5.0 * @since 3.9.0 The `$default_value` argument was introduced. * * @global
WP_Query $wp_query WordPress Query object. * * @param string $query_var The
variable key to retrieve. * @param mixed $default_value Optional. Value to
return if the query variable is not set. * Default empty string. * @return mixed
Contents of the query variable. */ function get_query_var( $query_var,
$default_value = '' ) { global $wp_query; return $wp_query->get( $query_var,
$default_value ); } /** * Retrieves the currently queried object. * * Wrapper
for WP_Query::get_queried_object(). * * @since 3.1.0 * * @global WP_Query
$wp_query WordPress Query object. * * @return
WP_Term|WP_Post_Type|WP_Post|WP_User|null The queried object. */ function
get_queried_object() { global $wp_query; return $wp_query->get_queried_object();
} /** * Retrieves the ID of the currently queried object. * * Wrapper for
WP_Query::get_queried_object_id(). * * @since 3.1.0 * * @global WP_Query
$wp_query WordPress Query object. * * @return int ID of the queried object. */
function get_queried_object_id() { global $wp_query; return
$wp_query->get_queried_object_id(); } /** * Sets the value of a query variable
in the WP_Query class. * * @since 2.2.0 * * @global WP_Query $wp_query WordPress
Query object. * * @param string $query_var Query variable key. * @param mixed
$value Query variable value. */ function set_query_var( $query_var, $value ) {
global $wp_query; $wp_query->set( $query_var, $value ); } /** * Sets up The Loop
with query parameters. * * Note: This function will completely override the main
query and isn't intended for use * by plugins or themes. Its overly-simplistic
approach to modifying the main query can be * problematic and should be avoided
wherever possible. In most cases, there are better, * more performant options
for modifying the main query such as via the {@see 'pre_get_posts'} * action
within WP_Query. * * This must not be used within the WordPress Loop. * * @since
1.5.0 * * @global WP_Query $wp_query WordPress Query object. * * @param
array|string $query Array or string of WP_Query arguments. * @return
WP_Post[]|int[] Array of post objects or post IDs. */ function query_posts(
$query ) { $GLOBALS['wp_query'] = new WP_Query(); return
$GLOBALS['wp_query']->query( $query ); } /** * Destroys the previous query and
sets up a new query. * * This should be used after query_posts() and before
another query_posts(). * This will remove obscure bugs that occur when the
previous WP_Query object * is not destroyed properly before another is set up. *
* @since 2.3.0 * * @global WP_Query $wp_query WordPress Query object. * @global
WP_Query $wp_the_query Copy of the global WP_Query instance created during
wp_reset_query(). */ function wp_reset_query() { $GLOBALS['wp_query'] =
$GLOBALS['wp_the_query']; wp_reset_postdata(); } /** * After looping through a
separate query, this function restores * the $post global to the current post in
the main query. * * @since 3.0.0 * * @global WP_Query $wp_query WordPress Query
object. */ function wp_reset_postdata() { global $wp_query; if ( isset(
$wp_query ) ) { $wp_query->reset_postdata(); } } /* * Query type checks. */ /**
* Determines whether the query is for an existing archive page. * * Archive
pages include category, tag, author, date, custom post type, * and custom
taxonomy based archives. * * For more information on this and similar theme
functions, check out * the {@link
https://developer.wordpress.org/themes/basics/conditional-tags/ * Conditional
Tags} article in the Theme Developer Handbook. * * @since 1.5.0 * * @see
is_category() * @see is_tag() * @see is_author() * @see is_date() * @see
is_post_type_archive() * @see is_tax() * @global WP_Query $wp_query WordPress
Query object. * * @return bool Whether the query is for an existing archive
page. */ function is_archive() { global $wp_query; if ( ! isset( $wp_query ) ) {
_doing_it_wrong( __FUNCTION__, __( 'Conditional query tags do not work before
the query is run. Before then, they always return false.' ), '3.1.0' ); return
false; } return $wp_query->is_archive(); } /** * Determines whether the query is
for an existing post type archive page. * * For more information on this and
similar theme functions, check out * the {@link
https://developer.wordpress.org/themes/basics/conditional-tags/ * Conditional
Tags} article in the Theme Developer Handbook. * * @since 3.1.0 * * @global
WP_Query $wp_query WordPress Query object. * * @param string|string[]
$post_types Optional. Post type or array of posts types * to check against.
Default empty. * @return bool Whether the query is for an existing post type
archive page. */ function is_post_type_archive( $post_types = '' ) { global
$wp_query; if ( ! isset( $wp_query ) ) { _doing_it_wrong( __FUNCTION__, __(
'Conditional query tags do not work before the query is run. Before then, they
always return false.' ), '3.1.0' ); return false; } return
$wp_query->is_post_type_archive( $post_types ); } /** * Determines whether the
query is for an existing attachment page. * * For more information on this and
similar theme functions, check out * the {@link
https://developer.wordpress.org/themes/basics/conditional-tags/ * Conditional
Tags} article in the Theme Developer Handbook. * * @since 2.0.0 * * @global
WP_Query $wp_query WordPress Query object. * * @param int|string|int[]|string[]
$attachment Optional. Attachment ID, title, slug, or array of such * to check
against. Default empty. * @return bool Whether the query is for an existing
attachment page. */ function is_attachment( $attachment = '' ) { global
$wp_query; if ( ! isset( $wp_query ) ) { _doing_it_wrong( __FUNCTION__, __(
'Conditional query tags do not work before the query is run. Before then, they
always return false.' ), '3.1.0' ); return false; } return
$wp_query->is_attachment( $attachment ); } /** * Determines whether the query is
for an existing author archive page. * * If the $author parameter is specified,
this function will additionally * check if the query is for one of the authors
specified. * * For more information on this and similar theme functions, check
out * the {@link https://developer.wordpress.org/themes/basics/conditional-tags/
* Conditional Tags} article in the Theme Developer Handbook. * * @since 1.5.0 *
* @global WP_Query $wp_query WordPress Query object. * * @param
int|string|int[]|string[] $author Optional. User ID, nickname, nicename, or
array of such * to check against. Default empty. * @return bool Whether the
query is for an existing author archive page. */ function is_author( $author =
'' ) { global $wp_query; if ( ! isset( $wp_query ) ) { _doing_it_wrong(
__FUNCTION__, __( 'Conditional query tags do not work before the query is run.
Before then, they always return false.' ), '3.1.0' ); return false; } return
$wp_query->is_author( $author ); } /** * Determines whether the query is for an
existing category archive page. * * If the $category parameter is specified,
this function will additionally * check if the query is for one of the
categories specified. * * For more information on this and similar theme
functions, check out * the {@link
https://developer.wordpress.org/themes/basics/conditional-tags/ * Conditional
Tags} article in the Theme Developer Handbook. * * @since 1.5.0 * * @global
WP_Query $wp_query WordPress Query object. * * @param int|string|int[]|string[]
$category Optional. Category ID, name, slug, or array of such * to check
against. Default empty. * @return bool Whether the query is for an existing
category archive page. */ function is_category( $category = '' ) { global
$wp_query; if ( ! isset( $wp_query ) ) { _doing_it_wrong( __FUNCTION__, __(
'Conditional query tags do not work before the query is run. Before then, they
always return false.' ), '3.1.0' ); return false; } return
$wp_query->is_category( $category ); } /** * Determines whether the query is for
an existing tag archive page. * * If the $tag parameter is specified, this
function will additionally * check if the query is for one of the tags
specified. * * For more information on this and similar theme functions, check
out * the {@link https://developer.wordpress.org/themes/basics/conditional-tags/
* Conditional Tags} article in the Theme Developer Handbook. * * @since 2.3.0 *
* @global WP_Query $wp_query WordPress Query object. * * @param
int|string|int[]|string[] $tag Optional. Tag ID, name, slug, or array of such *
to check against. Default empty. * @return bool Whether the query is for an
existing tag archive page. */ function is_tag( $tag = '' ) { global $wp_query;
if ( ! isset( $wp_query ) ) { _doing_it_wrong( __FUNCTION__, __( 'Conditional
query tags do not work before the query is run. Before then, they always return
false.' ), '3.1.0' ); return false; } return $wp_query->is_tag( $tag ); } /** *
Determines whether the query is for an existing custom taxonomy archive page. *
* If the $taxonomy parameter is specified, this function will additionally *
check if the query is for that specific $taxonomy. * * If the $term parameter is
specified in addition to the $taxonomy parameter, * this function will
additionally check if the query is for one of the terms * specified. * * For
more information on this and similar theme functions, check out * the {@link
https://developer.wordpress.org/themes/basics/conditional-tags/ * Conditional
Tags} article in the Theme Developer Handbook. * * @since 2.5.0 * * @global
WP_Query $wp_query WordPress Query object. * * @param string|string[] $taxonomy
Optional. Taxonomy slug or slugs to check against. * Default empty. * @param
int|string|int[]|string[] $term Optional. Term ID, name, slug, or array of such
* to check against. Default empty. * @return bool Whether the query is for an
existing custom taxonomy archive page. * True for custom taxonomy archive pages,
false for built-in taxonomies * (category and tag archives). */ function is_tax(
$taxonomy = '', $term = '' ) { global $wp_query; if ( ! isset( $wp_query ) ) {
_doing_it_wrong( __FUNCTION__, __( 'Conditional query tags do not work before
the query is run. Before then, they always return false.' ), '3.1.0' ); return
false; } return $wp_query->is_tax( $taxonomy, $term ); } /** * Determines
whether the query is for an existing date archive. * * For more information on
this and similar theme functions, check out * the {@link
https://developer.wordpress.org/themes/basics/conditional-tags/ * Conditional
Tags} article in the Theme Developer Handbook. * * @since 1.5.0 * * @global
WP_Query $wp_query WordPress Query object. * * @return bool Whether the query is
for an existing date archive. */ function is_date() { global $wp_query; if ( !
isset( $wp_query ) ) { _doing_it_wrong( __FUNCTION__, __( 'Conditional query
tags do not work before the query is run. Before then, they always return
false.' ), '3.1.0' ); return false; } return $wp_query->is_date(); } /** *
Determines whether the query is for an existing day archive. * * A conditional
check to test whether the page is a date-based archive page displaying posts for
the current day. * * For more information on this and similar theme functions,
check out * the {@link
https://developer.wordpress.org/themes/basics/conditional-tags/ * Conditional
Tags} article in the Theme Developer Handbook. * * @since 1.5.0 * * @global
WP_Query $wp_query WordPress Query object. * * @return bool Whether the query is
for an existing day archive. */ function is_day() { global $wp_query; if ( !
isset( $wp_query ) ) { _doing_it_wrong( __FUNCTION__, __( 'Conditional query
tags do not work before the query is run. Before then, they always return
false.' ), '3.1.0' ); return false; } return $wp_query->is_day(); } /** *
Determines whether the query is for a feed. * * For more information on this and
similar theme functions, check out * the {@link
https://developer.wordpress.org/themes/basics/conditional-tags/ * Conditional
Tags} article in the Theme Developer Handbook. * * @since 1.5.0 * * @global
WP_Query $wp_query WordPress Query object. * * @param string|string[] $feeds
Optional. Feed type or array of feed types * to check against. Default empty. *
@return bool Whether the query is for a feed. */ function is_feed( $feeds = '' )
{ global $wp_query; if ( ! isset( $wp_query ) ) { _doing_it_wrong( __FUNCTION__,
__( 'Conditional query tags do not work before the query is run. Before then,
they always return false.' ), '3.1.0' ); return false; } return
$wp_query->is_feed( $feeds ); } /** * Is the query for a comments feed? * *
@since 3.0.0 * * @global WP_Query $wp_query WordPress Query object. * * @return
bool Whether the query is for a comments feed. */ function is_comment_feed() {
global $wp_query; if ( ! isset( $wp_query ) ) { _doing_it_wrong( __FUNCTION__,
__( 'Conditional query tags do not work before the query is run. Before then,
they always return false.' ), '3.1.0' ); return false; } return
$wp_query->is_comment_feed(); } /** * Determines whether the query is for the
front page of the site. * * This is for what is displayed at your site's main
URL. * * Depends on the site's "Front page displays" Reading Settings
'show_on_front' and 'page_on_front'. * * If you set a static page for the front
page of your site, this function will return * true when viewing that page. * *
Otherwise the same as {@see is_home()}. * * For more information on this and
similar theme functions, check out * the {@link
https://developer.wordpress.org/themes/basics/conditional-tags/ * Conditional
Tags} article in the Theme Developer Handbook. * * @since 2.5.0 * * @global
WP_Query $wp_query WordPress Query object. * * @return bool Whether the query is
for the front page of the site. */ function is_front_page() { global $wp_query;
if ( ! isset( $wp_query ) ) { _doing_it_wrong( __FUNCTION__, __( 'Conditional
query tags do not work before the query is run. Before then, they always return
false.' ), '3.1.0' ); return false; } return $wp_query->is_front_page(); } /** *
Determines whether the query is for the blog homepage. * * The blog homepage is
the page that shows the time-based blog content of the site. * * is_home() is
dependent on the site's "Front page displays" Reading Settings 'show_on_front' *
and 'page_for_posts'. * * If a static page is set for the front page of the
site, this function will return true only * on the page you set as the "Posts
page". * * For more information on this and similar theme functions, check out *
the {@link https://developer.wordpress.org/themes/basics/conditional-tags/ *
Conditional Tags} article in the Theme Developer Handbook. * * @since 1.5.0 * *
@see is_front_page() * @global WP_Query $wp_query WordPress Query object. * *
@return bool Whether the query is for the blog homepage. */ function is_home() {
global $wp_query; if ( ! isset( $wp_query ) ) { _doing_it_wrong( __FUNCTION__,
__( 'Conditional query tags do not work before the query is run. Before then,
they always return false.' ), '3.1.0' ); return false; } return
$wp_query->is_home(); } /** * Determines whether the query is for the Privacy
Policy page. * * The Privacy Policy page is the page that shows the Privacy
Policy content of the site. * * is_privacy_policy() is dependent on the site's
"Change your Privacy Policy page" Privacy Settings 'wp_page_for_privacy_policy'.
* * This function will return true only on the page you set as the "Privacy
Policy page". * * For more information on this and similar theme functions,
check out * the {@link
https://developer.wordpress.org/themes/basics/conditional-tags/ * Conditional
Tags} article in the Theme Developer Handbook. * * @since 5.2.0 * * @global
WP_Query $wp_query WordPress Query object. * * @return bool Whether the query is
for the Privacy Policy page. */ function is_privacy_policy() { global $wp_query;
if ( ! isset( $wp_query ) ) { _doing_it_wrong( __FUNCTION__, __( 'Conditional
query tags do not work before the query is run. Before then, they always return
false.' ), '3.1.0' ); return false; } return $wp_query->is_privacy_policy(); }
/** * Determines whether the query is for an existing month archive. * * For
more information on this and similar theme functions, check out * the {@link
https://developer.wordpress.org/themes/basics/conditional-tags/ * Conditional
Tags} article in the Theme Developer Handbook. * * @since 1.5.0 * * @global
WP_Query $wp_query WordPress Query object. * * @return bool Whether the query is
for an existing month archive. */ function is_month() { global $wp_query; if ( !
isset( $wp_query ) ) { _doing_it_wrong( __FUNCTION__, __( 'Conditional query
tags do not work before the query is run. Before then, they always return
false.' ), '3.1.0' ); return false; } return $wp_query->is_month(); } /** *
Determines whether the query is for an existing single page. * * If the $page
parameter is specified, this function will additionally * check if the query is
for one of the pages specified. * * For more information on this and similar
theme functions, check out * the {@link
https://developer.wordpress.org/themes/basics/conditional-tags/ * Conditional
Tags} article in the Theme Developer Handbook. * * @since 1.5.0 * * @see
is_single() * @see is_singular() * @global WP_Query $wp_query WordPress Query
object. * * @param int|string|int[]|string[] $page Optional. Page ID, title,
slug, or array of such * to check against. Default empty. * @return bool Whether
the query is for an existing single page. */ function is_page( $page = '' ) {
global $wp_query; if ( ! isset( $wp_query ) ) { _doing_it_wrong( __FUNCTION__,
__( 'Conditional query tags do not work before the query is run. Before then,
they always return false.' ), '3.1.0' ); return false; } return
$wp_query->is_page( $page ); } /** * Determines whether the query is for a paged
result and not for the first page. * * For more information on this and similar
theme functions, check out * the {@link
https://developer.wordpress.org/themes/basics/conditional-tags/ * Conditional
Tags} article in the Theme Developer Handbook. * * @since 1.5.0 * * @global
WP_Query $wp_query WordPress Query object. * * @return bool Whether the query is
for a paged result. */ function is_paged() { global $wp_query; if ( ! isset(
$wp_query ) ) { _doing_it_wrong( __FUNCTION__, __( 'Conditional query tags do
not work before the query is run. Before then, they always return false.' ),
'3.1.0' ); return false; } return $wp_query->is_paged(); } /** * Determines
whether the query is for a post or page preview. * * For more information on
this and similar theme functions, check out * the {@link
https://developer.wordpress.org/themes/basics/conditional-tags/ * Conditional
Tags} article in the Theme Developer Handbook. * * @since 2.0.0 * * @global
WP_Query $wp_query WordPress Query object. * * @return bool Whether the query is
for a post or page preview. */ function is_preview() { global $wp_query; if ( !
isset( $wp_query ) ) { _doing_it_wrong( __FUNCTION__, __( 'Conditional query
tags do not work before the query is run. Before then, they always return
false.' ), '3.1.0' ); return false; } return $wp_query->is_preview(); } /** * Is
the query for the robots.txt file? * * @since 2.1.0 * * @global WP_Query
$wp_query WordPress Query object. * * @return bool Whether the query is for the
robots.txt file. */ function is_robots() { global $wp_query; if ( ! isset(
$wp_query ) ) { _doing_it_wrong( __FUNCTION__, __( 'Conditional query tags do
not work before the query is run. Before then, they always return false.' ),
'3.1.0' ); return false; } return $wp_query->is_robots(); } /** * Is the query
for the favicon.ico file? * * @since 5.4.0 * * @global WP_Query $wp_query
WordPress Query object. * * @return bool Whether the query is for the
favicon.ico file. */ function is_favicon() { global $wp_query; if ( ! isset(
$wp_query ) ) { _doing_it_wrong( __FUNCTION__, __( 'Conditional query tags do
not work before the query is run. Before then, they always return false.' ),
'3.1.0' ); return false; } return $wp_query->is_favicon(); } /** * Determines
whether the query is for a search. * * For more information on this and similar
theme functions, check out * the {@link
https://developer.wordpress.org/themes/basics/conditional-tags/ * Conditional
Tags} article in the Theme Developer Handbook. * * @since 1.5.0 * * @global
WP_Query $wp_query WordPress Query object. * * @return bool Whether the query is
for a search. */ function is_search() { global $wp_query; if ( ! isset(
$wp_query ) ) { _doing_it_wrong( __FUNCTION__, __( 'Conditional query tags do
not work before the query is run. Before then, they always return false.' ),
'3.1.0' ); return false; } return $wp_query->is_search(); } /** * Determines
whether the query is for an existing single post. * * Works for any post type,
except attachments and pages * * If the $post parameter is specified, this
function will additionally * check if the query is for one of the Posts
specified. * * For more information on this and similar theme functions, check
out * the {@link https://developer.wordpress.org/themes/basics/conditional-tags/
* Conditional Tags} article in the Theme Developer Handbook. * * @since 1.5.0 *
* @see is_page() * @see is_singular() * @global WP_Query $wp_query WordPress
Query object. * * @param int|string|int[]|string[] $post Optional. Post ID,
title, slug, or array of such * to check against. Default empty. * @return bool
Whether the query is for an existing single post. */ function is_single( $post =
'' ) { global $wp_query; if ( ! isset( $wp_query ) ) { _doing_it_wrong(
__FUNCTION__, __( 'Conditional query tags do not work before the query is run.
Before then, they always return false.' ), '3.1.0' ); return false; } return
$wp_query->is_single( $post ); } /** * Determines whether the query is for an
existing single post of any post type * (post, attachment, page, custom post
types). * * If the $post_types parameter is specified, this function will
additionally * check if the query is for one of the Posts Types specified. * *
For more information on this and similar theme functions, check out * the {@link
https://developer.wordpress.org/themes/basics/conditional-tags/ * Conditional
Tags} article in the Theme Developer Handbook. * * @since 1.5.0 * * @see
is_page() * @see is_single() * @global WP_Query $wp_query WordPress Query
object. * * @param string|string[] $post_types Optional. Post type or array of
post types * to check against. Default empty. * @return bool Whether the query
is for an existing single post * or any of the given post types. */ function
is_singular( $post_types = '' ) { global $wp_query; if ( ! isset( $wp_query ) )
{ _doing_it_wrong( __FUNCTION__, __( 'Conditional query tags do not work before
the query is run. Before then, they always return false.' ), '3.1.0' ); return
false; } return $wp_query->is_singular( $post_types ); } /** * Determines
whether the query is for a specific time. * * For more information on this and
similar theme functions, check out * the {@link
https://developer.wordpress.org/themes/basics/conditional-tags/ * Conditional
Tags} article in the Theme Developer Handbook. * * @since 1.5.0 * * @global
WP_Query $wp_query WordPress Query object. * * @return bool Whether the query is
for a specific time. */ function is_time() { global $wp_query; if ( ! isset(
$wp_query ) ) { _doing_it_wrong( __FUNCTION__, __( 'Conditional query tags do
not work before the query is run. Before then, they always return false.' ),
'3.1.0' ); return false; } return $wp_query->is_time(); } /** * Determines
whether the query is for a trackback endpoint call. * * For more information on
this and similar theme functions, check out * the {@link
https://developer.wordpress.org/themes/basics/conditional-tags/ * Conditional
Tags} article in the Theme Developer Handbook. * * @since 1.5.0 * * @global
WP_Query $wp_query WordPress Query object. * * @return bool Whether the query is
for a trackback endpoint call. */ function is_trackback() { global $wp_query; if
( ! isset( $wp_query ) ) { _doing_it_wrong( __FUNCTION__, __( 'Conditional query
tags do not work before the query is run. Before then, they always return
false.' ), '3.1.0' ); return false; } return $wp_query->is_trackback(); } /** *
Determines whether the query is for an existing year archive. * * For more
information on this and similar theme functions, check out * the {@link
https://developer.wordpress.org/themes/basics/conditional-tags/ * Conditional
Tags} article in the Theme Developer Handbook. * * @since 1.5.0 * * @global
WP_Query $wp_query WordPress Query object. * * @return bool Whether the query is
for an existing year archive. */ function is_year() { global $wp_query; if ( !
isset( $wp_query ) ) { _doing_it_wrong( __FUNCTION__, __( 'Conditional query
tags do not work before the query is run. Before then, they always return
false.' ), '3.1.0' ); return false; } return $wp_query->is_year(); } /** *
Determines whether the query has resulted in a 404 (returns no results). * * For
more information on this and similar theme functions, check out * the {@link
https://developer.wordpress.org/themes/basics/conditional-tags/ * Conditional
Tags} article in the Theme Developer Handbook. * * @since 1.5.0 * * @global
WP_Query $wp_query WordPress Query object. * * @return bool Whether the query is
a 404 error. */ function is_404() { global $wp_query; if ( ! isset( $wp_query )
) { _doing_it_wrong( __FUNCTION__, __( 'Conditional query tags do not work
before the query is run. Before then, they always return false.' ), '3.1.0' );
return false; } return $wp_query->is_404(); } /** * Is the query for an embedded
post? * * @since 4.4.0 * * @global WP_Query $wp_query WordPress Query object. *
* @return bool Whether the query is for an embedded post. */ function is_embed()
{ global $wp_query; if ( ! isset( $wp_query ) ) { _doing_it_wrong( __FUNCTION__,
__( 'Conditional query tags do not work before the query is run. Before then,
they always return false.' ), '3.1.0' ); return false; } return
$wp_query->is_embed(); } /** * Determines whether the query is the main query. *
* For more information on this and similar theme functions, check out * the
{@link https://developer.wordpress.org/themes/basics/conditional-tags/ *
Conditional Tags} article in the Theme Developer Handbook. * * @since 3.3.0 * *
@global WP_Query $wp_query WordPress Query object. * * @return bool Whether the
query is the main query. */ function is_main_query() { global $wp_query; if ( !
isset( $wp_query ) ) { _doing_it_wrong( __FUNCTION__, __( 'Conditional query
tags do not work before the query is run. Before then, they always return
false.' ), '6.1.0' ); return false; } if ( 'pre_get_posts' === current_filter()
) { _doing_it_wrong( __FUNCTION__, sprintf( /* translators: 1: pre_get_posts, 2:
WP_Query->is_main_query(), 3: is_main_query(), 4: Documentation URL. */ __( 'In
%1$s, use the %2$s method, not the %3$s function. See %4$s.' ), 'pre_get_posts',
'WP_Query->is_main_query()', 'is_main_query()', __(
'https://developer.wordpress.org/reference/functions/is_main_query/' ) ),
'3.7.0' ); } return $wp_query->is_main_query(); } /* * The Loop. Post loop
control. */ /** * Determines whether current WordPress query has posts to loop
over. * * @since 1.5.0 * * @global WP_Query $wp_query WordPress Query object. *
* @return bool True if posts are available, false if end of the loop. */
function have_posts() { global $wp_query; if ( ! isset( $wp_query ) ) { return
false; } return $wp_query->have_posts(); } /** * Determines whether the caller
is in the Loop. * * For more information on this and similar theme functions,
check out * the {@link
https://developer.wordpress.org/themes/basics/conditional-tags/ * Conditional
Tags} article in the Theme Developer Handbook. * * @since 2.0.0 * * @global
WP_Query $wp_query WordPress Query object. * * @return bool True if caller is
within loop, false if loop hasn't started or ended. */ function in_the_loop() {
global $wp_query; if ( ! isset( $wp_query ) ) { return false; } return
$wp_query->in_the_loop; } /** * Rewind the loop posts. * * @since 1.5.0 * *
@global WP_Query $wp_query WordPress Query object. */ function rewind_posts() {
global $wp_query; if ( ! isset( $wp_query ) ) { return; }
$wp_query->rewind_posts(); } /** * Iterate the post index in the loop. * *
@since 1.5.0 * * @global WP_Query $wp_query WordPress Query object. */ function
the_post() { global $wp_query; if ( ! isset( $wp_query ) ) { return; }
$wp_query->the_post(); } /* * Comments loop. */ /** * Determines whether current
WordPress query has comments to loop over. * * @since 2.2.0 * * @global WP_Query
$wp_query WordPress Query object. * * @return bool True if comments are
available, false if no more comments. */ function have_comments() { global
$wp_query; if ( ! isset( $wp_query ) ) { return false; } return
$wp_query->have_comments(); } /** * Iterate comment index in the comment loop. *
* @since 2.2.0 * * @global WP_Query $wp_query WordPress Query object. */
function the_comment() { global $wp_query; if ( ! isset( $wp_query ) ) { return;
} $wp_query->the_comment(); } /** * Redirect old slugs to the correct permalink.
* * Attempts to find the current slug from the past slugs. * * @since 2.1.0 */
function wp_old_slug_redirect() { if ( is_404() && '' !== get_query_var( 'name'
) ) { // Guess the current post type based on the query vars. if (
get_query_var( 'post_type' ) ) { $post_type = get_query_var( 'post_type' ); }
elseif ( get_query_var( 'attachment' ) ) { $post_type = 'attachment'; } elseif (
get_query_var( 'pagename' ) ) { $post_type = 'page'; } else { $post_type =
'post'; } if ( is_array( $post_type ) ) { if ( count( $post_type ) > 1 ) {
return; } $post_type = reset( $post_type ); } // Do not attempt redirect for
hierarchical post types. if ( is_post_type_hierarchical( $post_type ) ) {
return; } $id = _find_post_by_old_slug( $post_type ); if ( ! $id ) { $id =
_find_post_by_old_date( $post_type ); } /** * Filters the old slug redirect post
ID. * * @since 4.9.3 * * @param int $id The redirect post ID. */ $id =
apply_filters( 'old_slug_redirect_post_id', $id ); if ( ! $id ) { return; }
$link = get_permalink( $id ); if ( get_query_var( 'paged' ) > 1 ) { $link =
user_trailingslashit( trailingslashit( $link ) . 'page/' . get_query_var(
'paged' ) ); } elseif ( is_embed() ) { $link = user_trailingslashit(
trailingslashit( $link ) . 'embed' ); } /** * Filters the old slug redirect URL.
* * @since 4.4.0 * * @param string $link The redirect URL. */ $link =
apply_filters( 'old_slug_redirect_url', $link ); if ( ! $link ) { return; }
wp_redirect( $link, 301 ); // Permanent redirect. exit; } } /** * Find the post
ID for redirecting an old slug. * * @since 4.9.3 * @access private * * @see
wp_old_slug_redirect() * @global wpdb $wpdb WordPress database abstraction
object. * * @param string $post_type The current post type based on the query
vars. * @return int The Post ID. */ function _find_post_by_old_slug( $post_type
) { global $wpdb; $query = $wpdb->prepare( "SELECT post_id FROM $wpdb->postmeta,
$wpdb->posts WHERE ID = post_id AND post_type = %s AND meta_key = '_wp_old_slug'
AND meta_value = %s", $post_type, get_query_var( 'name' ) ); /* * If year,
monthnum, or day have been specified, make our query more precise * just in case
there are multiple identical _wp_old_slug values. */ if ( get_query_var( 'year'
) ) { $query .= $wpdb->prepare( ' AND YEAR(post_date) = %d', get_query_var(
'year' ) ); } if ( get_query_var( 'monthnum' ) ) { $query .= $wpdb->prepare( '
AND MONTH(post_date) = %d', get_query_var( 'monthnum' ) ); } if ( get_query_var(
'day' ) ) { $query .= $wpdb->prepare( ' AND DAYOFMONTH(post_date) = %d',
get_query_var( 'day' ) ); } $key = md5( $query ); $last_changed =
wp_cache_get_last_changed( 'posts' ); $cache_key =
"find_post_by_old_slug:$key:$last_changed"; $cache = wp_cache_get( $cache_key,
'post-queries' ); if ( false !== $cache ) { $id = $cache; } else { $id = (int)
$wpdb->get_var( $query ); wp_cache_set( $cache_key, $id, 'post-queries' ); }
return $id; } /** * Find the post ID for redirecting an old date. * * @since
4.9.3 * @access private * * @see wp_old_slug_redirect() * @global wpdb $wpdb
WordPress database abstraction object. * * @param string $post_type The current
post type based on the query vars. * @return int The Post ID. */ function
_find_post_by_old_date( $post_type ) { global $wpdb; $date_query = ''; if (
get_query_var( 'year' ) ) { $date_query .= $wpdb->prepare( ' AND
YEAR(pm_date.meta_value) = %d', get_query_var( 'year' ) ); } if ( get_query_var(
'monthnum' ) ) { $date_query .= $wpdb->prepare( ' AND MONTH(pm_date.meta_value)
= %d', get_query_var( 'monthnum' ) ); } if ( get_query_var( 'day' ) ) {
$date_query .= $wpdb->prepare( ' AND DAYOFMONTH(pm_date.meta_value) = %d',
get_query_var( 'day' ) ); } $id = 0; if ( $date_query ) { $query =
$wpdb->prepare( "SELECT post_id FROM $wpdb->postmeta AS pm_date, $wpdb->posts
WHERE ID = post_id AND post_type = %s AND meta_key = '_wp_old_date' AND
post_name = %s" . $date_query, $post_type, get_query_var( 'name' ) ); $key =
md5( $query ); $last_changed = wp_cache_get_last_changed( 'posts' ); $cache_key
= "find_post_by_old_date:$key:$last_changed"; $cache = wp_cache_get( $cache_key,
'post-queries' ); if ( false !== $cache ) { $id = $cache; } else { $id = (int)
$wpdb->get_var( $query ); if ( ! $id ) { // Check to see if an old slug matches
the old date. $id = (int) $wpdb->get_var( $wpdb->prepare( "SELECT ID FROM
$wpdb->posts, $wpdb->postmeta AS pm_slug, $wpdb->postmeta AS pm_date WHERE ID =
pm_slug.post_id AND ID = pm_date.post_id AND post_type = %s AND pm_slug.meta_key
= '_wp_old_slug' AND pm_slug.meta_value = %s AND pm_date.meta_key =
'_wp_old_date'" . $date_query, $post_type, get_query_var( 'name' ) ) ); }
wp_cache_set( $cache_key, $id, 'post-queries' ); } } return $id; } /** * Set up
global post data. * * @since 1.5.0 * @since 4.4.0 Added the ability to pass a
post ID to `$post`. * * @global WP_Query $wp_query WordPress Query object. * *
@param WP_Post|object|int $post WP_Post instance or Post ID/object. * @return
bool True when finished. */ function setup_postdata( $post ) { global $wp_query;
if ( ! empty( $wp_query ) && $wp_query instanceof WP_Query ) { return
$wp_query->setup_postdata( $post ); } return false; } /** * Generates post data.
* * @since 5.2.0 * * @global WP_Query $wp_query WordPress Query object. * *
@param WP_Post|object|int $post WP_Post instance or Post ID/object. * @return
array|false Elements of post, or false on failure. */ function
generate_postdata( $post ) { global $wp_query; if ( ! empty( $wp_query ) &&
$wp_query instanceof WP_Query ) { return $wp_query->generate_postdata( $post );
} return false; } /** * Class for generating SQL clauses that filter a primary
query according to date. * * WP_Date_Query is a helper that allows primary query
classes, such as WP_Query, to filter * their results by date columns, by
generating `WHERE` subclauses to be attached to the * primary SQL query string.
* * Attempting to filter by an invalid date value (eg month=13) will generate
SQL that will * return no results. In these cases, a _doing_it_wrong() error
notice is also thrown. * See WP_Date_Query::validate_date_values(). * * @link
https://developer.wordpress.org/reference/classes/wp_query/ * * @since 3.7.0 */
#[AllowDynamicProperties] class WP_Date_Query { /** * Array of date queries. * *
See WP_Date_Query::__construct() for information on date query arguments. * *
@since 3.7.0 * @var array */ public $queries = array(); /** * The default
relation between top-level queries. Can be either 'AND' or 'OR'. * * @since
3.7.0 * @var string */ public $relation = 'AND'; /** * The column to query
against. Can be changed via the query arguments. * * @since 3.7.0 * @var string
*/ public $column = 'post_date'; /** * The value comparison operator. Can be
changed via the query arguments. * * @since 3.7.0 * @var string */ public
$compare = '='; /** * Supported time-related parameter keys. * * @since 4.1.0 *
@var string[] */ public $time_keys = array( 'after', 'before', 'year', 'month',
'monthnum', 'week', 'w', 'dayofyear', 'day', 'dayofweek', 'dayofweek_iso',
'hour', 'minute', 'second' ); /** * Constructor. * * Time-related parameters
that normally require integer values ('year', 'month', 'week', 'dayofyear',
'day', * 'dayofweek', 'dayofweek_iso', 'hour', 'minute', 'second') accept arrays
of integers for some values of * 'compare'. When 'compare' is 'IN' or 'NOT IN',
arrays are accepted; when 'compare' is 'BETWEEN' or 'NOT * BETWEEN', arrays of
two valid values are required. See individual argument descriptions for accepted
values. * * @since 3.7.0 * @since 4.0.0 The $inclusive logic was updated to
include all times within the date range. * @since 4.1.0 Introduced
'dayofweek_iso' time type parameter. * * @param array $date_query { * Array of
date query clauses. * * @type array ...$0 { * @type string $column Optional. The
column to query against. If undefined, inherits the value of * the
`$default_column` parameter. See WP_Date_Query::validate_column() and * the
{@see 'date_query_valid_columns'} filter for the list of accepted values. *
Default 'post_date'. * @type string $compare Optional. The comparison operator.
Accepts '=', '!=', '>', '>=', '<', '<=', * 'IN', 'NOT IN', 'BETWEEN', 'NOT
BETWEEN'. Default '='. * @type string $relation Optional. The boolean
relationship between the date queries. Accepts 'OR' or 'AND'. * Default 'OR'. *
@type array ...$0 { * Optional. An array of first-order clause parameters, or
another fully-formed date query. * * @type string|array $before { * Optional.
Date to retrieve posts before. Accepts `strtotime()`-compatible string, * or
array of 'year', 'month', 'day' values. * * @type string $year The four-digit
year. Default empty. Accepts any four-digit year. * @type string $month Optional
when passing array.The month of the year. * Default (string:empty)|(array:1).
Accepts numbers 1-12. * @type string $day Optional when passing array.The day of
the month. * Default (string:empty)|(array:1). Accepts numbers 1-31. * } * @type
string|array $after { * Optional. Date to retrieve posts after. Accepts
`strtotime()`-compatible string, * or array of 'year', 'month', 'day' values. *
* @type string $year The four-digit year. Accepts any four-digit year. Default
empty. * @type string $month Optional when passing array. The month of the year.
Accepts numbers 1-12. * Default (string:empty)|(array:12). * @type string $day
Optional when passing array.The day of the month. Accepts numbers 1-31. *
Default (string:empty)|(array:last day of month). * } * @type string $column
Optional. Used to add a clause comparing a column other than * the column
specified in the top-level `$column` parameter. * See
WP_Date_Query::validate_column() and * the {@see 'date_query_valid_columns'}
filter for the list * of accepted values. Default is the value of top-level
`$column`. * @type string $compare Optional. The comparison operator. Accepts
'=', '!=', '>', '>=', * '<', '<=', 'IN', 'NOT IN', 'BETWEEN', 'NOT BETWEEN'.
'IN', * 'NOT IN', 'BETWEEN', and 'NOT BETWEEN'. Comparisons support * arrays in
some time-related parameters. Default '='. * @type bool $inclusive Optional.
Include results from dates specified in 'before' or * 'after'. Default false. *
@type int|int[] $year Optional. The four-digit year number. Accepts any
four-digit year * or an array of years if `$compare` supports it. Default empty.
* @type int|int[] $month Optional. The two-digit month number. Accepts numbers
1-12 or an * array of valid numbers if `$compare` supports it. Default empty. *
@type int|int[] $week Optional. The week number of the year. Accepts numbers
0-53 or an * array of valid numbers if `$compare` supports it. Default empty. *
@type int|int[] $dayofyear Optional. The day number of the year. Accepts numbers
1-366 or an * array of valid numbers if `$compare` supports it. * @type
int|int[] $day Optional. The day of the month. Accepts numbers 1-31 or an array
* of valid numbers if `$compare` supports it. Default empty. * @type int|int[]
$dayofweek Optional. The day number of the week. Accepts numbers 1-7 (1 is *
Sunday) or an array of valid numbers if `$compare` supports it. * Default empty.
* @type int|int[] $dayofweek_iso Optional. The day number of the week (ISO).
Accepts numbers 1-7 * (1 is Monday) or an array of valid numbers if `$compare`
supports it. * Default empty. * @type int|int[] $hour Optional. The hour of the
day. Accepts numbers 0-23 or an array * of valid numbers if `$compare` supports
it. Default empty. * @type int|int[] $minute Optional. The minute of the hour.
Accepts numbers 0-59 or an array * of valid numbers if `$compare` supports it.
Default empty. * @type int|int[] $second Optional. The second of the minute.
Accepts numbers 0-59 or an * array of valid numbers if `$compare` supports it.
Default empty. * } * } * } * @param string $default_column Optional. Default
column to query against. See WP_Date_Query::validate_column() * and the {@see
'date_query_valid_columns'} filter for the list of accepted values. * Default
'post_date'. */ public function __construct( $date_query, $default_column =
'post_date' ) { if ( empty( $date_query ) || ! is_array( $date_query ) ) {
return; } if ( isset( $date_query['relation'] ) ) { $this->relation =
$this->sanitize_relation( $date_query['relation'] ); } else { $this->relation =
'AND'; } // Support for passing time-based keys in the top level of the
$date_query array. if ( ! isset( $date_query[0] ) ) { $date_query = array(
$date_query ); } if ( ! empty( $date_query['column'] ) ) { $date_query['column']
= esc_sql( $date_query['column'] ); } else { $date_query['column'] = esc_sql(
$default_column ); } $this->column = $this->validate_column( $this->column );
$this->compare = $this->get_compare( $date_query ); $this->queries =
$this->sanitize_query( $date_query ); } /** * Recursive-friendly query
sanitizer. * * Ensures that each query-level clause has a 'relation' key, and
that * each first-order clause contains all the necessary keys from `$defaults`.
* * @since 4.1.0 * * @param array $queries * @param array $parent_query *
@return array Sanitized queries. */ public function sanitize_query( $queries,
$parent_query = null ) { $cleaned_query = array(); $defaults = array( 'column'
=> 'post_date', 'compare' => '=', 'relation' => 'AND', ); // Numeric keys should
always have array values. foreach ( $queries as $qkey => $qvalue ) { if (
is_numeric( $qkey ) && ! is_array( $qvalue ) ) { unset( $queries[ $qkey ] ); } }
// Each query should have a value for each default key. Inherit from the parent
when possible. foreach ( $defaults as $dkey => $dvalue ) { if ( isset( $queries[
$dkey ] ) ) { continue; } if ( isset( $parent_query[ $dkey ] ) ) { $queries[
$dkey ] = $parent_query[ $dkey ]; } else { $queries[ $dkey ] = $dvalue; } } //
Validate the dates passed in the query. if ( $this->is_first_order_clause(
$queries ) ) { $this->validate_date_values( $queries ); } // Sanitize the
relation parameter. $queries['relation'] = $this->sanitize_relation(
$queries['relation'] ); foreach ( $queries as $key => $q ) { if ( ! is_array( $q
) || in_array( $key, $this->time_keys, true ) ) { // This is a first-order
query. Trust the values and sanitize when building SQL. $cleaned_query[ $key ] =
$q; } else { // Any array without a time key is another query, so we recurse.
$cleaned_query[] = $this->sanitize_query( $q, $queries ); } } return
$cleaned_query; } /** * Determines whether this is a first-order clause. * *
Checks to see if the current clause has any time-related keys. * If so, it's
first-order. * * @since 4.1.0 * * @param array $query Query clause. * @return
bool True if this is a first-order clause. */ protected function
is_first_order_clause( $query ) { $time_keys = array_intersect(
$this->time_keys, array_keys( $query ) ); return ! empty( $time_keys ); } /** *
Determines and validates what comparison operator to use. * * @since 3.7.0 * *
@param array $query A date query or a date subquery. * @return string The
comparison operator. */ public function get_compare( $query ) { if ( ! empty(
$query['compare'] ) && in_array( $query['compare'], array( '=', '!=', '>', '>=',
'<', '<=', 'IN', 'NOT IN', 'BETWEEN', 'NOT BETWEEN' ), true ) ) { return
strtoupper( $query['compare'] ); } return $this->compare; } /** * Validates the
given date_query values and triggers errors if something is not valid. * * Note
that date queries with invalid date ranges are allowed to * continue (though of
course no items will be found for impossible dates). * This method only
generates debug notices for these cases. * * @since 4.1.0 * * @param array
$date_query The date_query array. * @return bool True if all values in the query
are valid, false if one or more fail. */ public function validate_date_values(
$date_query = array() ) { if ( empty( $date_query ) ) { return false; } $valid =
true; /* * Validate 'before' and 'after' up front, then let the * validation
routine continue to be sure that all invalid * values generate errors too. */ if
( array_key_exists( 'before', $date_query ) && is_array( $date_query['before'] )
) { $valid = $this->validate_date_values( $date_query['before'] ); } if (
array_key_exists( 'after', $date_query ) && is_array( $date_query['after'] ) ) {
$valid = $this->validate_date_values( $date_query['after'] ); } // Array
containing all min-max checks. $min_max_checks = array(); // Days per year. if (
array_key_exists( 'year', $date_query ) ) { /* * If a year exists in the date
query, we can use it to get the days. * If multiple years are provided (as in a
BETWEEN), use the first one. */ if ( is_array( $date_query['year'] ) ) { $_year
= reset( $date_query['year'] ); } else { $_year = $date_query['year']; }
$max_days_of_year = gmdate( 'z', mktime( 0, 0, 0, 12, 31, $_year ) ) + 1; } else
{ // Otherwise we use the max of 366 (leap-year). $max_days_of_year = 366; }
$min_max_checks['dayofyear'] = array( 'min' => 1, 'max' => $max_days_of_year, );
// Days per week. $min_max_checks['dayofweek'] = array( 'min' => 1, 'max' => 7,
); // Days per week. $min_max_checks['dayofweek_iso'] = array( 'min' => 1, 'max'
=> 7, ); // Months per year. $min_max_checks['month'] = array( 'min' => 1, 'max'
=> 12, ); // Weeks per year. if ( isset( $_year ) ) { /* * If we have a specific
year, use it to calculate number of weeks. * Note: the number of weeks in a year
is the date in which Dec 28 appears. */ $week_count = gmdate( 'W', mktime( 0, 0,
0, 12, 28, $_year ) ); } else { // Otherwise set the week-count to a maximum of
53. $week_count = 53; } $min_max_checks['week'] = array( 'min' => 1, 'max' =>
$week_count, ); // Days per month. $min_max_checks['day'] = array( 'min' => 1,
'max' => 31, ); // Hours per day. $min_max_checks['hour'] = array( 'min' => 0,
'max' => 23, ); // Minutes per hour. $min_max_checks['minute'] = array( 'min' =>
0, 'max' => 59, ); // Seconds per minute. $min_max_checks['second'] = array(
'min' => 0, 'max' => 59, ); // Concatenate and throw a notice for each invalid
value. foreach ( $min_max_checks as $key => $check ) { if ( ! array_key_exists(
$key, $date_query ) ) { continue; } // Throw a notice for each failing value.
foreach ( (array) $date_query[ $key ] as $_value ) { $is_between = $_value >=
$check['min'] && $_value <= $check['max']; if ( ! is_numeric( $_value ) || !
$is_between ) { $error = sprintf( /* translators: Date query invalid date
message. 1: Invalid value, 2: Type of value, 3: Minimum valid value, 4: Maximum
valid value. */ __( 'Invalid value %1$s for %2$s. Expected value should be
between %3$s and %4$s.' ), '' . esc_html( $_value ) . '', '' . esc_html( $key )
. '', '' . esc_html( $check['min'] ) . '', '' . esc_html( $check['max'] ) . ''
); _doing_it_wrong( __CLASS__, $error, '4.1.0' ); $valid = false; } } } // If we
already have invalid date messages, don't bother running through checkdate(). if
( ! $valid ) { return $valid; } $day_month_year_error_msg = ''; $day_exists =
array_key_exists( 'day', $date_query ) && is_numeric( $date_query['day'] );
$month_exists = array_key_exists( 'month', $date_query ) && is_numeric(
$date_query['month'] ); $year_exists = array_key_exists( 'year', $date_query )
&& is_numeric( $date_query['year'] ); if ( $day_exists && $month_exists &&
$year_exists ) { // 1. Checking day, month, year combination. if ( !
wp_checkdate( $date_query['month'], $date_query['day'], $date_query['year'],
sprintf( '%s-%s-%s', $date_query['year'], $date_query['month'],
$date_query['day'] ) ) ) { $day_month_year_error_msg = sprintf( /* translators:
1: Year, 2: Month, 3: Day of month. */ __( 'The following values do not describe
a valid date: year %1$s, month %2$s, day %3$s.' ), '' . esc_html(
$date_query['year'] ) . '', '' . esc_html( $date_query['month'] ) . '', '' .
esc_html( $date_query['day'] ) . '' ); $valid = false; } } elseif ( $day_exists
&& $month_exists ) { /* * 2. checking day, month combination * We use 2012
because, as a leap year, it's the most permissive. */ if ( ! wp_checkdate(
$date_query['month'], $date_query['day'], 2012, sprintf( '2012-%s-%s',
$date_query['month'], $date_query['day'] ) ) ) { $day_month_year_error_msg =
sprintf( /* translators: 1: Month, 2: Day of month. */ __( 'The following values
do not describe a valid date: month %1$s, day %2$s.' ), '' . esc_html(
$date_query['month'] ) . '', '' . esc_html( $date_query['day'] ) . '' ); $valid
= false; } } if ( ! empty( $day_month_year_error_msg ) ) { _doing_it_wrong(
__CLASS__, $day_month_year_error_msg, '4.1.0' ); } return $valid; } /** *
Validates a column name parameter. * * Column names without a table prefix (like
'post_date') are checked against a list of * allowed and known tables, and then,
if found, have a table prefix (such as 'wp_posts.') * prepended. Prefixed column
names (such as 'wp_posts.post_date') bypass this allowed * check, and are only
sanitized to remove illegal characters. * * @since 3.7.0 * * @global wpdb $wpdb
WordPress database abstraction object. * * @param string $column The
user-supplied column name. * @return string A validated column name value. */
public function validate_column( $column ) { global $wpdb; $valid_columns =
array( 'post_date', 'post_date_gmt', 'post_modified', 'post_modified_gmt',
'comment_date', 'comment_date_gmt', 'user_registered', 'registered',
'last_updated', ); // Attempt to detect a table prefix. if ( ! str_contains(
$column, '.' ) ) { /** * Filters the list of valid date query columns. * *
@since 3.7.0 * @since 4.1.0 Added 'user_registered' to the default recognized
columns. * @since 4.6.0 Added 'registered' and 'last_updated' to the default
recognized columns. * * @param string[] $valid_columns An array of valid date
query columns. Defaults * are 'post_date', 'post_date_gmt', 'post_modified', *
'post_modified_gmt', 'comment_date', 'comment_date_gmt', * 'user_registered',
'registered', 'last_updated'. */ if ( ! in_array( $column, apply_filters(
'date_query_valid_columns', $valid_columns ), true ) ) { $column = 'post_date';
} $known_columns = array( $wpdb->posts => array( 'post_date', 'post_date_gmt',
'post_modified', 'post_modified_gmt', ), $wpdb->comments => array(
'comment_date', 'comment_date_gmt', ), $wpdb->users => array( 'user_registered',
), $wpdb->blogs => array( 'registered', 'last_updated', ), ); // If it's a known
column name, add the appropriate table prefix. foreach ( $known_columns as
$table_name => $table_columns ) { if ( in_array( $column, $table_columns, true )
) { $column = $table_name . '.' . $column; break; } } } // Remove unsafe
characters. return preg_replace( '/[^a-zA-Z0-9_$\.]/', '', $column ); } /** *
Generates WHERE clause to be appended to a main query. * * @since 3.7.0 * *
@return string MySQL WHERE clause. */ public function get_sql() { $sql =
$this->get_sql_clauses(); $where = $sql['where']; /** * Filters the date query
WHERE clause. * * @since 3.7.0 * * @param string $where WHERE clause of the date
query. * @param WP_Date_Query $query The WP_Date_Query instance. */ return
apply_filters( 'get_date_sql', $where, $this ); } /** * Generates SQL clauses to
be appended to a main query. * * Called by the public WP_Date_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() { $sql =
$this->get_sql_for_query( $this->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. * @param int $depth Optional. Number of
tree levels deep we currently are. * Used to calculate indentation. Default 0. *
@return array { * 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; }
/** * Turns a single date clause into pieces for a WHERE clause. * * A wrapper
for get_sql_for_clause(), included here for backward * compatibility while
retaining the naming convention across Query classes. * * @since 3.7.0 * *
@param array $query Date query arguments. * @return array { * Array containing
JOIN and WHERE SQL clauses to append to the main 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. * } */
protected function get_sql_for_subquery( $query ) { return
$this->get_sql_for_clause( $query, '' ); } /** * Turns a first-order date query
into SQL for a WHERE clause. * * @since 4.1.0 * * @global wpdb $wpdb WordPress
database abstraction object. * * @param array $query Date query clause. * @param
array $parent_query Parent query of the current date query. * @return array { *
Array containing JOIN and WHERE SQL clauses to append to the main 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.
* } */ protected function get_sql_for_clause( $query, $parent_query ) { global
$wpdb; // The sub-parts of a $where part. $where_parts = array(); $column = ( !
empty( $query['column'] ) ) ? esc_sql( $query['column'] ) : $this->column;
$column = $this->validate_column( $column ); $compare = $this->get_compare(
$query ); $inclusive = ! empty( $query['inclusive'] ); // Assign greater- and
less-than values. $lt = '<'; $gt = '>'; if ( $inclusive ) { $lt .= '='; $gt .=
'='; } // Range queries. if ( ! empty( $query['after'] ) ) { $where_parts[] =
$wpdb->prepare( "$column $gt %s", $this->build_mysql_datetime( $query['after'],
! $inclusive ) ); } if ( ! empty( $query['before'] ) ) { $where_parts[] =
$wpdb->prepare( "$column $lt %s", $this->build_mysql_datetime( $query['before'],
$inclusive ) ); } // Specific value queries. $date_units = array( 'YEAR' =>
array( 'year' ), 'MONTH' => array( 'month', 'monthnum' ), '_wp_mysql_week' =>
array( 'week', 'w' ), 'DAYOFYEAR' => array( 'dayofyear' ), 'DAYOFMONTH' =>
array( 'day' ), 'DAYOFWEEK' => array( 'dayofweek' ), 'WEEKDAY' => array(
'dayofweek_iso' ), ); // Check of the possible date units and add them to the
query. foreach ( $date_units as $sql_part => $query_parts ) { foreach (
$query_parts as $query_part ) { if ( isset( $query[ $query_part ] ) ) { $value =
$this->build_value( $compare, $query[ $query_part ] ); if ( $value ) { switch (
$sql_part ) { case '_wp_mysql_week': $where_parts[] = _wp_mysql_week( $column )
. " $compare $value"; break; case 'WEEKDAY': $where_parts[] = "$sql_part(
$column ) + 1 $compare $value"; break; default: $where_parts[] = "$sql_part(
$column ) $compare $value"; } break; } } } } if ( isset( $query['hour'] ) ||
isset( $query['minute'] ) || isset( $query['second'] ) ) { // Avoid notices.
foreach ( array( 'hour', 'minute', 'second' ) as $unit ) { if ( ! isset( $query[
$unit ] ) ) { $query[ $unit ] = null; } } $time_query = $this->build_time_query(
$column, $compare, $query['hour'], $query['minute'], $query['second'] ); if (
$time_query ) { $where_parts[] = $time_query; } } /* * Return an array of 'join'
and 'where' for compatibility * with other query classes. */ return array(
'where' => $where_parts, 'join' => array(), ); } /** * Builds and validates a
value string based on the comparison operator. * * @since 3.7.0 * * @param
string $compare The compare operator to use. * @param string|array $value The
value. * @return string|false|int The value to be used in SQL or false on error.
*/ public function build_value( $compare, $value ) { if ( ! isset( $value ) ) {
return false; } switch ( $compare ) { case 'IN': case 'NOT IN': $value = (array)
$value; // Remove non-numeric values. $value = array_filter( $value,
'is_numeric' ); if ( empty( $value ) ) { return false; } return '(' . implode(
',', array_map( 'intval', $value ) ) . ')'; case 'BETWEEN': case 'NOT BETWEEN':
if ( ! is_array( $value ) || 2 !== count( $value ) ) { $value = array( $value,
$value ); } else { $value = array_values( $value ); } // If either value is
non-numeric, bail. foreach ( $value as $v ) { if ( ! is_numeric( $v ) ) { return
false; } } $value = array_map( 'intval', $value ); return $value[0] . ' AND ' .
$value[1]; default: if ( ! is_numeric( $value ) ) { return false; } return (int)
$value; } } /** * Builds a MySQL format date/time based on some query
parameters. * * You can pass an array of values (year, month, etc.) with missing
parameter values being defaulted to * either the maximum or minimum values
(controlled by the $default_to parameter). Alternatively you can * pass a string
that will be passed to date_create(). * * @since 3.7.0 * * @param string|array
$datetime An array of parameters or a strtotime() string. * @param bool
$default_to_max Whether to round up incomplete dates. Supported by values * of
$datetime that are arrays, or string values that are a * subset of MySQL date
format ('Y', 'Y-m', 'Y-m-d', 'Y-m-d H:i'). * Default: false. * @return
string|false A MySQL format date/time or false on failure. */ public function
build_mysql_datetime( $datetime, $default_to_max = false ) { if ( ! is_array(
$datetime ) ) { /* * Try to parse some common date formats, so we can detect *
the level of precision and support the 'inclusive' parameter. */ if (
preg_match( '/^(\d{4})$/', $datetime, $matches ) ) { // Y $datetime = array(
'year' => (int) $matches[1], ); } elseif ( preg_match( '/^(\d{4})\-(\d{2})$/',
$datetime, $matches ) ) { // Y-m $datetime = array( 'year' => (int) $matches[1],
'month' => (int) $matches[2], ); } elseif ( preg_match(
'/^(\d{4})\-(\d{2})\-(\d{2})$/', $datetime, $matches ) ) { // Y-m-d $datetime =
array( 'year' => (int) $matches[1], 'month' => (int) $matches[2], 'day' => (int)
$matches[3], ); } elseif ( preg_match( '/^(\d{4})\-(\d{2})\-(\d{2})
(\d{2}):(\d{2})$/', $datetime, $matches ) ) { // Y-m-d H:i $datetime = array(
'year' => (int) $matches[1], 'month' => (int) $matches[2], 'day' => (int)
$matches[3], 'hour' => (int) $matches[4], 'minute' => (int) $matches[5], ); } //
If no match is found, we don't support default_to_max. if ( ! is_array(
$datetime ) ) { $wp_timezone = wp_timezone(); // Assume local timezone if not
provided. $dt = date_create( $datetime, $wp_timezone ); if ( false === $dt ) {
return gmdate( 'Y-m-d H:i:s', false ); } return $dt->setTimezone( $wp_timezone
)->format( 'Y-m-d H:i:s' ); } } $datetime = array_map( 'absint', $datetime ); if
( ! isset( $datetime['year'] ) ) { $datetime['year'] = current_time( 'Y' ); } if
( ! isset( $datetime['month'] ) ) { $datetime['month'] = ( $default_to_max ) ?
12 : 1; } if ( ! isset( $datetime['day'] ) ) { $datetime['day'] = (
$default_to_max ) ? (int) gmdate( 't', mktime( 0, 0, 0, $datetime['month'], 1,
$datetime['year'] ) ) : 1; } if ( ! isset( $datetime['hour'] ) ) {
$datetime['hour'] = ( $default_to_max ) ? 23 : 0; } if ( ! isset(
$datetime['minute'] ) ) { $datetime['minute'] = ( $default_to_max ) ? 59 : 0; }
if ( ! isset( $datetime['second'] ) ) { $datetime['second'] = ( $default_to_max
) ? 59 : 0; } return sprintf( '%04d-%02d-%02d %02d:%02d:%02d',
$datetime['year'], $datetime['month'], $datetime['day'], $datetime['hour'],
$datetime['minute'], $datetime['second'] ); } /** * Builds a query string for
comparing time values (hour, minute, second). * * If just hour, minute, or
second is set than a normal comparison will be done. * However if multiple
values are passed, a pseudo-decimal time will be created * in order to be able
to accurately compare against. * * @since 3.7.0 * * @global wpdb $wpdb WordPress
database abstraction object. * * @param string $column The column to query
against. Needs to be pre-validated! * @param string $compare The comparison
operator. Needs to be pre-validated! * @param int|null $hour Optional. An hour
value (0-23). * @param int|null $minute Optional. A minute value (0-59). *
@param int|null $second Optional. A second value (0-59). * @return string|false
A query part or false on failure. */ public function build_time_query( $column,
$compare, $hour = null, $minute = null, $second = null ) { global $wpdb; // Have
to have at least one. if ( ! isset( $hour ) && ! isset( $minute ) && ! isset(
$second ) ) { return false; } // Complex combined queries aren't supported for
multi-value queries. if ( in_array( $compare, array( 'IN', 'NOT IN', 'BETWEEN',
'NOT BETWEEN' ), true ) ) { $return = array(); $value = $this->build_value(
$compare, $hour ); if ( false !== $value ) { $return[] = "HOUR( $column )
$compare $value"; } $value = $this->build_value( $compare, $minute ); if ( false
!== $value ) { $return[] = "MINUTE( $column ) $compare $value"; } $value =
$this->build_value( $compare, $second ); if ( false !== $value ) { $return[] =
"SECOND( $column ) $compare $value"; } return implode( ' AND ', $return ); } //
Cases where just one unit is set. if ( isset( $hour ) && ! isset( $minute ) && !
isset( $second ) ) { $value = $this->build_value( $compare, $hour ); if ( false
!== $value ) { return "HOUR( $column ) $compare $value"; } } elseif ( ! isset(
$hour ) && isset( $minute ) && ! isset( $second ) ) { $value =
$this->build_value( $compare, $minute ); if ( false !== $value ) { return
"MINUTE( $column ) $compare $value"; } } elseif ( ! isset( $hour ) && ! isset(
$minute ) && isset( $second ) ) { $value = $this->build_value( $compare, $second
); if ( false !== $value ) { return "SECOND( $column ) $compare $value"; } } //
Single units were already handled. Since hour & second isn't allowed, minute
must to be set. if ( ! isset( $minute ) ) { return false; } $format = ''; $time
= ''; // Hour. if ( null !== $hour ) { $format .= '%H.'; $time .= sprintf(
'%02d', $hour ) . '.'; } else { $format .= '0.'; $time .= '0.'; } // Minute.
$format .= '%i'; $time .= sprintf( '%02d', $minute ); if ( isset( $second ) ) {
$format .= '%s'; $time .= sprintf( '%02d', $second ); } return $wpdb->prepare(
"DATE_FORMAT( $column, %s ) $compare %f", $format, $time ); } /** * Sanitizes a
'relation' operator. * * @since 6.0.3 * * @param string $relation Raw relation
key from the query argument. * @return string Sanitized relation. Either 'AND'
or 'OR'. */ public function sanitize_relation( $relation ) { if ( 'OR' ===
strtoupper( $relation ) ) { return 'OR'; } else { return 'AND'; } } }