-
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
Conversation
I've not yet adjusted unit tests to the changes. Will do that later this week. |
lib/class-wp-block-type.php
Outdated
* @param array $args Array of arguments for registering a block type. | ||
* @param string $block_type Block type key. | ||
*/ | ||
$args = apply_filters( 'register_block_type_args', $args, $this->name ); |
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.
What's the use case for this?
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.
I liked the idea of opening up block types for extensions. A similar filter is also available when registering post types, and it could be used here to add custom block type arguments that could be pre-populated by other default arguments.
If you think we should leave this out for now, I'm not opposed. It's probably too early to figure out a specific use-case where this can come in handy.
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.
I think it would be better to only add the filter when we know we need it, rather than add filters up front when we may not know that they will ever be used.
lib/class-wp-block-type.php
Outdated
|
||
$args['name'] = $this->name; | ||
|
||
foreach ( $args as $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.
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 comment
The 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. WP_Post_Type
and WP_Taxonomy
work the same way, and it has proven to make sense there.
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.
In that case would it not be better to allow WP_Block_Type
to be extensible so that the subclasses could be passed to the register
method as opposed to just the args? This would be similar to how WP_Customize_Manager::add_control()
allows you to pass the ID and args, or just a previously instantiated WP_Customize_Control
.
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.
That might be a better idea, but I'm not entirely sure. Allowing all $args
to be set as property especially makes sense with a filter such as register_block_type_args
(which per the above we're gonna remove for now, but might re-add it again at some point).
I like the idea of passing a custom instance directly (to support sub-classes), but supporting any $args
key to be set as property makes it possible (in collaboration with a filter) to provide extension points that could affect all block types, regardless of their class. Let's wait for more opinions on this, I would say.
* @static | ||
* @var WP_Block_Type_Registry|null | ||
*/ | ||
private static $instance = null; |
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.
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 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.
Hi @felixarntz, Great work so far! I like that we are moving towards a more class based approach, and it seems to be moving quick! A quick suggestion: it could be an interesting approach to change the |
When introducing a |
@BE-Webdesign I generally like the idea of providing a block type in Given that we're not used to this behavior of functions, I rather suggest simply supporting a Supporting registration of multiple block types in one might be useful, we could add a simple wrapper for the singular registration method to support this. Not entirely sure if we need to have this though. @westonruter That makes sense. As said before, I'd prefer if we worked on this in another PR after this one is ready. |
lib/blocks.php
Outdated
* @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 comment
The reason will be displayed to describe this comment to others. Learn more.
What benefit is there to returning a WP_Block_Type
object just after it has been unregistered?
In case someone wants to amend it and re-register it?
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.
It's a shortcut to avoid having to do a separate getter call, yes. This was also implemented in the Customizer for calls like WP_Customize_Manager::add_setting()
. Previously it returned void
but not it returns the WP_Customize_Setting
instance which is particularly useful if you provided an $args
array instead of a subclassed instance of WP_Customize_Setting
. See https://core.trac.wordpress.org/ticket/34596
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.
Oh, woops. I missed that this is in the unregister call. Nevertheless, seems useful to return. As essentially if false
is the return value then nothing was unregistered, whereas if it is truthy (an instance of WP_Block_Type
then the unregistration was successful.
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.
I get the value of something truthy being returned to indicate a successful unregister, but why not true
? i.e. what could a developer be reasonably expected to do with this object (other than tweak and re-register)? Is there any drawback compared to just returning true
? Do other situations in WP return something more than a boolean when something has been unregistered?
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.
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 array_shift()
, array_pop()
or array_splice()
: The moment you remove something, also return what has been removed on success.
lib/class-wp-block-type-registry.php
Outdated
* @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 comment
The reason will be displayed to describe this comment to others. Learn more.
Typo: unregistered
=> registered
.
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.
Fixed in a408132
In ecc6dbb, I added a new test class specific to methods of In the |
What's left here? Should we add support for passing a |
*/ | ||
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 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)?
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.
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).
I think passing subclasses of |
…tributes and content to it (see #1631).
d45da39 fixes the merge conflicts that have come up with recent Gutenberg upstream changes. 547b89b introduces a |
ca02a61 adds support for passing a I think once these changes have been reviewed and accepted, the PR should be good. I'd prefer if we work on further enhancements to the new infrastructure through separate issues / PRs. |
lib/class-wp-block-type.php
Outdated
* @access public | ||
* @var callable | ||
*/ | ||
public $render; |
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.
This should perhaps be called render_callback
to help disambiguate from render
the method. This is similar to what WP_Customize_Control
does with its active
method and active_callback
member variable.
6b3ec26 renames the |
For some more thoughts on how the PHP API should look, see https://twitter.com/Josh412/status/883061703004631041 |
@westonruter I don't think it makes sense to suddenly start using such patterns in a feature project, as part of a relatively unrelated PR. In short: Introducing a WordPress container for a random PR in Gutenberg seems wrong to me. Introducing it after a more thorough general discussion, not connected to a specific code change, makes much more sense I think. |
@westonruter What is left to do here to get ready for merge? |
Probably a 👍 from @mtias. Note that I was suggesting a similar in the JS API, but it was not endorsed: #362 (comment) |
I'm by far not a JS expert, but there are probably reasons for it not to work the same as in PHP. All I know is that in PHP these classes are a much better practice than globals and random arrays. :) @mtias What are your thoughts? Any objections, suggestions? |
Let's merge this soon; thank you @felixarntz for staying on top of it. The existing global variable was written because it was the quickest way to get the code working; this approach is much better. Regarding differences between JS and PHP APIs: I think the main benefit of this approach is encapsulation, which ES modules also give us. PHP and JS (especially ES6) are very different languages, and these differences should imply different coding styles. It looks like there are a few minor things that need to be updated. I'll leave review comments. |
lib/class-wp-block-type-registry.php
Outdated
* @since 0.4.0 | ||
* @access public | ||
* | ||
* @param string $name Block type name including namespace. |
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.
Like register
does, should this function also accept a WP_Block_Type
object?
lib/blocks.php
Outdated
* 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 comment
The reason will be displayed to describe this comment to others. Learn more.
Should this accept a WP_Block_Type
instance like register_block_type
does?
lib/class-wp-block-type-registry.php
Outdated
* 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 comment
The reason will be displayed to describe this comment to others. Learn more.
Should be @since 0.6.0
.
lib/class-wp-block-type-registry.php
Outdated
/** | ||
* 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 comment
The reason will be displayed to describe this comment to others. Learn more.
Should be @since 0.6.0
.
lib/class-wp-block-type-registry.php
Outdated
/** | ||
* Registered block types, as `$name => $instance` pairs. | ||
* | ||
* @since 0.4.0 |
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
.
lib/class-wp-block-type.php
Outdated
/** | ||
* Block type key. | ||
* | ||
* @since 0.4.0 |
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
.
lib/class-wp-block-type.php
Outdated
/** | ||
* Block type render callback. | ||
* | ||
* @since 0.4.0 |
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
.
lib/class-wp-block-type.php
Outdated
* | ||
* 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 comment
The reason will be displayed to describe this comment to others. Learn more.
Should be @since 0.6.0
.
lib/class-wp-block-type.php
Outdated
/** | ||
* 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 comment
The reason will be displayed to describe this comment to others. Learn more.
Should be @since 0.6.0
.
lib/class-wp-block-type.php
Outdated
/** | ||
* Sets block type properties. | ||
* | ||
* @since 0.4.0 |
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
.
I haven't had a chance to read this one with attention, but I hope to get to it in the next couple days. |
@nylen Will make the version number changes later today! |
👍 There was one other change in there too, about letting the |
Thanks for working on this one. With further structure in place, it begins to highlight a drift between editor-client and back-end regarding which blocks are registered or available at any given point in time (which is also not absolute) that may require us thinking at a bit higher level. In a way, behaviour is ok to be different since they are entirely separate paradigms, but presence may require us to look at a more abstracted way of defining a block—say, a That may become more relevant if we want to have access to a reliable list of potentially available blocks on the back-end for something like storing a default block in Settings → Writing, or for constructing PHP templates declaring blocks that are static (meaning they don't have a representation on the back-end). But equally not sure if this is needed in any case, as there are ways to handle it. In any case, let's merge this once the version number changes are in. |
@nylen I updated the version numbers and also added support passing a |
This could tie in with using JSON schema to describe block attributes: #1905 |
This is a PR for #1321.
It currently does the following:
WP_Block_Type
class that follows the models of other core classes for entity types, such asWP_Post_Type
andWP_Taxonomy
.WP_Block_Type_Registry
class as a container forWP_Block_Type
objects, including methods to register, unregister and retrieve block types. A staticget_instance()
method is provided for access to the main class instance.register_block_type()
andunregister_block_type()
are now wrappers for methods of theWP_Block_Type_Registry
class. Thedo_blocks()
function has been adjusted to use that class as well. The previous$wp_registered_blocks
global no longer exists.Further ideas/observations:
WP_Block_Type
orWP_Block_Type_Registry
to ensure therender
argument is set. We could either continue to have it optional and introduce a check whether it is set, or alternatively require it and not register a block type without the argument. I think I'm leaning towards leaving it optional because otherwise it should not be part of$args
, but a required parameter.WP_Block
class that represents an actual block parsed from the content. I'd rather discuss this in a separate issue / PR though._doing_it_wrong()
it might be a better idea to return aWP_Error
object if something goes wrong when registering or unregistering a block type.parse_block_attributes()
anddo_blocks()
do not follow the WordPress documentation standards. We can fix this here, but it could also be part of another PR if that's preferred.