View file system/functions.php

File size: 3.98Kb
<?php
/**
 * CMS: LaiCMS (v1.0 Edition 2026)
 * File: system/functions.php
 * Description: Вспомогательные функции с усиленной безопасностью и мультиязычностью.
 */

declare(strict_types=1);

/**
 * 1. БЕЗОПАСНОЕ ФОРМАТИРОВАНИЕ ВРЕМЕНИ
 */
if (!function_exists('time_ago')) {
    function time_ago(?string $datetime): string {
        if (!$datetime) return __t('time_never'); // "Никогда"
        
        $time = strtotime($datetime);
        if (!$time) return __t('time_invalid'); // "Дата некорректна"
        
        $diff = time() - $time;
        
        if ($diff < 0) return __t('time_future'); // "В будущем"
        if ($diff < 60) return __t('time_now'); // "Только что"
        if ($diff < 3600) return floor($diff / 60) . " " . __t('time_min_ago'); // "мин. назад"
        if ($diff < 86400) return floor($diff / 3600) . " " . __t('time_hour_ago'); // "час. назад"
        
        return date("d.m.Y", $time);
    }
}

/**
 * 2. УМНОЕ СОКРАЩЕНИЕ ТЕКСТА (XSS Safe)
 */
if (!function_exists('cut_text')) {
    function cut_text(string $text, int $limit = 100): string {
        $text = strip_tags($text); // Принудительная очистка от HTML
        if (mb_strlen($text) <= $limit) return $text;
        return mb_substr($text, 0, $limit) . '...';
    }
}

/**
 * 3. ПОДСВЕТКА МЕНЮ (Safe Path Check)
 */
if (!function_exists('is_active')) {
    function is_active(string $path): string {
        $current = parse_url($_SERVER['REQUEST_URI'], PHP_URL_PATH);
        return (strpos($current, $path) !== false) ? 'active' : '';
    }
}

/**
 * 4. УЛЬТРА-БЕЗОПАСНЫЙ МОНИТОРИНГ ОНЛАЙН
 * Защита от SQL-инъекций через заголовки и спуфинга IP.
 */
if (!function_exists('track_online_status')) {
    function track_online_status(): void {
        global $mysqli;
        if (empty($_SESSION['user_id']) || !isset($mysqli)) return;

        $user_id = (int)$_SESSION['user_id'];
        
        // Получаем реальный IP с защитой от подмены
        $ip = $_SERVER['HTTP_X_REAL_IP'] ?? $_SERVER['HTTP_X_FORWARDED_FOR'] ?? $_SERVER['REMOTE_ADDR'];
        $ip = filter_var($ip, FILTER_VALIDATE_IP) ?: '0.0.0.0';
        
        // Очищаем User-Agent от скрытых символов
        $ua = mb_strimwidth(strip_tags($_SERVER['HTTP_USER_AGENT'] ?? 'Unknown'), 0, 255);

        try {
            $stmt = $mysqli->prepare("UPDATE users SET last_seen = NOW(), last_ip = ?, last_ua = ? WHERE id = ?");
            $stmt->bind_param("ssi", $ip, $ua, $user_id);
            $stmt->execute();
        } catch (Exception $e) {
            error_log("Security Monitor Error: " . $e->getMessage());
        }
    }
}

/**
 * 5. ОПТИМИЗИРОВАННЫЙ СЧЕТЧИК СООБЩЕНИЙ
 */
if (!function_exists('get_unread_count')) {
    function get_unread_count(int $user_id): int {
        global $mysqli;
        if (!isset($mysqli)) return 0;

        try {
            $stmt = $mysqli->prepare("SELECT COUNT(id) FROM messages WHERE receiver_id = ? AND is_read = 0");
            $stmt->bind_param("i", $user_id);
            $stmt->execute();
            $res = $stmt->get_result()->fetch_row();
            return (int)($res[0] ?? 0);
        } catch (Exception $e) {
            return 0;
        }
    }
}

/**
 * 6. ПРЕДПРОСМОТР (XSS & Buffer Overflow protection)
 */
if (!function_exists('preview_text')) {
    function preview_text(?string $text, int $limit = 50): string {
        if (!$text) return "";
        // Убираем теги, лишние пробелы и ограничиваем ширину
        $clean = preg_replace('/\s+/', ' ', strip_tags($text));
        return mb_strimwidth(trim($clean), 0, $limit, "...");
    }
}

// Прямой запуск мониторинга
track_online_status();