HEX
Server: LiteSpeed
System: Linux eko108.isimtescil.net 4.18.0-477.21.1.lve.1.el8.x86_64 #1 SMP Tue Sep 5 23:08:35 UTC 2023 x86_64
User: uyarreklamcomtr (11202)
PHP: 7.4.33
Disabled: opcache_get_status
Upload Files
File: /var/www/vhosts/uyarreklam.com.tr/httpdocs/BlockTemplates.tar
BlockContainerInterface.php000064400000000272151543213070011775 0ustar00<?php

namespace Automattic\WooCommerce\Admin\BlockTemplates;

/**
 * Interface for block containers.
 */
interface BlockContainerInterface extends BlockInterface, ContainerInterface {}
BlockInterface.php000064400000003523151543213070010134 0ustar00<?php

namespace Automattic\WooCommerce\Admin\BlockTemplates;

/**
 * Interface for block configuration used to specify blocks in BlockTemplate.
 */
interface BlockInterface {
	/**
	 * Key for the block name in the block configuration.
	 */
	public const NAME_KEY = 'blockName';

	/**
	 * Key for the block ID in the block configuration.
	 */
	public const ID_KEY = 'id';

	/**
	 * Key for the internal order in the block configuration.
	 */
	public const ORDER_KEY = 'order';

	/**
	 * Key for the block attributes in the block configuration.
	 */
	public const ATTRIBUTES_KEY = 'attributes';

	/**
	 * Get the block name.
	 */
	public function get_name(): string;

	/**
	 * Get the block ID.
	 */
	public function get_id(): string;

	/**
	 * Get the block order.
	 */
	public function get_order(): int;

	/**
	 * Set the block order.
	 *
	 * @param int $order The block order.
	 */
	public function set_order( int $order );

	/**
	 * Get the block attributes.
	 */
	public function get_attributes(): array;

	/**
	 * Set the block attributes.
	 *
	 * @param array $attributes The block attributes.
	 */
	public function set_attributes( array $attributes );

	/**
	 * Get the parent container that the block belongs to.
	 */
	public function &get_parent(): ContainerInterface;

	/**
	 * Get the root template that the block belongs to.
	 */
	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.
	 *
	 * @return bool True if the block is detached from its parent or root template.
	 */
	public function is_detached(): bool;

	/**
	 * Get the block configuration as a formatted template.
	 *
	 * @return array The block configuration as a formatted template.
	 */
	public function get_formatted_template(): array;
}
BlockTemplateInterface.php000064400000001261151543213070011625 0ustar00<?php

namespace Automattic\WooCommerce\Admin\BlockTemplates;

/**
 * Interface for block-based template.
 */
interface BlockTemplateInterface extends ContainerInterface {
	/**
	 * Get the template ID.
	 */
	public function get_id(): string;

	/**
	 * Get the template title.
	 */
	public function get_title(): string;

	/**
	 * Get the template description.
	 */
	public function get_description(): string;

	/**
	 * Get the template area.
	 */
	public function get_area(): string;

	/**
	 * Generate a block ID based on a base.
	 *
	 * @param string $id_base The base to use when generating an ID.
	 * @return string
	 */
	public function generate_block_id( string $id_base ): string;
}
ContainerInterface.php000064400000001536151543213070011026 0ustar00<?php

namespace Automattic\WooCommerce\Admin\BlockTemplates;

/**
 * Interface for block containers.
 */
interface ContainerInterface {
	/**
	 * Get the root template that the block belongs to.
	 */
	public function &get_root_template(): BlockTemplateInterface;

	/**
	 * Get the block configuration as a formatted template.
	 */
	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.
	 *
	 * @param string $block_id The block ID.
	 *
	 * @throws \UnexpectedValueException If the block container is not an ancestor of the block.
	 */
	public function remove_block( string $block_id );

	/**
	 * Removes all blocks from the container.
	 */
	public function remove_blocks();
}
AbstractBlock.php000064400000012175151547026010010002 0ustar00<?php

namespace Automattic\WooCommerce\Internal\Admin\BlockTemplates;

use Automattic\WooCommerce\Admin\BlockTemplates\BlockInterface;
use Automattic\WooCommerce\Admin\BlockTemplates\BlockTemplateInterface;
use Automattic\WooCommerce\Admin\BlockTemplates\ContainerInterface;

/**
 * Block configuration used to specify blocks in BlockTemplate.
 */
class AbstractBlock implements BlockInterface {
	/**
	 * The block name.
	 *
	 * @var string
	 */
	private $name;

	/**
	 * The block ID.
	 *
	 * @var string
	 */
	private $id;

	/**
	 * The block order.
	 *
	 * @var int
	 */
	private $order = 10;

