$value) { if (\is_string($value)) { $parts[] = "{$key}=\"".self::escapeValue($value).'"'; } elseif (\is_bool($value)) { $parts[] = "{$key}=".($value ? 'true' : 'false'); } elseif ($value === null) { $parts[] = "{$key}=null"; } else { $parts[] = "{$key}={$value}"; } } $contextStr = ' ('.\implode(', ', $parts).')'; } else { $contextStr = ' ('.self::escapeValue((string) $context).')'; } } self::writeLine("{$category}: {$message}{$contextStr}"); } /** * Log a separator for readability. */ public static function separator(string $label = ''): void { if (!self::$enabled) { return; } $sep = \str_repeat('-', 60); self::writeLine($label ? "{$sep} {$label} {$sep}" : $sep); } /** * Write a timestamped line to the log file. */ private static function writeLine(string $content): void { if (self::$handle !== null && \is_resource(self::$handle)) { $timestamp = \date('H:i:s'); \fwrite(self::$handle, "[{$timestamp}] {$content}\n"); \fflush(self::$handle); } } /** * Escape non-printable bytes for safe debug output. */ private static function escapeValue(string $value): string { $escaped = \addcslashes($value, "\0..\37\177..\377\\\""); if (\strlen($escaped) > 200) { return \substr($escaped, 0, 200).'...'; } return $escaped; } /** * Get the log file path. */ public static function getLogPath(): string { return self::$logPath; } /** * Check if debug logging is enabled. */ public static function isEnabled(): bool { return self::$enabled; } }