diff --git a/wp-includes/blocks.php b/wp-includes/blocks.php index f6e023c67f..d00faa5d5c 100644 --- a/wp-includes/blocks.php +++ b/wp-includes/blocks.php @@ -662,7 +662,8 @@ function render_block( $parsed_block ) { global $post, $wp_query; /** - * Allows render_block() to be short-circuited, by returning a non-null value. + * Allows render_block() or WP_Block::render() to be short-circuited, by + * returning a non-null value. * * @since 5.1.0 * @@ -674,18 +675,6 @@ function render_block( $parsed_block ) { return $pre_render; } - $source_block = $parsed_block; - - /** - * Filters the block being rendered in render_block(), before it's processed. - * - * @since 5.1.0 - * - * @param array $parsed_block The block being rendered. - * @param array $source_block An un-modified copy of $parsed_block, as it appeared in the source content. - */ - $parsed_block = apply_filters( 'render_block_data', $parsed_block, $source_block ); - $context = array(); if ( $post instanceof WP_Post ) { @@ -707,16 +696,6 @@ function render_block( $parsed_block ) { } } - /** - * Filters the default context provided to a rendered block. - * - * @since 5.5.0 - * - * @param array $context Default context. - * @param array $parsed_block Block being rendered, filtered by `render_block_data`. - */ - $context = apply_filters( 'render_block_context', $context, $parsed_block ); - $block = new WP_Block( $parsed_block, $context ); return $block->render(); diff --git a/wp-includes/class-wp-block.php b/wp-includes/class-wp-block.php index 10c7d92dcd..f66d268e45 100644 --- a/wp-includes/class-wp-block.php +++ b/wp-includes/class-wp-block.php @@ -11,6 +11,10 @@ * * @since 5.5.0 * @property array $attributes + * @property array $context + * @property WP_Block[] $inner_blocks; + * @property string $inner_html; + * @property array $inner_content; */ class WP_Block { @@ -22,6 +26,15 @@ class WP_Block { */ public $parsed_block; + /** + * All available context of the current hierarchy. + * + * @since 5.5.0 + * @var array + * @access protected + */ + protected $available_context; + /** * Name of block. * @@ -41,59 +54,20 @@ class WP_Block { public $block_type; /** - * Block context values. + * Map of block property names and their cached value. * - * @since 5.5.0 + * Some block properties are computed lazily using a getter function. The + * result is then cached here for subsequent use. + * + * @since 5.6.0 * @var array */ - public $context = array(); + protected $cached_properties = array(); /** - * All available context of the current hierarchy. - * - * @since 5.5.0 - * @var array - * @access protected - */ - protected $available_context; - - /** - * List of inner blocks (of this same class) - * - * @since 5.5.0 - * @var WP_Block[] - */ - public $inner_blocks = array(); - - /** - * Resultant HTML from inside block comment delimiters after removing inner - * blocks. - * - * @example "...Just testing..." -> "Just testing..." - * - * @since 5.5.0 - * @var string - */ - public $inner_html = ''; - - /** - * List of string fragments and null markers where inner blocks were found - * - * @example array( - * 'inner_html' => 'BeforeInnerAfter', - * 'inner_blocks' => array( block, block ), - * 'inner_content' => array( 'Before', null, 'Inner', null, 'After' ), - * ) - * - * @since 5.5.0 - * @var array - */ - public $inner_content = array(); - - /** - * Constructor. - * - * Populates object properties from the provided block instance argument. + * Creates a block instance from a backing `$parsed_block` array and list of + * `$available_context`. From these, the block's dynamic properties can be + * derived. * * The given array of context values will not necessarily be available on * the instance itself, but is treated as the full set of values provided by @@ -103,31 +77,124 @@ class WP_Block { * * @since 5.5.0 * - * @param array $block Array of parsed block properties. + * @param array $parsed_block Array of parsed block properties. * @param array $available_context Optional array of ancestry context values. * @param WP_Block_Type_Registry $registry Optional block type registry. */ - public function __construct( $block, $available_context = array(), $registry = null ) { - $this->parsed_block = $block; - $this->name = $block['blockName']; - + public function __construct( $parsed_block, $available_context = array(), $registry = null ) { if ( is_null( $registry ) ) { - $registry = WP_Block_Type_Registry::get_instance(); + $this->registry = WP_Block_Type_Registry::get_instance(); + } else { + $this->registry = $registry; } - $this->block_type = $registry->get_registered( $this->name ); + $this->reset( $parsed_block, $available_context ); + } + /** + * Changes the backing `$parsed_block` and `$available_context` used to + * derive the block's dynamic properties. + * + * @since 5.6.0 + + * @param array $parsed_block Array of parsed block properties. + * @param array $available_context Optional array of ancestry context values. + * @param array $cached_properties Optional cache of dynamic properties to use. + */ + protected function reset( + $parsed_block, + $available_context = array(), + $cached_properties = array() + ) { + $this->parsed_block = $parsed_block; $this->available_context = $available_context; + $this->name = $parsed_block['blockName']; + $this->block_type = $this->registry->get_registered( $this->name ); + $this->cached_properties = $cached_properties; + } + + /** + * Getter used for the block's dynamic properties: + * + * - `$block->attributes` + * - `$block->context` + * - `$block->inner_blocks` + * - `$block->inner_html` + * - `$block->inner_content` + * + * Each dynamic property is obtained by calling the associated getter + * function (e.g. `this->get_attributes()`). The result is then cached in + * `$this->cached_attributes` for subsequent calls. + * + * @since 5.5.0 + * + * @param string $name Property name. + * @return array|null Prepared attributes, or null. + */ + public function __get( $name ) { + if ( method_exists( $this, "get_$name" ) ) { + if ( ! isset( $this->cached_properties[ $name ] ) ) { + $this->cached_properties[ $name ] = $this->{"get_$name"}(); + } + + return $this->cached_properties[ $name ]; + } + + return null; + } + + /** + * Block attributes. + * + * Use `$block->attributes` to access this. + * + * @since 5.6.0 + * @return array + */ + protected function get_attributes() { + $attributes = isset( $this->parsed_block['attrs'] ) ? + $this->parsed_block['attrs'] : + array(); + + if ( ! is_null( $this->block_type ) ) { + return $this->block_type->prepare_attributes_for_render( $attributes ); + } + + return $attributes; + } + + /** + * Block context values. + * + * Use `$block->context` to access this. + * + * @since 5.6.0 + * @return array + */ + protected function get_context() { + $context = array(); if ( ! empty( $this->block_type->uses_context ) ) { foreach ( $this->block_type->uses_context as $context_name ) { if ( array_key_exists( $context_name, $this->available_context ) ) { - $this->context[ $context_name ] = $this->available_context[ $context_name ]; + $context[ $context_name ] = $this->available_context[ $context_name ]; } } } - if ( ! empty( $block['innerBlocks'] ) ) { + return $context; + } + + /** + * List of inner blocks (of this same class). + * + * Use `$block->inner_blocks` to access this. + * + * @since 5.6.0 + * @return WP_Block[] + */ + protected function get_inner_blocks() { + if ( ! empty( $this->parsed_block['innerBlocks'] ) ) { $child_context = $this->available_context; if ( ! empty( $this->block_type->provides_context ) ) { @@ -138,45 +205,55 @@ class WP_Block { } } - $this->inner_blocks = new WP_Block_List( $block['innerBlocks'], $child_context, $registry ); + return new WP_Block_List( + $this->parsed_block['innerBlocks'], + $child_context, + $this->registry + ); } - if ( ! empty( $block['innerHTML'] ) ) { - $this->inner_html = $block['innerHTML']; - } - - if ( ! empty( $block['innerContent'] ) ) { - $this->inner_content = $block['innerContent']; - } + return array(); } /** - * Returns a value from an inaccessible property. + * Resultant HTML from inside block comment delimiters after removing inner + * blocks. * - * This is used to lazily initialize the `attributes` property of a block, - * such that it is only prepared with default attributes at the time that - * the property is accessed. For all other inaccessible properties, a `null` - * value is returned. + * Use `$block->inner_html` to access this. * - * @since 5.5.0 + * @example "...Just testing..." -> "Just testing..." * - * @param string $name Property name. - * @return array|null Prepared attributes, or null. + * @since 5.6.0 + * @return string */ - public function __get( $name ) { - if ( 'attributes' === $name ) { - $this->attributes = isset( $this->parsed_block['attrs'] ) ? - $this->parsed_block['attrs'] : - array(); - - if ( ! is_null( $this->block_type ) ) { - $this->attributes = $this->block_type->prepare_attributes_for_render( $this->attributes ); - } - - return $this->attributes; + protected function get_inner_html() { + if ( ! empty( $this->parsed_block['innerHTML'] ) ) { + return $this->parsed_block['innerHTML']; } - return null; + return ''; + } + + /** + * List of string fragments and null markers where inner blocks were found + * + * Use `$block->inner_content` to access this. + * + * @example array( + * 'inner_html' => 'BeforeInnerAfter', + * 'inner_blocks' => array( block, block ), + * 'inner_content' => array( 'Before', null, 'Inner', null, 'After' ), + * ) + * + * @since 5.6.0 + * @return array + */ + protected function get_inner_content() { + if ( ! empty( $this->parsed_block['innerContent'] ) ) { + return $this->parsed_block['innerContent']; + } + + return array(); } /** @@ -193,6 +270,13 @@ class WP_Block { */ public function render( $options = array() ) { global $post; + + /** This filter is documented in src/wp-includes/blocks.php. */ + $pre_render = apply_filters( 'pre_render_block', null, $this->parsed_block ); + if ( ! is_null( $pre_render ) ) { + return $pre_render; + } + $options = wp_parse_args( $options, array( @@ -200,7 +284,47 @@ class WP_Block { ) ); - $is_dynamic = $options['dynamic'] && $this->name && null !== $this->block_type && $this->block_type->is_dynamic(); + $initial_parsed_block = $this->parsed_block; + $initial_available_context = $this->available_context; + $initial_cached_properties = $this->cached_properties; + + /** + * Filters a block which is to be rendered by render_block() or + * WP_Block::render(). + * + * @since 5.1.0 + * + * @param array $parsed_block The block being rendered. + * @param array $source_block An un-modified copy of $parsed_block, as it appeared in the source content. + */ + $parsed_block = apply_filters( + 'render_block_data', + $this->parsed_block, + $initial_parsed_block + ); + + /** + * Filters the default context of a block which is to be rendered by + * render_block() or WP_Block::render(). + * + * @since 5.5.0 + * + * @param array $available_context Default context. + * @param array $parsed_block Block being rendered, filtered by `render_block_data`. + */ + $available_context = apply_filters( + 'render_block_context', + $this->available_context, + $this->parsed_block + ); + + $this->reset( $parsed_block, $available_context ); + + $is_dynamic = $options['dynamic'] + && $this->name + && null !== $this->block_type + && $this->block_type->is_dynamic(); + $block_content = ''; if ( ! $options['dynamic'] || empty( $this->block_type->skip_inner_blocks ) ) { @@ -218,7 +342,12 @@ class WP_Block { WP_Block_Supports::$block_to_render = $this->parsed_block; - $block_content = (string) call_user_func( $this->block_type->render_callback, $this->attributes, $block_content, $this ); + $block_content = (string) call_user_func( + $this->block_type->render_callback, + $this->attributes, + $block_content, + $this + ); WP_Block_Supports::$block_to_render = $parent; @@ -241,7 +370,15 @@ class WP_Block { * @param string $block_content The block content about to be appended. * @param array $block The full block, including name and attributes. */ - return apply_filters( 'render_block', $block_content, $this->parsed_block ); + $block_content = apply_filters( 'render_block', $block_content, $this->parsed_block ); + + $this->reset( + $initial_parsed_block, + $initial_available_context, + $initial_cached_properties + ); + + return $block_content; } } diff --git a/wp-includes/version.php b/wp-includes/version.php index ff7b7fa00c..4f8cb453af 100644 --- a/wp-includes/version.php +++ b/wp-includes/version.php @@ -13,7 +13,7 @@ * * @global string $wp_version */ -$wp_version = '5.6-beta4-49607'; +$wp_version = '5.6-beta4-49608'; /** * Holds the WordPress DB revision, increments when changes are made to the WordPress DB schema.