PluginProbe
Image Optimizer – Compress Images and Convert to WebP or AVIF / 1.7.7
Image Optimizer – Compress Images and Convert to WebP or AVIF v1.7.7
1.7.7 1.7.6 1.7.5 1.7.4 trunk 1.0.0 1.0.1 1.0.2 1.1.0 1.2.0 1.2.1 1.3.0 1.4.0 1.4.1 1.5.0 1.5.1 1.5.2 1.5.3 1.5.4 1.6.0 1.6.1 1.6.2 1.6.3 1.6.4 1.6.5 All 33 releases
image-optimization / vendor / elementor / wp-one-package / src / LogStore.php

LogStore.php in Image Optimizer – Compress Images and Convert to WebP or AVIF 1.7.7, at vendor/elementor/wp-one-package/src/LogStore.php

156 lines 3.1 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2
3 namespace ElementorOne;
4
5 if ( ! defined( 'ABSPATH' ) ) {
6 exit;
7 }
8
9 /**
10 * Ring buffer persisted in a non-autoloaded wp_option.
11 */
12 class LogStore {
13
14 public const OPTION_KEY = 'elementor_one_internal_logs';
15
16 public const MAX_ENTRIES = 50;
17
18 public const MAX_MESSAGE_LENGTH = 1048;
19
20 public const MAX_CALL_STACK_LENGTH = 1048;
21
22 /**
23 * @var LogStore|null
24 */
25 private static ?LogStore $instance = null;
26
27 /**
28 * @var bool
29 */
30 private bool $is_writing = false;
31
32 /**
33 * @return LogStore
34 */
35 public static function instance(): LogStore {
36 if ( null === self::$instance ) {
37 self::$instance = new self();
38 }
39
40 return self::$instance;
41 }
42
43 /**
44 * @param string $level
45 * @param string $message
46 * @param array<string, mixed> $context
47 * @return void
48 */
49 public function append( string $level, string $message, array $context = [] ): void {
50 if ( $this->is_writing ) {
51 return;
52 }
53
54 $this->is_writing = true;
55
56 try {
57 $entries = get_option( self::OPTION_KEY, [] );
58
59 if ( ! is_array( $entries ) ) {
60 $entries = [];
61 }
62
63 $entries[] = [
64 'id' => $this->generate_entry_id(),
65 'timestamp' => gmdate( 'c' ),
66 'level' => $level,
67 'message' => $this->truncate_message( $message ),
68 'context' => $this->normalize_context( $context ),
69 ];
70
71 if ( count( $entries ) > self::MAX_ENTRIES ) {
72 $entries = array_slice( $entries, -self::MAX_ENTRIES );
73 }
74
75 update_option( self::OPTION_KEY, $entries, false );
76 } catch ( \Throwable $e ) {
77 unset( $e );
78 } finally {
79 $this->is_writing = false;
80 }
81 }
82
83 /**
84 * @return array<int, array<string, mixed>>
85 */
86 public function all(): array {
87 $entries = get_option( self::OPTION_KEY, [] );
88
89 if ( ! is_array( $entries ) ) {
90 return [];
91 }
92
93 return $entries;
94 }
95
96 /**
97 * @return array<int, array<string, mixed>>
98 */
99 public function all_newest_first(): array {
100 return array_reverse( $this->all() );
101 }
102
103 /**
104 * @return bool
105 */
106 public function clear(): bool {
107 return delete_option( self::OPTION_KEY );
108 }
109
110 /**
111 * @return string
112 */
113 private function generate_entry_id(): string {
114 if ( function_exists( 'wp_generate_uuid4' ) ) {
115 return wp_generate_uuid4();
116 }
117
118 return uniqid( 'log_', true );
119 }
120
121 /**
122 * @param string $message
123 * @return string
124 */
125 private function truncate_message( string $message ): string {
126 if ( strlen( $message ) <= self::MAX_MESSAGE_LENGTH ) {
127 return $message;
128 }
129
130 return substr( $message, 0, self::MAX_MESSAGE_LENGTH );
131 }
132
133 /**
134 * @param array<string, mixed> $context
135 * @return array{source: string, call_stack: string}
136 */
137 private function normalize_context( array $context ): array {
138 $source = isset( $context['source'] ) && is_string( $context['source'] )
139 ? $context['source']
140 : '';
141
142 $call_stack = isset( $context['call_stack'] ) && is_string( $context['call_stack'] )
143 ? $context['call_stack']
144 : '';
145
146 if ( strlen( $call_stack ) > self::MAX_CALL_STACK_LENGTH ) {
147 $call_stack = substr( $call_stack, 0, self::MAX_CALL_STACK_LENGTH );
148 }
149
150 return [
151 'source' => $source,
152 'call_stack' => $call_stack,
153 ];
154 }
155 }
156