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

Introduce proper PHP classes for interacting with block types #1322

Merged
merged 20 commits into from
Jul 26, 2017
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
20 commits
Select commit Hold shift + click to select a range
ce45293
Introduce WP_Block_Type and WP_Block_Type_Registry classes.
Jun 20, 2017
d213309
Use WP_Block_Type_Registry class to manage registered block types.
Jun 20, 2017
9aa5fde
Use proper Block Type terminology in warnings instead of simply Block.
Jun 20, 2017
a84b787
Adjust version numbers for WP_Block_Type and WP_Block_Type_Registry c…
Jun 20, 2017
8e379bf
Remove register_block_type_args filter.
Jun 20, 2017
a408132
Fix typo in WP_Block_Type_Registry::get_registered() docs.
Jun 23, 2017
6542555
Use correct __METHOD__ constant instead of __FUNCTION__ in WP_Block_T…
Jun 23, 2017
e0539c7
Implement method to get all registered block types.
Jun 23, 2017
ecc6dbb
Add tests for WP_Block_Type_Registry and adjust existing tests to work.
Jun 23, 2017
c8e1464
Add missing doc comment for block type registry test property.
Jun 23, 2017
d45da39
Merge remote-tracking branch 'upstream/master' into add/1321-block-cl…
Jul 2, 2017
547b89b
Introduce WP_Block_Type::render() method and support passing block at…
Jul 2, 2017
75a065a
Fix coding standards.
Jul 2, 2017
ca02a61
Add support for passing a WP_Block_Type instance to register_block_ty…
Jul 2, 2017
11c47bc
Adjust version numbers.
Jul 2, 2017
6b3ec26
Rename the WP_Block_Type render property to render_callback.
Jul 3, 2017
d45a92a
Merge remote-tracking branch 'upstream/master' into add/1321-block-cl…
Jul 10, 2017
c296165
Merge remote-tracking branch 'upstream/master' into add/1321-block-cl…
Jul 26, 2017
b7d1824
Update PHP block type version numbers.
Jul 26, 2017
21d8ee6
Support passing a WP_Block_Type instance to unregister_block_type().
Jul 26, 2017
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
2 changes: 2 additions & 0 deletions gutenberg.php
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@
require_once dirname( __FILE__ ) . '/lib/init-checks.php';
if ( gutenberg_can_init() ) {
// Load API functions, register scripts and actions, etc.
require_once dirname( __FILE__ ) . '/lib/class-wp-block-type.php';
require_once dirname( __FILE__ ) . '/lib/class-wp-block-type-registry.php';
require_once dirname( __FILE__ ) . '/lib/blocks.php';
require_once dirname( __FILE__ ) . '/lib/client-assets.php';
require_once dirname( __FILE__ ) . '/lib/compat.php';
Expand Down
95 changes: 34 additions & 61 deletions lib/blocks.php
Original file line number Diff line number Diff line change
Expand Up @@ -9,64 +9,39 @@
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.6.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 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.', 'gutenberg' );
_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', 'gutenberg' );
_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.', 'gutenberg' ), $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
* @since 0.6.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.
* @return WP_Block_Type|false The unregistered block type on success, or false on failure.
*/
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.', 'gutenberg' ), $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 );
}

/**
Expand All @@ -91,7 +66,7 @@ function gutenberg_parse_blocks( $content ) {
* @return string Updated post content.
*/
function do_blocks( $content ) {
global $wp_registered_blocks;
$registry = WP_Block_Type_Registry::get_instance();

$blocks = gutenberg_parse_blocks( $content );

Expand All @@ -100,20 +75,18 @@ 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 ] ) ) {
$raw_content = isset( $block['rawContent'] ) ? $block['rawContent'] : null;

$content = null;
if ( isset( $block['rawContent'] ) ) {
$content = $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 .= call_user_func(
$wp_registered_blocks[ $block_name ]['render'],
$attributes,
$content
);
} else {
$content_after_blocks .= $block['rawContent'];
if ( $raw_content ) {
$content_after_blocks .= $raw_content;
}
}

Expand Down
2 changes: 1 addition & 1 deletion lib/blocks/latest-posts.php
Original file line number Diff line number Diff line change
Expand Up @@ -85,5 +85,5 @@ function gutenberg_render_block_core_latest_posts( $attributes ) {
}

register_block_type( 'core/latest-posts', array(
'render' => 'gutenberg_render_block_core_latest_posts',
'render_callback' => 'gutenberg_render_block_core_latest_posts',
) );
175 changes: 175 additions & 0 deletions lib/class-wp-block-type-registry.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,175 @@
<?php
/**
* Blocks API: WP_Block_Type_Registry class
*
* @package gutenberg
* @since 0.6.0
*/

/**
* Core class used for interacting with block types.
*
* @since 0.6.0
*/
final class WP_Block_Type_Registry {
/**
* Registered block types, as `$name => $instance` pairs.
*
* @since 0.6.0
* @access private
* @var array
*/
private $registered_block_types = array();

/**
* Container for the main instance of the class.
*
* @since 0.6.0
* @access private
* @static
* @var WP_Block_Type_Registry|null
*/
private static $instance = null;
Copy link
Member

Choose a reason for hiding this comment

The 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?

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 think testing should be possible as we could manually spin up instances of the WP_Block_Type_Registry class for most test cases, as we wouldn't need to use the main instance. Once tests have been added, we'll know for sure.


/**
* Registers a block type.
*
* @since 0.6.0
* @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 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.', 'gutenberg' );
_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', 'gutenberg' );
_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.', 'gutenberg' ), $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.6.0
* @access public
*
* @param string|WP_Block_Type $name Block type name including namespace, or alternatively a
* complete WP_Block_Type instance.
* @return WP_Block_Type|false The unregistered block type on success, or false on failure.
*/
public function unregister( $name ) {
if ( is_a( $name, 'WP_Block_Type' ) ) {
$name = $name->name;
}

if ( ! $this->is_registered( $name ) ) {
/* translators: 1: block name */
$message = sprintf( __( 'Block type "%s" is not registered.', 'gutenberg' ), $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.6.0
* @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;
Copy link
Member

Choose a reason for hiding this comment

The 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 WP_Null_Block_Type (not yet defined)?

Copy link
Member Author

Choose a reason for hiding this comment

The 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.6.0
* @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.6.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.6.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;
}
}
Loading