From 94c6807a9604ee3de47d98700ea236239283d626 Mon Sep 17 00:00:00 2001 From: Matt Sherman Date: Thu, 7 Sep 2023 14:40:30 -0400 Subject: [PATCH 01/17] Implement woocommerce_block_template_after_add_block action --- .../Admin/BlockTemplates/BlockContainerTrait.php | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/plugins/woocommerce/src/Internal/Admin/BlockTemplates/BlockContainerTrait.php b/plugins/woocommerce/src/Internal/Admin/BlockTemplates/BlockContainerTrait.php index ad2983ada42..11243c847cd 100644 --- a/plugins/woocommerce/src/Internal/Admin/BlockTemplates/BlockContainerTrait.php +++ b/plugins/woocommerce/src/Internal/Admin/BlockTemplates/BlockContainerTrait.php @@ -40,6 +40,19 @@ trait BlockContainerTrait { $root_template = $block->get_root_template(); $root_template->cache_block( $block ); $this->inner_blocks[] = &$block; + + /** + * Action called after a block is added to a block container. + * + * This action can be used to perform actions after a block is added to the block container, + * such as adding a dependent block. + * + * @param BlockInterface $block The block. + * + * @since 8.2.0 + */ + do_action( 'woocommerce_block_template_after_add_block', $block ); + return $block; } From c37cd715d7ea77cc6d978e6fa1cd7eb1a460b1d7 Mon Sep 17 00:00:00 2001 From: Matt Sherman Date: Mon, 11 Sep 2023 19:58:32 -0400 Subject: [PATCH 02/17] Add BlockInterface::is_detached() --- .../Admin/BlockTemplates/BlockInterface.php | 12 +++---- .../BlockTemplates/BlockTemplateInterface.php | 7 ---- .../BlockTemplates/ContainerInterface.php | 7 ++++ .../Admin/BlockTemplates/AbstractBlock.php | 25 +++++-------- .../BlockTemplates/BlockContainerTrait.php | 29 ++++++++++++--- .../Admin/BlockTemplates/BlockTest.php | 35 +++++++++++++++---- 6 files changed, 74 insertions(+), 41 deletions(-) diff --git a/plugins/woocommerce/src/Admin/BlockTemplates/BlockInterface.php b/plugins/woocommerce/src/Admin/BlockTemplates/BlockInterface.php index 0302272f0b2..e4c4a382ab9 100644 --- a/plugins/woocommerce/src/Admin/BlockTemplates/BlockInterface.php +++ b/plugins/woocommerce/src/Admin/BlockTemplates/BlockInterface.php @@ -62,22 +62,20 @@ interface BlockInterface { /** * Get the parent container that the block belongs to. - * - * @throws \RuntimeException If the block does not have a parent. */ - public function &get_parent(): ?ContainerInterface; + public function &get_parent(): ContainerInterface; /** * Get the root template that the block belongs to. - * - * @throws \RuntimeException If the block does not have a root template. */ public function &get_root_template(): BlockTemplateInterface; /** - * Detach the block from its parent and root template. + * Check if the block is detached from its parent or root template. + * + * @return bool True if the block is detached from its parent or root template. */ - public function detach(); + public function is_detached(): bool; /** * Get the block configuration as a formatted template. diff --git a/plugins/woocommerce/src/Admin/BlockTemplates/BlockTemplateInterface.php b/plugins/woocommerce/src/Admin/BlockTemplates/BlockTemplateInterface.php index 8e61e853241..3da4a2a1e16 100644 --- a/plugins/woocommerce/src/Admin/BlockTemplates/BlockTemplateInterface.php +++ b/plugins/woocommerce/src/Admin/BlockTemplates/BlockTemplateInterface.php @@ -26,13 +26,6 @@ interface BlockTemplateInterface extends ContainerInterface { */ public function get_area(): string; - /** - * Get a block by ID. - * - * @param string $block_id The block ID. - */ - public function get_block( string $block_id ): ?BlockInterface; - /** * Generate a block ID based on a base. * diff --git a/plugins/woocommerce/src/Admin/BlockTemplates/ContainerInterface.php b/plugins/woocommerce/src/Admin/BlockTemplates/ContainerInterface.php index 3119e67b0c8..32305403183 100644 --- a/plugins/woocommerce/src/Admin/BlockTemplates/ContainerInterface.php +++ b/plugins/woocommerce/src/Admin/BlockTemplates/ContainerInterface.php @@ -16,6 +16,13 @@ interface ContainerInterface { */ public function get_formatted_template(): array; + /** + * Get a block by ID. + * + * @param string $block_id The block ID. + */ + public function get_block( string $block_id ): ?BlockInterface; + /** * Removes a block from the container. * diff --git a/plugins/woocommerce/src/Internal/Admin/BlockTemplates/AbstractBlock.php b/plugins/woocommerce/src/Internal/Admin/BlockTemplates/AbstractBlock.php index 1551b7cd959..0e1266192da 100644 --- a/plugins/woocommerce/src/Internal/Admin/BlockTemplates/AbstractBlock.php +++ b/plugins/woocommerce/src/Internal/Admin/BlockTemplates/AbstractBlock.php @@ -161,38 +161,29 @@ class AbstractBlock implements BlockInterface { /** * Get the template that this block belongs to. - * - * @throws \RuntimeException If the block does not have a root template. */ public function &get_root_template(): BlockTemplateInterface { - if ( is_null( $this->root_template ) ) { - throw new \RuntimeException( 'The block does not have a root template.' ); - } - return $this->root_template; } /** * Get the parent block container. - * - * @throws \RuntimeException If the block does not have a parent. */ public function &get_parent(): ContainerInterface { - if ( is_null( $this->parent ) ) { - throw new \RuntimeException( 'The block does not have a parent.' ); - } - return $this->parent; } /** - * Detach the block from its parent block container and the template it belongs to. + * Check if the block is detached from its parent block container or the template it belongs to. + * + * @return bool True if the block is detached from its parent block container or the template it belongs to. */ - public function detach() { - $this->parent = null; - $this->root_template = null; - } + public function is_detached(): bool { + $is_in_parent = $this->parent->get_block( $this->id ) === $this; + $is_in_root_template = $this->get_root_template()->get_block( $this->id ) === $this; + return ! $is_in_parent || ! $is_in_root_template; + } /** * Get the block configuration as a formatted template. * diff --git a/plugins/woocommerce/src/Internal/Admin/BlockTemplates/BlockContainerTrait.php b/plugins/woocommerce/src/Internal/Admin/BlockTemplates/BlockContainerTrait.php index 11243c847cd..eaaf9171462 100644 --- a/plugins/woocommerce/src/Internal/Admin/BlockTemplates/BlockContainerTrait.php +++ b/plugins/woocommerce/src/Internal/Admin/BlockTemplates/BlockContainerTrait.php @@ -77,6 +77,31 @@ trait BlockContainerTrait { return $this->is_block_descendant( $parent ); } + /** + * Get a block by ID. + * + * @param string $block_id The block ID. + */ + public function get_block( string $block_id ): ?BlockInterface { + foreach ( $this->inner_blocks as $block ) { + if ( $block->get_id() === $block_id ) { + return $block; + } + } + + foreach ( $this->inner_blocks as $block ) { + if ( $block instanceof ContainerInterface ) { + $block = $block->get_block( $block_id ); + + if ( $block ) { + return $block; + } + } + } + + return null; + } + /** * Remove a block from the block container. * @@ -108,10 +133,6 @@ trait BlockContainerTrait { $parent = $block->get_parent(); $parent->remove_inner_block( $block ); - - // Detach block from parent and root template. - $block->detach(); - } /** diff --git a/plugins/woocommerce/tests/php/src/Internal/Admin/BlockTemplates/BlockTest.php b/plugins/woocommerce/tests/php/src/Internal/Admin/BlockTemplates/BlockTest.php index 2b37a715189..f7283f1f3dc 100644 --- a/plugins/woocommerce/tests/php/src/Internal/Admin/BlockTemplates/BlockTest.php +++ b/plugins/woocommerce/tests/php/src/Internal/Admin/BlockTemplates/BlockTest.php @@ -133,9 +133,15 @@ class BlockTest extends WC_Unit_Test_Case { 'Failed asserting that the child block was removed from the root template.' ); - $this->expectException( \RuntimeException::class ); + $this->assertNull( + $block->get_block( 'test-block-id-2' ), + 'Failed asserting that the child block was removed from the parent.' + ); - $child_block->get_parent(); + $this->assertTrue( + $child_block->is_detached(), + 'Failed asserting that the child block is detached from its parent and root template.' + ); } /** @@ -166,9 +172,15 @@ class BlockTest extends WC_Unit_Test_Case { 'Failed asserting that the nested descendent block was removed from the root template.' ); - $this->expectException( \RuntimeException::class ); + $this->assertNull( + $block->get_block( 'test-block-id-2' ), + 'Failed asserting that the nested descendent block was removed from the parent.' + ); - $child_block->get_parent(); + $this->assertTrue( + $child_block->is_detached(), + 'Failed asserting that the nested descendent block is detached from its parent and root template.' + ); } /** @@ -204,9 +216,20 @@ class BlockTest extends WC_Unit_Test_Case { 'Failed asserting that the nested descendent block was removed from the root template.' ); - $this->expectException( \RuntimeException::class ); + $this->assertNull( + $block->get_block( 'test-block-id-2' ), + 'Failed asserting that the child block was removed from the parent.' + ); - $child_block->get_parent(); + $this->assertTrue( + $block->is_detached(), + 'Failed asserting that the block is detached from its parent and root template.' + ); + + $this->assertTrue( + $child_block->is_detached(), + 'Failed asserting that the child block is detached from its parent and root template.' + ); } /** From ea1b1854e2add73d73cc26e95c1f5ef6c54ecea4 Mon Sep 17 00:00:00 2001 From: Matt Sherman Date: Mon, 11 Sep 2023 20:09:20 -0400 Subject: [PATCH 03/17] Clean up exceptions in add_inner_block --- .../Admin/BlockTemplates/BlockContainerTrait.php | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/plugins/woocommerce/src/Internal/Admin/BlockTemplates/BlockContainerTrait.php b/plugins/woocommerce/src/Internal/Admin/BlockTemplates/BlockContainerTrait.php index eaaf9171462..275da2c9799 100644 --- a/plugins/woocommerce/src/Internal/Admin/BlockTemplates/BlockContainerTrait.php +++ b/plugins/woocommerce/src/Internal/Admin/BlockTemplates/BlockContainerTrait.php @@ -27,18 +27,18 @@ trait BlockContainerTrait { * @throws \ValueError If the block configuration is invalid. * @throws \ValueError If a block with the specified ID already exists in the template. * @throws \UnexpectedValueException If the block container is not the parent of the block. + * @throws \UnexpectedValueException If the block container's root template is not the same as the block's root template. */ protected function &add_inner_block( BlockInterface $block ): BlockInterface { - if ( ! $block instanceof BlockInterface ) { - throw new \UnexpectedValueException( 'The block must return an instance of BlockInterface.' ); - } - if ( $block->get_parent() !== $this ) { throw new \UnexpectedValueException( 'The block container is not the parent of the block.' ); } - $root_template = $block->get_root_template(); - $root_template->cache_block( $block ); + if ( $block->get_root_template() !== $this->get_root_template() ) { + throw new \UnexpectedValueException( 'The block container\'s root template is not the same as the block\'s root template.' ); + } + + $this->get_root_template()->cache_block( $block ); $this->inner_blocks[] = &$block; /** From f3455e5102dd7beb6725b7fc5e76a9c6904f2410 Mon Sep 17 00:00:00 2001 From: Matt Sherman Date: Mon, 11 Sep 2023 20:40:58 -0400 Subject: [PATCH 04/17] Add tests for BlockInterface::is_detached() --- .../Admin/BlockTemplates/BlockTest.php | 43 +++++++++++++++++++ 1 file changed, 43 insertions(+) diff --git a/plugins/woocommerce/tests/php/src/Internal/Admin/BlockTemplates/BlockTest.php b/plugins/woocommerce/tests/php/src/Internal/Admin/BlockTemplates/BlockTest.php index f7283f1f3dc..17ab308fe59 100644 --- a/plugins/woocommerce/tests/php/src/Internal/Admin/BlockTemplates/BlockTest.php +++ b/plugins/woocommerce/tests/php/src/Internal/Admin/BlockTemplates/BlockTest.php @@ -289,6 +289,49 @@ class BlockTest extends WC_Unit_Test_Case { ); } + /** + * Test that a block added to a detached block is detached. + */ + public function test_block_added_to_detached_block_is_detached() { + $template = new BlockTemplate(); + + $block = $template->add_block( + [ + 'id' => 'test-block-id', + 'blockName' => 'test-block-name', + ] + ); + + $template->remove_block( 'test-block-id' ); + + $child_block = $block->add_block( + [ + 'id' => 'test-block-id-2', + 'blockName' => 'test-block-name-2', + ] + ); + + $this->assertNull( + $template->get_block( 'test-block-id' ), + 'Failed asserting that the block was removed from the root template.' + ); + + $this->assertNull( + $template->get_block( 'test-block-id-2' ), + 'Failed asserting that the nested block is not in the root template.' + ); + + $this->assertNotNull( + $block->get_block( 'test-block-id-2' ), + 'Failed asserting that the nested block is in the parent.' + ); + + $this->assertTrue( + $child_block->is_detached(), + 'Failed asserting that the nested descendent block is detached from its parent and root template.' + ); + } + /** * Test that getting the block as a formatted template is structured correctly. */ From 1179a5339a40bd366839b640b813fe461097c690 Mon Sep 17 00:00:00 2001 From: Matt Sherman Date: Mon, 11 Sep 2023 20:41:21 -0400 Subject: [PATCH 05/17] Do not cache inner block in template if container is detached --- .../Internal/Admin/BlockTemplates/BlockContainerTrait.php | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/plugins/woocommerce/src/Internal/Admin/BlockTemplates/BlockContainerTrait.php b/plugins/woocommerce/src/Internal/Admin/BlockTemplates/BlockContainerTrait.php index 275da2c9799..d298a95038e 100644 --- a/plugins/woocommerce/src/Internal/Admin/BlockTemplates/BlockContainerTrait.php +++ b/plugins/woocommerce/src/Internal/Admin/BlockTemplates/BlockContainerTrait.php @@ -38,7 +38,11 @@ trait BlockContainerTrait { throw new \UnexpectedValueException( 'The block container\'s root template is not the same as the block\'s root template.' ); } - $this->get_root_template()->cache_block( $block ); + $is_detached = method_exists( $this, 'is_detached' ) && $this->is_detached(); + if ( ! $is_detached ) { + $this->get_root_template()->cache_block( $block ); + } + $this->inner_blocks[] = &$block; /** From 2be3dbe47c62578ebdb0fd9a1aa2b8a4a4c257e0 Mon Sep 17 00:00:00 2001 From: Matt Sherman Date: Mon, 11 Sep 2023 20:41:59 -0400 Subject: [PATCH 06/17] Fix up CustomBlockTest (it was creating detached blocks by mistake) --- .../BlockTemplates/CustomBlockTemplate.php | 2 +- .../Admin/BlockTemplates/CustomBlockTest.php | 30 +++++-------------- 2 files changed, 8 insertions(+), 24 deletions(-) diff --git a/plugins/woocommerce/tests/php/src/Internal/Admin/BlockTemplates/CustomBlockTemplate.php b/plugins/woocommerce/tests/php/src/Internal/Admin/BlockTemplates/CustomBlockTemplate.php index 2d791599822..6d448fb8d81 100644 --- a/plugins/woocommerce/tests/php/src/Internal/Admin/BlockTemplates/CustomBlockTemplate.php +++ b/plugins/woocommerce/tests/php/src/Internal/Admin/BlockTemplates/CustomBlockTemplate.php @@ -42,7 +42,7 @@ class CustomBlockTemplate extends AbstractBlockTemplate { * * @param array $block_config The block data. */ - public function add_custom_block( array $block_config ): BlockInterface { + public function add_custom_block( array $block_config ): CustomBlockInterface { $block = new CustomBlock( $block_config, $this->get_root_template(), $this ); return $this->add_inner_block( $block ); } diff --git a/plugins/woocommerce/tests/php/src/Internal/Admin/BlockTemplates/CustomBlockTest.php b/plugins/woocommerce/tests/php/src/Internal/Admin/BlockTemplates/CustomBlockTest.php index d9914b44390..c707f8505ec 100644 --- a/plugins/woocommerce/tests/php/src/Internal/Admin/BlockTemplates/CustomBlockTest.php +++ b/plugins/woocommerce/tests/php/src/Internal/Admin/BlockTemplates/CustomBlockTest.php @@ -7,7 +7,7 @@ use Automattic\WooCommerce\Admin\BlockTemplates\BlockTemplateInterface; use Automattic\WooCommerce\Internal\Admin\BlockTemplates\Block; use Automattic\WooCommerce\Internal\Admin\BlockTemplates\BlockTemplate; - +use Customers; use WC_Unit_Test_Case; /** @@ -18,13 +18,8 @@ class CustomBlockTest extends WC_Unit_Test_Case { * Test that the add_block method does not exist by default on blocks. */ public function test_add_block_does_not_exist() { - $template = new BlockTemplate(); - $block = new CustomBlock( - [ - 'blockName' => 'test-block-name', - ], - $template - ); + $template = new CustomBlockTemplate(); + $block = $template->add_custom_block( [ 'blockName' => 'test-block-name' ] ); $this->assertFalse( method_exists( $block, 'add_block' ) ); } @@ -33,13 +28,8 @@ class CustomBlockTest extends WC_Unit_Test_Case { * Test that a custom block inserter method inserts as expected. */ public function test_add_custom_inner_block() { - $template = new BlockTemplate(); - $block = new CustomBlock( - [ - 'blockName' => 'test-block-name', - ], - $template - ); + $template = new CustomBlockTemplate(); + $block = $template->add_custom_block( [ 'blockName' => 'test-block-name' ] ); $block->add_custom_inner_block( 'a' ); $block->add_custom_inner_block( 'b' ); @@ -72,14 +62,8 @@ class CustomBlockTest extends WC_Unit_Test_Case { * Test that a custom block is removed as expected. */ public function test_remove_custom_inner_block() { - $template = new BlockTemplate(); - $block = new CustomBlock( - [ - 'blockName' => 'test-block-name', - ], - $template - ); - + $template = new CustomBlockTemplate(); + $block = $template->add_custom_block( [ 'blockName' => 'test-block-name' ] ); $block->add_custom_inner_block( 'a' ); $block->add_custom_inner_block( 'b' ); From 4114d16383e7a0430abe3acbfe8b303fd91af886 Mon Sep 17 00:00:00 2001 From: Matt Sherman Date: Tue, 12 Sep 2023 16:51:48 -0400 Subject: [PATCH 07/17] Logger for block template modifications --- .../BlockTemplates/BlockTemplateLogger.php | 180 ++++++++++++++++++ 1 file changed, 180 insertions(+) create mode 100644 plugins/woocommerce/src/Internal/Admin/BlockTemplates/BlockTemplateLogger.php diff --git a/plugins/woocommerce/src/Internal/Admin/BlockTemplates/BlockTemplateLogger.php b/plugins/woocommerce/src/Internal/Admin/BlockTemplates/BlockTemplateLogger.php new file mode 100644 index 00000000000..0394de4c28c --- /dev/null +++ b/plugins/woocommerce/src/Internal/Admin/BlockTemplates/BlockTemplateLogger.php @@ -0,0 +1,180 @@ +logger = wc_get_logger(); + } + + /** + * Log an informational message. + * + * @param string $message Message to log. + * @param array $info Additional info to log. + */ + public function info( string $message, array $info = [] ) { + $this->logger->info( + $this->format_message( $message, $info ), + [ 'source' => 'block_template' ] + ); + } + + /** + * Log a warning message. + * + * @param string $message Message to log. + * @param array $info Additional info to log. + */ + public function warning( string $message, array $info = [] ) { + $this->logger->warning( + $this->format_message( $message, $info ), + [ 'source' => 'block_template' ] + ); + } + + /** + * Log an error message. + * + * @param string $message Message to log. + * @param array $info Additional info to log. + */ + public function error( string $message, array $info = [] ) { + $this->logger->error( + $this->format_message( $message, $info ), + [ 'source' => 'block_template' ] + ); + } + + /** + * Format a message for logging. + * + * @param string $message Message to log. + * @param array $info Additional info to log. + */ + private function format_message( string $message, array $info = [] ): string { + $formatted_message = sprintf( + "%s\n%s", + $message, + // phpcs:ignore WordPress.PHP.DevelopmentFunctions.error_log_print_r + print_r( $this->format_info( $info ), true ), + ); + + return $formatted_message; + } + + /** + * Format info for logging. + * + * @param array $info Info to log. + */ + private function format_info( array $info ): array { + $formatted_info = $info; + + if ( isset( $info['exception'] ) && $info['exception'] instanceof \Exception ) { + $formatted_info['exception'] = $this->format_exception( $info['exception'] ); + } + + if ( isset( $info['container'] ) ) { + if ( $info['container'] instanceof BlockContainerInterface ) { + $formatted_info['container'] = $this->format_block( $info['container'] ); + } elseif ( $info['container'] instanceof BlockTemplateInterface ) { + $formatted_info['container'] = $this->format_template( $info['container'] ); + } elseif ( $info['container'] instanceof BlockInterface ) { + $formatted_info['container'] = $this->format_block( $info['container'] ); + } + } + + if ( isset( $info['block'] ) && $info['block'] instanceof BlockInterface ) { + $formatted_info['block'] = $this->format_block( $info['block'] ); + } + + if ( isset( $info['template'] ) && $info['template'] instanceof BlockTemplateInterface ) { + $formatted_info['template'] = $this->format_template( $info['template'] ); + } + + return $formatted_info; + } + + /** + * Format an exception for logging. + * + * @param \Exception $exception Exception to format. + */ + private function format_exception( \Exception $exception ): array { + return [ + 'message' => $exception->getMessage(), + 'source' => "{$exception->getFile()}: {$exception->getLine()}", + // phpcs:ignore WordPress.PHP.DevelopmentFunctions.error_log_print_r + 'trace' => print_r( $this->format_exception_trace( $exception->getTrace() ), true ), + ]; + } + + /** + * Format an exception trace for logging. + * + * @param array $trace Exception trace to format. + */ + private function format_exception_trace( array $trace ): array { + $formatted_trace = []; + + foreach ( $trace as $source ) { + $formatted_trace[] = "{$source['file']}: {$source['line']}"; + } + + return $formatted_trace; + } + + /** + * Format a block template for logging. + * + * @param BlockTemplateInterface $template Template to format. + */ + private function format_template( BlockTemplateInterface $template ): string { + return "{$template->get_id()} (area: {$template->get_area()})"; + } + + /** + * Format a block for logging. + * + * @param BlockInterface $block Block to format. + */ + private function format_block( BlockInterface $block ): string { + return "{$block->get_id()} (name: {$block->get_name()})"; + } +} From b2aa1c18f30965d172490c1cd0c1461fe7e56488 Mon Sep 17 00:00:00 2001 From: Matt Sherman Date: Tue, 12 Sep 2023 16:52:18 -0400 Subject: [PATCH 08/17] Set area for product form templates --- .../ProductTemplates/AbstractProductFormTemplate.php | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/plugins/woocommerce/src/Admin/Features/ProductBlockEditor/ProductTemplates/AbstractProductFormTemplate.php b/plugins/woocommerce/src/Admin/Features/ProductBlockEditor/ProductTemplates/AbstractProductFormTemplate.php index 3b5b7ed22e8..e62be41d0aa 100644 --- a/plugins/woocommerce/src/Admin/Features/ProductBlockEditor/ProductTemplates/AbstractProductFormTemplate.php +++ b/plugins/woocommerce/src/Admin/Features/ProductBlockEditor/ProductTemplates/AbstractProductFormTemplate.php @@ -9,6 +9,13 @@ use Automattic\WooCommerce\Internal\Admin\BlockTemplates\AbstractBlockTemplate; * Block template class. */ abstract class AbstractProductFormTemplate extends AbstractBlockTemplate implements ProductFormTemplateInterface { + /** + * Get the template area. + */ + public function get_area(): string { + return 'product-form'; + } + /** * Get a group block by ID. * From 229f26274808b7eb2d905ac33c03b00ffd96e455 Mon Sep 17 00:00:00 2001 From: Matt Sherman Date: Tue, 12 Sep 2023 16:52:44 -0400 Subject: [PATCH 09/17] Fix code formatting issue --- .../src/Internal/Admin/BlockTemplates/BlockTemplateLogger.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/plugins/woocommerce/src/Internal/Admin/BlockTemplates/BlockTemplateLogger.php b/plugins/woocommerce/src/Internal/Admin/BlockTemplates/BlockTemplateLogger.php index 0394de4c28c..95c46f03189 100644 --- a/plugins/woocommerce/src/Internal/Admin/BlockTemplates/BlockTemplateLogger.php +++ b/plugins/woocommerce/src/Internal/Admin/BlockTemplates/BlockTemplateLogger.php @@ -139,9 +139,9 @@ class BlockTemplateLogger { private function format_exception( \Exception $exception ): array { return [ 'message' => $exception->getMessage(), - 'source' => "{$exception->getFile()}: {$exception->getLine()}", + 'source' => "{$exception->getFile()}: {$exception->getLine()}", // phpcs:ignore WordPress.PHP.DevelopmentFunctions.error_log_print_r - 'trace' => print_r( $this->format_exception_trace( $exception->getTrace() ), true ), + 'trace' => print_r( $this->format_exception_trace( $exception->getTrace() ), true ), ]; } From e15bc6a13adcc365a50c86e9c5215ff28ffd55ca Mon Sep 17 00:00:00 2001 From: Matt Sherman Date: Tue, 12 Sep 2023 16:53:26 -0400 Subject: [PATCH 10/17] Add after_add_block and after_remove_block hooks, and logging --- .../BlockTemplates/BlockContainerTrait.php | 182 ++++++++++++++++-- 1 file changed, 164 insertions(+), 18 deletions(-) diff --git a/plugins/woocommerce/src/Internal/Admin/BlockTemplates/BlockContainerTrait.php b/plugins/woocommerce/src/Internal/Admin/BlockTemplates/BlockContainerTrait.php index d298a95038e..2a69006f109 100644 --- a/plugins/woocommerce/src/Internal/Admin/BlockTemplates/BlockContainerTrait.php +++ b/plugins/woocommerce/src/Internal/Admin/BlockTemplates/BlockContainerTrait.php @@ -39,23 +39,23 @@ trait BlockContainerTrait { } $is_detached = method_exists( $this, 'is_detached' ) && $this->is_detached(); - if ( ! $is_detached ) { + if ( $is_detached ) { + BlockTemplateLogger::get_instance()->warning( + 'Block added to detached container. Block will not be included in the template, since the container will not be included in the template.', + [ + 'block' => $block, + 'container' => $this, + 'template' => $this->get_root_template(), + ] + ); + } else { $this->get_root_template()->cache_block( $block ); } $this->inner_blocks[] = &$block; - /** - * Action called after a block is added to a block container. - * - * This action can be used to perform actions after a block is added to the block container, - * such as adding a dependent block. - * - * @param BlockInterface $block The block. - * - * @since 8.2.0 - */ - do_action( 'woocommerce_block_template_after_add_block', $block ); + $this->do_after_add_block_action( $block ); + $this->do_after_add_specific_block_action( $block ); return $block; } @@ -131,10 +131,6 @@ trait BlockContainerTrait { $block->remove_blocks(); } - // Remove block from root template's cache. - $root_template = $this->get_root_template(); - $root_template->uncache_block( $block->get_id() ); - $parent = $block->get_parent(); $parent->remove_inner_block( $block ); } @@ -158,16 +154,29 @@ trait BlockContainerTrait { * @param BlockInterface $block The block. */ public function remove_inner_block( BlockInterface $block ) { + // Remove block from root template's cache. + $root_template = $this->get_root_template(); + $root_template->uncache_block( $block->get_id() ); + $this->inner_blocks = array_filter( $this->inner_blocks, function ( BlockInterface $inner_block ) use ( $block ) { return $inner_block !== $block; } ); + + BlockTemplateLogger::get_instance()->info( + 'Block removed from template.', + [ + 'block' => $block, + 'template' => $root_template, + ] + ); + + $this->do_after_remove_block_action( $block ); + $this->do_after_remove_specific_block_action( $block ); } - - /** * Get the inner blocks sorted by order. */ @@ -206,4 +215,141 @@ trait BlockContainerTrait { return $arr; } + + /** + * Do the `woocommerce_block_template_after_add_block` action. + * Handle exceptions thrown by the action. + * + * @param BlockInterface $block The block. + */ + private function do_after_add_block_action( BlockInterface $block ) { + try { + /** + * Action called after a block is added to a block container. + * + * This action can be used to perform actions after a block is added to the block container, + * such as adding a dependent block. + * + * @param BlockInterface $block The block. + * + * @since 8.2.0 + */ + do_action( 'woocommerce_block_template_after_add_block', $block ); + } catch ( \Exception $e ) { + $this->handle_exception_doing_action( + 'Error after adding block to template.', + 'woocommerce_block_template_after_add_block', + $block, + $e + ); + } + } + + /** + * Do the `woocommerce_block_template_area_{template_area}_after_add_block_{block_id}` action. + * Handle exceptions thrown by the action. + * + * @param BlockInterface $block The block. + */ + private function do_after_add_specific_block_action( BlockInterface $block ) { + try { + /** + * Action called after a specific block is added to a template with a specific area. + * + * This action can be used to perform actions after a specific block is added to a template with a specific area, + * such as adding a dependent block. + * + * @param BlockInterface $block The block. + * + * @since 8.2.0 + */ + do_action( "woocommerce_block_template_area_{$this->get_root_template()->get_area()}_after_add_block_{$block->get_id()}", $block ); + } catch ( \Exception $e ) { + $this->handle_exception_doing_action( + 'Error after adding block to template.', + "woocommerce_block_template_area_{$this->get_root_template()->get_area()}_after_add_block_{$block->get_id()}", + $block, + $e + ); + } + } + + /** + * Do the `woocommerce_block_template_after_remove_block` action. + * Handle exceptions thrown by the action. + * + * @param BlockInterface $block The block. + */ + private function do_after_remove_block_action( BlockInterface $block ) { + try { + /** + * Action called after a block is removed from a block container. + * + * This action can be used to perform actions after a block is removed from the block container, + * such as removing a dependent block. + * + * @param BlockInterface $block The block. + * + * @since 8.2.0 + */ + do_action( 'woocommerce_block_template_after_remove_block', $block ); + } catch ( \Exception $e ) { + $this->handle_exception_doing_action( + 'Error after removing block from template.', + 'woocommerce_block_template_after_remove_block', + $block, + $e + ); + } + } + + /** + * Do the `woocommerce_block_template_area_{template_area}_after_remove_block_{block_id}` action. + * Handle exceptions thrown by the action. + * + * @param BlockInterface $block The block. + */ + private function do_after_remove_specific_block_action( BlockInterface $block ) { + try { + /** + * Action called after a specific block is removed from a template with a specific area. + * + * This action can be used to perform actions after a specific block is removed from a template with a specific area, + * such as removing a dependent block. + * + * @param BlockInterface $block The block. + * + * @since 8.2.0 + */ + do_action( "woocommerce_block_template_area_{$this->get_root_template()->get_area()}_after_remove_block_{$block->get_id()}", $block ); + } catch ( \Exception $e ) { + $this->handle_exception_doing_action( + 'Error after removing block from template.', + "woocommerce_block_template_area_{$this->get_root_template()->get_area()}_after_remove_block_{$block->get_id()}", + $block, + $e + ); + } + } + + /** + * Handle an exception thrown by an action. + * + * @param string $message The message. + * @param string $action_tag The action tag. + * @param BlockInterface $block The block. + * @param \Exception $e The exception. + */ + private function handle_exception_doing_action( string $message, string $action_tag, BlockInterface $block, \Exception $e ) { + BlockTemplateLogger::get_instance()->error( + $message, + [ + 'exception' => $e, + 'action' => $action_tag, + 'container' => $this, + 'block' => $block, + 'template' => $this->get_root_template(), + ], + ); + } } From 0b83163d9c176910fd5be21f8ac15456d62ac35f Mon Sep 17 00:00:00 2001 From: Matt Sherman Date: Wed, 13 Sep 2023 07:58:23 -0400 Subject: [PATCH 11/17] Changelog --- .../changelog/add-block-template-after-add-remove-hooks | 4 ++++ 1 file changed, 4 insertions(+) create mode 100644 plugins/woocommerce/changelog/add-block-template-after-add-remove-hooks diff --git a/plugins/woocommerce/changelog/add-block-template-after-add-remove-hooks b/plugins/woocommerce/changelog/add-block-template-after-add-remove-hooks new file mode 100644 index 00000000000..8ed094455e4 --- /dev/null +++ b/plugins/woocommerce/changelog/add-block-template-after-add-remove-hooks @@ -0,0 +1,4 @@ +Significance: minor +Type: add + +Add after_add_block and after_remove block hooks to the block template API. From 514f96cbfeea064ce1f98b0ba4d918e54f3d656f Mon Sep 17 00:00:00 2001 From: Matt Sherman Date: Wed, 13 Sep 2023 08:13:42 -0400 Subject: [PATCH 12/17] Add BlockInterface::remove() --- .../Admin/BlockTemplates/BlockInterface.php | 5 +++++ .../Admin/BlockTemplates/AbstractBlock.php | 7 +++++++ .../Admin/BlockTemplates/BlockTest.php | 21 +++++++++++++++++++ 3 files changed, 33 insertions(+) diff --git a/plugins/woocommerce/src/Admin/BlockTemplates/BlockInterface.php b/plugins/woocommerce/src/Admin/BlockTemplates/BlockInterface.php index e4c4a382ab9..12a784b50ac 100644 --- a/plugins/woocommerce/src/Admin/BlockTemplates/BlockInterface.php +++ b/plugins/woocommerce/src/Admin/BlockTemplates/BlockInterface.php @@ -70,6 +70,11 @@ interface BlockInterface { */ public function &get_root_template(): BlockTemplateInterface; + /** + * Remove the block from its parent. + */ + public function remove(); + /** * Check if the block is detached from its parent or root template. * diff --git a/plugins/woocommerce/src/Internal/Admin/BlockTemplates/AbstractBlock.php b/plugins/woocommerce/src/Internal/Admin/BlockTemplates/AbstractBlock.php index 0e1266192da..2af5c74c0c1 100644 --- a/plugins/woocommerce/src/Internal/Admin/BlockTemplates/AbstractBlock.php +++ b/plugins/woocommerce/src/Internal/Admin/BlockTemplates/AbstractBlock.php @@ -173,6 +173,13 @@ class AbstractBlock implements BlockInterface { return $this->parent; } + /** + * Remove the block from its parent. + */ + public function remove() { + $this->parent->remove_block( $this->id ); + } + /** * Check if the block is detached from its parent block container or the template it belongs to. * diff --git a/plugins/woocommerce/tests/php/src/Internal/Admin/BlockTemplates/BlockTest.php b/plugins/woocommerce/tests/php/src/Internal/Admin/BlockTemplates/BlockTest.php index 17ab308fe59..93aeb45b223 100644 --- a/plugins/woocommerce/tests/php/src/Internal/Admin/BlockTemplates/BlockTest.php +++ b/plugins/woocommerce/tests/php/src/Internal/Admin/BlockTemplates/BlockTest.php @@ -232,6 +232,27 @@ class BlockTest extends WC_Unit_Test_Case { ); } + /** + * Test that removing a block by calling remove on it detaches it. + */ + public function test_remove_block_self() { + $template = new BlockTemplate(); + + $block = $template->add_block( + [ + 'id' => 'test-block-id', + 'blockName' => 'test-block-name', + ] + ); + + $block->remove(); + + $this->assertTrue( + $block->is_detached(), + 'Failed asserting that the block is detached from its parent and root template.' + ); + } + /** * Test that adding nested blocks sets the parent and root template correctly. */ From 036158ad4b5d9b6e119dcece8f988492f705d9be Mon Sep 17 00:00:00 2001 From: Matt Sherman Date: Wed, 13 Sep 2023 08:15:43 -0400 Subject: [PATCH 13/17] Update test function code docs --- .../php/src/Internal/Admin/BlockTemplates/BlockTest.php | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/plugins/woocommerce/tests/php/src/Internal/Admin/BlockTemplates/BlockTest.php b/plugins/woocommerce/tests/php/src/Internal/Admin/BlockTemplates/BlockTest.php index 93aeb45b223..ae340d81a9d 100644 --- a/plugins/woocommerce/tests/php/src/Internal/Admin/BlockTemplates/BlockTest.php +++ b/plugins/woocommerce/tests/php/src/Internal/Admin/BlockTemplates/BlockTest.php @@ -106,7 +106,7 @@ class BlockTest extends WC_Unit_Test_Case { } /** - * Test that removing a block from a block sets the parent and root template to null + * Test that removing a block from a block detaches it * and that the block is removed from the root template. */ public function test_remove_block() { @@ -145,7 +145,7 @@ class BlockTest extends WC_Unit_Test_Case { } /** - * Test that removing a block from a block sets the parent and root template to null + * Test that removing a block from a block detaches it * and that the block is removed from the root template, as well as any descendants. */ public function test_remove_nested_block() { @@ -184,7 +184,7 @@ class BlockTest extends WC_Unit_Test_Case { } /** - * Test that removing a block from a block sets the parent and root template to null + * Test that removing a block from a block detaches it * and that the block is removed from the root template, as well as any descendants. */ public function test_remove_block_and_descendants() { From b3ec4f982fefbcfd65cc316d1c12188415efc5ea Mon Sep 17 00:00:00 2001 From: Matt Sherman Date: Wed, 13 Sep 2023 10:55:43 -0400 Subject: [PATCH 14/17] Add unit tests for after_add_block and after_remove_block hooks --- .../BlockTemplates/BlockTemplateTest.php | 130 ++++++++++++++++++ 1 file changed, 130 insertions(+) diff --git a/plugins/woocommerce/tests/php/src/Internal/Admin/BlockTemplates/BlockTemplateTest.php b/plugins/woocommerce/tests/php/src/Internal/Admin/BlockTemplates/BlockTemplateTest.php index 30678bba3fe..6dd2c742063 100644 --- a/plugins/woocommerce/tests/php/src/Internal/Admin/BlockTemplates/BlockTemplateTest.php +++ b/plugins/woocommerce/tests/php/src/Internal/Admin/BlockTemplates/BlockTemplateTest.php @@ -2,6 +2,7 @@ namespace Automattic\WooCommerce\Tests\Internal\Admin\BlockTemplates; +use Automattic\WooCommerce\Admin\BlockTemplates\BlockInterface; use Automattic\WooCommerce\Internal\Admin\BlockTemplates\BlockTemplate; use WC_Unit_Test_Case; @@ -35,6 +36,135 @@ class BlockTemplateTest extends WC_Unit_Test_Case { $this->assertSame( $block, $template->get_block( 'test-block-id' ) ); } + /** + * Test the after_add_block hooks fires. + */ + public function test_after_add_block_hooks() { + $template = new BlockTemplate(); + + $hook_called = false; + + $after_add_block_hook = function( BlockInterface $block ) use ( &$hook_called ) { + $hook_called = true; + + if ( 'test-block-id' === $block->get_id() ) { + $hook_called = true; + } + + $root_template = $block->get_root_template(); + + if ( $root_template->get_block( 'test-block-id-2' ) ) { + // The block was already added, so just return. + // This short-circuiting is needed because this hook will be called two times: + // 1. When the `test-block-id` block is added to the root template. + // 2. When the `test-block-id-2` block is added to the template in this hook. + return; + } + + $root_template->add_block( + [ + 'id' => 'test-block-id-2', + 'blockName' => 'test-block-name-2', + ] + ); + }; + + add_action( 'woocommerce_block_template_after_add_block', $after_add_block_hook ); + + $specific_hook_called = false; + + $specific_after_add_block_hook = function( BlockInterface $block ) use ( &$specific_hook_called ) { + if ( 'test-block-id' === $block->get_id() ) { + $specific_hook_called = true; + } + }; + + add_action( 'woocommerce_block_template_area_uncategorized_after_add_block_test-block-id', $specific_after_add_block_hook ); + + $template->add_block( + [ + 'id' => 'test-block-id', + 'blockName' => 'test-block-name', + ] + ); + + $this->assertTrue( + $hook_called, + 'Failed asserting that that the hook was called.' + ); + + $this->assertTrue( + $specific_hook_called, + 'Failed asserting that that the specific hook was called.' + ); + + $this->assertNotNull( + $template->get_block( 'test-block-id-2' ), + 'Failed asserting that the block was added to the template from the hook.' + ); + + remove_action( 'woocommerce_block_template_after_add_block', $after_add_block_hook ); + remove_action( 'woocommerce_block_template_area_uncategorized_after_add_block_test-block-id', $specific_after_add_block_hook ); + } + + /** + * Test the after_remove_block hooks fires. + */ + public function test_after_remove_block_hooks() { + $template = new BlockTemplate(); + + $hook_called = false; + + $after_remove_block_hook = function( BlockInterface $block ) use ( &$hook_called ) { + $hook_called = true; + + if ( 'test-block-id' === $block->get_id() ) { + $hook_called = true; + } + + $root_template = $block->get_root_template(); + }; + + add_action( 'woocommerce_block_template_after_remove_block', $after_remove_block_hook ); + + $specific_hook_called = false; + + $specific_after_remove_block_hook = function( BlockInterface $block ) use ( &$specific_hook_called ) { + if ( 'test-block-id' === $block->get_id() ) { + $specific_hook_called = true; + } + }; + + add_action( 'woocommerce_block_template_area_uncategorized_after_remove_block_test-block-id', $specific_after_remove_block_hook ); + + $block = $template->add_block( + [ + 'id' => 'test-block-id', + 'blockName' => 'test-block-name', + ] + ); + + $block->remove(); + + $this->assertTrue( + $hook_called, + 'Failed asserting that that the hook was called.' + ); + + $this->assertTrue( + $specific_hook_called, + 'Failed asserting that that the specific hook was called.' + ); + + $this->assertTrue( + $block->is_detached(), + 'Failed asserting that the block was added to the template from the hook.' + ); + + remove_action( 'woocommerce_block_template_after_remove_block', $after_remove_block_hook ); + remove_action( 'woocommerce_block_template_area_uncategorized_after_remove_block_test-block-id', $specific_after_remove_block_hook ); + } + /** * Test adding a block throws an exception if a block with the same ID already exists. */ From 68607e02e81be41370d9e696b3641354a7f7e49f Mon Sep 17 00:00:00 2001 From: Matt Sherman Date: Wed, 13 Sep 2023 11:00:27 -0400 Subject: [PATCH 15/17] Update comment to make it clearer why short-circuiting is used in hook --- .../src/Internal/Admin/BlockTemplates/BlockTemplateTest.php | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/plugins/woocommerce/tests/php/src/Internal/Admin/BlockTemplates/BlockTemplateTest.php b/plugins/woocommerce/tests/php/src/Internal/Admin/BlockTemplates/BlockTemplateTest.php index 6dd2c742063..dd2c8781c1b 100644 --- a/plugins/woocommerce/tests/php/src/Internal/Admin/BlockTemplates/BlockTemplateTest.php +++ b/plugins/woocommerce/tests/php/src/Internal/Admin/BlockTemplates/BlockTemplateTest.php @@ -55,9 +55,11 @@ class BlockTemplateTest extends WC_Unit_Test_Case { if ( $root_template->get_block( 'test-block-id-2' ) ) { // The block was already added, so just return. - // This short-circuiting is needed because this hook will be called two times: + // This short-circuiting done because this hook will be called two times: // 1. When the `test-block-id` block is added to the root template. // 2. When the `test-block-id-2` block is added to the template in this hook. + // Without this short-circuiting, the second time `add_block` is called in this + // hook would throw an exception, which is handled by the API (an error gets logged). return; } From e03acdf662837837a217e52e0a128b3073e7dea5 Mon Sep 17 00:00:00 2001 From: Matt Sherman Date: Fri, 15 Sep 2023 12:54:16 -0400 Subject: [PATCH 16/17] Simplify logic in is_detached() --- .../src/Internal/Admin/BlockTemplates/AbstractBlock.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/woocommerce/src/Internal/Admin/BlockTemplates/AbstractBlock.php b/plugins/woocommerce/src/Internal/Admin/BlockTemplates/AbstractBlock.php index 2af5c74c0c1..31696720603 100644 --- a/plugins/woocommerce/src/Internal/Admin/BlockTemplates/AbstractBlock.php +++ b/plugins/woocommerce/src/Internal/Admin/BlockTemplates/AbstractBlock.php @@ -189,7 +189,7 @@ class AbstractBlock implements BlockInterface { $is_in_parent = $this->parent->get_block( $this->id ) === $this; $is_in_root_template = $this->get_root_template()->get_block( $this->id ) === $this; - return ! $is_in_parent || ! $is_in_root_template; + return ! ( $is_in_parent && $is_in_root_template ); } /** * Get the block configuration as a formatted template. From 3520b388d933445a3b0695655092d766420897c6 Mon Sep 17 00:00:00 2001 From: Matt Sherman Date: Fri, 15 Sep 2023 12:59:30 -0400 Subject: [PATCH 17/17] Add try/finally to ensure action hooks are removed --- .../BlockTemplates/BlockTemplateTest.php | 111 +++++++++--------- 1 file changed, 57 insertions(+), 54 deletions(-) diff --git a/plugins/woocommerce/tests/php/src/Internal/Admin/BlockTemplates/BlockTemplateTest.php b/plugins/woocommerce/tests/php/src/Internal/Admin/BlockTemplates/BlockTemplateTest.php index dd2c8781c1b..850c55dde58 100644 --- a/plugins/woocommerce/tests/php/src/Internal/Admin/BlockTemplates/BlockTemplateTest.php +++ b/plugins/woocommerce/tests/php/src/Internal/Admin/BlockTemplates/BlockTemplateTest.php @@ -71,42 +71,44 @@ class BlockTemplateTest extends WC_Unit_Test_Case { ); }; - add_action( 'woocommerce_block_template_after_add_block', $after_add_block_hook ); + try { + add_action( 'woocommerce_block_template_after_add_block', $after_add_block_hook ); - $specific_hook_called = false; + $specific_hook_called = false; - $specific_after_add_block_hook = function( BlockInterface $block ) use ( &$specific_hook_called ) { - if ( 'test-block-id' === $block->get_id() ) { - $specific_hook_called = true; - } - }; + $specific_after_add_block_hook = function( BlockInterface $block ) use ( &$specific_hook_called ) { + if ( 'test-block-id' === $block->get_id() ) { + $specific_hook_called = true; + } + }; - add_action( 'woocommerce_block_template_area_uncategorized_after_add_block_test-block-id', $specific_after_add_block_hook ); + add_action( 'woocommerce_block_template_area_uncategorized_after_add_block_test-block-id', $specific_after_add_block_hook ); - $template->add_block( - [ - 'id' => 'test-block-id', - 'blockName' => 'test-block-name', - ] - ); + $template->add_block( + [ + 'id' => 'test-block-id', + 'blockName' => 'test-block-name', + ] + ); - $this->assertTrue( - $hook_called, - 'Failed asserting that that the hook was called.' - ); + $this->assertTrue( + $hook_called, + 'Failed asserting that that the hook was called.' + ); - $this->assertTrue( - $specific_hook_called, - 'Failed asserting that that the specific hook was called.' - ); + $this->assertTrue( + $specific_hook_called, + 'Failed asserting that that the specific hook was called.' + ); - $this->assertNotNull( - $template->get_block( 'test-block-id-2' ), - 'Failed asserting that the block was added to the template from the hook.' - ); - - remove_action( 'woocommerce_block_template_after_add_block', $after_add_block_hook ); - remove_action( 'woocommerce_block_template_area_uncategorized_after_add_block_test-block-id', $specific_after_add_block_hook ); + $this->assertNotNull( + $template->get_block( 'test-block-id-2' ), + 'Failed asserting that the block was added to the template from the hook.' + ); + } finally { + remove_action( 'woocommerce_block_template_after_add_block', $after_add_block_hook ); + remove_action( 'woocommerce_block_template_area_uncategorized_after_add_block_test-block-id', $specific_after_add_block_hook ); + } } /** @@ -127,8 +129,6 @@ class BlockTemplateTest extends WC_Unit_Test_Case { $root_template = $block->get_root_template(); }; - add_action( 'woocommerce_block_template_after_remove_block', $after_remove_block_hook ); - $specific_hook_called = false; $specific_after_remove_block_hook = function( BlockInterface $block ) use ( &$specific_hook_called ) { @@ -137,34 +137,37 @@ class BlockTemplateTest extends WC_Unit_Test_Case { } }; - add_action( 'woocommerce_block_template_area_uncategorized_after_remove_block_test-block-id', $specific_after_remove_block_hook ); + try { + add_action( 'woocommerce_block_template_after_remove_block', $after_remove_block_hook ); + add_action( 'woocommerce_block_template_area_uncategorized_after_remove_block_test-block-id', $specific_after_remove_block_hook ); - $block = $template->add_block( - [ - 'id' => 'test-block-id', - 'blockName' => 'test-block-name', - ] - ); + $block = $template->add_block( + [ + 'id' => 'test-block-id', + 'blockName' => 'test-block-name', + ] + ); - $block->remove(); + $block->remove(); - $this->assertTrue( - $hook_called, - 'Failed asserting that that the hook was called.' - ); + $this->assertTrue( + $hook_called, + 'Failed asserting that that the hook was called.' + ); - $this->assertTrue( - $specific_hook_called, - 'Failed asserting that that the specific hook was called.' - ); + $this->assertTrue( + $specific_hook_called, + 'Failed asserting that that the specific hook was called.' + ); - $this->assertTrue( - $block->is_detached(), - 'Failed asserting that the block was added to the template from the hook.' - ); - - remove_action( 'woocommerce_block_template_after_remove_block', $after_remove_block_hook ); - remove_action( 'woocommerce_block_template_area_uncategorized_after_remove_block_test-block-id', $specific_after_remove_block_hook ); + $this->assertTrue( + $block->is_detached(), + 'Failed asserting that the block was added to the template from the hook.' + ); + } finally { + remove_action( 'woocommerce_block_template_after_remove_block', $after_remove_block_hook ); + remove_action( 'woocommerce_block_template_area_uncategorized_after_remove_block_test-block-id', $specific_after_remove_block_hook ); + } } /**