Skip to content

Commit

Permalink
Blocks: Parse the arguments earlier in `register_block_type_from_meta…
Browse files Browse the repository at this point in the history
…data()`.

This makes it possible to register a block by passing an array of arguments, without the presence of a `block.json` file.

Follow-up to [48141], [49948].

Props aristath, spacedmonkey, mukesh27, costdev, audrasjb, oglekler, felipeelia, hellofromTonya.
Fixes #56865.

git-svn-id: https://develop.svn.wordpress.org/trunk@57026 602fd350-edb4-49c9-b593-d223f7449a82
  • Loading branch information
SergeyBiryukov committed Oct 28, 2023
1 parent d571da5 commit cc2133f
Show file tree
Hide file tree
Showing 2 changed files with 168 additions and 42 deletions.
90 changes: 48 additions & 42 deletions src/wp-includes/blocks.php
Original file line number Diff line number Diff line change
Expand Up @@ -352,13 +352,14 @@ function register_block_type_from_metadata( $file_or_folder, $args = array() ) {
$file_or_folder;

$is_core_block = str_starts_with( $file_or_folder, ABSPATH . WPINC );

if ( ! $is_core_block && ! file_exists( $metadata_file ) ) {
// If the block is not a core block, the metadata file must exist.
$metadata_file_exists = $is_core_block || file_exists( $metadata_file );
if ( ! $metadata_file_exists && empty( $args['name'] ) ) {
return false;
}

// Try to get metadata from the static cache for core blocks.
$metadata = false;
$metadata = array();
if ( $is_core_block ) {
$core_block_name = str_replace( ABSPATH . WPINC . '/blocks/', '', $file_or_folder );
if ( ! empty( $core_blocks_meta[ $core_block_name ] ) ) {
Expand All @@ -367,14 +368,15 @@ function register_block_type_from_metadata( $file_or_folder, $args = array() ) {
}

// If metadata is not found in the static cache, read it from the file.
if ( ! $metadata ) {
if ( $metadata_file_exists && empty( $metadata ) ) {
$metadata = wp_json_file_decode( $metadata_file, array( 'associative' => true ) );
}

if ( ! is_array( $metadata ) || empty( $metadata['name'] ) ) {
if ( ! is_array( $metadata ) || ( empty( $metadata['name'] ) && empty( $args['name'] ) ) ) {
return false;
}
$metadata['file'] = wp_normalize_path( realpath( $metadata_file ) );

$metadata['file'] = $metadata_file_exists ? wp_normalize_path( realpath( $metadata_file ) ) : null;

/**
* Filters the metadata provided for registering a block type.
Expand Down Expand Up @@ -404,6 +406,7 @@ function register_block_type_from_metadata( $file_or_folder, $args = array() ) {
$settings = array();
$property_mappings = array(
'apiVersion' => 'api_version',
'name' => 'name',
'title' => 'title',
'category' => 'category',
'parent' => 'parent',
Expand All @@ -426,18 +429,50 @@ function register_block_type_from_metadata( $file_or_folder, $args = array() ) {
foreach ( $property_mappings as $key => $mapped_key ) {
if ( isset( $metadata[ $key ] ) ) {
$settings[ $mapped_key ] = $metadata[ $key ];
if ( $textdomain && isset( $i18n_schema->$key ) ) {
if ( $metadata_file_exists && $textdomain && isset( $i18n_schema->$key ) ) {
$settings[ $mapped_key ] = translate_settings_using_i18n_schema( $i18n_schema->$key, $settings[ $key ], $textdomain );
}
}
}

if ( ! empty( $metadata['render'] ) ) {
$template_path = wp_normalize_path(
realpath(
dirname( $metadata['file'] ) . '/' .
remove_block_asset_path_prefix( $metadata['render'] )
)
);
if ( $template_path ) {
/**
* Renders the block on the server.
*
* @since 6.1.0
*
* @param array $attributes Block attributes.
* @param string $content Block default content.
* @param WP_Block $block Block instance.
*
* @return string Returns the block content.
*/
$settings['render_callback'] = static function ( $attributes, $content, $block ) use ( $template_path ) {
ob_start();
require $template_path;
return ob_get_clean();
};
}
}

$settings = array_merge( $settings, $args );

$script_fields = array(
'editorScript' => 'editor_script_handles',
'script' => 'script_handles',
'viewScript' => 'view_script_handles',
);
foreach ( $script_fields as $metadata_field_name => $settings_field_name ) {
if ( ! empty( $settings[ $metadata_field_name ] ) ) {
$metadata[ $metadata_field_name ] = $settings[ $metadata_field_name ];
}
if ( ! empty( $metadata[ $metadata_field_name ] ) ) {
$scripts = $metadata[ $metadata_field_name ];
$processed_scripts = array();
Expand Down Expand Up @@ -470,6 +505,9 @@ function register_block_type_from_metadata( $file_or_folder, $args = array() ) {
'style' => 'style_handles',
);
foreach ( $style_fields as $metadata_field_name => $settings_field_name ) {
if ( ! empty( $settings[ $metadata_field_name ] ) ) {
$metadata[ $metadata_field_name ] = $settings[ $metadata_field_name ];
}
if ( ! empty( $metadata[ $metadata_field_name ] ) ) {
$styles = $metadata[ $metadata_field_name ];
$processed_styles = array();
Expand Down Expand Up @@ -530,33 +568,6 @@ function register_block_type_from_metadata( $file_or_folder, $args = array() ) {
}
}

if ( ! empty( $metadata['render'] ) ) {
$template_path = wp_normalize_path(
realpath(
dirname( $metadata['file'] ) . '/' .
remove_block_asset_path_prefix( $metadata['render'] )
)
);
if ( $template_path ) {
/**
* Renders the block on the server.
*
* @since 6.1.0
*
* @param array $attributes Block attributes.
* @param string $content Block default content.
* @param WP_Block $block Block instance.
*
* @return string Returns the block content.
*/
$settings['render_callback'] = static function ( $attributes, $content, $block ) use ( $template_path ) {
ob_start();
require $template_path;
return ob_get_clean();
};
}
}

/**
* Filters the settings determined from the block type metadata.
*
Expand All @@ -565,14 +576,9 @@ function register_block_type_from_metadata( $file_or_folder, $args = array() ) {
* @param array $settings Array of determined settings for registering a block type.
* @param array $metadata Metadata provided for registering a block type.
*/
$settings = apply_filters(
'block_type_metadata_settings',
array_merge(
$settings,
$args
),
$metadata
);
$settings = apply_filters( 'block_type_metadata_settings', $settings, $metadata );

$metadata['name'] = ! empty( $settings['name'] ) ? $settings['name'] : $metadata['name'];

return WP_Block_Type_Registry::get_instance()->register(
$metadata['name'],
Expand Down
120 changes: 120 additions & 0 deletions tests/phpunit/tests/blocks/register.php
Original file line number Diff line number Diff line change
Expand Up @@ -599,6 +599,126 @@ public function test_metadata_not_found_in_the_current_directory() {
$this->assertFalse( $result );
}

/**
* Tests registering a block using arguments instead of a block.json file.
*
* @ticket 56865
*
* @covers ::register_block_type_from_metadata
*/
public function test_register_block_type_from_metadata_with_arguments() {
$result = register_block_type_from_metadata(
'',
array(
'api_version' => 2,
'name' => 'tests/notice-from-array',
'title' => 'Notice from array',
'category' => 'common',
'icon' => 'star',
'description' => 'Shows warning, error or success notices… (registered from an array)',
'keywords' => array(
'alert',
'message',
),
'textdomain' => 'notice-from-array',
)
);

$this->assertInstanceOf( 'WP_Block_Type', $result, 'The block was not registered' );
$this->assertSame( 2, $result->api_version, 'The API version is incorrect' );
$this->assertSame( 'tests/notice-from-array', $result->name, 'The block name is incorrect' );
$this->assertSame( 'Notice from array', $result->title, 'The block title is incorrect' );
$this->assertSame( 'common', $result->category, 'The block category is incorrect' );
$this->assertSame( 'star', $result->icon, 'The block icon is incorrect' );
$this->assertSame(
'Shows warning, error or success notices… (registered from an array)',
$result->description,
'The block description is incorrect'
);
$this->assertSameSets( array( 'alert', 'message' ), $result->keywords, 'The block keywords are incorrect' );
}

/**
* Tests that defined $args can properly override the block.json file.
*
* @ticket 56865
*
* @covers ::register_block_type_from_metadata
*/
public function test_block_registers_with_args_override() {
$result = register_block_type_from_metadata(
DIR_TESTDATA . '/blocks/notice',
array(
'name' => 'tests/notice-with-overrides',
'title' => 'Overriden title',
'style' => array( 'tests-notice-style-overridden' ),
)
);

$this->assertInstanceOf( 'WP_Block_Type', $result, 'The block was not registered' );
$this->assertSame( 2, $result->api_version, 'The API version is incorrect' );
$this->assertSame( 'tests/notice-with-overrides', $result->name, 'The block name was not overridden' );
$this->assertSame( 'Overriden title', $result->title, 'The block title was not overridden' );
$this->assertSameSets(
array( 'tests-notice-editor-script' ),
$result->editor_script_handles,
'The block editor script is incorrect'
);
$this->assertSameSets(
array( 'tests-notice-style-overridden' ),
$result->style_handles,
'The block style was not overridden'
);
$this->assertIsCallable( $result->render_callback );
}

/**
* Tests that when the `name` is missing, `register_block_type_from_metadata()`
* will return `false`.
*
* @ticket 56865
*
* @covers ::register_block_type_from_metadata
*
* @dataProvider data_register_block_registers_with_args_override_returns_false_when_name_is_missing
*
* @param string $file The metadata file.
* @param array $args Array of block type arguments.
*/
public function test_block_registers_with_args_override_returns_false_when_name_is_missing( $file, $args ) {
$this->assertFalse( register_block_type_from_metadata( $file, $args ) );
}

/**
* Data provider.
*
* @return array[]
*/
public function data_register_block_registers_with_args_override_returns_false_when_name_is_missing() {
return array(
'no block.json file and no name argument' => array(
'file' => '', // No block.json file.
'args' => array(
'title' => 'Overriden title',
'style' => array( 'tests-notice-style-overridden' ),
),
),
'existing file and args not an array' => array(
// A file that exists but is empty. This will bypass the file_exists() check.
'file' => DIR_TESTDATA . '/blocks/notice/block.js',
'args' => false,
),
'existing file and args[name] missing' => array(
// A file that exists but is empty. This will bypass the file_exists() check.
'file' => DIR_TESTDATA . '/blocks/notice/block.js',
'args' => array(
'title' => 'Overriden title',
'style' => array( 'tests-notice-style-overridden' ),
),
),
);
}

/**
* Tests that the function returns the registered block when the `block.json`
* is found in the fixtures directory.
Expand Down

0 comments on commit cc2133f

Please sign in to comment.