* 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.
* Adds metadata for the specified object.
* @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 ) {
if ( ! $meta_type || ! $meta_key || ! is_numeric( $object_id ) ) {
$object_id = absint( $object_id );
$table = _get_meta_table( $meta_type );
$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, `$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.
* @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 ( $unique && $wpdb->get_var(
"SELECT COUNT(*) FROM $table WHERE meta_key = %s AND $column = %d",
$_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, `$meta_type`, refers to the meta object type
* (post, comment, term, user, or any other 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. Serialized if non-scalar.
do_action( "add_{$meta_type}_meta", $object_id, $meta_key, $_meta_value );
'meta_value' => $meta_value,
$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, `$meta_type`, refers to the meta object type
* (post, comment, term, user, or any other type with an associated meta table).
* @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. Serialized if non-scalar.
do_action( "added_{$meta_type}_meta", $mid, $object_id, $meta_key, $_meta_value );
* Updates metadata for the specified object. If no value already exists for the specified object
* ID and metadata key, the metadata will be added.
* @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.
* @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 = '' ) {
if ( ! $meta_type || ! $meta_key || ! is_numeric( $object_id ) ) {
$object_id = absint( $object_id );
$table = _get_meta_table( $meta_type );
$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, `$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.
* @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 );
// 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 ) {
$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' );
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, `$meta_type`, refers to the meta object type
* (post, comment, term, user, or any other type with an associated meta table).
* @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. Serialized if non-scalar.
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.
* @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 );
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, `$meta_type`, refers to the meta object type
* (post, comment, term, user, or any other type with an associated meta table).
* @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. Serialized if non-scalar.
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.
* @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 );
* Deletes metadata for the specified object.
* @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.)
* @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 ) {
if ( ! $meta_type || ! $meta_key || ! is_numeric( $object_id ) && ! $delete_all ) {
$object_id = absint( $object_id );
if ( ! $object_id && ! $delete_all ) {
$table = _get_meta_table( $meta_type );
$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, `$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.
* @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.
$check = apply_filters( "delete_{$meta_type}_metadata", null, $object_id, $meta_key, $meta_value, $delete_all );
$_meta_value = $meta_value;
$meta_value = maybe_serialize( $meta_value );
$query = $wpdb->prepare( "SELECT $id_column FROM $table WHERE meta_key = %s", $meta_key );
$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 ) ) {
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 ) );
$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, `$meta_type`, refers to the meta object type
* (post, comment, term, user, or any other type with an associated meta table).
* @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. Serialized if non-scalar.
do_action( "delete_{$meta_type}_meta", $meta_ids, $object_id, $meta_key, $_meta_value );
if ( 'post' === $meta_type ) {
* Fires immediately before deleting metadata for a post.
* @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 );
foreach ( (array) $object_ids as $o_id ) {
wp_cache_delete( $o_id, $meta_type . '_meta' );
wp_cache_delete( $object_id, $meta_type . '_meta' );
* Fires immediately after deleting metadata of a specific type.
* The dynamic portion of the hook, `$meta_type`, refers to the meta object type
* (post, comment, term, user, or any other type with an associated meta table).
* @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. Serialized if non-scalar.
do_action( "deleted_{$meta_type}_meta", $meta_ids, $object_id, $meta_key, $_meta_value );
if ( 'post' === $meta_type ) {
* Fires immediately after deleting metadata for a post.
* @param string[] $meta_ids An array of metadata entry IDs to delete.
do_action( 'deleted_postmeta', $meta_ids );
* 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
* @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.
* @param bool $single Optional. If true, return only the first value of the specified meta_key.