loggers/base.php 0000644 00000004520 14720730760 0007640 0 ustar 00 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 ? '
' : '';
$close_tab = $table ? ' |
' : 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;
}
}
loggers/db.php 0000644 00000002006 14720730760 0007310 0 ustar 00 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;
}
}
loggers/logger-interface.php 0000644 00000002364 14720730760 0012147 0 ustar 00 in format
*
* @return array [ 'key' => [ 'total_count' => int, 'count' => int, 'entries' => Log_Item[] ] ]
*/
public function get_formatted_log_entries( $max_entries, $table = true );
}
log-reporter.php 0000644 00000006005 14720730760 0007705 0 ustar 00 '',
];
}
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 .= '' . esc_html__( 'Clear Log', 'elementor' ) . '';
$title .= '';
}
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 .= '' . sprintf( '%s: showing %s of %s', $key, $log_entry['count'], $log_entry['total_count'] ) . '
';
$entries_string .= '' . $log_entry['entries'] . '
';
}
}
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,
];
}
}
manager.php 0000644 00000015661 14720730760 0006706 0 ustar 00 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' ] );
}
}
items/log-item-interface.php 0000644 00000002307 14720730760 0012061 0 ustar 00 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';
}
}
items/base.php 0000644 00000012275 14720730760 0007325 0 ustar 00 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', 'message', 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'] );
}
}
}
items/js.php 0000644 00000001536 14720730760 0007025 0 ustar 00 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';
}
}
items/php.php 0000644 00000000412 14720730760 0007170 0 ustar 00