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.

Issues (1881)

Security Analysis    not enabled

This project does not seem to handle request data directly as such no vulnerable execution paths were found.

  Cross-Site Scripting
Cross-Site Scripting enables an attacker to inject code into the response of a web-request that is viewed by other users. It can for example be used to bypass access controls, or even to take over other users' accounts.
  File Exposure
File Exposure allows an attacker to gain access to local files that he should not be able to access. These files can for example include database credentials, or other configuration files.
  File Manipulation
File Manipulation enables an attacker to write custom data to files. This potentially leads to injection of arbitrary code on the server.
  Object Injection
Object Injection enables an attacker to inject an object into PHP code, and can lead to arbitrary code execution, file exposure, or file manipulation attacks.
  Code Injection
Code Injection enables an attacker to execute arbitrary code on the server.
  Response Splitting
Response Splitting can be used to send arbitrary responses.
  File Inclusion
File Inclusion enables an attacker to inject custom files into PHP's file loading mechanism, either explicitly passed to include, or for example via PHP's auto-loading mechanism.
  Command Injection
Command Injection enables an attacker to inject a shell command that is execute with the privileges of the web-server. This can be used to expose sensitive data, or gain access of your server.
  SQL Injection
SQL Injection enables an attacker to execute arbitrary SQL code on your database server gaining access to user data, or manipulating user data.
  XPath Injection
XPath Injection enables an attacker to modify the parts of XML document that are read. If that XML document is for example used for authentication, this can lead to further vulnerabilities similar to SQL Injection.
  LDAP Injection
LDAP Injection enables an attacker to inject LDAP statements potentially granting permission to run unauthorized queries, or modify content inside the LDAP tree.
  Header Injection
  Other Vulnerability
This category comprises other attack vectors such as manipulating the PHP runtime, loading custom extensions, freezing the runtime, or similar.
  Regex Injection
Regex Injection enables an attacker to execute arbitrary code in your PHP process.
  XML Injection
XML Injection enables an attacker to read files on your local filesystem including configuration files, or can be abused to freeze your web-server process.
  Variable Injection
Variable Injection enables an attacker to overwrite program variables with custom data, and can lead to further vulnerabilities.
Unfortunately, the security analysis is currently not available for your project. If you are a non-commercial open-source project, please contact support to gain access.

freemius/includes/class-fs-logger.php (23 issues)

Upgrade to new PHP Analysis Engine

These results are based on our legacy PHP analysis, consider migrating to our new PHP analysis engine instead. Learn more

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     https://www.gnu.org/licenses/gpl-3.0.html GNU General Public License Version 3
6
	 * @since       1.0.3
7
	 */
8
9
	if ( ! defined( 'ABSPATH' ) ) {
10
		exit;
11
	}
12
13
	class FS_Logger {
0 ignored issues
show
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
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
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
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
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
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
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
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
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
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
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
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 ) &&
224
			     ! empty( $api_result->error ) &&
225
			     ! empty( $api_result->error->message )
226
			) {
227
				$message = $api_result->error->message;
228
			} else if ( is_object( $api_result ) ) {
229
				$message = var_export( $api_result, true );
230
			} else if ( is_string( $api_result ) ) {
231
				$message = $api_result;
232
			} else if ( empty( $api_result ) ) {
233
				$message = 'Empty API result.';
234
			}
235
236
			$message = 'API Error: ' . $message;
237
238
			$this->_log( $message, 'error', $wrapper );
239
		}
240
241
		function entrance( $message = '', $wrapper = false ) {
0 ignored issues
show
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...
242
			$msg = 'Entrance' . ( empty( $message ) ? '' : ' > ' ) . $message;
243
244
			$this->_log( $msg, 'log', $wrapper );
245
		}
246
247
		function departure( $message = '', $wrapper = false ) {
0 ignored issues
show
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...
248
			$msg = 'Departure' . ( empty( $message ) ? '' : ' > ' ) . $message;
249
250
			$this->_log( $msg, 'log', $wrapper );
251
		}
252
253
		#--------------------------------------------------------------------------------
254
		#region Log Formatting
255
		#--------------------------------------------------------------------------------
256
257
		private static function format( $log, $show_type = true ) {
258
			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'] . ']';
259
		}
260
261
		private static function format_html( $log ) {
262
			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>';
263
		}
264
265
		#endregion
266
267
		static function dump() {
0 ignored issues
show
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...
268
			?>
269
			<!-- BEGIN: Freemius PHP Console Log -->
270
			<script type="text/javascript">
271
				<?php
272
				foreach ( self::$LOG as $log ) {
273
					echo 'console.' . $log['log_type'] . '(' . json_encode( self::format( $log, false ) ) . ')' . "\n";
274
				}
275
				?>
276
			</script>
277
			<!-- END: Freemius PHP Console Log -->
278
			<?php
279
		}
