1
|
|
|
<?php |
|
|
|
|
2
|
|
|
/** |
3
|
|
|
* @package Freemius |
4
|
|
|
* @copyright Copyright (c) 2015, Freemius, Inc. |
5
|
|
|
* @license http://opensource.org/licenses/gpl-2.0.php GNU Public License |
6
|
|
|
* @since 1.0.3 |
7
|
|
|
*/ |
8
|
|
|
|
9
|
|
|
if ( ! defined( 'ABSPATH' ) ) { |
10
|
|
|
exit; |
11
|
|
|
} |
12
|
|
|
|
13
|
|
|
class FS_Logger { |
|
|
|
|
14
|
|
|
private $_id; |
15
|
|
|
private $_on = false; |
16
|
|
|
private $_echo = false; |
17
|
|
|
private $_file_start = 0; |
18
|
|
|
/** |
19
|
|
|
* @var int PHP Process ID. |
20
|
|
|
*/ |
21
|
|
|
private static $_processID; |
22
|
|
|
/** |
23
|
|
|
* @var string PHP Script user name. |
24
|
|
|
*/ |
25
|
|
|
private static $_ownerName; |
26
|
|
|
/** |
27
|
|
|
* @var bool Is storage logging turned on. |
28
|
|
|
*/ |
29
|
|
|
private static $_isStorageLoggingOn; |
30
|
|
|
/** |
31
|
|
|
* @var int ABSPATH length. |
32
|
|
|
*/ |
33
|
|
|
private static $_abspathLength; |
34
|
|
|
|
35
|
|
|
private static $LOGGERS = array(); |
36
|
|
|
private static $LOG = array(); |
37
|
|
|
private static $CNT = 0; |
38
|
|
|
private static $_HOOKED_FOOTER = false; |
39
|
|
|
|
40
|
|
|
private function __construct( $id, $on = false, $echo = false ) { |
41
|
|
|
$this->_id = $id; |
42
|
|
|
|
43
|
|
|
$bt = debug_backtrace(); |
44
|
|
|
$caller = $bt[2]; |
45
|
|
|
|
46
|
|
|
if ( false !== strpos( $caller['file'], 'plugins' ) ) { |
47
|
|
|
$this->_file_start = strpos( $caller['file'], 'plugins' ) + strlen( 'plugins/' ); |
48
|
|
|
} else { |
49
|
|
|
$this->_file_start = strpos( $caller['file'], 'themes' ) + strlen( 'themes/' ); |
50
|
|
|
} |
51
|
|
|
|
52
|
|
|
if ( $on ) { |
53
|
|
|
$this->on(); |
54
|
|
|
} |
55
|
|
|
if ( $echo ) { |
56
|
|
|
$this->echo_on(); |
57
|
|
|
} |
58
|
|
|
} |
59
|
|
|
|
60
|
|
|
/** |
61
|
|
|
* @param string $id |
62
|
|
|
* @param bool $on |
63
|
|
|
* @param bool $echo |
64
|
|
|
* |
65
|
|
|
* @return FS_Logger |
66
|
|
|
*/ |
67
|
|
|
public static function get_logger( $id, $on = false, $echo = false ) { |
68
|
|
|
$id = strtolower( $id ); |
69
|
|
|
|
70
|
|
|
if ( ! isset( self::$_processID ) ) { |
71
|
|
|
self::init(); |
72
|
|
|
} |
73
|
|
|
|
74
|
|
|
if ( ! isset( self::$LOGGERS[ $id ] ) ) { |
75
|
|
|
self::$LOGGERS[ $id ] = new FS_Logger( $id, $on, $echo ); |
76
|
|
|
} |
77
|
|
|
|
78
|
|
|
return self::$LOGGERS[ $id ]; |
79
|
|
|
} |
80
|
|
|
|
81
|
|
|
/** |
82
|
|
|
* Initialize logging global info. |
83
|
|
|
* |
84
|
|
|
* @author Vova Feldman (@svovaf) |
85
|
|
|
* @since 1.2.1.6 |
86
|
|
|
*/ |
87
|
|
|
private static function init() { |
88
|
|
|
self::$_ownerName = function_exists( 'get_current_user' ) ? |
89
|
|
|
get_current_user() : |
90
|
|
|
'unknown'; |
91
|
|
|
self::$_isStorageLoggingOn = ( 1 == get_option( 'fs_storage_logger', 0 ) ); |
92
|
|
|
self::$_abspathLength = strlen( ABSPATH ); |
93
|
|
|
self::$_processID = mt_rand( 0, 32000 ); |
94
|
|
|
|
95
|
|
|
// Process ID may be `false` on errors. |
96
|
|
|
if ( ! is_numeric( self::$_processID ) ) { |
97
|
|
|
self::$_processID = 0; |
98
|
|
|
} |
99
|
|
|
} |
100
|
|
|
|
101
|
|
|
private static function hook_footer() { |
102
|
|
|
if ( self::$_HOOKED_FOOTER ) { |
103
|
|
|
return; |
104
|
|
|
} |
105
|
|
|
|
106
|
|
|
if ( is_admin() ) { |
107
|
|
|
add_action( 'admin_footer', 'FS_Logger::dump', 100 ); |
108
|
|
|
} else { |
109
|
|
|
add_action( 'wp_footer', 'FS_Logger::dump', 100 ); |
110
|
|
|
} |
111
|
|
|
} |
112
|
|
|
|
113
|
|
|
function is_on() { |
|
|
|
|
114
|
|
|
return $this->_on; |
115
|
|
|
} |
116
|
|
|
|
117
|
|
|
function on() { |
|
|
|
|
118
|
|
|
$this->_on = true; |
119
|
|
|
|
120
|
|
|
if ( ! function_exists( 'dbDelta' ) ) { |
121
|
|
|
require_once ABSPATH . 'wp-admin/includes/upgrade.php'; |
122
|
|
|
} |
123
|
|
|
|
124
|
|
|
self::hook_footer(); |
125
|
|
|
} |
126
|
|
|
|
127
|
|
|
function echo_on() { |
|
|
|
|
128
|
|
|
$this->on(); |
129
|
|
|
|
130
|
|
|
$this->_echo = true; |
131
|
|
|
} |
132
|
|
|
|
133
|
|
|
function is_echo_on() { |
|
|
|
|
134
|
|
|
return $this->_echo; |
135
|
|
|
} |
136
|
|
|
|
137
|
|
|
function get_id() { |
|
|
|
|
138
|
|
|
return $this->_id; |
139
|
|
|
} |
140
|
|
|
|
141
|
|
|
function get_file() { |
|
|
|
|
142
|
|
|
return $this->_file_start; |
143
|
|
|
} |
144
|
|
|
|
145
|
|
|
private function _log( &$message, $type = 'log', $wrapper ) { |
146
|
|
|
if ( ! $this->is_on() ) { |
147
|
|
|
return; |
148
|
|
|
} |
149
|
|
|
|
150
|
|
|
$bt = debug_backtrace(); |
151
|
|
|
$depth = $wrapper ? 3 : 2; |
152
|
|
|
while ( $depth < count( $bt ) - 1 && 'eval' === $bt[ $depth ]['function'] ) { |
153
|
|
|
$depth ++; |
154
|
|
|
} |
155
|
|
|
|
156
|
|
|
$caller = $bt[ $depth ]; |
157
|
|
|
|
158
|
|
|
/** |
159
|
|
|
* Retrieve the correct call file & line number from backtrace |
160
|
|
|
* when logging from a wrapper method. |
161
|
|
|
* |
162
|
|
|
* @author Vova Feldman |
163
|
|
|
* @since 1.2.1.6 |
164
|
|
|
*/ |
165
|
|
|
if ( empty( $caller['line'] ) ) { |
166
|
|
|
$depth --; |
167
|
|
|
|
168
|
|
|
while ( $depth >= 0 ) { |
169
|
|
|
if ( ! empty( $bt[ $depth ]['line'] ) ) { |
170
|
|
|
$caller['line'] = $bt[ $depth ]['line']; |
171
|
|
|
$caller['file'] = $bt[ $depth ]['file']; |
172
|
|
|
break; |
173
|
|
|
} |
174
|
|
|
} |
175
|
|
|
} |
176
|
|
|
|
177
|
|
|
$log = array_merge( $caller, array( |
178
|
|
|
'cnt' => self::$CNT ++, |
179
|
|
|
'logger' => $this, |
180
|
|
|
'timestamp' => microtime( true ), |
181
|
|
|
'log_type' => $type, |
182
|
|
|
'msg' => $message, |
183
|
|
|
) ); |
184
|
|
|
|
185
|
|
|
if ( self::$_isStorageLoggingOn ) { |
186
|
|
|
$this->db_log( $type, $message, self::$CNT, $caller ); |
187
|
|
|
} |
188
|
|
|
|
189
|
|
|
self::$LOG[] = $log; |
190
|
|
|
|
191
|
|
|
if ( $this->is_echo_on() && ! Freemius::is_ajax() ) { |
192
|
|
|
echo self::format_html( $log ) . "\n"; |
193
|
|
|
} |
194
|
|
|
} |
195
|
|
|
|
196
|
|
|
function log( $message, $wrapper = false ) { |
|
|
|
|
197
|
|
|
$this->_log( $message, 'log', $wrapper ); |
198
|
|
|
} |
199
|
|
|
|
200
|
|
|
function info( $message, $wrapper = false ) { |
|
|
|
|
201
|
|
|
$this->_log( $message, 'info', $wrapper ); |
202
|
|
|
} |
203
|
|
|
|
204
|
|
|
function warn( $message, $wrapper = false ) { |
|
|
|
|
205
|
|
|
$this->_log( $message, 'warn', $wrapper ); |
206
|
|
|
} |
207
|
|
|
|
208
|
|
|
function error( $message, $wrapper = false ) { |
|
|
|
|
209
|
|
|
$this->_log( $message, 'error', $wrapper ); |
210
|
|
|
} |
211
|
|
|
|
212
|
|
|
/** |
213
|
|
|
* Log API error. |
214
|
|
|
* |
215
|
|
|
* @author Vova Feldman (@svovaf) |
216
|
|
|
* @since 1.2.1.5 |
217
|
|
|
* |
218
|
|
|
* @param mixed $api_result |
219
|
|
|
* @param bool $wrapper |
220
|
|
|
*/ |
221
|
|
|
function api_error( $api_result, $wrapper = false ) { |
|
|
|
|
222
|
|
|
$message = ''; |
223
|
|
|
if ( is_object( $api_result ) && isset( $api_result->error ) ) { |
224
|
|
|
$message = $api_result->error->message; |
225
|
|
|
} else if ( is_object( $api_result ) ) { |
226
|
|
|
$message = var_export( $api_result, true ); |
227
|
|
|
} else if ( is_string( $api_result ) ) { |
228
|
|
|
$message = $api_result; |
229
|
|
|
} else if ( empty( $api_result ) ) { |
230
|
|
|
$message = 'Empty API result.'; |
231
|
|
|
} |
232
|
|
|
|
233
|
|
|
$message = 'API Error: ' . $message; |
234
|
|
|
|
235
|
|
|
$this->_log( $message, 'error', $wrapper ); |
236
|
|
|
} |
237
|
|
|
|
238
|
|
|
function entrance( $message = '', $wrapper = false ) { |
|
|
|
|
239
|
|
|
$msg = 'Entrance' . ( empty( $message ) ? '' : ' > ' ) . $message; |
240
|
|
|
|
241
|
|
|
$this->_log( $msg, 'log', $wrapper ); |
242
|
|
|
} |
243
|
|
|
|
244
|
|
|
function departure( $message = '', $wrapper = false ) { |
|
|
|
|
245
|
|
|
$msg = 'Departure' . ( empty( $message ) ? '' : ' > ' ) . $message; |
246
|
|
|
|
247
|
|
|
$this->_log( $msg, 'log', $wrapper ); |
248
|
|
|
} |
249
|
|
|
|
250
|
|
|
#-------------------------------------------------------------------------------- |
251
|
|
|
#region Log Formatting |
252
|
|
|
#-------------------------------------------------------------------------------- |
253
|
|
|
|
254
|
|
|
private static function format( $log, $show_type = true ) { |
255
|
|
|
return '[' . str_pad( $log['cnt'], strlen( self::$CNT ), '0', STR_PAD_LEFT ) . '] [' . $log['logger']->_id . '] ' . ( $show_type ? '[' . $log['log_type'] . ']' : '' ) . ( ! empty( $log['class'] ) ? $log['class'] . $log['type'] : '' ) . $log['function'] . ' >> ' . $log['msg'] . ( isset( $log['file'] ) ? ' (' . substr( $log['file'], $log['logger']->_file_start ) . ' ' . $log['line'] . ') ' : '' ) . ' [' . $log['timestamp'] . ']'; |
256
|
|
|
} |
257
|
|
|
|
258
|
|
|
private static function format_html( $log ) { |
259
|
|
|
return '<div style="font-size: 13px; font-family: monospace; color: #7da767; padding: 8px 3px; background: #000; border-bottom: 1px solid #555;">[' . $log['cnt'] . '] [' . $log['logger']->_id . '] [' . $log['log_type'] . '] <b><code style="color: #c4b1e0;">' . ( ! empty( $log['class'] ) ? $log['class'] . $log['type'] : '' ) . $log['function'] . '</code> >> <b style="color: #f59330;">' . esc_html( $log['msg'] ) . '</b></b>' . ( isset( $log['file'] ) ? ' (' . substr( $log['file'], $log['logger']->_file_start ) . ' ' . $log['line'] . ')' : '' ) . ' [' . $log['timestamp'] . ']</div>'; |
260
|
|
|
} |
261
|
|
|
|
262
|
|
|
#endregion |
263
|
|
|
|
264
|
|
|
static function dump() { |
|
|
|
|
265
|
|
|
?> |
266
|
|
|
<!-- BEGIN: Freemius PHP Console Log --> |
267
|
|
|
<script type="text/javascript"> |
268
|
|
|
<?php |
269
|
|
|
foreach ( self::$LOG as $log ) { |
270
|
|
|
echo 'console.' . $log['log_type'] . '(' . json_encode( self::format( $log, false ) ) . ')' . "\n"; |
271
|
|
|
} |
272
|
|
|
?> |
273
|
|
|
</script> |
274
|
|
|
<!-- END: Freemius PHP Console Log --> |
275
|
|
|
<?php |
276
|
|
|
} |
277
|
|
|
|
278
|
|
|
static function get_log() { |
|
|
|
|
279
|
|
|
return self::$LOG; |
280
|
|
|
} |
281
|
|
|
|
282
|
|
|
#-------------------------------------------------------------------------------- |
283
|
|
|
#region Database Logging |
284
|
|
|
#-------------------------------------------------------------------------------- |
285
|
|
|
|
286
|
|
|
/** |
287
|
|
|
* @author Vova Feldman (@svovaf) |
288
|
|
|
* @since 1.2.1.6 |
289
|
|
|
* |
290
|
|
|
* @return bool |
291
|
|
|
*/ |
292
|
|
|
public static function is_storage_logging_on() { |
293
|
|
|
if ( ! isset( self::$_isStorageLoggingOn ) ) { |
294
|
|
|
self::$_isStorageLoggingOn = ( 1 == get_option( 'fs_storage_logger', 0 ) ); |
295
|
|
|
} |
296
|
|
|
|
297
|
|
|
return self::$_isStorageLoggingOn; |
298
|
|
|
} |
299
|
|
|
|
300
|
|
|
/** |
301
|
|
|
* Turns on/off database persistent debugging to capture |
302
|
|
|
* multi-session logs to debug complex flows like |
303
|
|
|
* plugin auto-deactivate on premium version activation. |
304
|
|
|
* |
305
|
|
|
* @todo Check if Theme Check has issues with DB tables for themes. |
306
|
|
|
* |
307
|
|
|
* @author Vova Feldman (@svovaf) |
308
|
|
|
* @since 1.2.1.6 |
309
|
|
|
* |
310
|
|
|
* @param bool $is_on |
311
|
|
|
* |
312
|
|
|
* @return bool |
313
|
|
|
*/ |
314
|
|
|
public static function _set_storage_logging( $is_on = true ) { |
315
|
|
|
global $wpdb; |
|
|
|
|
316
|
|
|
|
317
|
|
|
$table = "{$wpdb->prefix}fs_logger"; |
318
|
|
|
|
319
|
|
|
if ( $is_on ) { |
320
|
|
|
/** |
321
|
|
|
* Create logging table. |
322
|
|
|
* |
323
|
|
|
* NOTE: |
324
|
|
|
* dbDelta must use KEY and not INDEX for indexes. |
325
|
|
|
* |
326
|
|
|
* @link https://core.trac.wordpress.org/ticket/2695 |
327
|
|
|
*/ |
328
|
|
|
$result = $wpdb->query( "CREATE TABLE {$table} ( |
329
|
|
|
`id` BIGINT(20) UNSIGNED NOT NULL AUTO_INCREMENT, |
330
|
|
|
`process_id` INT UNSIGNED NOT NULL, |
331
|
|
|
`user_name` VARCHAR(64) NOT NULL, |
332
|
|
|
`logger` VARCHAR(128) NOT NULL, |
333
|
|
|
`log_order` INT UNSIGNED NOT NULL, |
334
|
|
|
`type` ENUM('log','info','warn','error') NOT NULL DEFAULT 'log', |
335
|
|
|
`message` TEXT NOT NULL, |
336
|
|
|
`file` VARCHAR(256) NOT NULL, |
337
|
|
|
`line` INT UNSIGNED NOT NULL, |
338
|
|
|
`function` VARCHAR(256) NOT NULL, |
339
|
|
|
`request_type` ENUM('call','ajax','cron') NOT NULL DEFAULT 'call', |
340
|
|
|
`request_url` VARCHAR(1024) NOT NULL, |
341
|
|
|
`created` DECIMAL(16, 6) NOT NULL, |
342
|
|
|
PRIMARY KEY (`id`), |
343
|
|
|
KEY `process_id` (`process_id` ASC), |
344
|
|
|
KEY `process_logger` (`process_id` ASC, `logger` ASC), |
345
|
|
|
KEY `function` (`function` ASC), |
346
|
|
|
KEY `type` (`type` ASC))" ); |
347
|
|
|
} else { |
348
|
|
|
/** |
349
|
|
|
* Drop logging table. |
350
|
|
|
*/ |
351
|
|
|
$result = $wpdb->query( "DROP TABLE IF EXISTS $table;" ); |
352
|
|
|
} |
353
|
|
|
|
354
|
|
|
if ( false !== $result ) { |
355
|
|
|
update_option( 'fs_storage_logger', ( $is_on ? 1 : 0 ) ); |
356
|
|
|
} |
357
|
|
|
|
358
|
|
|
return ( false !== $result ); |
359
|
|
|
} |
360
|
|
|
|
361
|
|
|
/** |
362
|
|
|
* @author Vova Feldman (@svovaf) |
363
|
|
|
* @since 1.2.1.6 |
364
|
|
|
* |
365
|
|
|
* @param string $type |
366
|
|
|
* @param string $message |
367
|
|
|
* @param int $log_order |
368
|
|
|
* @param array $caller |
369
|
|
|
* |
370
|
|
|
* @return false|int |
371
|
|
|
*/ |
372
|
|
|
private function db_log( |
|
|
|
|
373
|
|
|
&$type, |
374
|
|
|
&$message, |
375
|
|
|
&$log_order, |
376
|
|
|
&$caller |
377
|
|
|
) { |
378
|
|
|
global $wpdb; |
|
|
|
|
379
|
|
|
|
380
|
|
|
$request_type = 'call'; |
381
|
|
|
if ( defined( 'DOING_CRON' ) && DOING_CRON ) { |
382
|
|
|
$request_type = 'cron'; |
383
|
|
|
} else if ( defined( 'DOING_AJAX' ) && DOING_AJAX ) { |
384
|
|
|
$request_type = 'ajax'; |
385
|
|
|
} |
386
|
|
|
|
387
|
|
|
$request_url = WP_FS__IS_HTTP_REQUEST ? |
388
|
|
|
$_SERVER['REQUEST_URI'] : |
389
|
|
|
''; |
390
|
|
|
|
391
|
|
|
return $wpdb->insert( |
392
|
|
|
"{$wpdb->prefix}fs_logger", |
393
|
|
|
array( |
394
|
|
|
'process_id' => self::$_processID, |
395
|
|
|
'user_name' => self::$_ownerName, |
396
|
|
|
'logger' => $this->_id, |
397
|
|
|
'log_order' => $log_order, |
398
|
|
|
'type' => $type, |
399
|
|
|
'request_type' => $request_type, |
400
|
|
|
'request_url' => $request_url, |
401
|
|
|
'message' => $message, |
402
|
|
|
'file' => isset( $caller['file'] ) ? |
403
|
|
|
substr( $caller['file'], self::$_abspathLength ) : |
404
|
|
|
'', |
405
|
|
|
'line' => $caller['line'], |
406
|
|
|
'function' => ( ! empty( $caller['class'] ) ? $caller['class'] . $caller['type'] : '' ) . $caller['function'], |
407
|
|
|
'created' => microtime( true ), |
408
|
|
|
) |
409
|
|
|
); |
410
|
|
|
} |
411
|
|
|
|
412
|
|
|
/** |
413
|
|
|
* Persistent DB logger columns. |
414
|
|
|
* |
415
|
|
|
* @var array |
416
|
|
|
*/ |
417
|
|
|
private static $_log_columns = array( |
418
|
|
|
'id', |
419
|
|
|
'process_id', |
420
|
|
|
'user_name', |
421
|
|
|
'logger', |
422
|
|
|
'log_order', |
423
|
|
|
'type', |
424
|
|
|
'message', |
425
|
|
|
'file', |
426
|
|
|
'line', |
427
|
|
|
'function', |
428
|
|
|
'request_type', |
429
|
|
|
'request_url', |
430
|
|
|
'created', |
431
|
|
|
); |
432
|
|
|
|
433
|
|
|
/** |
434
|
|
|
* Create DB logs query. |
435
|
|
|
* |
436
|
|
|
* @author Vova Feldman (@svovaf) |
437
|
|
|
* @since 1.2.1.6 |
438
|
|
|
* |
439
|
|
|
* @param bool $filters |
440
|
|
|
* @param int $limit |
441
|
|
|
* @param int $offset |
442
|
|
|
* @param bool $order |
443
|
|
|
* @param bool $escape_eol |
444
|
|
|
* |
445
|
|
|
* @return string |
446
|
|
|
*/ |
447
|
|
|
private static function build_db_logs_query( |
448
|
|
|
$filters = false, |
449
|
|
|
$limit = 200, |
450
|
|
|
$offset = 0, |
451
|
|
|
$order = false, |
452
|
|
|
$escape_eol = false |
453
|
|
|
) { |
454
|
|
|
global $wpdb; |
|
|
|
|
455
|
|
|
|
456
|
|
|
$select = '*'; |
457
|
|
|
|
458
|
|
|
if ( $escape_eol ) { |
459
|
|
|
$select = ''; |
460
|
|
|
for ( $i = 0, $len = count( self::$_log_columns ); $i < $len; $i ++ ) { |
461
|
|
|
if ( $i > 0 ) { |
462
|
|
|
$select .= ', '; |
463
|
|
|
} |
464
|
|
|
|
465
|
|
|
if ( 'message' !== self::$_log_columns[ $i ] ) { |
466
|
|
|
$select .= self::$_log_columns[ $i ]; |
467
|
|
|
} else { |
468
|
|
|
$select .= 'REPLACE(message , \'\n\', \' \') AS message'; |
469
|
|
|
} |
470
|
|
|
} |
471
|
|
|
} |
472
|
|
|
|
473
|
|
|
$query = "SELECT {$select} FROM {$wpdb->prefix}fs_logger"; |
474
|
|
|
if ( is_array( $filters ) ) { |
475
|
|
|
$criteria = array(); |
476
|
|
|
|
477
|
|
|
if ( ! empty( $filters['type'] ) && 'all' !== $filters['type'] ) { |
478
|
|
|
$filters['type'] = strtolower( $filters['type'] ); |
479
|
|
|
|
480
|
|
|
switch ( $filters['type'] ) { |
481
|
|
|
case 'warn_error': |
482
|
|
|
$criteria[] = array( 'col' => 'type', 'val' => array( 'warn', 'error' ) ); |
483
|
|
|
break; |
484
|
|
|
case 'error': |
485
|
|
|
case 'warn': |
486
|
|
|
$criteria[] = array( 'col' => 'type', 'val' => $filters['type'] ); |
487
|
|
|
break; |
488
|
|
|
case 'info': |
489
|
|
|
default: |
490
|
|
|
$criteria[] = array( 'col' => 'type', 'val' => array( 'info', 'log' ) ); |
491
|
|
|
break; |
492
|
|
|
} |
493
|
|
|
} |
494
|
|
|
|
495
|
|
|
if ( ! empty( $filters['request_type'] ) ) { |
496
|
|
|
$filters['request_type'] = strtolower( $filters['request_type'] ); |
497
|
|
|
|
498
|
|
|
if ( in_array( $filters['request_type'], array( 'call', 'ajax', 'cron' ) ) ) { |
499
|
|
|
$criteria[] = array( 'col' => 'request_type', 'val' => $filters['request_type'] ); |
500
|
|
|
} |
501
|
|
|
} |
502
|
|
|
|
503
|
|
|
if ( ! empty( $filters['file'] ) ) { |
504
|
|
|
$criteria[] = array( |
505
|
|
|
'col' => 'file', |
506
|
|
|
'op' => 'LIKE', |
507
|
|
|
'val' => '%' . esc_sql( $filters['file'] ), |
508
|
|
|
); |
509
|
|
|
} |
510
|
|
|
|
511
|
|
|
if ( ! empty( $filters['function'] ) ) { |
512
|
|
|
$criteria[] = array( |
513
|
|
|
'col' => 'function', |
514
|
|
|
'op' => 'LIKE', |
515
|
|
|
'val' => '%' . esc_sql( $filters['function'] ), |
516
|
|
|
); |
517
|
|
|
} |
518
|
|
|
|
519
|
|
|
if ( ! empty( $filters['process_id'] ) && is_numeric( $filters['process_id'] ) ) { |
520
|
|
|
$criteria[] = array( 'col' => 'process_id', 'val' => $filters['process_id'] ); |
521
|
|
|
} |
522
|
|
|
|
523
|
|
|
if ( ! empty( $filters['logger'] ) ) { |
524
|
|
|
$criteria[] = array( |
525
|
|
|
'col' => 'logger', |
526
|
|
|
'op' => 'LIKE', |
527
|
|
|
'val' => '%' . esc_sql( $filters['logger'] ) . '%', |
528
|
|
|
); |
529
|
|
|
} |
530
|
|
|
|
531
|
|
|
if ( ! empty( $filters['message'] ) ) { |
532
|
|
|
$criteria[] = array( |
533
|
|
|
'col' => 'message', |
534
|
|
|
'op' => 'LIKE', |
535
|
|
|
'val' => '%' . esc_sql( $filters['message'] ) . '%', |
536
|
|
|
); |
537
|
|
|
} |
538
|
|
|
|
539
|
|
|
if ( 0 < count( $criteria ) ) { |
540
|
|
|
$query .= "\nWHERE\n"; |
541
|
|
|
|
542
|
|
|
$first = true; |
543
|
|
|
foreach ( $criteria as $c ) { |
544
|
|
|
if ( ! $first ) { |
545
|
|
|
$query .= "AND\n"; |
546
|
|
|
} |
547
|
|
|
|
548
|
|
|
if ( is_array( $c['val'] ) ) { |
549
|
|
|
$operator = 'IN'; |
550
|
|
|
|
551
|
|
|
for ( $i = 0, $len = count( $c['val'] ); $i < $len; $i ++ ) { |
552
|
|
|
$c['val'][ $i ] = "'" . esc_sql( $c['val'][ $i ] ) . "'"; |
553
|
|
|
} |
554
|
|
|
|
555
|
|
|
$val = '(' . implode( ',', $c['val'] ) . ')'; |
556
|
|
|
} else { |
557
|
|
|
$operator = ! empty( $c['op'] ) ? $c['op'] : '='; |
558
|
|
|
$val = "'" . esc_sql( $c['val'] ) . "'"; |
559
|
|
|
} |
560
|
|
|
|
561
|
|
|
$query .= "`{$c['col']}` {$operator} {$val}\n"; |
562
|
|
|
|
563
|
|
|
$first = false; |
564
|
|
|
} |
565
|
|
|
} |
566
|
|
|
} |
567
|
|
|
|
568
|
|
|
if ( ! is_array( $order ) ) { |
569
|
|
|
$order = array( |
570
|
|
|
'col' => 'id', |
571
|
|
|
'order' => 'desc' |
572
|
|
|
); |
573
|
|
|
} |
574
|
|
|
|
575
|
|
|
$query .= " ORDER BY {$order['col']} {$order['order']} LIMIT {$offset},{$limit}"; |
576
|
|
|
|
577
|
|
|
return $query; |
578
|
|
|
} |
579
|
|
|
|
580
|
|
|
/** |
581
|
|
|
* Load logs from DB. |
582
|
|
|
* |
583
|
|
|
* @author Vova Feldman (@svovaf) |
584
|
|
|
* @since 1.2.1.6 |
585
|
|
|
* |
586
|
|
|
* @param bool $filters |
587
|
|
|
* @param int $limit |
588
|
|
|
* @param int $offset |
589
|
|
|
* @param bool $order |
590
|
|
|
* |
591
|
|
|
* @return object[]|null |
592
|
|
|
*/ |
593
|
|
|
public static function load_db_logs( |
594
|
|
|
$filters = false, |
595
|
|
|
$limit = 200, |
596
|
|
|
$offset = 0, |
597
|
|
|
$order = false |
598
|
|
|
) { |
599
|
|
|
global $wpdb; |
|
|
|
|
600
|
|
|
|
601
|
|
|
$query = self::build_db_logs_query( |
602
|
|
|
$filters, |
603
|
|
|
$limit, |
604
|
|
|
$offset, |
605
|
|
|
$order |
606
|
|
|
); |
607
|
|
|
|
608
|
|
|
return $wpdb->get_results( $query ); |
609
|
|
|
} |
610
|
|
|
|
611
|
|
|
/** |
612
|
|
|
* Load logs from DB. |
613
|
|
|
* |
614
|
|
|
* @author Vova Feldman (@svovaf) |
615
|
|
|
* @since 1.2.1.6 |
616
|
|
|
* |
617
|
|
|
* @param bool $filters |
618
|
|
|
* @param string $filename |
619
|
|
|
* @param int $limit |
620
|
|
|
* @param int $offset |
621
|
|
|
* @param bool $order |
622
|
|
|
* |
623
|
|
|
* @return false|string File download URL or false on failure. |
624
|
|
|
*/ |
625
|
|
|
public static function download_db_logs( |
626
|
|
|
$filters = false, |
627
|
|
|
$filename = '', |
628
|
|
|
$limit = 10000, |
629
|
|
|
$offset = 0, |
630
|
|
|
$order = false |
631
|
|
|
) { |
632
|
|
|
global $wpdb; |
|
|
|
|
633
|
|
|
|
634
|
|
|
$query = self::build_db_logs_query( |
635
|
|
|
$filters, |
636
|
|
|
$limit, |
637
|
|
|
$offset, |
638
|
|
|
$order, |
639
|
|
|
true |
640
|
|
|
); |
641
|
|
|
|
642
|
|
|
$upload_dir = wp_upload_dir(); |
643
|
|
|
if ( empty( $filename ) ) { |
644
|
|
|
$filename = 'fs-logs-' . date( 'Y-m-d_H-i-s', WP_FS__SCRIPT_START_TIME ) . '.csv'; |
645
|
|
|
} |
646
|
|
|
$filepath = rtrim( $upload_dir['path'], '/' ) . "/{$filename}"; |
647
|
|
|
|
648
|
|
|
$query .= " INTO OUTFILE '{$filepath}' FIELDS TERMINATED BY '\t' ESCAPED BY '\\\\' OPTIONALLY ENCLOSED BY '\"' LINES TERMINATED BY '\\n'"; |
649
|
|
|
|
650
|
|
|
$columns = ''; |
651
|
|
|
for ( $i = 0, $len = count( self::$_log_columns ); $i < $len; $i ++ ) { |
652
|
|
|
if ( $i > 0 ) { |
653
|
|
|
$columns .= ', '; |
654
|
|
|
} |
655
|
|
|
|
656
|
|
|
$columns .= "'" . self::$_log_columns[ $i ] . "'"; |
657
|
|
|
} |
658
|
|
|
|
659
|
|
|
$query = "SELECT {$columns} UNION ALL " . $query; |
660
|
|
|
|
661
|
|
|
$result = $wpdb->query( $query ); |
662
|
|
|
|
663
|
|
|
if ( false === $result ) { |
664
|
|
|
return false; |
665
|
|
|
} |
666
|
|
|
|
667
|
|
|
return rtrim( $upload_dir['url'], '/' ) . '/' . $filename; |
668
|
|
|
} |
669
|
|
|
|
670
|
|
|
/** |
671
|
|
|
* @author Vova Feldman (@svovaf) |
672
|
|
|
* @since 1.2.1.6 |
673
|
|
|
* |
674
|
|
|
* @param string $filename |
675
|
|
|
* |
676
|
|
|
* @return string |
677
|
|
|
*/ |
678
|
|
|
public static function get_logs_download_url( $filename = '' ) { |
679
|
|
|
$upload_dir = wp_upload_dir(); |
680
|
|
|
if ( empty( $filename ) ) { |
681
|
|
|
$filename = 'fs-logs-' . date( 'Y-m-d_H-i-s', WP_FS__SCRIPT_START_TIME ) . '.csv'; |
682
|
|
|
} |
683
|
|
|
|
684
|
|
|
return rtrim( $upload_dir['url'], '/' ) . $filename; |
685
|
|
|
} |
686
|
|
|
|
687
|
|
|
#endregion |
688
|
|
|
} |
The PSR-1: Basic Coding Standard recommends that a file should either introduce new symbols, that is classes, functions, constants or similar, or have side effects. Side effects are anything that executes logic, like for example printing output, changing ini settings or writing to a file.
The idea behind this recommendation is that merely auto-loading a class should not change the state of an application. It also promotes a cleaner style of programming and makes your code less prone to errors, because the logic is not spread out all over the place.
To learn more about the PSR-1, please see the PHP-FIG site on the PSR-1.