From 84dfea907f97e0066cd7fa0f376e4e1cecc7fc0a Mon Sep 17 00:00:00 2001 From: "K. Adam White" Date: Thu, 19 Sep 2019 14:35:47 +0000 Subject: [PATCH] REST API: Issue warning if array meta is registered without item schema. The purpose of meta registration is to assert that the meta key will contain a predictable value conforming to a schema, so the schema is therefore considered to be required. Props TimothyBlynJacobs, grapplerulrich. Fixes #43392. git-svn-id: https://develop.svn.wordpress.org/trunk@46186 602fd350-edb4-49c9-b593-d223f7449a82 --- src/wp-includes/meta.php | 35 +++++++---- .../tests/rest-api/rest-post-meta-fields.php | 60 +++++++++++++++++++ 2 files changed, 84 insertions(+), 11 deletions(-) diff --git a/src/wp-includes/meta.php b/src/wp-includes/meta.php index 3966a23469..bc6b05d953 100644 --- a/src/wp-includes/meta.php +++ b/src/wp-includes/meta.php @@ -1115,23 +1115,27 @@ function sanitize_meta( $meta_key, $meta_value, $object_type, $object_subtype = * to support an array of data to attach to registered meta keys}. Previous arguments for * `$sanitize_callback` and `$auth_callback` have been folded into this array. * @since 4.9.8 The `$object_subtype` argument was added to the arguments array. + * @since 5.3.0 Valid meta types expanded to include "array" and "object". * * @param string $object_type Type of object this meta is registered to. * @param string $meta_key Meta key to register. * @param array $args { * Data used to describe the meta key when registered. * - * @type string $object_subtype A subtype; e.g. if the object type is "post", the post type. If left empty, - * the meta key will be registered on the entire object type. Default empty. - * @type string $type The type of data associated with this meta key. - * Valid values are 'string', 'boolean', 'integer', and 'number'. - * @type string $description A description of the data attached to this meta key. - * @type bool $single Whether the meta key has one value per object, or an array of values per object. - * @type string $sanitize_callback A function or method to call when sanitizing `$meta_key` data. - * @type string $auth_callback Optional. A function or method to call when performing edit_post_meta, add_post_meta, and delete_post_meta capability checks. - * @type bool $show_in_rest Whether data associated with this meta key can be considered public and - * should be accessible via the REST API. A custom post type must also declare - * support for custom fields for registered meta to be accessible via REST. + * @type string $object_subtype A subtype; e.g. if the object type is "post", the post type. If left empty, + * the meta key will be registered on the entire object type. Default empty. + * @type string $type The type of data associated with this meta key. + * Valid values are 'string', 'boolean', 'integer', 'number', 'array', and 'object'. + * @type string $description A description of the data attached to this meta key. + * @type bool $single Whether the meta key has one value per object, or an array of values per object. + * @type string $sanitize_callback A function or method to call when sanitizing `$meta_key` data. + * @type string $auth_callback Optional. A function or method to call when performing edit_post_meta, + * add_post_meta, and delete_post_meta capability checks. + * @type bool|array $show_in_rest Whether data associated with this meta key can be considered public and + * should be accessible via the REST API. A custom post type must also declare + * support for custom fields for registered meta to be accessible via REST. + * When registering complex meta values this argument may optionally be an + * array with 'schema' or 'prepare_callback' keys instead of a boolean. * } * @param string|array $deprecated Deprecated. Use `$args` instead. * @@ -1188,6 +1192,15 @@ function register_meta( $object_type, $meta_key, $args, $deprecated = null ) { $args = apply_filters( 'register_meta_args', $args, $defaults, $object_type, $meta_key ); $args = wp_parse_args( $args, $defaults ); + // Require an item schema when registering array meta. + if ( false !== $args['show_in_rest'] && 'array' === $args['type'] ) { + if ( ! is_array( $args['show_in_rest'] ) || ! isset( $args['show_in_rest']['schema']['items'] ) ) { + _doing_it_wrong( __FUNCTION__, __( 'When registering an "array" meta type to show in the REST API, you must specify the schema for each array item in "show_in_rest.schema.items".' ), '5.3.0' ); + + return false; + } + } + $object_subtype = ! empty( $args['object_subtype'] ) ? $args['object_subtype'] : ''; // If `auth_callback` is not provided, fall back to `is_protected_meta()`. diff --git a/tests/phpunit/tests/rest-api/rest-post-meta-fields.php b/tests/phpunit/tests/rest-api/rest-post-meta-fields.php index 1db1798fd6..2007e77ef2 100644 --- a/tests/phpunit/tests/rest-api/rest-post-meta-fields.php +++ b/tests/phpunit/tests/rest-api/rest-post-meta-fields.php @@ -2204,6 +2204,66 @@ class WP_Test_REST_Post_Meta_Fields extends WP_Test_REST_TestCase { $this->assertEquals( array( 'project' => 'WordCamp' ), $data['meta']['object'] ); } + /** + * @ticket 43392 + */ + public function test_register_meta_issues_doing_it_wrong_when_show_in_rest_is_true() { + $this->setExpectedIncorrectUsage( 'register_meta' ); + + $registered = register_meta( + 'post', + 'invalid_array', + array( + 'type' => 'array', + 'show_in_rest' => true, + ) + ); + + self::assertFalse( $registered ); + } + + /** + * @ticket 43392 + */ + public function test_register_meta_issues_doing_it_wrong_when_show_in_rest_omits_schema() { + $this->setExpectedIncorrectUsage( 'register_meta' ); + + $registered = register_meta( + 'post', + 'invalid_array', + array( + 'type' => 'array', + 'show_in_rest' => array( + 'prepare_callback' => 'rest_sanitize_value_from_schema', + ), + ) + ); + + self::assertFalse( $registered ); + } + + /** + * @ticket 43392 + */ + public function test_register_meta_issues_doing_it_wrong_when_show_in_rest_omits_schema_items() { + $this->setExpectedIncorrectUsage( 'register_meta' ); + + $registered = register_meta( + 'post', + 'invalid_array', + array( + 'type' => 'array', + 'show_in_rest' => array( + 'schema' => array( + 'default' => array( 'Hi!' ), + ), + ), + ) + ); + + self::assertFalse( $registered ); + } + /** * Internal function used to disable an insert query which * will trigger a wpdb error for testing purposes.