Make WordPress Core

Changeset 57315


Ignore:
Timestamp:
01/19/2024 08:52:06 PM (5 months ago)
Author:
joemcgill
Message:

Editor: Support deferred block variation initialization on the server.

When registering blocks on the server using register_block_type() or similar functions, a set of block type variations can also be registered. However, in some cases building this variation data during block registration can be an expensive process, which is not needed in most contexts.

To address this problem, this adds support to the WP_Block_Type object for a new property, variation_callback, which can be used to register a callback for building variation data only when the block variations data is needed. The WP_Block_Type::variations property has been changed to a private property that is now accessed through the magic __get() method. The magic getter makes use of a new public method, WP_Block_Type::get_variations which will build variations from a registered callback if variations have not already been built.

Props spacedmonkey, thekt12, Mamaduka, gaambo, gziolo, mukesh27, joemcgill.
Fixes #59969.

Location:
trunk
Files:
3 edited

Legend:

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

    r57140 r57315  
    114114     *
    115115     * @since 5.8.0
    116      * @var array[]
    117      */
    118     public $variations = array();
     116     * @since 6.5.0 Only accessible through magic getter. null by default.
     117     * @var array[]|null
     118     */
     119    private $variations = null;
     120
     121    /**
     122     * Block variations callback.
     123     *
     124     * @since 6.5.0
     125     * @var callable|null
     126     */
     127    public $variation_callback = null;
    119128
    120129    /**
     
    297306     *     @type array|null    $example                  Structured data for the block preview.
    298307     *     @type callable|null $render_callback          Block type render callback.
     308     *     @type callable|null $variation_callback       Block type variations callback.
    299309     *     @type array|null    $attributes               Block type attributes property schemas.
    300310     *     @type string[]      $uses_context             Context values inherited by blocks of this type.
     
    326336     */
    327337    public function __get( $name ) {
     338        if ( 'variations' === $name ) {
     339            return $this->get_variations();
     340        }
     341
    328342        if ( ! in_array( $name, $this->deprecated_properties, true ) ) {
    329343            return;
     
    354368     */
    355369    public function __isset( $name ) {
     370        if ( 'variations' === $name ) {
     371            return true;
     372        }
     373
    356374        if ( ! in_array( $name, $this->deprecated_properties, true ) ) {
    357375            return false;
     
    373391     */
    374392    public function __set( $name, $value ) {
     393        if ( 'variations' === $name ) {
     394            $this->variations = $value;
     395            return;
     396        }
     397
    375398        if ( ! in_array( $name, $this->deprecated_properties, true ) ) {
    376399            $this->{$name} = $value;
     
    541564            array();
    542565    }
     566
     567    /**
     568     * Get block variations.
     569     *
     570     * @since 6.5.0
     571     *
     572     * @return array[]
     573     */
     574    public function get_variations() {
     575        if ( ! isset( $this->variations ) ) {
     576            $this->variations = array();
     577            if ( is_callable( $this->variation_callback ) ) {
     578                $this->variations = call_user_func( $this->variation_callback );
     579            }
     580        }
     581
     582        /**
     583         * Filters the registered variations for a block type.
     584         *
     585         * @since 6.5.0
     586         *
     587         * @param array         $variations Array of registered variations for a block type.
     588         * @param WP_Block_Type $block_type The full block type object.
     589         */
     590        return apply_filters( 'get_block_type_variations', $this->variations, $this );
     591    }
    543592}
  • trunk/tests/phpunit/tests/blocks/wpBlockType.php

    r57068 r57315  
    461461        );
    462462    }
     463
     464    /**
     465     * @ticket 59969
     466     */
     467    public function test_variation_callback() {
     468        $block_type = new WP_Block_Type(
     469            'test/block',
     470            array(
     471                'title'              => 'Test title',
     472                'variation_callback' => array( $this, 'mock_variation_callback' ),
     473            )
     474        );
     475
     476        $this->assertSameSets( $this->mock_variation_callback(), $block_type->variations );
     477    }
     478
     479    /**
     480     * @ticket 59969
     481     * @covers WP_Block_Type::get_variations
     482     */
     483    public function test_get_variations() {
     484        $block_type = new WP_Block_Type(
     485            'test/block',
     486            array(
     487                'title'              => 'Test title',
     488                'variation_callback' => array( $this, 'mock_variation_callback' ),
     489            )
     490        );
     491
     492        $this->assertSameSets( $this->mock_variation_callback(), $block_type->get_variations() );
     493    }
     494
     495    /**
     496     * @ticket 59969
     497     */
     498    public function test_variations_precedence_over_callback() {
     499        $test_variations = array( 'name' => 'test1' );
     500
     501        $block_type = new WP_Block_Type(
     502            'test/block',
     503            array(
     504                'title'              => 'Test title',
     505                'variations'         => $test_variations,
     506                'variation_callback' => array( $this, 'mock_variation_callback' ),
     507            )
     508        );
     509
     510        // If the variations are defined, the callback should not be used.
     511        $this->assertSameSets( $test_variations, $block_type->variations );
     512    }
     513
     514    /**
     515     * @ticket 59969
     516     */
     517    public function test_variations_callback_are_lazy_loaded() {
     518        $callback_called = false;
     519
     520        $block_type = new WP_Block_Type(
     521            'test/block',
     522            array(
     523                'title'              => 'Test title',
     524                'variation_callback' => function () use ( &$callback_called ) {
     525                    $callback_called = true;
     526                    return $this->mock_variation_callback();
     527                },
     528            )
     529        );
     530
     531        $this->assertSame( false, $callback_called, 'The callback should not be called before the variations are accessed.' );
     532        $block_type->variations; // access the variations.
     533        $this->assertSame( true, $callback_called, 'The callback should be called when the variations are accessed.' );
     534    }
     535
     536    /**
     537     * @ticket 59969
     538     * @covers WP_Block_Type::get_variations
     539     */
     540    public function test_variations_precedence_over_callback_post_registration() {
     541        $test_variations = array( 'name' => 'test1' );
     542        $callback_called = false;
     543
     544        $block_type             = new WP_Block_Type(
     545            'test/block',
     546            array(
     547                'title'              => 'Test title',
     548                'variation_callback' => function () use ( &$callback_called ) {
     549                    $callback_called = true;
     550                    return $this->mock_variation_callback();
     551                },
     552            )
     553        );
     554        $block_type->variations = $test_variations;
     555
     556        // If the variations are defined after registration but before first access, the callback should not override it.
     557        $this->assertSameSets( $test_variations, $block_type->get_variations(), 'Variations are same as variations set' );
     558        $this->assertSame( false, $callback_called, 'The callback was never called.' );
     559    }
     560
     561    /**
     562     * @ticket 59969
     563     * @covers WP_Block_Type::get_variations
     564     */
     565    public function test_variations_callback_happens_only_once() {
     566        $callback_count = 0;
     567
     568        $block_type = new WP_Block_Type(
     569            'test/block',
     570            array(
     571                'title'              => 'Test title',
     572                'variation_callback' => function () use ( &$callback_count ) {
     573                    $callback_count++;
     574                    return $this->mock_variation_callback();
     575                },
     576            )
     577        );
     578
     579        $this->assertSame( 0, $callback_count, 'The callback should not be called before the variations are accessed.' );
     580        $block_type->get_variations(); // access the variations.
     581        $this->assertSame( 1, $callback_count, 'The callback should be called when the variations are accessed.' );
     582        $block_type->get_variations(); // access the variations again.
     583        $this->assertSame( 1, $callback_count, 'The callback should not be called again.' );
     584    }
     585
     586    /**
     587     * Test filter function for get_block_type_variations filter.
     588     *
     589     * @param array $variations Block variations before filter.
     590     * @param WP_Block_Type $block_type Block type.
     591     *
     592     * @return array Block variations after filter.
     593     */
     594    public function filter_test_variations( $variations, $block_type ) {
     595        return array( array( 'name' => 'test1' ) );
     596    }
     597
     598    /**
     599     * @ticket 59969
     600     */
     601    public function test_get_block_type_variations_filter_with_variation_callback() {
     602        // Filter will override the variations obtained from the callback.
     603        add_filter( 'get_block_type_variations', array( $this, 'filter_test_variations' ), 10, 2 );
     604        $expected_variations = array( array( 'name' => 'test1' ) );
     605
     606        $callback_called = false;
     607        $block_type      = new WP_Block_Type(
     608            'test/block',
     609            array(
     610                'title'              => 'Test title',
     611                'variation_callback' => function () use ( &$callback_called ) {
     612                    $callback_called = true;
     613                    return $this->mock_variation_callback();
     614                },
     615            )
     616        );
     617
     618        $obtained_variations = $block_type->variations; // access the variations.
     619
     620        $this->assertSame( true, $callback_called, 'The callback should be called when the variations are accessed.' );
     621        $this->assertSameSets( $obtained_variations, $expected_variations, 'The variations obtained from the callback should be filtered.' );
     622    }
     623
     624    /**
     625     * @ticket 59969
     626     */
     627    public function test_get_block_type_variations_filter_variations() {
     628        // Filter will override the variations set during registration.
     629        add_filter( 'get_block_type_variations', array( $this, 'filter_test_variations' ), 10, 2 );
     630        $expected_variations = array( array( 'name' => 'test1' ) );
     631
     632        $block_type = new WP_Block_Type(
     633            'test/block',
     634            array(
     635                'title'      => 'Test title',
     636                'variations' => $this->mock_variation_callback(),
     637            )
     638        );
     639
     640        $obtained_variations = $block_type->variations; // access the variations.
     641        $this->assertSameSets( $obtained_variations, $expected_variations, 'The variations that was initially set should be filtered.' );
     642    }
     643
     644    /**
     645     * Mock variation callback.
     646     *
     647     * @return array
     648     */
     649    public function mock_variation_callback() {
     650        return array(
     651            array( 'name' => 'var1' ),
     652            array( 'name' => 'var2' ),
     653        );
     654    }
    463655}
  • trunk/tests/phpunit/tests/rest-api/rest-block-type-controller.php

    r57068 r57315  
    736736
    737737    /**
     738     * @ticket 59969
     739     */
     740    public function test_variation_callback() {
     741        $block_type = 'test/block';
     742        $settings   = array(
     743            'title'              => true,
     744            'variation_callback' => array( $this, 'mock_variation_callback' ),
     745        );
     746        register_block_type( $block_type, $settings );
     747        wp_set_current_user( self::$admin_id );
     748        $request  = new WP_REST_Request( 'GET', '/wp/v2/block-types/' . $block_type );
     749        $response = rest_get_server()->dispatch( $request );
     750        $data     = $response->get_data();
     751        $this->assertSameSets( $this->mock_variation_callback(), $data['variations'] );
     752    }
     753
     754    /**
     755     * Mock variation callback.
     756     *
     757     * @return array
     758     */
     759    public function mock_variation_callback() {
     760        return array(
     761            array( 'name' => 'var1' ),
     762            array( 'name' => 'var2' ),
     763        );
     764    }
     765
     766    /**
    738767     * The create_item() method does not exist for block types.
    739768     *
Note: See TracChangeset for help on using the changeset viewer.