wp/wp-includes/meta.php
changeset 5 5e2f62d02dcd
parent 0 d970ebf37754
child 7 cf61fcea0001
equal deleted inserted replaced
4:346c88efed21 5:5e2f62d02dcd
    13 
    13 
    14 /**
    14 /**
    15  * Add metadata for the specified object.
    15  * Add metadata for the specified object.
    16  *
    16  *
    17  * @since 2.9.0
    17  * @since 2.9.0
    18  * @uses $wpdb WordPress database object for queries.
    18  *
    19  * @uses do_action() Calls 'added_{$meta_type}_meta' with meta_id of added metadata entry,
    19  * @global wpdb $wpdb WordPress database abstraction object.
    20  * 		object ID, meta key, and meta value
       
    21  *
    20  *
    22  * @param string $meta_type Type of object metadata is for (e.g., comment, post, or user)
    21  * @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
    22  * @param int $object_id ID of the object metadata is for
    24  * @param string $meta_key Metadata key
    23  * @param string $meta_key Metadata key
    25  * @param mixed $meta_value Metadata value. Must be serializable if non-scalar.
    24  * @param mixed $meta_value Metadata value. Must be serializable if non-scalar.
    26  * @param bool $unique Optional, default is false. Whether the specified metadata key should be
    25  * @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
    26  * 		unique for the object. If true, and the object already has a value for the specified
    28  * 		metadata key, no change will be made
    27  * 		metadata key, no change will be made
    29  * @return int|bool The meta ID on successful update, false on failure.
    28  * @return int|bool The meta ID on success, false on failure.
    30  */
    29  */
    31 function add_metadata($meta_type, $object_id, $meta_key, $meta_value, $unique = false) {
    30 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;
    31 	global $wpdb;
       
    32 
       
    33 	if ( ! $meta_type || ! $meta_key || ! is_numeric( $object_id ) ) {
       
    34 		return false;
       
    35 	}
       
    36 
       
    37 	$object_id = absint( $object_id );
       
    38 	if ( ! $object_id ) {
       
    39 		return false;
       
    40 	}
       
    41 
       
    42 	$table = _get_meta_table( $meta_type );
       
    43 	if ( ! $table ) {
       
    44 		return false;
       
    45 	}
    42 
    46 
    43 	$column = sanitize_key($meta_type . '_id');
    47 	$column = sanitize_key($meta_type . '_id');
    44 
    48 
    45 	// expected_slashed ($meta_key)
    49 	// expected_slashed ($meta_key)
    46 	$meta_key = wp_unslash($meta_key);
    50 	$meta_key = wp_unslash($meta_key);
    47 	$meta_value = wp_unslash($meta_value);
    51 	$meta_value = wp_unslash($meta_value);
    48 	$meta_value = sanitize_meta( $meta_key, $meta_value, $meta_type );
    52 	$meta_value = sanitize_meta( $meta_key, $meta_value, $meta_type );
    49 
    53 
       
    54 	/**
       
    55 	 * Filter whether to add metadata of a specific type.
       
    56 	 *
       
    57 	 * The dynamic portion of the hook, `$meta_type`, refers to the meta
       
    58 	 * object type (comment, post, or user). Returning a non-null value
       
    59 	 * will effectively short-circuit the function.
       
    60 	 *
       
    61 	 * @since 3.1.0
       
    62 	 *
       
    63 	 * @param null|bool $check      Whether to allow adding metadata for the given type.
       
    64 	 * @param int       $object_id  Object ID.
       
    65 	 * @param string    $meta_key   Meta key.
       
    66 	 * @param mixed     $meta_value Meta value. Must be serializable if non-scalar.
       
    67 	 * @param bool      $unique     Whether the specified meta key should be unique
       
    68 	 *                              for the object. Optional. Default false.
       
    69 	 */
    50 	$check = apply_filters( "add_{$meta_type}_metadata", null, $object_id, $meta_key, $meta_value, $unique );
    70 	$check = apply_filters( "add_{$meta_type}_metadata", null, $object_id, $meta_key, $meta_value, $unique );
    51 	if ( null !== $check )
    71 	if ( null !== $check )
    52 		return $check;
    72 		return $check;
    53 
    73 
    54 	if ( $unique && $wpdb->get_var( $wpdb->prepare(
    74 	if ( $unique && $wpdb->get_var( $wpdb->prepare(
    57 		return false;
    77 		return false;
    58 
    78 
    59 	$_meta_value = $meta_value;
    79 	$_meta_value = $meta_value;
    60 	$meta_value = maybe_serialize( $meta_value );
    80 	$meta_value = maybe_serialize( $meta_value );
    61 
    81 
       
    82 	/**
       
    83 	 * Fires immediately before meta of a specific type is added.
       
    84 	 *
       
    85 	 * The dynamic portion of the hook, `$meta_type`, refers to the meta
       
    86 	 * object type (comment, post, or user).
       
    87 	 *
       
    88 	 * @since 3.1.0
       
    89 	 *
       
    90 	 * @param int    $object_id  Object ID.
       
    91 	 * @param string $meta_key   Meta key.
       
    92 	 * @param mixed  $meta_value Meta value.
       
    93 	 */
    62 	do_action( "add_{$meta_type}_meta", $object_id, $meta_key, $_meta_value );
    94 	do_action( "add_{$meta_type}_meta", $object_id, $meta_key, $_meta_value );
    63 
    95 
    64 	$result = $wpdb->insert( $table, array(
    96 	$result = $wpdb->insert( $table, array(
    65 		$column => $object_id,
    97 		$column => $object_id,
    66 		'meta_key' => $meta_key,
    98 		'meta_key' => $meta_key,
    72 
   104 
    73 	$mid = (int) $wpdb->insert_id;
   105 	$mid = (int) $wpdb->insert_id;
    74 
   106 
    75 	wp_cache_delete($object_id, $meta_type . '_meta');
   107 	wp_cache_delete($object_id, $meta_type . '_meta');
    76 
   108 
       
   109 	/**
       
   110 	 * Fires immediately after meta of a specific type is added.
       
   111 	 *
       
   112 	 * The dynamic portion of the hook, `$meta_type`, refers to the meta
       
   113 	 * object type (comment, post, or user).
       
   114 	 *
       
   115 	 * @since 2.9.0
       
   116 	 *
       
   117 	 * @param int    $mid        The meta ID after successful update.
       
   118 	 * @param int    $object_id  Object ID.
       
   119 	 * @param string $meta_key   Meta key.
       
   120 	 * @param mixed  $meta_value Meta value.
       
   121 	 */
    77 	do_action( "added_{$meta_type}_meta", $mid, $object_id, $meta_key, $_meta_value );
   122 	do_action( "added_{$meta_type}_meta", $mid, $object_id, $meta_key, $_meta_value );
    78 
   123 
    79 	return $mid;
   124 	return $mid;
    80 }
   125 }
    81 
   126 
    82 /**
   127 /**
    83  * Update metadata for the specified object. If no value already exists for the specified object
   128  * Update metadata for the specified object. If no value already exists for the specified object
    84  * ID and metadata key, the metadata will be added.
   129  * ID and metadata key, the metadata will be added.
    85  *
   130  *
    86  * @since 2.9.0
   131  * @since 2.9.0
    87  * @uses $wpdb WordPress database object for queries.
   132  *
    88  * @uses do_action() Calls 'update_{$meta_type}_meta' before updating metadata with meta_id of
   133  * @global wpdb $wpdb WordPress database abstraction object.
    89  * 		metadata entry to update, object ID, meta key, and meta value
       
    90  * @uses do_action() Calls 'updated_{$meta_type}_meta' after updating metadata with meta_id of
       
    91  * 		updated metadata entry, object ID, meta key, and meta value
       
    92  *
   134  *
    93  * @param string $meta_type Type of object metadata is for (e.g., comment, post, or user)
   135  * @param string $meta_type Type of object metadata is for (e.g., comment, post, or user)
    94  * @param int $object_id ID of the object metadata is for
   136  * @param int $object_id ID of the object metadata is for
    95  * @param string $meta_key Metadata key
   137  * @param string $meta_key Metadata key
    96  * @param mixed $meta_value Metadata value. Must be serializable if non-scalar.
   138  * @param mixed $meta_value Metadata value. Must be serializable if non-scalar.
    97  * @param mixed $prev_value Optional. If specified, only update existing metadata entries with
   139  * @param mixed $prev_value Optional. If specified, only update existing metadata entries with
    98  * 		the specified value. Otherwise, update all entries.
   140  * 		the specified value. Otherwise, update all entries.
    99  * @return bool True on successful update, false on failure.
   141  * @return int|bool Meta ID if the key didn't exist, true on successful update, false on failure.
   100  */
   142  */
   101 function update_metadata($meta_type, $object_id, $meta_key, $meta_value, $prev_value = '') {
   143 function update_metadata($meta_type, $object_id, $meta_key, $meta_value, $prev_value = '') {
   102 	if ( !$meta_type || !$meta_key )
       
   103 		return false;
       
   104 
       
   105 	if ( !$object_id = absint($object_id) )
       
   106 		return false;
       
   107 
       
   108 	if ( ! $table = _get_meta_table($meta_type) )
       
   109 		return false;
       
   110 
       
   111 	global $wpdb;
   144 	global $wpdb;
       
   145 
       
   146 	if ( ! $meta_type || ! $meta_key || ! is_numeric( $object_id ) ) {
       
   147 		return false;
       
   148 	}
       
   149 
       
   150 	$object_id = absint( $object_id );
       
   151 	if ( ! $object_id ) {
       
   152 		return false;
       
   153 	}
       
   154 
       
   155 	$table = _get_meta_table( $meta_type );
       
   156 	if ( ! $table ) {
       
   157 		return false;
       
   158 	}
   112 
   159 
   113 	$column = sanitize_key($meta_type . '_id');
   160 	$column = sanitize_key($meta_type . '_id');
   114 	$id_column = 'user' == $meta_type ? 'umeta_id' : 'meta_id';
   161 	$id_column = 'user' == $meta_type ? 'umeta_id' : 'meta_id';
   115 
   162 
   116 	// expected_slashed ($meta_key)
   163 	// expected_slashed ($meta_key)
   117 	$meta_key = wp_unslash($meta_key);
   164 	$meta_key = wp_unslash($meta_key);
   118 	$passed_value = $meta_value;
   165 	$passed_value = $meta_value;
   119 	$meta_value = wp_unslash($meta_value);
   166 	$meta_value = wp_unslash($meta_value);
   120 	$meta_value = sanitize_meta( $meta_key, $meta_value, $meta_type );
   167 	$meta_value = sanitize_meta( $meta_key, $meta_value, $meta_type );
   121 
   168 
       
   169 	/**
       
   170 	 * Filter whether to update metadata of a specific type.
       
   171 	 *
       
   172 	 * The dynamic portion of the hook, `$meta_type`, refers to the meta
       
   173 	 * object type (comment, post, or user). Returning a non-null value
       
   174 	 * will effectively short-circuit the function.
       
   175 	 *
       
   176 	 * @since 3.1.0
       
   177 	 *
       
   178 	 * @param null|bool $check      Whether to allow updating metadata for the given type.
       
   179 	 * @param int       $object_id  Object ID.
       
   180 	 * @param string    $meta_key   Meta key.
       
   181 	 * @param mixed     $meta_value Meta value. Must be serializable if non-scalar.
       
   182 	 * @param mixed     $prev_value Optional. If specified, only update existing
       
   183 	 *                              metadata entries with the specified value.
       
   184 	 *                              Otherwise, update all entries.
       
   185 	 */
   122 	$check = apply_filters( "update_{$meta_type}_metadata", null, $object_id, $meta_key, $meta_value, $prev_value );
   186 	$check = apply_filters( "update_{$meta_type}_metadata", null, $object_id, $meta_key, $meta_value, $prev_value );
   123 	if ( null !== $check )
   187 	if ( null !== $check )
   124 		return (bool) $check;
   188 		return (bool) $check;
   125 
   189 
   126 	// Compare existing value to new value if no prev value given and the key exists only once.
   190 	// Compare existing value to new value if no prev value given and the key exists only once.
   130 			if ( $old_value[0] === $meta_value )
   194 			if ( $old_value[0] === $meta_value )
   131 				return false;
   195 				return false;
   132 		}
   196 		}
   133 	}
   197 	}
   134 
   198 
   135 	if ( ! $meta_id = $wpdb->get_var( $wpdb->prepare( "SELECT $id_column FROM $table WHERE meta_key = %s AND $column = %d", $meta_key, $object_id ) ) )
   199 	$meta_ids = $wpdb->get_col( $wpdb->prepare( "SELECT $id_column FROM $table WHERE meta_key = %s AND $column = %d", $meta_key, $object_id ) );
       
   200 	if ( empty( $meta_ids ) ) {
   136 		return add_metadata($meta_type, $object_id, $meta_key, $passed_value);
   201 		return add_metadata($meta_type, $object_id, $meta_key, $passed_value);
       
   202 	}
   137 
   203 
   138 	$_meta_value = $meta_value;
   204 	$_meta_value = $meta_value;
   139 	$meta_value = maybe_serialize( $meta_value );
   205 	$meta_value = maybe_serialize( $meta_value );
   140 
   206 
   141 	$data  = compact( 'meta_value' );
   207 	$data  = compact( 'meta_value' );
   144 	if ( !empty( $prev_value ) ) {
   210 	if ( !empty( $prev_value ) ) {
   145 		$prev_value = maybe_serialize($prev_value);
   211 		$prev_value = maybe_serialize($prev_value);
   146 		$where['meta_value'] = $prev_value;
   212 		$where['meta_value'] = $prev_value;
   147 	}
   213 	}
   148 
   214 
   149 	do_action( "update_{$meta_type}_meta", $meta_id, $object_id, $meta_key, $_meta_value );
   215 	foreach ( $meta_ids as $meta_id ) {
   150 
   216 		/**
   151 	if ( 'post' == $meta_type )
   217 		 * Fires immediately before updating metadata of a specific type.
   152 		do_action( 'update_postmeta', $meta_id, $object_id, $meta_key, $meta_value );
   218 		 *
       
   219 		 * The dynamic portion of the hook, `$meta_type`, refers to the meta
       
   220 		 * object type (comment, post, or user).
       
   221 		 *
       
   222 		 * @since 2.9.0
       
   223 		 *
       
   224 		 * @param int    $meta_id    ID of the metadata entry to update.
       
   225 		 * @param int    $object_id  Object ID.
       
   226 		 * @param string $meta_key   Meta key.
       
   227 		 * @param mixed  $meta_value Meta value.
       
   228 		 */
       
   229 		do_action( "update_{$meta_type}_meta", $meta_id, $object_id, $meta_key, $_meta_value );
       
   230 	}
       
   231 
       
   232 	if ( 'post' == $meta_type ) {
       
   233 		foreach ( $meta_ids as $meta_id ) {
       
   234 			/**
       
   235 			 * Fires immediately before updating a post's metadata.
       
   236 			 *
       
   237 			 * @since 2.9.0
       
   238 			 *
       
   239 			 * @param int    $meta_id    ID of metadata entry to update.
       
   240 			 * @param int    $object_id  Object ID.
       
   241 			 * @param string $meta_key   Meta key.
       
   242 			 * @param mixed  $meta_value Meta value.
       
   243 			 */
       
   244 			do_action( 'update_postmeta', $meta_id, $object_id, $meta_key, $meta_value );
       
   245 		}
       
   246 	}
   153 
   247 
   154 	$result = $wpdb->update( $table, $data, $where );
   248 	$result = $wpdb->update( $table, $data, $where );
   155 	if ( ! $result )
   249 	if ( ! $result )
   156 		return false;
   250 		return false;
   157 
   251 
   158 	wp_cache_delete($object_id, $meta_type . '_meta');
   252 	wp_cache_delete($object_id, $meta_type . '_meta');
   159 
   253 
   160 	do_action( "updated_{$meta_type}_meta", $meta_id, $object_id, $meta_key, $_meta_value );
   254 	foreach ( $meta_ids as $meta_id ) {
   161 
   255 		/**
   162 	if ( 'post' == $meta_type )
   256 		 * Fires immediately after updating metadata of a specific type.
   163 		do_action( 'updated_postmeta', $meta_id, $object_id, $meta_key, $meta_value );
   257 		 *
       
   258 		 * The dynamic portion of the hook, `$meta_type`, refers to the meta
       
   259 		 * object type (comment, post, or user).
       
   260 		 *
       
   261 		 * @since 2.9.0
       
   262 		 *
       
   263 		 * @param int    $meta_id    ID of updated metadata entry.
       
   264 		 * @param int    $object_id  Object ID.
       
   265 		 * @param string $meta_key   Meta key.
       
   266 		 * @param mixed  $meta_value Meta value.
       
   267 		 */
       
   268 		do_action( "updated_{$meta_type}_meta", $meta_id, $object_id, $meta_key, $_meta_value );
       
   269 	}
       
   270 
       
   271 	if ( 'post' == $meta_type ) {
       
   272 		foreach ( $meta_ids as $meta_id ) {
       
   273 			/**
       
   274 			 * Fires immediately after updating a post's metadata.
       
   275 			 *
       
   276 			 * @since 2.9.0
       
   277 			 *
       
   278 			 * @param int    $meta_id    ID of updated metadata entry.
       
   279 			 * @param int    $object_id  Object ID.
       
   280 			 * @param string $meta_key   Meta key.
       
   281 			 * @param mixed  $meta_value Meta value.
       
   282 			 */
       
   283 			do_action( 'updated_postmeta', $meta_id, $object_id, $meta_key, $meta_value );
       
   284 		}
       
   285 	}
   164 
   286 
   165 	return true;
   287 	return true;
   166 }
   288 }
   167 
   289 
   168 /**
   290 /**
   169  * Delete metadata for the specified object.
   291  * Delete metadata for the specified object.
   170  *
   292  *
   171  * @since 2.9.0
   293  * @since 2.9.0
   172  * @uses $wpdb WordPress database object for queries.
   294  *
   173  * @uses do_action() Calls 'deleted_{$meta_type}_meta' after deleting with meta_id of
   295  * @global wpdb $wpdb WordPress database abstraction object.
   174  * 		deleted metadata entries, object ID, meta key, and meta value
       
   175  *
   296  *
   176  * @param string $meta_type Type of object metadata is for (e.g., comment, post, or user)
   297  * @param string $meta_type Type of object metadata is for (e.g., comment, post, or user)
   177  * @param int $object_id ID of the object metadata is for
   298  * @param int $object_id ID of the object metadata is for
   178  * @param string $meta_key Metadata key
   299  * @param string $meta_key Metadata key
   179  * @param mixed $meta_value Optional. Metadata value. Must be serializable if non-scalar. If specified, only delete metadata entries
   300  * @param mixed $meta_value Optional. Metadata value. Must be serializable if non-scalar. If specified, only delete metadata entries
   182  * 		for all objects, ignoring the specified object_id. Otherwise, only delete matching
   303  * 		for all objects, ignoring the specified object_id. Otherwise, only delete matching
   183  * 		metadata entries for the specified object_id.
   304  * 		metadata entries for the specified object_id.
   184  * @return bool True on successful delete, false on failure.
   305  * @return bool True on successful delete, false on failure.
   185  */
   306  */
   186 function delete_metadata($meta_type, $object_id, $meta_key, $meta_value = '', $delete_all = false) {
   307 function delete_metadata($meta_type, $object_id, $meta_key, $meta_value = '', $delete_all = false) {
   187 	if ( !$meta_type || !$meta_key )
       
   188 		return false;
       
   189 
       
   190 	if ( (!$object_id = absint($object_id)) && !$delete_all )
       
   191 		return false;
       
   192 
       
   193 	if ( ! $table = _get_meta_table($meta_type) )
       
   194 		return false;
       
   195 
       
   196 	global $wpdb;
   308 	global $wpdb;
       
   309 
       
   310 	if ( ! $meta_type || ! $meta_key || ! is_numeric( $object_id ) && ! $delete_all ) {
       
   311 		return false;
       
   312 	}
       
   313 
       
   314 	$object_id = absint( $object_id );
       
   315 	if ( ! $object_id && ! $delete_all ) {
       
   316 		return false;
       
   317 	}
       
   318 
       
   319 	$table = _get_meta_table( $meta_type );
       
   320 	if ( ! $table ) {
       
   321 		return false;
       
   322 	}
   197 
   323 
   198 	$type_column = sanitize_key($meta_type . '_id');
   324 	$type_column = sanitize_key($meta_type . '_id');
   199 	$id_column = 'user' == $meta_type ? 'umeta_id' : 'meta_id';
   325 	$id_column = 'user' == $meta_type ? 'umeta_id' : 'meta_id';
   200 	// expected_slashed ($meta_key)
   326 	// expected_slashed ($meta_key)
   201 	$meta_key = wp_unslash($meta_key);
   327 	$meta_key = wp_unslash($meta_key);
   202 	$meta_value = wp_unslash($meta_value);
   328 	$meta_value = wp_unslash($meta_value);
   203 
   329 
       
   330 	/**
       
   331 	 * Filter whether to delete metadata of a specific type.
       
   332 	 *
       
   333 	 * The dynamic portion of the hook, `$meta_type`, refers to the meta
       
   334 	 * object type (comment, post, or user). Returning a non-null value
       
   335 	 * will effectively short-circuit the function.
       
   336 	 *
       
   337 	 * @since 3.1.0
       
   338 	 *
       
   339 	 * @param null|bool $delete     Whether to allow metadata deletion of the given type.
       
   340 	 * @param int       $object_id  Object ID.
       
   341 	 * @param string    $meta_key   Meta key.
       
   342 	 * @param mixed     $meta_value Meta value. Must be serializable if non-scalar.
       
   343 	 * @param bool      $delete_all Whether to delete the matching metadata entries
       
   344 	 *                              for all objects, ignoring the specified $object_id.
       
   345 	 *                              Default false.
       
   346 	 */
   204 	$check = apply_filters( "delete_{$meta_type}_metadata", null, $object_id, $meta_key, $meta_value, $delete_all );
   347 	$check = apply_filters( "delete_{$meta_type}_metadata", null, $object_id, $meta_key, $meta_value, $delete_all );
   205 	if ( null !== $check )
   348 	if ( null !== $check )
   206 		return (bool) $check;
   349 		return (bool) $check;
   207 
   350 
   208 	$_meta_value = $meta_value;
   351 	$_meta_value = $meta_value;
   221 		return false;
   364 		return false;
   222 
   365 
   223 	if ( $delete_all )
   366 	if ( $delete_all )
   224 		$object_ids = $wpdb->get_col( $wpdb->prepare( "SELECT $type_column FROM $table WHERE meta_key = %s", $meta_key ) );
   367 		$object_ids = $wpdb->get_col( $wpdb->prepare( "SELECT $type_column FROM $table WHERE meta_key = %s", $meta_key ) );
   225 
   368 
       
   369 	/**
       
   370 	 * Fires immediately before deleting metadata of a specific type.
       
   371 	 *
       
   372 	 * The dynamic portion of the hook, `$meta_type`, refers to the meta
       
   373 	 * object type (comment, post, or user).
       
   374 	 *
       
   375 	 * @since 3.1.0
       
   376 	 *
       
   377 	 * @param array  $meta_ids   An array of metadata entry IDs to delete.
       
   378 	 * @param int    $object_id  Object ID.
       
   379 	 * @param string $meta_key   Meta key.
       
   380 	 * @param mixed  $meta_value Meta value.
       
   381 	 */
   226 	do_action( "delete_{$meta_type}_meta", $meta_ids, $object_id, $meta_key, $_meta_value );
   382 	do_action( "delete_{$meta_type}_meta", $meta_ids, $object_id, $meta_key, $_meta_value );
   227 
   383 
   228 	// Old-style action.
   384 	// Old-style action.
   229 	if ( 'post' == $meta_type )
   385 	if ( 'post' == $meta_type ) {
       
   386 		/**
       
   387 		 * Fires immediately before deleting metadata for a post.
       
   388 		 *
       
   389 		 * @since 2.9.0
       
   390 		 *
       
   391 		 * @param array $meta_ids An array of post metadata entry IDs to delete.
       
   392 		 */
   230 		do_action( 'delete_postmeta', $meta_ids );
   393 		do_action( 'delete_postmeta', $meta_ids );
       
   394 	}
   231 
   395 
   232 	$query = "DELETE FROM $table WHERE $id_column IN( " . implode( ',', $meta_ids ) . " )";
   396 	$query = "DELETE FROM $table WHERE $id_column IN( " . implode( ',', $meta_ids ) . " )";
   233 
   397 
   234 	$count = $wpdb->query($query);
   398 	$count = $wpdb->query($query);
   235 
   399 
   242 		}
   406 		}
   243 	} else {
   407 	} else {
   244 		wp_cache_delete($object_id, $meta_type . '_meta');
   408 		wp_cache_delete($object_id, $meta_type . '_meta');
   245 	}
   409 	}
   246 
   410 
       
   411 	/**
       
   412 	 * Fires immediately after deleting metadata of a specific type.
       
   413 	 *
       
   414 	 * The dynamic portion of the hook name, `$meta_type`, refers to the meta
       
   415 	 * object type (comment, post, or user).
       
   416 	 *
       
   417 	 * @since 2.9.0
       
   418 	 *
       
   419 	 * @param array  $meta_ids   An array of deleted metadata entry IDs.
       
   420 	 * @param int    $object_id  Object ID.
       
   421 	 * @param string $meta_key   Meta key.
       
   422 	 * @param mixed  $meta_value Meta value.
       
   423 	 */
   247 	do_action( "deleted_{$meta_type}_meta", $meta_ids, $object_id, $meta_key, $_meta_value );
   424 	do_action( "deleted_{$meta_type}_meta", $meta_ids, $object_id, $meta_key, $_meta_value );
   248 
   425 
   249 	// Old-style action.
   426 	// Old-style action.
   250 	if ( 'post' == $meta_type )
   427 	if ( 'post' == $meta_type ) {
       
   428 		/**
       
   429 		 * Fires immediately after deleting metadata for a post.
       
   430 		 *
       
   431 		 * @since 2.9.0
       
   432 		 *
       
   433 		 * @param array $meta_ids An array of deleted post metadata entry IDs.
       
   434 		 */
   251 		do_action( 'deleted_postmeta', $meta_ids );
   435 		do_action( 'deleted_postmeta', $meta_ids );
       
   436 	}
   252 
   437 
   253 	return true;
   438 	return true;
   254 }
   439 }
   255 
   440 
   256 /**
   441 /**
   262  * @param int $object_id ID of the object metadata is for
   447  * @param int $object_id ID of the object metadata is for
   263  * @param string $meta_key Optional. Metadata key. If not specified, retrieve all metadata for
   448  * @param string $meta_key Optional. Metadata key. If not specified, retrieve all metadata for
   264  * 		the specified object.
   449  * 		the specified object.
   265  * @param bool $single Optional, default is false. If true, return only the first value of the
   450  * @param bool $single Optional, default is false. If true, return only the first value of the
   266  * 		specified meta_key. This parameter has no effect if meta_key is not specified.
   451  * 		specified meta_key. This parameter has no effect if meta_key is not specified.
   267  * @return string|array Single metadata value, or array of values
   452  * @return mixed Single metadata value, or array of values
   268  */
   453  */
   269 function get_metadata($meta_type, $object_id, $meta_key = '', $single = false) {
   454 function get_metadata($meta_type, $object_id, $meta_key = '', $single = false) {
   270 	if ( !$meta_type )
   455 	if ( ! $meta_type || ! is_numeric( $object_id ) ) {
   271 		return false;
   456 		return false;
   272 
   457 	}
   273 	if ( !$object_id = absint($object_id) )
   458 
   274 		return false;
   459 	$object_id = absint( $object_id );
   275 
   460 	if ( ! $object_id ) {
       
   461 		return false;
       
   462 	}
       
   463 
       
   464 	/**
       
   465 	 * Filter whether to retrieve metadata of a specific type.
       
   466 	 *
       
   467 	 * The dynamic portion of the hook, `$meta_type`, refers to the meta
       
   468 	 * object type (comment, post, or user). Returning a non-null value
       
   469 	 * will effectively short-circuit the function.
       
   470 	 *
       
   471 	 * @since 3.1.0
       
   472 	 *
       
   473 	 * @param null|array|string $value     The value get_metadata() should
       
   474 	 *                                     return - a single metadata value,
       
   475 	 *                                     or an array of values.
       
   476 	 * @param int               $object_id Object ID.
       
   477 	 * @param string            $meta_key  Meta key.
       
   478 	 * @param string|array      $single    Meta value, or an array of values.
       
   479 	 */
   276 	$check = apply_filters( "get_{$meta_type}_metadata", null, $object_id, $meta_key, $single );
   480 	$check = apply_filters( "get_{$meta_type}_metadata", null, $object_id, $meta_key, $single );
   277 	if ( null !== $check ) {
   481 	if ( null !== $check ) {
   278 		if ( $single && is_array( $check ) )
   482 		if ( $single && is_array( $check ) )
   279 			return $check[0];
   483 			return $check[0];
   280 		else
   484 		else
   286 	if ( !$meta_cache ) {
   490 	if ( !$meta_cache ) {
   287 		$meta_cache = update_meta_cache( $meta_type, array( $object_id ) );
   491 		$meta_cache = update_meta_cache( $meta_type, array( $object_id ) );
   288 		$meta_cache = $meta_cache[$object_id];
   492 		$meta_cache = $meta_cache[$object_id];
   289 	}
   493 	}
   290 
   494 
   291 	if ( !$meta_key )
   495 	if ( ! $meta_key ) {
   292 		return $meta_cache;
   496 		return $meta_cache;
       
   497 	}
   293 
   498 
   294 	if ( isset($meta_cache[$meta_key]) ) {
   499 	if ( isset($meta_cache[$meta_key]) ) {
   295 		if ( $single )
   500 		if ( $single )
   296 			return maybe_unserialize( $meta_cache[$meta_key][0] );
   501 			return maybe_unserialize( $meta_cache[$meta_key][0] );
   297 		else
   502 		else
   313  * @param int $object_id ID of the object metadata is for
   518  * @param int $object_id ID of the object metadata is for
   314  * @param string $meta_key Metadata key.
   519  * @param string $meta_key Metadata key.
   315  * @return boolean true of the key is set, false if not.
   520  * @return boolean true of the key is set, false if not.
   316  */
   521  */
   317 function metadata_exists( $meta_type, $object_id, $meta_key ) {
   522 function metadata_exists( $meta_type, $object_id, $meta_key ) {
   318 	if ( ! $meta_type )
   523 	if ( ! $meta_type || ! is_numeric( $object_id ) ) {
   319 		return false;
   524 		return false;
   320 
   525 	}
   321 	if ( ! $object_id = absint( $object_id ) )
   526 
   322 		return false;
   527 	$object_id = absint( $object_id );
   323 
   528 	if ( ! $object_id ) {
       
   529 		return false;
       
   530 	}
       
   531 
       
   532 	/** This filter is documented in wp-includes/meta.php */
   324 	$check = apply_filters( "get_{$meta_type}_metadata", null, $object_id, $meta_key, true );
   533 	$check = apply_filters( "get_{$meta_type}_metadata", null, $object_id, $meta_key, true );
   325 	if ( null !== $check )
   534 	if ( null !== $check )
   326 		return true;
   535 		return (bool) $check;
   327 
   536 
   328 	$meta_cache = wp_cache_get( $object_id, $meta_type . '_meta' );
   537 	$meta_cache = wp_cache_get( $object_id, $meta_type . '_meta' );
   329 
   538 
   330 	if ( !$meta_cache ) {
   539 	if ( !$meta_cache ) {
   331 		$meta_cache = update_meta_cache( $meta_type, array( $object_id ) );
   540 		$meta_cache = update_meta_cache( $meta_type, array( $object_id ) );
   348  * @return object Meta object or false.
   557  * @return object Meta object or false.
   349  */
   558  */
   350 function get_metadata_by_mid( $meta_type, $meta_id ) {
   559 function get_metadata_by_mid( $meta_type, $meta_id ) {
   351 	global $wpdb;
   560 	global $wpdb;
   352 
   561 
   353 	if ( ! $meta_type )
   562 	if ( ! $meta_type || ! is_numeric( $meta_id ) ) {
   354 		return false;
   563 		return false;
   355 
   564 	}
   356 	if ( !$meta_id = absint( $meta_id ) )
   565 
   357 		return false;
   566 	$meta_id = absint( $meta_id );
   358 
   567 	if ( ! $meta_id ) {
   359 	if ( ! $table = _get_meta_table($meta_type) )
   568 		return false;
   360 		return false;
   569 	}
       
   570 
       
   571 	$table = _get_meta_table( $meta_type );
       
   572 	if ( ! $table ) {
       
   573 		return false;
       
   574 	}
   361 
   575 
   362 	$id_column = ( 'user' == $meta_type ) ? 'umeta_id' : 'meta_id';
   576 	$id_column = ( 'user' == $meta_type ) ? 'umeta_id' : 'meta_id';
   363 
   577 
   364 	$meta = $wpdb->get_row( $wpdb->prepare( "SELECT * FROM $table WHERE $id_column = %d", $meta_id ) );
   578 	$meta = $wpdb->get_row( $wpdb->prepare( "SELECT * FROM $table WHERE $id_column = %d", $meta_id ) );
   365 
   579 
   374 
   588 
   375 /**
   589 /**
   376  * Update meta data by meta ID
   590  * Update meta data by meta ID
   377  *
   591  *
   378  * @since 3.3.0
   592  * @since 3.3.0
   379  *
       
   380  * @uses get_metadata_by_mid() Calls get_metadata_by_mid() to fetch the meta key, value
       
   381  *		and object_id of the given meta_id.
       
   382  *
   593  *
   383  * @param string $meta_type Type of object metadata is for (e.g., comment, post, or user)
   594  * @param string $meta_type Type of object metadata is for (e.g., comment, post, or user)
   384  * @param int $meta_id ID for a specific meta row
   595  * @param int $meta_id ID for a specific meta row
   385  * @param string $meta_value Metadata value
   596  * @param string $meta_value Metadata value
   386  * @param string $meta_key Optional, you can provide a meta key to update it
   597  * @param string $meta_key Optional, you can provide a meta key to update it
   388  */
   599  */
   389 function update_metadata_by_mid( $meta_type, $meta_id, $meta_value, $meta_key = false ) {
   600 function update_metadata_by_mid( $meta_type, $meta_id, $meta_value, $meta_key = false ) {
   390 	global $wpdb;
   601 	global $wpdb;
   391 
   602 
   392 	// Make sure everything is valid.
   603 	// Make sure everything is valid.
   393 	if ( ! $meta_type )
   604 	if ( ! $meta_type || ! is_numeric( $meta_id ) ) {
   394 		return false;
   605 		return false;
   395 
   606 	}
   396 	if ( ! $meta_id = absint( $meta_id ) )
   607 
   397 		return false;
   608 	$meta_id = absint( $meta_id );
   398 
   609 	if ( ! $meta_id ) {
   399 	if ( ! $table = _get_meta_table( $meta_type ) )
   610 		return false;
   400 		return false;
   611 	}
       
   612 
       
   613 	$table = _get_meta_table( $meta_type );
       
   614 	if ( ! $table ) {
       
   615 		return false;
       
   616 	}
   401 
   617 
   402 	$column = sanitize_key($meta_type . '_id');
   618 	$column = sanitize_key($meta_type . '_id');
   403 	$id_column = 'user' == $meta_type ? 'umeta_id' : 'meta_id';
   619 	$id_column = 'user' == $meta_type ? 'umeta_id' : 'meta_id';
   404 
   620 
   405 	// Fetch the meta and go on if it's found.
   621 	// Fetch the meta and go on if it's found.
   406 	if ( $meta = get_metadata_by_mid( $meta_type, $meta_id ) ) {
   622 	if ( $meta = get_metadata_by_mid( $meta_type, $meta_id ) ) {
   407 		$original_key = $meta->meta_key;
   623 		$original_key = $meta->meta_key;
   408 		$original_value = $meta->meta_value;
       
   409 		$object_id = $meta->{$column};
   624 		$object_id = $meta->{$column};
   410 
   625 
   411 		// If a new meta_key (last parameter) was specified, change the meta key,
   626 		// If a new meta_key (last parameter) was specified, change the meta key,
   412 		// otherwise use the original key in the update statement.
   627 		// otherwise use the original key in the update statement.
   413 		if ( false === $meta_key ) {
   628 		if ( false === $meta_key ) {
   429 
   644 
   430 		// Format the where query arguments.
   645 		// Format the where query arguments.
   431 		$where = array();
   646 		$where = array();
   432 		$where[$id_column] = $meta_id;
   647 		$where[$id_column] = $meta_id;
   433 
   648 
       
   649 		/** This action is documented in wp-includes/meta.php */
   434 		do_action( "update_{$meta_type}_meta", $meta_id, $object_id, $meta_key, $_meta_value );
   650 		do_action( "update_{$meta_type}_meta", $meta_id, $object_id, $meta_key, $_meta_value );
   435 
   651 
   436 		if ( 'post' == $meta_type )
   652 		if ( 'post' == $meta_type ) {
       
   653 			/** This action is documented in wp-includes/meta.php */
   437 			do_action( 'update_postmeta', $meta_id, $object_id, $meta_key, $meta_value );
   654 			do_action( 'update_postmeta', $meta_id, $object_id, $meta_key, $meta_value );
       
   655 		}
   438 
   656 
   439 		// Run the update query, all fields in $data are %s, $where is a %d.
   657 		// Run the update query, all fields in $data are %s, $where is a %d.
   440 		$result = $wpdb->update( $table, $data, $where, '%s', '%d' );
   658 		$result = $wpdb->update( $table, $data, $where, '%s', '%d' );
   441 		if ( ! $result )
   659 		if ( ! $result )
   442 			return false;
   660 			return false;
   443 
   661 
   444 		// Clear the caches.
   662 		// Clear the caches.
   445 		wp_cache_delete($object_id, $meta_type . '_meta');
   663 		wp_cache_delete($object_id, $meta_type . '_meta');
   446 
   664 
       
   665 		/** This action is documented in wp-includes/meta.php */
   447 		do_action( "updated_{$meta_type}_meta", $meta_id, $object_id, $meta_key, $_meta_value );
   666 		do_action( "updated_{$meta_type}_meta", $meta_id, $object_id, $meta_key, $_meta_value );
   448 
   667 
   449 		if ( 'post' == $meta_type )
   668 		if ( 'post' == $meta_type ) {
       
   669 			/** This action is documented in wp-includes/meta.php */
   450 			do_action( 'updated_postmeta', $meta_id, $object_id, $meta_key, $meta_value );
   670 			do_action( 'updated_postmeta', $meta_id, $object_id, $meta_key, $meta_value );
       
   671 		}
   451 
   672 
   452 		return true;
   673 		return true;
   453 	}
   674 	}
   454 
   675 
   455 	// And if the meta was not found.
   676 	// And if the meta was not found.
   458 
   679 
   459 /**
   680 /**
   460  * Delete meta data by meta ID
   681  * Delete meta data by meta ID
   461  *
   682  *
   462  * @since 3.3.0
   683  * @since 3.3.0
   463  *
       
   464  * @uses get_metadata_by_mid() Calls get_metadata_by_mid() to fetch the meta key, value
       
   465  *		and object_id of the given meta_id.
       
   466  *
   684  *
   467  * @param string $meta_type Type of object metadata is for (e.g., comment, post, or user)
   685  * @param string $meta_type Type of object metadata is for (e.g., comment, post, or user)
   468  * @param int $meta_id ID for a specific meta row
   686  * @param int $meta_id ID for a specific meta row
   469  * @return bool True on successful delete, false on failure.
   687  * @return bool True on successful delete, false on failure.
   470  */
   688  */
   471 function delete_metadata_by_mid( $meta_type, $meta_id ) {
   689 function delete_metadata_by_mid( $meta_type, $meta_id ) {
   472 	global $wpdb;
   690 	global $wpdb;
   473 
   691 
   474 	// Make sure everything is valid.
   692 	// Make sure everything is valid.
   475 	if ( ! $meta_type )
   693 	if ( ! $meta_type || ! is_numeric( $meta_id ) ) {
   476 		return false;
   694 		return false;
   477 
   695 	}
   478 	if ( ! $meta_id = absint( $meta_id ) )
   696 
   479 		return false;
   697 	$meta_id = absint( $meta_id );
   480 
   698 	if ( ! $meta_id ) {
   481 	if ( ! $table = _get_meta_table( $meta_type ) )
   699 		return false;
   482 		return false;
   700 	}
       
   701 
       
   702 	$table = _get_meta_table( $meta_type );
       
   703 	if ( ! $table ) {
       
   704 		return false;
       
   705 	}
   483 
   706 
   484 	// object and id columns
   707 	// object and id columns
   485 	$column = sanitize_key($meta_type . '_id');
   708 	$column = sanitize_key($meta_type . '_id');
   486 	$id_column = 'user' == $meta_type ? 'umeta_id' : 'meta_id';
   709 	$id_column = 'user' == $meta_type ? 'umeta_id' : 'meta_id';
   487 
   710 
   488 	// Fetch the meta and go on if it's found.
   711 	// Fetch the meta and go on if it's found.
   489 	if ( $meta = get_metadata_by_mid( $meta_type, $meta_id ) ) {
   712 	if ( $meta = get_metadata_by_mid( $meta_type, $meta_id ) ) {
   490 		$object_id = $meta->{$column};
   713 		$object_id = $meta->{$column};
   491 
   714 
       
   715 		/** This action is documented in wp-includes/meta.php */
   492 		do_action( "delete_{$meta_type}_meta", (array) $meta_id, $object_id, $meta->meta_key, $meta->meta_value );
   716 		do_action( "delete_{$meta_type}_meta", (array) $meta_id, $object_id, $meta->meta_key, $meta->meta_value );
   493 
   717 
   494 		// Old-style action.
   718 		// Old-style action.
   495 		if ( 'post' == $meta_type || 'comment' == $meta_type )
   719 		if ( 'post' == $meta_type || 'comment' == $meta_type ) {
       
   720 			/**
       
   721 			 * Fires immediately before deleting post or comment metadata of a specific type.
       
   722 			 *
       
   723 			 * The dynamic portion of the hook, `$meta_type`, refers to the meta
       
   724 			 * object type (post or comment).
       
   725 			 *
       
   726 			 * @since 3.4.0
       
   727 			 *
       
   728 			 * @param int $meta_id ID of the metadata entry to delete.
       
   729 			 */
   496 			do_action( "delete_{$meta_type}meta", $meta_id );
   730 			do_action( "delete_{$meta_type}meta", $meta_id );
       
   731 		}
   497 
   732 
   498 		// Run the query, will return true if deleted, false otherwise
   733 		// Run the query, will return true if deleted, false otherwise
   499 		$result = (bool) $wpdb->delete( $table, array( $id_column => $meta_id ) );
   734 		$result = (bool) $wpdb->delete( $table, array( $id_column => $meta_id ) );
   500 
   735 
   501 		// Clear the caches.
   736 		// Clear the caches.
   502 		wp_cache_delete($object_id, $meta_type . '_meta');
   737 		wp_cache_delete($object_id, $meta_type . '_meta');
   503 
   738 
       
   739 		/** This action is documented in wp-includes/meta.php */
   504 		do_action( "deleted_{$meta_type}_meta", (array) $meta_id, $object_id, $meta->meta_key, $meta->meta_value );
   740 		do_action( "deleted_{$meta_type}_meta", (array) $meta_id, $object_id, $meta->meta_key, $meta->meta_value );
   505 
   741 
   506 		// Old-style action.
   742 		// Old-style action.
   507 		if ( 'post' == $meta_type || 'comment' == $meta_type )
   743 		if ( 'post' == $meta_type || 'comment' == $meta_type ) {
       
   744 			/**
       
   745 			 * Fires immediately after deleting post or comment metadata of a specific type.
       
   746 			 *
       
   747 			 * The dynamic portion of the hook, `$meta_type`, refers to the meta
       
   748 			 * object type (post or comment).
       
   749 			 *
       
   750 			 * @since 3.4.0
       
   751 			 *
       
   752 			 * @param int $meta_ids Deleted metadata entry ID.
       
   753 			 */
   508 			do_action( "deleted_{$meta_type}meta", $meta_id );
   754 			do_action( "deleted_{$meta_type}meta", $meta_id );
       
   755 		}
   509 
   756 
   510 		return $result;
   757 		return $result;
   511 
   758 
   512 	}
   759 	}
   513 
   760 
   517 
   764 
   518 /**
   765 /**
   519  * Update the metadata cache for the specified objects.
   766  * Update the metadata cache for the specified objects.
   520  *
   767  *
   521  * @since 2.9.0
   768  * @since 2.9.0
   522  * @uses $wpdb WordPress database object for queries.
   769  *
       
   770  * @global wpdb $wpdb WordPress database abstraction object.
   523  *
   771  *
   524  * @param string $meta_type Type of object metadata is for (e.g., comment, post, or user)
   772  * @param string $meta_type Type of object metadata is for (e.g., comment, post, or user)
   525  * @param int|array $object_ids array or comma delimited list of object IDs to update cache for
   773  * @param int|array $object_ids array or comma delimited list of object IDs to update cache for
   526  * @return mixed Metadata cache for the specified objects, or false on failure.
   774  * @return mixed Metadata cache for the specified objects, or false on failure.
   527  */
   775  */
   528 function update_meta_cache($meta_type, $object_ids) {
   776 function update_meta_cache($meta_type, $object_ids) {
   529 	if ( empty( $meta_type ) || empty( $object_ids ) )
   777 	global $wpdb;
   530 		return false;
   778 
   531 
   779 	if ( ! $meta_type || ! $object_ids ) {
   532 	if ( ! $table = _get_meta_table($meta_type) )
   780 		return false;
   533 		return false;
   781 	}
       
   782 
       
   783 	$table = _get_meta_table( $meta_type );
       
   784 	if ( ! $table ) {
       
   785 		return false;
       
   786 	}
   534 
   787 
   535 	$column = sanitize_key($meta_type . '_id');
   788 	$column = sanitize_key($meta_type . '_id');
   536 
       
   537 	global $wpdb;
       
   538 
   789 
   539 	if ( !is_array($object_ids) ) {
   790 	if ( !is_array($object_ids) ) {
   540 		$object_ids = preg_replace('|[^0-9,]|', '', $object_ids);
   791 		$object_ids = preg_replace('|[^0-9,]|', '', $object_ids);
   541 		$object_ids = explode(',', $object_ids);
   792 		$object_ids = explode(',', $object_ids);
   542 	}
   793 	}
   556 
   807 
   557 	if ( empty( $ids ) )
   808 	if ( empty( $ids ) )
   558 		return $cache;
   809 		return $cache;
   559 
   810 
   560 	// Get meta info
   811 	// Get meta info
   561 	$id_list = join(',', $ids);
   812 	$id_list = join( ',', $ids );
   562 	$meta_list = $wpdb->get_results( $wpdb->prepare("SELECT $column, meta_key, meta_value FROM $table WHERE $column IN ($id_list)",
   813 	$id_column = 'user' == $meta_type ? 'umeta_id' : 'meta_id';
   563 		$meta_type), ARRAY_A );
   814 	$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 );
   564 
   815 
   565 	if ( !empty($meta_list) ) {
   816 	if ( !empty($meta_list) ) {
   566 		foreach ( $meta_list as $metarow) {
   817 		foreach ( $meta_list as $metarow) {
   567 			$mpid = intval($metarow[$column]);
   818 			$mpid = intval($metarow[$column]);
   568 			$mkey = $metarow['meta_key'];
   819 			$mkey = $metarow['meta_key'];
   587 
   838 
   588 	return $cache;
   839 	return $cache;
   589 }
   840 }
   590 
   841 
   591 /**
   842 /**
   592  * Given a meta query, generates SQL clauses to be appended to a main query
   843  * Given a meta query, generates SQL clauses to be appended to a main query.
   593  *
   844  *
   594  * @since 3.2.0
   845  * @since 3.2.0
   595  *
   846  *
   596  * @see WP_Meta_Query
   847  * @see WP_Meta_Query
   597  *
   848  *
   598  * @param array $meta_query A meta query
   849  * @param array $meta_query         A meta query.
   599  * @param string $type Type of meta
   850  * @param string $type              Type of meta.
   600  * @param string $primary_table
   851  * @param string $primary_table     Primary database table name.
   601  * @param string $primary_id_column
   852  * @param string $primary_id_column Primary ID column name.
   602  * @param object $context (optional) The main query object
   853  * @param object $context           Optional. The main query object
   603  * @return array( 'join' => $join_sql, 'where' => $where_sql )
   854  * @return array Associative array of `JOIN` and `WHERE` SQL.
   604  */
   855  */
   605 function get_meta_sql( $meta_query, $type, $primary_table, $primary_id_column, $context = null ) {
   856 function get_meta_sql( $meta_query, $type, $primary_table, $primary_id_column, $context = null ) {
   606 	$meta_query_obj = new WP_Meta_Query( $meta_query );
   857 	$meta_query_obj = new WP_Meta_Query( $meta_query );
   607 	return $meta_query_obj->get_sql( $type, $primary_table, $primary_id_column, $context );
   858 	return $meta_query_obj->get_sql( $type, $primary_table, $primary_id_column, $context );
   608 }
   859 }
   609 
   860 
   610 /**
   861 /**
   611  * Container class for a multiple metadata query
   862  * Class for generating SQL clauses that filter a primary query according to metadata keys and values.
       
   863  *
       
   864  * `WP_Meta_Query` is a helper that allows primary query classes, such as {@see WP_Query} and {@see WP_User_Query},
       
   865  * to filter their results by object metadata, by generating `JOIN` and `WHERE` subclauses to be attached
       
   866  * to the primary SQL query string.
   612  *
   867  *
   613  * @since 3.2.0
   868  * @since 3.2.0
   614  */
   869  */
   615 class WP_Meta_Query {
   870 class WP_Meta_Query {
   616 	/**
   871 	/**
   617 	* List of metadata queries. A single query is an associative array:
   872 	 * Array of metadata queries.
   618 	* - 'key' string The meta key
   873 	 *
   619 	* - 'value' string|array The meta value
   874 	 * See {@see WP_Meta_Query::__construct()} for information on meta query arguments.
   620 	* - 'compare' (optional) string How to compare the key to the value.
   875 	 *
   621 	*              Possible values: '=', '!=', '>', '>=', '<', '<=', 'LIKE', 'NOT LIKE', 'IN', 'NOT IN',
   876 	 * @since 3.2.0
   622 	*              'BETWEEN', 'NOT BETWEEN', 'REGEXP', 'NOT REGEXP', 'RLIKE'.
   877 	 * @access public
   623 	*              Default: '='
   878 	 * @var array
   624 	* - 'type' string (optional) The type of the value.
   879 	 */
   625 	*              Possible values: 'NUMERIC', 'BINARY', 'CHAR', 'DATE', 'DATETIME', 'DECIMAL', 'SIGNED', 'TIME', 'UNSIGNED'.
       
   626 	*              Default: 'CHAR'
       
   627 	*
       
   628 	* @since 3.2.0
       
   629 	* @access public
       
   630 	* @var array
       
   631 	*/
       
   632 	public $queries = array();
   880 	public $queries = array();
   633 
   881 
   634 	/**
   882 	/**
   635 	 * The relation between the queries. Can be one of 'AND' or 'OR'.
   883 	 * The relation between the queries. Can be one of 'AND' or 'OR'.
   636 	 *
   884 	 *
   639 	 * @var string
   887 	 * @var string
   640 	 */
   888 	 */
   641 	public $relation;
   889 	public $relation;
   642 
   890 
   643 	/**
   891 	/**
   644 	 * Constructor
   892 	 * Database table to query for the metadata.
   645 	 *
   893 	 *
   646 	 * @param array $meta_query (optional) A meta query
   894 	 * @since 4.1.0
   647 	 */
   895 	 * @access public
   648 	function __construct( $meta_query = false ) {
   896 	 * @var string
       
   897 	 */
       
   898 	public $meta_table;
       
   899 
       
   900 	/**
       
   901 	 * Column in meta_table that represents the ID of the object the metadata belongs to.
       
   902 	 *
       
   903 	 * @since 4.1.0
       
   904 	 * @access public
       
   905 	 * @var string
       
   906 	 */
       
   907 	public $meta_id_column;
       
   908 
       
   909 	/**
       
   910 	 * Database table that where the metadata's objects are stored (eg $wpdb->users).
       
   911 	 *
       
   912 	 * @since 4.1.0
       
   913 	 * @access public
       
   914 	 * @var string
       
   915 	 */
       
   916 	public $primary_table;
       
   917 
       
   918 	/**
       
   919 	 * Column in primary_table that represents the ID of the object.
       
   920 	 *
       
   921 	 * @since 4.1.0
       
   922 	 * @access public
       
   923 	 * @var string
       
   924 	 */
       
   925 	public $primary_id_column;
       
   926 
       
   927 	/**
       
   928 	 * A flat list of table aliases used in JOIN clauses.
       
   929 	 *
       
   930 	 * @since 4.1.0
       
   931 	 * @access protected
       
   932 	 * @var array
       
   933 	 */
       
   934 	protected $table_aliases = array();
       
   935 
       
   936 	/**
       
   937 	 * A flat list of clauses, keyed by clause 'name'.
       
   938 	 *
       
   939 	 * @since 4.2.0
       
   940 	 * @access protected
       
   941 	 * @var array
       
   942 	 */
       
   943 	protected $clauses = array();
       
   944 
       
   945 	/**
       
   946 	 * Constructor.
       
   947 	 *
       
   948 	 * @since 3.2.0
       
   949 	 * @since 4.2.0 Introduced support for naming query clauses by associative array keys.
       
   950 	 *
       
   951 	 * @access public
       
   952 	 *
       
   953 	 * @param array $meta_query {
       
   954 	 *     Array of meta query clauses. When first-order clauses use strings as their array keys, they may be
       
   955 	 *     referenced in the 'orderby' parameter of the parent query.
       
   956 	 *
       
   957 	 *     @type string $relation Optional. The MySQL keyword used to join
       
   958 	 *                            the clauses of the query. Accepts 'AND', or 'OR'. Default 'AND'.
       
   959 	 *     @type array {
       
   960 	 *         Optional. An array of first-order clause parameters, or another fully-formed meta query.
       
   961 	 *
       
   962 	 *         @type string $key     Meta key to filter by.
       
   963 	 *         @type string $value   Meta value to filter by.
       
   964 	 *         @type string $compare MySQL operator used for comparing the $value. Accepts '=',
       
   965 	 *                               '!=', '>', '>=', '<', '<=', 'LIKE', 'NOT LIKE', 'IN', 'NOT IN',
       
   966 	 *                               'BETWEEN', 'NOT BETWEEN', 'REGEXP', 'NOT REGEXP', or 'RLIKE'.
       
   967 	 *                               Default is 'IN' when `$value` is an array, '=' otherwise.
       
   968 	 *         @type string $type    MySQL data type that the meta_value column will be CAST to for
       
   969 	 *                               comparisons. Accepts 'NUMERIC', 'BINARY', 'CHAR', 'DATE',
       
   970 	 *                               'DATETIME', 'DECIMAL', 'SIGNED', 'TIME', or 'UNSIGNED'.
       
   971 	 *                               Default is 'CHAR'.
       
   972 	 *     }
       
   973 	 * }
       
   974 	 */
       
   975 	public function __construct( $meta_query = false ) {
   649 		if ( !$meta_query )
   976 		if ( !$meta_query )
   650 			return;
   977 			return;
   651 
   978 
   652 		if ( isset( $meta_query['relation'] ) && strtoupper( $meta_query['relation'] ) == 'OR' ) {
   979 		if ( isset( $meta_query['relation'] ) && strtoupper( $meta_query['relation'] ) == 'OR' ) {
   653 			$this->relation = 'OR';
   980 			$this->relation = 'OR';
   654 		} else {
   981 		} else {
   655 			$this->relation = 'AND';
   982 			$this->relation = 'AND';
   656 		}
   983 		}
   657 
   984 
   658 		$this->queries = array();
   985 		$this->queries = $this->sanitize_query( $meta_query );
   659 
   986 	}
   660 		foreach ( $meta_query as $key => $query ) {
   987 
   661 			if ( ! is_array( $query ) )
   988 	/**
       
   989 	 * Ensure the 'meta_query' argument passed to the class constructor is well-formed.
       
   990 	 *
       
   991 	 * Eliminates empty items and ensures that a 'relation' is set.
       
   992 	 *
       
   993 	 * @since 4.1.0
       
   994 	 * @access public
       
   995 	 *
       
   996 	 * @param array $queries Array of query clauses.
       
   997 	 * @return array Sanitized array of query clauses.
       
   998 	 */
       
   999 	public function sanitize_query( $queries ) {
       
  1000 		$clean_queries = array();
       
  1001 
       
  1002 		if ( ! is_array( $queries ) ) {
       
  1003 			return $clean_queries;
       
  1004 		}
       
  1005 
       
  1006 		foreach ( $queries as $key => $query ) {
       
  1007 			if ( 'relation' === $key ) {
       
  1008 				$relation = $query;
       
  1009 
       
  1010 			} elseif ( ! is_array( $query ) ) {
   662 				continue;
  1011 				continue;
   663 
  1012 
   664 			$this->queries[] = $query;
  1013 			// First-order clause.
   665 		}
  1014 			} elseif ( $this->is_first_order_clause( $query ) ) {
       
  1015 				if ( isset( $query['value'] ) && array() === $query['value'] ) {
       
  1016 					unset( $query['value'] );
       
  1017 				}
       
  1018 
       
  1019 				$clean_queries[ $key ] = $query;
       
  1020 
       
  1021 			// Otherwise, it's a nested query, so we recurse.
       
  1022 			} else {
       
  1023 				$cleaned_query = $this->sanitize_query( $query );
       
  1024 
       
  1025 				if ( ! empty( $cleaned_query ) ) {
       
  1026 					$clean_queries[ $key ] = $cleaned_query;
       
  1027 				}
       
  1028 			}
       
  1029 		}
       
  1030 
       
  1031 		if ( empty( $clean_queries ) ) {
       
  1032 			return $clean_queries;
       
  1033 		}
       
  1034 
       
  1035 		// Sanitize the 'relation' key provided in the query.
       
  1036 		if ( isset( $relation ) && 'OR' === strtoupper( $relation ) ) {
       
  1037 			$clean_queries['relation'] = 'OR';
       
  1038 
       
  1039 		/*
       
  1040 		 * If there is only a single clause, call the relation 'OR'.
       
  1041 		 * This value will not actually be used to join clauses, but it
       
  1042 		 * simplifies the logic around combining key-only queries.
       
  1043 		 */
       
  1044 		} elseif ( 1 === count( $clean_queries ) ) {
       
  1045 			$clean_queries['relation'] = 'OR';
       
  1046 
       
  1047 		// Default to AND.
       
  1048 		} else {
       
  1049 			$clean_queries['relation'] = 'AND';
       
  1050 		}
       
  1051 
       
  1052 		return $clean_queries;
       
  1053 	}
       
  1054 
       
  1055 	/**
       
  1056 	 * Determine whether a query clause is first-order.
       
  1057 	 *
       
  1058 	 * A first-order meta query clause is one that has either a 'key' or
       
  1059 	 * a 'value' array key.
       
  1060 	 *
       
  1061 	 * @since 4.1.0
       
  1062 	 * @access protected
       
  1063 	 *
       
  1064 	 * @param array $query Meta query arguments.
       
  1065 	 * @return bool Whether the query clause is a first-order clause.
       
  1066 	 */
       
  1067 	protected function is_first_order_clause( $query ) {
       
  1068 		return isset( $query['key'] ) || isset( $query['value'] );
   666 	}
  1069 	}
   667 
  1070 
   668 	/**
  1071 	/**
   669 	 * Constructs a meta query based on 'meta_*' query vars
  1072 	 * Constructs a meta query based on 'meta_*' query vars
   670 	 *
  1073 	 *
   671 	 * @since 3.2.0
  1074 	 * @since 3.2.0
   672 	 * @access public
  1075 	 * @access public
   673 	 *
  1076 	 *
   674 	 * @param array $qv The query variables
  1077 	 * @param array $qv The query variables
   675 	 */
  1078 	 */
   676 	function parse_query_vars( $qv ) {
  1079 	public function parse_query_vars( $qv ) {
   677 		$meta_query = array();
  1080 		$meta_query = array();
   678 
  1081 
   679 		// Simple query needs to be first for orderby=meta_value to work correctly
  1082 		/*
       
  1083 		 * For orderby=meta_value to work correctly, simple query needs to be
       
  1084 		 * first (so that its table join is against an unaliased meta table) and
       
  1085 		 * needs to be its own clause (so it doesn't interfere with the logic of
       
  1086 		 * the rest of the meta_query).
       
  1087 		 */
       
  1088 		$primary_meta_query = array();
   680 		foreach ( array( 'key', 'compare', 'type' ) as $key ) {
  1089 		foreach ( array( 'key', 'compare', 'type' ) as $key ) {
   681 			if ( !empty( $qv[ "meta_$key" ] ) )
  1090 			if ( ! empty( $qv[ "meta_$key" ] ) ) {
   682 				$meta_query[0][ $key ] = $qv[ "meta_$key" ];
  1091 				$primary_meta_query[ $key ] = $qv[ "meta_$key" ];
   683 		}
  1092 			}
   684 
  1093 		}
   685 		// WP_Query sets 'meta_value' = '' by default
  1094 
   686 		if ( isset( $qv[ 'meta_value' ] ) && '' !== $qv[ 'meta_value' ] && ( ! is_array( $qv[ 'meta_value' ] ) || $qv[ 'meta_value' ] ) )
  1095 		// WP_Query sets 'meta_value' = '' by default.
   687 			$meta_query[0]['value'] = $qv[ 'meta_value' ];
  1096 		if ( isset( $qv['meta_value'] ) && '' !== $qv['meta_value'] && ( ! is_array( $qv['meta_value'] ) || $qv['meta_value'] ) ) {
   688 
  1097 			$primary_meta_query['value'] = $qv['meta_value'];
   689 		if ( !empty( $qv['meta_query'] ) && is_array( $qv['meta_query'] ) ) {
  1098 		}
   690 			$meta_query = array_merge( $meta_query, $qv['meta_query'] );
  1099 
       
  1100 		$existing_meta_query = isset( $qv['meta_query'] ) && is_array( $qv['meta_query'] ) ? $qv['meta_query'] : array();
       
  1101 
       
  1102 		if ( ! empty( $primary_meta_query ) && ! empty( $existing_meta_query ) ) {
       
  1103 			$meta_query = array(
       
  1104 				'relation' => 'AND',
       
  1105 				$primary_meta_query,
       
  1106 				$existing_meta_query,
       
  1107 			);
       
  1108 		} elseif ( ! empty( $primary_meta_query ) ) {
       
  1109 			$meta_query = array(
       
  1110 				$primary_meta_query,
       
  1111 			);
       
  1112 		} elseif ( ! empty( $existing_meta_query ) ) {
       
  1113 			$meta_query = $existing_meta_query;
   691 		}
  1114 		}
   692 
  1115 
   693 		$this->__construct( $meta_query );
  1116 		$this->__construct( $meta_query );
   694 	}
  1117 	}
   695 
  1118 
   696 	/**
  1119 	/**
   697 	 * Given a meta type, return the appropriate alias if applicable
  1120 	 * Return the appropriate alias for the given meta type if applicable.
   698 	 *
  1121 	 *
   699 	 * @since 3.7.0
  1122 	 * @since 3.7.0
   700 	 *
  1123 	 * @access public
   701 	 * @param string $type MySQL type to cast meta_value
  1124 	 *
   702 	 * @return string MySQL type
  1125 	 * @param string $type MySQL type to cast meta_value.
   703 	 */
  1126 	 * @return string MySQL type.
   704 	function get_cast_for_type( $type = '' ) {
  1127 	 */
       
  1128 	public function get_cast_for_type( $type = '' ) {
   705 		if ( empty( $type ) )
  1129 		if ( empty( $type ) )
   706 			return 'CHAR';
  1130 			return 'CHAR';
   707 
  1131 
   708 		$meta_type = strtoupper( $type );
  1132 		$meta_type = strtoupper( $type );
   709 
  1133 
   710 		if ( ! in_array( $meta_type, array( 'BINARY', 'CHAR', 'DATE', 'DATETIME', 'DECIMAL', 'SIGNED', 'TIME', 'UNSIGNED', 'NUMERIC' ) ) )
  1134 		if ( ! preg_match( '/^(?:BINARY|CHAR|DATE|DATETIME|SIGNED|UNSIGNED|TIME|NUMERIC(?:\(\d+(?:,\s?\d+)?\))?|DECIMAL(?:\(\d+(?:,\s?\d+)?\))?)$/', $meta_type ) )
   711 			return 'CHAR';
  1135 			return 'CHAR';
   712 
  1136 
   713 		if ( 'NUMERIC' == $meta_type )
  1137 		if ( 'NUMERIC' == $meta_type )
   714 			$meta_type = 'SIGNED';
  1138 			$meta_type = 'SIGNED';
   715 
  1139 
   720 	 * Generates SQL clauses to be appended to a main query.
  1144 	 * Generates SQL clauses to be appended to a main query.
   721 	 *
  1145 	 *
   722 	 * @since 3.2.0
  1146 	 * @since 3.2.0
   723 	 * @access public
  1147 	 * @access public
   724 	 *
  1148 	 *
   725 	 * @param string $type Type of meta
  1149 	 * @param string $type              Type of meta, eg 'user', 'post'.
   726 	 * @param string $primary_table
  1150 	 * @param string $primary_table     Database table where the object being filtered is stored (eg wp_users).
   727 	 * @param string $primary_id_column
  1151 	 * @param string $primary_id_column ID column for the filtered object in $primary_table.
   728 	 * @param object $context (optional) The main query object
  1152 	 * @param object $context           Optional. The main query object.
   729 	 * @return array( 'join' => $join_sql, 'where' => $where_sql )
  1153 	 * @return array {
   730 	 */
  1154 	 *     Array containing JOIN and WHERE SQL clauses to append to the main query.
   731 	function get_sql( $type, $primary_table, $primary_id_column, $context = null ) {
  1155 	 *
       
  1156 	 *     @type string $join  SQL fragment to append to the main JOIN clause.
       
  1157 	 *     @type string $where SQL fragment to append to the main WHERE clause.
       
  1158 	 * }
       
  1159 	 */
       
  1160 	public function get_sql( $type, $primary_table, $primary_id_column, $context = null ) {
       
  1161 		if ( ! $meta_table = _get_meta_table( $type ) ) {
       
  1162 			return false;
       
  1163 		}
       
  1164 
       
  1165 		$this->meta_table     = $meta_table;
       
  1166 		$this->meta_id_column = sanitize_key( $type . '_id' );
       
  1167 
       
  1168 		$this->primary_table     = $primary_table;
       
  1169 		$this->primary_id_column = $primary_id_column;
       
  1170 
       
  1171 		$sql = $this->get_sql_clauses();
       
  1172 
       
  1173 		/*
       
  1174 		 * If any JOINs are LEFT JOINs (as in the case of NOT EXISTS), then all JOINs should
       
  1175 		 * be LEFT. Otherwise posts with no metadata will be excluded from results.
       
  1176 		 */
       
  1177 		if ( false !== strpos( $sql['join'], 'LEFT JOIN' ) ) {
       
  1178 			$sql['join'] = str_replace( 'INNER JOIN', 'LEFT JOIN', $sql['join'] );
       
  1179 		}
       
  1180 
       
  1181 		/**
       
  1182 		 * Filter the meta query's generated SQL.
       
  1183 		 *
       
  1184 		 * @since 3.1.0
       
  1185 		 *
       
  1186 		 * @param array $args {
       
  1187 		 *     An array of meta query SQL arguments.
       
  1188 		 *
       
  1189 		 *     @type array  $clauses           Array containing the query's JOIN and WHERE clauses.
       
  1190 		 *     @type array  $queries           Array of meta queries.
       
  1191 		 *     @type string $type              Type of meta.
       
  1192 		 *     @type string $primary_table     Primary table.
       
  1193 		 *     @type string $primary_id_column Primary column ID.
       
  1194 		 *     @type object $context           The main query object.
       
  1195 		 * }
       
  1196 		 */
       
  1197 		return apply_filters_ref_array( 'get_meta_sql', array( $sql, $this->queries, $type, $primary_table, $primary_id_column, $context ) );
       
  1198 	}
       
  1199 
       
  1200 	/**
       
  1201 	 * Generate SQL clauses to be appended to a main query.
       
  1202 	 *
       
  1203 	 * Called by the public {@see WP_Meta_Query::get_sql()}, this method
       
  1204 	 * is abstracted out to maintain parity with the other Query classes.
       
  1205 	 *
       
  1206 	 * @since 4.1.0
       
  1207 	 * @access protected
       
  1208 	 *
       
  1209 	 * @return array {
       
  1210 	 *     Array containing JOIN and WHERE SQL clauses to append to the main query.
       
  1211 	 *
       
  1212 	 *     @type string $join  SQL fragment to append to the main JOIN clause.
       
  1213 	 *     @type string $where SQL fragment to append to the main WHERE clause.
       
  1214 	 * }
       
  1215 	 */
       
  1216 	protected function get_sql_clauses() {
       
  1217 		/*
       
  1218 		 * $queries are passed by reference to get_sql_for_query() for recursion.
       
  1219 		 * To keep $this->queries unaltered, pass a copy.
       
  1220 		 */
       
  1221 		$queries = $this->queries;
       
  1222 		$sql = $this->get_sql_for_query( $queries );
       
  1223 
       
  1224 		if ( ! empty( $sql['where'] ) ) {
       
  1225 			$sql['where'] = ' AND ' . $sql['where'];
       
  1226 		}
       
  1227 
       
  1228 		return $sql;
       
  1229 	}
       
  1230 
       
  1231 	/**
       
  1232 	 * Generate SQL clauses for a single query array.
       
  1233 	 *
       
  1234 	 * If nested subqueries are found, this method recurses the tree to
       
  1235 	 * produce the properly nested SQL.
       
  1236 	 *
       
  1237 	 * @since 4.1.0
       
  1238 	 * @access protected
       
  1239 	 *
       
  1240 	 * @param array $query Query to parse, passed by reference.
       
  1241 	 * @param int   $depth Optional. Number of tree levels deep we currently are.
       
  1242 	 *                     Used to calculate indentation. Default 0.
       
  1243 	 * @return array {
       
  1244 	 *     Array containing JOIN and WHERE SQL clauses to append to a single query array.
       
  1245 	 *
       
  1246 	 *     @type string $join  SQL fragment to append to the main JOIN clause.
       
  1247 	 *     @type string $where SQL fragment to append to the main WHERE clause.
       
  1248 	 * }
       
  1249 	 */
       
  1250 	protected function get_sql_for_query( &$query, $depth = 0 ) {
       
  1251 		$sql_chunks = array(
       
  1252 			'join'  => array(),
       
  1253 			'where' => array(),
       
  1254 		);
       
  1255 
       
  1256 		$sql = array(
       
  1257 			'join'  => '',
       
  1258 			'where' => '',
       
  1259 		);
       
  1260 
       
  1261 		$indent = '';
       
  1262 		for ( $i = 0; $i < $depth; $i++ ) {
       
  1263 			$indent .= "  ";
       
  1264 		}
       
  1265 
       
  1266 		foreach ( $query as $key => &$clause ) {
       
  1267 			if ( 'relation' === $key ) {
       
  1268 				$relation = $query['relation'];
       
  1269 			} elseif ( is_array( $clause ) ) {
       
  1270 
       
  1271 				// This is a first-order clause.
       
  1272 				if ( $this->is_first_order_clause( $clause ) ) {
       
  1273 					$clause_sql = $this->get_sql_for_clause( $clause, $query, $key );
       
  1274 
       
  1275 					$where_count = count( $clause_sql['where'] );
       
  1276 					if ( ! $where_count ) {
       
  1277 						$sql_chunks['where'][] = '';
       
  1278 					} elseif ( 1 === $where_count ) {
       
  1279 						$sql_chunks['where'][] = $clause_sql['where'][0];
       
  1280 					} else {
       
  1281 						$sql_chunks['where'][] = '( ' . implode( ' AND ', $clause_sql['where'] ) . ' )';
       
  1282 					}
       
  1283 
       
  1284 					$sql_chunks['join'] = array_merge( $sql_chunks['join'], $clause_sql['join'] );
       
  1285 				// This is a subquery, so we recurse.
       
  1286 				} else {
       
  1287 					$clause_sql = $this->get_sql_for_query( $clause, $depth + 1 );
       
  1288 
       
  1289 					$sql_chunks['where'][] = $clause_sql['where'];
       
  1290 					$sql_chunks['join'][]  = $clause_sql['join'];
       
  1291 				}
       
  1292 			}
       
  1293 		}
       
  1294 
       
  1295 		// Filter to remove empties.
       
  1296 		$sql_chunks['join']  = array_filter( $sql_chunks['join'] );
       
  1297 		$sql_chunks['where'] = array_filter( $sql_chunks['where'] );
       
  1298 
       
  1299 		if ( empty( $relation ) ) {
       
  1300 			$relation = 'AND';
       
  1301 		}
       
  1302 
       
  1303 		// Filter duplicate JOIN clauses and combine into a single string.
       
  1304 		if ( ! empty( $sql_chunks['join'] ) ) {
       
  1305 			$sql['join'] = implode( ' ', array_unique( $sql_chunks['join'] ) );
       
  1306 		}
       
  1307 
       
  1308 		// Generate a single WHERE clause with proper brackets and indentation.
       
  1309 		if ( ! empty( $sql_chunks['where'] ) ) {
       
  1310 			$sql['where'] = '( ' . "\n  " . $indent . implode( ' ' . "\n  " . $indent . $relation . ' ' . "\n  " . $indent, $sql_chunks['where'] ) . "\n" . $indent . ')';
       
  1311 		}
       
  1312 
       
  1313 		return $sql;
       
  1314 	}
       
  1315 
       
  1316 	/**
       
  1317 	 * Generate SQL JOIN and WHERE clauses for a first-order query clause.
       
  1318 	 *
       
  1319 	 * "First-order" means that it's an array with a 'key' or 'value'.
       
  1320 	 *
       
  1321 	 * @since 4.1.0
       
  1322 	 * @access public
       
  1323 	 *
       
  1324 	 * @param array  $clause       Query clause, passed by reference.
       
  1325 	 * @param array  $parent_query Parent query array.
       
  1326 	 * @param string $clause_key   Optional. The array key used to name the clause in the original `$meta_query`
       
  1327 	 *                             parameters. If not provided, a key will be generated automatically.
       
  1328 	 * @return array {
       
  1329 	 *     Array containing JOIN and WHERE SQL clauses to append to a first-order query.
       
  1330 	 *
       
  1331 	 *     @type string $join  SQL fragment to append to the main JOIN clause.
       
  1332 	 *     @type string $where SQL fragment to append to the main WHERE clause.
       
  1333 	 * }
       
  1334 	 */
       
  1335 	public function get_sql_for_clause( &$clause, $parent_query, $clause_key = '' ) {
   732 		global $wpdb;
  1336 		global $wpdb;
   733 
  1337 
   734 		if ( ! $meta_table = _get_meta_table( $type ) )
  1338 		$sql_chunks = array(
   735 			return false;
  1339 			'where' => array(),
   736 
  1340 			'join' => array(),
   737 		$meta_id_column = sanitize_key( $type . '_id' );
  1341 		);
   738 
  1342 
   739 		$join = array();
  1343 		if ( isset( $clause['compare'] ) ) {
   740 		$where = array();
  1344 			$clause['compare'] = strtoupper( $clause['compare'] );
   741 
  1345 		} else {
   742 		$key_only_queries = array();
  1346 			$clause['compare'] = isset( $clause['value'] ) && is_array( $clause['value'] ) ? 'IN' : '=';
   743 		$queries = array();
  1347 		}
   744 
  1348 
   745 		// Split out the queries with empty arrays as value
  1349 		if ( ! in_array( $clause['compare'], array(
   746 		foreach ( $this->queries as $k => $q ) {
  1350 			'=', '!=', '>', '>=', '<', '<=',
   747 			if ( isset( $q['value'] ) && is_array( $q['value'] ) && empty( $q['value'] ) ) {
  1351 			'LIKE', 'NOT LIKE',
   748 				$key_only_queries[$k] = $q;
  1352 			'IN', 'NOT IN',
   749 				unset( $this->queries[$k] );
  1353 			'BETWEEN', 'NOT BETWEEN',
       
  1354 			'EXISTS', 'NOT EXISTS',
       
  1355 			'REGEXP', 'NOT REGEXP', 'RLIKE'
       
  1356 		) ) ) {
       
  1357 			$clause['compare'] = '=';
       
  1358 		}
       
  1359 
       
  1360 		$meta_compare = $clause['compare'];
       
  1361 
       
  1362 		// First build the JOIN clause, if one is required.
       
  1363 		$join = '';
       
  1364 
       
  1365 		// We prefer to avoid joins if possible. Look for an existing join compatible with this clause.
       
  1366 		$alias = $this->find_compatible_table_alias( $clause, $parent_query );
       
  1367 		if ( false === $alias ) {
       
  1368 			$i = count( $this->table_aliases );
       
  1369 			$alias = $i ? 'mt' . $i : $this->meta_table;
       
  1370 
       
  1371 			// JOIN clauses for NOT EXISTS have their own syntax.
       
  1372 			if ( 'NOT EXISTS' === $meta_compare ) {
       
  1373 				$join .= " LEFT JOIN $this->meta_table";
       
  1374 				$join .= $i ? " AS $alias" : '';
       
  1375 				$join .= $wpdb->prepare( " ON ($this->primary_table.$this->primary_id_column = $alias.$this->meta_id_column AND $alias.meta_key = %s )", $clause['key'] );
       
  1376 
       
  1377 			// All other JOIN clauses.
       
  1378 			} else {
       
  1379 				$join .= " INNER JOIN $this->meta_table";
       
  1380 				$join .= $i ? " AS $alias" : '';
       
  1381 				$join .= " ON ( $this->primary_table.$this->primary_id_column = $alias.$this->meta_id_column )";
   750 			}
  1382 			}
   751 		}
  1383 
   752 
  1384 			$this->table_aliases[] = $alias;
   753 		// Split out the meta_key only queries (we can only do this for OR)
  1385 			$sql_chunks['join'][] = $join;
   754 		if ( 'OR' == $this->relation ) {
  1386 		}
   755 			foreach ( $this->queries as $k => $q ) {
  1387 
   756 				if ( ! isset( $q['value'] ) && ! empty( $q['key'] ) )
  1388 		// Save the alias to this clause, for future siblings to find.
   757 					$key_only_queries[$k] = $q;
  1389 		$clause['alias'] = $alias;
   758 				else
  1390 
   759 					$queries[$k] = $q;
  1391 		// Determine the data type.
       
  1392 		$_meta_type = isset( $clause['type'] ) ? $clause['type'] : '';
       
  1393 		$meta_type  = $this->get_cast_for_type( $_meta_type );
       
  1394 		$clause['cast'] = $meta_type;
       
  1395 
       
  1396 		// Fallback for clause keys is the table alias.
       
  1397 		if ( ! $clause_key ) {
       
  1398 			$clause_key = $clause['alias'];
       
  1399 		}
       
  1400 
       
  1401 		// Ensure unique clause keys, so none are overwritten.
       
  1402 		$iterator = 1;
       
  1403 		$clause_key_base = $clause_key;
       
  1404 		while ( isset( $this->clauses[ $clause_key ] ) ) {
       
  1405 			$clause_key = $clause_key_base . '-' . $iterator;
       
  1406 			$iterator++;
       
  1407 		}
       
  1408 
       
  1409 		// Store the clause in our flat array.
       
  1410 		$this->clauses[ $clause_key ] =& $clause;
       
  1411 
       
  1412 		// Next, build the WHERE clause.
       
  1413 
       
  1414 		// meta_key.
       
  1415 		if ( array_key_exists( 'key', $clause ) ) {
       
  1416 			if ( 'NOT EXISTS' === $meta_compare ) {
       
  1417 				$sql_chunks['where'][] = $alias . '.' . $this->meta_id_column . ' IS NULL';
       
  1418 			} else {
       
  1419 				$sql_chunks['where'][] = $wpdb->prepare( "$alias.meta_key = %s", trim( $clause['key'] ) );
   760 			}
  1420 			}
   761 		} else {
  1421 		}
   762 			$queries = $this->queries;
  1422 
   763 		}
  1423 		// meta_value.
   764 
  1424 		if ( array_key_exists( 'value', $clause ) ) {
   765 		// Specify all the meta_key only queries in one go
  1425 			$meta_value = $clause['value'];
   766 		if ( $key_only_queries ) {
       
   767 			$join[]  = "INNER JOIN $meta_table ON $primary_table.$primary_id_column = $meta_table.$meta_id_column";
       
   768 
       
   769 			foreach ( $key_only_queries as $key => $q )
       
   770 				$where["key-only-$key"] = $wpdb->prepare( "$meta_table.meta_key = %s", trim( $q['key'] ) );
       
   771 		}
       
   772 
       
   773 		foreach ( $queries as $k => $q ) {
       
   774 			$meta_key = isset( $q['key'] ) ? trim( $q['key'] ) : '';
       
   775 			$meta_type = $this->get_cast_for_type( isset( $q['type'] ) ? $q['type'] : '' );
       
   776 
       
   777 			$meta_value = isset( $q['value'] ) ? $q['value'] : null;
       
   778 
       
   779 			if ( isset( $q['compare'] ) )
       
   780 				$meta_compare = strtoupper( $q['compare'] );
       
   781 			else
       
   782 				$meta_compare = is_array( $meta_value ) ? 'IN' : '=';
       
   783 
       
   784 			if ( ! in_array( $meta_compare, array(
       
   785 				'=', '!=', '>', '>=', '<', '<=',
       
   786 				'LIKE', 'NOT LIKE',
       
   787 				'IN', 'NOT IN',
       
   788 				'BETWEEN', 'NOT BETWEEN',
       
   789 				'NOT EXISTS',
       
   790 				'REGEXP', 'NOT REGEXP', 'RLIKE'
       
   791 			) ) )
       
   792 				$meta_compare = '=';
       
   793 
       
   794 			$i = count( $join );
       
   795 			$alias = $i ? 'mt' . $i : $meta_table;
       
   796 
       
   797 			if ( 'NOT EXISTS' == $meta_compare ) {
       
   798 				$join[$i]  = "LEFT JOIN $meta_table";
       
   799 				$join[$i] .= $i ? " AS $alias" : '';
       
   800 				$join[$i] .= " ON ($primary_table.$primary_id_column = $alias.$meta_id_column AND $alias.meta_key = '$meta_key')";
       
   801 
       
   802 				$where[$k] = ' ' . $alias . '.' . $meta_id_column . ' IS NULL';
       
   803 
       
   804 				continue;
       
   805 			}
       
   806 
       
   807 			$join[$i]  = "INNER JOIN $meta_table";
       
   808 			$join[$i] .= $i ? " AS $alias" : '';
       
   809 			$join[$i] .= " ON ($primary_table.$primary_id_column = $alias.$meta_id_column)";
       
   810 
       
   811 			$where[$k] = '';
       
   812 			if ( !empty( $meta_key ) )
       
   813 				$where[$k] = $wpdb->prepare( "$alias.meta_key = %s", $meta_key );
       
   814 
       
   815 			if ( is_null( $meta_value ) ) {
       
   816 				if ( empty( $where[$k] ) )
       
   817 					unset( $join[$i] );
       
   818 				continue;
       
   819 			}
       
   820 
  1426 
   821 			if ( in_array( $meta_compare, array( 'IN', 'NOT IN', 'BETWEEN', 'NOT BETWEEN' ) ) ) {
  1427 			if ( in_array( $meta_compare, array( 'IN', 'NOT IN', 'BETWEEN', 'NOT BETWEEN' ) ) ) {
   822 				if ( ! is_array( $meta_value ) )
  1428 				if ( ! is_array( $meta_value ) ) {
   823 					$meta_value = preg_split( '/[,\s]+/', $meta_value );
  1429 					$meta_value = preg_split( '/[,\s]+/', $meta_value );
   824 
       
   825 				if ( empty( $meta_value ) ) {
       
   826 					unset( $join[$i] );
       
   827 					continue;
       
   828 				}
  1430 				}
   829 			} else {
  1431 			} else {
   830 				$meta_value = trim( $meta_value );
  1432 				$meta_value = trim( $meta_value );
   831 			}
  1433 			}
   832 
  1434 
   833 			if ( 'IN' == substr( $meta_compare, -2) ) {
  1435 			switch ( $meta_compare ) {
   834 				$meta_compare_string = '(' . substr( str_repeat( ',%s', count( $meta_value ) ), 1 ) . ')';
  1436 				case 'IN' :
   835 			} elseif ( 'BETWEEN' == substr( $meta_compare, -7) ) {
  1437 				case 'NOT IN' :
   836 				$meta_value = array_slice( $meta_value, 0, 2 );
  1438 					$meta_compare_string = '(' . substr( str_repeat( ',%s', count( $meta_value ) ), 1 ) . ')';
   837 				$meta_compare_string = '%s AND %s';
  1439 					$where = $wpdb->prepare( $meta_compare_string, $meta_value );
   838 			} elseif ( 'LIKE' == substr( $meta_compare, -4 ) ) {
  1440 					break;
   839 				$meta_value = '%' . like_escape( $meta_value ) . '%';
  1441 
   840 				$meta_compare_string = '%s';
  1442 				case 'BETWEEN' :
   841 			} else {
  1443 				case 'NOT BETWEEN' :
   842 				$meta_compare_string = '%s';
  1444 					$meta_value = array_slice( $meta_value, 0, 2 );
       
  1445 					$where = $wpdb->prepare( '%s AND %s', $meta_value );
       
  1446 					break;
       
  1447 
       
  1448 				case 'LIKE' :
       
  1449 				case 'NOT LIKE' :
       
  1450 					$meta_value = '%' . $wpdb->esc_like( $meta_value ) . '%';
       
  1451 					$where = $wpdb->prepare( '%s', $meta_value );
       
  1452 					break;
       
  1453 
       
  1454 				// EXISTS with a value is interpreted as '='.
       
  1455 				case 'EXISTS' :
       
  1456 					$meta_compare = '=';
       
  1457 					$where = $wpdb->prepare( '%s', $meta_value );
       
  1458 					break;
       
  1459 
       
  1460 				// 'value' is ignored for NOT EXISTS.
       
  1461 				case 'NOT EXISTS' :
       
  1462 					$where = '';
       
  1463 					break;
       
  1464 
       
  1465 				default :
       
  1466 					$where = $wpdb->prepare( '%s', $meta_value );
       
  1467 					break;
       
  1468 
   843 			}
  1469 			}
   844 
  1470 
   845 			if ( ! empty( $where[$k] ) )
  1471 			if ( $where ) {
   846 				$where[$k] .= ' AND ';
  1472 				$sql_chunks['where'][] = "CAST($alias.meta_value AS {$meta_type}) {$meta_compare} {$where}";
   847 
  1473 			}
   848 			$where[$k] = ' (' . $where[$k] . $wpdb->prepare( "CAST($alias.meta_value AS {$meta_type}) {$meta_compare} {$meta_compare_string})", $meta_value );
  1474 		}
   849 		}
  1475 
   850 
  1476 		/*
   851 		$where = array_filter( $where );
  1477 		 * Multiple WHERE clauses (for meta_key and meta_value) should
   852 
  1478 		 * be joined in parentheses.
   853 		if ( empty( $where ) )
  1479 		 */
   854 			$where = '';
  1480 		if ( 1 < count( $sql_chunks['where'] ) ) {
   855 		else
  1481 			$sql_chunks['where'] = array( '( ' . implode( ' AND ', $sql_chunks['where'] ) . ' )' );
   856 			$where = ' AND (' . implode( "\n{$this->relation} ", $where ) . ' )';
  1482 		}
   857 
  1483 
   858 		$join = implode( "\n", $join );
  1484 		return $sql_chunks;
   859 		if ( ! empty( $join ) )
  1485 	}
   860 			$join = ' ' . $join;
  1486 
   861 
  1487 	/**
   862 		return apply_filters_ref_array( 'get_meta_sql', array( compact( 'join', 'where' ), $this->queries, $type, $primary_table, $primary_id_column, $context ) );
  1488 	 * Get a flattened list of sanitized meta clauses.
       
  1489 	 *
       
  1490 	 * This array should be used for clause lookup, as when the table alias and CAST type must be determined for
       
  1491 	 * a value of 'orderby' corresponding to a meta clause.
       
  1492 	 *
       
  1493 	 * @since 4.2.0
       
  1494 	 * @access public
       
  1495 	 *
       
  1496 	 * @return array Meta clauses.
       
  1497 	 */
       
  1498 	public function get_clauses() {
       
  1499 		return $this->clauses;
       
  1500 	}
       
  1501 
       
  1502 	/**
       
  1503 	 * Identify an existing table alias that is compatible with the current
       
  1504 	 * query clause.
       
  1505 	 *
       
  1506 	 * We avoid unnecessary table joins by allowing each clause to look for
       
  1507 	 * an existing table alias that is compatible with the query that it
       
  1508 	 * needs to perform.
       
  1509 	 *
       
  1510 	 * An existing alias is compatible if (a) it is a sibling of `$clause`
       
  1511 	 * (ie, it's under the scope of the same relation), and (b) the combination
       
  1512 	 * of operator and relation between the clauses allows for a shared table join.
       
  1513 	 * In the case of {@see WP_Meta_Query}, this only applies to 'IN' clauses that
       
  1514 	 * are connected by the relation 'OR'.
       
  1515 	 *
       
  1516 	 * @since 4.1.0
       
  1517 	 * @access protected
       
  1518 	 *
       
  1519 	 * @param  array       $clause       Query clause.
       
  1520 	 * @param  array       $parent_query Parent query of $clause.
       
  1521 	 * @return string|bool Table alias if found, otherwise false.
       
  1522 	 */
       
  1523 	protected function find_compatible_table_alias( $clause, $parent_query ) {
       
  1524 		$alias = false;
       
  1525 
       
  1526 		foreach ( $parent_query as $sibling ) {
       
  1527 			// If the sibling has no alias yet, there's nothing to check.
       
  1528 			if ( empty( $sibling['alias'] ) ) {
       
  1529 				continue;
       
  1530 			}
       
  1531 
       
  1532 			// We're only interested in siblings that are first-order clauses.
       
  1533 			if ( ! is_array( $sibling ) || ! $this->is_first_order_clause( $sibling ) ) {
       
  1534 				continue;
       
  1535 			}
       
  1536 
       
  1537 			$compatible_compares = array();
       
  1538 
       
  1539 			// Clauses connected by OR can share joins as long as they have "positive" operators.
       
  1540 			if ( 'OR' === $parent_query['relation'] ) {
       
  1541 				$compatible_compares = array( '=', 'IN', 'BETWEEN', 'LIKE', 'REGEXP', 'RLIKE', '>', '>=', '<', '<=' );
       
  1542 
       
  1543 			// Clauses joined by AND with "negative" operators share a join only if they also share a key.
       
  1544 			} elseif ( isset( $sibling['key'] ) && isset( $clause['key'] ) && $sibling['key'] === $clause['key'] ) {
       
  1545 				$compatible_compares = array( '!=', 'NOT IN', 'NOT LIKE' );
       
  1546 			}
       
  1547 
       
  1548 			$clause_compare  = strtoupper( $clause['compare'] );
       
  1549 			$sibling_compare = strtoupper( $sibling['compare'] );
       
  1550 			if ( in_array( $clause_compare, $compatible_compares ) && in_array( $sibling_compare, $compatible_compares ) ) {
       
  1551 				$alias = $sibling['alias'];
       
  1552 				break;
       
  1553 			}
       
  1554 		}
       
  1555 
       
  1556 		/**
       
  1557 		 * Filter the table alias identified as compatible with the current clause.
       
  1558 		 *
       
  1559 		 * @since 4.1.0
       
  1560 		 *
       
  1561 		 * @param string|bool $alias        Table alias, or false if none was found.
       
  1562 		 * @param array       $clause       First-order query clause.
       
  1563 		 * @param array       $parent_query Parent of $clause.
       
  1564 		 * @param object      $this         WP_Meta_Query object.
       
  1565 		 */
       
  1566 		return apply_filters( 'meta_query_find_compatible_table_alias', $alias, $clause, $parent_query, $this ) ;
   863 	}
  1567 	}
   864 }
  1568 }
   865 
  1569 
   866 /**
  1570 /**
   867  * Retrieve the name of the metadata table for the specified object type.
  1571  * Retrieve the name of the metadata table for the specified object type.
   868  *
  1572  *
   869  * @since 2.9.0
  1573  * @since 2.9.0
   870  * @uses $wpdb WordPress database object for queries.
  1574  *
       
  1575  * @global wpdb $wpdb WordPress database abstraction object.
   871  *
  1576  *
   872  * @param string $type Type of object to get metadata table for (e.g., comment, post, or user)
  1577  * @param string $type Type of object to get metadata table for (e.g., comment, post, or user)
   873  * @return mixed Metadata table name, or false if no metadata table exists
  1578  * @return mixed Metadata table name, or false if no metadata table exists
   874  */
  1579  */
   875 function _get_meta_table($type) {
  1580 function _get_meta_table($type) {
   882 
  1587 
   883 	return $wpdb->$table_name;
  1588 	return $wpdb->$table_name;
   884 }
  1589 }
   885 
  1590 
   886 /**
  1591 /**
   887  * Determine whether a meta key is protected
  1592  * Determine whether a meta key is protected.
   888  *
  1593  *
   889  * @since 3.1.3
  1594  * @since 3.1.3
   890  *
  1595  *
   891  * @param string $meta_key Meta key
  1596  * @param string      $meta_key Meta key
       
  1597  * @param string|null $meta_type
   892  * @return bool True if the key is protected, false otherwise.
  1598  * @return bool True if the key is protected, false otherwise.
   893  */
  1599  */
   894 function is_protected_meta( $meta_key, $meta_type = null ) {
  1600 function is_protected_meta( $meta_key, $meta_type = null ) {
   895 	$protected = ( '_' == $meta_key[0] );
  1601 	$protected = ( '_' == $meta_key[0] );
   896 
  1602 
       
  1603 	/**
       
  1604 	 * Filter whether a meta key is protected.
       
  1605 	 *
       
  1606 	 * @since 3.2.0
       
  1607 	 *
       
  1608 	 * @param bool   $protected Whether the key is protected. Default false.
       
  1609 	 * @param string $meta_key  Meta key.
       
  1610 	 * @param string $meta_type Meta type.
       
  1611 	 */
   897 	return apply_filters( 'is_protected_meta', $protected, $meta_key, $meta_type );
  1612 	return apply_filters( 'is_protected_meta', $protected, $meta_key, $meta_type );
   898 }
  1613 }
   899 
  1614 
   900 /**
  1615 /**
   901  * Sanitize meta value
  1616  * Sanitize meta value.
   902  *
  1617  *
   903  * @since 3.1.3
  1618  * @since 3.1.3
   904  *
  1619  *
   905  * @param string $meta_key Meta key
  1620  * @param string $meta_key Meta key
   906  * @param mixed $meta_value Meta value to sanitize
  1621  * @param mixed $meta_value Meta value to sanitize
   907  * @param string $meta_type Type of meta
  1622  * @param string $meta_type Type of meta
   908  * @return mixed Sanitized $meta_value
  1623  * @return mixed Sanitized $meta_value
   909  */
  1624  */
   910 function sanitize_meta( $meta_key, $meta_value, $meta_type ) {
  1625 function sanitize_meta( $meta_key, $meta_value, $meta_type ) {
       
  1626 
       
  1627 	/**
       
  1628 	 * Filter the sanitization of a specific meta key of a specific meta type.
       
  1629 	 *
       
  1630 	 * The dynamic portions of the hook name, `$meta_type`, and `$meta_key`,
       
  1631 	 * refer to the metadata object type (comment, post, or user) and the meta
       
  1632 	 * key value,
       
  1633 	 * respectively.
       
  1634 	 *
       
  1635 	 * @since 3.3.0
       
  1636 	 *
       
  1637 	 * @param mixed  $meta_value Meta value to sanitize.
       
  1638 	 * @param string $meta_key   Meta key.
       
  1639 	 * @param string $meta_type  Meta type.
       
  1640 	 */
   911 	return apply_filters( "sanitize_{$meta_type}_meta_{$meta_key}", $meta_value, $meta_key, $meta_type );
  1641 	return apply_filters( "sanitize_{$meta_type}_meta_{$meta_key}", $meta_value, $meta_key, $meta_type );
   912 }
  1642 }
   913 
  1643 
   914 /**
  1644 /**
   915  * Register meta key
  1645  * Register meta key
   918  *
  1648  *
   919  * @param string $meta_type Type of meta
  1649  * @param string $meta_type Type of meta
   920  * @param string $meta_key Meta key
  1650  * @param string $meta_key Meta key
   921  * @param string|array $sanitize_callback A function or method to call when sanitizing the value of $meta_key.
  1651  * @param string|array $sanitize_callback A function or method to call when sanitizing the value of $meta_key.
   922  * @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.
  1652  * @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.
   923  * @param array $args Arguments
       
   924  */
  1653  */
   925 function register_meta( $meta_type, $meta_key, $sanitize_callback, $auth_callback = null ) {
  1654 function register_meta( $meta_type, $meta_key, $sanitize_callback, $auth_callback = null ) {
   926 	if ( is_callable( $sanitize_callback ) )
  1655 	if ( is_callable( $sanitize_callback ) )
   927 		add_filter( "sanitize_{$meta_type}_meta_{$meta_key}", $sanitize_callback, 10, 3 );
  1656 		add_filter( "sanitize_{$meta_type}_meta_{$meta_key}", $sanitize_callback, 10, 3 );
   928 
  1657