[ Index ]

PHP Cross Reference of WordPress Trunk (Updated Daily)

Search

title

Body

[close]

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

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


Generated : Mon Nov 9 08:20:01 2026 Cross-referenced by PHPXref