Skip to content

Commit

Permalink
Add predis v2 support + relay support + linter fixes
Browse files Browse the repository at this point in the history
  • Loading branch information
a-menshchikov committed Jan 8, 2024
1 parent 67e4b61 commit 6ca7950
Show file tree
Hide file tree
Showing 13 changed files with 692 additions and 93 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/audit.yml
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@ jobs:
echo "::endgroup::"
- name: Auditor
uses: docker://nbgrp/auditor:0.22.0
uses: docker://nbgrp/auditor:0.24.0

- name: Tests
run: vendor/bin/simple-phpunit
Expand Down
528 changes: 528 additions & 0 deletions .psalm/stubs/predis.phpstub

Large diffs are not rendered by default.

7 changes: 4 additions & 3 deletions composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@
"nbgrp/singlea-contracts": "^1",
"psr/cache": "^3",
"psr/log": "^3",
"snc/redis-bundle": "^3.6 || ^4",
"snc/redis-bundle": "^4.5",
"symfony/cache": "^6.4",
"symfony/cache-contracts": "^3",
"symfony/config": "^6.4",
Expand All @@ -43,10 +43,11 @@
"symfony/security-core": "^6.4",
"symfony/security-http": "^6.4",
"symfony/uid": "^6.4",
"web-token/jwt-framework": "^3.0.7"
"web-token/jwt-framework": "^3.2"
},
"require-dev": {
"predis/predis": "^1",
"ext-redis": "*",
"predis/predis": "^2",
"psr/event-dispatcher": "^1",
"psr/event-dispatcher-implementation": "1.0",
"roave/security-advisories": "dev-latest",
Expand Down
16 changes: 7 additions & 9 deletions grumphp.yml
Original file line number Diff line number Diff line change
Expand Up @@ -25,21 +25,19 @@ grumphp:
phpmd:
ruleset: [ 'phpmd-ruleset' ]
whitelist_patterns: [ '/^src\/(.*)/' ]
exclude:
- 'src/Bundles/*/Tests'

phpmnd:
directory: 'src'
exclude_name:
- 'Configuration.php'
- 'JwtFetcherConfigFactory.php'
- 'JwtTokenizerConfigFactory.php'
exclude:
- 'Bundles/*/Tests'
exclude_path:
- 'Bundles/Jwt/FeatureConfig/JwtTokenizerConfigFactory.php'
- 'Bundles/JwtFetcher/FeatureConfig/JwtFetcherConfigFactory.php'
- 'Bundles/Redis/Tests/DependencyInjection/Compiler/AddFeatureConfigManagersPassTest.php'
- 'Bundles/Singlea/Tests/Command/Client/PurgeTest.php'

phpstan:
use_grumphp_paths: false

psalm: ~
psalm:
no_cache: true

securitychecker_local: ~
5 changes: 5 additions & 0 deletions phpstan.neon
Original file line number Diff line number Diff line change
Expand Up @@ -15,3 +15,8 @@ parameters:

checkGenericClassInNonGenericObjectType: false
checkMissingIterableValueType: false

ignoreErrors:
-
message: '#Call to an undefined method Predis#'
path: src/Bundles/Redis/*
4 changes: 3 additions & 1 deletion phpunit.xml.dist
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,12 @@
bootstrap="vendor/autoload.php"
>
<php>
<ini name="display_errors" value="1" />
<ini name="error_reporting" value="-1" />
<ini name="memory_limit" value="-1" />
<server name="APP_ENV" value="test" force="true" />
<server name="SYMFONY_PHPUNIT_VERSION" value="9.5" />
<server name="SHELL_VERBOSITY" value="-1" />
<server name="SYMFONY_PHPUNIT_VERSION" value="9.6" />
<env name="SYMFONY_DEPRECATIONS_HELPER" value="max[indirect]=1" />
</php>

Expand Down
1 change: 1 addition & 0 deletions psalm.xml
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@

<stubs>
<file name=".psalm/stubs/igbinary.phpstub" />
<file name=".psalm/stubs/predis.phpstub" />
</stubs>

<plugins>
Expand Down
1 change: 0 additions & 1 deletion src/Bundles/Jwt/JwtTokenizer.php
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,6 @@ public function tokenize(string $subject, array $payload, TokenizerConfigInterfa
$jweConfig = $config->getJweConfig();

if ($jweConfig === null) {
/** @psalm-suppress InvalidArgument */
$jws = $this->jwsBuilderFactory->create([$jwsConfig->getAlgorithm()])
->withPayload(json_encode($payload, \JSON_THROW_ON_ERROR)) // @phan-suppress-current-line PhanPossiblyFalseTypeArgument
->addSignature($jwsConfig->getJwk(), [
Expand Down
1 change: 0 additions & 1 deletion src/Bundles/JwtFetcher/JwtFetcher.php
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,6 @@ public function fetch(array $requestData, FetcherConfigInterface $config): array
private function makeRequestJwt(array $payload, JwtFetcherConfig $config): string
{
$jwsConfig = $config->getRequestJwsConfig();
/** @psalm-suppress InvalidArgument */
$jws = $this->jwsBuilderFactory->create([$jwsConfig->getAlgorithm()])
->withPayload(json_encode($payload, \JSON_THROW_ON_ERROR)) // @phan-suppress-current-line PhanPossiblyFalseTypeArgument
->addSignature($jwsConfig->getJwk(), [
Expand Down
64 changes: 36 additions & 28 deletions src/Bundles/Redis/ClientManager.php
Original file line number Diff line number Diff line change
Expand Up @@ -12,13 +12,13 @@ final class ClientManager implements ClientManagerInterface
{
public function __construct(
private readonly string $key,
private readonly \Predis\ClientInterface|\Redis|\RedisCluster $redis,
private readonly \Predis\ClientInterface|\Redis|\RedisCluster|\Relay\Relay $redis,
private readonly ?LoggerInterface $logger = null,
) {}

public function exists(string $id, bool $touch = true): bool
{
$exists = (bool) $this->redis->hexists($this->key, $id);
$exists = (bool) $this->redis->hExists($this->key, $id);
if ($exists && $touch) {
$this->touch($id);
}
Expand All @@ -29,15 +29,15 @@ public function exists(string $id, bool $touch = true): bool
public function touch(string $id): void
{
try {
$this->redis->hset($this->key, $id, (string) time());
$this->redis->hSet($this->key, $id, (string) time());
} finally {
$this->logger?->debug('Key "'.$this->key.'": client '.$id.' touched.');
}
}

public function getLastAccess(string $id): \DateTimeImmutable
{
$timestamp = $this->redis->hget($this->key, $id);
$timestamp = $this->redis->hGet($this->key, $id);
if (!is_numeric($timestamp)) {
throw new \InvalidArgumentException('Unknown id specified: '.$id);
}
Expand All @@ -50,27 +50,25 @@ public function findInactiveSince(\DateTimeInterface $datetime): iterable
$inactiveIds = null;
$error = null;

$script = <<<'LUA'
local last_access = redis.call('hgetall', KEYS[1])
local ids = {}
for i = 1, #last_access, 2 do
if last_access[i + 1] <= KEYS[2] then
ids[#ids + 1] = last_access[i]
end
end
return ids
LUA;
$args = [$this->key, (string) $datetime->getTimestamp()];

try {
/** @psalm-suppress MixedAssignment */
$inactiveIds = $this->redis->eval(
<<<'LUA'
local last_access = redis.call('hgetall', KEYS[1])
local ids = {}
for i = 1, #last_access, 2 do
if last_access[i + 1] <= KEYS[2] then
ids[#ids + 1] = last_access[i]
end
end
return ids
LUA,
[
$this->key,
$datetime->getTimestamp(),
],
2,
);
/** @psalm-suppress InvalidArgument, MixedAssignment */
$inactiveIds = $this->redis instanceof \Predis\ClientInterface
? $this->redis->eval($script, \count($args), ...$args)
: $this->redis->eval($script, $args, \count($args));
} catch (\Throwable $exception) {
$error = $exception->getMessage();
}
Expand All @@ -91,7 +89,7 @@ public function findInactiveSince(\DateTimeInterface $datetime): iterable

public function findOldest(): ?string
{
$ids = $this->redis->hkeys($this->key);
$ids = $this->redis->hKeys($this->key);

if (!\is_array($ids) || empty($ids)) {
return null;
Expand All @@ -102,15 +100,24 @@ public function findOldest(): ?string
return (string) reset($ids);
}

/**
* @psalm-suppress InvalidArgument, MixedAssignment
*/
public function remove(string ...$ids): int
{
if (empty($ids)) {
return 0;
}

try {
/** @psalm-suppress InvalidArgument, InvalidCast */
return (int) $this->redis->hdel($this->key, ...$ids); // @phan-suppress-current-line PhanParamTooManyUnpack, PhanTypeMismatchArgumentProbablyReal
if ($this->redis instanceof \Predis\ClientInterface) {
/** @psalm-suppress RedundantCast */
return (int) $this->redis->hDel($this->key, $ids);
}

$result = $this->redis->hDel($this->key, ...$ids);

return \is_int($result) ? $result : 0;
} finally {
$this->logger?->debug('Key "'.$this->key.'": clients removed: '.implode(', ', $ids).'.');
}
Expand All @@ -119,7 +126,8 @@ public function remove(string ...$ids): int
private function getRedisLastError(): ?string
{
$error = null;
if ($this->redis instanceof \Redis || $this->redis instanceof \RedisCluster) {
if ($this->redis instanceof \Redis || $this->redis instanceof \RedisCluster || $this->redis instanceof \Relay\Relay) {
/** @var string|null $error */
$error = $this->redis->getLastError();
$this->redis->clearLastError();
}
Expand Down
23 changes: 16 additions & 7 deletions src/Bundles/Redis/FeatureConfigManagerFactory.php
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ final class FeatureConfigManagerFactory
{
public function __invoke(
string $key,
\Predis\ClientInterface|\Redis|\RedisCluster $redis,
\Predis\ClientInterface|\Redis|\RedisCluster|\Relay\Relay $redis,
FeatureConfigMarshallerInterface $marshaller,
FeatureConfigEncryptorInterface $encryptor,
bool $required,
Expand All @@ -24,7 +24,7 @@ public function __invoke(
return new class($key, $redis, $marshaller, $encryptor, $required, $logger) implements FeatureConfigManagerInterface {
public function __construct(
private readonly string $key,
private readonly \Predis\ClientInterface|\Redis|\RedisCluster $redis,
private readonly \Predis\ClientInterface|\Redis|\RedisCluster|\Relay\Relay $redis,
private readonly FeatureConfigMarshallerInterface $marshaller,
private readonly FeatureConfigEncryptorInterface $encryptor,
private readonly bool $required,
Expand All @@ -43,7 +43,7 @@ public function isRequired(): bool

public function exists(string $id): bool
{
return (bool) $this->redis->hexists($this->key, $id);
return (bool) $this->redis->hExists($this->key, $id);
}

public function persist(string $id, FeatureConfigInterface $config, string $secret): void
Expand All @@ -52,7 +52,7 @@ public function persist(string $id, FeatureConfigInterface $config, string $secr
$value = $this->encryptor->encrypt($value, $secret);

try {
$this->redis->hset($this->key, $id, $value);
$this->redis->hSet($this->key, $id, $value);
} finally {
$this->logger?->debug('Key "'.$this->key.'": config '.$id.' persisted.');
}
Expand All @@ -61,7 +61,7 @@ public function persist(string $id, FeatureConfigInterface $config, string $secr
public function find(string $id, string $secret): ?FeatureConfigInterface
{
/** @var string|false $value */
$value = $this->redis->hget($this->key, $id);
$value = $this->redis->hGet($this->key, $id);
if (\is_string($value)) {
return $this->marshaller->unmarshall(
$this->encryptor->decrypt($value, $secret),
Expand All @@ -71,15 +71,24 @@ public function find(string $id, string $secret): ?FeatureConfigInterface
return null;
}

/**
* @psalm-suppress InvalidArgument, MixedAssignment
*/
public function remove(string ...$ids): int
{
if (empty($ids)) {
return 0;
}

try {
/** @psalm-suppress InvalidArgument, InvalidCast */
return (int) $this->redis->hdel($this->key, ...$ids); // @phan-suppress-current-line PhanParamTooManyUnpack, PhanTypeMismatchArgumentProbablyReal
if ($this->redis instanceof \Predis\ClientInterface) {
/** @psalm-suppress RedundantCast */
return (int) $this->redis->hDel($this->key, $ids);
}

$result = $this->redis->hDel($this->key, ...$ids);

return \is_int($result) ? $result : 0;
} finally {
$this->logger?->debug('Key "'.$this->key.'": configs removed: '.implode(', ', $ids).'.');
}
Expand Down
18 changes: 9 additions & 9 deletions src/Bundles/Redis/Tests/ClientManagerTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ public function testExists(bool $touch, bool $exists, bool $expected): void
$redis = $this->createMock(\Redis::class);
$redis
->expects(self::once())
->method('hexists')
->method('hExists')
->with($key, $id)
->willReturn($exists)
;
Expand All @@ -39,7 +39,7 @@ public function testExists(bool $touch, bool $exists, bool $expected): void
if ($touch) {
$redis
->expects(self::once())
->method('hset')
->method('hSet')
->with($key, $id, self::stringContains(substr((string) time(), 0, -2)))
;

Expand All @@ -50,7 +50,7 @@ public function testExists(bool $touch, bool $exists, bool $expected): void
} else {
$redis
->expects(self::never())
->method('hset')
->method('hSet')
;

$logger
Expand Down Expand Up @@ -93,7 +93,7 @@ public function testGetLastAccessUnknownException(): void
$redis = $this->createMock(\Redis::class);
$redis
->expects(self::once())
->method('hget')
->method('hGet')
->with($key, $id)
->willReturn(false)
;
Expand All @@ -114,7 +114,7 @@ public function testGetLastAccess(): void
$redis = $this->createMock(\Redis::class);
$redis
->expects(self::once())
->method('hget')
->method('hGet')
->with($key, $id)
->willReturn('1609495200')
;
Expand Down Expand Up @@ -180,7 +180,7 @@ public function testFindOldest(array $keys, ?string $expected): void
$redis = $this->createMock(\Redis::class);
$redis
->expects(self::once())
->method('hkeys')
->method('hKeys')
->willReturn($keys)
;

Expand All @@ -207,7 +207,7 @@ public function provideRemoveCases(): iterable
$redis = $this->createMock(\Redis::class);
$redis
->expects(self::once())
->method('hdel')
->method('hDel')
->with('key', '1', '2')
->willReturn(2)
;
Expand All @@ -232,7 +232,7 @@ public function provideRemoveCases(): iterable
$redis = $this->createMock(\Redis::class);
$redis
->expects(self::once())
->method('hdel')
->method('hDel')
->with('key', '1', '2')
->willReturn(0)
;
Expand All @@ -257,7 +257,7 @@ public function provideRemoveCases(): iterable
$redis = $this->createMock(\Redis::class);
$redis
->expects(self::never())
->method('hdel')
->method('hDel')
;

return $redis;
Expand Down
Loading

0 comments on commit 6ca7950

Please sign in to comment.