Make WordPress Core

Opened 4 years ago

Closed 4 years ago

#50786 closed defect (bug) (fixed)

REST API: delete post from block editor triggers a warning

Reported by: manooweb's profile manooweb Owned by: timothyblynjacobs's profile TimothyBlynJacobs
Milestone: 5.5 Priority: normal
Severity: normal Version: 5.5
Component: REST API Keywords: has-patch has-unit-tests dev-reviewed commit
Focuses: Cc:

Description

Hello,

Tested with WordPress 5.5-beta3-48630

When I tried to delete a post from the block editor by using the "Move to trash" link, I obtained a warning in the debug log file with this stack trace.

[27-Jul-2020 14:15:56 UTC] PHP Warning:  array_key_exists() expects parameter 2 to be array, null given in C:\wamp64\www\wpbeta\wp-includes\rest-api\class-wp-rest-request.php on line 436
[27-Jul-2020 14:15:56 UTC] PHP Stack trace:
[27-Jul-2020 14:15:56 UTC] PHP   1. {main}() C:\wamp64\www\wpbeta\index.php:0
[27-Jul-2020 14:15:56 UTC] PHP   2. require() C:\wamp64\www\wpbeta\index.php:17
[27-Jul-2020 14:15:56 UTC] PHP   3. wp($query_vars = *uninitialized*) C:\wamp64\www\wpbeta\wp-blog-header.php:16
[27-Jul-2020 14:15:56 UTC] PHP   4. WP->main($query_args = *uninitialized*) C:\wamp64\www\wpbeta\wp-includes\functions.php:1285
[27-Jul-2020 14:15:56 UTC] PHP   5. WP->parse_request($extra_query_vars = *uninitialized*) C:\wamp64\www\wpbeta\wp-includes\class-wp.php:745
[27-Jul-2020 14:15:56 UTC] PHP   6. do_action_ref_array($tag = *uninitialized*, $args = *uninitialized*) C:\wamp64\www\wpbeta\wp-includes\class-wp.php:388
[27-Jul-2020 14:15:56 UTC] PHP   7. WP_Hook->do_action($args = *uninitialized*) C:\wamp64\www\wpbeta\wp-includes\plugin.php:544
[27-Jul-2020 14:15:56 UTC] PHP   8. WP_Hook->apply_filters($value = *uninitialized*, $args = *uninitialized*) C:\wamp64\www\wpbeta\wp-includes\class-wp-hook.php:311
[27-Jul-2020 14:15:56 UTC] PHP   9. rest_api_loaded(*uninitialized*) C:\wamp64\www\wpbeta\wp-includes\class-wp-hook.php:287
[27-Jul-2020 14:15:56 UTC] PHP  10. WP_REST_Server->serve_request($path = *uninitialized*) C:\wamp64\www\wpbeta\wp-includes\rest-api.php:338
[27-Jul-2020 14:15:56 UTC] PHP  11. WP_REST_Server->dispatch($request = *uninitialized*) C:\wamp64\www\wpbeta\wp-includes\rest-api\class-wp-rest-server.php:376
[27-Jul-2020 14:15:56 UTC] PHP  12. WP_REST_Posts_Controller->delete_item($request = *uninitialized*) C:\wamp64\www\wpbeta\wp-includes\rest-api\class-wp-rest-server.php:1050
[27-Jul-2020 14:15:56 UTC] PHP  13. WP_REST_Request->set_param($key = *uninitialized*, $value = *uninitialized*) C:\wamp64\www\wpbeta\wp-includes\rest-api\endpoints\class-wp-rest-posts-controller.php:906

Indeed the code of the WP_REST_Request::set_param method changed in WordPress 5.5 from WordPress 5.4

in 5.4

	public function set_param( $key, $value ) {
		$order                             = $this->get_parameter_order();
		$this->params[ $order[0] ][ $key ] = $value;
	}

https://github.com/WordPress/WordPress/blob/5.4-branch/wp-includes/rest-api/class-wp-rest-request.php#L428-L431

in 5.5

	public function set_param( $key, $value ) {
		$order     = $this->get_parameter_order();
		$found_key = false;

		foreach ( $order as $type ) {
			if ( 'defaults' !== $type && array_key_exists( $key, $this->params[ $type ] ) ) {
				$this->params[ $type ][ $key ] = $value;
				$found_key                     = true;
			}
		}

		if ( ! $found_key ) {
			$this->params[ $order[0] ][ $key ] = $value;
		}
	}

https://github.com/WordPress/WordPress/blob/master/wp-includes/rest-api/class-wp-rest-request.php#L431-L445

At line 436 where the warning is triggered, the JSON key isn't initialized as an array as expected by the array_key_exists function but is null which causes the issue.

Change History (6)

#1 @TimothyBlynJacobs
4 years ago

  • Milestone changed from Awaiting Review to 5.5
  • Owner set to TimothyBlynJacobs
  • Status changed from new to assigned

Thanks for the report @manooweb! I was able to replicate this. I'm trying to figure out how that can happen as each parameter is initialized to an empty array in WP_REST_Request::__construct.

#2 @TimothyBlynJacobs
4 years ago

I think I figured this out, it looks like this happens because the delete request is sent with an application/json content type header but an empty body. This causes WP_REST_Request::parse_json_params() to bail out early before setting the JSON parameter. It seems odd to me that we don't default JSON to an empty array if it is a JSON request considering we do do that for the other parameter types, but I think making that change would be too large to do at this point.

I'm adding a patch that will add safety checks in set_param().

This ticket was mentioned in PR #434 on WordPress/wordpress-develop by TimothyBJacobs.


4 years ago
#3

  • Keywords has-patch has-unit-tests added

#4 @TimothyBlynJacobs
4 years ago

For reference, the behavior was changed in #40838.

#5 @whyisjake
4 years ago

  • Keywords dev-reviewed commit added

#6 @TimothyBlynJacobs
4 years ago

  • Resolution set to fixed
  • Status changed from assigned to closed

In 48642:

REST API: Fix warning when using set_param() on a JSON request with no body.

In [47559] the WP_REST_Request::set_param() method was adjusted to try and overwrite an existing parameter definition before forcing the value in the first parameter slot. If set_param() was called on a request with an application/json content type and an empty body, a PHP warning would be issued. This was due to the JSON parameter type not being set to an array when the body is empty.

This commit avoids the warning by adding an is_array() check before calling array_key_exists. Ideally, WP_REST_Reuest::parse_json_params() would set the JSON parameter type to an empty array in this case, but that is too large of a change at this point in the cycle.

Props manooweb.
Fixes #50786.

Note: See TracTickets for help on using tickets.