GitHub Access Token became invalid

It seems like the GitHub access token used for retrieving details about this repository from GitHub became invalid. This might prevent certain types of inspections from being run (in particular, everything related to pull requests).
Please ask an admin of your repository to re-new the access token on this website.
Completed
Push — master ( 74a577...62d274 )
by Brad
02:30
created

FS_Logger::warn()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 3
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 1
eloc 2
nc 1
nop 2
dl 0
loc 3
rs 10
c 0
b 0
f 0
1
<?php
0 ignored issues
show
Coding Style Compatibility introduced by
For compatibility and reusability of your code, PSR1 recommends that a file should introduce either new symbols (like classes, functions, etc.) or have side-effects (like outputting something, or including other files), but not both at the same time. The first symbol is defined on line 13 and the first side effect is on line 10.

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.

Loading history...
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 {
0 ignored issues
show
Coding Style introduced by
Since you have declared the constructor as private, maybe you should also declare the class as final.
Loading history...
Coding Style Compatibility introduced by
PSR1 recommends that each class must be in a namespace of at least one level to avoid collisions.

You can fix this by adding a namespace to your class:

namespace YourVendor;

class YourClass { }

When choosing a vendor namespace, try to pick something that is not too generic to avoid conflicts with other libraries.

Loading history...
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() {
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
114
			return $this->_on;
115
		}
116
117
		function on() {
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
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() {
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
128
			$this->on();
129
130
			$this->_echo = true;
131
		}
132
133
		function is_echo_on() {
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
134
			return $this->_echo;
135
		}
136
137
		function get_id() {
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
138
			return $this->_id;
139
		}
140
141
		function get_file() {
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
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 ) {
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
197
			$this->_log( $message, 'log', $wrapper );
198
		}
199
200
		function info( $message, $wrapper = false ) {
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
201
			$this->_log( $message, 'info', $wrapper );
202
		}
203
204
		function warn( $message, $wrapper = false ) {
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
205
			$this->_log( $message, 'warn', $wrapper );
206
		}
207
208
		function error( $message, $wrapper = false ) {
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
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 ) {
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
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 ) {
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
239
			$msg = 'Entrance' . ( empty( $message ) ? '' : ' > ' ) . $message;
240
241
			$this->_log( $msg, 'log', $wrapper );
242
		}
243
244
		function departure( $message = '', $wrapper = false ) {
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
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() {
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
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() {
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
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;
0 ignored issues
show
Compatibility Best Practice introduced by
Use of global functionality is not recommended; it makes your code harder to test, and less reusable.

Instead of relying on global state, we recommend one of these alternatives:

1. Pass all data via parameters

function myFunction($a, $b) {
    // Do something
}

2. Create a class that maintains your state

class MyClass {
    private $a;
    private $b;

    public function __construct($a, $b) {
        $this->a = $a;
        $this->b = $b;
    }

    public function myFunction() {
        // Do something
    }
}
Loading history...
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(
0 ignored issues
show
Coding Style introduced by
db_log uses the super-global variable $_SERVER which is generally not recommended.

Instead of super-globals, we recommend to explicitly inject the dependencies of your class. This makes your code less dependent on global state and it becomes generally more testable:

// Bad
class Router
{
    public function generate($path)
    {
        return $_SERVER['HOST'].$path;
    }
}

// Better
class Router
{
    private $host;

    public function __construct($host)
    {
        $this->host = $host;
    }

    public function generate($path)
    {
        return $this->host.$path;
    }
}

class Controller
{
    public function myAction(Request $request)
    {
        // Instead of
        $page = isset($_GET['page']) ? intval($_GET['page']) : 1;

        // Better (assuming you use the Symfony2 request)
        $page = $request->query->get('page', 1);
    }
}
Loading history...
373
			&$type,
374
			&$message,
375
			&$log_order,
376
			&$caller
377
		) {
378
			global $wpdb;
0 ignored issues
show
Compatibility Best Practice introduced by
Use of global functionality is not recommended; it makes your code harder to test, and less reusable.

Instead of relying on global state, we recommend one of these alternatives:

1. Pass all data via parameters

function myFunction($a, $b) {
    // Do something
}

2. Create a class that maintains your state

class MyClass {
    private $a;
    private $b;

    public function __construct($a, $b) {
        $this->a = $a;
        $this->b = $b;
    }

    public function myFunction() {
        // Do something
    }
}
Loading history...
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;
0 ignored issues
show
Compatibility Best Practice introduced by
Use of global functionality is not recommended; it makes your code harder to test, and less reusable.

Instead of relying on global state, we recommend one of these alternatives:

1. Pass all data via parameters

function myFunction($a, $b) {
    // Do something
}

2. Create a class that maintains your state

class MyClass {
    private $a;
    private $b;

    public function __construct($a, $b) {
        $this->a = $a;
        $this->b = $b;
    }

    public function myFunction() {
        // Do something
    }
}
Loading history...
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;
0 ignored issues
show
Compatibility Best Practice introduced by
Use of global functionality is not recommended; it makes your code harder to test, and less reusable.

Instead of relying on global state, we recommend one of these alternatives:

1. Pass all data via parameters

function myFunction($a, $b) {
    // Do something
}

2. Create a class that maintains your state

class MyClass {
    private $a;
    private $b;

    public function __construct($a, $b) {
        $this->a = $a;
        $this->b = $b;
    }

    public function myFunction() {
        // Do something
    }
}
Loading history...
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;
0 ignored issues
show
Compatibility Best Practice introduced by
Use of global functionality is not recommended; it makes your code harder to test, and less reusable.

Instead of relying on global state, we recommend one of these alternatives:

1. Pass all data via parameters

function myFunction($a, $b) {
    // Do something
}

2. Create a class that maintains your state

class MyClass {
    private $a;
    private $b;

    public function __construct($a, $b) {
        $this->a = $a;
        $this->b = $b;
    }

    public function myFunction() {
        // Do something
    }
}
Loading history...
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
	}