280
281
		static function get_log() {
0 ignored issues
show
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...
282
			return self::$LOG;
283
		}
284
285
		#--------------------------------------------------------------------------------
286
		#region Database Logging
287
		#--------------------------------------------------------------------------------
288
289
		/**
290
		 * @author Vova Feldman (@svovaf)
291
		 * @since  1.2.1.6
292
		 *
293
		 * @return bool
294
		 */
295
		public static function is_storage_logging_on() {
296
			if ( ! isset( self::$_isStorageLoggingOn ) ) {
297
				self::$_isStorageLoggingOn = ( 1 == get_option( 'fs_storage_logger', 0 ) );
298
			}
299
300
			return self::$_isStorageLoggingOn;
301
		}
302
303
		/**
304
		 * Turns on/off database persistent debugging to capture
305
		 * multi-session logs to debug complex flows like
306
		 * plugin auto-deactivate on premium version activation.
307
		 *
308
		 * @todo   Check if Theme Check has issues with DB tables for themes.
309
		 *
310
		 * @author Vova Feldman (@svovaf)
311
		 * @since  1.2.1.6
312
		 *
313
		 * @param bool $is_on
314
		 *
315
		 * @return bool
316
		 */
317
		public static function _set_storage_logging( $is_on = true ) {
318
			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...
319
320
			$table = "{$wpdb->prefix}fs_logger";
321
322
			if ( $is_on ) {
323
				/**
324
				 * Create logging table.
325
				 *
326
				 * NOTE:
327
				 *  dbDelta must use KEY and not INDEX for indexes.
328
				 *
329
				 * @link https://core.trac.wordpress.org/ticket/2695
330
				 */
331
				$result = $wpdb->query( "CREATE TABLE {$table} (
332
`id` BIGINT(20) UNSIGNED NOT NULL AUTO_INCREMENT,
333
`process_id` INT UNSIGNED NOT NULL,
334
`user_name` VARCHAR(64) NOT NULL,
335
`logger` VARCHAR(128) NOT NULL,
336
`log_order` INT UNSIGNED NOT NULL,
337
`type` ENUM('log','info','warn','error') NOT NULL DEFAULT 'log',
338
`message` TEXT NOT NULL,
339
`file` VARCHAR(256) NOT NULL,
340
`line` INT UNSIGNED NOT NULL,
341
`function` VARCHAR(256) NOT NULL,
342
`request_type` ENUM('call','ajax','cron') NOT NULL DEFAULT 'call',
343
`request_url` VARCHAR(1024) NOT NULL,
344
`created` DECIMAL(16, 6) NOT NULL,
345
PRIMARY KEY (`id`),
346
KEY `process_id` (`process_id` ASC),
347
KEY `process_logger` (`process_id` ASC, `logger` ASC),
348
KEY `function` (`function` ASC),
349
KEY `type` (`type` ASC))" );
350
			} else {
351
				/**
352
				 * Drop logging table.
353
				 */
354
				$result = $wpdb->query( "DROP TABLE IF EXISTS $table;" );
355
			}
356
357
			if ( false !== $result ) {
358
				update_option( 'fs_storage_logger', ( $is_on ? 1 : 0 ) );
359
			}
360
361
			return ( false !== $result );
362
		}
363
364
		/**
365
		 * @author Vova Feldman (@svovaf)
366
		 * @since  1.2.1.6
367
		 *
368
		 * @param string $type
369
		 * @param string $message
370
		 * @param int    $log_order
371
		 * @param array  $caller
372
		 *
373
		 * @return false|int
374
		 */
375
		private function db_log(
376
			&$type,
377
			&$message,
378
			&$log_order,
379
			&$caller
380
		) {
381
			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...
382
383
			$request_type = 'call';
384
			if ( defined( 'DOING_CRON' ) && DOING_CRON ) {
385
				$request_type = 'cron';
386
			} else if ( defined( 'DOING_AJAX' ) && DOING_AJAX ) {
387
				$request_type = 'ajax';
388
			}
389
390
			$request_url = WP_FS__IS_HTTP_REQUEST ?
391
				$_SERVER['REQUEST_URI'] :
392
				'';
393
394
			return $wpdb->insert(
395
				"{$wpdb->prefix}fs_logger",
396
				array(
397
					'process_id'   => self::$_processID,
398
					'user_name'    => self::$_ownerName,
399
					'logger'       => $this->_id,
400
					'log_order'    => $log_order,
401
					'type'         => $type,
402
					'request_type' => $request_type,
403
					'request_url'  => $request_url,
404
					'message'      => $message,
405
					'file'         => isset( $caller['file'] ) ?
406
						substr( $caller['file'], self::$_abspathLength ) :
407
						'',
408
					'line'         => $caller['line'],
409
					'function'     => ( ! empty( $caller['class'] ) ? $caller['class'] . $caller['type'] : '' ) . $caller['function'],
410
					'created'      => microtime( true ),
411
				)
412
			);
413
		}
