PageRenderTime 65ms CodeModel.GetById 32ms RepoModel.GetById 0ms app.codeStats 1ms

/wp-includes/meta.php

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