	/**
	 * The block attributes.
	 *
	 * @var array
	 */
	private $attributes = [];

	/**
	 * The block template that this block belongs to.
	 *
	 * @var BlockTemplate
	 */
	private $root_template;

	/**
	 * The parent container.
	 *
	 * @var ContainerInterface
	 */
	private $parent;

	/**
	 * Block constructor.
	 *
	 * @param array                        $config The block configuration.
	 * @param BlockTemplateInterface       $root_template The block template that this block belongs to.
	 * @param BlockContainerInterface|null $parent The parent block container.
	 *
	 * @throws \ValueError If the block configuration is invalid.
	 * @throws \ValueError If the parent block container does not belong to the same template as the block.
	 */
	public function __construct( array $config, BlockTemplateInterface &$root_template, ContainerInterface &$parent = null ) {
		$this->validate( $config, $root_template, $parent );

		$this->root_template = $root_template;
		$this->parent        = is_null( $parent ) ? $root_template : $parent;

		$this->name = $config[ self::NAME_KEY ];

		if ( ! isset( $config[ self::ID_KEY ] ) ) {
			$this->id = $this->root_template->generate_block_id( $this->get_name() );
		} else {
			$this->id = $config[ self::ID_KEY ];
		}

		if ( isset( $config[ self::ORDER_KEY ] ) ) {
			$this->order = $config[ self::ORDER_KEY ];
		}

		if ( isset( $config[ self::ATTRIBUTES_KEY ] ) ) {
			$this->attributes = $config[ self::ATTRIBUTES_KEY ];
		}
	}

	/**
	 * Validate block configuration.
	 *
	 * @param array                   $config The block configuration.
	 * @param BlockTemplateInterface  $root_template The block template that this block belongs to.
	 * @param ContainerInterface|null $parent The parent block container.
	 *
	 * @throws \ValueError If the block configuration is invalid.
	 * @throws \ValueError If the parent block container does not belong to the same template as the block.
	 */
	protected function validate( array $config, BlockTemplateInterface &$root_template, ContainerInterface &$parent = null ) {
		if ( isset( $parent ) && ( $parent->get_root_template() !== $root_template ) ) {
			throw new \ValueError( 'The parent block must belong to the same template as the block.' );
		}

		if ( ! isset( $config[ self::NAME_KEY ] ) || ! is_string( $config[ self::NAME_KEY ] ) ) {
			throw new \ValueError( 'The block name must be specified.' );
		}

		if ( isset( $config[ self::ORDER_KEY ] ) && ! is_int( $config[ self::ORDER_KEY ] ) ) {
			throw new \ValueError( 'The block order must be an integer.' );
		}

		if ( isset( $config[ self::ATTRIBUTES_KEY ] ) && ! is_array( $config[ self::ATTRIBUTES_KEY ] ) ) {
			throw new \ValueError( 'The block attributes must be an array.' );
		}
	}

	/**
	 * Get the block name.
	 */
	public function get_name(): string {
		return $this->name;
	}

	/**
	 * Get the block ID.
	 */
	public function get_id(): string {
		return $this->id;
	}

	/**
	 * Get the block order.
	 */
	public function get_order(): int {
		return $this->order;
	}

	/**
	 * Set the block order.
	 *
	 * @param int $order The block order.
	 */
	public function set_order( int $order ) {
		$this->order = $order;
	}

	/**
	 * Get the block attributes.
	 */
	public function get_attributes(): array {
		return $this->attributes;
	}

	/**
	 * Set the block attributes.
	 *
	 * @param array $attributes The block attributes.
	 */
	public function set_attributes( array $attributes ) {
		$this->attributes = $attributes;
	}

	/**
	 * Get the template that this block belongs to.
	 */
	public function &get_root_template(): BlockTemplateInterface {
		return $this->root_template;
	}

	/**
	 * Get the parent block container.
	 */
	public function &get_parent(): ContainerInterface {
		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.
	 *
	 * @return bool True if the block is detached from its parent block container or the template it belongs to.
	 */
	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.
	 *
	 * @return array The block configuration as a formatted template.
	 */
	public function get_formatted_template(): array {
		$arr = [
			$this->get_name(),
			$this->get_attributes(),
		];

		return $arr;
	}
}
AbstractBlockTemplate.php000064400000006152151547026010011474 0ustar00<?php

namespace Automattic\WooCommerce\Internal\Admin\BlockTemplates;

use Automattic\WooCommerce\Admin\BlockTemplates\BlockInterface;
use Automattic\WooCommerce\Admin\BlockTemplates\BlockTemplateInterface;

/**
 * Block template class.
 */
abstract class AbstractBlockTemplate implements BlockTemplateInterface {
	use BlockContainerTrait;

