Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Blocks: Add block bindings registry class #5965

Closed
wants to merge 3 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
194 changes: 194 additions & 0 deletions src/wp-includes/class-wp-block-bindings-registry.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,194 @@
<?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 binding 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 array
Comment on lines +22 to +24
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The line break is not needed, similar to the property below.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@gziolo ☝️

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'll include that change in the next batch of changes planned: #5966.

*/
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;

/**
* Registers a new block binding 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.
* @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 an 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.
* }
* @return array|false Source when the registration was successful, or `false` on failure.
*/
public function register( $source_name, array $source_properties ) {
if ( ! is_string( $source_name ) ) {
_doing_it_wrong(
__METHOD__,
__( 'Block binding source name must be a string.' ),
'6.5.0'
);
return false;
}

if ( preg_match( '/[A-Z]+/', $source_name ) ) {
_doing_it_wrong(
__METHOD__,
__( 'Block binding source names must not contain uppercase characters.' ),
'6.5.0'
);
return false;
}

$name_matcher = '/^[a-z0-9-]+\/[a-z0-9-]+$/';
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This regex is probably a bit too eager. Only alphanumeric characters and hyphens might be too arbitrarily limited. Underscores, dots or colons are all common in technical names. It also doesn't take into account any non-english characters.

I think we should only check whether there is a / present to indicate a namespace and accept any string before- and after.

On another note, the _doing_it_wrong() warnings do not show up in PHP unit tests. I don't know if this is by design or there is another method that could be used here?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I copied the same logic that the block types registry uses:

if ( preg_match( '/[A-Z]+/', $name ) ) {
_doing_it_wrong(
__METHOD__,
__( 'Block type names must not contain uppercase characters.' ),
'5.0.0'
);
return false;
}
$name_matcher = '/^[a-z0-9-]+\/[a-z0-9-]+$/';
if ( ! preg_match( $name_matcher, $name ) ) {
_doing_it_wrong(
__METHOD__,
__( 'Block type names must contain a namespace prefix. Example: my-plugin/my-custom-block-type' ),
'5.0.0'
);
return false;
}

I thought it would be good to follow the same pattern for consistency.

On another note, the _doing_it_wrong() warnings do not show up in PHP unit tests. I don't know if this is by design or there is another method that could be used here?

_doing_it_wrong() call should fail the test if the following PHPDoc annotation isn't provided:

* @expectedIncorrectUsage WP_Block_Bindings_Registry::register

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

_doing_it_wrong() call should fail the test if the following PHPDoc annotation isn't provided:

I see, thanks!

I copied the same logic that the block types registry uses:

OK, I think that's fine in that case! 👍 Let's not forget to mention it in the documentation. I'll update the docstring for register_block_bindings_source() & registry->register() in #5888 as well.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good point. That would be important to have covered 👍

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done in f2a611b

if ( ! preg_match( $name_matcher, $source_name ) ) {
_doing_it_wrong(
__METHOD__,
__( 'Block binding 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 binding source name. */
sprintf( __( 'Block binding source "%s" already registered.' ), $source_name ),
'6.5.0'
);
return false;
}

$source = array_merge(
array( 'name' => $source_name ),
$source_properties
);

$this->sources[ $source_name ] = $source;

return $source;
}

/**
* Unregisters a block binding source.
*
* @since 6.5.0
*
* @param string $source_name Block binding source name including namespace.
* @return array|false The unregistred block binding source on success and `false` otherwise.
*/
public function unregister( $source_name ) {
if ( ! $this->is_registered( $source_name ) ) {
_doing_it_wrong(
__METHOD__,
/* translators: %s: Block binding 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 array 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 array|null The registered block binding source, or `null` if it is not registered.
*/
public function get_registered( $source_name ) {
if ( ! $this->is_registered( $source_name ) ) {
return null;
}

return $this->sources[ $source_name ];
}

/**
* Checks if a block binding source is registered.
*
* @since 6.5.0
*
* @param string $source_name The name of the source.
* @return bool `true` if the block binding source is registered, `false` otherwise.
*/
public function is_registered( $source_name ) {
return isset( $this->sources[ $source_name ] );
}

/**
* 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;
}
}
1 change: 1 addition & 0 deletions src/wp-settings.php
Original file line number Diff line number Diff line change
Expand Up @@ -330,6 +330,7 @@
require ABSPATH . WPINC . '/sitemaps/providers/class-wp-sitemaps-taxonomies.php';
require ABSPATH . WPINC . '/sitemaps/providers/class-wp-sitemaps-users.php';
require ABSPATH . WPINC . '/class-wp-block-editor-context.php';
require ABSPATH . WPINC . '/class-wp-block-bindings-registry.php';
require ABSPATH . WPINC . '/class-wp-block-type.php';
require ABSPATH . WPINC . '/class-wp-block-pattern-categories-registry.php';
require ABSPATH . WPINC . '/class-wp-block-patterns-registry.php';
Expand Down
Loading
Loading