Opened 11 months ago
Closed 11 months ago
#60585 closed defect (bug) (invalid)
Recursion in WP_REST_Posts_Controller schema
Reported by: | derrickkoo | Owned by: | |
---|---|---|---|
Milestone: | Priority: | normal | |
Severity: | normal | Version: | |
Component: | REST API | Keywords: | needs-testing |
Focuses: | performance | Cc: |
Description
In the schema definition for the WP_REST_Posts_Controller
class, any attribute which invokes a array-like callable with $this
for its validate_callback
or sanitize_callback
option will result in a recursive instance of the WP_REST_Posts_Controller
class to be attached to that schema definition. Here's an example of such a callable that causes this issue (code pasted below; see the value of sanitize_callback
): https://github.com/WordPress/WordPress/blame/master/wp-includes/rest-api/endpoints/class-wp-rest-posts-controller.php#L2288
'slug' => array(
'description' => __( 'An alphanumeric identifier for the post unique to its type.' ),
'type' => 'string',
'context' => array( 'view', 'edit', 'embed' ),
'arg_options' => array(
'sanitize_callback' => array( $this, 'sanitize_slug' ),
),
),
For most sites this doesn't seem to cause any issues, but on sites that register a lot of custom meta on the post
CPT, because each registered meta key adds its own schema attribute, this can result in a huge WP_REST_Posts_Controller
object which can potentially cause an unexpected response to POST
requests to the posts
endpoint. Curiously, this doesn't seem to cause an OOM error in my testing, but instead returns a 403
response with a generic The link you followed has expired
error, which sounds more like an expired nonce error than a memory error.
This can be replicated and seen in a couple of ways:
- Using
wp shell
to instantiate theWP_REST_Posts_Controller
class for theposts
endpoint and fetching its schema:
wp> $controller = new WP_REST_Posts_Controller( 'post' );
wp> $controller->get_item_schema();
The output of get_item_schema()
will return a schema object showing the recursive instances of WP_REST_Posts_Controller
where those callables are defined.
- By making a
POST
request to theposts
endpoint, hooking into therest_insert_post
action hook (see: https://developer.wordpress.org/reference/hooks/rest_insert_this-post_type/) and logging the$request
argument. The['attributes']['args']
property will contain the full schema and show the recursion for any schema attribute invoking a callable using$this
.
Potential (untested) solution: instead of invoking callables using $this
, convert the callable methods to static methods on the WP_REST_Posts_Controller
class or its parent, and invoke using [ __CLASS__, 'method_name' ]
to avoid recursion.
Tested with v6.4.3 and v5.9.9 so far.
Change History (4)
This ticket was mentioned in Slack in #core-performance by mukeshpanchal27. View the logs.
11 months ago
#3
@
11 months ago
@johnbillion thanks for the response! On our end we haven't been able to confirm whether the 403 was truly a result of this recursion, and after some more testing we believe the recursion isn't actually consuming memory, as you state. Let's park this for now until I can confirm whether there's a true connection between this issue and the 403 response.
Thanks for the ticket @derrickkoo and welcome to WordPress Trac.
An object that contains deeply nested or fully recursive references doesn't necessarily consume a lot of memory because each of those objecss will be a reference to an instance rather than a copy (this is what allows a fully recursive property to exist on an object without exhausting all the available memory).
I think we'll need some more info from you in order to investigate if there's an underlying problem and what its impact is.