Make WordPress Core


Ignore:
Timestamp:
10/19/2026 05:39:57 PM (33 hours ago)
Author:
westonruter
Message:

Code Quality: Narrow types for REST requests and responses.

Adds array shapes and narrowed types to WP_HTTP_Response, WP_REST_Request and WP_REST_Response, and to the parts of WP_REST_Server that pass data to them, reusing the route handler and endpoint argument aliases the server already defines. Several existing types were wrong rather than merely loose, and are corrected:

  • A request's body is null until one is set.
  • A request's JSON params decode to a scalar for a JSON body that is not an object or array.
  • A request's headers hold lists of values.
  • A file param may describe a failed upload, or a field named with brackets such as files[].

The status code is typed int<100, 599>, and absint() gains a template conditional return so that an argument already known to be a non-negative integer keeps its narrower type through it. The one call where that type cannot be inferred, in the deprecated numeric branch of wp_get_recent_posts(), is added to the PHPStan baseline. The no-cache check for a 4xx response to a method-overridden request now compares the status numerically instead of passing an integer to str_starts_with(), which removes another baseline entry, and a test now covers that path.

The narrower types also expose existing bugs that are left for follow-up: a scalar JSON body makes WP_REST_Request::set_param() fatal and WP_REST_Request::sanitize_params() warn, and uploading to the media endpoint with a file[] field makes WP_REST_Attachments_Controller::create_item() throw a TypeError.

Developed in ​https://github.com/WordPress/wordpress-develop/pull/14471.
Follow-up to r34928, r56834, r57126, r62618, r63646, r63756.

Props westonruter, apermo.
See #65817.

File:
1 edited

Legend:

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

    r64490 r64527  
    2121 * @since 4.4.0
    2222 *
     23 * @phpstan-import-type Target_Hints from WP_REST_Response
     24 *
    2325 * @phpstan-type Endpoint_Arg array{
    2426 *     required?: bool,
    … …  
    3335 *     callback?: callable|null,
    3436 *     permission_callback?: callable|null,
     37 *     validate_callback?: callable|null,
    3538 *     args: array<non-empty-string, Endpoint_Arg>,
    3639 *     accept_json: bool,
    … …  
    271274         * @param int|null $status  Optional. HTTP status code to send. Default null.
    272275         * @return string JSON representation of the error.
     276         *
     277         * @phpstan-param int<100, 599>|null $status
    273278         */
    274279        protected function json_error( $code, $message, $status = null ) {
    … …  
    532537                 * OR if the HTTP_X_HTTP_METHOD_OVERRIDE is used but resulted a 4xx response code.
    533538                 */
    534                 if ( $send_no_cache_headers || ( true === $method_overridden && str_starts_with( $code, '4' ) ) ) {
     539                if ( $send_no_cache_headers || ( true === $method_overridden && $code >= 400 && $code < 500 ) ) {
    535540                        foreach ( wp_get_nocache_headers() as $header => $header_value ) {
    536541                                if ( empty( $header_value ) ) {
    … …  
    702707         * @since 6.7.0
    703708         *
    704          * @param array $link The link to get target hints for.
    705          * @return array|null
     709         * @param array<string, mixed> $link The link to get target hints for.
     710         * @return array<string, string[]>|null Map of hint name to list of values, which is empty if the route
     711         *                                      allows no methods. Null if the link already has an `allow` hint,
     712         *                                      or its URL does not resolve to a valid REST API request.
     713         *
     714         * @phpstan-param array{
     715         *     href: non-falsy-string,
     716         *     targetHints?: Target_Hints,
     717         *     ...<string, mixed>
     718         * } $link
     719         * @phpstan-return array<lowercase-string, list<string>>|null
    706720         */
    707721        protected static function get_target_hints_for_link( $link ) {
    … …  
    12141228         *
    12151229         * @param WP_REST_Request $request The request object.
    1216          * @return array|WP_Error The route and request handler on success or a WP_Error instance if no handler was found.
     1230         * @return array<int, string|array<string, mixed>>|WP_Error The route and request handler on success or a WP_Error instance if no handler was found.
     1231         *
     1232         * @phpstan-return array{non-empty-string, Route_Handler}|WP_Error
    12171233         */
    12181234        protected function match_request_to_handler( $request ) {
    … …  
    12951311         * @since 5.6.0
    12961312         *
    1297          * @param WP_REST_Request $request  The request object.
    1298          * @param string          $route    The matched route regex.
    1299          * @param array          $handler  The matched route handler.
    1300          * @param WP_Error|null   $response The current error object if any.
     1313         * @param WP_REST_Request      $request  The request object.
     1314         * @param string               $route    The matched route regex.
     1315         * @param array<string, mixed> $handler  The matched route handler.
     1316         * @param WP_Error|null        $response The current error object if any.
    13011317         * @return WP_REST_Response
     1318         *
     1319         * @phpstan-param Route_Handler $handler
    13021320         */
    13031321        protected function respond_to_request( $request, $route, $handler, $response ) {
    … …  
    13161334                 * @param WP_REST_Response|WP_HTTP_Response|WP_Error|mixed $response Result to send to the client.
    13171335                 *                                                                   Usually a WP_REST_Response or WP_Error.
    1318                  * @param array                                            $handler  Route handler used for the request.
     1336                 * @param array<string, mixed>                             $handler  Route handler used for the request.
    13191337                 * @param WP_REST_Request                                  $request  Request used to generate the response.
     1338                 *
     1339                 * @phpstan-param Route_Handler $handler
    13201340                 */
    13211341                $response = apply_filters( 'rest_request_before_callbacks', $response, $handler, $request );
    … …  
    13451365                         * @since 4.5.0 Added `$route` and `$handler` parameters.
    13461366                         *
    1347                          * @param mixed           $dispatch_result Dispatch result, will be used if not empty.
    1348                          * @param WP_REST_Request $request         Request used to generate the response.
    1349                          * @param string          $route           Route matched for the request.
    1350                          * @param array           $handler         Route handler used for the request.
     1367                         * @param mixed                $dispatch_result Dispatch result, will be used if not empty.
     1368                         * @param WP_REST_Request      $request         Request used to generate the response.
     1369                         * @param string               $route           Route matched for the request.
     1370                         * @param array<string, mixed> $handler         Route handler used for the request.
     1371                         *
     1372                         * @phpstan-param Route_Handler $handler
    13511373                         */
    13521374                        $dispatch_result = apply_filters( 'rest_dispatch_request', null, $request, $route, $handler );
    … …  
    13781400                 * @param WP_REST_Response|WP_HTTP_Response|WP_Error|mixed $response Result to send to the client.
    13791401                 *                                                                   Usually a WP_REST_Response or WP_Error.
    1380                  * @param array                                            $handler  Route handler used for the request.
     1402                 * @param array<string, mixed>                             $handler  Route handler used for the request.
    13811403                 * @param WP_REST_Request                                  $request  Request used to generate the response.
     1404                 *
     1405                 * @phpstan-param Route_Handler $handler
    13821406                 */
    13831407                $response = apply_filters( 'rest_request_after_callbacks', $response, $handler, $request );
    … …  
    19882012         *
    19892013         * @param int $code HTTP status.
     2014         *
     2015         * @phpstan-param int<100, 599> $code
    19902016         */
    19912017        protected function set_status( $code ) {
    … …  
    20182044         * @since 4.4.0
    20192045         *
    2020          * @param array $headers Map of header name to header value.
     2046         * @param array<string, string> $headers Map of header name to header value.
    20212047         */
    20222048        public function send_headers( $headers ) {
Note: See TracChangeset for help on using the changeset viewer.