-
Notifications
You must be signed in to change notification settings - Fork 4.3k
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
Introduce proper PHP classes for interacting with block types #1322
Changes from 5 commits
ce45293
d213309
9aa5fde
a84b787
8e379bf
a408132
6542555
e0539c7
ecc6dbb
c8e1464
d45da39
547b89b
75a065a
ca02a61
11c47bc
6b3ec26
d45a92a
c296165
b7d1824
21d8ee6
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -9,64 +9,34 @@ | |
die( 'Silence is golden.' ); | ||
} | ||
|
||
$wp_registered_blocks = array(); | ||
|
||
/** | ||
* Registers a block. | ||
* Registers a block type. | ||
* | ||
* @param string $name Block name including namespace. | ||
* @param array $settings Block settings. | ||
|
||
* @return array The block, if it has been successfully registered. | ||
* @since 0.1.0 | ||
* | ||
* @param string $name Block type name including namespace. | ||
* @param array $args { | ||
* Array of block type arguments. Any arguments may be defined, however the following | ||
* ones are supported by default. | ||
* | ||
* @type callable $render Callback used to render blocks of this block type. | ||
* } | ||
* @return WP_Block_Type|false The registered block type on success, or false on failure. | ||
*/ | ||
function register_block_type( $name, $settings ) { | ||
global $wp_registered_blocks; | ||
|
||
if ( ! is_string( $name ) ) { | ||
$message = __( 'Block names must be strings.' ); | ||
_doing_it_wrong( __FUNCTION__, $message, '0.1.0' ); | ||
return false; | ||
} | ||
|
||
$name_matcher = '/^[a-z0-9-]+\/[a-z0-9-]+$/'; | ||
if ( ! preg_match( $name_matcher, $name ) ) { | ||
$message = __( 'Block names must contain a namespace prefix. Example: my-plugin/my-custom-block' ); | ||
_doing_it_wrong( __FUNCTION__, $message, '0.1.0' ); | ||
return false; | ||
} | ||
|
||
if ( isset( $wp_registered_blocks[ $name ] ) ) { | ||
/* translators: 1: block name */ | ||
$message = sprintf( __( 'Block "%s" is already registered.' ), $name ); | ||
_doing_it_wrong( __FUNCTION__, $message, '0.1.0' ); | ||
return false; | ||
} | ||
|
||
$settings['name'] = $name; | ||
$wp_registered_blocks[ $name ] = $settings; | ||
|
||
return $settings; | ||
function register_block_type( $name, $args ) { | ||
return WP_Block_Type_Registry::get_instance()->register( $name, $args ); | ||
} | ||
|
||
/** | ||
* Unregisters a block. | ||
* Unregisters a block type. | ||
* | ||
* @param string $name Block name. | ||
* @return array The previous block value, if it has been | ||
* successfully unregistered; otherwise `null`. | ||
* @since 0.1.0 | ||
* | ||
* @param string $name Block type name including namespace. | ||
* @return WP_Block_Type|false The unregistered block type on success, or false on failure. | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. What benefit is there to returning a There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It's a shortcut to avoid having to do a separate getter call, yes. This was also implemented in the Customizer for calls like There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Oh, woops. I missed that this is in the unregister call. Nevertheless, seems useful to return. As essentially if There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I get the value of something truthy being returned to indicate a successful unregister, but why not There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think it's a good practice to return the data that has been removed/unregistered, to allow further actions with it, for example tweaking and re-registering or passing it to a custom hook. It's a bit similar to how several PHP array functions work like |
||
*/ | ||
function unregister_block_type( $name ) { | ||
global $wp_registered_blocks; | ||
if ( ! isset( $wp_registered_blocks[ $name ] ) ) { | ||
/* translators: 1: block name */ | ||
$message = sprintf( __( 'Block "%s" is not registered.' ), $name ); | ||
_doing_it_wrong( __FUNCTION__, $message, '0.1.0' ); | ||
return false; | ||
} | ||
$unregistered_block = $wp_registered_blocks[ $name ]; | ||
unset( $wp_registered_blocks[ $name ] ); | ||
|
||
return $unregistered_block; | ||
return WP_Block_Type_Registry::get_instance()->unregister( $name ); | ||
} | ||
|
||
/** | ||
|
@@ -99,33 +69,34 @@ function parse_block_attributes( $attr_string ) { | |
* @return string Updated post content. | ||
*/ | ||
function do_blocks( $content ) { | ||
global $wp_registered_blocks; | ||
$registry = WP_Block_Type_Registry::get_instance(); | ||
|
||
// Extract the blocks from the post content. | ||
$matcher = '#' . join( '', array( | ||
'(?P<opener><!--\s*', | ||
'wp:(?P<block_name>[a-z](?:[a-z0-9/]+)*)\s+', | ||
'wp:(?P<block_type_name>[a-z](?:[a-z0-9/]+)*)\s+', | ||
'(?P<attributes>(?:(?!-->).)*)', | ||
'\s*/?-->\n?)', | ||
'(?:', | ||
'(?P<content>.*?)', | ||
'(?P<closer><!--\s*/wp:\g{block_name}\s+-->\n?)', | ||
'(?P<closer><!--\s*/wp:\g{block_type_name}\s+-->\n?)', | ||
')?', | ||
) ) . '#s'; | ||
preg_match_all( $matcher, $content, $matches, PREG_OFFSET_CAPTURE ); | ||
|
||
$new_content = $content; | ||
$offset_differential = 0; | ||
foreach ( $matches[0] as $index => $block_match ) { | ||
$block_name = $matches['block_name'][ $index ][0]; | ||
$block_type_name = $matches['block_type_name'][ $index ][0]; | ||
$block_type = $registry->get_registered( $block_type_name ); | ||
|
||
$output = ''; | ||
if ( isset( $wp_registered_blocks[ $block_name ] ) ) { | ||
if ( null !== $block_type ) { | ||
$block_attributes_string = $matches['attributes'][ $index ][0]; | ||
$block_attributes = parse_block_attributes( $block_attributes_string ); | ||
|
||
// Call the block's render function to generate the dynamic output. | ||
$output = call_user_func( $wp_registered_blocks[ $block_name ]['render'], $block_attributes ); | ||
$output = call_user_func( $block_type->render, $block_attributes ); | ||
} elseif ( isset( $matches['content'][ $index ][0] ) ) { | ||
$output = $matches['content'][ $index ][0]; | ||
} | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,148 @@ | ||
<?php | ||
/** | ||
* Blocks API: WP_Block_Type_Registry class | ||
* | ||
* @package gutenberg | ||
* @since 0.2.0 | ||
*/ | ||
|
||
/** | ||
* Core class used for interacting with block types. | ||
* | ||
* @since 0.2.0 | ||
*/ | ||
final class WP_Block_Type_Registry { | ||
/** | ||
* Registered block types, as `$name => $instance` pairs. | ||
* | ||
* @since 0.2.0 | ||
* @access private | ||
* @var array | ||
*/ | ||
private $registered_block_types = array(); | ||
|
||
/** | ||
* Container for the main instance of the class. | ||
* | ||
* @since 0.2.0 | ||
* @access private | ||
* @static | ||
* @var WP_Block_Type_Registry|null | ||
*/ | ||
private static $instance = null; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Is this going to facilitate testing since the instance can't be cleared? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think testing should be possible as we could manually spin up instances of the |
||
|
||
/** | ||
* Registers a block type. | ||
* | ||
* @since 0.2.0 | ||
* @access public | ||
* | ||
* @param string $name Block type name including namespace. | ||
* @param array $args { | ||
* Array of block type arguments. Any arguments may be defined, however the following | ||
* ones are supported by default. | ||
* | ||
* @type callable $render Callback used to render blocks of this block type. | ||
* } | ||
* @return WP_Block_Type|false The registered block type on success, or false on failure. | ||
*/ | ||
public function register( $name, $args ) { | ||
if ( ! is_string( $name ) ) { | ||
$message = __( 'Block type names must be strings.' ); | ||
_doing_it_wrong( __FUNCTION__, $message, '0.1.0' ); | ||
return false; | ||
} | ||
|
||
$name_matcher = '/^[a-z0-9-]+\/[a-z0-9-]+$/'; | ||
if ( ! preg_match( $name_matcher, $name ) ) { | ||
$message = __( 'Block type names must contain a namespace prefix. Example: my-plugin/my-custom-block-type' ); | ||
_doing_it_wrong( __FUNCTION__, $message, '0.1.0' ); | ||
return false; | ||
} | ||
|
||
if ( $this->is_registered( $name ) ) { | ||
/* translators: 1: block name */ | ||
$message = sprintf( __( 'Block type "%s" is already registered.' ), $name ); | ||
_doing_it_wrong( __FUNCTION__, $message, '0.1.0' ); | ||
return false; | ||
} | ||
|
||
$block_type = new WP_Block_Type( $name, $args ); | ||
|
||
$this->registered_block_types[ $name ] = $block_type; | ||
|
||
return $block_type; | ||
} | ||
|
||
/** | ||
* Unregisters a block type. | ||
* | ||
* @since 0.2.0 | ||
* @access public | ||
* | ||
* @param string $name Block type name including namespace. | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Like |
||
* @return WP_Block_Type|false The unregistered block type on success, or false on failure. | ||
*/ | ||
public function unregister( $name ) { | ||
if ( ! $this->is_registered( $name ) ) { | ||
/* translators: 1: block name */ | ||
$message = sprintf( __( 'Block type "%s" is not registered.' ), $name ); | ||
_doing_it_wrong( __FUNCTION__, $message, '0.1.0' ); | ||
return false; | ||
} | ||
|
||
$unregistered_block_type = $this->registered_block_types[ $name ]; | ||
unset( $this->registered_block_types[ $name ] ); | ||
|
||
return $unregistered_block_type; | ||
} | ||
|
||
/** | ||
* Retrieves a registered block type. | ||
* | ||
* @since 0.2.0 | ||
* @access public | ||
* | ||
* @param string $name Block type name including namespace. | ||
* @return WP_Block_Type|null The unregistered block type, or null if it is not registered. | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Typo: There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Fixed in a408132 |
||
*/ | ||
public function get_registered( $name ) { | ||
if ( ! $this->is_registered( $name ) ) { | ||
return null; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Instead of having mixed return types, is there an advantage to returning an instance of a There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. That is a good question. Generally returning something like you say (implementing a general block type interface) makes sense, however these practices have never been followed by WordPress core. I'm wary of introducing such a pattern here as it contradicts of what people are used from working with core. I think such a topic belongs into a discussion of a bigger scope, which I'd love to have a working group for at some point (we had a similar discussion at WCEU). |
||
} | ||
|
||
return $this->registered_block_types[ $name ]; | ||
} | ||
|
||
/** | ||
* Checks if a block type is registered. | ||
* | ||
* @since 0.2.0 | ||
* @access public | ||
* | ||
* @param tring $name Block type name including namespace. | ||
* @return bool True if the block type is registered, false otherwise. | ||
*/ | ||
public function is_registered( $name ) { | ||
return isset( $this->registered_block_types[ $name ] ); | ||
} | ||
|
||
/** | ||
* Utility method to retrieve the main instance of the class. | ||
* | ||
* The instance will be created if it does not exist yet. | ||
* | ||
* @since 0.2.0 | ||
* @access public | ||
* @static | ||
* | ||
* @return WP_Block_Type_Registry The main instance. | ||
*/ | ||
public static function get_instance() { | ||
if ( null === self::$instance ) { | ||
self::$instance = new self(); | ||
} | ||
|
||
return self::$instance; | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,74 @@ | ||
<?php | ||
/** | ||
* Blocks API: WP_Block_Type class | ||
* | ||
* @package gutenberg | ||
* @since 0.2.0 | ||
*/ | ||
|
||
/** | ||
* Core class representing a block type. | ||
* | ||
* @since 0.2.0 | ||
* | ||
* @see register_block_type() | ||
*/ | ||
final class WP_Block_Type { | ||
/** | ||
* Block type key. | ||
* | ||
* @since 0.2.0 | ||
* @access public | ||
* @var string | ||
*/ | ||
public $name; | ||
|
||
/** | ||
* Block type render callback. | ||
* | ||
* @since 0.2.0 | ||
* @access public | ||
* @var callable | ||
*/ | ||
public $render; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This should perhaps be called |
||
|
||
/** | ||
* Constructor. | ||
* | ||
* Will populate object properties from the provided arguments. | ||
* | ||
* @since 0.2.0 | ||
* @access public | ||
* | ||
* @see register_block_type() | ||
* | ||
* @param string $block_type Block type name including namespace. | ||
* @param array|string $args Optional. Array or string of arguments for registering a block type. | ||
* Default empty array. | ||
*/ | ||
public function __construct( $block_type, $args = array() ) { | ||
$this->name = $block_type; | ||
|
||
$this->set_props( $args ); | ||
} | ||
|
||
/** | ||
* Sets block type properties. | ||
* | ||
* @since 0.2.0 | ||
* @access public | ||
* | ||
* @param array|string $args Array or string of arguments for registering a block type. | ||
*/ | ||
public function set_props( $args ) { | ||
$args = wp_parse_args( $args, array( | ||
'render' => null, | ||
) ); | ||
|
||
$args['name'] = $this->name; | ||
|
||
foreach ( $args as $property_name => $property_value ) { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This will set props even if they are undeclared. Would that be desired? Should something like this be done instead: https://github.com/WordPress/wordpress-develop/blob/4.8.0/src/wp-includes/class-wp-customize-setting.php#L179-L184 There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think it should, in order to allow setting properties that are unsupported by core, but possibly are by plugins doing something custom with Gutenberg. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. In that case would it not be better to allow There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. That might be a better idea, but I'm not entirely sure. Allowing all I like the idea of passing a custom instance directly (to support sub-classes), but supporting any |
||
$this->$property_name = $property_value; | ||
} | ||
} | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should this accept a
WP_Block_Type
instance likeregister_block_type
does?