	/**
	 * Get the template ID.
	 */
	abstract public function get_id(): string;

	/**
	 * Get the template title.
	 */
	public function get_title(): string {
		return '';
	}

	/**
	 * Get the template description.
	 */
	public function get_description(): string {
		return '';
	}

	/**
	 * Get the template area.
	 */
	public function get_area(): string {
		return 'uncategorized';
	}

	/**
	 * The block cache.
	 *
	 * @var BlockInterface[]
	 */
	private $block_cache = [];

	/**
	 * Get a block by ID.
	 *
	 * @param string $block_id The block ID.
	 */
	public function get_block( string $block_id ): ?BlockInterface {
		return $this->block_cache[ $block_id ] ?? null;
	}

	/**
	 * Caches a block in the template. This is an internal method and should not be called directly
	 * except for from the BlockContainerTrait's add_inner_block() method.
	 *
	 * @param BlockInterface $block The block to cache.
	 *
	 * @throws \ValueError If a block with the specified ID already exists in the template.
	 * @throws \ValueError If the block template that the block belongs to is not this template.
	 *
	 * @ignore
	 */
	public function cache_block( BlockInterface &$block ) {
		$id = $block->get_id();

		if ( isset( $this->block_cache[ $id ] ) ) {
			throw new \ValueError( 'A block with the specified ID already exists in the template.' );
		}

		if ( $block->get_root_template() !== $this ) {
			throw new \ValueError( 'The block template that the block belongs to must be the same as this template.' );
		}

		$this->block_cache[ $id ] = $block;
	}

	/**
	 * Uncaches a block in the template. This is an internal method and should not be called directly
	 * except for from the BlockContainerTrait's remove_block() method.
	 *
	 * @param string $block_id The block ID.
	 *
	 * @ignore
	 */
	public function uncache_block( string $block_id ) {
		if ( isset( $this->block_cache[ $block_id ] ) ) {
			unset( $this->block_cache[ $block_id ] );
		}
	}

	/**
	 * Generate a block ID based on a base.
	 *
	 * @param string $id_base The base to use when generating an ID.
	 * @return string
	 */
	public function generate_block_id( string $id_base ): string {
		$instance_count = 0;

		do {
			$instance_count++;
			$block_id = $id_base . '-' . $instance_count;
		} while ( isset( $this->block_cache[ $block_id ] ) );

		return $block_id;
	}

	/**
	 * Get the root template.
	 */
	public function &get_root_template(): BlockTemplateInterface {
		return $this;
	}

	/**
	 * Get the inner blocks as a formatted template.
	 */
	public function get_formatted_template(): array {
		$inner_blocks = $this->get_inner_blocks_sorted_by_order();

		$inner_blocks_formatted_template = array_map(
			function( BlockInterface $block ) {
				return $block->get_formatted_template();
			},
			$inner_blocks
		);

		return $inner_blocks_formatted_template;
	}
}
Block.php000064400000001356151547026010006315 0ustar00<?php

namespace Automattic\WooCommerce\Internal\Admin\BlockTemplates;

use Automattic\WooCommerce\Admin\BlockTemplates\BlockContainerInterface;
use Automattic\WooCommerce\Admin\BlockTemplates\BlockInterface;
use Automattic\WooCommerce\Admin\BlockTemplates\BlockTemplateInterface;

/**
 * Generic block with container properties to be used in BlockTemplate.
 */
class Block extends AbstractBlock implements BlockContainerInterface {
	use BlockContainerTrait;

	/**
	 * Add an inner block to this block.
	 *
	 * @param array $block_config The block data.
	 */
	public function &add_block( array $block_config ): BlockInterface {
		$block = new Block( $block_config, $this->get_root_template(), $this );
		return $this->add_inner_block( $block );
	}
}
BlockContainerTrait.php000064400000023452151547026010011165 0ustar00<?php

namespace Automattic\WooCommerce\Internal\Admin\BlockTemplates;

use Automattic\WooCommerce\Admin\BlockTemplates\BlockInterface;
use Automattic\WooCommerce\Admin\BlockTemplates\ContainerInterface;

/**
 * Trait for block containers.
 */
trait BlockContainerTrait {
	/**
	 * The inner blocks.
	 *
	 * @var BlockInterface[]
	 */
	private $inner_blocks = [];

	// phpcs doesn't take into account exceptions thrown by called methods.
	// phpcs:disable Squiz.Commenting.FunctionCommentThrowTag.WrongNumber