414
415
		/**
416
		 * Persistent DB logger columns.
417
		 *
418
		 * @var array
419
		 */
420
		private static $_log_columns = array(
421
			'id',
422
			'process_id',
423
			'user_name',
424
			'logger',
425
			'log_order',
426
			'type',
427
			'message',
428
			'file',
429
			'line',
430
			'function',
431
			'request_type',
432
			'request_url',
433
			'created',
434
		);
435
436
		/**
437
		 * Create DB logs query.
438
		 *
439
		 * @author Vova Feldman (@svovaf)
440
		 * @since  1.2.1.6
441
		 *
442
		 * @param bool $filters
443
		 * @param int  $limit
444
		 * @param int  $offset
445
		 * @param bool $order
446
		 * @param bool $escape_eol
447
		 *
448
		 * @return string
449
		 */
450
		private static function build_db_logs_query(
451
			$filters = false,
452
			$limit = 200,
453
			$offset = 0,
454
			$order = false,
455
			$escape_eol = false
456
		) {
457
			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...
458
459
			$select = '*';
460
461
			if ( $escape_eol ) {
462
				$select = '';
463
				for ( $i = 0, $len = count( self::$_log_columns ); $i < $len; $i ++ ) {
464
					if ( $i > 0 ) {
465
						$select .= ', ';
466
					}
467
468
					if ( 'message' !== self::$_log_columns[ $i ] ) {
469
						$select .= self::$_log_columns[ $i ];
470
					} else {
471
						$select .= 'REPLACE(message , \'\n\', \' \') AS message';
472
					}
473
				}
474
			}
475
476
			$query = "SELECT {$select} FROM {$wpdb->prefix}fs_logger";
477
			if ( is_array( $filters ) ) {
478
				$criteria = array();
479
480
				if ( ! empty( $filters['type'] ) && 'all' !== $filters['type'] ) {
481
					$filters['type'] = strtolower( $filters['type'] );
482
483
					switch ( $filters['type'] ) {
484
						case 'warn_error':
485
							$criteria[] = array( 'col' => 'type', 'val' => array( 'warn', 'error' ) );
486
							break;
487
						case 'error':
488
						case 'warn':
489
							$criteria[] = array( 'col' => 'type', 'val' => $filters['type'] );
490
							break;
491
						case 'info':
492
						default:
493
							$criteria[] = array( 'col' => 'type', 'val' => array( 'info', 'log' ) );
494
							break;
495
					}
496
				}
497
498
				if ( ! empty( $filters['request_type'] ) ) {
499
					$filters['request_type'] = strtolower( $filters['request_type'] );
500
501
					if ( in_array( $filters['request_type'], array( 'call', 'ajax', 'cron' ) ) ) {
502
						$criteria[] = array( 'col' => 'request_type', 'val' => $filters['request_type'] );
503
					}
504
				}
505
506
				if ( ! empty( $filters['file'] ) ) {
507
					$criteria[] = array(
508
						'col' => 'file',
509
						'op'  => 'LIKE',
510
						'val' => '%' . esc_sql( $filters['file'] ),
511
					);
512
				}
513
514
				if ( ! empty( $filters['function'] ) ) {
515
					$criteria[] = array(
516
						'col' => 'function',
517
						'op'  => 'LIKE',
518
						'val' => '%' . esc_sql( $filters['function'] ),
519
					);
520
				}
521
522
				if ( ! empty( $filters['process_id'] ) && is_numeric( $filters['process_id'] ) ) {
523
					$criteria[] = array( 'col' => 'process_id', 'val' => $filters['process_id'] );
524
				}
525
526
				if ( ! empty( $filters['logger'] ) ) {
527
					$criteria[] = array(
528
						'col' => 'logger',
529
						'op'  => 'LIKE',
530
						'val' => '%' . esc_sql( $filters['logger'] ) . '%',
531
					);
532
				}
533
534
				if ( ! empty( $filters['message'] ) ) {
535
					$criteria[] = array(
536
						'col' => 'message',
537
						'op'  => 'LIKE',
538
						'val' => '%' . esc_sql( $filters['message'] ) . '%',
539
					);
540
				}
541
542
				if ( 0 < count( $criteria ) ) {
543
					$query .= "\nWHERE\n";
544
545
					$first = true;
546
					foreach ( $criteria as $c ) {
547
						if ( ! $first ) {
548
							$query .= "AND\n";
549
						}
550
551
						if ( is_array( $c['val'] ) ) {
552
							$operator = 'IN';
553
554
							for ( $i = 0, $len = count( $c['val'] ); $i < $len; $i ++ ) {
555
								$c['val'][ $i ] = "'" . esc_sql( $c['val'][ $i ] ) . "'";
556
							}
557
558
							$val = '(' . implode( ',', $c['val'] ) . ')';
559
						} else {
560
							$operator = ! empty( $c['op'] ) ? $c['op'] : '=';
561
							$val      = "'" . esc_sql( $c['val'] ) . "'";
562
						}
563
564
						$query .= "`{$c['col']}` {$operator} {$val}\n";
565
566
						$first = false;
567
					}
568
				}
569
			}
570
571
			if ( ! is_array( $order ) ) {
572
				$order = array(
573
					'col'   => 'id',
574
					'order' => 'desc'
575
				);
576
			}
577
578
			$query .= " ORDER BY {$order['col']} {$order['order']} LIMIT {$offset},{$limit}";
579
580
			return $query;
581
		}
