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