Make WordPress Core

Changeset 62549


Ignore:
Timestamp:
06/23/2026 11:40:49 AM (4 months ago)
Author:
gziolo
Message:

REST API: Add a shared helper for JSON Schema allowed keywords.

Introduce wp_get_json_schema_allowed_keywords() as the single place to decide which JSON Schema keywords may stay in schema output. It supports two profiles: rest-api (the default, used for REST route output) and draft-04 (a wider draft-04 set used when publishing schemas to clients such as the Abilities API).

Route schema output now flows through the helper and the new wp_json_schema_allowed_keywords filter, replacing the Abilities-specific special casing in the REST list controller. Validation internals still call rest_get_allowed_schema_keywords() directly, so REST validation behavior does not change.

Props gziolo, apermo.
See #64955.

Location:
trunk
Files:
2 added
5 edited

Legend:

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

    r62489 r62549  
    16501650                }
    16511651
    1652                 $allowed_schema_keywords = array_flip( rest_get_allowed_schema_keywords() );
     1652                $allowed_schema_keywords = array_flip( wp_get_json_schema_allowed_keywords( 'rest-api' ) );
    16531653
    16541654                $route = preg_replace( '#\(\?P<(\w+?)>.*?\)#', '{$1}', $route );
  • trunk/src/wp-includes/rest-api/endpoints/class-wp-rest-abilities-v1-list-controller.php

    r62548 r62549  
    3434         */
    3535        protected $rest_base = 'abilities';
     36
     37        /**
     38         * Lookup map of allowed schema keywords for preparing ability schemas in REST responses.
     39         *
     40         * Keyword names are stored as keys so they can be matched with
     41         * array_intersect_key(). Computed lazily on first use and reused while
     42         * preparing nested schemas.
     43         *
     44         * @since 7.1.0
     45         * @var array<string, true>
     46         */
     47        private array $allowed_schema_keyword_lookup;
    3648
    3749        /**
    … …  
    195207
    196208        /**
    197          * Additional schema keywords to preserve in REST responses.
    198          *
    199          * Ability schemas are exposed to clients as JSON Schema. Preserve additional
    200          * draft-04 keywords so clients can validate richer schemas, even when some
    201          * of those keywords are not enforced by the server-side REST schema validator.
    202          *
    203          * @since 7.1.0
    204          * @var string[]
    205          */
    206         private const ADDITIONAL_ALLOWED_SCHEMA_KEYWORDS = array(
    207                 'required',
    208                 'allOf',
    209                 'not',
    210                 '$ref',
    211                 'definitions',
    212                 'dependencies',
    213                 'additionalItems',
    214         );
    215 
    216         /**
    217209         * Determines whether the value is an associative array.
    218210         *
    … …  
    226218        private function is_associative_array( $value ): bool {
    227219                return is_array( $value ) && ! wp_is_numeric_array( $value );
     220        }
     221
     222        /**
     223         * Gets the allowed schema keywords for preparing ability schemas in REST responses.
     224         *
     225         * Uses the fuller draft-04 keyword set, not the smaller REST API subset.
     226         * The published schema is consumed by clients that re-validate values
     227         * against standard draft-04, so it keeps the keywords those validators
     228         * expect.
     229         *
     230         * @since 7.1.0
     231         *
     232         * @return array<string, true> Allowed schema keywords.
     233         */
     234        private function get_allowed_schema_keywords_for_response(): array {
     235                if ( ! isset( $this->allowed_schema_keyword_lookup ) ) {
     236                        $this->allowed_schema_keyword_lookup = array_fill_keys( wp_get_json_schema_allowed_keywords( 'draft-04' ), true );
     237                }
     238
     239                return $this->allowed_schema_keyword_lookup;
    228240        }
    229241
    … …  
    259271                }
    260272
    261                 // Computed once and reused across the recursive calls for every schema node.
    262                 static $allowed_keywords = null;
    263                 $allowed_keywords      ??= array_fill_keys(
    264                         array_merge(
    265                                 rest_get_allowed_schema_keywords(),
    266                                 self::ADDITIONAL_ALLOWED_SCHEMA_KEYWORDS
    267                         ),
    268                         true
    269                 );
    270 
    271                 $schema = array_intersect_key( $schema, $allowed_keywords );
     273                $schema = array_intersect_key( $schema, $this->get_allowed_schema_keywords_for_response() );
    272274
    273275                // Collect draft-03 per-property `required: true` flags into a draft-04
  • trunk/src/wp-settings.php

    r62547 r62549  
    314314require ABSPATH . WPINC . '/abilities.php';
    315315require ABSPATH . WPINC . '/rest-api.php';
     316require ABSPATH . WPINC . '/json-schema.php';
    316317require ABSPATH . WPINC . '/rest-api/class-wp-rest-server.php';
    317318require ABSPATH . WPINC . '/rest-api/class-wp-rest-response.php';
  • trunk/tests/phpunit/tests/abilities-api/wpRegisterCoreAbilities.php

    r62426 r62549  
    386386         */
    387387        public function test_core_abilities_schemas_use_only_valid_keywords(): void {
    388                 $allowed_keywords = rest_get_allowed_schema_keywords();
    389                 // Add 'required' which is valid at the property level for draft-04.
    390                 $allowed_keywords[] = 'required';
     388                $allowed_keywords = wp_get_json_schema_allowed_keywords( 'draft-04' );
    391389
    392390                $abilities = wp_get_abilities();
  • trunk/tests/phpunit/tests/rest-api/rest-server.php

    r62205 r62549  
    25212521
    25222522        /**
     2523         * @ticket 64955
     2524         */
     2525        public function test_get_data_for_route_includes_filtered_json_schema_keywords() {
     2526                $filter = static function ( $keywords, $schema_profile ) {
     2527                        if ( 'rest-api' === $schema_profile ) {
     2528                                $keywords[] = 'xRestApiKeyword';
     2529                        }
     2530
     2531                        return $keywords;
     2532                };
     2533
     2534                add_filter( 'wp_json_schema_allowed_keywords', $filter, 10, 2 );
     2535
     2536                register_rest_route(
     2537                        'test-ns/v1',
     2538                        '/test',
     2539                        array(
     2540                                'methods'             => 'POST',
     2541                                'callback'            => static function () {
     2542                                        return new WP_REST_Response( 'test' );
     2543                                },
     2544                                'permission_callback' => '__return_true',
     2545                                'args'                => array(
     2546                                        'param' => array(
     2547                                                'type'            => 'string',
     2548                                                'xRestApiKeyword' => true,
     2549                                                'invalid'         => true,
     2550                                        ),
     2551                                ),
     2552                        )
     2553                );
     2554
     2555                $response = rest_do_request( new WP_REST_Request( 'OPTIONS', '/test-ns/v1/test' ) );
     2556
     2557                $args = $response->get_data()['endpoints'][0]['args'];
     2558
     2559                $this->assertArrayHasKey( 'xRestApiKeyword', $args['param'] );
     2560                $this->assertArrayNotHasKey( 'invalid', $args['param'] );
     2561        }
     2562
     2563        /**
    25232564         * @ticket 53056
    25242565         */
Note: See TracChangeset for help on using the changeset viewer.