(no commit message)
[opensuse:news_o_o.git] / wp-includes / meta.php
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 /**
15  * Add metadata for the specified object.
16  *
17  * @since 2.9.0
18  * @uses $wpdb WordPress database object for queries.
19  * @uses do_action() Calls 'added_{$meta_type}_meta' with meta_id of added metadata entry,
20  *              object ID, meta key, and meta value
21  *
22  * @param string $meta_type Type of object metadata is for (e.g., comment, post, or user)
23  * @param int $object_id ID of the object metadata is for
24  * @param string $meta_key Metadata key
25  * @param string $meta_value Metadata value
26  * @param bool $unique Optional, default is false.  Whether the specified metadata key should be
27  *              unique for the object.  If true, and the object already has a value for the specified
28  *              metadata key, no change will be made
29  * @return bool True on successful update, false on failure.
30  */
31 function add_metadata($meta_type, $object_id, $meta_key, $meta_value, $unique = false) {
32         if ( !$meta_type || !$meta_key )
33                 return false;
34
35         if ( !$object_id = absint($object_id) )
36                 return false;
37
38         if ( ! $table = _get_meta_table($meta_type) )
39                 return false;
40
41         global $wpdb;
42
43         $column = esc_sql($meta_type . '_id');
44
45         // expected_slashed ($meta_key)
46         $meta_key = stripslashes($meta_key);
47
48         if ( $unique && $wpdb->get_var( $wpdb->prepare(
49                 "SELECT COUNT(*) FROM $table WHERE meta_key = %s AND $column = %d",
50                 $meta_key, $object_id ) ) )
51                 return false;
52
53         $_meta_value = $meta_value;
54         $meta_value = maybe_serialize( stripslashes_deep($meta_value) );
55
56         $wpdb->insert( $table, array(
57                 $column => $object_id,
58                 'meta_key' => $meta_key,
59                 'meta_value' => $meta_value
60         ) );
61
62         wp_cache_delete($object_id, $meta_type . '_meta');
63         // users cache stores usermeta that must be cleared.
64         if ( 'user' == $meta_type )
65                 clean_user_cache($object_id);
66
67         do_action( "added_{$meta_type}_meta", $wpdb->insert_id, $object_id, $meta_key, $_meta_value );
68
69         return true;
70 }
71
72 /**
73  * Update metadata for the specified object.  If no value already exists for the specified object
74  * ID and metadata key, the metadata will be added.
75  *
76  * @since 2.9.0
77  * @uses $wpdb WordPress database object for queries.
78  * @uses do_action() Calls 'update_{$meta_type}_meta' before updating metadata with meta_id of
79  *              metadata entry to update, object ID, meta key, and meta value
80  * @uses do_action() Calls 'updated_{$meta_type}_meta' after updating metadata with meta_id of
81  *              updated metadata entry, object ID, meta key, and meta value
82  *
83  * @param string $meta_type Type of object metadata is for (e.g., comment, post, or user)
84  * @param int $object_id ID of the object metadata is for
85  * @param string $meta_key Metadata key
86  * @param string $meta_value Metadata value
87  * @param string $prev_value Optional.  If specified, only update existing metadata entries with
88  *              the specified value.  Otherwise, update all entries.
89  * @return bool True on successful update, false on failure.
90  */
91 function update_metadata($meta_type, $object_id, $meta_key, $meta_value, $prev_value = '') {
92         if ( !$meta_type || !$meta_key )
93                 return false;
94
95         if ( !$object_id = absint($object_id) )
96                 return false;
97
98         if ( ! $table = _get_meta_table($meta_type) )
99                 return false;
100
101         global $wpdb;
102
103         $column = esc_sql($meta_type . '_id');
104         $id_column = 'user' == $meta_type ? 'umeta_id' : 'meta_id';
105
106         // expected_slashed ($meta_key)
107         $meta_key = stripslashes($meta_key);
108
109         if ( ! $meta_id = $wpdb->get_var( $wpdb->prepare( "SELECT $id_column FROM $table WHERE meta_key = %s AND $column = %d", $meta_key, $object_id ) ) )
110                 return add_metadata($meta_type, $object_id, $meta_key, $meta_value);
111
112         // Compare existing value to new value if no prev value given and the key exists only once.
113         if ( empty($prev_value) ) {
114                 $old_value = get_metadata($meta_type, $object_id, $meta_key);
115                 if ( count($old_value) == 1 ) {
116                         if ( $old_value[0] === $meta_value )
117                                 return false;
118                 }
119         }
120
121         $_meta_value = $meta_value;
122         $meta_value = maybe_serialize( stripslashes_deep($meta_value) );
123
124         $data  = compact( 'meta_value' );
125         $where = array( $column => $object_id, 'meta_key' => $meta_key );
126
127         if ( !empty( $prev_value ) ) {
128                 $prev_value = maybe_serialize($prev_value);
129                 $where['meta_value'] = $prev_value;
130         }
131
132         do_action( "update_{$meta_type}_meta", $meta_id, $object_id, $meta_key, $_meta_value );
133
134         $wpdb->update( $table, $data, $where );
135         wp_cache_delete($object_id, $meta_type . '_meta');
136         // users cache stores usermeta that must be cleared.
137         if ( 'user' == $meta_type )
138                 clean_user_cache($object_id);
139
140         do_action( "updated_{$meta_type}_meta", $meta_id, $object_id, $meta_key, $_meta_value );
141
142         return true;
143 }
144
145 /**
146  * Delete metadata for the specified object.
147  *
148  * @since 2.9.0
149  * @uses $wpdb WordPress database object for queries.
150  * @uses do_action() Calls 'deleted_{$meta_type}_meta' after deleting with meta_id of
151  *              deleted metadata entries, object ID, meta key, and meta value
152  *
153  * @param string $meta_type Type of object metadata is for (e.g., comment, post, or user)
154  * @param int $object_id ID of the object metadata is for
155  * @param string $meta_key Metadata key
156  * @param string $meta_value Optional. Metadata value.  If specified, only delete metadata entries
157  *              with this value.  Otherwise, delete all entries with the specified meta_key.
158  * @param bool $delete_all Optional, default is false.  If true, delete matching metadata entries
159  *              for all objects, ignoring the specified object_id.  Otherwise, only delete matching
160  *              metadata entries for the specified object_id.
161  * @return bool True on successful delete, false on failure.
162  */
163 function delete_metadata($meta_type, $object_id, $meta_key, $meta_value = '', $delete_all = false) {
164         if ( !$meta_type || !$meta_key )
165                 return false;
166
167         if ( (!$object_id = absint($object_id)) && !$delete_all )
168                 return false;
169
170         if ( ! $table = _get_meta_table($meta_type) )
171                 return false;
172
173         global $wpdb;
174
175         $type_column = esc_sql($meta_type . '_id');
176         $id_column = 'user' == $meta_type ? 'umeta_id' : 'meta_id';
177         // expected_slashed ($meta_key)
178         $meta_key = stripslashes($meta_key);
179         $meta_value = maybe_serialize( stripslashes_deep($meta_value) );
180
181         $query = $wpdb->prepare( "SELECT $id_column FROM $table WHERE meta_key = %s", $meta_key );
182
183         if ( !$delete_all )
184                 $query .= $wpdb->prepare(" AND $type_column = %d", $object_id );
185
186         if ( $meta_value )
187                 $query .= $wpdb->prepare(" AND meta_value = %s", $meta_value );
188
189         $meta_ids = $wpdb->get_col( $query );
190         if ( !count( $meta_ids ) )
191                 return false;
192
193         $query = "DELETE FROM $table WHERE $id_column IN( " . implode( ',', $meta_ids ) . " )";
194
195         $count = $wpdb->query($query);
196
197         if ( !$count )
198                 return false;
199
200         wp_cache_delete($object_id, $meta_type . '_meta');
201         // users cache stores usermeta that must be cleared.
202         if ( 'user' == $meta_type )
203                 clean_user_cache($object_id);
204
205         do_action( "deleted_{$meta_type}_meta", $meta_ids, $object_id, $meta_key, $meta_value );
206
207         return true;
208 }
209
210 /**
211  * Retrieve metadata for the specified object.
212  *
213  * @since 2.9.0
214  *
215  * @param string $meta_type Type of object metadata is for (e.g., comment, post, or user)
216  * @param int $object_id ID of the object metadata is for
217  * @param string $meta_key Optional.  Metadata key.  If not specified, retrieve all metadata for
218  *              the specified object.
219  * @param bool $single Optional, default is false.  If true, return only the first value of the
220  *              specified meta_key.  This parameter has no effect if meta_key is not specified.
221  * @return string|array Single metadata value, or array of values
222  */
223 function get_metadata($meta_type, $object_id, $meta_key = '', $single = false) {
224         if ( !$meta_type )
225                 return false;
226
227         if ( !$object_id = absint($object_id) )
228                 return false;
229
230         $meta_cache = wp_cache_get($object_id, $meta_type . '_meta');
231
232         if ( !$meta_cache ) {
233                 update_meta_cache($meta_type, $object_id);
234                 $meta_cache = wp_cache_get($object_id, $meta_type . '_meta');
235         }
236
237         if ( ! $meta_key )
238                 return $meta_cache;
239
240         if ( isset($meta_cache[$meta_key]) ) {
241                 if ( $single )
242                         return maybe_unserialize( $meta_cache[$meta_key][0] );
243                 else
244                         return array_map('maybe_unserialize', $meta_cache[$meta_key]);
245         }
246
247         if ($single)
248                 return '';
249         else
250                 return array();
251 }
252
253 /**
254  * Update the metadata cache for the specified objects.
255  *
256  * @since 2.9.0
257  * @uses $wpdb WordPress database object for queries.
258  *
259  * @param string $meta_type Type of object metadata is for (e.g., comment, post, or user)
260  * @param int|array $object_ids array or comma delimited list of object IDs to update cache for
261  * @return mixed Metadata cache for the specified objects, or false on failure.
262  */
263 function update_meta_cache($meta_type, $object_ids) {
264         if ( empty( $meta_type ) || empty( $object_ids ) )
265                 return false;
266
267         if ( ! $table = _get_meta_table($meta_type) )
268                 return false;
269
270         $column = esc_sql($meta_type . '_id');
271
272         global $wpdb;
273
274         if ( !is_array($object_ids) ) {
275                 $object_ids = preg_replace('|[^0-9,]|', '', $object_ids);
276                 $object_ids = explode(',', $object_ids);
277         }
278
279         $object_ids = array_map('intval', $object_ids);
280
281         $cache_key = $meta_type . '_meta';
282         $ids = array();
283         foreach ( $object_ids as $id ) {
284                 if ( false === wp_cache_get($id, $cache_key) )
285                         $ids[] = $id;
286         }
287
288         if ( empty( $ids ) )
289                 return false;
290
291         // Get meta info
292         $id_list = join(',', $ids);
293         $cache = array();
294         $meta_list = $wpdb->get_results( $wpdb->prepare("SELECT $column, meta_key, meta_value FROM $table WHERE $column IN ($id_list)",
295                 $meta_type), ARRAY_A );
296
297         if ( !empty($meta_list) ) {
298                 foreach ( $meta_list as $metarow) {
299                         $mpid = intval($metarow[$column]);
300                         $mkey = $metarow['meta_key'];
301                         $mval = $metarow['meta_value'];
302
303                         // Force subkeys to be array type:
304                         if ( !isset($cache[$mpid]) || !is_array($cache[$mpid]) )
305                                 $cache[$mpid] = array();
306                         if ( !isset($cache[$mpid][$mkey]) || !is_array($cache[$mpid][$mkey]) )
307                                 $cache[$mpid][$mkey] = array();
308
309                         // Add a value to the current pid/key:
310                         $cache[$mpid][$mkey][] = $mval;
311                 }
312         }
313
314         foreach ( $ids as $id ) {
315                 if ( ! isset($cache[$id]) )
316                         $cache[$id] = array();
317         }
318
319         foreach ( array_keys($cache) as $object)
320                 wp_cache_set($object, $cache[$object], $cache_key);
321
322         return $cache;
323 }
324
325 /**
326  * Retrieve the name of the metadata table for the specified object type.
327  *
328  * @since 2.9.0
329  * @uses $wpdb WordPress database object for queries.
330  *
331  * @param string $meta_type Type of object to get metadata table for (e.g., comment, post, or user)
332  * @return mixed Metadata table name, or false if no metadata table exists
333  */
334 function _get_meta_table($type) {
335         global $wpdb;
336
337         $table_name = $type . 'meta';
338
339         if ( empty($wpdb->$table_name) )
340                 return false;
341
342         return $wpdb->$table_name;
343 }
344 ?>