ru-se.com

Unnamed repository; edit this file 'description' to name the repository.
Log | Files | Refs

meta.php (60126B)


      1 <?php
      2 /**
      3  * Core Metadata API
      4  *
      5  * Functions for retrieving and manipulating metadata of various WordPress object types. Metadata
      6  * for an object is a represented by a simple key-value pair. Objects may contain multiple
      7  * metadata entries that share the same key and differ only in their value.
      8  *
      9  * @package WordPress
     10  * @subpackage Meta
     11  */
     12 
     13 /**
     14  * Adds metadata for the specified object.
     15  *
     16  * @since 2.9.0
     17  *
     18  * @global wpdb $wpdb WordPress database abstraction object.
     19  *
     20  * @param string $meta_type  Type of object metadata is for. Accepts 'post', 'comment', 'term', 'user',
     21  *                           or any other object type with an associated meta table.
     22  * @param int    $object_id  ID of the object metadata is for.
     23  * @param string $meta_key   Metadata key.
     24  * @param mixed  $meta_value Metadata value. Must be serializable if non-scalar.
     25  * @param bool   $unique     Optional. Whether the specified metadata key should be unique for the object.
     26  *                           If true, and the object already has a value for the specified metadata key,
     27  *                           no change will be made. Default false.
     28  * @return int|false The meta ID on success, false on failure.
     29  */
     30 function add_metadata( $meta_type, $object_id, $meta_key, $meta_value, $unique = false ) {
     31 	global $wpdb;
     32 
     33 	if ( ! $meta_type || ! $meta_key || ! is_numeric( $object_id ) ) {
     34 		return false;
     35 	}
     36 
     37 	$object_id = absint( $object_id );
     38 	if ( ! $object_id ) {
     39 		return false;
     40 	}
     41 
     42 	$table = _get_meta_table( $meta_type );
     43 	if ( ! $table ) {
     44 		return false;
     45 	}
     46 
     47 	$meta_subtype = get_object_subtype( $meta_type, $object_id );
     48 
     49 	$column = sanitize_key( $meta_type . '_id' );
     50 
     51 	// expected_slashed ($meta_key)
     52 	$meta_key   = wp_unslash( $meta_key );
     53 	$meta_value = wp_unslash( $meta_value );
     54 	$meta_value = sanitize_meta( $meta_key, $meta_value, $meta_type, $meta_subtype );
     55 
     56 	/**
     57 	 * Short-circuits adding metadata of a specific type.
     58 	 *
     59 	 * The dynamic portion of the hook, `$meta_type`, refers to the meta object type
     60 	 * (post, comment, term, user, or any other type with an associated meta table).
     61 	 * Returning a non-null value will effectively short-circuit the function.
     62 	 *
     63 	 * @since 3.1.0
     64 	 *
     65 	 * @param null|bool $check      Whether to allow adding metadata for the given type.
     66 	 * @param int       $object_id  ID of the object metadata is for.
     67 	 * @param string    $meta_key   Metadata key.
     68 	 * @param mixed     $meta_value Metadata value. Must be serializable if non-scalar.
     69 	 * @param bool      $unique     Whether the specified meta key should be unique for the object.
     70 	 */
     71 	$check = apply_filters( "add_{$meta_type}_metadata", null, $object_id, $meta_key, $meta_value, $unique );
     72 	if ( null !== $check ) {
     73 		return $check;
     74 	}
     75 
     76 	if ( $unique && $wpdb->get_var(
     77 		$wpdb->prepare(
     78 			"SELECT COUNT(*) FROM $table WHERE meta_key = %s AND $column = %d",
     79 			$meta_key,
     80 			$object_id
     81 		)
     82 	) ) {
     83 		return false;
     84 	}
     85 
     86 	$_meta_value = $meta_value;
     87 	$meta_value  = maybe_serialize( $meta_value );
     88 
     89 	/**
     90 	 * Fires immediately before meta of a specific type is added.
     91 	 *
     92 	 * The dynamic portion of the hook, `$meta_type`, refers to the meta object type
     93 	 * (post, comment, term, user, or any other type with an associated meta table).
     94 	 *
     95 	 * @since 3.1.0
     96 	 *
     97 	 * @param int    $object_id   ID of the object metadata is for.
     98 	 * @param string $meta_key    Metadata key.
     99 	 * @param mixed  $_meta_value Metadata value. Serialized if non-scalar.
    100 	 */
    101 	do_action( "add_{$meta_type}_meta", $object_id, $meta_key, $_meta_value );
    102 
    103 	$result = $wpdb->insert(
    104 		$table,
    105 		array(
    106 			$column      => $object_id,
    107 			'meta_key'   => $meta_key,
    108 			'meta_value' => $meta_value,
    109 		)
    110 	);
    111 
    112 	if ( ! $result ) {
    113 		return false;
    114 	}
    115 
    116 	$mid = (int) $wpdb->insert_id;
    117 
    118 	wp_cache_delete( $object_id, $meta_type . '_meta' );
    119 
    120 	/**
    121 	 * Fires immediately after meta of a specific type is added.
    122 	 *
    123 	 * The dynamic portion of the hook, `$meta_type`, refers to the meta object type
    124 	 * (post, comment, term, user, or any other type with an associated meta table).
    125 	 *
    126 	 * @since 2.9.0
    127 	 *
    128 	 * @param int    $mid         The meta ID after successful update.
    129 	 * @param int    $object_id   ID of the object metadata is for.
    130 	 * @param string $meta_key    Metadata key.
    131 	 * @param mixed  $_meta_value Metadata value. Serialized if non-scalar.
    132 	 */
    133 	do_action( "added_{$meta_type}_meta", $mid, $object_id, $meta_key, $_meta_value );
    134 
    135 	return $mid;
    136 }
    137 
    138 /**
    139  * Updates metadata for the specified object. If no value already exists for the specified object
    140  * ID and metadata key, the metadata will be added.
    141  *
    142  * @since 2.9.0
    143  *
    144  * @global wpdb $wpdb WordPress database abstraction object.
    145  *
    146  * @param string $meta_type  Type of object metadata is for. Accepts 'post', 'comment', 'term', 'user',
    147  *                           or any other object type with an associated meta table.
    148  * @param int    $object_id  ID of the object metadata is for.
    149  * @param string $meta_key   Metadata key.
    150  * @param mixed  $meta_value Metadata value. Must be serializable if non-scalar.
    151  * @param mixed  $prev_value Optional. Previous value to check before updating.
    152  *                           If specified, only update existing metadata entries with
    153  *                           this value. Otherwise, update all entries. Default empty.
    154  * @return int|bool The new meta field ID if a field with the given key didn't exist
    155  *                  and was therefore added, true on successful update,
    156  *                  false on failure or if the value passed to the function
    157  *                  is the same as the one that is already in the database.
    158  */
    159 function update_metadata( $meta_type, $object_id, $meta_key, $meta_value, $prev_value = '' ) {
    160 	global $wpdb;
    161 
    162 	if ( ! $meta_type || ! $meta_key || ! is_numeric( $object_id ) ) {
    163 		return false;
    164 	}
    165 
    166 	$object_id = absint( $object_id );
    167 	if ( ! $object_id ) {
    168 		return false;
    169 	}
    170 
    171 	$table = _get_meta_table( $meta_type );
    172 	if ( ! $table ) {
    173 		return false;
    174 	}
    175 
    176 	$meta_subtype = get_object_subtype( $meta_type, $object_id );
    177 
    178 	$column    = sanitize_key( $meta_type . '_id' );
    179 	$id_column = ( 'user' === $meta_type ) ? 'umeta_id' : 'meta_id';
    180 
    181 	// expected_slashed ($meta_key)
    182 	$raw_meta_key = $meta_key;
    183 	$meta_key     = wp_unslash( $meta_key );
    184 	$passed_value = $meta_value;
    185 	$meta_value   = wp_unslash( $meta_value );
    186 	$meta_value   = sanitize_meta( $meta_key, $meta_value, $meta_type, $meta_subtype );
    187 
    188 	/**
    189 	 * Short-circuits updating metadata of a specific type.
    190 	 *
    191 	 * The dynamic portion of the hook, `$meta_type`, refers to the meta object type
    192 	 * (post, comment, term, user, or any other type with an associated meta table).
    193 	 * Returning a non-null value will effectively short-circuit the function.
    194 	 *
    195 	 * @since 3.1.0
    196 	 *
    197 	 * @param null|bool $check      Whether to allow updating metadata for the given type.
    198 	 * @param int       $object_id  ID of the object metadata is for.
    199 	 * @param string    $meta_key   Metadata key.
    200 	 * @param mixed     $meta_value Metadata value. Must be serializable if non-scalar.
    201 	 * @param mixed     $prev_value Optional. Previous value to check before updating.
    202 	 *                              If specified, only update existing metadata entries with
    203 	 *                              this value. Otherwise, update all entries.
    204 	 */
    205 	$check = apply_filters( "update_{$meta_type}_metadata", null, $object_id, $meta_key, $meta_value, $prev_value );
    206 	if ( null !== $check ) {
    207 		return (bool) $check;
    208 	}
    209 
    210 	// Compare existing value to new value if no prev value given and the key exists only once.
    211 	if ( empty( $prev_value ) ) {
    212 		$old_value = get_metadata_raw( $meta_type, $object_id, $meta_key );
    213 		if ( is_countable( $old_value ) && count( $old_value ) === 1 ) {
    214 			if ( $old_value[0] === $meta_value ) {
    215 				return false;
    216 			}
    217 		}
    218 	}
    219 
    220 	$meta_ids = $wpdb->get_col( $wpdb->prepare( "SELECT $id_column FROM $table WHERE meta_key = %s AND $column = %d", $meta_key, $object_id ) );
    221 	if ( empty( $meta_ids ) ) {
    222 		return add_metadata( $meta_type, $object_id, $raw_meta_key, $passed_value );
    223 	}
    224 
    225 	$_meta_value = $meta_value;
    226 	$meta_value  = maybe_serialize( $meta_value );
    227 
    228 	$data  = compact( 'meta_value' );
    229 	$where = array(
    230 		$column    => $object_id,
    231 		'meta_key' => $meta_key,
    232 	);
    233 
    234 	if ( ! empty( $prev_value ) ) {
    235 		$prev_value          = maybe_serialize( $prev_value );
    236 		$where['meta_value'] = $prev_value;
    237 	}
    238 
    239 	foreach ( $meta_ids as $meta_id ) {
    240 		/**
    241 		 * Fires immediately before updating metadata of a specific type.
    242 		 *
    243 		 * The dynamic portion of the hook, `$meta_type`, refers to the meta object type
    244 		 * (post, comment, term, user, or any other type with an associated meta table).
    245 		 *
    246 		 * @since 2.9.0
    247 		 *
    248 		 * @param int    $meta_id     ID of the metadata entry to update.
    249 		 * @param int    $object_id   ID of the object metadata is for.
    250 		 * @param string $meta_key    Metadata key.
    251 		 * @param mixed  $_meta_value Metadata value. Serialized if non-scalar.
    252 		 */
    253 		do_action( "update_{$meta_type}_meta", $meta_id, $object_id, $meta_key, $_meta_value );
    254 
    255 		if ( 'post' === $meta_type ) {
    256 			/**
    257 			 * Fires immediately before updating a post's metadata.
    258 			 *
    259 			 * @since 2.9.0
    260 			 *
    261 			 * @param int    $meta_id    ID of metadata entry to update.
    262 			 * @param int    $object_id  Post ID.
    263 			 * @param string $meta_key   Metadata key.
    264 			 * @param mixed  $meta_value Metadata value. This will be a PHP-serialized string representation of the value
    265 			 *                           if the value is an array, an object, or itself a PHP-serialized string.
    266 			 */
    267 			do_action( 'update_postmeta', $meta_id, $object_id, $meta_key, $meta_value );
    268 		}
    269 	}
    270 
    271 	$result = $wpdb->update( $table, $data, $where );
    272 	if ( ! $result ) {
    273 		return false;
    274 	}
    275 
    276 	wp_cache_delete( $object_id, $meta_type . '_meta' );
    277 
    278 	foreach ( $meta_ids as $meta_id ) {
    279 		/**
    280 		 * Fires immediately after updating metadata of a specific type.
    281 		 *
    282 		 * The dynamic portion of the hook, `$meta_type`, refers to the meta object type
    283 		 * (post, comment, term, user, or any other type with an associated meta table).
    284 		 *
    285 		 * @since 2.9.0
    286 		 *
    287 		 * @param int    $meta_id     ID of updated metadata entry.
    288 		 * @param int    $object_id   ID of the object metadata is for.
    289 		 * @param string $meta_key    Metadata key.
    290 		 * @param mixed  $_meta_value Metadata value. Serialized if non-scalar.
    291 		 */
    292 		do_action( "updated_{$meta_type}_meta", $meta_id, $object_id, $meta_key, $_meta_value );
    293 
    294 		if ( 'post' === $meta_type ) {
    295 			/**
    296 			 * Fires immediately after updating a post's metadata.
    297 			 *
    298 			 * @since 2.9.0
    299 			 *
    300 			 * @param int    $meta_id    ID of updated metadata entry.
    301 			 * @param int    $object_id  Post ID.
    302 			 * @param string $meta_key   Metadata key.
    303 			 * @param mixed  $meta_value Metadata value. This will be a PHP-serialized string representation of the value
    304 			 *                           if the value is an array, an object, or itself a PHP-serialized string.
    305 			 */
    306 			do_action( 'updated_postmeta', $meta_id, $object_id, $meta_key, $meta_value );
    307 		}
    308 	}
    309 
    310 	return true;
    311 }
    312 
    313 /**
    314  * Deletes metadata for the specified object.
    315  *
    316  * @since 2.9.0
    317  *
    318  * @global wpdb $wpdb WordPress database abstraction object.
    319  *
    320  * @param string $meta_type  Type of object metadata is for. Accepts 'post', 'comment', 'term', 'user',
    321  *                           or any other object type with an associated meta table.
    322  * @param int    $object_id  ID of the object metadata is for.
    323  * @param string $meta_key   Metadata key.
    324  * @param mixed  $meta_value Optional. Metadata value. Must be serializable if non-scalar.
    325  *                           If specified, only delete metadata entries with this value.
    326  *                           Otherwise, delete all entries with the specified meta_key.
    327  *                           Pass `null`, `false`, or an empty string to skip this check.
    328  *                           (For backward compatibility, it is not possible to pass an empty string
    329  *                           to delete those entries with an empty string for a value.)
    330  * @param bool   $delete_all Optional. If true, delete matching metadata entries for all objects,
    331  *                           ignoring the specified object_id. Otherwise, only delete
    332  *                           matching metadata entries for the specified object_id. Default false.
    333  * @return bool True on successful delete, false on failure.
    334  */
    335 function delete_metadata( $meta_type, $object_id, $meta_key, $meta_value = '', $delete_all = false ) {
    336 	global $wpdb;
    337 
    338 	if ( ! $meta_type || ! $meta_key || ! is_numeric( $object_id ) && ! $delete_all ) {
    339 		return false;
    340 	}
    341 
    342 	$object_id = absint( $object_id );
    343 	if ( ! $object_id && ! $delete_all ) {
    344 		return false;
    345 	}
    346 
    347 	$table = _get_meta_table( $meta_type );
    348 	if ( ! $table ) {
    349 		return false;
    350 	}
    351 
    352 	$type_column = sanitize_key( $meta_type . '_id' );
    353 	$id_column   = ( 'user' === $meta_type ) ? 'umeta_id' : 'meta_id';
    354 
    355 	// expected_slashed ($meta_key)
    356 	$meta_key   = wp_unslash( $meta_key );
    357 	$meta_value = wp_unslash( $meta_value );
    358 
    359 	/**
    360 	 * Short-circuits deleting metadata of a specific type.
    361 	 *
    362 	 * The dynamic portion of the hook, `$meta_type`, refers to the meta object type
    363 	 * (post, comment, term, user, or any other type with an associated meta table).
    364 	 * Returning a non-null value will effectively short-circuit the function.
    365 	 *
    366 	 * @since 3.1.0
    367 	 *
    368 	 * @param null|bool $delete     Whether to allow metadata deletion of the given type.
    369 	 * @param int       $object_id  ID of the object metadata is for.
    370 	 * @param string    $meta_key   Metadata key.
    371 	 * @param mixed     $meta_value Metadata value. Must be serializable if non-scalar.
    372 	 * @param bool      $delete_all Whether to delete the matching metadata entries
    373 	 *                              for all objects, ignoring the specified $object_id.
    374 	 *                              Default false.
    375 	 */
    376 	$check = apply_filters( "delete_{$meta_type}_metadata", null, $object_id, $meta_key, $meta_value, $delete_all );
    377 	if ( null !== $check ) {
    378 		return (bool) $check;
    379 	}
    380 
    381 	$_meta_value = $meta_value;
    382 	$meta_value  = maybe_serialize( $meta_value );
    383 
    384 	$query = $wpdb->prepare( "SELECT $id_column FROM $table WHERE meta_key = %s", $meta_key );
    385 
    386 	if ( ! $delete_all ) {
    387 		$query .= $wpdb->prepare( " AND $type_column = %d", $object_id );
    388 	}
    389 
    390 	if ( '' !== $meta_value && null !== $meta_value && false !== $meta_value ) {
    391 		$query .= $wpdb->prepare( ' AND meta_value = %s', $meta_value );
    392 	}
    393 
    394 	$meta_ids = $wpdb->get_col( $query );
    395 	if ( ! count( $meta_ids ) ) {
    396 		return false;
    397 	}
    398 
    399 	if ( $delete_all ) {
    400 		if ( '' !== $meta_value && null !== $meta_value && false !== $meta_value ) {
    401 			$object_ids = $wpdb->get_col( $wpdb->prepare( "SELECT $type_column FROM $table WHERE meta_key = %s AND meta_value = %s", $meta_key, $meta_value ) );
    402 		} else {
    403 			$object_ids = $wpdb->get_col( $wpdb->prepare( "SELECT $type_column FROM $table WHERE meta_key = %s", $meta_key ) );
    404 		}
    405 	}
    406 
    407 	/**
    408 	 * Fires immediately before deleting metadata of a specific type.
    409 	 *
    410 	 * The dynamic portion of the hook, `$meta_type`, refers to the meta object type
    411 	 * (post, comment, term, user, or any other type with an associated meta table).
    412 	 *
    413 	 * @since 3.1.0
    414 	 *
    415 	 * @param string[] $meta_ids    An array of metadata entry IDs to delete.
    416 	 * @param int      $object_id   ID of the object metadata is for.
    417 	 * @param string   $meta_key    Metadata key.
    418 	 * @param mixed    $_meta_value Metadata value. Serialized if non-scalar.
    419 	 */
    420 	do_action( "delete_{$meta_type}_meta", $meta_ids, $object_id, $meta_key, $_meta_value );
    421 
    422 	// Old-style action.
    423 	if ( 'post' === $meta_type ) {
    424 		/**
    425 		 * Fires immediately before deleting metadata for a post.
    426 		 *
    427 		 * @since 2.9.0
    428 		 *
    429 		 * @param string[] $meta_ids An array of metadata entry IDs to delete.
    430 		 */
    431 		do_action( 'delete_postmeta', $meta_ids );
    432 	}
    433 
    434 	$query = "DELETE FROM $table WHERE $id_column IN( " . implode( ',', $meta_ids ) . ' )';
    435 
    436 	$count = $wpdb->query( $query );
    437 
    438 	if ( ! $count ) {
    439 		return false;
    440 	}
    441 
    442 	if ( $delete_all ) {
    443 		foreach ( (array) $object_ids as $o_id ) {
    444 			wp_cache_delete( $o_id, $meta_type . '_meta' );
    445 		}
    446 	} else {
    447 		wp_cache_delete( $object_id, $meta_type . '_meta' );
    448 	}
    449 
    450 	/**
    451 	 * Fires immediately after deleting metadata of a specific type.
    452 	 *
    453 	 * The dynamic portion of the hook, `$meta_type`, refers to the meta object type
    454 	 * (post, comment, term, user, or any other type with an associated meta table).
    455 	 *
    456 	 * @since 2.9.0
    457 	 *
    458 	 * @param string[] $meta_ids    An array of metadata entry IDs to delete.
    459 	 * @param int      $object_id   ID of the object metadata is for.
    460 	 * @param string   $meta_key    Metadata key.
    461 	 * @param mixed    $_meta_value Metadata value. Serialized if non-scalar.
    462 	 */
    463 	do_action( "deleted_{$meta_type}_meta", $meta_ids, $object_id, $meta_key, $_meta_value );
    464 
    465 	// Old-style action.
    466 	if ( 'post' === $meta_type ) {
    467 		/**
    468 		 * Fires immediately after deleting metadata for a post.
    469 		 *
    470 		 * @since 2.9.0
    471 		 *
    472 		 * @param string[] $meta_ids An array of metadata entry IDs to delete.
    473 		 */
    474 		do_action( 'deleted_postmeta', $meta_ids );
    475 	}
    476 
    477 	return true;
    478 }
    479 
    480 /**
    481  * Retrieves the value of a metadata field for the specified object type and ID.
    482  *
    483  * If the meta field exists, a single value is returned if `$single` is true,
    484  * or an array of values if it's false.
    485  *
    486  * If the meta field does not exist, the result depends on get_metadata_default().
    487  * By default, an empty string is returned if `$single` is true, or an empty array
    488  * if it's false.
    489  *
    490  * @since 2.9.0
    491  *
    492  * @see get_metadata_raw()
    493  * @see get_metadata_default()
    494  *
    495  * @param string $meta_type Type of object metadata is for. Accepts 'post', 'comment', 'term', 'user',
    496  *                          or any other object type with an associated meta table.
    497  * @param int    $object_id ID of the object metadata is for.
    498  * @param string $meta_key  Optional. Metadata key. If not specified, retrieve all metadata for
    499  *                          the specified object. Default empty.
    500  * @param bool   $single    Optional. If true, return only the first value of the specified `$meta_key`.
    501  *                          This parameter has no effect if `$meta_key` is not specified. Default false.
    502  * @return mixed An array of values if `$single` is false.
    503  *               The value of the meta field if `$single` is true.
    504  *               False for an invalid `$object_id` (non-numeric, zero, or negative value),
    505  *               or if `$meta_type` is not specified.
    506  *               An empty string if a valid but non-existing object ID is passed.
    507  */
    508 function get_metadata( $meta_type, $object_id, $meta_key = '', $single = false ) {
    509 	$value = get_metadata_raw( $meta_type, $object_id, $meta_key, $single );
    510 	if ( ! is_null( $value ) ) {
    511 		return $value;
    512 	}
    513 
    514 	return get_metadata_default( $meta_type, $object_id, $meta_key, $single );
    515 }
    516 
    517 /**
    518  * Retrieves raw metadata value for the specified object.
    519  *
    520  * @since 5.5.0
    521  *
    522  * @param string $meta_type Type of object metadata is for. Accepts 'post', 'comment', 'term', 'user',
    523  *                          or any other object type with an associated meta table.
    524  * @param int    $object_id ID of the object metadata is for.
    525  * @param string $meta_key  Optional. Metadata key. If not specified, retrieve all metadata for
    526  *                          the specified object. Default empty.
    527  * @param bool   $single    Optional. If true, return only the first value of the specified `$meta_key`.
    528  *                          This parameter has no effect if `$meta_key` is not specified. Default false.
    529  * @return mixed An array of values if `$single` is false.
    530  *               The value of the meta field if `$single` is true.
    531  *               False for an invalid `$object_id` (non-numeric, zero, or negative value),
    532  *               or if `$meta_type` is not specified.
    533  *               Null if the value does not exist.
    534  */
    535 function get_metadata_raw( $meta_type, $object_id, $meta_key = '', $single = false ) {
    536 	if ( ! $meta_type || ! is_numeric( $object_id ) ) {
    537 		return false;
    538 	}
    539 
    540 	$object_id = absint( $object_id );
    541 	if ( ! $object_id ) {
    542 		return false;
    543 	}
    544 
    545 	/**
    546 	 * Short-circuits the return value of a meta field.
    547 	 *
    548 	 * The dynamic portion of the hook, `$meta_type`, refers to the meta object type
    549 	 * (post, comment, term, user, or any other type with an associated meta table).
    550 	 * Returning a non-null value will effectively short-circuit the function.
    551 	 *
    552 	 * Possible filter names include:
    553 	 *
    554 	 *  - `get_post_metadata`
    555 	 *  - `get_comment_metadata`
    556 	 *  - `get_term_metadata`
    557 	 *  - `get_user_metadata`
    558 	 *
    559 	 * @since 3.1.0
    560 	 * @since 5.5.0 Added the `$meta_type` parameter.
    561 	 *
    562 	 * @param mixed  $value     The value to return, either a single metadata value or an array
    563 	 *                          of values depending on the value of `$single`. Default null.
    564 	 * @param int    $object_id ID of the object metadata is for.
    565 	 * @param string $meta_key  Metadata key.
    566 	 * @param bool   $single    Whether to return only the first value of the specified `$meta_key`.
    567 	 * @param string $meta_type Type of object metadata is for. Accepts 'post', 'comment', 'term', 'user',
    568 	 *                          or any other object type with an associated meta table.
    569 	 */
    570 	$check = apply_filters( "get_{$meta_type}_metadata", null, $object_id, $meta_key, $single, $meta_type );
    571 	if ( null !== $check ) {
    572 		if ( $single && is_array( $check ) ) {
    573 			return $check[0];
    574 		} else {
    575 			return $check;
    576 		}
    577 	}
    578 
    579 	$meta_cache = wp_cache_get( $object_id, $meta_type . '_meta' );
    580 
    581 	if ( ! $meta_cache ) {
    582 		$meta_cache = update_meta_cache( $meta_type, array( $object_id ) );
    583 		if ( isset( $meta_cache[ $object_id ] ) ) {
    584 			$meta_cache = $meta_cache[ $object_id ];
    585 		} else {
    586 			$meta_cache = null;
    587 		}
    588 	}
    589 
    590 	if ( ! $meta_key ) {
    591 		return $meta_cache;
    592 	}
    593 
    594 	if ( isset( $meta_cache[ $meta_key ] ) ) {
    595 		if ( $single ) {
    596 			return maybe_unserialize( $meta_cache[ $meta_key ][0] );
    597 		} else {
    598 			return array_map( 'maybe_unserialize', $meta_cache[ $meta_key ] );
    599 		}
    600 	}
    601 
    602 	return null;
    603 }
    604 
    605 /**
    606  * Retrieves default metadata value for the specified meta key and object.
    607  *
    608  * By default, an empty string is returned if `$single` is true, or an empty array
    609  * if it's false.
    610  *
    611  * @since 5.5.0
    612  *
    613  * @param string $meta_type Type of object metadata is for. Accepts 'post', 'comment', 'term', 'user',
    614  *                          or any other object type with an associated meta table.
    615  * @param int    $object_id ID of the object metadata is for.
    616  * @param string $meta_key  Metadata key.
    617  * @param bool   $single    Optional. If true, return only the first value of the specified `$meta_key`.
    618  *                          This parameter has no effect if `$meta_key` is not specified. Default false.
    619  * @return mixed An array of default values if `$single` is false.
    620  *               The default value of the meta field if `$single` is true.
    621  */
    622 function get_metadata_default( $meta_type, $object_id, $meta_key, $single = false ) {
    623 	if ( $single ) {
    624 		$value = '';
    625 	} else {
    626 		$value = array();
    627 	}
    628 
    629 	/**
    630 	 * Filters the default metadata value for a specified meta key and object.
    631 	 *
    632 	 * The dynamic portion of the hook, `$meta_type`, refers to the meta object type
    633 	 * (post, comment, term, user, or any other type with an associated meta table).
    634 	 *
    635 	 * Possible filter names include:
    636 	 *
    637 	 *  - `default_post_metadata`
    638 	 *  - `default_comment_metadata`
    639 	 *  - `default_term_metadata`
    640 	 *  - `default_user_metadata`
    641 	 *
    642 	 * @since 5.5.0
    643 	 *
    644 	 * @param mixed  $value     The value to return, either a single metadata value or an array
    645 	 *                          of values depending on the value of `$single`.
    646 	 * @param int    $object_id ID of the object metadata is for.
    647 	 * @param string $meta_key  Metadata key.
    648 	 * @param bool   $single    Whether to return only the first value of the specified `$meta_key`.
    649 	 * @param string $meta_type Type of object metadata is for. Accepts 'post', 'comment', 'term', 'user',
    650 	 *                          or any other object type with an associated meta table.
    651 	 */
    652 	$value = apply_filters( "default_{$meta_type}_metadata", $value, $object_id, $meta_key, $single, $meta_type );
    653 
    654 	if ( ! $single && ! wp_is_numeric_array( $value ) ) {
    655 		$value = array( $value );
    656 	}
    657 
    658 	return $value;
    659 }
    660 
    661 /**
    662  * Determines if a meta field with the given key exists for the given object ID.
    663  *
    664  * @since 3.3.0
    665  *
    666  * @param string $meta_type Type of object metadata is for. Accepts 'post', 'comment', 'term', 'user',
    667  *                          or any other object type with an associated meta table.
    668  * @param int    $object_id ID of the object metadata is for.
    669  * @param string $meta_key  Metadata key.
    670  * @return bool Whether a meta field with the given key exists.
    671  */
    672 function metadata_exists( $meta_type, $object_id, $meta_key ) {
    673 	if ( ! $meta_type || ! is_numeric( $object_id ) ) {
    674 		return false;
    675 	}
    676 
    677 	$object_id = absint( $object_id );
    678 	if ( ! $object_id ) {
    679 		return false;
    680 	}
    681 
    682 	/** This filter is documented in wp-includes/meta.php */
    683 	$check = apply_filters( "get_{$meta_type}_metadata", null, $object_id, $meta_key, true, $meta_type );
    684 	if ( null !== $check ) {
    685 		return (bool) $check;
    686 	}
    687 
    688 	$meta_cache = wp_cache_get( $object_id, $meta_type . '_meta' );
    689 
    690 	if ( ! $meta_cache ) {
    691 		$meta_cache = update_meta_cache( $meta_type, array( $object_id ) );
    692 		$meta_cache = $meta_cache[ $object_id ];
    693 	}
    694 
    695 	if ( isset( $meta_cache[ $meta_key ] ) ) {
    696 		return true;
    697 	}
    698 
    699 	return false;
    700 }
    701 
    702 /**
    703  * Retrieves metadata by meta ID.
    704  *
    705  * @since 3.3.0
    706  *
    707  * @global wpdb $wpdb WordPress database abstraction object.
    708  *
    709  * @param string $meta_type Type of object metadata is for. Accepts 'post', 'comment', 'term', 'user',
    710  *                          or any other object type with an associated meta table.
    711  * @param int    $meta_id   ID for a specific meta row.
    712  * @return stdClass|false {
    713  *     Metadata object, or boolean `false` if the metadata doesn't exist.
    714  *
    715  *     @type string $meta_key   The meta key.
    716  *     @type mixed  $meta_value The unserialized meta value.
    717  *     @type string $meta_id    Optional. The meta ID when the meta type is any value except 'user'.
    718  *     @type string $umeta_id   Optional. The meta ID when the meta type is 'user'.
    719  *     @type string $post_id    Optional. The object ID when the meta type is 'post'.
    720  *     @type string $comment_id Optional. The object ID when the meta type is 'comment'.
    721  *     @type string $term_id    Optional. The object ID when the meta type is 'term'.
    722  *     @type string $user_id    Optional. The object ID when the meta type is 'user'.
    723  * }
    724  */
    725 function get_metadata_by_mid( $meta_type, $meta_id ) {
    726 	global $wpdb;
    727 
    728 	if ( ! $meta_type || ! is_numeric( $meta_id ) || floor( $meta_id ) != $meta_id ) {
    729 		return false;
    730 	}
    731 
    732 	$meta_id = (int) $meta_id;
    733 	if ( $meta_id <= 0 ) {
    734 		return false;
    735 	}
    736 
    737 	$table = _get_meta_table( $meta_type );
    738 	if ( ! $table ) {
    739 		return false;
    740 	}
    741 
    742 	/**
    743 	 * Short-circuits the return value when fetching a meta field by meta ID.
    744 	 *
    745 	 * The dynamic portion of the hook, `$meta_type`, refers to the meta object type
    746 	 * (post, comment, term, user, or any other type with an associated meta table).
    747 	 * Returning a non-null value will effectively short-circuit the function.
    748 	 *
    749 	 * @since 5.0.0
    750 	 *
    751 	 * @param stdClass|null $value   The value to return.
    752 	 * @param int           $meta_id Meta ID.
    753 	 */
    754 	$check = apply_filters( "get_{$meta_type}_metadata_by_mid", null, $meta_id );
    755 	if ( null !== $check ) {
    756 		return $check;
    757 	}
    758 
    759 	$id_column = ( 'user' === $meta_type ) ? 'umeta_id' : 'meta_id';
    760 
    761 	$meta = $wpdb->get_row( $wpdb->prepare( "SELECT * FROM $table WHERE $id_column = %d", $meta_id ) );
    762 
    763 	if ( empty( $meta ) ) {
    764 		return false;
    765 	}
    766 
    767 	if ( isset( $meta->meta_value ) ) {
    768 		$meta->meta_value = maybe_unserialize( $meta->meta_value );
    769 	}
    770 
    771 	return $meta;
    772 }
    773 
    774 /**
    775  * Updates metadata by meta ID.
    776  *
    777  * @since 3.3.0
    778  *
    779  * @global wpdb $wpdb WordPress database abstraction object.
    780  *
    781  * @param string       $meta_type  Type of object metadata is for. Accepts 'post', 'comment', 'term', 'user',
    782  *                                 or any other object type with an associated meta table.
    783  * @param int          $meta_id    ID for a specific meta row.
    784  * @param string       $meta_value Metadata value. Must be serializable if non-scalar.
    785  * @param string|false $meta_key   Optional. You can provide a meta key to update it. Default false.
    786  * @return bool True on successful update, false on failure.
    787  */
    788 function update_metadata_by_mid( $meta_type, $meta_id, $meta_value, $meta_key = false ) {
    789 	global $wpdb;
    790 
    791 	// Make sure everything is valid.
    792 	if ( ! $meta_type || ! is_numeric( $meta_id ) || floor( $meta_id ) != $meta_id ) {
    793 		return false;
    794 	}
    795 
    796 	$meta_id = (int) $meta_id;
    797 	if ( $meta_id <= 0 ) {
    798 		return false;
    799 	}
    800 
    801 	$table = _get_meta_table( $meta_type );
    802 	if ( ! $table ) {
    803 		return false;
    804 	}
    805 
    806 	$column    = sanitize_key( $meta_type . '_id' );
    807 	$id_column = ( 'user' === $meta_type ) ? 'umeta_id' : 'meta_id';
    808 
    809 	/**
    810 	 * Short-circuits updating metadata of a specific type by meta ID.
    811 	 *
    812 	 * The dynamic portion of the hook, `$meta_type`, refers to the meta object type
    813 	 * (post, comment, term, user, or any other type with an associated meta table).
    814 	 * Returning a non-null value will effectively short-circuit the function.
    815 	 *
    816 	 * @since 5.0.0
    817 	 *
    818 	 * @param null|bool    $check      Whether to allow updating metadata for the given type.
    819 	 * @param int          $meta_id    Meta ID.
    820 	 * @param mixed        $meta_value Meta value. Must be serializable if non-scalar.
    821 	 * @param string|false $meta_key   Meta key, if provided.
    822 	 */
    823 	$check = apply_filters( "update_{$meta_type}_metadata_by_mid", null, $meta_id, $meta_value, $meta_key );
    824 	if ( null !== $check ) {
    825 		return (bool) $check;
    826 	}
    827 
    828 	// Fetch the meta and go on if it's found.
    829 	$meta = get_metadata_by_mid( $meta_type, $meta_id );
    830 	if ( $meta ) {
    831 		$original_key = $meta->meta_key;
    832 		$object_id    = $meta->{$column};
    833 
    834 		// If a new meta_key (last parameter) was specified, change the meta key,
    835 		// otherwise use the original key in the update statement.
    836 		if ( false === $meta_key ) {
    837 			$meta_key = $original_key;
    838 		} elseif ( ! is_string( $meta_key ) ) {
    839 			return false;
    840 		}
    841 
    842 		$meta_subtype = get_object_subtype( $meta_type, $object_id );
    843 
    844 		// Sanitize the meta.
    845 		$_meta_value = $meta_value;
    846 		$meta_value  = sanitize_meta( $meta_key, $meta_value, $meta_type, $meta_subtype );
    847 		$meta_value  = maybe_serialize( $meta_value );
    848 
    849 		// Format the data query arguments.
    850 		$data = array(
    851 			'meta_key'   => $meta_key,
    852 			'meta_value' => $meta_value,
    853 		);
    854 
    855 		// Format the where query arguments.
    856 		$where               = array();
    857 		$where[ $id_column ] = $meta_id;
    858 
    859 		/** This action is documented in wp-includes/meta.php */
    860 		do_action( "update_{$meta_type}_meta", $meta_id, $object_id, $meta_key, $_meta_value );
    861 
    862 		if ( 'post' === $meta_type ) {
    863 			/** This action is documented in wp-includes/meta.php */
    864 			do_action( 'update_postmeta', $meta_id, $object_id, $meta_key, $meta_value );
    865 		}
    866 
    867 		// Run the update query, all fields in $data are %s, $where is a %d.
    868 		$result = $wpdb->update( $table, $data, $where, '%s', '%d' );
    869 		if ( ! $result ) {
    870 			return false;
    871 		}
    872 
    873 		// Clear the caches.
    874 		wp_cache_delete( $object_id, $meta_type . '_meta' );
    875 
    876 		/** This action is documented in wp-includes/meta.php */
    877 		do_action( "updated_{$meta_type}_meta", $meta_id, $object_id, $meta_key, $_meta_value );
    878 
    879 		if ( 'post' === $meta_type ) {
    880 			/** This action is documented in wp-includes/meta.php */
    881 			do_action( 'updated_postmeta', $meta_id, $object_id, $meta_key, $meta_value );
    882 		}
    883 
    884 		return true;
    885 	}
    886 
    887 	// And if the meta was not found.
    888 	return false;
    889 }
    890 
    891 /**
    892  * Deletes metadata by meta ID.
    893  *
    894  * @since 3.3.0
    895  *
    896  * @global wpdb $wpdb WordPress database abstraction object.
    897  *
    898  * @param string $meta_type Type of object metadata is for. Accepts 'post', 'comment', 'term', 'user',
    899  *                          or any other object type with an associated meta table.
    900  * @param int    $meta_id   ID for a specific meta row.
    901  * @return bool True on successful delete, false on failure.
    902  */
    903 function delete_metadata_by_mid( $meta_type, $meta_id ) {
    904 	global $wpdb;
    905 
    906 	// Make sure everything is valid.
    907 	if ( ! $meta_type || ! is_numeric( $meta_id ) || floor( $meta_id ) != $meta_id ) {
    908 		return false;
    909 	}
    910 
    911 	$meta_id = (int) $meta_id;
    912 	if ( $meta_id <= 0 ) {
    913 		return false;
    914 	}
    915 
    916 	$table = _get_meta_table( $meta_type );
    917 	if ( ! $table ) {
    918 		return false;
    919 	}
    920 
    921 	// Object and ID columns.
    922 	$column    = sanitize_key( $meta_type . '_id' );
    923 	$id_column = ( 'user' === $meta_type ) ? 'umeta_id' : 'meta_id';
    924 
    925 	/**
    926 	 * Short-circuits deleting metadata of a specific type by meta ID.
    927 	 *
    928 	 * The dynamic portion of the hook, `$meta_type`, refers to the meta object type
    929 	 * (post, comment, term, user, or any other type with an associated meta table).
    930 	 * Returning a non-null value will effectively short-circuit the function.
    931 	 *
    932 	 * @since 5.0.0
    933 	 *
    934 	 * @param null|bool $delete  Whether to allow metadata deletion of the given type.
    935 	 * @param int       $meta_id Meta ID.
    936 	 */
    937 	$check = apply_filters( "delete_{$meta_type}_metadata_by_mid", null, $meta_id );
    938 	if ( null !== $check ) {
    939 		return (bool) $check;
    940 	}
    941 
    942 	// Fetch the meta and go on if it's found.
    943 	$meta = get_metadata_by_mid( $meta_type, $meta_id );
    944 	if ( $meta ) {
    945 		$object_id = (int) $meta->{$column};
    946 
    947 		/** This action is documented in wp-includes/meta.php */
    948 		do_action( "delete_{$meta_type}_meta", (array) $meta_id, $object_id, $meta->meta_key, $meta->meta_value );
    949 
    950 		// Old-style action.
    951 		if ( 'post' === $meta_type || 'comment' === $meta_type ) {
    952 			/**
    953 			 * Fires immediately before deleting post or comment metadata of a specific type.
    954 			 *
    955 			 * The dynamic portion of the hook, `$meta_type`, refers to the meta
    956 			 * object type (post or comment).
    957 			 *
    958 			 * @since 3.4.0
    959 			 *
    960 			 * @param int $meta_id ID of the metadata entry to delete.
    961 			 */
    962 			do_action( "delete_{$meta_type}meta", $meta_id );
    963 		}
    964 
    965 		// Run the query, will return true if deleted, false otherwise.
    966 		$result = (bool) $wpdb->delete( $table, array( $id_column => $meta_id ) );
    967 
    968 		// Clear the caches.
    969 		wp_cache_delete( $object_id, $meta_type . '_meta' );
    970 
    971 		/** This action is documented in wp-includes/meta.php */
    972 		do_action( "deleted_{$meta_type}_meta", (array) $meta_id, $object_id, $meta->meta_key, $meta->meta_value );
    973 
    974 		// Old-style action.
    975 		if ( 'post' === $meta_type || 'comment' === $meta_type ) {
    976 			/**
    977 			 * Fires immediately after deleting post or comment metadata of a specific type.
    978 			 *
    979 			 * The dynamic portion of the hook, `$meta_type`, refers to the meta
    980 			 * object type (post or comment).
    981 			 *
    982 			 * @since 3.4.0
    983 			 *
    984 			 * @param int $meta_ids Deleted metadata entry ID.
    985 			 */
    986 			do_action( "deleted_{$meta_type}meta", $meta_id );
    987 		}
    988 
    989 		return $result;
    990 
    991 	}
    992 
    993 	// Meta ID was not found.
    994 	return false;
    995 }
    996 
    997 /**
    998  * Updates the metadata cache for the specified objects.
    999  *
   1000  * @since 2.9.0
   1001  *
   1002  * @global wpdb $wpdb WordPress database abstraction object.
   1003  *
   1004  * @param string       $meta_type  Type of object metadata is for. Accepts 'post', 'comment', 'term', 'user',
   1005  *                                 or any other object type with an associated meta table.
   1006  * @param string|int[] $object_ids Array or comma delimited list of object IDs to update cache for.
   1007  * @return array|false Metadata cache for the specified objects, or false on failure.
   1008  */
   1009 function update_meta_cache( $meta_type, $object_ids ) {
   1010 	global $wpdb;
   1011 
   1012 	if ( ! $meta_type || ! $object_ids ) {
   1013 		return false;
   1014 	}
   1015 
   1016 	$table = _get_meta_table( $meta_type );
   1017 	if ( ! $table ) {
   1018 		return false;
   1019 	}
   1020 
   1021 	$column = sanitize_key( $meta_type . '_id' );
   1022 
   1023 	if ( ! is_array( $object_ids ) ) {
   1024 		$object_ids = preg_replace( '|[^0-9,]|', '', $object_ids );
   1025 		$object_ids = explode( ',', $object_ids );
   1026 	}
   1027 
   1028 	$object_ids = array_map( 'intval', $object_ids );
   1029 
   1030 	/**
   1031 	 * Short-circuits updating the metadata cache of a specific type.
   1032 	 *
   1033 	 * The dynamic portion of the hook, `$meta_type`, refers to the meta object type
   1034 	 * (post, comment, term, user, or any other type with an associated meta table).
   1035 	 * Returning a non-null value will effectively short-circuit the function.
   1036 	 *
   1037 	 * @since 5.0.0
   1038 	 *
   1039 	 * @param mixed $check      Whether to allow updating the meta cache of the given type.
   1040 	 * @param int[] $object_ids Array of object IDs to update the meta cache for.
   1041 	 */
   1042 	$check = apply_filters( "update_{$meta_type}_metadata_cache", null, $object_ids );
   1043 	if ( null !== $check ) {
   1044 		return (bool) $check;
   1045 	}
   1046 
   1047 	$cache_key      = $meta_type . '_meta';
   1048 	$non_cached_ids = array();
   1049 	$cache          = array();
   1050 	$cache_values   = wp_cache_get_multiple( $object_ids, $cache_key );
   1051 
   1052 	foreach ( $cache_values as $id => $cached_object ) {
   1053 		if ( false === $cached_object ) {
   1054 			$non_cached_ids[] = $id;
   1055 		} else {
   1056 			$cache[ $id ] = $cached_object;
   1057 		}
   1058 	}
   1059 
   1060 	if ( empty( $non_cached_ids ) ) {
   1061 		return $cache;
   1062 	}
   1063 
   1064 	// Get meta info.
   1065 	$id_list   = implode( ',', $non_cached_ids );
   1066 	$id_column = ( 'user' === $meta_type ) ? 'umeta_id' : 'meta_id';
   1067 
   1068 	$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 );
   1069 
   1070 	if ( ! empty( $meta_list ) ) {
   1071 		foreach ( $meta_list as $metarow ) {
   1072 			$mpid = (int) $metarow[ $column ];
   1073 			$mkey = $metarow['meta_key'];
   1074 			$mval = $metarow['meta_value'];
   1075 
   1076 			// Force subkeys to be array type.
   1077 			if ( ! isset( $cache[ $mpid ] ) || ! is_array( $cache[ $mpid ] ) ) {
   1078 				$cache[ $mpid ] = array();
   1079 			}
   1080 			if ( ! isset( $cache[ $mpid ][ $mkey ] ) || ! is_array( $cache[ $mpid ][ $mkey ] ) ) {
   1081 				$cache[ $mpid ][ $mkey ] = array();
   1082 			}
   1083 
   1084 			// Add a value to the current pid/key.
   1085 			$cache[ $mpid ][ $mkey ][] = $mval;
   1086 		}
   1087 	}
   1088 
   1089 	foreach ( $non_cached_ids as $id ) {
   1090 		if ( ! isset( $cache[ $id ] ) ) {
   1091 			$cache[ $id ] = array();
   1092 		}
   1093 		wp_cache_add( $id, $cache[ $id ], $cache_key );
   1094 	}
   1095 
   1096 	return $cache;
   1097 }
   1098 
   1099 /**
   1100  * Retrieves the queue for lazy-loading metadata.
   1101  *
   1102  * @since 4.5.0
   1103  *
   1104  * @return WP_Metadata_Lazyloader Metadata lazyloader queue.
   1105  */
   1106 function wp_metadata_lazyloader() {
   1107 	static $wp_metadata_lazyloader;
   1108 
   1109 	if ( null === $wp_metadata_lazyloader ) {
   1110 		$wp_metadata_lazyloader = new WP_Metadata_Lazyloader();
   1111 	}
   1112 
   1113 	return $wp_metadata_lazyloader;
   1114 }
   1115 
   1116 /**
   1117  * Given a meta query, generates SQL clauses to be appended to a main query.
   1118  *
   1119  * @since 3.2.0
   1120  *
   1121  * @see WP_Meta_Query
   1122  *
   1123  * @param array  $meta_query        A meta query.
   1124  * @param string $type              Type of meta.
   1125  * @param string $primary_table     Primary database table name.
   1126  * @param string $primary_id_column Primary ID column name.
   1127  * @param object $context           Optional. The main query object
   1128  * @return array Associative array of `JOIN` and `WHERE` SQL.
   1129  */
   1130 function get_meta_sql( $meta_query, $type, $primary_table, $primary_id_column, $context = null ) {
   1131 	$meta_query_obj = new WP_Meta_Query( $meta_query );
   1132 	return $meta_query_obj->get_sql( $type, $primary_table, $primary_id_column, $context );
   1133 }
   1134 
   1135 /**
   1136  * Retrieves the name of the metadata table for the specified object type.
   1137  *
   1138  * @since 2.9.0
   1139  *
   1140  * @global wpdb $wpdb WordPress database abstraction object.
   1141  *
   1142  * @param string $type Type of object metadata is for. Accepts 'post', 'comment', 'term', 'user',
   1143  *                     or any other object type with an associated meta table.
   1144  * @return string|false Metadata table name, or false if no metadata table exists
   1145  */
   1146 function _get_meta_table( $type ) {
   1147 	global $wpdb;
   1148 
   1149 	$table_name = $type . 'meta';
   1150 
   1151 	if ( empty( $wpdb->$table_name ) ) {
   1152 		return false;
   1153 	}
   1154 
   1155 	return $wpdb->$table_name;
   1156 }
   1157 
   1158 /**
   1159  * Determines whether a meta key is considered protected.
   1160  *
   1161  * @since 3.1.3
   1162  *
   1163  * @param string $meta_key  Metadata key.
   1164  * @param string $meta_type Optional. Type of object metadata is for. Accepts 'post', 'comment', 'term', 'user',
   1165  *                          or any other object type with an associated meta table. Default empty.
   1166  * @return bool Whether the meta key is considered protected.
   1167  */
   1168 function is_protected_meta( $meta_key, $meta_type = '' ) {
   1169 	$sanitized_key = preg_replace( "/[^\x20-\x7E\p{L}]/", '', $meta_key );
   1170 	$protected     = strlen( $sanitized_key ) > 0 && ( '_' === $sanitized_key[0] );
   1171 
   1172 	/**
   1173 	 * Filters whether a meta key is considered protected.
   1174 	 *
   1175 	 * @since 3.2.0
   1176 	 *
   1177 	 * @param bool   $protected Whether the key is considered protected.
   1178 	 * @param string $meta_key  Metadata key.
   1179 	 * @param string $meta_type Type of object metadata is for. Accepts 'post', 'comment', 'term', 'user',
   1180 	 *                          or any other object type with an associated meta table.
   1181 	 */
   1182 	return apply_filters( 'is_protected_meta', $protected, $meta_key, $meta_type );
   1183 }
   1184 
   1185 /**
   1186  * Sanitizes meta value.
   1187  *
   1188  * @since 3.1.3
   1189  * @since 4.9.8 The `$object_subtype` parameter was added.
   1190  *
   1191  * @param string $meta_key       Metadata key.
   1192  * @param mixed  $meta_value     Metadata value to sanitize.
   1193  * @param string $object_type    Type of object metadata is for. Accepts 'post', 'comment', 'term', 'user',
   1194  *                               or any other object type with an associated meta table.
   1195  * @param string $object_subtype Optional. The subtype of the object type.
   1196  * @return mixed Sanitized $meta_value.
   1197  */
   1198 function sanitize_meta( $meta_key, $meta_value, $object_type, $object_subtype = '' ) {
   1199 	if ( ! empty( $object_subtype ) && has_filter( "sanitize_{$object_type}_meta_{$meta_key}_for_{$object_subtype}" ) ) {
   1200 
   1201 		/**
   1202 		 * Filters the sanitization of a specific meta key of a specific meta type and subtype.
   1203 		 *
   1204 		 * The dynamic portions of the hook name, `$object_type`, `$meta_key`,
   1205 		 * and `$object_subtype`, refer to the metadata object type (comment, post, term, or user),
   1206 		 * the meta key value, and the object subtype respectively.
   1207 		 *
   1208 		 * @since 4.9.8
   1209 		 *
   1210 		 * @param mixed  $meta_value     Metadata value to sanitize.
   1211 		 * @param string $meta_key       Metadata key.
   1212 		 * @param string $object_type    Type of object metadata is for. Accepts 'post', 'comment', 'term', 'user',
   1213 		 *                               or any other object type with an associated meta table.
   1214 		 * @param string $object_subtype Object subtype.
   1215 		 */
   1216 		return apply_filters( "sanitize_{$object_type}_meta_{$meta_key}_for_{$object_subtype}", $meta_value, $meta_key, $object_type, $object_subtype );
   1217 	}
   1218 
   1219 	/**
   1220 	 * Filters the sanitization of a specific meta key of a specific meta type.
   1221 	 *
   1222 	 * The dynamic portions of the hook name, `$meta_type`, and `$meta_key`,
   1223 	 * refer to the metadata object type (comment, post, term, or user) and the meta
   1224 	 * key value, respectively.
   1225 	 *
   1226 	 * @since 3.3.0
   1227 	 *
   1228 	 * @param mixed  $meta_value  Metadata value to sanitize.
   1229 	 * @param string $meta_key    Metadata key.
   1230 	 * @param string $object_type Type of object metadata is for. Accepts 'post', 'comment', 'term', 'user',
   1231 	 *                            or any other object type with an associated meta table.
   1232 	 */
   1233 	return apply_filters( "sanitize_{$object_type}_meta_{$meta_key}", $meta_value, $meta_key, $object_type );
   1234 }
   1235 
   1236 /**
   1237  * Registers a meta key.
   1238  *
   1239  * It is recommended to register meta keys for a specific combination of object type and object subtype. If passing
   1240  * an object subtype is omitted, the meta key will be registered for the entire object type, however it can be partly
   1241  * overridden in case a more specific meta key of the same name exists for the same object type and a subtype.
   1242  *
   1243  * If an object type does not support any subtypes, such as users or comments, you should commonly call this function
   1244  * without passing a subtype.
   1245  *
   1246  * @since 3.3.0
   1247  * @since 4.6.0 {@link https://core.trac.wordpress.org/ticket/35658 Modified
   1248  *              to support an array of data to attach to registered meta keys}. Previous arguments for
   1249  *              `$sanitize_callback` and `$auth_callback` have been folded into this array.
   1250  * @since 4.9.8 The `$object_subtype` argument was added to the arguments array.
   1251  * @since 5.3.0 Valid meta types expanded to include "array" and "object".
   1252  * @since 5.5.0 The `$default` argument was added to the arguments array.
   1253  *
   1254  * @param string       $object_type Type of object metadata is for. Accepts 'post', 'comment', 'term', 'user',
   1255  *                                  or any other object type with an associated meta table.
   1256  * @param string       $meta_key    Meta key to register.
   1257  * @param array        $args {
   1258  *     Data used to describe the meta key when registered.
   1259  *
   1260  *     @type string     $object_subtype    A subtype; e.g. if the object type is "post", the post type. If left empty,
   1261  *                                         the meta key will be registered on the entire object type. Default empty.
   1262  *     @type string     $type              The type of data associated with this meta key.
   1263  *                                         Valid values are 'string', 'boolean', 'integer', 'number', 'array', and 'object'.
   1264  *     @type string     $description       A description of the data attached to this meta key.
   1265  *     @type bool       $single            Whether the meta key has one value per object, or an array of values per object.
   1266  *     @type mixed      $default           The default value returned from get_metadata() if no value has been set yet.
   1267  *                                         When using a non-single meta key, the default value is for the first entry.
   1268  *                                         In other words, when calling get_metadata() with `$single` set to `false`,
   1269  *                                         the default value given here will be wrapped in an array.
   1270  *     @type callable   $sanitize_callback A function or method to call when sanitizing `$meta_key` data.
   1271  *     @type callable   $auth_callback     Optional. A function or method to call when performing edit_post_meta,
   1272  *                                         add_post_meta, and delete_post_meta capability checks.
   1273  *     @type bool|array $show_in_rest      Whether data associated with this meta key can be considered public and
   1274  *                                         should be accessible via the REST API. A custom post type must also declare
   1275  *                                         support for custom fields for registered meta to be accessible via REST.
   1276  *                                         When registering complex meta values this argument may optionally be an
   1277  *                                         array with 'schema' or 'prepare_callback' keys instead of a boolean.
   1278  * }
   1279  * @param string|array $deprecated Deprecated. Use `$args` instead.
   1280  * @return bool True if the meta key was successfully registered in the global array, false if not.
   1281  *              Registering a meta key with distinct sanitize and auth callbacks will fire those callbacks,
   1282  *              but will not add to the global registry.
   1283  */
   1284 function register_meta( $object_type, $meta_key, $args, $deprecated = null ) {
   1285 	global $wp_meta_keys;
   1286 
   1287 	if ( ! is_array( $wp_meta_keys ) ) {
   1288 		$wp_meta_keys = array();
   1289 	}
   1290 
   1291 	$defaults = array(
   1292 		'object_subtype'    => '',
   1293 		'type'              => 'string',
   1294 		'description'       => '',
   1295 		'default'           => '',
   1296 		'single'            => false,
   1297 		'sanitize_callback' => null,
   1298 		'auth_callback'     => null,
   1299 		'show_in_rest'      => false,
   1300 	);
   1301 
   1302 	// There used to be individual args for sanitize and auth callbacks.
   1303 	$has_old_sanitize_cb = false;
   1304 	$has_old_auth_cb     = false;
   1305 
   1306 	if ( is_callable( $args ) ) {
   1307 		$args = array(
   1308 			'sanitize_callback' => $args,
   1309 		);
   1310 
   1311 		$has_old_sanitize_cb = true;
   1312 	} else {
   1313 		$args = (array) $args;
   1314 	}
   1315 
   1316 	if ( is_callable( $deprecated ) ) {
   1317 		$args['auth_callback'] = $deprecated;
   1318 		$has_old_auth_cb       = true;
   1319 	}
   1320 
   1321 	/**
   1322 	 * Filters the registration arguments when registering meta.
   1323 	 *
   1324 	 * @since 4.6.0
   1325 	 *
   1326 	 * @param array  $args        Array of meta registration arguments.
   1327 	 * @param array  $defaults    Array of default arguments.
   1328 	 * @param string $object_type Type of object metadata is for. Accepts 'post', 'comment', 'term', 'user',
   1329 	 *                            or any other object type with an associated meta table.
   1330 	 * @param string $meta_key    Meta key.
   1331 	 */
   1332 	$args = apply_filters( 'register_meta_args', $args, $defaults, $object_type, $meta_key );
   1333 	unset( $defaults['default'] );
   1334 	$args = wp_parse_args( $args, $defaults );
   1335 
   1336 	// Require an item schema when registering array meta.
   1337 	if ( false !== $args['show_in_rest'] && 'array' === $args['type'] ) {
   1338 		if ( ! is_array( $args['show_in_rest'] ) || ! isset( $args['show_in_rest']['schema']['items'] ) ) {
   1339 			_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' );
   1340 
   1341 			return false;
   1342 		}
   1343 	}
   1344 
   1345 	$object_subtype = ! empty( $args['object_subtype'] ) ? $args['object_subtype'] : '';
   1346 
   1347 	// If `auth_callback` is not provided, fall back to `is_protected_meta()`.
   1348 	if ( empty( $args['auth_callback'] ) ) {
   1349 		if ( is_protected_meta( $meta_key, $object_type ) ) {
   1350 			$args['auth_callback'] = '__return_false';
   1351 		} else {
   1352 			$args['auth_callback'] = '__return_true';
   1353 		}
   1354 	}
   1355 
   1356 	// Back-compat: old sanitize and auth callbacks are applied to all of an object type.
   1357 	if ( is_callable( $args['sanitize_callback'] ) ) {
   1358 		if ( ! empty( $object_subtype ) ) {
   1359 			add_filter( "sanitize_{$object_type}_meta_{$meta_key}_for_{$object_subtype}", $args['sanitize_callback'], 10, 4 );
   1360 		} else {
   1361 			add_filter( "sanitize_{$object_type}_meta_{$meta_key}", $args['sanitize_callback'], 10, 3 );
   1362 		}
   1363 	}
   1364 
   1365 	if ( is_callable( $args['auth_callback'] ) ) {
   1366 		if ( ! empty( $object_subtype ) ) {
   1367 			add_filter( "auth_{$object_type}_meta_{$meta_key}_for_{$object_subtype}", $args['auth_callback'], 10, 6 );
   1368 		} else {
   1369 			add_filter( "auth_{$object_type}_meta_{$meta_key}", $args['auth_callback'], 10, 6 );
   1370 		}
   1371 	}
   1372 
   1373 	if ( array_key_exists( 'default', $args ) ) {
   1374 		$schema = $args;
   1375 		if ( is_array( $args['show_in_rest'] ) && isset( $args['show_in_rest']['schema'] ) ) {
   1376 			$schema = array_merge( $schema, $args['show_in_rest']['schema'] );
   1377 		}
   1378 
   1379 		$check = rest_validate_value_from_schema( $args['default'], $schema );
   1380 		if ( is_wp_error( $check ) ) {
   1381 			_doing_it_wrong( __FUNCTION__, __( 'When registering a default meta value the data must match the type provided.' ), '5.5.0' );
   1382 
   1383 			return false;
   1384 		}
   1385 
   1386 		if ( ! has_filter( "default_{$object_type}_metadata", 'filter_default_metadata' ) ) {
   1387 			add_filter( "default_{$object_type}_metadata", 'filter_default_metadata', 10, 5 );
   1388 		}
   1389 	}
   1390 
   1391 	// Global registry only contains meta keys registered with the array of arguments added in 4.6.0.
   1392 	if ( ! $has_old_auth_cb && ! $has_old_sanitize_cb ) {
   1393 		unset( $args['object_subtype'] );
   1394 
   1395 		$wp_meta_keys[ $object_type ][ $object_subtype ][ $meta_key ] = $args;
   1396 
   1397 		return true;
   1398 	}
   1399 
   1400 	return false;
   1401 }
   1402 
   1403 /**
   1404  * Filters into default_{$object_type}_metadata and adds in default value.
   1405  *
   1406  * @since 5.5.0
   1407  *
   1408  * @param mixed  $value     Current value passed to filter.
   1409  * @param int    $object_id ID of the object metadata is for.
   1410  * @param string $meta_key  Metadata key.
   1411  * @param bool   $single    If true, return only the first value of the specified `$meta_key`.
   1412  *                          This parameter has no effect if `$meta_key` is not specified.
   1413  * @param string $meta_type Type of object metadata is for. Accepts 'post', 'comment', 'term', 'user',
   1414  *                          or any other object type with an associated meta table.
   1415  * @return mixed An array of default values if `$single` is false.
   1416  *               The default value of the meta field if `$single` is true.
   1417  */
   1418 function filter_default_metadata( $value, $object_id, $meta_key, $single, $meta_type ) {
   1419 	global $wp_meta_keys;
   1420 
   1421 	if ( wp_installing() ) {
   1422 		return $value;
   1423 	}
   1424 
   1425 	if ( ! is_array( $wp_meta_keys ) || ! isset( $wp_meta_keys[ $meta_type ] ) ) {
   1426 		return $value;
   1427 	}
   1428 
   1429 	$defaults = array();
   1430 	foreach ( $wp_meta_keys[ $meta_type ] as $sub_type => $meta_data ) {
   1431 		foreach ( $meta_data as $_meta_key => $args ) {
   1432 			if ( $_meta_key === $meta_key && array_key_exists( 'default', $args ) ) {
   1433 				$defaults[ $sub_type ] = $args;
   1434 			}
   1435 		}
   1436 	}
   1437 
   1438 	if ( ! $defaults ) {
   1439 		return $value;
   1440 	}
   1441 
   1442 	// If this meta type does not have subtypes, then the default is keyed as an empty string.
   1443 	if ( isset( $defaults[''] ) ) {
   1444 		$metadata = $defaults[''];
   1445 	} else {
   1446 		$sub_type = get_object_subtype( $meta_type, $object_id );
   1447 		if ( ! isset( $defaults[ $sub_type ] ) ) {
   1448 			return $value;
   1449 		}
   1450 		$metadata = $defaults[ $sub_type ];
   1451 	}
   1452 
   1453 	if ( $single ) {
   1454 		$value = $metadata['default'];
   1455 	} else {
   1456 		$value = array( $metadata['default'] );
   1457 	}
   1458 
   1459 	return $value;
   1460 }
   1461 
   1462 /**
   1463  * Checks if a meta key is registered.
   1464  *
   1465  * @since 4.6.0
   1466  * @since 4.9.8 The `$object_subtype` parameter was added.
   1467  *
   1468  * @param string $object_type    Type of object metadata is for. Accepts 'post', 'comment', 'term', 'user',
   1469  *                               or any other object type with an associated meta table.
   1470  * @param string $meta_key       Metadata key.
   1471  * @param string $object_subtype Optional. The subtype of the object type.
   1472  * @return bool True if the meta key is registered to the object type and, if provided,
   1473  *              the object subtype. False if not.
   1474  */
   1475 function registered_meta_key_exists( $object_type, $meta_key, $object_subtype = '' ) {
   1476 	$meta_keys = get_registered_meta_keys( $object_type, $object_subtype );
   1477 
   1478 	return isset( $meta_keys[ $meta_key ] );
   1479 }
   1480 
   1481 /**
   1482  * Unregisters a meta key from the list of registered keys.
   1483  *
   1484  * @since 4.6.0
   1485  * @since 4.9.8 The `$object_subtype` parameter was added.
   1486  *
   1487  * @param string $object_type    Type of object metadata is for. Accepts 'post', 'comment', 'term', 'user',
   1488  *                               or any other object type with an associated meta table.
   1489  * @param string $meta_key       Metadata key.
   1490  * @param string $object_subtype Optional. The subtype of the object type.
   1491  * @return bool True if successful. False if the meta key was not registered.
   1492  */
   1493 function unregister_meta_key( $object_type, $meta_key, $object_subtype = '' ) {
   1494 	global $wp_meta_keys;
   1495 
   1496 	if ( ! registered_meta_key_exists( $object_type, $meta_key, $object_subtype ) ) {
   1497 		return false;
   1498 	}
   1499 
   1500 	$args = $wp_meta_keys[ $object_type ][ $object_subtype ][ $meta_key ];
   1501 
   1502 	if ( isset( $args['sanitize_callback'] ) && is_callable( $args['sanitize_callback'] ) ) {
   1503 		if ( ! empty( $object_subtype ) ) {
   1504 			remove_filter( "sanitize_{$object_type}_meta_{$meta_key}_for_{$object_subtype}", $args['sanitize_callback'] );
   1505 		} else {
   1506 			remove_filter( "sanitize_{$object_type}_meta_{$meta_key}", $args['sanitize_callback'] );
   1507 		}
   1508 	}
   1509 
   1510 	if ( isset( $args['auth_callback'] ) && is_callable( $args['auth_callback'] ) ) {
   1511 		if ( ! empty( $object_subtype ) ) {
   1512 			remove_filter( "auth_{$object_type}_meta_{$meta_key}_for_{$object_subtype}", $args['auth_callback'] );
   1513 		} else {
   1514 			remove_filter( "auth_{$object_type}_meta_{$meta_key}", $args['auth_callback'] );
   1515 		}
   1516 	}
   1517 
   1518 	unset( $wp_meta_keys[ $object_type ][ $object_subtype ][ $meta_key ] );
   1519 
   1520 	// Do some clean up.
   1521 	if ( empty( $wp_meta_keys[ $object_type ][ $object_subtype ] ) ) {
   1522 		unset( $wp_meta_keys[ $object_type ][ $object_subtype ] );
   1523 	}
   1524 	if ( empty( $wp_meta_keys[ $object_type ] ) ) {
   1525 		unset( $wp_meta_keys[ $object_type ] );
   1526 	}
   1527 
   1528 	return true;
   1529 }
   1530 
   1531 /**
   1532  * Retrieves a list of registered meta keys for an object type.
   1533  *
   1534  * @since 4.6.0
   1535  * @since 4.9.8 The `$object_subtype` parameter was added.
   1536  *
   1537  * @param string $object_type    Type of object metadata is for. Accepts 'post', 'comment', 'term', 'user',
   1538  *                               or any other object type with an associated meta table.
   1539  * @param string $object_subtype Optional. The subtype of the object type.
   1540  * @return string[] List of registered meta keys.
   1541  */
   1542 function get_registered_meta_keys( $object_type, $object_subtype = '' ) {
   1543 	global $wp_meta_keys;
   1544 
   1545 	if ( ! is_array( $wp_meta_keys ) || ! isset( $wp_meta_keys[ $object_type ] ) || ! isset( $wp_meta_keys[ $object_type ][ $object_subtype ] ) ) {
   1546 		return array();
   1547 	}
   1548 
   1549 	return $wp_meta_keys[ $object_type ][ $object_subtype ];
   1550 }
   1551 
   1552 /**
   1553  * Retrieves registered metadata for a specified object.
   1554  *
   1555  * The results include both meta that is registered specifically for the
   1556  * object's subtype and meta that is registered for the entire object type.
   1557  *
   1558  * @since 4.6.0
   1559  *
   1560  * @param string $object_type Type of object metadata is for. Accepts 'post', 'comment', 'term', 'user',
   1561  *                            or any other object type with an associated meta table.
   1562  * @param int    $object_id   ID of the object the metadata is for.
   1563  * @param string $meta_key    Optional. Registered metadata key. If not specified, retrieve all registered
   1564  *                            metadata for the specified object.
   1565  * @return mixed A single value or array of values for a key if specified. An array of all registered keys
   1566  *               and values for an object ID if not. False if a given $meta_key is not registered.
   1567  */
   1568 function get_registered_metadata( $object_type, $object_id, $meta_key = '' ) {
   1569 	$object_subtype = get_object_subtype( $object_type, $object_id );
   1570 
   1571 	if ( ! empty( $meta_key ) ) {
   1572 		if ( ! empty( $object_subtype ) && ! registered_meta_key_exists( $object_type, $meta_key, $object_subtype ) ) {
   1573 			$object_subtype = '';
   1574 		}
   1575 
   1576 		if ( ! registered_meta_key_exists( $object_type, $meta_key, $object_subtype ) ) {
   1577 			return false;
   1578 		}
   1579 
   1580 		$meta_keys     = get_registered_meta_keys( $object_type, $object_subtype );
   1581 		$meta_key_data = $meta_keys[ $meta_key ];
   1582 
   1583 		$data = get_metadata( $object_type, $object_id, $meta_key, $meta_key_data['single'] );
   1584 
   1585 		return $data;
   1586 	}
   1587 
   1588 	$data = get_metadata( $object_type, $object_id );
   1589 	if ( ! $data ) {
   1590 		return array();
   1591 	}
   1592 
   1593 	$meta_keys = get_registered_meta_keys( $object_type );
   1594 	if ( ! empty( $object_subtype ) ) {
   1595 		$meta_keys = array_merge( $meta_keys, get_registered_meta_keys( $object_type, $object_subtype ) );
   1596 	}
   1597 
   1598 	return array_intersect_key( $data, $meta_keys );
   1599 }
   1600 
   1601 /**
   1602  * Filters out `register_meta()` args based on an allowed list.
   1603  *
   1604  * `register_meta()` args may change over time, so requiring the allowed list
   1605  * to be explicitly turned off is a warranty seal of sorts.
   1606  *
   1607  * @access private
   1608  * @since 5.5.0
   1609  *
   1610  * @param array $args         Arguments from `register_meta()`.
   1611  * @param array $default_args Default arguments for `register_meta()`.
   1612  * @return array Filtered arguments.
   1613  */
   1614 function _wp_register_meta_args_allowed_list( $args, $default_args ) {
   1615 	return array_intersect_key( $args, $default_args );
   1616 }
   1617 
   1618 /**
   1619  * Returns the object subtype for a given object ID of a specific type.
   1620  *
   1621  * @since 4.9.8
   1622  *
   1623  * @param string $object_type Type of object metadata is for. Accepts 'post', 'comment', 'term', 'user',
   1624  *                            or any other object type with an associated meta table.
   1625  * @param int    $object_id   ID of the object to retrieve its subtype.
   1626  * @return string The object subtype or an empty string if unspecified subtype.
   1627  */
   1628 function get_object_subtype( $object_type, $object_id ) {
   1629 	$object_id      = (int) $object_id;
   1630 	$object_subtype = '';
   1631 
   1632 	switch ( $object_type ) {
   1633 		case 'post':
   1634 			$post_type = get_post_type( $object_id );
   1635 
   1636 			if ( ! empty( $post_type ) ) {
   1637 				$object_subtype = $post_type;
   1638 			}
   1639 			break;
   1640 
   1641 		case 'term':
   1642 			$term = get_term( $object_id );
   1643 			if ( ! $term instanceof WP_Term ) {
   1644 				break;
   1645 			}
   1646 
   1647 			$object_subtype = $term->taxonomy;
   1648 			break;
   1649 
   1650 		case 'comment':
   1651 			$comment = get_comment( $object_id );
   1652 			if ( ! $comment ) {
   1653 				break;
   1654 			}
   1655 
   1656 			$object_subtype = 'comment';
   1657 			break;
   1658 
   1659 		case 'user':
   1660 			$user = get_user_by( 'id', $object_id );
   1661 			if ( ! $user ) {
   1662 				break;
   1663 			}
   1664 
   1665 			$object_subtype = 'user';
   1666 			break;
   1667 	}
   1668 
   1669 	/**
   1670 	 * Filters the object subtype identifier for a non-standard object type.
   1671 	 *
   1672 	 * The dynamic portion of the hook, `$object_type`, refers to the meta object type
   1673 	 * (post, comment, term, user, or any other type with an associated meta table).
   1674 	 *
   1675 	 * @since 4.9.8
   1676 	 *
   1677 	 * @param string $object_subtype Empty string to override.
   1678 	 * @param int    $object_id      ID of the object to get the subtype for.
   1679 	 */
   1680 	return apply_filters( "get_object_subtype_{$object_type}", $object_subtype, $object_id );
   1681 }