Make WordPress Core

Changeset 57315


Ignore:
Timestamp:
01/19/2024 08:52:06 PM (3 years 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.