PluginProbe
Media Cloud Sync / trunk
Media Cloud Sync vtrunk
1.4.0 1.3.12 1.3.11 1.3.10 trunk 1.0.0 1.0.1 1.0.2 1.0.3 1.1.0 1.1.1 1.2.0 1.2.10 1.2.11 1.2.12 1.2.13 1.2.2 1.2.3 1.2.4 1.2.5 1.2.6 1.2.7 1.2.8 1.2.9 1.3.0 All 34 releases
media-cloud-sync / includes / base / logger.php

logger.php in Media Cloud Sync trunk, at includes/base/logger.php

295 lines 8.0 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 namespace Dudlewebs\WPMCS;
3
4 defined('ABSPATH') || exit;
5
6 class Logger {
7 private static $instance = null;
8 private $log_dir;
9 private $log_file;
10 private $handle = null;
11
12 private function __construct() {
13 $upload_dir = wp_upload_dir();
14 $this->log_dir = trailingslashit($upload_dir['basedir']) . Schema::getConstant('UPLOADS');
15
16 if (!file_exists($this->log_dir)) {
17 wp_mkdir_p($this->log_dir);
18 }
19
20 $this->log_file = $this->log_dir . '/sync-log.json';
21 }
22
23 public function __destruct() {
24 if (is_resource($this->handle)) {
25 fclose($this->handle);
26 }
27 }
28
29 /**
30 * Singleton instance
31 */
32 public static function instance() {
33 if (self::$instance === null) {
34 self::$instance = new self();
35 }
36 return self::$instance;
37 }
38
39 /**
40 * Reuse a single open file handle for the process instead of
41 * reopening/closing on every call (heavy when many logs happen at once)
42 */
43 private function get_handle($create = true) {
44 if (is_resource($this->handle)) {
45 return $this->handle;
46 }
47
48 if (!$create && !file_exists($this->log_file)) {
49 return null;
50 }
51
52 $this->handle = fopen($this->log_file, 'c+');
53 return $this->handle ?: null;
54 }
55
56 /**
57 * Read logs under a shared lock
58 */
59 private function read_logs() {
60 $handle = $this->get_handle(false);
61 if (!$handle) return [];
62
63 $logs = [];
64 try {
65 if (flock($handle, LOCK_SH)) {
66 rewind($handle);
67 $content = stream_get_contents($handle);
68 $logs = json_decode($content, true) ?: [];
69 }
70 } finally {
71 flock($handle, LOCK_UN);
72 }
73
74 return $logs;
75 }
76
77 /**
78 * Read-modify-write logs under an exclusive lock held for the whole cycle
79 */
80 private function update_logs(callable $mutator) {
81 $handle = $this->get_handle(true);
82 if (!$handle) return;
83
84 try {
85 if (flock($handle, LOCK_EX)) {
86 rewind($handle);
87 $content = stream_get_contents($handle);
88 $logs = json_decode($content, true) ?: [];
89
90 $logs = $mutator($logs);
91
92 rewind($handle);
93 ftruncate($handle, 0);
94 fwrite($handle, wp_json_encode($logs));
95 fflush($handle);
96 }
97 } finally {
98 flock($handle, LOCK_UN);
99 }
100 }
101
102 /**
103 * Add or update a log entry
104 */
105 public function add_log($type, $media_id, $source_type, $log = []) {
106 $entry = [
107 'time' => current_time('mysql'),
108 'type' => $type,
109 'media_id' => $media_id,
110 'source_type' => $source_type,
111 ];
112
113 $this->append_log($entry, $log);
114 }
115
116 /**
117 * Append log using file-based indexing
118 */
119 private function append_log($entry, $log=[]) {
120 $this->update_logs(function($logs) use ($entry, $log) {
121 // Unique key per media/type/source
122 $key = $entry['media_id'] . '|' . $entry['type'] . '|' . $entry['source_type'];
123
124 if (isset($logs[$key])) {
125 if (!isset($logs[$key]['logs']) || !is_array($logs[$key]['logs'])) {
126 $logs[$key]['logs'] = [];
127 }
128 $logs[$key]['logs'][] = $log;
129 $logs[$key]['time'] = $entry['time'] ?? current_time('mysql');
130 } else {
131 $logs[$key] = $entry;
132 $logs[$key]['logs'][] = $log;
133 }
134
135 return $logs;
136 });
137 }
138
139 /**
140 * Get log by media ID, type, and source type
141 */
142 public function get_log($type, $media_id, $source_type) {
143 $key = $media_id . '|' . $type . '|' . $source_type;
144 return $this->read_logs()[$key] ?? [];
145 }
146
147 /**
148 * Get all logs (numeric array, supports pagination)
149 */
150 public function get_all_logs($page = null, $per_page = null) {
151 if (!file_exists($this->log_file)) {
152 return [
153 'logs' => [],
154 'total' => 0,
155 'total_pages' => 0,
156 'page' => 1,
157 'per_page' => $per_page ?:1,
158 ];
159 }
160
161 $logs = array_values($this->read_logs());
162
163 $total = count($logs);
164
165 if ($page === null || $per_page === null) {
166 return [
167 'logs' => $logs,
168 'total' => $total,
169 'total_pages' => 1,
170 'page' => 1,
171 'per_page' => $total,
172 ];
173 }
174
175 $total_pages = (int) ceil($total / $per_page);
176 $page = max(1, (int) $page);
177 $offset = ($page - 1) * $per_page;
178
179 return [
180 'logs' => array_slice($logs, $offset, $per_page),
181 'total' => $total,
182 'total_pages' => $total_pages,
183 'page' => $page,
184 'per_page' => $per_page,
185 ];
186 }
187
188 /**
189 * Get logs by type (supports pagination)
190 */
191 public function get_logs_by_type($type, $page = null, $per_page = null) {
192 if (!file_exists($this->log_file)) {
193 return [
194 'logs' => [],
195 'total' => 0,
196 'total_pages' => 0,
197 'page' => 1,
198 'per_page' => $per_page ?:1,
199 ];
200 };
201
202 $all_logs = $this->read_logs();
203
204 $logs = array_values(array_filter($all_logs, function($log) use ($type) {
205 return isset($log['type']) && $log['type'] === $type;
206 }));
207
208 $total = count($logs);
209
210 if ($page === null || $per_page === null) {
211 return [
212 'logs' => $logs,
213 'total' => $total,
214 'total_pages' => 1,
215 'page' => 1,
216 'per_page' => $total,
217 ];
218 }
219
220
221 $total_pages = (int) ceil($total / $per_page);
222 $page = max(1, (int) $page);
223 $offset = ($page - 1) * $per_page;
224
225 return [
226 'logs' => array_slice($logs, $offset, $per_page),
227 'total' => $total,
228 'total_pages' => $total_pages,
229 'page' => $page,
230 'per_page' => $per_page,
231 ];
232 }
233
234
235 /**
236 * Remove logs by type
237 */
238 public function remove_logs_by_type($type) {
239 if (!file_exists($this->log_file)) return;
240
241 $this->update_logs(function($logs) use ($type) {
242 foreach ($logs as $key => $log) {
243 if (isset($log['type']) && $log['type'] === $type) {
244 unset($logs[$key]);
245 }
246 }
247
248 return $logs;
249 });
250 }
251
252 /**
253 * Remove logs by media ID, type, and source type
254 */
255 public function remove_log($type, $media_id, $source_type) {
256 if (!file_exists($this->log_file)) return;
257
258 $this->update_logs(function($logs) use ($type, $media_id, $source_type) {
259 $key = $media_id . '|' . $type . '|' . $source_type;
260
261 if (isset($logs[$key])) {
262 unset($logs[$key]);
263 }
264
265 return $logs;
266 });
267 }
268
269 /**
270 * Remove Log by media ID & source type
271 */
272 public function remove_log_by_media_id($media_id, $source_type) {
273 if (!file_exists($this->log_file)) return;
274
275 $this->update_logs(function($logs) use ($media_id, $source_type) {
276 return array_filter($logs, function($log) use ($media_id, $source_type) {
277 return $log['media_id'] !== $media_id || $log['source_type'] !== $source_type;
278 });
279 });
280 }
281
282 /**
283 * Clear all logs
284 */
285 public function clear_logs() {
286 if (is_resource($this->handle)) {
287 fclose($this->handle);
288 $this->handle = null;
289 }
290
291 if (file_exists($this->log_file)) {
292 unlink($this->log_file);
293 }
294 }
295 }