| 1 |
<?php |
| 2 |
|
| 3 |
namespace StoreEngine\Classes; |
| 4 |
|
| 5 |
use StoreEngine\Classes\Exceptions\StoreEngineRuntimeException; |
| 6 |
use StoreEngine\Utils\Constants; |
| 7 |
|
| 8 |
if ( ! defined( 'ABSPATH' ) ) { |
| 9 |
exit; |
| 10 |
} |
| 11 |
|
| 12 |
class Logger { |
| 13 |
|
| 14 |
const SUCCESS = 'success'; |
| 15 |
const WARNING = 'warning'; |
| 16 |
const ERROR = 'error'; |
| 17 |
const INFO = 'info'; |
| 18 |
const DEBUG = 'debug'; |
| 19 |
const EMERGENCY = 'emergency'; |
| 20 |
const CRITICAL = 'critical'; |
| 21 |
const ALERT = 'alert'; |
| 22 |
|
| 23 |
public static function log( string $title, $content, string $status = 'success', string $module = 'system' ) { |
| 24 |
try { |
| 25 |
self::log_to_db( $title, $content, $status, $module ); |
| 26 |
} catch ( \Throwable $t ) { |
| 27 |
if ( defined( 'WP_DEBUG' ) && WP_DEBUG ) { |
| 28 |
error_log( 'Unable to write StoreEngine log data to database. Error: ' . $t->getMessage() ); // phpcs:ignore WordPress.PHP.DevelopmentFunctions.error_log_error_log -- Last-resort diagnostic when DB logging fails; gated behind WP_DEBUG. |
| 29 |
} |
| 30 |
|
| 31 |
// Fallback. |
| 32 |
try { |
| 33 |
self::log_to_file( $title, $content, $status, $module ); |
| 34 |
} catch ( \Throwable $t ) { |
| 35 |
if ( defined( 'WP_DEBUG' ) && WP_DEBUG ) { |
| 36 |
error_log( 'Unable to write StoreEngine log data to log file. Error: ' . $t->getMessage() ); // phpcs:ignore WordPress.PHP.DevelopmentFunctions.error_log_error_log -- Last-resort diagnostic when both DB and file logging fail; gated behind WP_DEBUG. |
| 37 |
} |
| 38 |
} |
| 39 |
} |
| 40 |
} |
| 41 |
|
| 42 |
public static function log_to_db( string $title, $content, string $status = 'success', string $module = 'system' ) { |
| 43 |
$log = new LogItem(); |
| 44 |
$log->set_props( [ |
| 45 |
'date' => current_time( 'mysql' ), |
| 46 |
'module' => $module, |
| 47 |
'title' => $title, |
| 48 |
'status' => $status, |
| 49 |
'content' => is_array( $content ) || is_object( $content ) ? json_encode( $content ) : $content, |
| 50 |
] ); |
| 51 |
|
| 52 |
$log->save(); |
| 53 |
} |
| 54 |
|
| 55 |
public static function log_to_file( string $title, $content, string $status = 'success', string $module = 'system' ) { |
| 56 |
if ( ! defined( 'STOREENGINE_LOGS_DIR' ) ) { |
| 57 |
throw new StoreEngineRuntimeException( 'Logs directory is not defined.' ); |
| 58 |
} |
| 59 |
|
| 60 |
if ( ! file_exists( STOREENGINE_LOGS_DIR ) ) { |
| 61 |
wp_mkdir_p( STOREENGINE_LOGS_DIR ); |
| 62 |
} |
| 63 |
|
| 64 |
$message = is_string( $content ) ? $title . $content : $title; |
| 65 |
|
| 66 |
if ( ! is_scalar( $content ) ) { |
| 67 |
$message .= PHP_EOL . json_encode( $content ); |
| 68 |
} |
| 69 |
|
| 70 |
$log_file = STOREENGINE_LOGS_DIR . 'storeengine-' . $module . '-' . gmdate( 'Y-m-d' ) . '.log'; |
| 71 |
$timestamp = gmdate( 'c' ); |
| 72 |
$module = strtoupper( $module ); |
| 73 |
$status = strtoupper( $status ); |
| 74 |
$message = "[$timestamp] – [$module] [$status] $message"; |
| 75 |
|
| 76 |
// Append via WP_Filesystem — Plugin Check disallows direct fopen/fwrite. |
| 77 |
// Log files rotate daily by name, so this read-modify-write stays cheap. |
| 78 |
require_once ABSPATH . 'wp-admin/includes/file.php'; |
| 79 |
global $wp_filesystem; |
| 80 |
if ( empty( $wp_filesystem ) && ! WP_Filesystem() ) { |
| 81 |
return; |
| 82 |
} |
| 83 |
|
| 84 |
$existing = $wp_filesystem->exists( $log_file ) ? (string) $wp_filesystem->get_contents( $log_file ) : ''; |
| 85 |
$chmod = Constants::is_defined( 'FS_CHMOD_FILE' ) ? FS_CHMOD_FILE : false; |
| 86 |
$wp_filesystem->put_contents( $log_file, $existing . $message . PHP_EOL, $chmod ); |
| 87 |
} |
| 88 |
} |
| 89 |
|