[ Index ]

PHP Cross Reference of WordPress

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


Generated: Mon Apr 19 01:00:04 2021 Cross-referenced by PHPXref 0.7.1