Make WordPress Core

Ticket #43392: 43392.4.diff

File 43392.4.diff, 24.3 KB (added by TimothyBlynJacobs, 5 years ago)
  • src/wp-includes/rest-api.php

    diff --git a/src/wp-includes/rest-api.php b/src/wp-includes/rest-api.php
    index adcca17d3b..b854d7d4b6 100644
    a b function rest_validate_value_from_schema( $value, $args, $param = '' ) { 
    11581158                if ( $value instanceof stdClass ) {
    11591159                        $value = (array) $value;
    11601160                }
     1161
     1162                if ( $value instanceof JsonSerializable ) {
     1163                    $value = $value->jsonSerialize();
     1164        }
     1165
    11611166                if ( ! is_array( $value ) ) {
    11621167                        /* translators: 1: parameter, 2: type name */
    11631168                        return new WP_Error( 'rest_invalid_param', sprintf( __( '%1$s is not of type %2$s.' ), $param, 'object' ) );
    function rest_validate_value_from_schema( $value, $args, $param = '' ) { 
    11691174                                if ( is_wp_error( $is_valid ) ) {
    11701175                                        return $is_valid;
    11711176                                }
    1172                         } elseif ( isset( $args['additionalProperties'] ) && false === $args['additionalProperties'] ) {
    1173                                 return new WP_Error( 'rest_invalid_param', sprintf( __( '%1$s is not a valid property of Object.' ), $property ) );
     1177                        } elseif ( isset( $args['additionalProperties'] ) ) {
     1178                            if ( false === $args['additionalProperties'] ) {
     1179                                    return new WP_Error( 'rest_invalid_param', sprintf( __( '%1$s is not a valid property of Object.' ), $property ) );
     1180                            }
     1181
     1182                            if ( is_array( $args['additionalProperties'] ) ) {
     1183                                    $is_valid = rest_validate_value_from_schema( $v, $args['additionalProperties'], $param . '[' . $property . ']' );
     1184                                    if ( is_wp_error( $is_valid ) ) {
     1185                                        return $is_valid;
     1186                    }
     1187                }
    11741188                        }
    11751189                }
    11761190        }
    function rest_sanitize_value_from_schema( $value, $args ) { 
    12971311                if ( $value instanceof stdClass ) {
    12981312                        $value = (array) $value;
    12991313                }
     1314
     1315                if ( $value instanceof JsonSerializable ) {
     1316                    $value = $value->jsonSerialize();
     1317        }
     1318
    13001319                if ( ! is_array( $value ) ) {
    13011320                        return array();
    13021321                }
    function rest_sanitize_value_from_schema( $value, $args ) { 
    13041323                foreach ( $value as $property => $v ) {
    13051324                        if ( isset( $args['properties'][ $property ] ) ) {
    13061325                                $value[ $property ] = rest_sanitize_value_from_schema( $v, $args['properties'][ $property ] );
    1307                         } elseif ( isset( $args['additionalProperties'] ) && false === $args['additionalProperties'] ) {
    1308                                 unset( $value[ $property ] );
     1326                        } elseif ( isset( $args['additionalProperties'] ) ) {
     1327                            if ( false === $args['additionalProperties'] ) {
     1328                                    unset( $value[ $property ] );
     1329                            } elseif ( is_array( $args['additionalProperties'] ) ) {
     1330                                $value[ $property ] = rest_sanitize_value_from_schema( $v, $args['additionalProperties'] );
     1331                }
    13091332                        }
    13101333                }
    13111334
  • src/wp-includes/rest-api/fields/class-wp-rest-meta-fields.php

    diff --git a/src/wp-includes/rest-api/fields/class-wp-rest-meta-fields.php b/src/wp-includes/rest-api/fields/class-wp-rest-meta-fields.php
    index a24a5f649c..51410a3f00 100644
    a b abstract class WP_REST_Meta_Fields { 
    143143                         * from the database and then relying on the default value.
    144144                         */
    145145                        if ( is_null( $meta[ $name ] ) ) {
     146                                $args = $this->get_registered_fields()[ $meta_key ];
     147
     148                                if ( $args['single'] ) {
     149                                        $current = get_metadata( $this->get_meta_type(), $object_id, $meta_key, true );
     150
     151                                        if ( is_wp_error( rest_validate_value_from_schema( $current, $args['schema'] ) ) ) {
     152                                                return new WP_Error(
     153                                                        'rest_invalid_stored_value',
     154                                                        sprintf( __( 'The %s property has an invalid stored value, and cannot be updated to null.' ), $name ),
     155                                                        array( 'status' => 500 )
     156                                                );
     157                                        }
     158                                }
     159
    146160                                $result = $this->delete_meta_value( $object_id, $meta_key, $name );
    147161                                if ( is_wp_error( $result ) ) {
    148162                                        return $result;
    abstract class WP_REST_Meta_Fields { 
    150164                                continue;
    151165                        }
    152166
    153                         $is_valid = rest_validate_value_from_schema( $meta[ $name ], $args['schema'], 'meta.' . $name );
     167                        $value = $meta[ $name ];
     168
     169                        if ( ! $args['single'] && is_array( $value ) && count( array_filter( $value, 'is_null' ) ) ) {
     170                                return new WP_Error(
     171                                        'rest_invalid_stored_value',
     172                                        sprintf( __( 'The %s property has an invalid stored value, and cannot be updated to null.' ), $name ),
     173                                        array( 'status' => 500 )
     174                                );
     175                        }
     176
     177                        $is_valid = rest_validate_value_from_schema( $value, $args['schema'], 'meta.' . $name );
    154178                        if ( is_wp_error( $is_valid ) ) {
    155179                                $is_valid->add_data( array( 'status' => 400 ) );
    156180                                return $is_valid;
    157181                        }
    158182
    159                         $value = rest_sanitize_value_from_schema( $meta[ $name ], $args['schema'] );
     183                        $value = rest_sanitize_value_from_schema( $value, $args['schema'] );
    160184
    161185                        if ( $args['single'] ) {
    162186                                $result = $this->update_meta_value( $object_id, $meta_key, $name, $value );
    abstract class WP_REST_Meta_Fields { 
    261285                }
    262286
    263287                // `delete_metadata` removes _all_ instances of the value, so only call once.
    264                 $to_remove = array_unique( $to_remove );
     288                $to_remove = array_map( 'maybe_unserialize', array_unique( array_map( static function( $value ) {
     289                        return is_scalar( $value ) ? $value : maybe_serialize( $value );
     290                }, $to_remove ) ) );
    265291
    266292                foreach ( $to_remove as $value ) {
    267293                        if ( ! delete_metadata( $meta_type, $object_id, wp_slash( $meta_key ), wp_slash( $value ) ) ) {
    abstract class WP_REST_Meta_Fields { 
    390416                        $type = ! empty( $rest_args['type'] ) ? $rest_args['type'] : null;
    391417                        $type = ! empty( $rest_args['schema']['type'] ) ? $rest_args['schema']['type'] : $type;
    392418
    393                         if ( ! in_array( $type, array( 'string', 'boolean', 'integer', 'number' ) ) ) {
     419                        if ( null === $rest_args['schema']['default'] ) {
     420                                $rest_args['schema']['default'] = $this->get_default_for_type( $type );
     421                        }
     422
     423                        $rest_args['schema'] = $this->default_additional_properties_to_false( $rest_args['schema'] );
     424
     425                        if ( ! in_array( $type, array( 'string', 'boolean', 'integer', 'number', 'array', 'object' ) ) ) {
    394426                                continue;
    395427                        }
    396428
    397429                        if ( empty( $rest_args['single'] ) ) {
    398                                 $rest_args['schema']['items'] = array(
    399                                         'type' => $rest_args['type'],
     430                                $rest_args['schema'] = array(
     431                                        'type'  => 'array',
     432                                        'items' => $rest_args['schema'],
    400433                                );
    401                                 $rest_args['schema']['type']  = 'array';
    402434                        }
    403435
    404436                        $registered[ $name ] = $rest_args;
    abstract class WP_REST_Meta_Fields { 
    449481         * @return mixed Value prepared for output. If a non-JsonSerializable object, null.
    450482         */
    451483        public static function prepare_value( $value, $request, $args ) {
    452                 $type = $args['schema']['type'];
    453484
    454                 // For multi-value fields, check the item type instead.
    455                 if ( 'array' === $type && ! empty( $args['schema']['items']['type'] ) ) {
    456                         $type = $args['schema']['items']['type'];
     485                if ( $args['single'] ) {
     486                        $schema = $args['schema'];
     487                } else {
     488                        $schema = $args['schema']['items'];
    457489                }
    458490
    459                 switch ( $type ) {
    460                         case 'string':
    461                                 $value = (string) $value;
    462                                 break;
    463                         case 'integer':
    464                                 $value = (int) $value;
    465                                 break;
    466                         case 'number':
    467                                 $value = (float) $value;
    468                                 break;
    469                         case 'boolean':
    470                                 $value = (bool) $value;
    471                                 break;
    472                 }
    473 
    474                 // Don't allow objects to be output.
    475                 if ( is_object( $value ) && ! ( $value instanceof JsonSerializable ) ) {
     491                if ( is_wp_error( rest_validate_value_from_schema( $value, $schema ) ) ) {
    476492                        return null;
    477493                }
    478494
    479                 return $value;
     495                return rest_sanitize_value_from_schema( $value, $schema );
    480496        }
    481497
    482498        /**
    abstract class WP_REST_Meta_Fields { 
    496512
    497513                return $value;
    498514        }
     515
     516        /**
     517         * Recursively add additionalProperties = false to all objects in a schema if no additionalProperties setting
     518         * is specified.
     519         *
     520         * This is need to restrict properties of objects in meta values to only
     521         * registered items, as the REST API will allow additional properties by
     522         * default.
     523         *
     524         * @since 5.3.0
     525         *
     526         * @param array $schema The schema array.
     527         * @return array
     528         */
     529        protected function default_additional_properties_to_false( $schema ) {
     530                switch ( $schema['type'] ) {
     531                        case 'object':
     532                                foreach ( $schema['properties'] as $key => $child_schema ) {
     533                                        $schema['properties'][ $key ] = $this->default_additional_properties_to_false( $child_schema );
     534                                }
     535
     536                                if ( ! isset( $schema['additionalProperties'] ) ) {
     537                                        $schema['additionalProperties'] = false;
     538                                }
     539                                break;
     540                        case 'array':
     541                                $schema['items'] = $this->default_additional_properties_to_false( $schema['items'] );
     542                                break;
     543                }
     544
     545                return $schema;
     546        }
     547
     548        /**
     549         * Gets the default value for a schema type.
     550         *
     551         * @since 5.3.0
     552         *
     553         * @param string $type
     554         * @return mixed
     555         */
     556        protected function get_default_for_type( $type ) {
     557                switch ( $type ) {
     558                        case 'string':
     559                                return '';
     560                        case 'boolean':
     561                                return false;
     562                        case 'integer':
     563                                return 0;
     564                        case 'number':
     565                                return 0.0;
     566                        case 'array':
     567                                return [];
     568                        case 'object':
     569                                return [];
     570                        default:
     571                                return null;
     572                }
     573        }
    499574}
  • tests/phpunit/includes/class-basic-object.php

    diff --git a/tests/phpunit/includes/class-basic-object.php b/tests/phpunit/includes/class-basic-object.php
    index f773c94ca1..a85a9f6596 100644
    a b  
    77 * @since 4.7.0
    88 */
    99
    10 trigger_error( __FILE__ . ' is deprecated since version 5.0.0 with no alternative available.' );
    11 
    1210/**
    1311 * Class used to test accessing methods and properties
    1412 *
  • new file tests/phpunit/includes/class-jsonserializable-object.php

    diff --git a/tests/phpunit/includes/class-jsonserializable-object.php b/tests/phpunit/includes/class-jsonserializable-object.php
    new file mode 100644
    index 0000000000..e58cfb9ebd
    - +  
     1<?php
     2/**
     3 * Unit Tests: JsonSerializbale_Object
     4 *
     5 * @package WordPress
     6 * @subpackage UnitTests
     7 * @since 5.3.0
     8 */
     9
     10class JsonSerializable_Object implements JsonSerializable {
     11
     12        private $data;
     13
     14        public function __construct( $data ) {
     15                $this->data = $data;
     16        }
     17
     18        public function jsonSerialize() {
     19                return $this->data;
     20        }
     21}
  • tests/phpunit/includes/functions.php

    diff --git a/tests/phpunit/includes/functions.php b/tests/phpunit/includes/functions.php
    index d7c2ff47ab..0a70ca17f6 100644
    a b  
    11<?php
     2require_once __DIR__ . '/class-basic-object.php';
    23
    34/**
    45 * Retrieves PHPUnit runner version.
  • tests/phpunit/tests/rest-api/rest-post-meta-fields.php

    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 dc2401b1c3..47751c53e6 100644
    a b class WP_Test_REST_Post_Meta_Fields extends WP_Test_REST_TestCase { 
    13471347                $this->assertEquals( 'Hello', $data['meta']['test\'slashed\'key'] );
    13481348        }
    13491349
     1350        /**
     1351         * @ticket 43392
     1352         */
     1353        public function test_object_single() {
     1354                $this->grant_write_permission();
     1355
     1356                register_post_meta( 'post', 'object', [
     1357                        'single'       => true,
     1358                        'type'         => 'object',
     1359                        'show_in_rest' => [
     1360                                'schema' => [
     1361                                        'type'                 => 'object',
     1362                                        'properties'           => [
     1363                                                'project' => [
     1364                                                        'type' => 'string',
     1365                                                ],
     1366                                        ],
     1367                                ],
     1368                        ],
     1369                ] );
     1370
     1371                $request = new WP_REST_Request( 'PUT', sprintf( '/wp/v2/posts/%d', self::$post_id ) );
     1372                $request->set_body_params( [
     1373                        'meta' => [
     1374                                'object' => [
     1375                                        'project' => 'WordPress',
     1376                                ],
     1377                        ],
     1378                ] );
     1379
     1380                $response = rest_get_server()->dispatch( $request );
     1381                $data = $response->get_data();
     1382
     1383                $this->assertArrayHasKey( 'object', $data['meta'] );
     1384                $this->assertArrayHasKey( 'project', $data['meta']['object'] );
     1385                $this->assertEquals( 'WordPress', $data['meta']['object']['project'] );
     1386
     1387                $meta = get_post_meta( self::$post_id, 'object', true );
     1388                $this->assertArrayHasKey( 'project', $meta );
     1389                $this->assertEquals( 'WordPress', $meta['project'] );
     1390        }
     1391
     1392        /**
     1393         * @ticket 43392
     1394         */
     1395        public function test_object_multiple() {
     1396                $this->grant_write_permission();
     1397
     1398                register_post_meta( 'post', 'object', [
     1399                        'single'       => false,
     1400                        'type'         => 'object',
     1401                        'show_in_rest' => [
     1402                                'schema' => [
     1403                                        'type'                 => 'object',
     1404                                        'properties'           => [
     1405                                                'project' => [
     1406                                                        'type' => 'string',
     1407                                                ],
     1408                                        ],
     1409                                ],
     1410                        ],
     1411                ] );
     1412
     1413                $request = new WP_REST_Request( 'PUT', sprintf( '/wp/v2/posts/%d', self::$post_id ) );
     1414                $request->set_body_params( [
     1415                        'meta' => [
     1416                                'object' => [
     1417                                        [
     1418                                                'project' => 'WordPress',
     1419                                        ],
     1420                                        [
     1421                                                'project' => 'bbPress',
     1422                                        ],
     1423                                ],
     1424                        ],
     1425                ] );
     1426
     1427                $response = rest_get_server()->dispatch( $request );
     1428                $data = $response->get_data();
     1429
     1430                $this->assertArrayHasKey( 'object', $data['meta'] );
     1431                $this->assertCount( 2, $data['meta']['object'] );
     1432
     1433                $this->assertArrayHasKey( 'project', $data['meta']['object'][0] );
     1434                $this->assertEquals( 'WordPress', $data['meta']['object'][0]['project'] );
     1435
     1436                $this->assertArrayHasKey( 'project', $data['meta']['object'][1] );
     1437                $this->assertEquals( 'bbPress', $data['meta']['object'][1]['project'] );
     1438
     1439                $meta = get_post_meta( self::$post_id, 'object' );
     1440
     1441                $this->assertCount( 2, $meta );
     1442
     1443                $this->assertArrayHasKey( 'project', $meta[0] );
     1444                $this->assertEquals( 'WordPress', $meta[0]['project'] );
     1445
     1446                $this->assertArrayHasKey( 'project', $meta[1] );
     1447                $this->assertEquals( 'bbPress', $meta[1]['project'] );
     1448        }
     1449
     1450        /**
     1451         * @ticket 43392
     1452         */
     1453        public function test_array_single() {
     1454                $this->grant_write_permission();
     1455
     1456                register_post_meta( 'post', 'list', [
     1457                        'single'       => true,
     1458                        'type'         => 'array',
     1459                        'show_in_rest' => [
     1460                                'schema' => [
     1461                                        'type'  => 'array',
     1462                                        'items' => [
     1463                                                'type' => 'string',
     1464                                        ],
     1465                                ],
     1466                        ],
     1467                ] );
     1468
     1469                $request = new WP_REST_Request( 'PUT', sprintf( '/wp/v2/posts/%d', self::$post_id ) );
     1470                $request->set_body_params( [
     1471                        'meta' => [
     1472                                'list' => [ 'WordPress', 'bbPress' ],
     1473                        ],
     1474                ] );
     1475
     1476                $response = rest_get_server()->dispatch( $request );
     1477                $data = $response->get_data();
     1478
     1479                $this->assertArrayHasKey( 'list', $data['meta'] );
     1480                $this->assertEquals( [ 'WordPress', 'bbPress' ], $data['meta']['list'] );
     1481
     1482                $meta = get_post_meta( self::$post_id, 'list', true );
     1483                $this->assertEquals( [ 'WordPress', 'bbPress' ], $meta );
     1484        }
     1485
     1486        /**
     1487         * @ticket 43392
     1488         */
     1489        public function test_array_of_objects_multiple() {
     1490                $this->grant_write_permission();
     1491
     1492                register_post_meta( 'post', 'list_of_objects', [
     1493                        'single'       => false,
     1494                        'type'         => 'array',
     1495                        'show_in_rest' => [
     1496                                'schema' => [
     1497                                        'type'  => 'array',
     1498                                        'items' => [
     1499                                                'type'       => 'object',
     1500                                                'properties' => [
     1501                                                        'version' => [
     1502                                                                'type' => 'string',
     1503                                                        ],
     1504                                                        'artist'  => [
     1505                                                                'type' => 'string',
     1506                                                        ]
     1507                                                ]
     1508                                        ],
     1509                                ],
     1510                        ],
     1511                ] );
     1512
     1513                $request = new WP_REST_Request( 'PUT', sprintf( '/wp/v2/posts/%d', self::$post_id ) );
     1514                $request->set_body_params( [
     1515                        'meta' => [
     1516                                'list_of_objects' => [
     1517                                        // Meta 1
     1518                                        [
     1519                                                [ 'version' => '5.2', 'artist' => 'Jaco' ],
     1520                                                [ 'version' => '5.1', 'artist' => 'Betty' ],
     1521                                        ],
     1522                                        // Meta 2
     1523                                        [
     1524                                                [ 'version' => '4.9', 'artist' => 'Tipton' ],
     1525                                        ],
     1526                                ],
     1527                        ],
     1528                ] );
     1529
     1530
     1531                $response = rest_get_server()->dispatch( $request );
     1532                $data     = $response->get_data();
     1533
     1534                $this->assertArrayHasKey( 'list_of_objects', $data['meta'] );
     1535                $this->assertCount( 2, $data['meta']['list_of_objects'] );
     1536
     1537                $this->assertEquals( [
     1538                        [ 'version' => '5.2', 'artist' => 'Jaco' ],
     1539                        [ 'version' => '5.1', 'artist' => 'Betty' ],
     1540                ], $data['meta']['list_of_objects'][0] );
     1541
     1542                $this->assertEquals( [
     1543                        [ 'version' => '4.9', 'artist' => 'Tipton' ],
     1544                ], $data['meta']['list_of_objects'][1] );
     1545
     1546                $meta = get_post_meta( self::$post_id, 'list_of_objects' );
     1547
     1548                $this->assertCount( 2, $meta );
     1549
     1550                $this->assertEquals( [
     1551                        [ 'version' => '5.2', 'artist' => 'Jaco' ],
     1552                        [ 'version' => '5.1', 'artist' => 'Betty' ],
     1553                ], $meta[0] );
     1554
     1555                $this->assertEquals( [
     1556                        [ 'version' => '4.9', 'artist' => 'Tipton' ],
     1557                ], $meta[1] );
     1558        }
     1559
     1560        /**
     1561         * @ticket 43392
     1562         */
     1563        public function test_php_objects_returned_as_null() {
     1564                register_post_meta( 'post', 'object', [
     1565                        'single'       => true,
     1566                        'type'         => 'object',
     1567                        'show_in_rest' => [
     1568                                'schema' => [
     1569                                        'type'                 => 'object',
     1570                                        'properties'           => [
     1571                                                'project' => [
     1572                                                        'type' => 'string',
     1573                                                ],
     1574                                        ],
     1575                                ],
     1576                        ],
     1577                ] );
     1578
     1579                update_post_meta( self::$post_id, 'object', new Basic_Object( [ 'project' => 'WordPress' ] ) );
     1580
     1581                $request = new WP_REST_Request( 'GET', sprintf( '/wp/v2/posts/%d', self::$post_id ) );
     1582                $response = rest_get_server()->dispatch( $request );
     1583                $data = $response->get_data();
     1584
     1585                $this->assertArrayHasKey( 'object', $data['meta'] );
     1586                $this->assertNull( $data['meta']['object'] );
     1587        }
     1588
     1589        /**
     1590         * @ticket 43392
     1591         */
     1592        public function test_php_objects_returned_as_null_multiple() {
     1593                register_post_meta( 'post', 'object', [
     1594                        'single'       => false,
     1595                        'type'         => 'object',
     1596                        'show_in_rest' => [
     1597                                'schema' => [
     1598                                        'type'                 => 'object',
     1599                                        'properties'           => [
     1600                                                'project' => [
     1601                                                        'type' => 'string',
     1602                                                ],
     1603                                        ],
     1604                                ],
     1605                        ],
     1606                ] );
     1607
     1608                add_post_meta( self::$post_id, 'object', [ 'project' => 'bbPress' ] );
     1609                add_post_meta( self::$post_id, 'object', new Basic_Object( [ 'project' => 'WordPress' ] ) );
     1610
     1611                $request = new WP_REST_Request( 'GET', sprintf( '/wp/v2/posts/%d', self::$post_id ) );
     1612                $response = rest_get_server()->dispatch( $request );
     1613                $data = $response->get_data();
     1614
     1615                $this->assertArrayHasKey( 'object', $data['meta'] );
     1616                $this->assertCount( 2, $data['meta']['object'] );
     1617                $this->assertEquals( [ 'project' => 'bbPress' ], $data['meta']['object'][0] );
     1618                $this->assertNull( $data['meta']['object'][1] );
     1619        }
     1620
     1621        /**
     1622         * @ticket 43392
     1623         */
     1624        public function test_php_jsonserializable_object_returns_value() {
     1625                require_once __DIR__ . '/../../includes/class-jsonserializable-object.php';
     1626
     1627                register_post_meta( 'post', 'object', [
     1628                        'single'       => true,
     1629                        'type'         => 'object',
     1630                        'show_in_rest' => [
     1631                                'schema' => [
     1632                                        'type'                 => 'object',
     1633                                        'properties'           => [
     1634                                                'project' => [
     1635                                                        'type' => 'string',
     1636                                                ],
     1637                                        ],
     1638                                ],
     1639                        ],
     1640                ] );
     1641
     1642                update_post_meta( self::$post_id, 'object', new JsonSerializable_Object( [ 'project' => 'WordPress' ] ) );
     1643
     1644                $request = new WP_REST_Request( 'GET', sprintf( '/wp/v2/posts/%d', self::$post_id ) );
     1645                $response = rest_get_server()->dispatch( $request );
     1646                $data = $response->get_data();
     1647
     1648                $this->assertArrayHasKey( 'object', $data['meta'] );
     1649                $this->assertEquals( [ 'project' => 'WordPress' ], $data['meta']['object'] );
     1650        }
     1651
     1652        /**
     1653         * @ticket 43392
     1654         */
     1655        public function test_updating_meta_to_null_for_key_with_existing_php_object_does_not_delete_meta_value() {
     1656                $this->grant_write_permission();
     1657
     1658                register_post_meta( 'post', 'object', [
     1659                        'single'       => true,
     1660                        'type'         => 'object',
     1661                        'show_in_rest' => [
     1662                                'schema' => [
     1663                                        'type'                 => 'object',
     1664                                        'properties'           => [
     1665                                                'project' => [
     1666                                                        'type' => 'string',
     1667                                                ],
     1668                                        ],
     1669                                ],
     1670                        ],
     1671                ] );
     1672
     1673                update_post_meta( self::$post_id, 'object', new Basic_Object( [ 'project' => 'WordPress' ] ) );
     1674
     1675                $request = new WP_REST_Request( 'PUT', sprintf( '/wp/v2/posts/%d', self::$post_id ) );
     1676                $request->set_body_params( [
     1677                        'meta' => [
     1678                                'object' => null,
     1679                        ]
     1680                ] );
     1681
     1682                $response = rest_get_server()->dispatch( $request );
     1683                $this->assertEquals( 500, $response->get_status() );
     1684        }
     1685
     1686        /**
     1687         * @ticket 43392
     1688         */
     1689        public function test_updating_non_single_meta_to_null_for_key_with_existing_php_object_does_not_set_meta_value_to_null() {
     1690                $this->grant_write_permission();
     1691
     1692                register_post_meta( 'post', 'object', [
     1693                        'single'       => false,
     1694                        'type'         => 'object',
     1695                        'show_in_rest' => [
     1696                                'schema' => [
     1697                                        'type'                 => 'object',
     1698                                        'properties'           => [
     1699                                                'project' => [
     1700                                                        'type' => 'string',
     1701                                                ],
     1702                                        ],
     1703                                ],
     1704                        ],
     1705                ] );
     1706
     1707                add_post_meta( self::$post_id, 'object', [ 'project' => 'bbPress' ] );
     1708                add_post_meta( self::$post_id, 'object', new Basic_Object( [ 'project' => 'WordPress' ] ) );
     1709
     1710                $request = new WP_REST_Request( 'PUT', sprintf( '/wp/v2/posts/%d', self::$post_id ) );
     1711                $request->set_body_params( [
     1712                        'meta' => [
     1713                                'object' => [
     1714                                        [ 'project' => 'BuddyPress' ],
     1715                                        null,
     1716                                ],
     1717                        ]
     1718                ] );
     1719
     1720                $response = rest_get_server()->dispatch( $request );
     1721                $this->assertEquals( 500, $response->get_status() );
     1722        }
     1723
     1724        /**
     1725         * @ticket 43392
     1726         */
     1727        public function test_object_rejects_additional_properties_by_default() {
     1728                $this->grant_write_permission();
     1729
     1730                register_post_meta( 'post', 'object', [
     1731                        'single'       => true,
     1732                        'type'         => 'object',
     1733                        'show_in_rest' => [
     1734                                'schema' => [
     1735                                        'type'       => 'object',
     1736                                        'properties' => [
     1737                                                'project' => [
     1738                                                        'type' => 'string',
     1739                                                ],
     1740                                        ],
     1741                                ],
     1742                        ],
     1743                ] );
     1744
     1745                $request = new WP_REST_Request( 'PUT', sprintf( '/wp/v2/posts/%d', self::$post_id ) );
     1746                $request->set_body_params( [
     1747                        'meta' => [
     1748                                'object' => [
     1749                                        'project'     => 'BuddyPress',
     1750                                        'awesomeness' => 100,
     1751                                ],
     1752                        ]
     1753                ] );
     1754
     1755                $response = rest_get_server()->dispatch( $request );
     1756                $this->assertEquals( 400, $response->get_status() );
     1757        }
     1758
     1759        /**
     1760         * @ticket 43392
     1761         */
     1762        public function test_object_allows_additional_properties_if_explicitly_set() {
     1763                $this->grant_write_permission();
     1764
     1765                $value = [
     1766                        'project'     => 'BuddyPress',
     1767                        'awesomeness' => 100,
     1768                ];
     1769
     1770                register_post_meta( 'post', 'object', [
     1771                        'single'       => true,
     1772                        'type'         => 'object',
     1773                        'show_in_rest' => [
     1774                                'schema' => [
     1775                                        'type'       => 'object',
     1776                                        'additionalProperties' => true,
     1777                                        'properties' => [
     1778                                                'project' => [
     1779                                                        'type' => 'string',
     1780                                                ],
     1781                                        ],
     1782                                ],
     1783                        ],
     1784                ] );
     1785
     1786                $request = new WP_REST_Request( 'PUT', sprintf( '/wp/v2/posts/%d', self::$post_id ) );
     1787                $request->set_body_params( [
     1788                        'meta' => [
     1789                                'object' => $value,
     1790                        ]
     1791                ] );
     1792
     1793                $response = rest_get_server()->dispatch( $request );
     1794                $this->assertEquals( 200, $response->get_status() );
     1795                $this->assertEquals( $value, $response->get_data()['meta']['object'] );
     1796
     1797                $this->assertEquals( $value, get_post_meta( self::$post_id, 'object', true ) );
     1798        }
     1799
     1800        /**
     1801         * @ticket 43392
     1802         */
     1803        public function test_object_allows_additional_properties_and_uses_its_schema() {
     1804                $this->grant_write_permission();
     1805
     1806                $value = [
     1807                        'project'     => 'BuddyPress',
     1808                        'awesomeness' => 'fabulous',
     1809                ];
     1810
     1811                register_post_meta( 'post', 'object', [
     1812                        'single'       => true,
     1813                        'type'         => 'object',
     1814                        'show_in_rest' => [
     1815                                'schema' => [
     1816                                        'type'       => 'object',
     1817                                        'additionalProperties' => [
     1818                                                'type' => 'number',
     1819                                        ],
     1820                                        'properties' => [
     1821                                                'project' => [
     1822                                                        'type' => 'string',
     1823                                                ],
     1824                                        ],
     1825                                ],
     1826                        ],
     1827                ] );
     1828
     1829                $request = new WP_REST_Request( 'PUT', sprintf( '/wp/v2/posts/%d', self::$post_id ) );
     1830                $request->set_body_params( [
     1831                        'meta' => [
     1832                                'object' => $value,
     1833                        ]
     1834                ] );
     1835
     1836                $response = rest_get_server()->dispatch( $request );
     1837                $this->assertEquals( 400, $response->get_status() );
     1838        }
     1839
     1840        public function test_invalid_meta_value_are_set_to_null_in_response() {
     1841                register_post_meta( 'post', 'email', [
     1842                        'single'       => true,
     1843                        'type'         => 'string',
     1844                        'show_in_rest' => [
     1845                                'schema' => [
     1846                                        'type'   => 'string',
     1847                                        'format' => 'email',
     1848                                ],
     1849                        ],
     1850                ] );
     1851
     1852                update_post_meta( self::$post_id, 'email', 'invalid_meta_value' );
     1853
     1854                $request  = new WP_REST_Request( 'GET', sprintf( '/wp/v2/posts/%d', self::$post_id ) );
     1855                $response = rest_get_server()->dispatch( $request );
     1856
     1857                $this->assertNull( $response->get_data()['meta']['email'] );
     1858        }
     1859
     1860        public function test_meta_values_are_not_set_to_null_in_response_if_type_safe() {
     1861                register_post_meta( 'post', 'boolean', [
     1862                        'single'       => true,
     1863                        'show_in_rest' => true,
     1864                        'type'         => 'boolean',
     1865                ] );
     1866
     1867                update_post_meta( self::$post_id, 'boolean', 'true' );
     1868
     1869                $request  = new WP_REST_Request( 'GET', sprintf( '/wp/v2/posts/%d', self::$post_id ) );
     1870                $response = rest_get_server()->dispatch( $request );
     1871
     1872                $this->assertTrue( $response->get_data()['meta']['boolean'] );
     1873        }
     1874
    13501875        /**
    13511876         * Internal function used to disable an insert query which
    13521877         * will trigger a wpdb error for testing purposes.