<?php namespace Automattic\WooCommerce\Blocks\Templates; /** * AbstractTemplateCompatibility class. * * To bridge the gap on compatibility with PHP hooks and blockified templates. * * @internal */ abstract class AbstractTemplateCompatibility { /** * The data of supported hooks, containing the hook name, the block name, * position, and the callbacks. * * @var array $hook_data The hook data. */ protected $hook_data; /** * Initialization method. */ public function init() { if ( ! wc_current_theme_is_fse_theme() ) { return; } $this->set_hook_data(); add_filter( 'render_block_data', function( $parsed_block, $source_block, $parent_block ) { /** * Filter to disable the compatibility layer for the blockified templates. * * This hook allows to disable the compatibility layer for the blockified templates. * * @since TBD * @param boolean. */ $is_disabled_compatility_layer = apply_filters( 'woocommerce_disable_compatibility_layer', false ); if ( $is_disabled_compatility_layer ) { return $parsed_block; } return $this->update_render_block_data( $parsed_block, $source_block, $parent_block ); }, 10, 3 ); add_filter( 'render_block', function ( $block_content, $block ) { /** * Filter to disable the compatibility layer for the blockified templates. * * This hook allows to disable the compatibility layer for the blockified. * * @since TBD * @param boolean. */ $is_disabled_compatility_layer = apply_filters( 'woocommerce_disable_compatibility_layer', false ); if ( $is_disabled_compatility_layer ) { return $block_content; } return $this->inject_hooks( $block_content, $block ); }, 10, 2 ); } /** * Update the render block data to inject our custom attribute needed to * determine which blocks belong to an inherited Products block. * * @param array $parsed_block The block being rendered. * @param array $source_block An un-modified copy of $parsed_block, as it appeared in the source content. * @param WP_Block|null $parent_block If this is a nested block, a reference to the parent block. * * @return array */ abstract public function update_render_block_data( $parsed_block, $source_block, $parent_block ); /** * Inject hooks to rendered content of corresponding blocks. * * @param mixed $block_content The rendered block content. * @param mixed $block The parsed block data. * @return string */ abstract public function inject_hooks( $block_content, $block ); /** * The hook data to inject to the rendered content of blocks. This also * contains hooked functions that will be removed by remove_default_hooks. * * The array format: * [ * <hook-name> => [ * block_names => [ <block-name>, ... ], * position => before|after, * hooked => [ * <function-name> => <priority>, * ... * ], * ], * ] * Where: * - hook-name is the name of the hook that will be replaced. * - block-names is the array block names that hook will be attached to. * - position is the position of the block relative to the hook. * - hooked is an array of functions hooked to the hook that will be * replaced. The key is the function name and the value is the * priority. */ abstract protected function set_hook_data(); /** * Remove the default callback added by WooCommerce. We replaced these * callbacks by blocks so we have to remove them to prevent duplicated * content. */ protected function remove_default_hooks() { foreach ( $this->hook_data as $hook => $data ) { if ( ! isset( $data['hooked'] ) ) { continue; } foreach ( $data['hooked'] as $callback => $priority ) { remove_action( $hook, $callback, $priority ); } } /** * When extensions implement their equivalent blocks of the template * hook functions, they can use this filter to register their old hooked * data here, so in the blockified template, the old hooked functions * can be removed in favor of the new blocks while keeping the old * hooked functions working in classic templates. * * Accepts an array of hooked data. The array should be in the following * format: * [ * [ * hook => <hook-name>, * function => <function-name>, * priority => <priority>, * ], * ... * ] * Where: * - hook-name is the name of the hook that have the functions hooked to. * - function-name is the hooked function name. * - priority is the priority of the hooked function. * * @since 9.5.0 * @param array $data Additional hooked data. Default to empty */ $additional_hook_data = apply_filters( 'woocommerce_blocks_hook_compatibility_additional_data', array() ); if ( empty( $additional_hook_data ) || ! is_array( $additional_hook_data ) ) { return; } foreach ( $additional_hook_data as $data ) { if ( ! isset( $data['hook'], $data['function'], $data['priority'] ) ) { continue; } remove_action( $data['hook'], $data['function'], $data['priority'] ); } } /** * Get the buffer content of the hooks to append/prepend to render content. * * @param array $hooks The hooks to be rendered. * @param string $position The position of the hooks. * * @return string */ protected function get_hooks_buffer( $hooks, $position ) { ob_start(); foreach ( $hooks as $hook => $data ) { if ( $data['position'] === $position ) { /** * Action to render the content of a hook. * * @since 9.5.0 */ do_action( $hook ); } } return ob_get_clean(); } }