Opened 7 weeks ago
Last modified 7 weeks ago
#65953 new defect (bug)
Fatal PHP error tested on PHP8.2 & 8.3 inside the file class-wp-rest-server.php (Can be fixed and improved)
| Reported by: | mdesignfa | Owned by: | |
|---|---|---|---|
| Priority: | normal | Milestone: | Awaiting Review |
| Component: | REST API | Version: | |
| Severity: | normal | Keywords: | has-patch has-unit-tests 2nd-opinion reporter-feedback |
| Cc: | Focuses: | rest-api, coding-standards, php-compatibility |
Description (last modified by )
Hello and best regards.
I am a WordPress plugin developer. I was working on a plugin when, following a PHP update from version 7.4 to 8.2 (or higher), I encountered a strange error:
Fatal error: Uncaught TypeError: array_merge(): Argument #1 must be of type array, null given in /.../wp-includes/rest-api/class-wp-rest-server.php:1166 Stack trace: #0 /.../wp-includes/rest-api/class-wp-rest-server.php(1166): array_merge(NULL)
In fact, line 1166 of the class-wp-rest-server.php file attempts to perform an array_merge without considering the variable's value or applying any specific validation; I resolved the error quite simply with a minor change:
Original: $routes = array_merge( ...$with_namespace );
Edited (Fixed):
//Fixed by MDesign
foreach ( $with_namespace as $namespace_routes ) {
if ( is_array( $namespace_routes ) ) {
$routes = array_merge( $routes, $namespace_routes );
}
}
/**/
Attachments (3)
Change History (7)
#2
@
7 weeks ago
The trace confirms the fatal is downstream of a bad filter, not of match_request_to_handler().
The request is /elementor/v1/globals/colors, dispatched internally by Elementor through rest_do_request(). Only that one namespace matches, which is why the trace shows array_merge(NULL) with a single argument. $with_namespace can only hold null if WP_REST_Server::get_routes() returned null, and that can only come from one line:
<?php $endpoints = apply_filters( 'rest_endpoints', $endpoints );
$this->endpoints is always an array. wp_list_filter() always returns an array. So a callback on rest_endpoints is returning null. This is usually a callback that unsets an endpoint and forgets to return $endpoints. Grepping wp-content for rest_endpoints should find it. The same null also raises a foreach() argument must be of type array|object, null given warning inside get_routes(), just before the fatal.
On PHP 7.4, array_merge( null ) only raised a warning and returned null. That is why the upgrade looks like the cause.
About the attached patch. It is a single change, and $routes has no prior assignment in that method. So it stays null, and array_merge( $routes, $namespace_routes ) throws the same TypeError. If every entry is skipped, the foreach ( $routes as $route => $handlers ) below warns instead. The failure only moves. It also swaps one spread array_merge() for a repeated merge on every REST request.
I am working on a fix and will share a patch once I have confirmed it.
This ticket was mentioned in PR #13254 on WordPress/wordpress-develop by @iamchitti.
7 weeks ago
#3
- Keywords has-patch has-unit-tests added
### Ticket
Trac ticket: https://core.trac.wordpress.org/ticket/65953
### Description
WP_REST_Server::get_routes() documents an array return, but does not validate what the rest_endpoints filter gives back. A callback that forgets to return $endpoints makes it return null, which reaches array_merge( ...$with_namespace ) in match_request_to_handler() and throws a fatal TypeError on PHP 8. On PHP 7.4 it only warned, so it looks like an upgrade regression.
The fix validates the filtered value in get_routes(), where the bad value originates, so every caller is covered. A non-array falls back to an empty array and calls _doing_it_wrong() to surface the offending callback. Dispatch then returns a normal rest_no_route 404.
Unit tests are included.
### Testing instructions
- Add a broken filter, e.g.
add_filter( 'rest_endpoints', function ( $endpoints ) { unset( $endpoints['/wp/v2/users'] ); } ); - Request any REST route on
trunkand observe the fatalTypeError. - With the patch, the request returns a
rest_no_route404 plus a_doing_it_wrong()notice forWP_REST_Server::get_routes.
## Use of AI Tools
AI assistance: Yes
Tool(s): Claude Code
Model(s): Claude Opus 5
Used for: drafting the fix and unit tests
#4
@
7 weeks ago
- Keywords 2nd-opinion reporter-feedback added
I'm not sure that PR #13254 is the right solution here. The reality is that filter callbacks almost everywhere in core do not have their return type validated. It would not be scalable to add such _doing_it_wrong() checks everywhere. This is what #51525 is about.
The rest_endpoints filter is documented as being passed an array and this is the value it is expected to return. Anything else is a plugin error. But adding runtime checks for all such cases, again, is not scalable.
I believe this should be reported as a bug to Elementor instead.
![(please configure the [header_logo] section in trac.ini)](/chrome/site/your_project_logo.png)
error-logs