Make WordPress Core


Ignore:
Timestamp:
07/05/2020 12:13:37 AM (4 years ago)
Author:
TimothyBlynJacobs
Message:

REST API: Make multi-typed schemas more robust.

A multi-type schema is a schema where the type keyword is an array of possible types instead of a single type. For instance, [ 'object', 'string' ] would allow objects or string values.

In [46249] basic support for these schemas was introduced. The validator would loop over each schema type trying to find a version that matched. This worked for valid values, but for invalid values it provided unhelpful error messages. The sanitizer also had its utility restricted.

In this commit, the validators and sanitizers will first determine the best type of the passed value and then apply the schema with that set type. In the case that a value could match multiple types, the schema of the first matching type will be used.

To maintain backward compatibility, if unsupported schema types are used, the value will always pass validation. A doing it wrong notice is issued in this case.

Fixes #50300.
Props pentatonicfunk, dlh, TimothyBlynJacobs.

File:
1 edited

Legend:

Unmodified
Added
Removed
  • trunk/tests/phpunit/tests/rest-api.php

    r48273 r48306  
    99require_once ABSPATH . 'wp-admin/includes/admin.php';
    1010require_once ABSPATH . WPINC . '/rest-api.php';
     11require_once __DIR__ . '/../includes/class-jsonserializable-object.php';
    1112
    1213/**
     
    14581459        $this->assertEquals( '/wp/v2/tags/' . $term->term_id, rest_get_route_for_term( $term->term_id ) );
    14591460    }
     1461
     1462    /**
     1463     * @dataProvider _dp_rest_is_object
     1464     * @ticket 50300
     1465     * @param bool  $expected Expected result of the check.
     1466     * @param mixed $value    The value to check.
     1467     */
     1468    public function test_rest_is_object( $expected, $value ) {
     1469        $is_object = rest_is_object( $value );
     1470
     1471        if ( $expected ) {
     1472            $this->assertTrue( $is_object );
     1473        } else {
     1474            $this->assertFalse( $is_object );
     1475        }
     1476    }
     1477
     1478    public function _dp_rest_is_object() {
     1479        return array(
     1480            array(
     1481                true,
     1482                '',
     1483            ),
     1484            array(
     1485                true,
     1486                new stdClass(),
     1487            ),
     1488            array(
     1489                true,
     1490                new JsonSerializable_Object( array( 'hi' => 'there' ) ),
     1491            ),
     1492            array(
     1493                true,
     1494                array( 'hi' => 'there' ),
     1495            ),
     1496            array(
     1497                true,
     1498                array(),
     1499            ),
     1500            array(
     1501                true,
     1502                array( 'a', 'b' ),
     1503            ),
     1504            array(
     1505                false,
     1506                new Basic_Object(),
     1507            ),
     1508            array(
     1509                false,
     1510                new JsonSerializable_Object( 'str' ),
     1511            ),
     1512            array(
     1513                false,
     1514                'str',
     1515            ),
     1516            array(
     1517                false,
     1518                5,
     1519            ),
     1520        );
     1521    }
     1522
     1523    /**
     1524     * @dataProvider _dp_rest_sanitize_object
     1525     * @ticket       50300
     1526     * @param array $expected Expected sanitized version.
     1527     * @param mixed $value    The value to sanitize.
     1528     */
     1529    public function test_rest_sanitize_object( $expected, $value ) {
     1530        $sanitized = rest_sanitize_object( $value );
     1531        $this->assertEquals( $expected, $sanitized );
     1532    }
     1533
     1534    public function _dp_rest_sanitize_object() {
     1535        return array(
     1536            array(
     1537                array(),
     1538                '',
     1539            ),
     1540            array(
     1541                array( 'a' => '1' ),
     1542                (object) array( 'a' => '1' ),
     1543            ),
     1544            array(
     1545                array( 'hi' => 'there' ),
     1546                new JsonSerializable_Object( array( 'hi' => 'there' ) ),
     1547            ),
     1548            array(
     1549                array( 'hi' => 'there' ),
     1550                array( 'hi' => 'there' ),
     1551            ),
     1552            array(
     1553                array(),
     1554                array(),
     1555            ),
     1556            array(
     1557                array( 'a', 'b' ),
     1558                array( 'a', 'b' ),
     1559            ),
     1560            array(
     1561                array(),
     1562                new Basic_Object(),
     1563            ),
     1564            array(
     1565                array(),
     1566                new JsonSerializable_Object( 'str' ),
     1567            ),
     1568            array(
     1569                array(),
     1570                'str',
     1571            ),
     1572            array(
     1573                array(),
     1574                5,
     1575            ),
     1576        );
     1577    }
     1578
     1579    /**
     1580     * @dataProvider _dp_rest_is_array
     1581     * @ticket 50300
     1582     * @param bool  $expected Expected result of the check.
     1583     * @param mixed $value    The value to check.
     1584     */
     1585    public function test_rest_is_array( $expected, $value ) {
     1586        $is_array = rest_is_array( $value );
     1587
     1588        if ( $expected ) {
     1589            $this->assertTrue( $is_array );
     1590        } else {
     1591            $this->assertFalse( $is_array );
     1592        }
     1593    }
     1594
     1595    public function _dp_rest_is_array() {
     1596        return array(
     1597            array(
     1598                true,
     1599                '',
     1600            ),
     1601            array(
     1602                true,
     1603                array( 'a', 'b' ),
     1604            ),
     1605            array(
     1606                true,
     1607                array(),
     1608            ),
     1609            array(
     1610                true,
     1611                'a,b,c',
     1612            ),
     1613            array(
     1614                true,
     1615                'a',
     1616            ),
     1617            array(
     1618                true,
     1619                5,
     1620            ),
     1621            array(
     1622                false,
     1623                new stdClass(),
     1624            ),
     1625            array(
     1626                false,
     1627                new JsonSerializable_Object( array( 'hi' => 'there' ) ),
     1628            ),
     1629            array(
     1630                false,
     1631                array( 'hi' => 'there' ),
     1632            ),
     1633            array(
     1634                false,
     1635                new Basic_Object(),
     1636            ),
     1637            array(
     1638                false,
     1639                new JsonSerializable_Object( 'str' ),
     1640            ),
     1641            array(
     1642                false,
     1643                null,
     1644            ),
     1645        );
     1646    }
     1647
     1648    /**
     1649     * @dataProvider _dp_rest_sanitize_array
     1650     * @ticket       50300
     1651     * @param array $expected Expected sanitized version.
     1652     * @param mixed $value    The value to sanitize.
     1653     */
     1654    public function test_rest_sanitize_array( $expected, $value ) {
     1655        $sanitized = rest_sanitize_array( $value );
     1656        $this->assertEquals( $expected, $sanitized );
     1657    }
     1658
     1659    public function _dp_rest_sanitize_array() {
     1660        return array(
     1661            array(
     1662                array(),
     1663                '',
     1664            ),
     1665            array(
     1666                array( 'a', 'b' ),
     1667                array( 'a', 'b' ),
     1668            ),
     1669            array(
     1670                array(),
     1671                array(),
     1672            ),
     1673            array(
     1674                array( 'a', 'b', 'c' ),
     1675                'a,b,c',
     1676            ),
     1677            array(
     1678                array( 'a' ),
     1679                'a',
     1680            ),
     1681            array(
     1682                array( 'a', 'b' ),
     1683                'a,b,',
     1684            ),
     1685            array(
     1686                array( '5' ),
     1687                5,
     1688            ),
     1689            array(
     1690                array(),
     1691                new stdClass(),
     1692            ),
     1693            array(
     1694                array(),
     1695                new JsonSerializable_Object( array( 'hi' => 'there' ) ),
     1696            ),
     1697            array(
     1698                array( 'there' ),
     1699                array( 'hi' => 'there' ),
     1700            ),
     1701            array(
     1702                array(),
     1703                new Basic_Object(),
     1704            ),
     1705            array(
     1706                array(),
     1707                new JsonSerializable_Object( 'str' ),
     1708            ),
     1709            array(
     1710                array(),
     1711                null,
     1712            ),
     1713        );
     1714    }
     1715
     1716    /**
     1717     * @dataProvider _dp_get_best_type_for_value
     1718     * @ticket 50300
     1719     * @param string $expected The expected best type.
     1720     * @param mixed  $value    The value to test.
     1721     * @param array  $types    The list of available types.
     1722     */
     1723    public function test_get_best_type_for_value( $expected, $value, $types ) {
     1724        $this->assertEquals( $expected, rest_get_best_type_for_value( $value, $types ) );
     1725    }
     1726
     1727    public function _dp_get_best_type_for_value() {
     1728        return array(
     1729            array(
     1730                'array',
     1731                array( 'hi' ),
     1732                array( 'array' ),
     1733            ),
     1734            array(
     1735                'object',
     1736                array( 'hi' => 'there' ),
     1737                array( 'object' ),
     1738            ),
     1739            array(
     1740                'integer',
     1741                5,
     1742                array( 'integer' ),
     1743            ),
     1744            array(
     1745                'number',
     1746                4.0,
     1747                array( 'number' ),
     1748            ),
     1749            array(
     1750                'boolean',
     1751                true,
     1752                array( 'boolean' ),
     1753            ),
     1754            array(
     1755                'string',
     1756                'str',
     1757                array( 'string' ),
     1758            ),
     1759            array(
     1760                'null',
     1761                null,
     1762                array( 'null' ),
     1763            ),
     1764            array(
     1765                'string',
     1766                '',
     1767                array( 'array', 'string' ),
     1768            ),
     1769            array(
     1770                'string',
     1771                '',
     1772                array( 'object', 'string' ),
     1773            ),
     1774            array(
     1775                'string',
     1776                'Hello',
     1777                array( 'object', 'string' ),
     1778            ),
     1779            array(
     1780                'object',
     1781                array( 'hello' => 'world' ),
     1782                array( 'object', 'string' ),
     1783            ),
     1784            array(
     1785                'number',
     1786                '5.0',
     1787                array( 'number', 'string' ),
     1788            ),
     1789            array(
     1790                'string',
     1791                '5.0',
     1792                array( 'string', 'number' ),
     1793            ),
     1794            array(
     1795                'boolean',
     1796                'false',
     1797                array( 'boolean', 'string' ),
     1798            ),
     1799            array(
     1800                'string',
     1801                'false',
     1802                array( 'string', 'boolean' ),
     1803            ),
     1804            array(
     1805                'string',
     1806                'a,b',
     1807                array( 'string', 'array' ),
     1808            ),
     1809            array(
     1810                'array',
     1811                'a,b',
     1812                array( 'array', 'string' ),
     1813            ),
     1814        );
     1815    }
    14601816}
Note: See TracChangeset for help on using the changeset viewer.