Skip to content

Commit

Permalink
Block Bindings API: Refactor logic into Block Bindings class and sing…
Browse files Browse the repository at this point in the history
…leton pattern (#57742)

* Move bindings logic to Block Bindings class

* Remove erroneous echo statement that was breaking UI

* Fix error in registering block bindings sources

* Add missing return statement

* Add docblocks

* Remove obsolete file

* Fix docblock

* Sync register_source docblock

* Remove erroneous subpackage declaration

* Update package name

* Fix gutenberg package declarations

* Remove extraneous comments

* Move allowed_blocks property to filter function

* Update description of Block Bindings class

* Address PHPCS spacing issue

* Rename function call and call using string in filter
  • Loading branch information
artemiomorales authored Jan 17, 2024
1 parent 0b9d8ae commit 81c3c96
Show file tree
Hide file tree
Showing 7 changed files with 148 additions and 70 deletions.
69 changes: 69 additions & 0 deletions lib/experimental/block-bindings/block-bindings.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
<?php
/**
* Block Bindings API
*
* This file contains functions for managing block bindings in WordPress.
*
* @since 17.6.0
* @package gutenberg
*/

/**
* Retrieves the singleton instance of WP_Block_Bindings.
*
* @return WP_Block_Bindings The WP_Block_Bindings instance.
*/
if ( ! function_exists( 'wp_block_bindings' ) ) {
function wp_block_bindings() {
static $instance = null;
if ( is_null( $instance ) ) {
$instance = new WP_Block_Bindings();
}
return $instance;
}
}

/**
* Registers a new source for block bindings.
*
* @param string $source_name The name of the source.
* @param string $label The label of the source.
* @param callable $apply The callback executed when the source is processed during block rendering. The callable should have the following signature:
* function (object $source_attrs, object $block_instance, string $attribute_name): string
* - object $source_attrs: Object containing source ID used to look up the override value, i.e. {"value": "{ID}"}.
* - object $block_instance: The block instance.
* - string $attribute_name: The name of an attribute used to retrieve an override value from the block context.
* The callable should return a string that will be used to override the block's original value.
* @return void
*/
if ( ! function_exists( 'wp_block_bindings_register_source' ) ) {
function wp_block_bindings_register_source( $source_name, $label, $apply ) {
wp_block_bindings()->register_source( $source_name, $label, $apply );
}
}

/**
* Retrieves the list of registered block sources.
*
* @return array The list of registered block sources.
*/
if ( ! function_exists( 'wp_block_bindings_get_sources' ) ) {
function wp_block_bindings_get_sources() {
return wp_block_bindings()->get_sources();
}
}

/**
* Replaces the HTML content of a block based on the provided source value.
*
* @param string $block_content Block Content.
* @param string $block_name The name of the block to process.
* @param string $block_attr The attribute of the block we want to process.
* @param string $source_value The value used to replace the HTML.
* @return string The modified block content.
*/
if ( ! function_exists( 'wp_block_bindings_replace_html' ) ) {
function wp_block_bindings_replace_html( $block_content, $block_name, $block_attr, $source_value ) {
return wp_block_bindings()->replace_html( $block_content, $block_name, $block_attr, $source_value );
}
}
Original file line number Diff line number Diff line change
@@ -1,11 +1,50 @@
<?php
/**
* Define the mechanism to replace the HTML depending on the block attributes.
* Block Bindings API: WP_Block_Bindings class.
*
* Support for overriding content in blocks by connecting them to different sources.
*
* @since 17.6.0
* @package gutenberg
*/

if ( ! function_exists( 'block_bindings_replace_html' ) ) {
if ( class_exists( 'WP_Block_Bindings' ) ) {
return;
}

/**
* Core class used to define supported blocks, register sources, and populate HTML with content from those sources.
*/
class WP_Block_Bindings {

/**
* Holds the registered block bindings sources, keyed by source identifier.
*
* @var array
*/
private $sources = array();

/**
* Function to register a new source.
*
* @param string $source_name The name of the source.
* @param string $label The label of the source.
* @param callable $apply The callback executed when the source is processed during block rendering. The callable should have the following signature:
* function (object $source_attrs, object $block_instance, string $attribute_name): string
* - object $source_attrs: Object containing source ID used to look up the override value, i.e. {"value": "{ID}"}.
* - object $block_instance: The block instance.
* - string $attribute_name: The name of an attribute used to retrieve an override value from the block context.
* The callable should return a string that will be used to override the block's original value.
*
* @return void
*/
public function register_source( $source_name, $label, $apply ) {
$this->sources[ $source_name ] = array(
'label' => $label,
'apply' => $apply,
);
}

/**
* Depending on the block attributes, replace the proper HTML based on the value returned by the source.
*
Expand All @@ -14,7 +53,7 @@
* @param string $block_attr The attribute of the block we want to process.
* @param string $source_value The value used to replace the HTML.
*/
function block_bindings_replace_html( $block_content, $block_name, $block_attr, $source_value ) {
public function replace_html( $block_content, $block_name, $block_attr, $source_value ) {
$block_type = WP_Block_Type_Registry::get_instance()->get_registered( $block_name );
if ( null === $block_type ) {
return;
Expand Down Expand Up @@ -107,4 +146,13 @@ function block_bindings_replace_html( $block_content, $block_name, $block_attr,
}
return;
}

/**
* Retrieves the list of registered block sources.
*
* @return array The array of registered sources.
*/
public function get_sources() {
return $this->sources;
}
}
5 changes: 2 additions & 3 deletions lib/experimental/block-bindings/index.php
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,8 @@
* @package gutenberg
*/

require_once __DIR__ . '/sources/index.php';
require_once __DIR__ . '/html-processing.php';

require_once __DIR__ . '/class-wp-block-bindings.php';
require_once __DIR__ . '/block-bindings.php';
// Register the sources.
$gutenberg_experiments = get_option( 'gutenberg-experiments' );
if ( $gutenberg_experiments ) {
Expand Down
32 changes: 0 additions & 32 deletions lib/experimental/block-bindings/sources/index.php

This file was deleted.

5 changes: 2 additions & 3 deletions lib/experimental/block-bindings/sources/pattern.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,16 +4,15 @@
*
* @package gutenberg
*/

if ( function_exists( 'register_block_bindings_source' ) ) {
if ( function_exists( 'wp_block_bindings_register_source' ) ) {
$pattern_source_callback = function ( $source_attrs, $block_instance, $attribute_name ) {
if ( ! _wp_array_get( $block_instance->attributes, array( 'metadata', 'id' ), false ) ) {
return null;
}
$block_id = $block_instance->attributes['metadata']['id'];
return _wp_array_get( $block_instance->context, array( 'pattern/overrides', $block_id, $attribute_name ), null );
};
register_block_bindings_source(
wp_block_bindings_register_source(
'pattern_attributes',
__( 'Pattern Attributes', 'gutenberg' ),
$pattern_source_callback
Expand Down
5 changes: 2 additions & 3 deletions lib/experimental/block-bindings/sources/post-meta.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,7 @@
*
* @package gutenberg
*/

if ( function_exists( 'register_block_bindings_source' ) ) {
if ( function_exists( 'wp_block_bindings_register_source' ) ) {
$post_meta_source_callback = function ( $source_attrs ) {
// Use the postId attribute if available
if ( isset( $source_attrs['postId'] ) ) {
Expand All @@ -17,7 +16,7 @@

return get_post_meta( $post_id, $source_attrs['value'], true );
};
register_block_bindings_source(
wp_block_bindings_register_source(
'post_meta',
__( 'Post Meta', 'gutenberg' ),
$post_meta_source_callback
Expand Down
48 changes: 22 additions & 26 deletions lib/experimental/blocks.php
Original file line number Diff line number Diff line change
Expand Up @@ -88,26 +88,28 @@ function wp_enqueue_block_view_script( $block_name, $args ) {
) ) {

require_once __DIR__ . '/block-bindings/index.php';
// Allowed blocks that support block bindings.
// TODO: Look for a mechanism to opt-in for this. Maybe adding a property to block attributes?
global $block_bindings_allowed_blocks;
$block_bindings_allowed_blocks = array(
'core/paragraph' => array( 'content' ),
'core/heading' => array( 'content' ),
'core/image' => array( 'url', 'title', 'alt' ),
'core/button' => array( 'url', 'text' ),
);
if ( ! function_exists( 'process_block_bindings' ) ) {

if ( ! function_exists( 'gutenberg_process_block_bindings' ) ) {
/**
* Process the block bindings attribute.
*
* @param string $block_content Block Content.
* @param array $block Block attributes.
* @param WP_Block $block_instance The block instance.
*/
function process_block_bindings( $block_content, $block, $block_instance ) {
// If the block doesn't have the bindings property, return.
if ( ! isset( $block['attrs']['metadata']['bindings'] ) ) {
function gutenberg_process_block_bindings( $block_content, $block, $block_instance ) {

// Allowed blocks that support block bindings.
// TODO: Look for a mechanism to opt-in for this. Maybe adding a property to block attributes?
$allowed_blocks = array(
'core/paragraph' => array( 'content' ),
'core/heading' => array( 'content' ),
'core/image' => array( 'url', 'title', 'alt' ),
'core/button' => array( 'url', 'text' ),
);

// If the block doesn't have the bindings property or isn't one of the allowed block types, return.
if ( ! isset( $block['attrs']['metadata']['bindings'] ) || ! isset( $allowed_blocks[ $block_instance->name ] ) ) {
return $block_content;
}

Expand All @@ -128,16 +130,13 @@ function process_block_bindings( $block_content, $block, $block_instance ) {
// }
// }
//
global $block_bindings_allowed_blocks;
global $block_bindings_sources;

$block_bindings_sources = wp_block_bindings_get_sources();
$modified_block_content = $block_content;
foreach ( $block['attrs']['metadata']['bindings'] as $binding_attribute => $binding_source ) {
// If the block is not in the list, stop processing.
if ( ! isset( $block_bindings_allowed_blocks[ $block['blockName'] ] ) ) {
return $block_content;
}

// If the attribute is not in the list, process next attribute.
if ( ! in_array( $binding_attribute, $block_bindings_allowed_blocks[ $block['blockName'] ], true ) ) {
if ( ! in_array( $binding_attribute, $allowed_blocks[ $block_instance->name ], true ) ) {
continue;
}
// If no source is provided, or that source is not registered, process next attribute.
Expand All @@ -159,14 +158,11 @@ function process_block_bindings( $block_content, $block, $block_instance ) {
}

// Process the HTML based on the block and the attribute.
$modified_block_content = block_bindings_replace_html( $modified_block_content, $block['blockName'], $binding_attribute, $source_value );
$modified_block_content = wp_block_bindings_replace_html( $modified_block_content, $block_instance->name, $binding_attribute, $source_value );
}
return $modified_block_content;
}

// Add filter only to the blocks in the list.
foreach ( $block_bindings_allowed_blocks as $block_name => $attributes ) {
add_filter( 'render_block_' . $block_name, 'process_block_bindings', 20, 3 );
}
}

add_filter( 'render_block', 'gutenberg_process_block_bindings', 20, 3 );
}

0 comments on commit 81c3c96

Please sign in to comment.