PageRenderTime 49ms CodeModel.GetById 22ms RepoModel.GetById 0ms app.codeStats 0ms

/wp-includes/meta.php

https://bitbucket.org/betaimages/chakalos
PHP | 906 lines | 459 code | 179 blank | 268 comment | 143 complexity | 44a8f6f570f15681b6dd968f16fee83f MD5 | raw file
Possible License(s): BSD-3-Clause
  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. * @uses do_action() Calls 'added_{$meta_type}_meta' with meta_id of added metadata entry,
  19. * object ID, meta key, and meta value
  20. *
  21. * @param string $meta_type Type of object metadata is for (e.g., comment, post, or user)
  22. * @param int $object_id ID of the object metadata is for
  23. * @param string $meta_key Metadata key
  24. * @param string $meta_value Metadata value
  25. * @param bool $unique Optional, default is false. Whether the specified metadata key should be
  26. * unique for the object. If true, and the object already has a value for the specified
  27. * metadata key, no change will be made
  28. * @return bool The meta ID on successful update, false on failure.
  29. */
  30. function add_metadata($meta_type, $object_id, $meta_key, $meta_value, $unique = false) {
  31. if ( !$meta_type || !$meta_key )
  32. return false;
  33. if ( !$object_id = absint($object_id) )
  34. return false;
  35. if ( ! $table = _get_meta_table($meta_type) )
  36. return false;
  37. global $wpdb;
  38. $column = esc_sql($meta_type . '_id');
  39. // expected_slashed ($meta_key)
  40. $meta_key = stripslashes($meta_key);
  41. $meta_value = stripslashes_deep($meta_value);
  42. $meta_value = sanitize_meta( $meta_key, $meta_value, $meta_type );
  43. $check = apply_filters( "add_{$meta_type}_metadata", null, $object_id, $meta_key, $meta_value, $unique );
  44. if ( null !== $check )
  45. return $check;
  46. if ( $unique && $wpdb->get_var( $wpdb->prepare(
  47. "SELECT COUNT(*) FROM $table WHERE meta_key = %s AND $column = %d",
  48. $meta_key, $object_id ) ) )
  49. return false;
  50. $_meta_value = $meta_value;
  51. $meta_value = maybe_serialize( $meta_value );
  52. do_action( "add_{$meta_type}_meta", $object_id, $meta_key, $_meta_value );
  53. $result = $wpdb->insert( $table, array(
  54. $column => $object_id,
  55. 'meta_key' => $meta_key,
  56. 'meta_value' => $meta_value
  57. ) );
  58. if ( ! $result )
  59. return false;
  60. $mid = (int) $wpdb->insert_id;
  61. wp_cache_delete($object_id, $meta_type . '_meta');
  62. do_action( "added_{$meta_type}_meta", $mid, $object_id, $meta_key, $_meta_value );
  63. return $mid;
  64. }
  65. /**
  66. * Update metadata for the specified object. If no value already exists for the specified object
  67. * ID and metadata key, the metadata will be added.
  68. *
  69. * @since 2.9.0
  70. * @uses $wpdb WordPress database object for queries.
  71. * @uses do_action() Calls 'update_{$meta_type}_meta' before updating metadata with meta_id of
  72. * metadata entry to update, object ID, meta key, and meta value
  73. * @uses do_action() Calls 'updated_{$meta_type}_meta' after updating metadata with meta_id of
  74. * updated metadata entry, object ID, meta key, and meta value
  75. *
  76. * @param string $meta_type Type of object metadata is for (e.g., comment, post, or user)
  77. * @param int $object_id ID of the object metadata is for
  78. * @param string $meta_key Metadata key
  79. * @param string $meta_value Metadata value
  80. * @param string $prev_value Optional. If specified, only update existing metadata entries with
  81. * the specified value. Otherwise, update all entries.
  82. * @return bool True on successful update, false on failure.
  83. */
  84. function update_metadata($meta_type, $object_id, $meta_key, $meta_value, $prev_value = '') {
  85. if ( !$meta_type || !$meta_key )
  86. return false;
  87. if ( !$object_id = absint($object_id) )
  88. return false;
  89. if ( ! $table = _get_meta_table($meta_type) )
  90. return false;
  91. global $wpdb;
  92. $column = esc_sql($meta_type . '_id');
  93. $id_column = 'user' == $meta_type ? 'umeta_id' : 'meta_id';
  94. // expected_slashed ($meta_key)
  95. $meta_key = stripslashes($meta_key);
  96. $passed_value = $meta_value;
  97. $meta_value = stripslashes_deep($meta_value);
  98. $meta_value = sanitize_meta( $meta_key, $meta_value, $meta_type );
  99. $check = apply_filters( "update_{$meta_type}_metadata", null, $object_id, $meta_key, $meta_value, $prev_value );
  100. if ( null !== $check )
  101. return (bool) $check;
  102. // Compare existing value to new value if no prev value given and the key exists only once.
  103. if ( empty($prev_value) ) {
  104. $old_value = get_metadata($meta_type, $object_id, $meta_key);
  105. if ( count($old_value) == 1 ) {
  106. if ( $old_value[0] === $meta_value )
  107. return false;
  108. }
  109. }
  110. if ( ! $meta_id = $wpdb->get_var( $wpdb->prepare( "SELECT $id_column FROM $table WHERE meta_key = %s AND $column = %d", $meta_key, $object_id ) ) )
  111. return add_metadata($meta_type, $object_id, $meta_key, $passed_value);
  112. $_meta_value = $meta_value;
  113. $meta_value = maybe_serialize( $meta_value );
  114. $data = compact( 'meta_value' );
  115. $where = array( $column => $object_id, 'meta_key' => $meta_key );
  116. if ( !empty( $prev_value ) ) {
  117. $prev_value = maybe_serialize($prev_value);
  118. $where['meta_value'] = $prev_value;
  119. }
  120. do_action( "update_{$meta_type}_meta", $meta_id, $object_id, $meta_key, $_meta_value );
  121. if ( 'post' == $meta_type )
  122. do_action( 'update_postmeta', $meta_id, $object_id, $meta_key, $meta_value );
  123. $wpdb->update( $table, $data, $where );
  124. wp_cache_delete($object_id, $meta_type . '_meta');
  125. do_action( "updated_{$meta_type}_meta", $meta_id, $object_id, $meta_key, $_meta_value );
  126. if ( 'post' == $meta_type )
  127. do_action( 'updated_postmeta', $meta_id, $object_id, $meta_key, $meta_value );
  128. return true;
  129. }
  130. /**
  131. * Delete metadata for the specified object.
  132. *
  133. * @since 2.9.0
  134. * @uses $wpdb WordPress database object for queries.
  135. * @uses do_action() Calls 'deleted_{$meta_type}_meta' after deleting with meta_id of
  136. * deleted metadata entries, object ID, meta key, and meta value
  137. *
  138. * @param string $meta_type Type of object metadata is for (e.g., comment, post, or user)
  139. * @param int $object_id ID of the object metadata is for
  140. * @param string $meta_key Metadata key
  141. * @param string $meta_value Optional. Metadata value. If specified, only delete metadata entries
  142. * with this value. Otherwise, delete all entries with the specified meta_key.
  143. * @param bool $delete_all Optional, default is false. If true, delete matching metadata entries
  144. * for all objects, ignoring the specified object_id. Otherwise, only delete matching
  145. * metadata entries for the specified object_id.
  146. * @return bool True on successful delete, false on failure.
  147. */
  148. function delete_metadata($meta_type, $object_id, $meta_key, $meta_value = '', $delete_all = false) {
  149. if ( !$meta_type || !$meta_key )
  150. return false;
  151. if ( (!$object_id = absint($object_id)) && !$delete_all )
  152. return false;
  153. if ( ! $table = _get_meta_table($meta_type) )
  154. return false;
  155. global $wpdb;
  156. $type_column = esc_sql($meta_type . '_id');
  157. $id_column = 'user' == $meta_type ? 'umeta_id' : 'meta_id';
  158. // expected_slashed ($meta_key)
  159. $meta_key = stripslashes($meta_key);
  160. $meta_value = stripslashes_deep($meta_value);
  161. $check = apply_filters( "delete_{$meta_type}_metadata", null, $object_id, $meta_key, $meta_value, $delete_all );
  162. if ( null !== $check )
  163. return (bool) $check;
  164. $_meta_value = $meta_value;
  165. $meta_value = maybe_serialize( $meta_value );
  166. $query = $wpdb->prepare( "SELECT $id_column FROM $table WHERE meta_key = %s", $meta_key );
  167. if ( !$delete_all )
  168. $query .= $wpdb->prepare(" AND $type_column = %d", $object_id );
  169. if ( $meta_value )
  170. $query .= $wpdb->prepare(" AND meta_value = %s", $meta_value );
  171. $meta_ids = $wpdb->get_col( $query );
  172. if ( !count( $meta_ids ) )
  173. return false;
  174. if ( $delete_all )
  175. $object_ids = $wpdb->get_col( $wpdb->prepare( "SELECT $type_column FROM $table WHERE meta_key = %s", $meta_key ) );
  176. do_action( "delete_{$meta_type}_meta", $meta_ids, $object_id, $meta_key, $_meta_value );
  177. // Old-style action.
  178. if ( 'post' == $meta_type )
  179. do_action( 'delete_postmeta', $meta_ids );
  180. $query = "DELETE FROM $table WHERE $id_column IN( " . implode( ',', $meta_ids ) . " )";
  181. $count = $wpdb->query($query);
  182. if ( !$count )
  183. return false;
  184. if ( $delete_all ) {
  185. foreach ( (array) $object_ids as $o_id ) {
  186. wp_cache_delete($o_id, $meta_type . '_meta');
  187. }
  188. } else {
  189. wp_cache_delete($object_id, $meta_type . '_meta');
  190. }
  191. do_action( "deleted_{$meta_type}_meta", $meta_ids, $object_id, $meta_key, $_meta_value );
  192. // Old-style action.
  193. if ( 'post' == $meta_type )
  194. do_action( 'deleted_postmeta', $meta_ids );
  195. return true;
  196. }
  197. /**
  198. * Retrieve metadata for the specified object.
  199. *
  200. * @since 2.9.0
  201. *
  202. * @param string $meta_type Type of object metadata is for (e.g., comment, post, or user)
  203. * @param int $object_id ID of the object metadata is for
  204. * @param string $meta_key Optional. Metadata key. If not specified, retrieve all metadata for
  205. * the specified object.
  206. * @param bool $single Optional, default is false. If true, return only the first value of the
  207. * specified meta_key. This parameter has no effect if meta_key is not specified.
  208. * @return string|array Single metadata value, or array of values
  209. */
  210. function get_metadata($meta_type, $object_id, $meta_key = '', $single = false) {
  211. if ( !$meta_type )
  212. return false;
  213. if ( !$object_id = absint($object_id) )
  214. return false;
  215. $check = apply_filters( "get_{$meta_type}_metadata", null, $object_id, $meta_key, $single );
  216. if ( null !== $check ) {
  217. if ( $single && is_array( $check ) )
  218. return $check[0];
  219. else
  220. return $check;
  221. }
  222. $meta_cache = wp_cache_get($object_id, $meta_type . '_meta');
  223. if ( !$meta_cache ) {
  224. $meta_cache = update_meta_cache( $meta_type, array( $object_id ) );
  225. $meta_cache = $meta_cache[$object_id];
  226. }
  227. if ( !$meta_key )
  228. return $meta_cache;
  229. if ( isset($meta_cache[$meta_key]) ) {
  230. if ( $single )
  231. return maybe_unserialize( $meta_cache[$meta_key][0] );
  232. else
  233. return array_map('maybe_unserialize', $meta_cache[$meta_key]);
  234. }
  235. if ($single)
  236. return '';
  237. else
  238. return array();
  239. }
  240. /**
  241. * Determine if a meta key is set for a given object
  242. *
  243. * @since 3.3.0
  244. *
  245. * @param string $meta_type Type of object metadata is for (e.g., comment, post, or user)
  246. * @param int $object_id ID of the object metadata is for
  247. * @param string $meta_key Metadata key.
  248. * @return boolean true of the key is set, false if not.
  249. */
  250. function metadata_exists( $meta_type, $object_id, $meta_key ) {
  251. if ( ! $meta_type )
  252. return false;
  253. if ( ! $object_id = absint( $object_id ) )
  254. return false;
  255. $check = apply_filters( "get_{$meta_type}_metadata", null, $object_id, $meta_key, true );
  256. if ( null !== $check )
  257. return true;
  258. $meta_cache = wp_cache_get( $object_id, $meta_type . '_meta' );
  259. if ( !$meta_cache ) {
  260. $meta_cache = update_meta_cache( $meta_type, array( $object_id ) );
  261. $meta_cache = $meta_cache[$object_id];
  262. }
  263. if ( isset( $meta_cache[ $meta_key ] ) )
  264. return true;
  265. return false;
  266. }
  267. /**
  268. * Get meta data by meta ID
  269. *
  270. * @since 3.3.0
  271. *
  272. * @param string $meta_type Type of object metadata is for (e.g., comment, post, or user)
  273. * @param int $meta_id ID for a specific meta row
  274. * @return object Meta object or false.
  275. */
  276. function get_metadata_by_mid( $meta_type, $meta_id ) {
  277. global $wpdb;
  278. if ( ! $meta_type )
  279. return false;
  280. if ( !$meta_id = absint( $meta_id ) )
  281. return false;
  282. if ( ! $table = _get_meta_table($meta_type) )
  283. return false;
  284. $id_column = ( 'user' == $meta_type ) ? 'umeta_id' : 'meta_id';
  285. $meta = $wpdb->get_row( $wpdb->prepare( "SELECT * FROM $table WHERE $id_column = %d", $meta_id ) );
  286. if ( empty( $meta ) )
  287. return false;
  288. if ( isset( $meta->meta_value ) )
  289. $meta->meta_value = maybe_unserialize( $meta->meta_value );
  290. return $meta;
  291. }
  292. /**
  293. * Update meta data by meta ID
  294. *
  295. * @since 3.3.0
  296. *
  297. * @uses get_metadata_by_mid() Calls get_metadata_by_mid() to fetch the meta key, value
  298. * and object_id of the given meta_id.
  299. *
  300. * @param string $meta_type Type of object metadata is for (e.g., comment, post, or user)
  301. * @param int $meta_id ID for a specific meta row
  302. * @param string $meta_value Metadata value
  303. * @param string $meta_key Optional, you can provide a meta key to update it
  304. * @return bool True on successful update, false on failure.
  305. */
  306. function update_metadata_by_mid( $meta_type, $meta_id, $meta_value, $meta_key = false ) {
  307. global $wpdb;
  308. // Make sure everything is valid.
  309. if ( ! $meta_type )
  310. return false;
  311. if ( ! $meta_id = absint( $meta_id ) )
  312. return false;
  313. if ( ! $table = _get_meta_table( $meta_type ) )
  314. return false;
  315. $column = esc_sql($meta_type . '_id');
  316. $id_column = 'user' == $meta_type ? 'umeta_id' : 'meta_id';
  317. // Fetch the meta and go on if it's found.
  318. if ( $meta = get_metadata_by_mid( $meta_type, $meta_id ) ) {
  319. $original_key = $meta->meta_key;
  320. $original_value = $meta->meta_value;
  321. $object_id = $meta->{$column};
  322. // If a new meta_key (last parameter) was specified, change the meta key,
  323. // otherwise use the original key in the update statement.
  324. if ( false === $meta_key ) {
  325. $meta_key = $original_key;
  326. } elseif ( ! is_string( $meta_key ) ) {
  327. return false;
  328. }
  329. // Sanitize the meta
  330. $_meta_value = $meta_value;
  331. $meta_value = sanitize_meta( $meta_key, $meta_value, $meta_type );
  332. $meta_value = maybe_serialize( $meta_value );
  333. // Format the data query arguments.
  334. $data = array(
  335. 'meta_key' => $meta_key,
  336. 'meta_value' => $meta_value
  337. );
  338. // Format the where query arguments.
  339. $where = array();
  340. $where[$id_column] = $meta_id;
  341. do_action( "update_{$meta_type}_meta", $meta_id, $object_id, $meta_key, $_meta_value );
  342. if ( 'post' == $meta_type )
  343. do_action( 'update_postmeta', $meta_id, $object_id, $meta_key, $meta_value );
  344. // Run the update query, all fields in $data are %s, $where is a %d.
  345. $result = (bool) $wpdb->update( $table, $data, $where, '%s', '%d' );
  346. // Clear the caches.
  347. wp_cache_delete($object_id, $meta_type . '_meta');
  348. do_action( "updated_{$meta_type}_meta", $meta_id, $object_id, $meta_key, $_meta_value );
  349. if ( 'post' == $meta_type )
  350. do_action( 'updated_postmeta', $meta_id, $object_id, $meta_key, $meta_value );
  351. return $result;
  352. }
  353. // And if the meta was not found.
  354. return false;
  355. }
  356. /**
  357. * Delete meta data by meta ID
  358. *
  359. * @since 3.3.0
  360. *
  361. * @uses get_metadata_by_mid() Calls get_metadata_by_mid() to fetch the meta key, value
  362. * and object_id of the given meta_id.
  363. *
  364. * @param string $meta_type Type of object metadata is for (e.g., comment, post, or user)
  365. * @param int $meta_id ID for a specific meta row
  366. * @return bool True on successful delete, false on failure.
  367. */
  368. function delete_metadata_by_mid( $meta_type, $meta_id ) {
  369. global $wpdb;
  370. // Make sure everything is valid.
  371. if ( ! $meta_type )
  372. return false;
  373. if ( ! $meta_id = absint( $meta_id ) )
  374. return false;
  375. if ( ! $table = _get_meta_table( $meta_type ) )
  376. return false;
  377. // object and id columns
  378. $column = esc_sql($meta_type . '_id');
  379. $id_column = 'user' == $meta_type ? 'umeta_id' : 'meta_id';
  380. // Fetch the meta and go on if it's found.
  381. if ( $meta = get_metadata_by_mid( $meta_type, $meta_id ) ) {
  382. $object_id = $meta->{$column};
  383. do_action( "delete_{$meta_type}_meta", (array) $meta_id, $object_id, $meta->meta_key, $meta->meta_value );
  384. // Old-style action.
  385. if ( 'post' == $meta_type || 'comment' == $meta_type )
  386. do_action( "delete_{$meta_type}meta", $meta_id );
  387. // Run the query, will return true if deleted, false otherwise
  388. $result = (bool) $wpdb->delete( $table, array( $id_column => $meta_id ) );
  389. // Clear the caches.
  390. wp_cache_delete($object_id, $meta_type . '_meta');
  391. do_action( "deleted_{$meta_type}_meta", (array) $meta_id, $object_id, $meta->meta_key, $meta->meta_value );
  392. // Old-style action.
  393. if ( 'post' == $meta_type || 'comment' == $meta_type )
  394. do_action( "deleted_{$meta_type}meta", $meta_id );
  395. return $result;
  396. }
  397. // Meta id was not found.
  398. return false;
  399. }
  400. /**
  401. * Update the metadata cache for the specified objects.
  402. *
  403. * @since 2.9.0
  404. * @uses $wpdb WordPress database object for queries.
  405. *
  406. * @param string $meta_type Type of object metadata is for (e.g., comment, post, or user)
  407. * @param int|array $object_ids array or comma delimited list of object IDs to update cache for
  408. * @return mixed Metadata cache for the specified objects, or false on failure.
  409. */
  410. function update_meta_cache($meta_type, $object_ids) {
  411. if ( empty( $meta_type ) || empty( $object_ids ) )
  412. return false;
  413. if ( ! $table = _get_meta_table($meta_type) )
  414. return false;
  415. $column = esc_sql($meta_type . '_id');
  416. global $wpdb;
  417. if ( !is_array($object_ids) ) {
  418. $object_ids = preg_replace('|[^0-9,]|', '', $object_ids);
  419. $object_ids = explode(',', $object_ids);
  420. }
  421. $object_ids = array_map('intval', $object_ids);
  422. $cache_key = $meta_type . '_meta';
  423. $ids = array();
  424. $cache = array();
  425. foreach ( $object_ids as $id ) {
  426. $cached_object = wp_cache_get( $id, $cache_key );
  427. if ( false === $cached_object )
  428. $ids[] = $id;
  429. else
  430. $cache[$id] = $cached_object;
  431. }
  432. if ( empty( $ids ) )
  433. return $cache;
  434. // Get meta info
  435. $id_list = join(',', $ids);
  436. $meta_list = $wpdb->get_results( $wpdb->prepare("SELECT $column, meta_key, meta_value FROM $table WHERE $column IN ($id_list)",
  437. $meta_type), ARRAY_A );
  438. if ( !empty($meta_list) ) {
  439. foreach ( $meta_list as $metarow) {
  440. $mpid = intval($metarow[$column]);
  441. $mkey = $metarow['meta_key'];
  442. $mval = $metarow['meta_value'];
  443. // Force subkeys to be array type:
  444. if ( !isset($cache[$mpid]) || !is_array($cache[$mpid]) )
  445. $cache[$mpid] = array();
  446. if ( !isset($cache[$mpid][$mkey]) || !is_array($cache[$mpid][$mkey]) )
  447. $cache[$mpid][$mkey] = array();
  448. // Add a value to the current pid/key:
  449. $cache[$mpid][$mkey][] = $mval;
  450. }
  451. }
  452. foreach ( $ids as $id ) {
  453. if ( ! isset($cache[$id]) )
  454. $cache[$id] = array();
  455. wp_cache_add( $id, $cache[$id], $cache_key );
  456. }
  457. return $cache;
  458. }
  459. /**
  460. * Given a meta query, generates SQL clauses to be appended to a main query
  461. *
  462. * @since 3.2.0
  463. *
  464. * @see WP_Meta_Query
  465. *
  466. * @param array $meta_query A meta query
  467. * @param string $type Type of meta
  468. * @param string $primary_table
  469. * @param string $primary_id_column
  470. * @param object $context (optional) The main query object
  471. * @return array( 'join' => $join_sql, 'where' => $where_sql )
  472. */
  473. function get_meta_sql( $meta_query, $type, $primary_table, $primary_id_column, $context = null ) {
  474. $meta_query_obj = new WP_Meta_Query( $meta_query );
  475. return $meta_query_obj->get_sql( $type, $primary_table, $primary_id_column, $context );
  476. }
  477. /**
  478. * Container class for a multiple metadata query
  479. *
  480. * @since 3.2.0
  481. */
  482. class WP_Meta_Query {
  483. /**
  484. * List of metadata queries. A single query is an associative array:
  485. * - 'key' string The meta key
  486. * - 'value' string|array The meta value
  487. * - 'compare' (optional) string How to compare the key to the value.
  488. * Possible values: '=', '!=', '>', '>=', '<', '<=', 'LIKE', 'NOT LIKE', 'IN', 'NOT IN', 'BETWEEN', 'NOT BETWEEN'.
  489. * Default: '='
  490. * - 'type' string (optional) The type of the value.
  491. * Possible values: 'NUMERIC', 'BINARY', 'CHAR', 'DATE', 'DATETIME', 'DECIMAL', 'SIGNED', 'TIME', 'UNSIGNED'.
  492. * Default: 'CHAR'
  493. *
  494. * @since 3.2.0
  495. * @access public
  496. * @var array
  497. */
  498. public $queries = array();
  499. /**
  500. * The relation between the queries. Can be one of 'AND' or 'OR'.
  501. *
  502. * @since 3.2.0
  503. * @access public
  504. * @var string
  505. */
  506. public $relation;
  507. /**
  508. * Constructor
  509. *
  510. * @param array $meta_query (optional) A meta query
  511. */
  512. function __construct( $meta_query = false ) {
  513. if ( !$meta_query )
  514. return;
  515. if ( isset( $meta_query['relation'] ) && strtoupper( $meta_query['relation'] ) == 'OR' ) {
  516. $this->relation = 'OR';
  517. } else {
  518. $this->relation = 'AND';
  519. }
  520. $this->queries = array();
  521. foreach ( $meta_query as $key => $query ) {
  522. if ( ! is_array( $query ) )
  523. continue;
  524. $this->queries[] = $query;
  525. }
  526. }
  527. /**
  528. * Constructs a meta query based on 'meta_*' query vars
  529. *
  530. * @since 3.2.0
  531. * @access public
  532. *
  533. * @param array $qv The query variables
  534. */
  535. function parse_query_vars( $qv ) {
  536. $meta_query = array();
  537. // Simple query needs to be first for orderby=meta_value to work correctly
  538. foreach ( array( 'key', 'compare', 'type' ) as $key ) {
  539. if ( !empty( $qv[ "meta_$key" ] ) )
  540. $meta_query[0][ $key ] = $qv[ "meta_$key" ];
  541. }
  542. // WP_Query sets 'meta_value' = '' by default
  543. if ( isset( $qv[ 'meta_value' ] ) && '' !== $qv[ 'meta_value' ] )
  544. $meta_query[0]['value'] = $qv[ 'meta_value' ];
  545. if ( !empty( $qv['meta_query'] ) && is_array( $qv['meta_query'] ) ) {
  546. $meta_query = array_merge( $meta_query, $qv['meta_query'] );
  547. }
  548. $this->__construct( $meta_query );
  549. }
  550. /**
  551. * Generates SQL clauses to be appended to a main query.
  552. *
  553. * @since 3.2.0
  554. * @access public
  555. *
  556. * @param string $type Type of meta
  557. * @param string $primary_table
  558. * @param string $primary_id_column
  559. * @param object $context (optional) The main query object
  560. * @return array( 'join' => $join_sql, 'where' => $where_sql )
  561. */
  562. function get_sql( $type, $primary_table, $primary_id_column, $context = null ) {
  563. global $wpdb;
  564. if ( ! $meta_table = _get_meta_table( $type ) )
  565. return false;
  566. $meta_id_column = esc_sql( $type . '_id' );
  567. $join = array();
  568. $where = array();
  569. $key_only_queries = array();
  570. $queries = array();
  571. // Split out the meta_key only queries (we can only do this for OR)
  572. if ( 'OR' == $this->relation ) {
  573. foreach ( $this->queries as $k => $q ) {
  574. if ( ! isset( $q['value'] ) && ! empty( $q['key'] ) )
  575. $key_only_queries[$k] = $q;
  576. else
  577. $queries[$k] = $q;
  578. }
  579. } else {
  580. $queries = $this->queries;
  581. }
  582. // Specify all the meta_key only queries in one go
  583. if ( $key_only_queries ) {
  584. $join[] = "INNER JOIN $meta_table ON $primary_table.$primary_id_column = $meta_table.$meta_id_column";
  585. foreach ( $key_only_queries as $key => $q )
  586. $where["key-only-$key"] = $wpdb->prepare( "$meta_table.meta_key = %s", trim( $q['key'] ) );
  587. }
  588. foreach ( $queries as $k => $q ) {
  589. $meta_key = isset( $q['key'] ) ? trim( $q['key'] ) : '';
  590. $meta_type = isset( $q['type'] ) ? strtoupper( $q['type'] ) : 'CHAR';
  591. if ( 'NUMERIC' == $meta_type )
  592. $meta_type = 'SIGNED';
  593. elseif ( ! in_array( $meta_type, array( 'BINARY', 'CHAR', 'DATE', 'DATETIME', 'DECIMAL', 'SIGNED', 'TIME', 'UNSIGNED' ) ) )
  594. $meta_type = 'CHAR';
  595. $meta_value = isset( $q['value'] ) ? $q['value'] : null;
  596. if ( isset( $q['compare'] ) )
  597. $meta_compare = strtoupper( $q['compare'] );
  598. else
  599. $meta_compare = is_array( $meta_value ) ? 'IN' : '=';
  600. if ( ! in_array( $meta_compare, array(
  601. '=', '!=', '>', '>=', '<', '<=',
  602. 'LIKE', 'NOT LIKE',
  603. 'IN', 'NOT IN',
  604. 'BETWEEN', 'NOT BETWEEN',
  605. 'NOT EXISTS'
  606. ) ) )
  607. $meta_compare = '=';
  608. $i = count( $join );
  609. $alias = $i ? 'mt' . $i : $meta_table;
  610. if ( 'NOT EXISTS' == $meta_compare ) {
  611. $join[$i] = "LEFT JOIN $meta_table";
  612. $join[$i] .= $i ? " AS $alias" : '';
  613. $join[$i] .= " ON ($primary_table.$primary_id_column = $alias.$meta_id_column AND $alias.meta_key = '$meta_key')";
  614. $where[$k] = ' ' . $alias . '.' . $meta_id_column . ' IS NULL';
  615. continue;
  616. }
  617. $join[$i] = "INNER JOIN $meta_table";
  618. $join[$i] .= $i ? " AS $alias" : '';
  619. $join[$i] .= " ON ($primary_table.$primary_id_column = $alias.$meta_id_column)";
  620. $where[$k] = '';
  621. if ( !empty( $meta_key ) )
  622. $where[$k] = $wpdb->prepare( "$alias.meta_key = %s", $meta_key );
  623. if ( is_null( $meta_value ) ) {
  624. if ( empty( $where[$k] ) )
  625. unset( $join[$i] );
  626. continue;
  627. }
  628. if ( in_array( $meta_compare, array( 'IN', 'NOT IN', 'BETWEEN', 'NOT BETWEEN' ) ) ) {
  629. if ( ! is_array( $meta_value ) )
  630. $meta_value = preg_split( '/[,\s]+/', $meta_value );
  631. if ( empty( $meta_value ) ) {
  632. unset( $join[$i] );
  633. continue;
  634. }
  635. } else {
  636. $meta_value = trim( $meta_value );
  637. }
  638. if ( 'IN' == substr( $meta_compare, -2) ) {
  639. $meta_compare_string = '(' . substr( str_repeat( ',%s', count( $meta_value ) ), 1 ) . ')';
  640. } elseif ( 'BETWEEN' == substr( $meta_compare, -7) ) {
  641. $meta_value = array_slice( $meta_value, 0, 2 );
  642. $meta_compare_string = '%s AND %s';
  643. } elseif ( 'LIKE' == substr( $meta_compare, -4 ) ) {
  644. $meta_value = '%' . like_escape( $meta_value ) . '%';
  645. $meta_compare_string = '%s';
  646. } else {
  647. $meta_compare_string = '%s';
  648. }
  649. if ( ! empty( $where[$k] ) )
  650. $where[$k] .= ' AND ';
  651. $where[$k] = ' (' . $where[$k] . $wpdb->prepare( "CAST($alias.meta_value AS {$meta_type}) {$meta_compare} {$meta_compare_string})", $meta_value );
  652. }
  653. $where = array_filter( $where );
  654. if ( empty( $where ) )
  655. $where = '';
  656. else
  657. $where = ' AND (' . implode( "\n{$this->relation} ", $where ) . ' )';
  658. $join = implode( "\n", $join );
  659. if ( ! empty( $join ) )
  660. $join = ' ' . $join;
  661. return apply_filters_ref_array( 'get_meta_sql', array( compact( 'join', 'where' ), $this->queries, $type, $primary_table, $primary_id_column, $context ) );
  662. }
  663. }
  664. /**
  665. * Retrieve the name of the metadata table for the specified object type.
  666. *
  667. * @since 2.9.0
  668. * @uses $wpdb WordPress database object for queries.
  669. *
  670. * @param string $type Type of object to get metadata table for (e.g., comment, post, or user)
  671. * @return mixed Metadata table name, or false if no metadata table exists
  672. */
  673. function _get_meta_table($type) {
  674. global $wpdb;
  675. $table_name = $type . 'meta';
  676. if ( empty($wpdb->$table_name) )
  677. return false;
  678. return $wpdb->$table_name;
  679. }
  680. /**
  681. * Determine whether a meta key is protected
  682. *
  683. * @since 3.1.3
  684. *
  685. * @param string $meta_key Meta key
  686. * @return bool True if the key is protected, false otherwise.
  687. */
  688. function is_protected_meta( $meta_key, $meta_type = null ) {
  689. $protected = ( '_' == $meta_key[0] );
  690. return apply_filters( 'is_protected_meta', $protected, $meta_key, $meta_type );
  691. }
  692. /**
  693. * Sanitize meta value
  694. *
  695. * @since 3.1.3
  696. *
  697. * @param string $meta_key Meta key
  698. * @param mixed $meta_value Meta value to sanitize
  699. * @param string $meta_type Type of meta
  700. * @return mixed Sanitized $meta_value
  701. */
  702. function sanitize_meta( $meta_key, $meta_value, $meta_type ) {
  703. return apply_filters( "sanitize_{$meta_type}_meta_{$meta_key}", $meta_value, $meta_key, $meta_type );
  704. }
  705. /**
  706. * Register meta key
  707. *
  708. * @since 3.3.0
  709. *
  710. * @param string $meta_type Type of meta
  711. * @param string $meta_key Meta key
  712. * @param string|array $sanitize_callback A function or method to call when sanitizing the value of $meta_key.
  713. * @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.
  714. * @param array $args Arguments
  715. */
  716. function register_meta( $meta_type, $meta_key, $sanitize_callback, $auth_callback = null ) {
  717. if ( is_callable( $sanitize_callback ) )
  718. add_filter( "sanitize_{$meta_type}_meta_{$meta_key}", $sanitize_callback, 10, 3 );
  719. if ( empty( $auth_callback ) ) {
  720. if ( is_protected_meta( $meta_key, $meta_type ) )
  721. $auth_callback = '__return_false';
  722. else
  723. $auth_callback = '__return_true';
  724. }
  725. if ( is_callable( $auth_callback ) )
  726. add_filter( "auth_{$meta_type}_meta_{$meta_key}", $auth_callback, 10, 6 );
  727. }