RateLimiter.php

3.01 KB
08/07/2025 12:55
PHP
RateLimiter.php
<?php

require_once __DIR__.'/SecurityHelper.php';

class RateLimiter
{
    /**
     * @param $userId
     */
    private static function getRateLimitFile($userId)
    {
        $secureUserId = SecurityHelper::securePath($userId);
        return __DIR__."/../../logs/rate_limit_{$secureUserId}.json";
    }

    /**
     * ตรวจสอบ rate limit สำหรับผู้ใช้
     */
    public static function checkRateLimit($userId, $limit = null, $timeWindow = null)
    {
        $limit = $limit ?? (defined('RATE_LIMIT_REQUESTS') ? RATE_LIMIT_REQUESTS : 10);
        $timeWindow = $timeWindow ?? (defined('RATE_LIMIT_WINDOW') ? RATE_LIMIT_WINDOW : 60);

        $file = self::getRateLimitFile($userId);
        $now = time();

        // สร้าง directory ถ้าไม่มี
        $dir = dirname($file);
        if (!is_dir($dir)) {
            mkdir($dir, 0755, true);
        }

        if (file_exists($file)) {
            $content = file_get_contents($file);
            $data = json_decode($content, true);

            if (!$data || !isset($data['requests'])) {
                $requests = [];
            } else {
                // กรองเอาเฉพาะ request ที่อยู่ใน time window
                $requests = array_filter($data['requests'], function ($time) use ($now, $timeWindow) {
                    return ($now - $time) < $timeWindow;
                });
            }
        } else {
            $requests = [];
        }

        // ตรวจสอบว่าเกิน limit หรือไม่
        if (count($requests) >= $limit) {
            SecurityHelper::logSecurityEvent('rate_limit_exceeded', $userId, [
                'requests_count' => count($requests),
                'limit' => $limit,
                'time_window' => $timeWindow
            ]);
            return false; // Rate limit exceeded
        }

        // เพิ่ม request ปัจจุบัน
        $requests[] = $now;

        // บันทึกข้อมูลกลับ
        $data = ['requests' => array_values($requests)];
        file_put_contents($file, json_encode($data), LOCK_EX);

        return true;
    }

    /**
     * ล้างข้อมูล rate limit สำหรับผู้ใช้
     */
    public static function clearRateLimit($userId)
    {
        $file = self::getRateLimitFile($userId);
        if (file_exists($file)) {
            unlink($file);
        }
    }

    /**
     * ล้างข้อมูล rate limit ที่หมดอายุ
     */
    public static function cleanupExpiredLimits()
    {
        $logsDir = __DIR__."/../../logs";
        if (!is_dir($logsDir)) {
            return;
        }

        $files = glob($logsDir."/rate_limit_*.json");
        $now = time();
        $maxAge = defined('RATE_LIMIT_WINDOW') ? RATE_LIMIT_WINDOW : 60;

        foreach ($files as $file) {
            if (filemtime($file) < ($now - $maxAge * 2)) {
                unlink($file);
            }
        }
    }
}