File "BlockTemplateLogger.php"
Full Path: /home/jlklyejr/public_html/wp-content/test/wp-content/plugins/woocommerce/src/Internal/Admin/BlockTemplates/BlockTemplateLogger.php
File size: 14.22 KB
MIME-type: text/x-php
Charset: utf-8
<?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;
use Automattic\WooCommerce\Admin\BlockTemplates\ContainerInterface;
/**
* Logger for block template modifications.
*/
class BlockTemplateLogger {
const BLOCK_ADDED = 'block_added';
const BLOCK_REMOVED = 'block_removed';
const BLOCK_MODIFIED = 'block_modified';
const BLOCK_ADDED_TO_DETACHED_CONTAINER = 'block_added_to_detached_container';
const HIDE_CONDITION_ADDED = 'hide_condition_added';
const HIDE_CONDITION_REMOVED = 'hide_condition_removed';
const HIDE_CONDITION_ADDED_TO_DETACHED_BLOCK = 'hide_condition_added_to_detached_block';
const ERROR_AFTER_BLOCK_ADDED = 'error_after_block_added';
const ERROR_AFTER_BLOCK_REMOVED = 'error_after_block_removed';
const LOG_HASH_TRANSIENT_BASE_NAME = 'wc_block_template_events_log_hash_';
/**
* Event types.
*
* @var array
*/
public static $event_types = array(
self::BLOCK_ADDED => array(
'level' => \WC_Log_Levels::DEBUG,
'message' => 'Block added to template.',
),
self::BLOCK_REMOVED => array(
'level' => \WC_Log_Levels::NOTICE,
'message' => 'Block removed from template.',
),
self::BLOCK_MODIFIED => array(
'level' => \WC_Log_Levels::NOTICE,
'message' => 'Block modified in template.',
),
self::BLOCK_ADDED_TO_DETACHED_CONTAINER => array(
'level' => \WC_Log_Levels::WARNING,
'message' => 'Block added to detached container. Block will not be included in the template, since the container will not be included in the template.',
),
self::HIDE_CONDITION_ADDED => array(
'level' => \WC_Log_Levels::NOTICE,
'message' => 'Hide condition added to block.',
),
self::HIDE_CONDITION_REMOVED => array(
'level' => \WC_Log_Levels::NOTICE,
'message' => 'Hide condition removed from block.',
),
self::HIDE_CONDITION_ADDED_TO_DETACHED_BLOCK => array(
'level' => \WC_Log_Levels::WARNING,
'message' => 'Hide condition added to detached block. Block will not be included in the template, so the hide condition is not needed.',
),
self::ERROR_AFTER_BLOCK_ADDED => array(
'level' => \WC_Log_Levels::WARNING,
'message' => 'Error after block added to template.',
),
self::ERROR_AFTER_BLOCK_REMOVED => array(
'level' => \WC_Log_Levels::WARNING,
'message' => 'Error after block removed from template.',
),
);
/**
* Singleton instance.
*
* @var BlockTemplateLogger
*/
protected static $instance = null;
/**
* Logger instance.
*
* @var \WC_Logger
*/
protected $logger = null;
/**
* All template events.
*
* @var array
*/
private $all_template_events = array();
/**
* Templates.
*
* @var array
*/
private $templates = array();
/**
* Threshold severity.
*
* @var int
*/
private $threshold_severity = 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();
$threshold = get_option( 'woocommerce_block_template_logging_threshold', \WC_Log_Levels::WARNING );
if ( ! \WC_Log_Levels::is_valid_level( $threshold ) ) {
$threshold = \WC_Log_Levels::INFO;
}
$this->threshold_severity = \WC_Log_Levels::get_level_severity( $threshold );
add_action(
'woocommerce_block_template_after_add_block',
function ( BlockInterface $block ) {
$is_detached = method_exists( $block->get_parent(), 'is_detached' ) && $block->get_parent()->is_detached();
$this->log(
$is_detached
? $this::BLOCK_ADDED_TO_DETACHED_CONTAINER
: $this::BLOCK_ADDED,
$block,
);
},
0,
);
add_action(
'woocommerce_block_template_after_remove_block',
function ( BlockInterface $block ) {
$this->log(
$this::BLOCK_REMOVED,
$block,
);
},
0,
);
add_action(
'woocommerce_block_template_after_add_hide_condition',
function ( BlockInterface $block ) {
$this->log(
$block->is_detached()
? $this::HIDE_CONDITION_ADDED_TO_DETACHED_BLOCK
: $this::HIDE_CONDITION_ADDED,
$block,
);
},
0
);
add_action(
'woocommerce_block_template_after_remove_hide_condition',
function ( BlockInterface $block ) {
$this->log(
$this::HIDE_CONDITION_REMOVED,
$block,
);
},
0
);
add_action(
'woocommerce_block_template_after_add_block_error',
function ( BlockInterface $block, string $action, \Exception $exception ) {
$this->log(
$this::ERROR_AFTER_BLOCK_ADDED,
$block,
array(
'action' => $action,
'exception' => $exception,
),
);
},
0,
3
);
add_action(
'woocommerce_block_template_after_remove_block_error',
function ( BlockInterface $block, string $action, \Exception $exception ) {
$this->log(
$this::ERROR_AFTER_BLOCK_REMOVED,
$block,
array(
'action' => $action,
'exception' => $exception,
),
);
},
0,
3
);
}
/**
* Get all template events for a given template as a JSON like array.
*
* @param string $template_id Template ID.
*/
public function template_events_to_json( string $template_id ): array {
if ( ! isset( $this->all_template_events[ $template_id ] ) ) {
return array();
}
$template_events = $this->all_template_events[ $template_id ];
return $this->to_json( $template_events );
}
/**
* Get all template events as a JSON like array.
*
* @param array $template_events Template events.
*
* @return array The JSON.
*/
private function to_json( array $template_events ): array {
$json = array();
foreach ( $template_events as $template_event ) {
$container = $template_event['container'];
$block = $template_event['block'];
$json[] = array(
'level' => $template_event['level'],
'event_type' => $template_event['event_type'],
'message' => $template_event['message'],
'container' => $container instanceof BlockInterface
? array(
'id' => $container->get_id(),
'name' => $container->get_name(),
)
: null,
'block' => array(
'id' => $block->get_id(),
'name' => $block->get_name(),
),
'additional_info' => $this->format_info( $template_event['additional_info'] ),
);
}
return $json;
}
/**
* Log all template events for a given template to the log file.
*
* @param string $template_id Template ID.
*/
public function log_template_events_to_file( string $template_id ) {
if ( ! isset( $this->all_template_events[ $template_id ] ) ) {
return;
}
$template_events = $this->all_template_events[ $template_id ];
$hash = $this->generate_template_events_hash( $template_events );
if ( ! $this->has_template_events_changed( $template_id, $hash ) ) {
// Nothing has changed since the last time this was logged,
// so don't log it again.
return;
}
$this->set_template_events_log_hash( $template_id, $hash );
$template = $this->templates[ $template_id ];
foreach ( $template_events as $template_event ) {
$info = array_merge(
array(
'template' => $template,
'container' => $template_event['container'],
'block' => $template_event['block'],
),
$template_event['additional_info']
);
$message = $this->format_message( $template_event['message'], $info );
$this->logger->log(
$template_event['level'],
$message,
array( 'source' => 'block_template' )
);
}
}
/**
* Has the template events changed since the last time they were logged?
*
* @param string $template_id Template ID.
* @param string $events_hash Events hash.
*/
private function has_template_events_changed( string $template_id, string $events_hash ) {
$previous_hash = get_transient( self::LOG_HASH_TRANSIENT_BASE_NAME . $template_id );
return $previous_hash !== $events_hash;
}
/**
* Generate a hash for a given set of template events.
*
* @param array $template_events Template events.
*/
private function generate_template_events_hash( array $template_events ): string {
return md5( wp_json_encode( $this->to_json( $template_events ) ) );
}
/**
* Set the template events hash for a given template.
*
* @param string $template_id Template ID.
* @param string $hash Hash of template events.
*/
private function set_template_events_log_hash( string $template_id, string $hash ) {
set_transient( self::LOG_HASH_TRANSIENT_BASE_NAME . $template_id, $hash );
}
/**
* Log an event.
*
* @param string $event_type Event type.
* @param BlockInterface $block Block.
* @param array $additional_info Additional info.
*/
private function log( string $event_type, BlockInterface $block, $additional_info = array() ) {
if ( ! isset( self::$event_types[ $event_type ] ) ) {
/* translators: 1: WC_Logger::log 2: level */
wc_doing_it_wrong( __METHOD__, sprintf( __( '%1$s was called with an invalid event type "%2$s".', 'woocommerce' ), '<code>BlockTemplateLogger::log</code>', $event_type ), '8.4' );
}
$event_type_info = isset( self::$event_types[ $event_type ] )
? array_merge(
self::$event_types[ $event_type ],
array(
'event_type' => $event_type,
)
)
: array(
'level' => \WC_Log_Levels::ERROR,
'event_type' => $event_type,
'message' => 'Unknown error.',
);
if ( ! $this->should_handle( $event_type_info['level'] ) ) {
return;
}
$template = $block->get_root_template();
$container = $block->get_parent();
$this->add_template_event( $event_type_info, $template, $container, $block, $additional_info );
}
/**
* Should the logger handle a given level?
*
* @param int $level Level to check.
*/
private function should_handle( $level ) {
return $this->threshold_severity <= \WC_Log_Levels::get_level_severity( $level );
}
/**
* Add a template event.
*
* @param array $event_type_info Event type info.
* @param BlockTemplateInterface $template Template.
* @param ContainerInterface $container Container.
* @param BlockInterface $block Block.
* @param array $additional_info Additional info.
*/
private function add_template_event( array $event_type_info, BlockTemplateInterface $template, ContainerInterface $container, BlockInterface $block, array $additional_info = array() ) {
$template_id = $template->get_id();
if ( ! isset( $this->all_template_events[ $template_id ] ) ) {
$this->all_template_events[ $template_id ] = array();
$this->templates[ $template_id ] = $template;
}
$template_events = &$this->all_template_events[ $template_id ];
$template_events[] = array(
'level' => $event_type_info['level'],
'event_type' => $event_type_info['event_type'],
'message' => $event_type_info['message'],
'container' => $container,
'block' => $block,
'additional_info' => $additional_info,
);
}
/**
* 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 = array() ): 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 array(
'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 = array();
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()})";
}
}