You've already forked wc-licensed-product
92 lines
2.6 KiB
PHP
92 lines
2.6 KiB
PHP
|
|
<?php
|
||
|
|
/**
|
||
|
|
* Rate Limit Trait
|
||
|
|
*
|
||
|
|
* Provides rate limiting functionality for frontend operations.
|
||
|
|
*
|
||
|
|
* @package Jeremias\WcLicensedProduct\Common
|
||
|
|
*/
|
||
|
|
|
||
|
|
declare(strict_types=1);
|
||
|
|
|
||
|
|
namespace Jeremias\WcLicensedProduct\Common;
|
||
|
|
|
||
|
|
/**
|
||
|
|
* Trait for implementing rate limiting on user actions
|
||
|
|
*
|
||
|
|
* Uses WordPress transients for storage. Rate limits are per-user when logged in,
|
||
|
|
* or per-IP when not logged in.
|
||
|
|
*/
|
||
|
|
trait RateLimitTrait
|
||
|
|
{
|
||
|
|
/**
|
||
|
|
* Check rate limit for a user action
|
||
|
|
*
|
||
|
|
* @param string $action Action identifier (e.g., 'transfer', 'download')
|
||
|
|
* @param int $limit Maximum attempts per window
|
||
|
|
* @param int $window Time window in seconds
|
||
|
|
* @return bool True if within limit, false if exceeded
|
||
|
|
*/
|
||
|
|
protected function checkUserRateLimit(string $action, int $limit, int $window): bool
|
||
|
|
{
|
||
|
|
$userId = get_current_user_id();
|
||
|
|
$key = $userId > 0
|
||
|
|
? (string) $userId
|
||
|
|
: 'ip_' . md5($_SERVER['REMOTE_ADDR'] ?? '0.0.0.0');
|
||
|
|
|
||
|
|
$transientKey = 'wclp_rate_' . $action . '_' . $key;
|
||
|
|
$data = get_transient($transientKey);
|
||
|
|
|
||
|
|
if ($data === false) {
|
||
|
|
// First request, start counting
|
||
|
|
set_transient($transientKey, ['count' => 1, 'start' => time()], $window);
|
||
|
|
return true;
|
||
|
|
}
|
||
|
|
|
||
|
|
$count = (int) ($data['count'] ?? 0);
|
||
|
|
$start = (int) ($data['start'] ?? time());
|
||
|
|
|
||
|
|
// Check if window has expired
|
||
|
|
if (time() - $start >= $window) {
|
||
|
|
// Reset counter
|
||
|
|
set_transient($transientKey, ['count' => 1, 'start' => time()], $window);
|
||
|
|
return true;
|
||
|
|
}
|
||
|
|
|
||
|
|
// Check if limit exceeded
|
||
|
|
if ($count >= $limit) {
|
||
|
|
return false;
|
||
|
|
}
|
||
|
|
|
||
|
|
// Increment counter
|
||
|
|
set_transient($transientKey, ['count' => $count + 1, 'start' => $start], $window);
|
||
|
|
return true;
|
||
|
|
}
|
||
|
|
|
||
|
|
/**
|
||
|
|
* Get remaining time until rate limit resets
|
||
|
|
*
|
||
|
|
* @param string $action Action identifier
|
||
|
|
* @param int $window Time window in seconds (must match the one used in checkUserRateLimit)
|
||
|
|
* @return int Seconds until rate limit resets, or 0 if not rate limited
|
||
|
|
*/
|
||
|
|
protected function getRateLimitRetryAfter(string $action, int $window): int
|
||
|
|
{
|
||
|
|
$userId = get_current_user_id();
|
||
|
|
$key = $userId > 0
|
||
|
|
? (string) $userId
|
||
|
|
: 'ip_' . md5($_SERVER['REMOTE_ADDR'] ?? '0.0.0.0');
|
||
|
|
|
||
|
|
$transientKey = 'wclp_rate_' . $action . '_' . $key;
|
||
|
|
$data = get_transient($transientKey);
|
||
|
|
|
||
|
|
if ($data === false) {
|
||
|
|
return 0;
|
||
|
|
}
|
||
|
|
|
||
|
|
$start = (int) ($data['start'] ?? time());
|
||
|
|
|
||
|
|
return max(0, $window - (time() - $start));
|
||
|
|
}
|
||
|
|
}
|