#62287 closed defect (bug) (fixed)
WP_REST_Posts_Controller::prepare_items_query() - Warning $prepared_args can be null
| Reported by: |
|
Owned by: |
|
|---|---|---|---|
| Milestone: | 7.0 | Priority: | normal |
| Severity: | normal | Version: | 4.7 |
| Component: | REST API | Keywords: | has-patch |
| Focuses: | Cc: |
Description
In a project I maintain, we are using sentry, and I've received a warning.
Warning: foreach() argument must be of type array|object, null given
For:
/wp/wp-includes/rest-api/endpoints/class-wp-rest-posts-controller.php in WP_REST_Posts_Controller::prepare_items_query at line 1124
This happens for GET request on /wp-json/wp/vs/posts with the query parameters order, orderby and per_page
I could not yet figure out, why null was given, but I propose to add a is_array() condition around the loop. This will work as intended, without possibly throwing errors as a type hint in the function header woul.d
I will prepare a PR on github with a proposed solution, that will ensure, this warning will not show up.
Change History (10)
This ticket was mentioned in PR #7625 on WordPress/wordpress-develop by @apermo.
17 months ago
#1
- Keywords has-patch added
#2
@
3 weeks ago
- Version set to 4.7
- Posts controller (class-wp-rest-posts-controller.php): The vulnerable foreach ($prepared_args as ...) pattern was introduced in commit 4f685410b2 ("REST API: Remove get_allowed_query_vars() now filter is gone.") — first released in WordPress 4.7.0.
- Revisions controller (class-wp-rest-revisions-controller.php): Introduced in commit b513113c49 ("REST API: Support pagination, order, search and other common query parameters for revisions.") — first released in WordPress 5.0.0.
#3
@
3 weeks ago
thanks for sharing this report @apermo — I can only guess that some custom code implementing the rest_{$this->post_type}_query is returning NULL, which then gets sent into prepare_items_query()
because of that, and because it’s protected/private, I feel like it could be more reasonable to fix the call sites, defaulting NULL values to array() before sending them to the method. why? this is coherent with the existing types, which state that the argument must be an array, and while it’s optional, that does not permit sending a NULL value — only in sending no values.
open to thoughts here. if this were more exposed to plugin and theme authors as part of a public interface I would probably favor adding more guards internally, but in doing so we create a inconsistency in the code between the types and the expectations.
3 weeks ago
#4
In order to see the tests pass I have merged trunk, which includes a fix for broken end-to-end tests.
@westonruter commented on PR #7625:
3 weeks ago
#5
@dmsnell I'm glad to see this. I think your addition is the key one (which I iterated on a bit more in e54f258ea1a23c5be7e89220e8c752a0776bb9e6). The return value of apply_filters() cannot be trusted. If that along addresses the issue, it would seem fine to revert the change to the prepare_items_query methods, which I also amended in 35606cfd2db5ca90e3369007e3a7e7c6abf89f3e to account for other values being passed in, for example an empty string.
With that in place, I'd ideally want to add the type hints to the methods and eliminate the default values, since the params are “always” provided:
-
src/wp-includes/rest-api/endpoints/class-wp-rest-posts-controller.php
diff --git a/src/wp-includes/rest-api/endpoints/class-wp-rest-posts-controller.php b/src/wp-includes/rest-api/endpoints/class-wp-rest-posts-controller.php index 720f122aed..83467201ba 100644
a b class WP_REST_Posts_Controller extends WP_REST_Controller { 1208 1208 * 1209 1209 * @since 4.7.0 1210 1210 * 1211 * @param array $prepared_args Optional.Prepared WP_Query arguments. Default empty array.1212 * @param WP_REST_Request $request Optional.Full details about the request.1211 * @param array $prepared_args Prepared WP_Query arguments. Default empty array. 1212 * @param WP_REST_Request $request Full details about the request. 1213 1213 * @return array Items query arguments. 1214 1214 */ 1215 protected function prepare_items_query( $prepared_args = array(), $request = null ){1215 protected function prepare_items_query( array $prepared_args, WP_REST_Request $request ): array { 1216 1216 $query_args = array(); 1217 if ( is_array( ! $prepared_args ) ) {1218 $prepared_args = array();1219 }1220 1217 1221 1218 foreach ( $prepared_args as $key => $value ) { 1222 1219 /** -
src/wp-includes/rest-api/endpoints/class-wp-rest-revisions-controller.php
diff --git a/src/wp-includes/rest-api/endpoints/class-wp-rest-revisions-controller.php b/src/wp-includes/rest-api/endpoints/class-wp-rest-revisions-controller.php index cc483edacf..20a050a6e8 100644
a b class WP_REST_Revisions_Controller extends WP_REST_Controller { 546 546 * 547 547 * @since 5.0.0 548 548 * 549 * @param array $prepared_args Optional.Prepared WP_Query arguments. Default empty array.550 * @param WP_REST_Request $request Optional.Full details about the request.549 * @param array $prepared_args Prepared WP_Query arguments. Default empty array. 550 * @param WP_REST_Request $request Full details about the request. 551 551 * @return array Items query arguments. 552 552 */ 553 protected function prepare_items_query( $prepared_args = array(), $request = null ){553 protected function prepare_items_query( array $prepared_args, WP_REST_Request $request ): array { 554 554 $query_args = array(); 555 if ( is_array( ! $prepared_args ) ) {556 $prepared_args = array();557 }558 555 559 556 foreach ( $prepared_args as $key => $value ) { 560 557 /** This filter is documented in wp-includes/rest-api/endpoints/class-wp-rest-posts-controller.php */
However, this does not account for subclasses which may be calling this protected method. So if we did that, we should do so early in a release cycle to ensure plugins which extend these classes are passing in the proper values as we've done in this PR with type checking the filter return values. But since a subclass may be also using a filter for the $prepared_args param being passed in to prepare_items_query() then it seems safest to add the type checking inside method as well since we didn't add the type hint in the first place back in 5.0.
2 weeks ago
#9
Thanks @westonruter — I had a commit prepared but had been stalled by some flakey PHP Unit Tests and was trying to wait until they passed. Appreciate the teamwork.
@westonruter commented on PR #7625:
2 weeks ago
#10
@dmsnell oh! Sorry if I stepped on your toes. 🤝
As discribed in the track ticket, it is possible that
prepare_items_query()can receive a null value as$prepared_argswhich results in a warning. This fixes this issue.Trac ticket: https://core.trac.wordpress.org/ticket/62287