| 1 |
<?php |
| 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 { |
| 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, $wrapper = false ) { |
| 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 ) && |
| 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 ) { |
| 242 |
$msg = 'Entrance' . ( empty( $message ) ? '' : ' > ' ) . $message; |
| 243 |
|
| 244 |
$this->_log( $msg, 'log', $wrapper ); |
| 245 |
} |
| 246 |
|
| 247 |
function departure( $message = '', $wrapper = false ) { |
| 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() { |
| 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() { |
| 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; |
| 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; |
| 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; |
| 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; |
| 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; |
| 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 |
} |
| 692 |
|