WordPress.org

Make WordPress Core

Changeset 46272


Ignore:
Timestamp:
09/23/2019 08:24:59 PM (3 months ago)
Author:
kadamwhite
Message:

REST API: Introduce WP_Post_Type::get_rest_controller() caching method to prevent unnecessary REST controller construction.

Cache REST controller references on their associated post type object to prevent unnecessary controller re-instantiation, which previously caused "rest_prepare_{$post_type}" and "rest_{$post_type}_query" to run twice per request.

Props TimothyBlynJacobs, patrelentlesstechnologycom.
Fixes #45677.

Location:
trunk
Files:
7 edited

Legend:

Unmodified
Added
Removed
  • trunk/src/wp-includes/class-wp-post-type.php

    r46160 r46272  
    335335     */
    336336    public $rest_controller_class;
     337
     338    /**
     339     * The controller instance for this post type's REST API endpoints.
     340     *
     341     * Lazily computed. Should be accessed using {@see WP_Post_Type::get_rest_controller()}.
     342     *
     343     * @since 5.3.0
     344     * @var WP_REST_Controller $rest_controller
     345     */
     346    private $rest_controller;
    337347
    338348    /**
     
    683693        remove_action( 'future_' . $this->name, '_future_post_hook', 5 );
    684694    }
     695
     696    /**
     697     * Gets the REST API controller for this post type.
     698     *
     699     * Will only instantiate the controller class once per request.
     700     *
     701     * @since 5.3.0
     702     *
     703     * @return WP_REST_Controller|null The controller instance, or null if the post type
     704     *                                 is set not to show in rest.
     705     */
     706    public function get_rest_controller() {
     707        if ( ! $this->show_in_rest ) {
     708            return null;
     709        }
     710
     711        $class = $this->rest_controller_class ? $this->rest_controller_class : WP_REST_Posts_Controller::class;
     712
     713        if ( ! class_exists( $class ) ) {
     714            return null;
     715        }
     716
     717        if ( ! is_subclass_of( $class, WP_REST_Controller::class ) ) {
     718            return null;
     719        }
     720
     721        if ( ! $this->rest_controller ) {
     722            $this->rest_controller = new $class( $this->name );
     723        }
     724
     725        return $this->rest_controller;
     726    }
    685727}
  • trunk/src/wp-includes/rest-api.php

    r46249 r46272  
    193193function create_initial_rest_routes() {
    194194    foreach ( get_post_types( array( 'show_in_rest' => true ), 'objects' ) as $post_type ) {
    195         $class = ! empty( $post_type->rest_controller_class ) ? $post_type->rest_controller_class : 'WP_REST_Posts_Controller';
    196 
    197         if ( ! class_exists( $class ) ) {
    198             continue;
    199         }
    200         $controller = new $class( $post_type->name );
    201         if ( ! is_subclass_of( $controller, 'WP_REST_Controller' ) ) {
     195        $controller = $post_type->get_rest_controller();
     196
     197        if ( ! $controller ) {
    202198            continue;
    203199        }
  • trunk/src/wp-includes/rest-api/endpoints/class-wp-rest-autosaves-controller.php

    r46088 r46272  
    6060        $this->parent_post_type = $parent_post_type;
    6161        $post_type_object       = get_post_type_object( $parent_post_type );
    62 
    63         // Ensure that post type-specific controller logic is available.
    64         $parent_controller_class = ! empty( $post_type_object->rest_controller_class ) ? $post_type_object->rest_controller_class : 'WP_REST_Posts_Controller';
    65 
    66         $this->parent_controller    = new $parent_controller_class( $post_type_object->name );
     62        $parent_controller      = $post_type_object->get_rest_controller();
     63
     64        if ( ! $parent_controller ) {
     65            $parent_controller = new WP_REST_Posts_Controller( $parent_post_type );
     66        }
     67
     68        $this->parent_controller    = $parent_controller;
    6769        $this->revisions_controller = new WP_REST_Revisions_Controller( $parent_post_type );
    6870        $this->rest_namespace       = 'wp/v2';
  • trunk/src/wp-includes/rest-api/endpoints/class-wp-rest-comments-controller.php

    r45932 r46272  
    15931593     */
    15941594    protected function check_read_post_permission( $post, $request ) {
    1595         $posts_controller = new WP_REST_Posts_Controller( $post->post_type );
    15961595        $post_type        = get_post_type_object( $post->post_type );
     1596        $posts_controller = $post_type->get_rest_controller();
     1597
     1598        // Ensure the posts controller is specifically a WP_REST_Posts_Controller instance
     1599        // before using methods specific to that controller.
     1600        if ( ! $posts_controller instanceof WP_REST_Posts_Controller ) {
     1601            $posts_controller = new WP_REST_Posts_Controller( $post->post_type );
     1602        }
    15971603
    15981604        $has_password_filter = false;
  • trunk/src/wp-includes/rest-api/endpoints/class-wp-rest-posts-controller.php

    r46252 r46272  
    1616 */
    1717class WP_REST_Posts_Controller extends WP_REST_Controller {
     18
     19    /**
     20     * Instances of post type controllers keyed by post type.
     21     *
     22     * @since 5.3.0
     23     * @var WP_REST_Controller[]
     24     */
     25    private static $post_type_controllers = array();
    1826
    1927    /**
  • trunk/src/wp-includes/rest-api/endpoints/class-wp-rest-revisions-controller.php

    r46069 r46272  
    5050    public function __construct( $parent_post_type ) {
    5151        $this->parent_post_type  = $parent_post_type;
    52         $this->parent_controller = new WP_REST_Posts_Controller( $parent_post_type );
    5352        $this->namespace         = 'wp/v2';
    5453        $this->rest_base         = 'revisions';
    5554        $post_type_object        = get_post_type_object( $parent_post_type );
    5655        $this->parent_base       = ! empty( $post_type_object->rest_base ) ? $post_type_object->rest_base : $post_type_object->name;
     56        $this->parent_controller = $post_type_object->get_rest_controller();
     57
     58        if ( ! $this->parent_controller ) {
     59            $this->parent_controller = new WP_REST_Posts_Controller( $parent_post_type );
     60        }
    5761    }
    5862
  • trunk/tests/phpunit/tests/rest-api/rest-posts-controller.php

    r46252 r46272  
    45274527    }
    45284528
     4529    /**
     4530     * @ticket 45677
     4531     */
     4532    public function test_get_for_post_type_reuses_same_instance() {
     4533        $this->assertSame(
     4534            get_post_type_object( 'post' )->get_rest_controller(),
     4535            get_post_type_object( 'post' )->get_rest_controller()
     4536        );
     4537    }
     4538
     4539    /**
     4540     * @ticket 45677
     4541     */
     4542    public function test_get_for_post_type_returns_null_if_post_type_does_not_show_in_rest() {
     4543        register_post_type(
     4544            'not_in_rest',
     4545            array(
     4546                'show_in_rest' => false,
     4547            )
     4548        );
     4549
     4550        $this->assertNull( get_post_type_object( 'not_in_rest' )->get_rest_controller() );
     4551    }
     4552
     4553    /**
     4554     * @ticket 45677
     4555     */
     4556    public function test_get_for_post_type_returns_null_if_class_does_not_exist() {
     4557        register_post_type(
     4558            'class_not_found',
     4559            array(
     4560                'show_in_rest'          => true,
     4561                'rest_controller_class' => 'Class_That_Does_Not_Exist',
     4562            )
     4563        );
     4564
     4565        $this->assertNull( get_post_type_object( 'class_not_found' )->get_rest_controller() );
     4566    }
     4567
     4568    /**
     4569     * @ticket 45677
     4570     */
     4571    public function test_get_for_post_type_returns_null_if_class_does_not_subclass_rest_controller() {
     4572        register_post_type(
     4573            'invalid_class',
     4574            array(
     4575                'show_in_rest'          => true,
     4576                'rest_controller_class' => 'WP_Post',
     4577            )
     4578        );
     4579
     4580        $this->assertNull( get_post_type_object( 'invalid_class' )->get_rest_controller() );
     4581    }
     4582
     4583    /**
     4584     * @ticket 45677
     4585     */
     4586    public function test_get_for_post_type_returns_posts_controller_if_custom_class_not_specified() {
     4587        register_post_type(
     4588            'test',
     4589            array(
     4590                'show_in_rest' => true,
     4591            )
     4592        );
     4593
     4594        $this->assertInstanceOf(
     4595            WP_REST_Posts_Controller::class,
     4596            get_post_type_object( 'test' )->get_rest_controller()
     4597        );
     4598    }
     4599
     4600    /**
     4601     * @ticket 45677
     4602     */
     4603    public function test_get_for_post_type_returns_provided_controller_class() {
     4604        $this->assertInstanceOf(
     4605            WP_REST_Blocks_Controller::class,
     4606            get_post_type_object( 'wp_block' )->get_rest_controller()
     4607        );
     4608    }
     4609
    45294610    public function tearDown() {
    45304611        _unregister_post_type( 'private-post' );
Note: See TracChangeset for help on using the changeset viewer.