-
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 15 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,37 @@ | |
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 | ||
* @since 0.4.0 Now also accepts a WP_Block_Type instance as first parameter. | ||
* | ||
* @param string|WP_Block_Type $name Block type name including namespace, or alternatively a | ||
* complete WP_Block_Type instance. In case a WP_Block_Type | ||
* is provided, the $args parameter will be ignored. | ||
* @param array $args { | ||
* Optional. Array of block type arguments. Any arguments may be defined, however the | ||
* ones described below are supported by default. Default empty array. | ||
* | ||
* @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 = array() ) { | ||
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. | ||
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. Should this accept a |
||
* @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 ); | ||
} | ||
|
||
/** | ||
|
@@ -79,7 +52,7 @@ function unregister_block_type( $name ) { | |
* @return string Updated post content. | ||
*/ | ||
function do_blocks( $content ) { | ||
global $wp_registered_blocks; | ||
$registry = WP_Block_Type_Registry::get_instance(); | ||
|
||
$parser = new Gutenberg_PEG_Parser; | ||
$blocks = $parser->parse( $content ); | ||
|
@@ -89,14 +62,17 @@ function do_blocks( $content ) { | |
foreach ( $blocks as $block ) { | ||
$block_name = isset( $block['blockName'] ) ? $block['blockName'] : null; | ||
$attributes = is_array( $block['attrs'] ) ? $block['attrs'] : array(); | ||
if ( $block_name && isset( $wp_registered_blocks[ $block_name ] ) ) { | ||
$content_after_blocks .= call_user_func( | ||
$wp_registered_blocks[ $block_name ]['render'], | ||
$attributes | ||
); | ||
} else { | ||
$content_after_blocks .= $block['rawContent']; | ||
$raw_content = isset( $block['rawContent'] ) ? $block['rawContent'] : ''; | ||
|
||
if ( $block_name ) { | ||
$block_type = $registry->get_registered( $block_name ); | ||
if ( null !== $block_type ) { | ||
$content_after_blocks .= $block_type->render( $attributes, $raw_content ); | ||
continue; | ||
} | ||
} | ||
|
||
$content_after_blocks .= $raw_content; | ||
} | ||
|
||
return $content_after_blocks; | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,170 @@ | ||
<?php | ||
/** | ||
* Blocks API: WP_Block_Type_Registry class | ||
* | ||
* @package gutenberg | ||
* @since 0.4.0 | ||
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. Should be |
||
*/ | ||
|
||
/** | ||
* Core class used for interacting with block types. | ||
* | ||
* @since 0.4.0 | ||
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. Should be |
||
*/ | ||
final class WP_Block_Type_Registry { | ||
/** | ||
* Registered block types, as `$name => $instance` pairs. | ||
* | ||
* @since 0.4.0 | ||
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. Should be |
||
* @access private | ||
* @var array | ||
*/ | ||
private $registered_block_types = array(); | ||
|
||
/** | ||
* Container for the main instance of the class. | ||
* | ||
* @since 0.4.0 | ||
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. Should be |
||
* @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.4.0 | ||
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. Should be |
||
* @access public | ||
* | ||
* @param string|WP_Block_Type $name Block type name including namespace, or alternatively a | ||
* complete WP_Block_Type instance. In case a WP_Block_Type | ||
* is provided, the $args parameter will be ignored. | ||
* @param array $args { | ||
* Optional. Array of block type arguments. Any arguments may be defined, however the | ||
* ones described below are supported by default. Default empty array. | ||
* | ||
* @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 = array() ) { | ||
$block_type = null; | ||
if ( is_a( $name, 'WP_Block_Type' ) ) { | ||
$block_type = $name; | ||
$name = $block_type->name; | ||
} | ||
|
||
if ( ! is_string( $name ) ) { | ||
$message = __( 'Block type names must be strings.' ); | ||
_doing_it_wrong( __METHOD__, $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( __METHOD__, $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( __METHOD__, $message, '0.1.0' ); | ||
return false; | ||
} | ||
|
||
if ( ! $block_type ) { | ||
$block_type = new WP_Block_Type( $name, $args ); | ||
} | ||
|
||
$this->registered_block_types[ $name ] = $block_type; | ||
|
||
return $block_type; | ||
} | ||
|
||
/** | ||
* Unregisters a block type. | ||
* | ||
* @since 0.4.0 | ||
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. Should be |
||
* @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( __METHOD__, $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.4.0 | ||
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. Should be |
||
* @access public | ||
* | ||
* @param string $name Block type name including namespace. | ||
* @return WP_Block_Type|null The registered block type, or null if it is not registered. | ||
*/ | ||
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 ]; | ||
} | ||
|
||
/** | ||
* Retrieves all registered block types. | ||
* | ||
* @since 0.4.0 | ||
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. Should be |
||
* @access public | ||
* | ||
* @return array Associative array of `$block_type_name => $block_type` pairs. | ||
*/ | ||
public function get_all_registered() { | ||
return $this->registered_block_types; | ||
} | ||
|
||
/** | ||
* Checks if a block type is registered. | ||
* | ||
* @since 0.4.0 | ||
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. Should be |
||
* @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.4.0 | ||
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. Should be |
||
* @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,92 @@ | ||
<?php | ||
/** | ||
* Blocks API: WP_Block_Type class | ||
* | ||
* @package gutenberg | ||
* @since 0.4.0 | ||
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. Should be |
||
*/ | ||
|
||
/** | ||
* Core class representing a block type. | ||
* | ||
* @since 0.4.0 | ||
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. Should be |
||
* | ||
* @see register_block_type() | ||
*/ | ||
class WP_Block_Type { | ||
/** | ||
* Block type key. | ||
* | ||
* @since 0.4.0 | ||
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. Should be |
||
* @access public | ||
* @var string | ||
*/ | ||
public $name; | ||
|
||
/** | ||
* Block type render callback. | ||
* | ||
* @since 0.4.0 | ||
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. Should be |
||
* @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.4.0 | ||
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. Should be |
||
* @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 ); | ||
} | ||
|
||
/** | ||
* Renders the block type output for given attributes and content. | ||
* | ||
* @since 0.4.0 | ||
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. Should be |
||
* @access public | ||
* | ||
* @param array $attributes Optional. Block attributes. Default empty array. | ||
* @param string $content Optional. Raw block content. Default empty string. | ||
* @return string Rendered block type output. | ||
*/ | ||
public function render( $attributes = array(), $content = '' ) { | ||
if ( ! is_callable( $this->render ) ) { | ||
return $content; | ||
} | ||
|
||
return call_user_func( $this->render, $attributes, $content ); | ||
} | ||
|
||
/** | ||
* Sets block type properties. | ||
* | ||
* @since 0.4.0 | ||
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. Should be |
||
* @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 be
@since 0.6.0
.