309 lines
		
	
	
		
			8.8 KiB
		
	
	
	
		
			PHP
		
	
	
	
	
	
			
		
		
	
	
			309 lines
		
	
	
		
			8.8 KiB
		
	
	
	
		
			PHP
		
	
	
	
	
	
| <?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;
 | |
| 	}
 | |
| }
 |