You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 

1212 lines
36 KiB

  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. * Add metadata for the specified object.
  14. *
  15. * @since 2.9.0
  16. *
  17. * @global wpdb $wpdb WordPress database abstraction object.
  18. *
  19. * @param string $meta_type Type of object metadata is for (e.g., comment, post, or user)
  20. * @param int $object_id ID of the object metadata is for
  21. * @param string $meta_key Metadata key
  22. * @param mixed $meta_value Metadata value. Must be serializable if non-scalar.
  23. * @param bool $unique Optional, default is false.
  24. * Whether the specified metadata key should be unique for the object.
  25. * If true, and the object already has a value for the specified metadata key,
  26. * no change will be made.
  27. * @return int|false The meta ID on success, false on failure.
  28. */
  29. function add_metadata($meta_type, $object_id, $meta_key, $meta_value, $unique = false) {
  30. global $wpdb;
  31. if ( ! $meta_type || ! $meta_key || ! is_numeric( $object_id ) ) {
  32. return false;
  33. }
  34. $object_id = absint( $object_id );
  35. if ( ! $object_id ) {
  36. return false;
  37. }
  38. $table = _get_meta_table( $meta_type );
  39. if ( ! $table ) {
  40. return false;
  41. }
  42. $column = sanitize_key($meta_type . '_id');
  43. // expected_slashed ($meta_key)
  44. $meta_key = wp_unslash($meta_key);
  45. $meta_value = wp_unslash($meta_value);
  46. $meta_value = sanitize_meta( $meta_key, $meta_value, $meta_type );
  47. /**
  48. * Filters whether to add metadata of a specific type.
  49. *
  50. * The dynamic portion of the hook, `$meta_type`, refers to the meta
  51. * object type (comment, post, or user). Returning a non-null value
  52. * will effectively short-circuit the function.
  53. *
  54. * @since 3.1.0
  55. *
  56. * @param null|bool $check Whether to allow adding metadata for the given type.
  57. * @param int $object_id Object ID.
  58. * @param string $meta_key Meta key.
  59. * @param mixed $meta_value Meta value. Must be serializable if non-scalar.
  60. * @param bool $unique Whether the specified meta key should be unique
  61. * for the object. Optional. Default false.
  62. */
  63. $check = apply_filters( "add_{$meta_type}_metadata", null, $object_id, $meta_key, $meta_value, $unique );
  64. if ( null !== $check )
  65. return $check;
  66. if ( $unique && $wpdb->get_var( $wpdb->prepare(
  67. "SELECT COUNT(*) FROM $table WHERE meta_key = %s AND $column = %d",
  68. $meta_key, $object_id ) ) )
  69. return false;
  70. $_meta_value = $meta_value;
  71. $meta_value = maybe_serialize( $meta_value );
  72. /**
  73. * Fires immediately before meta of a specific type is added.
  74. *
  75. * The dynamic portion of the hook, `$meta_type`, refers to the meta
  76. * object type (comment, post, or user).
  77. *
  78. * @since 3.1.0
  79. *
  80. * @param int $object_id Object ID.
  81. * @param string $meta_key Meta key.
  82. * @param mixed $meta_value Meta value.
  83. */
  84. do_action( "add_{$meta_type}_meta", $object_id, $meta_key, $_meta_value );
  85. $result = $wpdb->insert( $table, array(
  86. $column => $object_id,
  87. 'meta_key' => $meta_key,
  88. 'meta_value' => $meta_value
  89. ) );
  90. if ( ! $result )
  91. return false;
  92. $mid = (int) $wpdb->insert_id;
  93. wp_cache_delete($object_id, $meta_type . '_meta');
  94. /**
  95. * Fires immediately after meta of a specific type is added.
  96. *
  97. * The dynamic portion of the hook, `$meta_type`, refers to the meta
  98. * object type (comment, post, or user).
  99. *
  100. * @since 2.9.0
  101. *
  102. * @param int $mid The meta ID after successful update.
  103. * @param int $object_id Object ID.
  104. * @param string $meta_key Meta key.
  105. * @param mixed $meta_value Meta value.
  106. */
  107. do_action( "added_{$meta_type}_meta", $mid, $object_id, $meta_key, $_meta_value );
  108. return $mid;
  109. }
  110. /**
  111. * Update metadata for the specified object. If no value already exists for the specified object
  112. * ID and metadata key, the metadata will be added.
  113. *
  114. * @since 2.9.0
  115. *
  116. * @global wpdb $wpdb WordPress database abstraction object.
  117. *
  118. * @param string $meta_type Type of object metadata is for (e.g., comment, post, or user)
  119. * @param int $object_id ID of the object metadata is for
  120. * @param string $meta_key Metadata key
  121. * @param mixed $meta_value Metadata value. Must be serializable if non-scalar.
  122. * @param mixed $prev_value Optional. If specified, only update existing metadata entries with
  123. * the specified value. Otherwise, update all entries.
  124. * @return int|bool Meta ID if the key didn't exist, true on successful update, false on failure.
  125. */
  126. function update_metadata($meta_type, $object_id, $meta_key, $meta_value, $prev_value = '') {
  127. global $wpdb;
  128. if ( ! $meta_type || ! $meta_key || ! is_numeric( $object_id ) ) {
  129. return false;
  130. }
  131. $object_id = absint( $object_id );
  132. if ( ! $object_id ) {
  133. return false;
  134. }
  135. $table = _get_meta_table( $meta_type );
  136. if ( ! $table ) {
  137. return false;
  138. }
  139. $column = sanitize_key($meta_type . '_id');
  140. $id_column = 'user' == $meta_type ? 'umeta_id' : 'meta_id';
  141. // expected_slashed ($meta_key)
  142. $raw_meta_key = $meta_key;
  143. $meta_key = wp_unslash($meta_key);
  144. $passed_value = $meta_value;
  145. $meta_value = wp_unslash($meta_value);
  146. $meta_value = sanitize_meta( $meta_key, $meta_value, $meta_type );
  147. /**
  148. * Filters whether to update metadata of a specific type.
  149. *
  150. * The dynamic portion of the hook, `$meta_type`, refers to the meta
  151. * object type (comment, post, or user). Returning a non-null value
  152. * will effectively short-circuit the function.
  153. *
  154. * @since 3.1.0
  155. *
  156. * @param null|bool $check Whether to allow updating metadata for the given type.
  157. * @param int $object_id Object ID.
  158. * @param string $meta_key Meta key.
  159. * @param mixed $meta_value Meta value. Must be serializable if non-scalar.
  160. * @param mixed $prev_value Optional. If specified, only update existing
  161. * metadata entries with the specified value.
  162. * Otherwise, update all entries.
  163. */
  164. $check = apply_filters( "update_{$meta_type}_metadata", null, $object_id, $meta_key, $meta_value, $prev_value );
  165. if ( null !== $check )
  166. return (bool) $check;
  167. // Compare existing value to new value if no prev value given and the key exists only once.
  168. if ( empty($prev_value) ) {
  169. $old_value = get_metadata($meta_type, $object_id, $meta_key);
  170. if ( count($old_value) == 1 ) {
  171. if ( $old_value[0] === $meta_value )
  172. return false;
  173. }
  174. }
  175. $meta_ids = $wpdb->get_col( $wpdb->prepare( "SELECT $id_column FROM $table WHERE meta_key = %s AND $column = %d", $meta_key, $object_id ) );
  176. if ( empty( $meta_ids ) ) {
  177. return add_metadata( $meta_type, $object_id, $raw_meta_key, $passed_value );
  178. }
  179. $_meta_value = $meta_value;
  180. $meta_value = maybe_serialize( $meta_value );
  181. $data = compact( 'meta_value' );
  182. $where = array( $column => $object_id, 'meta_key' => $meta_key );
  183. if ( !empty( $prev_value ) ) {
  184. $prev_value = maybe_serialize($prev_value);
  185. $where['meta_value'] = $prev_value;
  186. }
  187. foreach ( $meta_ids as $meta_id ) {
  188. /**
  189. * Fires immediately before updating metadata of a specific type.
  190. *
  191. * The dynamic portion of the hook, `$meta_type`, refers to the meta
  192. * object type (comment, post, or user).
  193. *
  194. * @since 2.9.0
  195. *
  196. * @param int $meta_id ID of the metadata entry to update.
  197. * @param int $object_id Object ID.
  198. * @param string $meta_key Meta key.
  199. * @param mixed $meta_value Meta value.
  200. */
  201. do_action( "update_{$meta_type}_meta", $meta_id, $object_id, $meta_key, $_meta_value );
  202. if ( 'post' == $meta_type ) {
  203. /**
  204. * Fires immediately before updating a post's metadata.
  205. *
  206. * @since 2.9.0
  207. *
  208. * @param int $meta_id ID of metadata entry to update.
  209. * @param int $object_id Object ID.
  210. * @param string $meta_key Meta key.
  211. * @param mixed $meta_value Meta value.
  212. */
  213. do_action( 'update_postmeta', $meta_id, $object_id, $meta_key, $meta_value );
  214. }
  215. }
  216. $result = $wpdb->update( $table, $data, $where );
  217. if ( ! $result )
  218. return false;
  219. wp_cache_delete($object_id, $meta_type . '_meta');
  220. foreach ( $meta_ids as $meta_id ) {
  221. /**
  222. * Fires immediately after updating metadata of a specific type.
  223. *
  224. * The dynamic portion of the hook, `$meta_type`, refers to the meta
  225. * object type (comment, post, or user).
  226. *
  227. * @since 2.9.0
  228. *
  229. * @param int $meta_id ID of updated metadata entry.
  230. * @param int $object_id Object ID.
  231. * @param string $meta_key Meta key.
  232. * @param mixed $meta_value Meta value.
  233. */
  234. do_action( "updated_{$meta_type}_meta", $meta_id, $object_id, $meta_key, $_meta_value );
  235. if ( 'post' == $meta_type ) {
  236. /**
  237. * Fires immediately after updating a post's metadata.
  238. *
  239. * @since 2.9.0
  240. *
  241. * @param int $meta_id ID of updated metadata entry.
  242. * @param int $object_id Object ID.
  243. * @param string $meta_key Meta key.
  244. * @param mixed $meta_value Meta value.
  245. */
  246. do_action( 'updated_postmeta', $meta_id, $object_id, $meta_key, $meta_value );
  247. }
  248. }
  249. return true;
  250. }
  251. /**
  252. * Delete metadata for the specified object.
  253. *
  254. * @since 2.9.0
  255. *
  256. * @global wpdb $wpdb WordPress database abstraction object.
  257. *
  258. * @param string $meta_type Type of object metadata is for (e.g., comment, post, or user)
  259. * @param int $object_id ID of the object metadata is for
  260. * @param string $meta_key Metadata key
  261. * @param mixed $meta_value Optional. Metadata value. Must be serializable if non-scalar. If specified, only delete
  262. * metadata entries with this value. Otherwise, delete all entries with the specified meta_key.
  263. * Pass `null, `false`, or an empty string to skip this check. (For backward compatibility,
  264. * it is not possible to pass an empty string to delete those entries with an empty string
  265. * for a value.)
  266. * @param bool $delete_all Optional, default is false. If true, delete matching metadata entries for all objects,
  267. * ignoring the specified object_id. Otherwise, only delete matching metadata entries for
  268. * the specified object_id.
  269. * @return bool True on successful delete, false on failure.
  270. */
  271. function delete_metadata($meta_type, $object_id, $meta_key, $meta_value = '', $delete_all = false) {
  272. global $wpdb;
  273. if ( ! $meta_type || ! $meta_key || ! is_numeric( $object_id ) && ! $delete_all ) {
  274. return false;
  275. }
  276. $object_id = absint( $object_id );
  277. if ( ! $object_id && ! $delete_all ) {
  278. return false;
  279. }
  280. $table = _get_meta_table( $meta_type );
  281. if ( ! $table ) {
  282. return false;
  283. }
  284. $type_column = sanitize_key($meta_type . '_id');
  285. $id_column = 'user' == $meta_type ? 'umeta_id' : 'meta_id';
  286. // expected_slashed ($meta_key)
  287. $meta_key = wp_unslash($meta_key);
  288. $meta_value = wp_unslash($meta_value);
  289. /**
  290. * Filters whether to delete metadata of a specific type.
  291. *
  292. * The dynamic portion of the hook, `$meta_type`, refers to the meta
  293. * object type (comment, post, or user). Returning a non-null value
  294. * will effectively short-circuit the function.
  295. *
  296. * @since 3.1.0
  297. *
  298. * @param null|bool $delete Whether to allow metadata deletion of the given type.
  299. * @param int $object_id Object ID.
  300. * @param string $meta_key Meta key.
  301. * @param mixed $meta_value Meta value. Must be serializable if non-scalar.
  302. * @param bool $delete_all Whether to delete the matching metadata entries
  303. * for all objects, ignoring the specified $object_id.
  304. * Default false.
  305. */
  306. $check = apply_filters( "delete_{$meta_type}_metadata", null, $object_id, $meta_key, $meta_value, $delete_all );
  307. if ( null !== $check )
  308. return (bool) $check;
  309. $_meta_value = $meta_value;
  310. $meta_value = maybe_serialize( $meta_value );
  311. $query = $wpdb->prepare( "SELECT $id_column FROM $table WHERE meta_key = %s", $meta_key );
  312. if ( !$delete_all )
  313. $query .= $wpdb->prepare(" AND $type_column = %d", $object_id );
  314. if ( '' !== $meta_value && null !== $meta_value && false !== $meta_value )
  315. $query .= $wpdb->prepare(" AND meta_value = %s", $meta_value );
  316. $meta_ids = $wpdb->get_col( $query );
  317. if ( !count( $meta_ids ) )
  318. return false;
  319. if ( $delete_all ) {
  320. $value_clause = '';
  321. if ( '' !== $meta_value && null !== $meta_value && false !== $meta_value ) {
  322. $value_clause = $wpdb->prepare( " AND meta_value = %s", $meta_value );
  323. }
  324. $object_ids = $wpdb->get_col( $wpdb->prepare( "SELECT $type_column FROM $table WHERE meta_key = %s $value_clause", $meta_key ) );
  325. }
  326. /**
  327. * Fires immediately before deleting metadata of a specific type.
  328. *
  329. * The dynamic portion of the hook, `$meta_type`, refers to the meta
  330. * object type (comment, post, or user).
  331. *
  332. * @since 3.1.0
  333. *
  334. * @param array $meta_ids An array of metadata entry IDs to delete.
  335. * @param int $object_id Object ID.
  336. * @param string $meta_key Meta key.
  337. * @param mixed $meta_value Meta value.
  338. */
  339. do_action( "delete_{$meta_type}_meta", $meta_ids, $object_id, $meta_key, $_meta_value );
  340. // Old-style action.
  341. if ( 'post' == $meta_type ) {
  342. /**
  343. * Fires immediately before deleting metadata for a post.
  344. *
  345. * @since 2.9.0
  346. *
  347. * @param array $meta_ids An array of post metadata entry IDs to delete.
  348. */
  349. do_action( 'delete_postmeta', $meta_ids );
  350. }
  351. $query = "DELETE FROM $table WHERE $id_column IN( " . implode( ',', $meta_ids ) . " )";
  352. $count = $wpdb->query($query);
  353. if ( !$count )
  354. return false;
  355. if ( $delete_all ) {
  356. foreach ( (array) $object_ids as $o_id ) {
  357. wp_cache_delete($o_id, $meta_type . '_meta');
  358. }
  359. } else {
  360. wp_cache_delete($object_id, $meta_type . '_meta');
  361. }
  362. /**
  363. * Fires immediately after deleting metadata of a specific type.
  364. *
  365. * The dynamic portion of the hook name, `$meta_type`, refers to the meta
  366. * object type (comment, post, or user).
  367. *
  368. * @since 2.9.0
  369. *
  370. * @param array $meta_ids An array of deleted metadata entry IDs.
  371. * @param int $object_id Object ID.
  372. * @param string $meta_key Meta key.
  373. * @param mixed $meta_value Meta value.
  374. */
  375. do_action( "deleted_{$meta_type}_meta", $meta_ids, $object_id, $meta_key, $_meta_value );
  376. // Old-style action.
  377. if ( 'post' == $meta_type ) {
  378. /**
  379. * Fires immediately after deleting metadata for a post.
  380. *
  381. * @since 2.9.0
  382. *
  383. * @param array $meta_ids An array of deleted post metadata entry IDs.
  384. */
  385. do_action( 'deleted_postmeta', $meta_ids );
  386. }
  387. return true;
  388. }
  389. /**
  390. * Retrieve metadata for the specified object.
  391. *
  392. * @since 2.9.0
  393. *
  394. * @param string $meta_type Type of object metadata is for (e.g., comment, post, or user)
  395. * @param int $object_id ID of the object metadata is for
  396. * @param string $meta_key Optional. Metadata key. If not specified, retrieve all metadata for
  397. * the specified object.
  398. * @param bool $single Optional, default is false.
  399. * If true, return only the first value of the specified meta_key.
  400. * This parameter has no effect if meta_key is not specified.
  401. * @return mixed Single metadata value, or array of values
  402. */
  403. function get_metadata($meta_type, $object_id, $meta_key = '', $single = false) {
  404. if ( ! $meta_type || ! is_numeric( $object_id ) ) {
  405. return false;
  406. }
  407. $object_id = absint( $object_id );
  408. if ( ! $object_id ) {
  409. return false;
  410. }
  411. /**
  412. * Filters whether to retrieve metadata of a specific type.
  413. *
  414. * The dynamic portion of the hook, `$meta_type`, refers to the meta
  415. * object type (comment, post, or user). Returning a non-null value
  416. * will effectively short-circuit the function.
  417. *
  418. * @since 3.1.0
  419. *
  420. * @param null|array|string $value The value get_metadata() should return - a single metadata value,
  421. * or an array of values.
  422. * @param int $object_id Object ID.
  423. * @param string $meta_key Meta key.
  424. * @param bool $single Whether to return only the first value of the specified $meta_key.
  425. */
  426. $check = apply_filters( "get_{$meta_type}_metadata", null, $object_id, $meta_key, $single );
  427. if ( null !== $check ) {
  428. if ( $single && is_array( $check ) )
  429. return $check[0];
  430. else
  431. return $check;
  432. }
  433. $meta_cache = wp_cache_get($object_id, $meta_type . '_meta');
  434. if ( !$meta_cache ) {
  435. $meta_cache = update_meta_cache( $meta_type, array( $object_id ) );
  436. $meta_cache = $meta_cache[$object_id];
  437. }
  438. if ( ! $meta_key ) {
  439. return $meta_cache;
  440. }
  441. if ( isset($meta_cache[$meta_key]) ) {
  442. if ( $single )
  443. return maybe_unserialize( $meta_cache[$meta_key][0] );
  444. else
  445. return array_map('maybe_unserialize', $meta_cache[$meta_key]);
  446. }
  447. if ($single)
  448. return '';
  449. else
  450. return array();
  451. }
  452. /**
  453. * Determine if a meta key is set for a given object
  454. *
  455. * @since 3.3.0
  456. *
  457. * @param string $meta_type Type of object metadata is for (e.g., comment, post, or user)
  458. * @param int $object_id ID of the object metadata is for
  459. * @param string $meta_key Metadata key.
  460. * @return bool True of the key is set, false if not.
  461. */
  462. function metadata_exists( $meta_type, $object_id, $meta_key ) {
  463. if ( ! $meta_type || ! is_numeric( $object_id ) ) {
  464. return false;
  465. }
  466. $object_id = absint( $object_id );
  467. if ( ! $object_id ) {
  468. return false;
  469. }
  470. /** This filter is documented in wp-includes/meta.php */
  471. $check = apply_filters( "get_{$meta_type}_metadata", null, $object_id, $meta_key, true );
  472. if ( null !== $check )
  473. return (bool) $check;
  474. $meta_cache = wp_cache_get( $object_id, $meta_type . '_meta' );
  475. if ( !$meta_cache ) {
  476. $meta_cache = update_meta_cache( $meta_type, array( $object_id ) );
  477. $meta_cache = $meta_cache[$object_id];
  478. }
  479. if ( isset( $meta_cache[ $meta_key ] ) )
  480. return true;
  481. return false;
  482. }
  483. /**
  484. * Get meta data by meta ID
  485. *
  486. * @since 3.3.0
  487. *
  488. * @global wpdb $wpdb WordPress database abstraction object.
  489. *
  490. * @param string $meta_type Type of object metadata is for (e.g., comment, post, term, or user).
  491. * @param int $meta_id ID for a specific meta row
  492. * @return object|false Meta object or false.
  493. */
  494. function get_metadata_by_mid( $meta_type, $meta_id ) {
  495. global $wpdb;
  496. if ( ! $meta_type || ! is_numeric( $meta_id ) || floor( $meta_id ) != $meta_id ) {
  497. return false;
  498. }
  499. $meta_id = intval( $meta_id );
  500. if ( $meta_id <= 0 ) {
  501. return false;
  502. }
  503. $table = _get_meta_table( $meta_type );
  504. if ( ! $table ) {
  505. return false;
  506. }
  507. $id_column = ( 'user' == $meta_type ) ? 'umeta_id' : 'meta_id';
  508. $meta = $wpdb->get_row( $wpdb->prepare( "SELECT * FROM $table WHERE $id_column = %d", $meta_id ) );
  509. if ( empty( $meta ) )
  510. return false;
  511. if ( isset( $meta->meta_value ) )
  512. $meta->meta_value = maybe_unserialize( $meta->meta_value );
  513. return $meta;
  514. }
  515. /**
  516. * Update meta data by meta ID
  517. *
  518. * @since 3.3.0
  519. *
  520. * @global wpdb $wpdb WordPress database abstraction object.
  521. *
  522. * @param string $meta_type Type of object metadata is for (e.g., comment, post, or user)
  523. * @param int $meta_id ID for a specific meta row
  524. * @param string $meta_value Metadata value
  525. * @param string $meta_key Optional, you can provide a meta key to update it
  526. * @return bool True on successful update, false on failure.
  527. */
  528. function update_metadata_by_mid( $meta_type, $meta_id, $meta_value, $meta_key = false ) {
  529. global $wpdb;
  530. // Make sure everything is valid.
  531. if ( ! $meta_type || ! is_numeric( $meta_id ) || floor( $meta_id ) != $meta_id ) {
  532. return false;
  533. }
  534. $meta_id = intval( $meta_id );
  535. if ( $meta_id <= 0 ) {
  536. return false;
  537. }
  538. $table = _get_meta_table( $meta_type );
  539. if ( ! $table ) {
  540. return false;
  541. }
  542. $column = sanitize_key($meta_type . '_id');
  543. $id_column = 'user' == $meta_type ? 'umeta_id' : 'meta_id';
  544. // Fetch the meta and go on if it's found.
  545. if ( $meta = get_metadata_by_mid( $meta_type, $meta_id ) ) {
  546. $original_key = $meta->meta_key;
  547. $object_id = $meta->{$column};
  548. // If a new meta_key (last parameter) was specified, change the meta key,
  549. // otherwise use the original key in the update statement.
  550. if ( false === $meta_key ) {
  551. $meta_key = $original_key;
  552. } elseif ( ! is_string( $meta_key ) ) {
  553. return false;
  554. }
  555. // Sanitize the meta
  556. $_meta_value = $meta_value;
  557. $meta_value = sanitize_meta( $meta_key, $meta_value, $meta_type );
  558. $meta_value = maybe_serialize( $meta_value );
  559. // Format the data query arguments.
  560. $data = array(
  561. 'meta_key' => $meta_key,
  562. 'meta_value' => $meta_value
  563. );
  564. // Format the where query arguments.
  565. $where = array();
  566. $where[$id_column] = $meta_id;
  567. /** This action is documented in wp-includes/meta.php */
  568. do_action( "update_{$meta_type}_meta", $meta_id, $object_id, $meta_key, $_meta_value );
  569. if ( 'post' == $meta_type ) {
  570. /** This action is documented in wp-includes/meta.php */
  571. do_action( 'update_postmeta', $meta_id, $object_id, $meta_key, $meta_value );
  572. }
  573. // Run the update query, all fields in $data are %s, $where is a %d.
  574. $result = $wpdb->update( $table, $data, $where, '%s', '%d' );
  575. if ( ! $result )
  576. return false;
  577. // Clear the caches.
  578. wp_cache_delete($object_id, $meta_type . '_meta');
  579. /** This action is documented in wp-includes/meta.php */
  580. do_action( "updated_{$meta_type}_meta", $meta_id, $object_id, $meta_key, $_meta_value );
  581. if ( 'post' == $meta_type ) {
  582. /** This action is documented in wp-includes/meta.php */
  583. do_action( 'updated_postmeta', $meta_id, $object_id, $meta_key, $meta_value );
  584. }
  585. return true;
  586. }
  587. // And if the meta was not found.
  588. return false;
  589. }
  590. /**
  591. * Delete meta data by meta ID
  592. *
  593. * @since 3.3.0
  594. *
  595. * @global wpdb $wpdb WordPress database abstraction object.
  596. *
  597. * @param string $meta_type Type of object metadata is for (e.g., comment, post, term, or user).
  598. * @param int $meta_id ID for a specific meta row
  599. * @return bool True on successful delete, false on failure.
  600. */
  601. function delete_metadata_by_mid( $meta_type, $meta_id ) {
  602. global $wpdb;
  603. // Make sure everything is valid.
  604. if ( ! $meta_type || ! is_numeric( $meta_id ) || floor( $meta_id ) != $meta_id ) {
  605. return false;
  606. }
  607. $meta_id = intval( $meta_id );
  608. if ( $meta_id <= 0 ) {
  609. return false;
  610. }
  611. $table = _get_meta_table( $meta_type );
  612. if ( ! $table ) {
  613. return false;
  614. }
  615. // object and id columns
  616. $column = sanitize_key($meta_type . '_id');
  617. $id_column = 'user' == $meta_type ? 'umeta_id' : 'meta_id';
  618. // Fetch the meta and go on if it's found.
  619. if ( $meta = get_metadata_by_mid( $meta_type, $meta_id ) ) {
  620. $object_id = $meta->{$column};
  621. /** This action is documented in wp-includes/meta.php */
  622. do_action( "delete_{$meta_type}_meta", (array) $meta_id, $object_id, $meta->meta_key, $meta->meta_value );
  623. // Old-style action.
  624. if ( 'post' == $meta_type || 'comment' == $meta_type ) {
  625. /**
  626. * Fires immediately before deleting post or comment metadata of a specific type.
  627. *
  628. * The dynamic portion of the hook, `$meta_type`, refers to the meta
  629. * object type (post or comment).
  630. *
  631. * @since 3.4.0
  632. *
  633. * @param int $meta_id ID of the metadata entry to delete.
  634. */
  635. do_action( "delete_{$meta_type}meta", $meta_id );
  636. }
  637. // Run the query, will return true if deleted, false otherwise
  638. $result = (bool) $wpdb->delete( $table, array( $id_column => $meta_id ) );
  639. // Clear the caches.
  640. wp_cache_delete($object_id, $meta_type . '_meta');
  641. /** This action is documented in wp-includes/meta.php */
  642. do_action( "deleted_{$meta_type}_meta", (array) $meta_id, $object_id, $meta->meta_key, $meta->meta_value );
  643. // Old-style action.
  644. if ( 'post' == $meta_type || 'comment' == $meta_type ) {
  645. /**
  646. * Fires immediately after deleting post or comment metadata of a specific type.
  647. *
  648. * The dynamic portion of the hook, `$meta_type`, refers to the meta
  649. * object type (post or comment).
  650. *
  651. * @since 3.4.0
  652. *
  653. * @param int $meta_ids Deleted metadata entry ID.
  654. */
  655. do_action( "deleted_{$meta_type}meta", $meta_id );
  656. }
  657. return $result;
  658. }
  659. // Meta id was not found.
  660. return false;
  661. }
  662. /**
  663. * Update the metadata cache for the specified objects.
  664. *
  665. * @since 2.9.0
  666. *
  667. * @global wpdb $wpdb WordPress database abstraction object.
  668. *
  669. * @param string $meta_type Type of object metadata is for (e.g., comment, post, or user)
  670. * @param int|array $object_ids Array or comma delimited list of object IDs to update cache for
  671. * @return array|false Metadata cache for the specified objects, or false on failure.
  672. */
  673. function update_meta_cache($meta_type, $object_ids) {
  674. global $wpdb;
  675. if ( ! $meta_type || ! $object_ids ) {
  676. return false;
  677. }
  678. $table = _get_meta_table( $meta_type );
  679. if ( ! $table ) {
  680. return false;
  681. }
  682. $column = sanitize_key($meta_type . '_id');
  683. if ( !is_array($object_ids) ) {
  684. $object_ids = preg_replace('|[^0-9,]|', '', $object_ids);
  685. $object_ids = explode(',', $object_ids);
  686. }
  687. $object_ids = array_map('intval', $object_ids);
  688. $cache_key = $meta_type . '_meta';
  689. $ids = array();
  690. $cache = array();
  691. foreach ( $object_ids as $id ) {
  692. $cached_object = wp_cache_get( $id, $cache_key );
  693. if ( false === $cached_object )
  694. $ids[] = $id;
  695. else
  696. $cache[$id] = $cached_object;
  697. }
  698. if ( empty( $ids ) )
  699. return $cache;
  700. // Get meta info
  701. $id_list = join( ',', $ids );
  702. $id_column = 'user' == $meta_type ? 'umeta_id' : 'meta_id';
  703. $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 );
  704. if ( !empty($meta_list) ) {
  705. foreach ( $meta_list as $metarow) {
  706. $mpid = intval($metarow[$column]);
  707. $mkey = $metarow['meta_key'];
  708. $mval = $metarow['meta_value'];
  709. // Force subkeys to be array type:
  710. if ( !isset($cache[$mpid]) || !is_array($cache[$mpid]) )
  711. $cache[$mpid] = array();
  712. if ( !isset($cache[$mpid][$mkey]) || !is_array($cache[$mpid][$mkey]) )
  713. $cache[$mpid][$mkey] = array();
  714. // Add a value to the current pid/key:
  715. $cache[$mpid][$mkey][] = $mval;
  716. }
  717. }
  718. foreach ( $ids as $id ) {
  719. if ( ! isset($cache[$id]) )
  720. $cache[$id] = array();
  721. wp_cache_add( $id, $cache[$id], $cache_key );
  722. }
  723. return $cache;
  724. }
  725. /**
  726. * Retrieves the queue for lazy-loading metadata.
  727. *
  728. * @since 4.5.0
  729. *
  730. * @return WP_Metadata_Lazyloader $lazyloader Metadata lazyloader queue.
  731. */
  732. function wp_metadata_lazyloader() {
  733. static $wp_metadata_lazyloader;
  734. if ( null === $wp_metadata_lazyloader ) {
  735. $wp_metadata_lazyloader = new WP_Metadata_Lazyloader();
  736. }
  737. return $wp_metadata_lazyloader;
  738. }
  739. /**
  740. * Given a meta query, generates SQL clauses to be appended to a main query.
  741. *
  742. * @since 3.2.0
  743. *
  744. * @see WP_Meta_Query
  745. *
  746. * @param array $meta_query A meta query.
  747. * @param string $type Type of meta.
  748. * @param string $primary_table Primary database table name.
  749. * @param string $primary_id_column Primary ID column name.
  750. * @param object $context Optional. The main query object
  751. * @return array Associative array of `JOIN` and `WHERE` SQL.
  752. */
  753. function get_meta_sql( $meta_query, $type, $primary_table, $primary_id_column, $context = null ) {
  754. $meta_query_obj = new WP_Meta_Query( $meta_query );
  755. return $meta_query_obj->get_sql( $type, $primary_table, $primary_id_column, $context );
  756. }
  757. /**
  758. * Retrieve the name of the metadata table for the specified object type.
  759. *
  760. * @since 2.9.0
  761. *
  762. * @global wpdb $wpdb WordPress database abstraction object.
  763. *
  764. * @param string $type Type of object to get metadata table for (e.g., comment, post, or user)
  765. * @return string|false Metadata table name, or false if no metadata table exists
  766. */
  767. function _get_meta_table($type) {
  768. global $wpdb;
  769. $table_name = $type . 'meta';
  770. if ( empty($wpdb->$table_name) )
  771. return false;
  772. return $wpdb->$table_name;
  773. }
  774. /**
  775. * Determine whether a meta key is protected.
  776. *
  777. * @since 3.1.3
  778. *
  779. * @param string $meta_key Meta key
  780. * @param string|null $meta_type
  781. * @return bool True if the key is protected, false otherwise.
  782. */
  783. function is_protected_meta( $meta_key, $meta_type = null ) {
  784. $protected = ( '_' == $meta_key[0] );
  785. /**
  786. * Filters whether a meta key is protected.
  787. *
  788. * @since 3.2.0
  789. *
  790. * @param bool $protected Whether the key is protected. Default false.
  791. * @param string $meta_key Meta key.
  792. * @param string $meta_type Meta type.
  793. */
  794. return apply_filters( 'is_protected_meta', $protected, $meta_key, $meta_type );
  795. }
  796. /**
  797. * Sanitize meta value.
  798. *
  799. * @since 3.1.3
  800. *
  801. * @param string $meta_key Meta key.
  802. * @param mixed $meta_value Meta value to sanitize.
  803. * @param string $object_type Type of object the meta is registered to.
  804. *
  805. * @return mixed Sanitized $meta_value.
  806. */
  807. function sanitize_meta( $meta_key, $meta_value, $object_type ) {
  808. /**
  809. * Filters the sanitization of a specific meta key of a specific meta type.
  810. *
  811. * The dynamic portions of the hook name, `$meta_type`, and `$meta_key`,
  812. * refer to the metadata object type (comment, post, or user) and the meta
  813. * key value, respectively.
  814. *
  815. * @since 3.3.0
  816. *
  817. * @param mixed $meta_value Meta value to sanitize.
  818. * @param string $meta_key Meta key.
  819. * @param string $object_type Object type.
  820. */
  821. return apply_filters( "sanitize_{$object_type}_meta_{$meta_key}", $meta_value, $meta_key, $object_type );
  822. }
  823. /**
  824. * Registers a meta key.
  825. *
  826. * @since 3.3.0
  827. * @since 4.6.0 {@link https://core.trac.wordpress.org/ticket/35658 Modified
  828. * to support an array of data to attach to registered meta keys}. Previous arguments for
  829. * `$sanitize_callback` and `$auth_callback` have been folded into this array.
  830. *
  831. * @param string $object_type Type of object this meta is registered to.
  832. * @param string $meta_key Meta key to register.
  833. * @param array $args {
  834. * Data used to describe the meta key when registered.
  835. *
  836. * @type string $type The type of data associated with this meta key.
  837. * @type string $description A description of the data attached to this meta key.
  838. * @type bool $single Whether the meta key has one value per object, or an array of values per object.
  839. * @type string $sanitize_callback A function or method to call when sanitizing `$meta_key` data.
  840. * @type string $auth_callback Optional. A function or method to call when performing edit_post_meta, add_post_meta, and delete_post_meta capability checks.
  841. * @type bool $show_in_rest Whether data associated with this meta key can be considered public.
  842. * }
  843. * @param string|array $deprecated Deprecated. Use `$args` instead.
  844. *
  845. * @return bool True if the meta key was successfully registered in the global array, false if not.
  846. * Registering a meta key with distinct sanitize and auth callbacks will fire those
  847. * callbacks, but will not add to the global registry.
  848. */
  849. function register_meta( $object_type, $meta_key, $args, $deprecated = null ) {
  850. global $wp_meta_keys;
  851. if ( ! is_array( $wp_meta_keys ) ) {
  852. $wp_meta_keys = array();
  853. }
  854. $defaults = array(
  855. 'type' => 'string',
  856. 'description' => '',
  857. 'single' => false,
  858. 'sanitize_callback' => null,
  859. 'auth_callback' => null,
  860. 'show_in_rest' => false,
  861. );
  862. // There used to be individual args for sanitize and auth callbacks
  863. $has_old_sanitize_cb = false;
  864. $has_old_auth_cb = false;
  865. if ( is_callable( $args ) ) {
  866. $args = array(
  867. 'sanitize_callback' => $args,
  868. );
  869. $has_old_sanitize_cb = true;
  870. } else {
  871. $args = (array) $args;
  872. }
  873. if ( is_callable( $deprecated ) ) {
  874. $args['auth_callback'] = $deprecated;
  875. $has_old_auth_cb = true;
  876. }
  877. /**
  878. * Filters the registration arguments when registering meta.
  879. *
  880. * @since 4.6.0
  881. *
  882. * @param array $args Array of meta registration arguments.
  883. * @param array $defaults Array of default arguments.
  884. * @param string $object_type Object type.
  885. * @param string $meta_key Meta key.
  886. */
  887. $args = apply_filters( 'register_meta_args', $args, $defaults, $object_type, $meta_key );
  888. $args = wp_parse_args( $args, $defaults );
  889. // If `auth_callback` is not provided, fall back to `is_protected_meta()`.
  890. if ( empty( $args['auth_callback'] ) ) {
  891. if ( is_protected_meta( $meta_key, $object_type ) ) {
  892. $args['auth_callback'] = '__return_false';
  893. } else {
  894. $args['auth_callback'] = '__return_true';
  895. }
  896. }
  897. // Back-compat: old sanitize and auth callbacks are applied to all of an object type.
  898. if ( is_callable( $args['sanitize_callback'] ) ) {
  899. add_filter( "sanitize_{$object_type}_meta_{$meta_key}", $args['sanitize_callback'], 10, 3 );
  900. }
  901. if ( is_callable( $args['auth_callback'] ) ) {
  902. add_filter( "auth_{$object_type}_meta_{$meta_key}", $args['auth_callback'], 10, 6 );
  903. }
  904. // Global registry only contains meta keys registered with the array of arguments added in 4.6.0.
  905. if ( ! $has_old_auth_cb && ! $has_old_sanitize_cb ) {
  906. $wp_meta_keys[ $object_type ][ $meta_key ] = $args;
  907. return true;
  908. }
  909. return false;
  910. }
  911. /**
  912. * Checks if a meta key is registered.
  913. *
  914. * @since 4.6.0
  915. *
  916. * @param string $object_type The type of object.
  917. * @param string $meta_key The meta key.
  918. *
  919. * @return bool True if the meta key is registered to the object type. False if not.
  920. */
  921. function registered_meta_key_exists( $object_type, $meta_key ) {
  922. global $wp_meta_keys;
  923. if ( ! is_array( $wp_meta_keys ) ) {
  924. return false;
  925. }
  926. if ( ! isset( $wp_meta_keys[ $object_type ] ) ) {
  927. return false;
  928. }
  929. if ( isset( $wp_meta_keys[ $object_type ][ $meta_key ] ) ) {
  930. return true;
  931. }
  932. return false;
  933. }
  934. /**
  935. * Unregisters a meta key from the list of registered keys.
  936. *
  937. * @since 4.6.0
  938. *
  939. * @param string $object_type The type of object.
  940. * @param string $meta_key The meta key.
  941. * @return bool True if successful. False if the meta key was not registered.
  942. */
  943. function unregister_meta_key( $object_type, $meta_key ) {
  944. global $wp_meta_keys;
  945. if ( ! registered_meta_key_exists( $object_type, $meta_key ) ) {
  946. return false;
  947. }
  948. $args = $wp_meta_keys[ $object_type ][ $meta_key ];
  949. if ( isset( $args['sanitize_callback'] ) && is_callable( $args['sanitize_callback'] ) ) {
  950. remove_filter( "sanitize_{$object_type}_meta_{$meta_key}", $args['sanitize_callback'] );
  951. }
  952. if ( isset( $args['auth_callback'] ) && is_callable( $args['auth_callback'] ) ) {
  953. remove_filter( "auth_{$object_type}_meta_{$meta_key}", $args['auth_callback'] );
  954. }
  955. unset( $wp_meta_keys[ $object_type ][ $meta_key ] );
  956. // Do some clean up
  957. if ( empty( $wp_meta_keys[ $object_type ] ) ) {
  958. unset( $wp_meta_keys[ $object_type ] );
  959. }
  960. return true;
  961. }
  962. /**
  963. * Retrieves a list of registered meta keys for an object type.
  964. *
  965. * @since 4.6.0
  966. *
  967. * @param string $object_type The type of object. Post, comment, user, term.
  968. * @return array List of registered meta keys.
  969. */
  970. function get_registered_meta_keys( $object_type ) {
  971. global $wp_meta_keys;
  972. if ( ! is_array( $wp_meta_keys ) || ! isset( $wp_meta_keys[ $object_type ] ) ) {
  973. return array();
  974. }
  975. return $wp_meta_keys[ $object_type ];
  976. }
  977. /**
  978. * Retrieves registered metadata for a specified object.
  979. *
  980. * @since 4.6.0
  981. *
  982. * @param string $object_type Type of object to request metadata for. (e.g. comment, post, term, user)
  983. * @param int $object_id ID of the object the metadata is for.
  984. * @param string $meta_key Optional. Registered metadata key. If not specified, retrieve all registered
  985. * metadata for the specified object.
  986. * @return mixed A single value or array of values for a key if specified. An array of all registered keys
  987. * and values for an object ID if not.
  988. */
  989. function get_registered_metadata( $object_type, $object_id, $meta_key = '' ) {
  990. if ( ! empty( $meta_key ) ) {
  991. if ( ! registered_meta_key_exists( $object_type, $meta_key ) ) {
  992. return false;
  993. }
  994. $meta_keys = get_registered_meta_keys( $object_type );
  995. $meta_key_data = $meta_keys[ $meta_key ];
  996. $data = get_metadata( $object_type, $object_id, $meta_key, $meta_key_data['single'] );
  997. return $data;
  998. }
  999. $data = get_metadata( $object_type, $object_id );
  1000. $meta_keys = get_registered_meta_keys( $object_type );
  1001. $registered_data = array();
  1002. // Someday, array_filter()
  1003. foreach ( $meta_keys as $k => $v ) {
  1004. if ( isset( $data[ $k ] ) ) {
  1005. $registered_data[ $k ] = $data[ $k ];
  1006. }
  1007. }
  1008. return $registered_data;
  1009. }
  1010. /**
  1011. * Filter out `register_meta()` args based on a whitelist.
  1012. * `register_meta()` args may change over time, so requiring the whitelist
  1013. * to be explicitly turned off is a warranty seal of sorts.
  1014. *
  1015. * @access private
  1016. * @since 4.6.0
  1017. *
  1018. * @param array $args Arguments from `register_meta()`.
  1019. * @param array $default_args Default arguments for `register_meta()`.
  1020. *
  1021. * @return array Filtered arguments.
  1022. */
  1023. function _wp_register_meta_args_whitelist( $args, $default_args ) {
  1024. $whitelist = array_keys( $default_args );
  1025. // In an anonymous function world, this would be better as an array_filter()
  1026. foreach ( $args as $key => $value ) {
  1027. if ( ! in_array( $key, $whitelist ) ) {
  1028. unset( $args[ $key ] );
  1029. }
  1030. }
  1031. return $args;
  1032. }