582
583
		/**
584
		 * Load logs from DB.
585
		 *
586
		 * @author Vova Feldman (@svovaf)
587
		 * @since  1.2.1.6
588
		 *
589
		 * @param bool $filters
590
		 * @param int  $limit
591
		 * @param int  $offset
592
		 * @param bool $order
593
		 *
594
		 * @return object[]|null
595
		 */
596
		public static function load_db_logs(
597
			$filters = false,
598
			$limit = 200,
599
			$offset = 0,
600
			$order = false
601
		) {
602
			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...
603
604
			$query = self::build_db_logs_query(
605
				$filters,
606
				$limit,
607
				$offset,
608
				$order
609
			);
610
611
			return $wpdb->get_results( $query );
612
		}
613
614
		/**
615
		 * Load logs from DB.
616
		 *
617
		 * @author Vova Feldman (@svovaf)
618
		 * @since  1.2.1.6
619
		 *
620
		 * @param bool   $filters
621
		 * @param string $filename
622
		 * @param int    $limit
623
		 * @param int    $offset
624
		 * @param bool   $order
625
		 *
626
		 * @return false|string File download URL or false on failure.
627
		 */
628
		public static function download_db_logs(
629
			$filters = false,
630
			$filename = '',
631
			$limit = 10000,
632
			$offset = 0,
633
			$order = false
634
		) {
635
			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...
636
637
			$query = self::build_db_logs_query(
638
				$filters,
639
				$limit,
640
				$offset,
641
				$order,
642
				true
643
			);
644
645
			$upload_dir = wp_upload_dir();
646
			if ( empty( $filename ) ) {
647
				$filename = 'fs-logs-' . date( 'Y-m-d_H-i-s', WP_FS__SCRIPT_START_TIME ) . '.csv';
648
			}
649
			$filepath = rtrim( $upload_dir['path'], '/' ) . "/{$filename}";
650
651
			$query .= " INTO OUTFILE '{$filepath}' FIELDS TERMINATED BY '\t' ESCAPED BY '\\\\' OPTIONALLY ENCLOSED BY '\"' LINES TERMINATED BY '\\n'";
652
653
			$columns = '';
654
			for ( $i = 0, $len = count( self::$_log_columns ); $i < $len; $i ++ ) {
655
				if ( $i > 0 ) {
656
					$columns .= ', ';
657
				}
658
659
				$columns .= "'" . self::$_log_columns[ $i ] . "'";
660
			}
661
662
			$query = "SELECT {$columns} UNION ALL " . $query;
663
664
			$result = $wpdb->query( $query );
665
666
			if ( false === $result ) {
667
				return false;
668
			}
669
670
			return rtrim( $upload_dir['url'], '/' ) . '/' . $filename;
671
		}
672
673
		/**
674
		 * @author Vova Feldman (@svovaf)
675
		 * @since  1.2.1.6
676
		 *
677
		 * @param string $filename
678
		 *
679
		 * @return string
680
		 */
681
		public static function get_logs_download_url( $filename = '' ) {
682
			$upload_dir = wp_upload_dir();
683
			if ( empty( $filename ) ) {
684
				$filename = 'fs-logs-' . date( 'Y-m-d_H-i-s', WP_FS__SCRIPT_START_TIME ) . '.csv';
685
			}
686
687
			return rtrim( $upload_dir['url'], '/' ) . $filename;
688
		}
689
690
		#endregion
691
	}