first commit
This commit is contained in:
178
wp-content/plugins/elementor/core/logger/items/base.php
Normal file
178
wp-content/plugins/elementor/core/logger/items/base.php
Normal file
@@ -0,0 +1,178 @@
|
||||
<?php
|
||||
namespace Elementor\Core\Logger\Items;
|
||||
|
||||
if ( ! defined( 'ABSPATH' ) ) {
|
||||
exit; // Exit if accessed directly
|
||||
}
|
||||
|
||||
class Base implements Log_Item_Interface {
|
||||
|
||||
const FORMAT = 'date [type] message [meta]';
|
||||
const TRACE_FORMAT = '#key: file(line): class type function()';
|
||||
const TRACE_LIMIT = 5;
|
||||
|
||||
protected $date;
|
||||
protected $type;
|
||||
protected $message;
|
||||
protected $meta = [];
|
||||
|
||||
protected $times = 0;
|
||||
protected $times_dates = [];
|
||||
protected $args = [];
|
||||
|
||||
public function __construct( $args ) {
|
||||
$this->date = current_time( 'mysql' );
|
||||
$this->message = ! empty( $args['message'] ) ? esc_html( $args['message'] ) : '';
|
||||
$this->type = ! empty( $args['type'] ) ? $args['type'] : 'info';
|
||||
$this->meta = ! empty( $args['meta'] ) ? $args['meta'] : [];
|
||||
$this->args = $args;
|
||||
|
||||
$this->set_trace();
|
||||
}
|
||||
|
||||
public function __get( $name ) {
|
||||
if ( property_exists( $this, $name ) ) {
|
||||
return $this->{$name};
|
||||
}
|
||||
|
||||
return '';
|
||||
}
|
||||
|
||||
public function __toString() {
|
||||
$vars = get_object_vars( $this );
|
||||
return strtr( static::FORMAT, $vars );
|
||||
}
|
||||
|
||||
#[\ReturnTypeWillChange]
|
||||
public function jsonSerialize() {
|
||||
return [
|
||||
'class' => get_class( $this ),
|
||||
'item' => [
|
||||
'date' => $this->date,
|
||||
'message' => $this->message,
|
||||
'type' => $this->type,
|
||||
'meta' => $this->meta,
|
||||
'times' => $this->times,
|
||||
'times_dates' => $this->times_dates,
|
||||
'args' => $this->args,
|
||||
],
|
||||
];
|
||||
}
|
||||
|
||||
public function deserialize( $properties ) {
|
||||
$this->date = ! empty( $properties['date'] ) && is_string( $properties['date'] ) ? $properties['date'] : '';
|
||||
$this->message = ! empty( $properties['message'] ) && is_string( $properties['message'] ) ? $properties['message'] : '';
|
||||
$this->type = ! empty( $properties['type'] ) && is_string( $properties['type'] ) ? $properties['type'] : '';
|
||||
$this->meta = ! empty( $properties['meta'] ) && is_array( $properties['meta'] ) ? $properties['meta'] : [];
|
||||
$this->times = ! empty( $properties['times'] ) && is_string( $properties['times'] ) ? $properties['times'] : '';
|
||||
$this->times_dates = ! empty( $properties['times_dates'] ) && is_array( $properties['times_dates'] ) ? $properties['times_dates'] : [];
|
||||
$this->args = ! empty( $properties['args'] ) && is_array( $properties['args'] ) ? $properties['args'] : [];
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Log_Item_Interface | null
|
||||
*/
|
||||
public static function from_json( $str ) {
|
||||
$obj = json_decode( $str, true );
|
||||
if ( ! array_key_exists( 'class', $obj ) ) {
|
||||
return null;
|
||||
}
|
||||
$class = $obj['class'];
|
||||
if ( class_exists( $class ) ) {
|
||||
/** @var Base $item */
|
||||
$item = new $class( $obj['item']['message'] );
|
||||
$item->deserialize( $obj['item'] );
|
||||
return $item;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public function to_formatted_string( $output_format = 'html' ) {
|
||||
$vars = get_object_vars( $this );
|
||||
$format = static::FORMAT;
|
||||
if ( 'html' === $output_format ) {
|
||||
$format = str_replace( 'message', '<strong>message</strong>', static::FORMAT );
|
||||
}
|
||||
if ( empty( $vars['meta'] ) ) {
|
||||
$format = str_replace( '[meta]', '', $format );
|
||||
} else {
|
||||
$vars['meta'] = stripslashes( var_export( $vars['meta'], true ) ); // @codingStandardsIgnoreLine
|
||||
}
|
||||
return strtr( $format, $vars );
|
||||
}
|
||||
|
||||
public function get_fingerprint() {
|
||||
$unique_key = $this->type . $this->message . var_export( $this->meta, true ); // @codingStandardsIgnoreLine
|
||||
//info messages are not be aggregated:
|
||||
if ( 'info' === $this->type ) {
|
||||
$unique_key .= $this->date;
|
||||
}
|
||||
return md5( $unique_key );
|
||||
}
|
||||
|
||||
public function increase_times( $item, $truncate = true ) {
|
||||
$this->times++;
|
||||
$this->times_dates[] = $item->date;
|
||||
|
||||
if ( $truncate && ( self::MAX_LOG_ENTRIES < count( $this->times_dates ) ) ) {
|
||||
$this->times_dates = array_slice( $this->times_dates, -self::MAX_LOG_ENTRIES );
|
||||
}
|
||||
}
|
||||
|
||||
public function format( $format = 'html' ) {
|
||||
$trace = $this->format_trace();
|
||||
if ( empty( $trace ) ) {
|
||||
return $this->to_formatted_string( $format );
|
||||
}
|
||||
$copy = clone $this;
|
||||
$copy->meta['trace'] = $trace;
|
||||
return $copy->to_formatted_string( $format );
|
||||
}
|
||||
|
||||
public function get_name() {
|
||||
return 'Log';
|
||||
}
|
||||
|
||||
private function format_trace() {
|
||||
$trace = empty( $this->meta['trace'] ) ? '' : $this->meta['trace'];
|
||||
|
||||
if ( is_string( $trace ) ) {
|
||||
return $trace;
|
||||
}
|
||||
|
||||
$trace_str = '';
|
||||
foreach ( $trace as $key => $trace_line ) {
|
||||
$format = static::TRACE_FORMAT;
|
||||
$trace_line['key'] = $key;
|
||||
if ( empty( $trace_line['file'] ) ) {
|
||||
$format = str_replace( 'file(line): ', '', $format );
|
||||
}
|
||||
|
||||
$trace_str .= PHP_EOL . strtr( $format, $trace_line );
|
||||
$trace_str .= empty( $trace_line['args'] ) ? '' : var_export( $trace_line['args'], true ); // @codingStandardsIgnoreLine
|
||||
}
|
||||
|
||||
return $trace_str . PHP_EOL;
|
||||
}
|
||||
|
||||
private function set_trace() {
|
||||
if ( ! empty( $this->args['trace'] ) && true === $this->args['trace'] ) {
|
||||
$limit = empty( $this->args['trace_limit'] ) ? static::TRACE_LIMIT : $this->args['trace_limit'];
|
||||
|
||||
$stack = debug_backtrace( DEBUG_BACKTRACE_IGNORE_ARGS ); // @codingStandardsIgnoreLine
|
||||
|
||||
while ( ! empty( $stack ) && ! empty( $stack[0]['file'] ) && ( false !== strpos( $stack[0]['file'], 'core' . DIRECTORY_SEPARATOR . 'logger' ) ) ) {
|
||||
array_shift( $stack );
|
||||
}
|
||||
|
||||
$this->meta['trace'] = array_slice( $stack, 0, $limit );
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if ( is_array( $this->args ) ) {
|
||||
unset( $this->args['trace'] );
|
||||
}
|
||||
}
|
||||
}
|
||||
39
wp-content/plugins/elementor/core/logger/items/file.php
Normal file
39
wp-content/plugins/elementor/core/logger/items/file.php
Normal file
@@ -0,0 +1,39 @@
|
||||
<?php
|
||||
namespace Elementor\Core\Logger\Items;
|
||||
|
||||
if ( ! defined( 'ABSPATH' ) ) {
|
||||
exit; // Exit if accessed directly
|
||||
}
|
||||
|
||||
class File extends Base {
|
||||
|
||||
const FORMAT = 'date [type X times][file:line] message [meta]';
|
||||
|
||||
protected $file;
|
||||
protected $line;
|
||||
|
||||
public function __construct( $args ) {
|
||||
parent::__construct( $args );
|
||||
|
||||
$this->file = empty( $args['file'] ) ? '' : $args['file'];
|
||||
$this->line = empty( $args['line'] ) ? '' : $args['line'];
|
||||
}
|
||||
|
||||
#[\ReturnTypeWillChange]
|
||||
public function jsonSerialize() {
|
||||
$json_arr = parent::jsonSerialize();
|
||||
$json_arr['file'] = $this->file;
|
||||
$json_arr['line'] = $this->line;
|
||||
return $json_arr;
|
||||
}
|
||||
|
||||
public function deserialize( $properties ) {
|
||||
parent::deserialize( $properties );
|
||||
$this->file = ! empty( $properties['file'] ) && is_string( $properties['file'] ) ? $properties['file'] : '';
|
||||
$this->line = ! empty( $properties['line'] ) && is_string( $properties['line'] ) ? $properties['line'] : '';
|
||||
}
|
||||
|
||||
public function get_name() {
|
||||
return 'File';
|
||||
}
|
||||
}
|
||||
36
wp-content/plugins/elementor/core/logger/items/js.php
Normal file
36
wp-content/plugins/elementor/core/logger/items/js.php
Normal file
@@ -0,0 +1,36 @@
|
||||
<?php
|
||||
namespace Elementor\Core\Logger\Items;
|
||||
|
||||
if ( ! defined( 'ABSPATH' ) ) {
|
||||
exit; // Exit if accessed directly
|
||||
}
|
||||
|
||||
class JS extends File {
|
||||
|
||||
const FORMAT = 'JS: date [type X times][file:line:column] message [meta]';
|
||||
|
||||
protected $column;
|
||||
|
||||
public function __construct( $args ) {
|
||||
parent::__construct( $args );
|
||||
$this->column = $args['column'];
|
||||
$this->file = $args['url'];
|
||||
$this->date = gmdate( 'Y-m-d H:i:s', $args['timestamp'] );
|
||||
}
|
||||
|
||||
#[\ReturnTypeWillChange]
|
||||
public function jsonSerialize() {
|
||||
$json_arr = parent::jsonSerialize();
|
||||
$json_arr['column'] = $this->column;
|
||||
return $json_arr;
|
||||
}
|
||||
|
||||
public function deserialize( $properties ) {
|
||||
parent::deserialize( $properties );
|
||||
$this->column = ! empty( $properties['column'] ) && is_string( $properties['column'] ) ? $properties['column'] : '';
|
||||
}
|
||||
|
||||
public function get_name() {
|
||||
return 'JS';
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,73 @@
|
||||
<?php
|
||||
namespace Elementor\Core\Logger\Items;
|
||||
|
||||
if ( ! defined( 'ABSPATH' ) ) {
|
||||
exit; // Exit if accessed directly
|
||||
}
|
||||
|
||||
/**
|
||||
* Interface Log_Item_Interface
|
||||
*
|
||||
* @package Elementor\Core\Logger
|
||||
*
|
||||
* @property string $date
|
||||
* @property string $type
|
||||
* @property string $message
|
||||
* @property int $times
|
||||
* @property array $meta
|
||||
* @property array $times_dates
|
||||
* @property array $args
|
||||
*
|
||||
*/
|
||||
|
||||
interface Log_Item_Interface extends \JsonSerializable {
|
||||
|
||||
const MAX_LOG_ENTRIES = 42;
|
||||
|
||||
/**
|
||||
* Log_Item_Interface constructor.
|
||||
*
|
||||
* @param array $args
|
||||
*/
|
||||
public function __construct( $args );
|
||||
|
||||
/**
|
||||
* @param string $name
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function __get( $name );
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function __toString();
|
||||
|
||||
/**
|
||||
* @param $str
|
||||
* @return Log_Item_Interface | null
|
||||
*/
|
||||
public static function from_json( $str );
|
||||
|
||||
/**
|
||||
* @param string $format
|
||||
* @return string
|
||||
*/
|
||||
public function format( $format = 'html' );
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function get_fingerprint();
|
||||
|
||||
/**
|
||||
* @param Log_Item_Interface $item
|
||||
* @param bool $truncate
|
||||
*/
|
||||
public function increase_times( $item, $truncate = true );
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function get_name();
|
||||
}
|
||||
15
wp-content/plugins/elementor/core/logger/items/php.php
Normal file
15
wp-content/plugins/elementor/core/logger/items/php.php
Normal file
@@ -0,0 +1,15 @@
|
||||
<?php
|
||||
namespace Elementor\Core\Logger\Items;
|
||||
|
||||
if ( ! defined( 'ABSPATH' ) ) {
|
||||
exit; // Exit if accessed directly
|
||||
}
|
||||
|
||||
class PHP extends File {
|
||||
|
||||
const FORMAT = 'PHP: date [type X times][file::line] message [meta]';
|
||||
|
||||
public function get_name() {
|
||||
return 'PHP';
|
||||
}
|
||||
}
|
||||
117
wp-content/plugins/elementor/core/logger/log-reporter.php
Normal file
117
wp-content/plugins/elementor/core/logger/log-reporter.php
Normal file
@@ -0,0 +1,117 @@
|
||||
<?php
|
||||
namespace Elementor\Core\Logger;
|
||||
|
||||
use Elementor\Modules\System_Info\Reporters\Base;
|
||||
use Elementor\Utils;
|
||||
|
||||
if ( ! defined( 'ABSPATH' ) ) {
|
||||
exit; // Exit if accessed directly
|
||||
}
|
||||
|
||||
/**
|
||||
* Elementor Log reporter.
|
||||
*
|
||||
* Elementor log reporter handler class is responsible for generating the
|
||||
* debug reports.
|
||||
*
|
||||
* @since 2.4.0
|
||||
*/
|
||||
class Log_Reporter extends Base {
|
||||
|
||||
const MAX_ENTRIES = 20;
|
||||
const CLEAR_LOG_ACTION = 'elementor-clear-log';
|
||||
|
||||
public function get_title() {
|
||||
return esc_html__( 'Log', 'elementor' );
|
||||
}
|
||||
|
||||
public function get_fields() {
|
||||
return [
|
||||
'log_entries' => '',
|
||||
];
|
||||
}
|
||||
|
||||
public function print_html_label( $log_label ) {
|
||||
$title = $this->get_title();
|
||||
|
||||
if ( empty( $_GET[ self::CLEAR_LOG_ACTION ] ) ) { // phpcs:ignore -- nonce validation is not require here.
|
||||
$nonce = wp_create_nonce( self::CLEAR_LOG_ACTION );
|
||||
$url = add_query_arg( [
|
||||
self::CLEAR_LOG_ACTION => 1,
|
||||
'_wpnonce' => $nonce,
|
||||
] );
|
||||
|
||||
$title .= '<a href="' . esc_url( $url ) . '#elementor-clear-log" class="box-title-tool">' . esc_html__( 'Clear Log', 'elementor' ) . '</a>';
|
||||
$title .= '<span id="elementor-clear-log"></span>';
|
||||
}
|
||||
|
||||
parent::print_html_label( $title );
|
||||
}
|
||||
|
||||
public function get_log_entries() {
|
||||
/** @var \Elementor\Core\Logger\Manager $manager */
|
||||
$manager = Manager::instance();
|
||||
|
||||
/** @var \Elementor\Core\Logger\Loggers\Db $logger */
|
||||
$logger = $manager->get_logger( 'db' );
|
||||
|
||||
if ( ! empty( $_GET[ self::CLEAR_LOG_ACTION ] ) ) {
|
||||
$nonce = Utils::get_super_global_value( $_GET, '_wpnonce' );
|
||||
|
||||
if ( ! wp_verify_nonce( $nonce, self::CLEAR_LOG_ACTION ) ) {
|
||||
wp_die( 'Invalid Nonce', 'Invalid Nonce', [
|
||||
'back_link' => true,
|
||||
] );
|
||||
}
|
||||
|
||||
$logger->clear();
|
||||
}
|
||||
|
||||
$log_string = 'No entries to display';
|
||||
$log_entries = $logger->get_formatted_log_entries( self::MAX_ENTRIES, false );
|
||||
|
||||
if ( ! empty( $log_entries ) ) {
|
||||
$entries_string = '';
|
||||
foreach ( $log_entries as $key => $log_entry ) {
|
||||
if ( $log_entry['count'] ) {
|
||||
$entries_string .= '<h3>' . sprintf( '%s: showing %s of %s', $key, $log_entry['count'], $log_entry['total_count'] ) . '</h3>';
|
||||
$entries_string .= '<div class="elementor-log-entries">' . $log_entry['entries'] . '</div>';
|
||||
}
|
||||
}
|
||||
|
||||
if ( ! empty( $entries_string ) ) {
|
||||
$log_string = $entries_string;
|
||||
}
|
||||
}
|
||||
|
||||
return [
|
||||
'value' => $log_string,
|
||||
];
|
||||
}
|
||||
|
||||
public function get_raw_log_entries() {
|
||||
$log_string = 'No entries to display';
|
||||
|
||||
/** @var \Elementor\Core\Logger\Manager $manager */
|
||||
$manager = Manager::instance();
|
||||
$logger = $manager->get_logger();
|
||||
$log_entries = $logger->get_formatted_log_entries( self::MAX_ENTRIES, false );
|
||||
|
||||
if ( ! empty( $log_entries ) ) {
|
||||
$entries_string = PHP_EOL;
|
||||
foreach ( $log_entries as $key => $log_entry ) {
|
||||
if ( $log_entry['count'] ) {
|
||||
$entries_string .= sprintf( '%s: showing %s of %s', $key, $log_entry['count'], $log_entry['total_count'] ) . $log_entry['entries'] . PHP_EOL;
|
||||
}
|
||||
}
|
||||
|
||||
if ( ! empty( $entries_string ) ) {
|
||||
$log_string = $entries_string;
|
||||
}
|
||||
}
|
||||
|
||||
return [
|
||||
'value' => $log_string,
|
||||
];
|
||||
}
|
||||
}
|
||||
93
wp-content/plugins/elementor/core/logger/loggers/base.php
Normal file
93
wp-content/plugins/elementor/core/logger/loggers/base.php
Normal file
@@ -0,0 +1,93 @@
|
||||
<?php
|
||||
namespace Elementor\Core\Logger\Loggers;
|
||||
|
||||
use Elementor\Core\Logger\Items\Base as Log_Item;
|
||||
use Elementor\Core\Logger\Items\Log_Item_Interface as Log_Item_Interface;
|
||||
|
||||
if ( ! defined( 'ABSPATH' ) ) {
|
||||
exit; // Exit if accessed directly
|
||||
}
|
||||
|
||||
abstract class Base implements Logger_Interface {
|
||||
|
||||
abstract protected function save_log( Log_Item_Interface $item );
|
||||
|
||||
/**
|
||||
* @return Log_Item_Interface[]
|
||||
*/
|
||||
abstract public function get_log();
|
||||
|
||||
public function log( $item, $type = self::LEVEL_INFO, $args = [] ) {
|
||||
if ( ! $item instanceof Log_Item ) {
|
||||
$item = $this->create_item( $item, $type, $args );
|
||||
}
|
||||
$this->save_log( $item );
|
||||
}
|
||||
|
||||
public function info( $message, $args = [] ) {
|
||||
$this->log( $message, self::LEVEL_INFO, $args );
|
||||
}
|
||||
|
||||
public function notice( $message, $args = [] ) {
|
||||
$this->log( $message, self::LEVEL_NOTICE, $args );
|
||||
}
|
||||
|
||||
public function warning( $message, $args = [] ) {
|
||||
$this->log( $message, self::LEVEL_WARNING, $args );
|
||||
}
|
||||
|
||||
public function error( $message, $args = [] ) {
|
||||
$this->log( $message, self::LEVEL_ERROR, $args );
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $message
|
||||
* @param string $type
|
||||
* @param array $args
|
||||
*
|
||||
* @return Log_Item_Interface
|
||||
*/
|
||||
private function create_item( $message, $type, $args = [] ) {
|
||||
$args['message'] = $message;
|
||||
$args['type'] = $type;
|
||||
|
||||
$item = new Log_Item( $args );
|
||||
|
||||
return $item;
|
||||
}
|
||||
|
||||
public function get_formatted_log_entries( $max_entries, $table = true ) {
|
||||
$entries = $this->get_log();
|
||||
|
||||
if ( empty( $entries ) ) {
|
||||
return [
|
||||
'All' => [
|
||||
'total_count' => 0,
|
||||
'count' => 0,
|
||||
'entries' => '',
|
||||
],
|
||||
];
|
||||
}
|
||||
|
||||
$sorted_entries = [];
|
||||
$open_tag = $table ? '<tr><td>' : '';
|
||||
$close_tab = $table ? '</td></tr>' : PHP_EOL;
|
||||
|
||||
$format = $table ? 'html' : 'raw';
|
||||
|
||||
foreach ( $entries as $entry ) {
|
||||
/** @var Log_Item $entry */
|
||||
$sorted_entries[ $entry->get_name() ][] = $open_tag . $entry->format( $format ) . $close_tab;
|
||||
}
|
||||
|
||||
$formatted_entries = [];
|
||||
foreach ( $sorted_entries as $key => $sorted_entry ) {
|
||||
$formatted_entries[ $key ]['total_count'] = count( $sorted_entry );
|
||||
$formatted_entries[ $key ]['count'] = count( $sorted_entry );
|
||||
$sorted_entry = array_slice( $sorted_entry, -$max_entries );
|
||||
$formatted_entries[ $key ]['count'] = count( $sorted_entry );
|
||||
$formatted_entries[ $key ]['entries'] = implode( $sorted_entry );
|
||||
}
|
||||
return $formatted_entries;
|
||||
}
|
||||
}
|
||||
54
wp-content/plugins/elementor/core/logger/loggers/db.php
Normal file
54
wp-content/plugins/elementor/core/logger/loggers/db.php
Normal file
@@ -0,0 +1,54 @@
|
||||
<?php
|
||||
namespace Elementor\Core\Logger\Loggers;
|
||||
|
||||
use Elementor\Core\Logger\Items\Log_Item_Interface as Log_Item;
|
||||
|
||||
if ( ! defined( 'ABSPATH' ) ) {
|
||||
exit; // Exit if accessed directly
|
||||
}
|
||||
|
||||
class Db extends Base {
|
||||
|
||||
public function save_log( Log_Item $item ) {
|
||||
$log = $this->maybe_truncate_log();
|
||||
|
||||
$id = $item->get_fingerprint();
|
||||
|
||||
if ( empty( $log[ $id ] ) ) {
|
||||
$log[ $id ] = $item;
|
||||
}
|
||||
|
||||
$log[ $id ]->increase_times( $item );
|
||||
|
||||
update_option( self::LOG_NAME, $log, 'no' );
|
||||
}
|
||||
|
||||
public function clear() {
|
||||
delete_option( self::LOG_NAME );
|
||||
}
|
||||
|
||||
private function maybe_truncate_log() {
|
||||
/** @var Log_Item[] $log */
|
||||
$log = $this->get_log();
|
||||
|
||||
if ( Log_Item::MAX_LOG_ENTRIES < count( $log ) ) {
|
||||
$log = array_slice( $log, -Log_Item::MAX_LOG_ENTRIES );
|
||||
}
|
||||
|
||||
return $log;
|
||||
}
|
||||
|
||||
public function get_log() {
|
||||
// Clear cache.
|
||||
wp_cache_delete( self::LOG_NAME, 'options' );
|
||||
|
||||
$log = get_option( self::LOG_NAME, [] );
|
||||
|
||||
// In case the DB log is corrupted.
|
||||
if ( ! is_array( $log ) ) {
|
||||
$log = [];
|
||||
}
|
||||
|
||||
return $log;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,64 @@
|
||||
<?php
|
||||
namespace Elementor\Core\Logger\Loggers;
|
||||
|
||||
if ( ! defined( 'ABSPATH' ) ) {
|
||||
exit; // Exit if accessed directly
|
||||
}
|
||||
|
||||
interface Logger_Interface {
|
||||
const LEVEL_INFO = 'info';
|
||||
const LEVEL_NOTICE = 'notice';
|
||||
const LEVEL_WARNING = 'warning';
|
||||
const LEVEL_ERROR = 'error';
|
||||
const LOG_NAME = 'elementor_log';
|
||||
|
||||
/**
|
||||
* @param string $message
|
||||
* @param string $type
|
||||
* @param array $meta
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function log( $message, $type = self::LEVEL_INFO, $meta = [] );
|
||||
|
||||
/**
|
||||
* @param string $message
|
||||
* @param array $meta
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function info( $message, $meta = [] );
|
||||
|
||||
/**
|
||||
* @param string $message
|
||||
* @param array $meta
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function notice( $message, $meta = [] );
|
||||
|
||||
/**
|
||||
* @param string $message
|
||||
* @param array $meta
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function warning( $message, $meta = [] );
|
||||
|
||||
/**
|
||||
* @param string $message
|
||||
* @param array $meta
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function error( $message, $meta = [] );
|
||||
|
||||
/**
|
||||
* @param int $max_entries
|
||||
* @param bool $table use <td> in format
|
||||
*
|
||||
* @return array [ 'key' => [ 'total_count' => int, 'count' => int, 'entries' => Log_Item[] ] ]
|
||||
*/
|
||||
public function get_formatted_log_entries( $max_entries, $table = true );
|
||||
|
||||
}
|
||||
281
wp-content/plugins/elementor/core/logger/manager.php
Normal file
281
wp-content/plugins/elementor/core/logger/manager.php
Normal file
@@ -0,0 +1,281 @@
|
||||
<?php
|
||||
namespace Elementor\Core\Logger;
|
||||
|
||||
use Elementor\Core\Base\Module as BaseModule;
|
||||
use Elementor\Core\Common\Modules\Ajax\Module;
|
||||
use Elementor\Core\Editor\Editor;
|
||||
use Elementor\Core\Logger\Loggers\Logger_Interface;
|
||||
use Elementor\Core\Logger\Items\PHP;
|
||||
use Elementor\Core\Logger\Items\JS;
|
||||
use Elementor\Plugin;
|
||||
use Elementor\Modules\System_Info\Module as System_Info;
|
||||
use Elementor\Utils;
|
||||
|
||||
if ( ! defined( 'ABSPATH' ) ) {
|
||||
exit; // Exit if accessed directly
|
||||
}
|
||||
|
||||
class Manager extends BaseModule {
|
||||
|
||||
protected $loggers = [];
|
||||
|
||||
protected $default_logger = '';
|
||||
|
||||
public function get_name() {
|
||||
return 'log';
|
||||
}
|
||||
|
||||
public function shutdown( $last_error = null, $should_exit = false ) {
|
||||
if ( ! $last_error ) {
|
||||
$last_error = error_get_last();
|
||||
}
|
||||
|
||||
if ( ! $last_error ) {
|
||||
return;
|
||||
}
|
||||
|
||||
if ( empty( $last_error['file'] ) ) {
|
||||
return;
|
||||
}
|
||||
|
||||
if ( ! Utils::is_elementor_path( $last_error['file'] ) ) {
|
||||
return;
|
||||
}
|
||||
|
||||
$last_error['type'] = $this->get_log_type_from_php_error( $last_error['type'] );
|
||||
$last_error['trace'] = true;
|
||||
|
||||
$item = new PHP( $last_error );
|
||||
|
||||
$this->get_logger()->log( $item );
|
||||
|
||||
if ( $should_exit ) {
|
||||
exit;
|
||||
}
|
||||
}
|
||||
|
||||
public function rest_error_handler( $error_number, $error_message, $error_file, $error_line ) {
|
||||
// Temporary solution until all PHP notices will be fixed in the core and pro.
|
||||
if ( Utils::is_wp_cli() ) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$error = new \WP_Error( $error_number, $error_message, [
|
||||
'type' => $error_number,
|
||||
'message' => $error_message,
|
||||
'file' => $error_file,
|
||||
'line' => $error_line,
|
||||
] );
|
||||
|
||||
if ( ! Utils::is_elementor_path( $error_file ) ) {
|
||||
// Do execute PHP internal error handler.
|
||||
return false;
|
||||
}
|
||||
|
||||
$is_an_error = in_array( // It can be notice or warning
|
||||
$error_number,
|
||||
[ E_ERROR, E_CORE_ERROR, E_COMPILE_ERROR, E_USER_ERROR ],
|
||||
true
|
||||
);
|
||||
|
||||
$error_data = $error->get_error_data();
|
||||
|
||||
// TODO: This part should be modular, temporary hard-coded.
|
||||
// Notify $e.data.
|
||||
if ( $is_an_error && ! headers_sent() ) {
|
||||
header( 'Content-Type: application/json; charset=UTF-8' );
|
||||
|
||||
http_response_code( 500 );
|
||||
|
||||
if ( defined( 'WP_DEBUG' ) && WP_DEBUG ) {
|
||||
echo wp_json_encode( $error_data );
|
||||
} else {
|
||||
echo wp_json_encode( [
|
||||
'message' => 'Server error, see Elementor => System Info',
|
||||
] );
|
||||
}
|
||||
}
|
||||
|
||||
$this->shutdown( $error_data, $is_an_error );
|
||||
}
|
||||
|
||||
public function register_error_handler() {
|
||||
set_error_handler( [ $this, 'rest_error_handler' ], E_ALL );
|
||||
}
|
||||
|
||||
public function add_system_info_report() {
|
||||
System_Info::add_report(
|
||||
'log', [
|
||||
'file_name' => __DIR__ . '/log-reporter.php',
|
||||
'class_name' => __NAMESPACE__ . '\Log_Reporter',
|
||||
]
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Javascript log.
|
||||
*
|
||||
* Log Elementor errors and save them in the database.
|
||||
*
|
||||
* Fired by `wp_ajax_elementor_js_log` action.
|
||||
*
|
||||
*/
|
||||
public function js_log() {
|
||||
/** @var Module $ajax */
|
||||
$ajax = Plugin::$instance->common->get_component( 'ajax' );
|
||||
|
||||
// PHPCS ignore is added throughout this method because nonce verification happens in the $ajax->verify_request_nonce() method.
|
||||
if ( ! $ajax->verify_request_nonce() || empty( $_POST['data'] ) ) { // phpcs:ignore WordPress.Security.NonceVerification.Missing
|
||||
wp_send_json_error();
|
||||
}
|
||||
|
||||
if ( ! current_user_can( Editor::EDITING_CAPABILITY ) ) {
|
||||
wp_send_json_error( 'Permission denied' );
|
||||
}
|
||||
|
||||
// PHPCS - See comment above.
|
||||
$data = Utils::get_super_global_value( $_POST, 'data' ) ?? []; // phpcs:ignore WordPress.Security.NonceVerification.Missing
|
||||
|
||||
array_walk_recursive( $data, function( &$value ) {
|
||||
$value = sanitize_text_field( $value );
|
||||
} );
|
||||
|
||||
// PHPCS - See comment above.
|
||||
foreach ( $data as $error ) { // phpcs:ignore WordPress.Security.NonceVerification.Missing
|
||||
$error['type'] = Logger_Interface::LEVEL_ERROR;
|
||||
|
||||
if ( ! empty( $error['customFields'] ) ) {
|
||||
$error['meta'] = $error['customFields'];
|
||||
}
|
||||
|
||||
$item = new JS( $error );
|
||||
$this->get_logger()->log( $item );
|
||||
}
|
||||
|
||||
wp_send_json_success();
|
||||
}
|
||||
|
||||
public function register_logger( $name, $class ) {
|
||||
$this->loggers[ $name ] = $class;
|
||||
}
|
||||
|
||||
public function set_default_logger( $name ) {
|
||||
if ( ! empty( $this->loggers[ $name ] ) ) {
|
||||
$this->default_logger = $name;
|
||||
}
|
||||
}
|
||||
|
||||
public function register_default_loggers() {
|
||||
$this->register_logger( 'db', 'Elementor\Core\Logger\Loggers\Db' );
|
||||
$this->set_default_logger( 'db' );
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $name
|
||||
*
|
||||
* @return Logger_Interface
|
||||
*/
|
||||
public function get_logger( $name = '' ) {
|
||||
$this->register_loggers();
|
||||
|
||||
if ( empty( $name ) || ! isset( $this->loggers[ $name ] ) ) {
|
||||
$name = $this->default_logger;
|
||||
}
|
||||
|
||||
if ( ! $this->get_component( $name ) ) {
|
||||
$this->add_component( $name, new $this->loggers[ $name ]() );
|
||||
}
|
||||
|
||||
return $this->get_component( $name );
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $message
|
||||
* @param array $args
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function log( $message, $args = [] ) {
|
||||
$this->get_logger()->log( $message, $args );
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $message
|
||||
* @param array $args
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function info( $message, $args = [] ) {
|
||||
$this->get_logger()->info( $message, $args );
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $message
|
||||
* @param array $args
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function notice( $message, $args = [] ) {
|
||||
$this->get_logger()->notice( $message, $args );
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $message
|
||||
* @param array $args
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function warning( $message, $args = [] ) {
|
||||
$this->get_logger()->warning( $message, $args );
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $message
|
||||
* @param array $args
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function error( $message, $args = [] ) {
|
||||
$this->get_logger()->error( $message, $args );
|
||||
}
|
||||
|
||||
private function get_log_type_from_php_error( $type ) {
|
||||
$error_map = [
|
||||
E_CORE_ERROR => Logger_Interface::LEVEL_ERROR,
|
||||
E_ERROR => Logger_Interface::LEVEL_ERROR,
|
||||
E_USER_ERROR => Logger_Interface::LEVEL_ERROR,
|
||||
E_COMPILE_ERROR => Logger_Interface::LEVEL_ERROR,
|
||||
E_RECOVERABLE_ERROR => Logger_Interface::LEVEL_ERROR,
|
||||
E_PARSE => Logger_Interface::LEVEL_ERROR,
|
||||
E_STRICT => Logger_Interface::LEVEL_ERROR,
|
||||
|
||||
E_WARNING => Logger_Interface::LEVEL_WARNING,
|
||||
E_USER_WARNING => Logger_Interface::LEVEL_WARNING,
|
||||
E_CORE_WARNING => Logger_Interface::LEVEL_WARNING,
|
||||
E_COMPILE_WARNING => Logger_Interface::LEVEL_WARNING,
|
||||
|
||||
E_NOTICE => Logger_Interface::LEVEL_NOTICE,
|
||||
E_USER_NOTICE => Logger_Interface::LEVEL_NOTICE,
|
||||
E_DEPRECATED => Logger_Interface::LEVEL_NOTICE,
|
||||
E_USER_DEPRECATED => Logger_Interface::LEVEL_NOTICE,
|
||||
];
|
||||
|
||||
return isset( $error_map[ $type ] ) ? $error_map[ $type ] : Logger_Interface::LEVEL_ERROR;
|
||||
}
|
||||
|
||||
private function register_loggers() {
|
||||
if ( ! did_action( 'elementor/loggers/register' ) ) {
|
||||
do_action( 'elementor/loggers/register', $this );
|
||||
}
|
||||
}
|
||||
|
||||
public function __construct() {
|
||||
register_shutdown_function( [ $this, 'shutdown' ] );
|
||||
|
||||
add_action( 'admin_init', [ $this, 'add_system_info_report' ], 80 );
|
||||
|
||||
add_action( 'wp_ajax_elementor_js_log', [ $this, 'js_log' ] );
|
||||
|
||||
add_action( 'elementor/loggers/register', [ $this, 'register_default_loggers' ] );
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user