Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,8 @@
"ext-pdo": "*",
"ext-curl": "*",
"ext-redis": "*",
"utopia-php/database": "0.*.*"
"utopia-php/database": "0.*.*",
"utopia-php/pools": "^0.8.2"
},
"require-dev": {
"phpunit/phpunit": "9.*",
Expand Down
54 changes: 53 additions & 1 deletion composer.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

33 changes: 33 additions & 0 deletions src/Abuse/Adapters/TimeLimit.php
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,39 @@ public function remaining(): int
return (0 > $left) ? 0 : $left;
}


/**
* Set key
* @param string $key
* @return static
*/
public function setKey(string $key): static
{
$this->key = $key;
return $this;
}

/**
* Set limit
* @param int $limit
* @return static
*/
public function setLimit(int $limit): static
{
$this->limit = $limit;
return $this;
}

/**
* Get key
* @return string
*/
public function getKey(): string
{
return $this->key;
}


/**
* Limit
*
Expand Down
88 changes: 88 additions & 0 deletions src/Abuse/Adapters/TimeLimit/PoolRedis.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
<?php

namespace Utopia\Abuse\Adapters\TimeLimit;

use Utopia\Pools\Pool as UtopiaPool;
use Utopia\Abuse\Adapters\TimeLimit\Redis as RedisAdapter;
use Redis;

class PoolRedis extends RedisAdapter
{
/**
* @var UtopiaPool<covariant Redis>
*/
protected UtopiaPool $pool;

/**
* @param string $key
* @param int $limit
* @param int $seconds
* @param UtopiaPool<covariant Redis> $pool The pool to use for connections. Must contain instances of TimeLimit.
*
* @throws \Exception
*/
public function __construct(string $key, int $limit, int $seconds, UtopiaPool $pool)
{
$this->pool = $pool;
$this->key = $key;
$this->limit = $limit;
$this->ttl = $seconds;
$now = \time();
$this->timestamp = (int)($now - ($now % $seconds));

$this->pool->use(function (mixed $resource) {
if (! ($resource instanceof Redis)) {
throw new \Exception('Pool must contain instances of '.Redis::class);
}
});
}

/**
* Forward method calls to the internal adapter instance via the pool.
*
* Required because __call() can't be used to implement abstract methods.
*
* @param string $method
* @param array<mixed> $args
* @return mixed
*/
public function delegate(string $method, array $args): mixed
{
return $this->pool->use(function (Redis $redis) use ($method, $args) {
$this->redis = $redis;
return parent::{$method}(...$args);
});
}

protected function count(string $key, int $timestamp): int
{
/**
* @var int $result
*/
$result = $this->delegate(__FUNCTION__, \func_get_args());
return $result;
}

protected function hit(string $key, int $timestamp): void
{
$this->delegate(__FUNCTION__, \func_get_args());
}

public function cleanup(int $timestamp): bool
{
/**
* @var bool $result
*/
$result = $this->delegate(__FUNCTION__, \func_get_args());
return $result;
}

public function getLogs(?int $offset = null, ?int $limit = 25): array
{
/**
* @var array<string, mixed> $result
*/
$result = $this->delegate(__FUNCTION__, \func_get_args());
return $result;
}
}
12 changes: 3 additions & 9 deletions src/Abuse/Adapters/TimeLimit/Redis.php
Original file line number Diff line number Diff line change
Expand Up @@ -41,19 +41,15 @@ protected function count(string $key, int $timestamp): int
return 0;
}

if (! \is_null($this->count)) { // Get fetched result
return $this->count;
}

/** @var string $count */
$count = $this->redis->get(self::NAMESPACE . '__'. $key .'__'. $timestamp);
if (!$count) {
$this->count = 0;
$count = 0;
} else {
$this->count = intval($count);
$count = intval($count);
}

return $this->count;
return $count;
}

/**
Expand All @@ -73,8 +69,6 @@ protected function hit(string $key, int $timestamp): void
->incr($key)
->expire($key, $this->ttl)
->exec();

$this->count = ($this->count ?? 0) + 1;
}

/**
Expand Down
29 changes: 29 additions & 0 deletions tests/Abuse/PoolRedisTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
<?php

namespace Utopia\Tests;

use Utopia\Abuse\Adapters\TimeLimit\PoolRedis;
use Utopia\Abuse\Adapters\TimeLimit;

class PoolRedisTest extends RedisTest
{
/**
* @var \Utopia\Pools\Pool<covariant \Redis> $pool
*/
protected static \Utopia\Pools\Pool $pool;
public static function setUpBeforeClass(): void
{
parent::setUpBeforeClass();


self::$pool = new \Utopia\Pools\Pool('test', 10, function () {
$redis = RedisTest::initialiseRedis();
return $redis;
});
}

public function getAdapter(string $key, int $limit, int $seconds): TimeLimit
{
return new PoolRedis($key, $limit, $seconds, self::$pool);
}
}
2 changes: 1 addition & 1 deletion tests/Abuse/RedisTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ public static function setUpBeforeClass(): void
self::$redis = self::initialiseRedis();
}

private static function initialiseRedis(): \Redis
protected static function initialiseRedis(): \Redis
{
$redis = new \Redis();
$redis->connect('redis', 6379);
Expand Down