Make WordPress Core


Ignore:
Timestamp:
06/10/2020 02:20:18 AM (6 years ago)
Author:
TimothyBlynJacobs
Message:

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.

File:
1 edited

Legend:

Unmodified
Added
Removed
  • trunk/src/wp-includes/rest-api/fields/class-wp-rest-meta-fields.php

    r47858 r47943  
    270270                }
    271271
    272                 $current = get_metadata( $meta_type, $object_id, $meta_key, false );
    273 
    274                 $to_remove = $current;
     272                $current_values = get_metadata( $meta_type, $object_id, $meta_key, false );
     273                $subtype        = get_object_subtype( $meta_type, $object_id );
     274
     275                $to_remove = $current_values;
    275276                $to_add    = $values;
    276277
    277278                foreach ( $to_add as $add_key => $value ) {
    278                         $remove_keys = array_keys( $to_remove, $value, true );
     279                        $remove_keys = array_keys(
     280                                array_filter(
     281                                        $current_values,
     282                                        function ( $stored_value ) use ( $meta_key, $subtype, $value ) {
     283                                                return $this->is_meta_value_same_as_stored_value( $meta_key, $subtype, $stored_value, $value );
     284                                        }
     285                                )
     286                        );
    279287
    280288                        if ( empty( $remove_keys ) ) {
     
    360368                $old_value = get_metadata( $meta_type, $object_id, $meta_key );
    361369                $subtype   = get_object_subtype( $meta_type, $object_id );
    362                 $args      = $this->get_registered_fields()[ $meta_key ];
    363 
    364                 if ( 1 === count( $old_value ) ) {
    365                         $sanitized = sanitize_meta( $meta_key, $value, $meta_type, $subtype );
    366 
    367                         if ( in_array( $args['type'], array( 'string', 'number', 'integer', 'boolean' ), true ) ) {
    368                                 // The return value of get_metadata will always be a string for scalar types.
    369                                 $sanitized = (string) $sanitized;
    370                         }
    371 
    372                         if ( $sanitized === $old_value[0] ) {
    373                                 return true;
    374                         }
     370
     371                if ( 1 === count( $old_value ) && $this->is_meta_value_same_as_stored_value( $meta_key, $subtype, $old_value[0], $value ) ) {
     372                        return true;
    375373                }
    376374
     
    388386
    389387                return true;
     388        }
     389
     390        /**
     391         * Checks if the user provided value is equivalent to a stored value for the given meta key.
     392         *
     393         * @since 5.5.0
     394         *
     395         * @param string $meta_key     The meta key being checked.
     396         * @param string $subtype      The object subtype.
     397         * @param mixed  $stored_value The currently stored value retrieved from get_metadata().
     398         * @param mixed  $user_value   The value provided by the user.
     399         * @return bool
     400         */
     401        protected function is_meta_value_same_as_stored_value( $meta_key, $subtype, $stored_value, $user_value ) {
     402                $args      = $this->get_registered_fields()[ $meta_key ];
     403                $sanitized = sanitize_meta( $meta_key, $user_value, $this->get_meta_type(), $subtype );
     404
     405                if ( in_array( $args['type'], array( 'string', 'number', 'integer', 'boolean' ), true ) ) {
     406                        // The return value of get_metadata will always be a string for scalar types.
     407                        $sanitized = (string) $sanitized;
     408                }
     409
     410                return $sanitized === $stored_value;
    390411        }
    391412
Note: See TracChangeset for help on using the changeset viewer.