REST API: Fix updating "multiple" meta keys with non-string values.

Previously, the REST API would end up deleting each row of metadata and recreating it unnecessarily. This was caused by a type mismatch where the metadata API would always return a string value, and the REST API operated on a typed value.

The REST API now applies the same sanitization and type casting for "multiple" meta keys and "single" meta keys.

Fixes #49339.
Props renathoc.

Built from https://develop.svn.wordpress.org/trunk@47943


git-svn-id: http://core.svn.wordpress.org/trunk@47716 1a063a9b-81f0-0310-95a4-ce76da25c4cd
This commit is contained in:
TimothyBlynJacobs 2020-06-10 02:22:13 +00:00
parent 38352d2c7a
commit 941a31318c
2 changed files with 37 additions and 16 deletions

View File

@ -269,13 +269,21 @@ abstract class WP_REST_Meta_Fields {
); );
} }
$current = get_metadata( $meta_type, $object_id, $meta_key, false ); $current_values = get_metadata( $meta_type, $object_id, $meta_key, false );
$subtype = get_object_subtype( $meta_type, $object_id );
$to_remove = $current; $to_remove = $current_values;
$to_add = $values; $to_add = $values;
foreach ( $to_add as $add_key => $value ) { foreach ( $to_add as $add_key => $value ) {
$remove_keys = array_keys( $to_remove, $value, true ); $remove_keys = array_keys(
array_filter(
$current_values,
function ( $stored_value ) use ( $meta_key, $subtype, $value ) {
return $this->is_meta_value_same_as_stored_value( $meta_key, $subtype, $stored_value, $value );
}
)
);
if ( empty( $remove_keys ) ) { if ( empty( $remove_keys ) ) {
continue; continue;
@ -359,20 +367,10 @@ abstract class WP_REST_Meta_Fields {
// Do the exact same check for a duplicate value as in update_metadata() to avoid update_metadata() returning false. // Do the exact same check for a duplicate value as in update_metadata() to avoid update_metadata() returning false.
$old_value = get_metadata( $meta_type, $object_id, $meta_key ); $old_value = get_metadata( $meta_type, $object_id, $meta_key );
$subtype = get_object_subtype( $meta_type, $object_id ); $subtype = get_object_subtype( $meta_type, $object_id );
$args = $this->get_registered_fields()[ $meta_key ];
if ( 1 === count( $old_value ) ) { if ( 1 === count( $old_value ) && $this->is_meta_value_same_as_stored_value( $meta_key, $subtype, $old_value[0], $value ) ) {
$sanitized = sanitize_meta( $meta_key, $value, $meta_type, $subtype );
if ( in_array( $args['type'], array( 'string', 'number', 'integer', 'boolean' ), true ) ) {
// The return value of get_metadata will always be a string for scalar types.
$sanitized = (string) $sanitized;
}
if ( $sanitized === $old_value[0] ) {
return true; return true;
} }
}
if ( ! update_metadata( $meta_type, $object_id, wp_slash( $meta_key ), wp_slash_strings_only( $value ) ) ) { if ( ! update_metadata( $meta_type, $object_id, wp_slash( $meta_key ), wp_slash_strings_only( $value ) ) ) {
return new WP_Error( return new WP_Error(
@ -389,6 +387,29 @@ abstract class WP_REST_Meta_Fields {
return true; return true;
} }
/**
* Checks if the user provided value is equivalent to a stored value for the given meta key.
*
* @since 5.5.0
*
* @param string $meta_key The meta key being checked.
* @param string $subtype The object subtype.
* @param mixed $stored_value The currently stored value retrieved from get_metadata().
* @param mixed $user_value The value provided by the user.
* @return bool
*/
protected function is_meta_value_same_as_stored_value( $meta_key, $subtype, $stored_value, $user_value ) {
$args = $this->get_registered_fields()[ $meta_key ];
$sanitized = sanitize_meta( $meta_key, $user_value, $this->get_meta_type(), $subtype );
if ( in_array( $args['type'], array( 'string', 'number', 'integer', 'boolean' ), true ) ) {
// The return value of get_metadata will always be a string for scalar types.
$sanitized = (string) $sanitized;
}
return $sanitized === $stored_value;
}
/** /**
* Retrieves all the registered meta fields. * Retrieves all the registered meta fields.
* *

View File

@ -13,7 +13,7 @@
* *
* @global string $wp_version * @global string $wp_version
*/ */
$wp_version = '5.5-alpha-47942'; $wp_version = '5.5-alpha-47943';
/** /**
* Holds the WordPress DB revision, increments when changes are made to the WordPress DB schema. * Holds the WordPress DB revision, increments when changes are made to the WordPress DB schema.