PluginProbe ʕ •ᴥ•ʔ
EmbedPress – PDF Embedder, Embed PDF viewer, YouTube Videos, 3D FlipBook, Social feeds & more / 1.7.1
EmbedPress – PDF Embedder, Embed PDF viewer, YouTube Videos, 3D FlipBook, Social feeds & more v1.7.1
4.5.6 4.5.5 4.5.4 4.5.3 4.5.2 trunk 1.0.0 1.1.0 1.1.1 1.1.2 1.1.3 1.2.0 1.3.0 1.3.1 1.4.0 1.4.1 1.4.2 1.4.3 1.4.4 1.5.0 1.6.0 1.6.1 1.6.2 1.6.3 1.7.0 1.7.1 1.7.2 1.7.3 1.7.4 1.7.5 2.0.0 2.0.1 2.0.2 2.0.3 2.1.0 2.1.1 2.1.2 2.1.3 2.1.4 2.1.5 2.1.6 2.2.0 2.2.1 2.2.2 2.3.0 2.3.1 2.3.2 2.3.3 2.4.0 2.4.1 2.5.0 2.5.1 2.5.2 2.5.3 2.5.4 2.5.5 2.6.0 2.6.1 2.6.2 2.7.0 2.7.1 2.7.2 2.7.3 2.7.4 2.7.5 2.7.6 2.7.7 3.0.0 3.0.1 3.0.2 3.0.3 3.0.4 3.1.0 3.1.1 3.1.2 3.1.3 3.2.0 3.2.1 3.3.0 3.3.1 3.3.2 3.3.3 3.3.4 3.3.5 3.3.6 3.3.7 3.4.0 3.4.1 3.4.2 3.4.3 3.5.0 3.5.1 3.5.2 3.5.3 3.6.0 3.6.1 3.6.2 3.6.3 3.6.4 3.6.5 3.6.6 3.6.7 3.6.8 3.7.0 3.7.1 3.7.2 3.7.3 3.8.0 3.8.1 3.8.2 3.8.3 3.8.4 3.8.5 3.9.0 3.9.1 3.9.10 3.9.11 3.9.12 3.9.13 3.9.14 3.9.15 3.9.16 3.9.17 3.9.2 3.9.3 3.9.4 3.9.5 3.9.6 3.9.7 3.9.8 3.9.9 4.0.0 4.0.1 4.0.10 4.0.11 4.0.12 4.0.13 4.0.14 4.0.2 4.0.3 4.0.4 4.0.5 4.0.6 4.0.7 4.0.8 4.0.9 4.1.0 4.1.1 4.1.10 4.1.2 4.1.3 4.1.4 4.1.5 4.1.6 4.1.7 4.1.8 4.1.9 4.2.0 4.2.1 4.2.2 4.2.3 4.2.4 4.2.5 4.2.6 4.2.7 4.2.8 4.2.9 4.3.0 4.3.1 4.4.0 4.4.1 4.4.10 4.4.11 4.4.2 4.4.3 4.4.4 4.4.5 4.4.6 4.4.7 4.4.8 4.4.9 4.5.0 4.5.1
embedpress / freemius / includes / class-fs-logger.php
embedpress / freemius / includes Last commit date
debug 9 years ago entities 9 years ago managers 9 years ago sdk 9 years ago supplements 9 years ago class-freemius-abstract.php 9 years ago class-freemius.php 9 years ago class-fs-api.php 9 years ago class-fs-logger.php 9 years ago class-fs-plugin-updater.php 9 years ago class-fs-security.php 9 years ago fs-core-functions.php 9 years ago fs-essential-functions.php 9 years ago fs-plugin-info-dialog.php 9 years ago i18n.php 9 years ago index.php 9 years ago l10n.php 9 years ago
class-fs-logger.php
686 lines
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 = get_current_user();
89 self::$_isStorageLoggingOn = ( 1 == get_option( 'fs_storage_logger', 0 ) );
90 self::$_abspathLength = strlen( ABSPATH );
91 self::$_processID = mt_rand(0, 32000);
92
93 // Process ID may be `false` on errors.
94 if ( ! is_numeric( self::$_processID ) ) {
95 self::$_processID = 0;
96 }
97 }
98
99 private static function hook_footer() {
100 if ( self::$_HOOKED_FOOTER ) {
101 return;
102 }
103
104 if ( is_admin() ) {
105 add_action( 'admin_footer', 'FS_Logger::dump', 100 );
106 } else {
107 add_action( 'wp_footer', 'FS_Logger::dump', 100 );
108 }
109 }
110
111 function is_on() {
112 return $this->_on;
113 }
114
115 function on() {
116 $this->_on = true;
117
118 if ( ! function_exists( 'dbDelta' ) ) {
119 require_once ABSPATH . 'wp-admin/includes/upgrade.php';
120 }
121
122 self::hook_footer();
123 }
124
125 function echo_on() {
126 $this->on();
127
128 $this->_echo = true;
129 }
130
131 function is_echo_on() {
132 return $this->_echo;
133 }
134
135 function get_id() {
136 return $this->_id;
137 }
138
139 function get_file() {
140 return $this->_file_start;
141 }
142
143 private function _log( &$message, $type = 'log', $wrapper ) {
144 if ( ! $this->is_on() ) {
145 return;
146 }
147
148 $bt = debug_backtrace();
149 $depth = $wrapper ? 3 : 2;
150 while ( $depth < count( $bt ) - 1 && 'eval' === $bt[ $depth ]['function'] ) {
151 $depth ++;
152 }
153
154 $caller = $bt[ $depth ];
155
156 /**
157 * Retrieve the correct call file & line number from backtrace
158 * when logging from a wrapper method.
159 *
160 * @author Vova Feldman
161 * @since 1.2.1.6
162 */
163 if ( empty( $caller['line'] ) ) {
164 $depth --;
165
166 while ( $depth >= 0 ) {
167 if ( ! empty( $bt[ $depth ]['line'] ) ) {
168 $caller['line'] = $bt[ $depth ]['line'];
169 $caller['file'] = $bt[ $depth ]['file'];
170 break;
171 }
172 }
173 }
174
175 $log = array_merge( $caller, array(
176 'cnt' => self::$CNT ++,
177 'logger' => $this,
178 'timestamp' => microtime( true ),
179 'log_type' => $type,
180 'msg' => $message,
181 ) );
182
183 if ( self::$_isStorageLoggingOn ) {
184 $this->db_log( $type, $message, self::$CNT, $caller );
185 }
186
187 self::$LOG[] = $log;
188
189 if ( $this->is_echo_on() ) {
190 echo self::format_html( $log ) . "\n";
191 }
192 }
193
194 function log( $message, $wrapper = false ) {
195 $this->_log( $message, 'log', $wrapper );
196 }
197
198 function info( $message, $wrapper = false ) {
199 $this->_log( $message, 'info', $wrapper );
200 }
201
202 function warn( $message, $wrapper = false ) {
203 $this->_log( $message, 'warn', $wrapper );
204 }
205
206 function error( $message, $wrapper = false ) {
207 $this->_log( $message, 'error', $wrapper );
208 }
209
210 /**
211 * Log API error.
212 *
213 * @author Vova Feldman (@svovaf)
214 * @since 1.2.1.5
215 *
216 * @param mixed $api_result
217 * @param bool $wrapper
218 */
219 function api_error( $api_result, $wrapper = false ) {
220 $message = '';
221 if ( is_object( $api_result ) && isset( $api_result->error ) ) {
222 $message = $api_result->error->message;
223 } else if ( is_object( $api_result ) ) {
224 $message = var_export( $api_result, true );
225 } else if ( is_string( $api_result ) ) {
226 $message = $api_result;
227 } else if ( empty( $api_result ) ) {
228 $message = 'Empty API result.';
229 }
230
231 $message = 'API Error: ' . $message;
232
233 $this->_log( $message, 'error', $wrapper );
234 }
235
236 function entrance( $message = '', $wrapper = false ) {
237 $msg = 'Entrance' . ( empty( $message ) ? '' : ' > ' ) . $message;
238
239 $this->_log( $msg, 'log', $wrapper );
240 }
241
242 function departure( $message = '', $wrapper = false ) {
243 $msg = 'Departure' . ( empty( $message ) ? '' : ' > ' ) . $message;
244
245 $this->_log( $msg, 'log', $wrapper );
246 }
247
248 #--------------------------------------------------------------------------------
249 #region Log Formatting
250 #--------------------------------------------------------------------------------
251
252 private static function format( $log, $show_type = true ) {
253 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'] . ']';
254 }
255
256 private static function format_html( $log ) {
257 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>';
258 }
259
260 #endregion
261
262 static function dump() {
263 ?>
264 <!-- BEGIN: Freemius PHP Console Log -->
265 <script type="text/javascript">
266 <?php
267 foreach ( self::$LOG as $log ) {
268 echo 'console.' . $log['log_type'] . '(' . json_encode( self::format( $log, false ) ) . ')' . "\n";
269 }
270 ?>
271 </script>
272 <!-- END: Freemius PHP Console Log -->
273 <?php
274 }
275
276 static function get_log() {
277 return self::$LOG;
278 }
279
280 #--------------------------------------------------------------------------------
281 #region Database Logging
282 #--------------------------------------------------------------------------------
283
284 /**
285 * @author Vova Feldman (@svovaf)
286 * @since 1.2.1.6
287 *
288 * @return bool
289 */
290 public static function is_storage_logging_on() {
291 if ( ! isset( self::$_isStorageLoggingOn ) ) {
292 self::$_isStorageLoggingOn = ( 1 == get_option( 'fs_storage_logger', 0 ) );
293 }
294
295 return self::$_isStorageLoggingOn;
296 }
297
298 /**
299 * Turns on/off database persistent debugging to capture
300 * multi-session logs to debug complex flows like
301 * plugin auto-deactivate on premium version activation.
302 *
303 * @todo Check if Theme Check has issues with DB tables for themes.
304 *
305 * @author Vova Feldman (@svovaf)
306 * @since 1.2.1.6
307 *
308 * @param bool $is_on
309 *
310 * @return bool
311 */
312 public static function _set_storage_logging( $is_on = true ) {
313 global $wpdb;
314
315 $table = "{$wpdb->prefix}fs_logger";
316
317 if ( $is_on ) {
318 /**
319 * Create logging table.
320 *
321 * NOTE:
322 * dbDelta must use KEY and not INDEX for indexes.
323 *
324 * @link https://core.trac.wordpress.org/ticket/2695
325 */
326 $result = $wpdb->query( "CREATE TABLE {$table} (
327 `id` BIGINT(20) UNSIGNED NOT NULL AUTO_INCREMENT,
328 `process_id` INT UNSIGNED NOT NULL,
329 `user_name` VARCHAR(64) NOT NULL,
330 `logger` VARCHAR(128) NOT NULL,
331 `log_order` INT UNSIGNED NOT NULL,
332 `type` ENUM('log','info','warn','error') NOT NULL DEFAULT 'log',
333 `message` TEXT NOT NULL,
334 `file` VARCHAR(256) NOT NULL,
335 `line` INT UNSIGNED NOT NULL,
336 `function` VARCHAR(256) NOT NULL,
337 `request_type` ENUM('call','ajax','cron') NOT NULL DEFAULT 'call',
338 `request_url` VARCHAR(1024) NOT NULL,
339 `created` DECIMAL(16, 6) NOT NULL,
340 PRIMARY KEY (`id`),
341 KEY `process_id` (`process_id` ASC),
342 KEY `process_logger` (`process_id` ASC, `logger` ASC),
343 KEY `function` (`function` ASC),
344 KEY `type` (`type` ASC))" );
345 } else {
346 /**
347 * Drop logging table.
348 */
349 $result = $wpdb->query( "DROP TABLE IF EXISTS $table;" );
350 }
351
352 if ( false !== $result ) {
353 update_option( 'fs_storage_logger', ( $is_on ? 1 : 0 ) );
354 }
355
356 return ( false !== $result );
357 }
358
359 /**
360 * @author Vova Feldman (@svovaf)
361 * @since 1.2.1.6
362 *
363 * @param string $type
364 * @param string $message
365 * @param int $log_order
366 * @param array $caller
367 *
368 * @return false|int
369 */
370 private function db_log(
371 &$type,
372 &$message,
373 &$log_order,
374 &$caller
375 ) {
376 global $wpdb;
377
378 $request_type = 'call';
379 if ( defined( 'DOING_CRON' ) && DOING_CRON ) {
380 $request_type = 'cron';
381 } else if ( defined( 'DOING_AJAX' ) && DOING_AJAX ) {
382 $request_type = 'ajax';
383 }
384
385 $request_url = WP_FS__IS_HTTP_REQUEST ?
386 $_SERVER['REQUEST_URI'] :
387 '';
388
389 return $wpdb->insert(
390 "{$wpdb->prefix}fs_logger",
391 array(
392 'process_id' => self::$_processID,
393 'user_name' => self::$_ownerName,
394 'logger' => $this->_id,
395 'log_order' => $log_order,
396 'type' => $type,
397 'request_type' => $request_type,
398 'request_url' => $request_url,
399 'message' => $message,
400 'file' => isset( $caller['file'] ) ?
401 substr( $caller['file'], self::$_abspathLength ) :
402 '',
403 'line' => $caller['line'],
404 'function' => ( ! empty( $caller['class'] ) ? $caller['class'] . $caller['type'] : '' ) . $caller['function'],
405 'created' => microtime( true ),
406 )
407 );
408 }
409
410 /**
411 * Persistent DB logger columns.
412 *
413 * @var array
414 */
415 private static $_log_columns = array(
416 'id',
417 'process_id',
418 'user_name',
419 'logger',
420 'log_order',
421 'type',
422 'message',
423 'file',
424 'line',
425 'function',
426 'request_type',
427 'request_url',
428 'created',
429 );
430
431 /**
432 * Create DB logs query.
433 *
434 * @author Vova Feldman (@svovaf)
435 * @since 1.2.1.6
436 *
437 * @param bool $filters
438 * @param int $limit
439 * @param int $offset
440 * @param bool $order
441 * @param bool $escape_eol
442 *
443 * @return string
444 */
445 private static function build_db_logs_query(
446 $filters = false,
447 $limit = 200,
448 $offset = 0,
449 $order = false,
450 $escape_eol = false
451 ) {
452 global $wpdb;
453
454 $select = '*';
455
456 if ( $escape_eol ) {
457 $select = '';
458 for ( $i = 0, $len = count( self::$_log_columns ); $i < $len; $i ++ ) {
459 if ( $i > 0 ) {
460 $select .= ', ';
461 }
462
463 if ( 'message' !== self::$_log_columns[ $i ] ) {
464 $select .= self::$_log_columns[ $i ];
465 } else {
466 $select .= 'REPLACE(message , \'\n\', \' \') AS message';
467 }
468 }
469 }
470
471 $query = "SELECT {$select} FROM {$wpdb->prefix}fs_logger";
472 if ( is_array( $filters ) ) {
473 $criteria = array();
474
475 if ( ! empty( $filters['type'] ) && 'all' !== $filters['type'] ) {
476 $filters['type'] = strtolower( $filters['type'] );
477
478 switch ( $filters['type'] ) {
479 case 'warn_error':
480 $criteria[] = array( 'col' => 'type', 'val' => array( 'warn', 'error' ) );
481 break;
482 case 'error':
483 case 'warn':
484 $criteria[] = array( 'col' => 'type', 'val' => $filters['type'] );
485 break;
486 case 'info':
487 default:
488 $criteria[] = array( 'col' => 'type', 'val' => array( 'info', 'log' ) );
489 break;
490 }
491 }
492
493 if ( ! empty( $filters['request_type'] ) ) {
494 $filters['request_type'] = strtolower( $filters['request_type'] );
495
496 if ( in_array( $filters['request_type'], array( 'call', 'ajax', 'cron' ) ) ) {
497 $criteria[] = array( 'col' => 'request_type', 'val' => $filters['request_type'] );
498 }
499 }
500
501 if ( ! empty( $filters['file'] ) ) {
502 $criteria[] = array(
503 'col' => 'file',
504 'op' => 'LIKE',
505 'val' => '%' . esc_sql( $filters['file'] ),
506 );
507 }
508
509 if ( ! empty( $filters['function'] ) ) {
510 $criteria[] = array(
511 'col' => 'function',
512 'op' => 'LIKE',
513 'val' => '%' . esc_sql( $filters['function'] ),
514 );
515 }
516
517 if ( ! empty( $filters['process_id'] ) && is_numeric( $filters['process_id'] ) ) {
518 $criteria[] = array( 'col' => 'process_id', 'val' => $filters['process_id'] );
519 }
520
521 if ( ! empty( $filters['logger'] ) ) {
522 $criteria[] = array(
523 'col' => 'logger',
524 'op' => 'LIKE',
525 'val' => '%' . esc_sql( $filters['logger'] ) . '%',
526 );
527 }
528
529 if ( ! empty( $filters['message'] ) ) {
530 $criteria[] = array(
531 'col' => 'message',
532 'op' => 'LIKE',
533 'val' => '%' . esc_sql( $filters['message'] ) . '%',
534 );
535 }
536
537 if ( 0 < count( $criteria ) ) {
538 $query .= "\nWHERE\n";
539
540 $first = true;
541 foreach ( $criteria as $c ) {
542 if ( ! $first ) {
543 $query .= "AND\n";
544 }
545
546 if ( is_array( $c['val'] ) ) {
547 $operator = 'IN';
548
549 for ( $i = 0, $len = count( $c['val'] ); $i < $len; $i ++ ) {
550 $c['val'][ $i ] = "'" . esc_sql( $c['val'][ $i ] ) . "'";
551 }
552
553 $val = '(' . implode( ',', $c['val'] ) . ')';
554 } else {
555 $operator = ! empty( $c['op'] ) ? $c['op'] : '=';
556 $val = "'" . esc_sql( $c['val'] ) . "'";
557 }
558
559 $query .= "`{$c['col']}` {$operator} {$val}\n";
560
561 $first = false;
562 }
563 }
564 }
565
566 if ( ! is_array( $order ) ) {
567 $order = array(
568 'col' => 'id',
569 'order' => 'desc'
570 );
571 }
572
573 $query .= " ORDER BY {$order['col']} {$order['order']} LIMIT {$offset},{$limit}";
574
575 return $query;
576 }
577
578 /**
579 * Load logs from DB.
580 *
581 * @author Vova Feldman (@svovaf)
582 * @since 1.2.1.6
583 *
584 * @param bool $filters
585 * @param int $limit
586 * @param int $offset
587 * @param bool $order
588 *
589 * @return object[]|null
590 */
591 public static function load_db_logs(
592 $filters = false,
593 $limit = 200,
594 $offset = 0,
595 $order = false
596 ) {
597 global $wpdb;
598
599 $query = self::build_db_logs_query(
600 $filters,
601 $limit,
602 $offset,
603 $order
604 );
605
606 return $wpdb->get_results( $query );
607 }
608
609 /**
610 * Load logs from DB.
611 *
612 * @author Vova Feldman (@svovaf)
613 * @since 1.2.1.6
614 *
615 * @param bool $filters
616 * @param string $filename
617 * @param int $limit
618 * @param int $offset
619 * @param bool $order
620 *
621 * @return false|string File download URL or false on failure.
622 */
623 public static function download_db_logs(
624 $filters = false,
625 $filename = '',
626 $limit = 10000,
627 $offset = 0,
628 $order = false
629 ) {
630 global $wpdb;
631
632 $query = self::build_db_logs_query(
633 $filters,
634 $limit,
635 $offset,
636 $order,
637 true
638 );
639
640 $upload_dir = wp_upload_dir();
641 if ( empty( $filename ) ) {
642 $filename = 'fs-logs-' . date( 'Y-m-d_H-i-s', WP_FS__SCRIPT_START_TIME ) . '.csv';
643 }
644 $filepath = rtrim( $upload_dir['path'], '/' ) . "/{$filename}";
645
646 $query .= " INTO OUTFILE '{$filepath}' FIELDS TERMINATED BY '\t' ESCAPED BY '\\\\' OPTIONALLY ENCLOSED BY '\"' LINES TERMINATED BY '\\n'";
647
648 $columns = '';
649 for ( $i = 0, $len = count( self::$_log_columns ); $i < $len; $i ++ ) {
650 if ( $i > 0 ) {
651 $columns .= ', ';
652 }
653
654 $columns .= "'" . self::$_log_columns[ $i ] . "'";
655 }
656
657 $query = "SELECT {$columns} UNION ALL " . $query;
658
659 $result = $wpdb->query( $query );
660
661 if ( false === $result ) {
662 return false;
663 }
664
665 return rtrim( $upload_dir['url'], '/' ) . '/' . $filename;
666 }
667
668 /**
669 * @author Vova Feldman (@svovaf)
670 * @since 1.2.1.6
671 *
672 * @param string $filename
673 *
674 * @return string
675 */
676 public static function get_logs_download_url( $filename = '' ) {
677 $upload_dir = wp_upload_dir();
678 if ( empty( $filename ) ) {
679 $filename = 'fs-logs-' . date( 'Y-m-d_H-i-s', WP_FS__SCRIPT_START_TIME ) . '.csv';
680 }
681
682 return rtrim( $upload_dir['url'], '/' ) . $filename;
683 }
684
685 #endregion
686 }