diff options
Diffstat (limited to 'wp-includes/class-wp-block-bindings-registry.php')
-rw-r--r-- | wp-includes/class-wp-block-bindings-registry.php | 308 |
1 files changed, 308 insertions, 0 deletions
diff --git a/wp-includes/class-wp-block-bindings-registry.php b/wp-includes/class-wp-block-bindings-registry.php new file mode 100644 index 0000000..6ec5ac8 --- /dev/null +++ b/wp-includes/class-wp-block-bindings-registry.php @@ -0,0 +1,308 @@ +<?php +/** + * Block Bindings API: WP_Block_Bindings_Registry class. + * + * Supports overriding content in blocks by connecting them to different sources. + * + * @package WordPress + * @subpackage Block Bindings + * @since 6.5.0 + */ + +/** + * Core class used for interacting with block bindings sources. + * + * @since 6.5.0 + */ +final class WP_Block_Bindings_Registry { + + /** + * Holds the registered block bindings sources, keyed by source identifier. + * + * @since 6.5.0 + * @var WP_Block_Bindings_Source[] + */ + private $sources = array(); + + /** + * Container for the main instance of the class. + * + * @since 6.5.0 + * @var WP_Block_Bindings_Registry|null + */ + private static $instance = null; + + /** + * Supported source properties that can be passed to the registered source. + * + * @since 6.5.0 + * @var array + */ + private $allowed_source_properties = array( + 'label', + 'get_value_callback', + 'uses_context', + ); + + /** + * Supported blocks that can use the block bindings API. + * + * @since 6.5.0 + * @var array + */ + private $supported_blocks = array( + 'core/paragraph', + 'core/heading', + 'core/image', + 'core/button', + ); + + /** + * Registers a new block bindings source. + * + * This is a low-level method. For most use cases, it is recommended to use + * the `register_block_bindings_source()` function instead. + * + * @see register_block_bindings_source() + * + * Sources are used to override block's original attributes with a value + * coming from the source. Once a source is registered, it can be used by a + * block by setting its `metadata.bindings` attribute to a value that refers + * to the source. + * + * @since 6.5.0 + * + * @param string $source_name The name of the source. It must be a string containing a namespace prefix, i.e. + * `my-plugin/my-custom-source`. It must only contain lowercase alphanumeric + * characters, the forward slash `/` and dashes. + * @param array $source_properties { + * The array of arguments that are used to register a source. + * + * @type string $label The label of the source. + * @type callback $get_value_callback A callback executed when the source is processed during block rendering. + * The callback should have the following signature: + * + * `function ($source_args, $block_instance,$attribute_name): mixed` + * - @param array $source_args Array containing source arguments + * used to look up the override value, + * i.e. {"key": "foo"}. + * - @param WP_Block $block_instance The block instance. + * - @param string $attribute_name The name of the target attribute. + * The callback has a mixed return type; it may return a string to override + * the block's original value, null, false to remove an attribute, etc. + * @type array $uses_context (optional) Array of values to add to block `uses_context` needed by the source. + * } + * @return WP_Block_Bindings_Source|false Source when the registration was successful, or `false` on failure. + */ + public function register( string $source_name, array $source_properties ) { + if ( ! is_string( $source_name ) ) { + _doing_it_wrong( + __METHOD__, + __( 'Block bindings source name must be a string.' ), + '6.5.0' + ); + return false; + } + + if ( preg_match( '/[A-Z]+/', $source_name ) ) { + _doing_it_wrong( + __METHOD__, + __( 'Block bindings source names must not contain uppercase characters.' ), + '6.5.0' + ); + return false; + } + + $name_matcher = '/^[a-z0-9-]+\/[a-z0-9-]+$/'; + if ( ! preg_match( $name_matcher, $source_name ) ) { + _doing_it_wrong( + __METHOD__, + __( 'Block bindings source names must contain a namespace prefix. Example: my-plugin/my-custom-source' ), + '6.5.0' + ); + return false; + } + + if ( $this->is_registered( $source_name ) ) { + _doing_it_wrong( + __METHOD__, + /* translators: %s: Block bindings source name. */ + sprintf( __( 'Block bindings source "%s" already registered.' ), $source_name ), + '6.5.0' + ); + return false; + } + + // Validates that the source properties contain the label. + if ( ! isset( $source_properties['label'] ) ) { + _doing_it_wrong( + __METHOD__, + __( 'The $source_properties must contain a "label".' ), + '6.5.0' + ); + return false; + } + + // Validates that the source properties contain the get_value_callback. + if ( ! isset( $source_properties['get_value_callback'] ) ) { + _doing_it_wrong( + __METHOD__, + __( 'The $source_properties must contain a "get_value_callback".' ), + '6.5.0' + ); + return false; + } + + // Validates that the get_value_callback is a valid callback. + if ( ! is_callable( $source_properties['get_value_callback'] ) ) { + _doing_it_wrong( + __METHOD__, + __( 'The "get_value_callback" parameter must be a valid callback.' ), + '6.5.0' + ); + return false; + } + + // Validates that the uses_context parameter is an array. + if ( isset( $source_properties['uses_context'] ) && ! is_array( $source_properties['uses_context'] ) ) { + _doing_it_wrong( + __METHOD__, + __( 'The "uses_context" parameter must be an array.' ), + '6.5.0' + ); + return false; + } + + if ( ! empty( array_diff( array_keys( $source_properties ), $this->allowed_source_properties ) ) ) { + _doing_it_wrong( + __METHOD__, + __( 'The $source_properties array contains invalid properties.' ), + '6.5.0' + ); + return false; + } + + $source = new WP_Block_Bindings_Source( + $source_name, + $source_properties + ); + + $this->sources[ $source_name ] = $source; + + // Adds `uses_context` defined by block bindings sources. + add_filter( + 'get_block_type_uses_context', + function ( $uses_context, $block_type ) use ( $source ) { + if ( ! in_array( $block_type->name, $this->supported_blocks, true ) || empty( $source->uses_context ) ) { + return $uses_context; + } + // Use array_values to reset the array keys. + return array_values( array_unique( array_merge( $uses_context, $source->uses_context ) ) ); + }, + 10, + 2 + ); + + return $source; + } + + /** + * Unregisters a block bindings source. + * + * @since 6.5.0 + * + * @param string $source_name Block bindings source name including namespace. + * @return WP_Block_Bindings_Source|false The unregistered block bindings source on success and `false` otherwise. + */ + public function unregister( string $source_name ) { + if ( ! $this->is_registered( $source_name ) ) { + _doing_it_wrong( + __METHOD__, + /* translators: %s: Block bindings source name. */ + sprintf( __( 'Block binding "%s" not found.' ), $source_name ), + '6.5.0' + ); + return false; + } + + $unregistered_source = $this->sources[ $source_name ]; + unset( $this->sources[ $source_name ] ); + + return $unregistered_source; + } + + /** + * Retrieves the list of all registered block bindings sources. + * + * @since 6.5.0 + * + * @return WP_Block_Bindings_Source[] The array of registered sources. + */ + public function get_all_registered() { + return $this->sources; + } + + /** + * Retrieves a registered block bindings source. + * + * @since 6.5.0 + * + * @param string $source_name The name of the source. + * @return WP_Block_Bindings_Source|null The registered block bindings source, or `null` if it is not registered. + */ + public function get_registered( string $source_name ) { + if ( ! $this->is_registered( $source_name ) ) { + return null; + } + + return $this->sources[ $source_name ]; + } + + /** + * Checks if a block bindings source is registered. + * + * @since 6.5.0 + * + * @param string $source_name The name of the source. + * @return bool `true` if the block bindings source is registered, `false` otherwise. + */ + public function is_registered( $source_name ) { + return isset( $this->sources[ $source_name ] ); + } + + /** + * Wakeup magic method. + * + * @since 6.5.0 + */ + public function __wakeup() { + if ( ! $this->sources ) { + return; + } + if ( ! is_array( $this->sources ) ) { + throw new UnexpectedValueException(); + } + foreach ( $this->sources as $value ) { + if ( ! $value instanceof WP_Block_Bindings_Source ) { + throw new UnexpectedValueException(); + } + } + } + + /** + * Utility method to retrieve the main instance of the class. + * + * The instance will be created if it does not exist yet. + * + * @since 6.5.0 + * + * @return WP_Block_Bindings_Registry The main instance. + */ + public static function get_instance() { + if ( null === self::$instance ) { + self::$instance = new self(); + } + + return self::$instance; + } +} |