	/**
	 * Add a block to the block container.
	 *
	 * @param BlockInterface $block The block.
	 *
	 * @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->get_parent() !== $this ) {
			throw new \UnexpectedValueException( 'The block container is not the parent of the 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.' );
		}

		$is_detached = method_exists( $this, 'is_detached' ) && $this->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;

		$this->do_after_add_block_action( $block );
		$this->do_after_add_specific_block_action( $block );

		return $block;
	}

	// phpcs:enable Squiz.Commenting.FunctionCommentThrowTag.WrongNumber

	/**
	 * Checks if a block is a descendant of the block container.
	 *
	 * @param BlockInterface $block The block.
	 */
	private function is_block_descendant( BlockInterface $block ): bool {
		$parent = $block->get_parent();

		if ( $parent === $this ) {
			return true;
		}

		if ( ! $parent instanceof BlockInterface ) {
			return false;
		}

		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.
	 *
	 * @param string $block_id The block ID.
	 *
	 * @throws \UnexpectedValueException If the block container is not an ancestor of the block.
	 */
	public function remove_block( string $block_id ) {
		$root_template = $this->get_root_template();

		$block = $root_template->get_block( $block_id );

		if ( ! $block ) {
			return;
		}

		if ( ! $this->is_block_descendant( $block ) ) {
			throw new \UnexpectedValueException( 'The block container is not an ancestor of the block.' );
		}

		// If the block is a container, remove all of its blocks.
		if ( $block instanceof ContainerInterface ) {
			$block->remove_blocks();
		}

		$parent = $block->get_parent();
		$parent->remove_inner_block( $block );
	}

	/**
	 * Remove all blocks from the block container.
	 */
	public function remove_blocks() {
		array_map(
			function ( BlockInterface $block ) {
				$this->remove_block( $block->get_id() );
			},
			$this->inner_blocks
		);
	}

	/**
	 * Remove a block from the block container's inner blocks. This is an internal method and should not be called directly
	 * except for from the BlockContainerTrait's remove_block() method.
	 *
	 * @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.
	 */
	private function get_inner_blocks_sorted_by_order(): array {
		$sorted_inner_blocks = $this->inner_blocks;

		usort(
			$sorted_inner_blocks,
			function( BlockInterface $a, BlockInterface $b ) {
				return $a->get_order() <=> $b->get_order();
			}
		);

		return $sorted_inner_blocks;
	}

	/**
	 * Get the inner blocks as a formatted template.
	 */
	public function get_formatted_template(): array {
		$arr = [
			$this->get_name(),
			$this->get_attributes(),
		];

		$inner_blocks = $this->get_inner_blocks_sorted_by_order();

		if ( ! empty( $inner_blocks ) ) {
			$arr[] = array_map(
				function( BlockInterface $block ) {
					return $block->get_formatted_template();
				},
				$inner_blocks
			);
		}

		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(),
			],
		);
	}
}
BlockTemplate.php000064400000001400151547026010007777 0ustar00<?php

namespace Automattic\WooCommerce\Internal\Admin\BlockTemplates;

use Automattic\WooCommerce\Admin\BlockTemplates\ContainerInterface;
use Automattic\WooCommerce\Admin\BlockTemplates\BlockInterface;
use Automattic\WooCommerce\Admin\BlockTemplates\BlockTemplateInterface;

/**
 * Block template class.
 */
class BlockTemplate extends AbstractBlockTemplate {
	/**
	 * Get the template ID.
	 */
	public function get_id(): string {
		return 'woocommerce-block-template';
	}

	/**
	 * Add an inner block to this template.
	 *
	 * @param array $block_config The block data.
	 */
	public function add_block( array $block_config ): BlockInterface {
		$block = new Block( $block_config, $this->get_root_template(), $this );
		return $this->add_inner_block( $block );
	}
}
BlockTemplateLogger.php000064400000011140151547026010011141 0ustar00<?php

namespace Automattic\WooCommerce\Internal\Admin\BlockTemplates;

use Automattic\WooCommerce\Admin\BlockTemplates\BlockContainerInterface;
use Automattic\WooCommerce\Admin\BlockTemplates\BlockInterface;
use Automattic\WooCommerce\Admin\BlockTemplates\BlockTemplateInterface;

/**
 * Logger for block template modifications.
 */
class BlockTemplateLogger {
	/**
	 * Singleton instance.
	 *
	 * @var BlockTemplateLogger
	 */
	protected static $instance = null;

	/**
	 * Logger instance.
	 *
	 * @var \WC_Logger
	 */
	protected $logger = null;

	/**
	 * Get the singleton instance.
	 */
	public static function get_instance(): BlockTemplateLogger {
		if ( ! self::$instance ) {
			self::$instance = new self();
		}

		return self::$instance;
	}

	/**
	 * Constructor.
	 */
	protected function __construct() {
		$this->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()})";
	}
}