diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 1dbea39..f152594 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -51,6 +51,48 @@ jobs: extensions: redis-5.3.7 coverage: "${{ matrix.coverage }}" + - name: "Allow legacy Symfony branches in CI" + run: composer config audit.block-insecure false + + - name: "Install dependencies" + uses: ramsey/composer-install@v2 + env: + SYMFONY_REQUIRE: "${{ matrix.symfony-version }}" + + - name: "Run tests" + run: composer test + + current-tests: + runs-on: ubuntu-24.04 + name: "Current tests" + services: + redis: + image: redis:7-alpine + ports: + - 6379:6379 + strategy: + fail-fast: false + matrix: + php-version: + - '8.2' + - '8.3' + - '8.4' + symfony-version: + - '6.4.*' + - '7.4.*' + coverage: [ 'none' ] + steps: + - name: "Checkout" + uses: "actions/checkout@v2" + + - name: "Install PHP" + uses: "shivammathur/setup-php@v2" + with: + tools: flex + php-version: "${{ matrix.php-version }}" + extensions: redis + coverage: "${{ matrix.coverage }}" + - name: "Install dependencies" uses: ramsey/composer-install@v2 env: @@ -69,6 +111,9 @@ jobs: - '7.4' - '8.0' - '8.1' + - '8.2' + - '8.3' + - '8.4' steps: - name: "Checkout" uses: "actions/checkout@v2" @@ -77,6 +122,7 @@ jobs: uses: "shivammathur/setup-php@v2" with: php-version: ${{ matrix.php-version }} + tools: flex coverage: "none" - name: "Cache dependencies installed with composer" @@ -86,6 +132,9 @@ jobs: key: "php-${{ matrix.php-version }}-composer-locked-${{ hashFiles('composer.lock') }}" restore-keys: "php-${{ matrix.php-version }}-composer-locked-" + - name: "Allow legacy Symfony branches in CI" + run: composer config audit.block-insecure false + - name: "Install dependencies" uses: ramsey/composer-install@v2 diff --git a/.php-cs-fixer.dist.php b/.php-cs-fixer.dist.php index 94f7ec3..947804a 100644 --- a/.php-cs-fixer.dist.php +++ b/.php-cs-fixer.dist.php @@ -12,12 +12,18 @@ ->in(__DIR__ . '/tests') ; +if (PHP_VERSION_ID < 80000) { + $finder->notName('RedisPhp8.php'); +} + $rules = [ '@Symfony' => true, '@Symfony:risky' => true, // exceptions 'single_line_throw' => false, + 'modernize_strpos' => false, + 'trailing_comma_in_multiline' => false, // php file 'concat_space' => ['spacing' => 'one'], diff --git a/Cache/Redis.php b/Cache/Redis.php index 9a2a1a6..59de691 100644 --- a/Cache/Redis.php +++ b/Cache/Redis.php @@ -2,222 +2,17 @@ namespace Intaro\PinbaBundle\Cache; -use Intaro\PinbaBundle\Stopwatch\Stopwatch; +require_once __DIR__ . '/RedisStopwatchTrait.php'; + +$redisVersion = phpversion('redis'); +if (PHP_VERSION_ID >= 80000 && is_string($redisVersion) && version_compare($redisVersion, '6.0.0', '>=')) { + require_once __DIR__ . '/RedisPhp8.php'; + class_alias(RedisPhp8::class, RedisBase::class); +} else { + require_once __DIR__ . '/RedisLegacy.php'; + class_alias(RedisLegacy::class, RedisBase::class); +} -class Redis extends \Redis +class Redis extends RedisBase { - protected $stopwatch; - protected $stopwatchAdditionalTags = []; - protected $serverName; - protected int $expireMethodArgumentsCount; - - public function addWatchedServer( - $host, - $port = 6379, - $timeout = 5 - ): void { - $this->serverName = $host . (6379 == $port ? '' : ':' . $port); - - $this->pconnect($host, $port, $timeout); - - // для совместимости с Redis 5 - $expireMethodReflection = new \ReflectionMethod(\Redis::class, 'expire'); - $this->expireMethodArgumentsCount = $expireMethodReflection->getNumberOfParameters(); - if ($this->expireMethodArgumentsCount < 2 || $this->expireMethodArgumentsCount > 3) { - throw new \RuntimeException( - 'Redis::expire method has wrong number of arguments ' . $this->expireMethodArgumentsCount . ' instead of 2 or 3' - ); - } - } - - public function setStopwatch(Stopwatch $stopwatch): void - { - $this->stopwatch = $stopwatch; - } - - public function setStopwatchTags(array $tags): void - { - $this->stopwatchAdditionalTags = $tags; - } - - protected function getStopwatchEvent($methodName) - { - $tags = $this->stopwatchAdditionalTags; - $tags['group'] = 'redis::' . $methodName; - - if ($this->serverName) { - $tags['server'] = $this->serverName; - } - - return $this->stopwatch->start($tags); - } - - public function get($key) - { - if ($this->stopwatch) { - $e = $this->getStopwatchEvent('get'); - } - - $result = parent::get($key); - - if ($this->stopwatch) { - $e->stop(); - } - - return $result; - } - - public function mGet(array $keys) - { - if ($this->stopwatch) { - $e = $this->getStopwatchEvent('mGet'); - } - - $result = parent::mGet($keys); - - if ($this->stopwatch) { - $e->stop(); - } - - return $result; - } - - public function exists($key, ...$other_keys) - { - if ($this->stopwatch) { - $e = $this->getStopwatchEvent('exists'); - } - - $result = parent::exists($key, ...$other_keys); - - if ($this->stopwatch) { - $e->stop(); - } - - return $result; - } - - public function set($key, $var, $opts = null) - { - if ($this->stopwatch) { - $e = $this->getStopwatchEvent('set'); - } - - $result = parent::set($key, $var, $opts); - - if ($this->stopwatch) { - $e->stop(); - } - - return $result; - } - - public function setex($key, $var, $expire) - { - if ($this->stopwatch) { - $e = $this->getStopwatchEvent('setex'); - } - - $result = parent::setex($key, $var, $expire); - - if ($this->stopwatch) { - $e->stop(); - } - - return $result; - } - - public function mSetNx($v) - { - if ($this->stopwatch) { - $e = $this->getStopwatchEvent('mSetNx'); - } - - $result = parent::mSetNx($v); - - if ($this->stopwatch) { - $e->stop(); - } - - return $result; - } - - public function expire($key, $expire, $mode = null) - { - if ($this->stopwatch) { - $e = $this->getStopwatchEvent('expire'); - } - - if (2 === $this->expireMethodArgumentsCount) { - $result = parent::expire($key, $expire); - } else { - $result = parent::expire($key, $expire, $mode); - } - - if ($this->stopwatch) { - $e->stop(); - } - - return $result; - } - - public function exec() - { - if ($this->stopwatch) { - $e = $this->getStopwatchEvent('exec'); - } - - $result = parent::exec(); - - if ($this->stopwatch) { - $e->stop(); - } - - return $result; - } - - public function delete($key, ...$other_keys) - { - if ($this->stopwatch) { - $e = $this->getStopwatchEvent('delete'); - } - - $result = parent::delete($key, ...$other_keys); - - if ($this->stopwatch) { - $e->stop(); - } - - return $result; - } - - public function sMembers($key) - { - if ($this->stopwatch) { - $e = $this->getStopwatchEvent('sMembers'); - } - - $result = parent::sMembers($key); - - if ($this->stopwatch) { - $e->stop(); - } - - return $result; - } - - public function sAdd($tag, $id, ...$other_values) - { - if ($this->stopwatch) { - $e = $this->getStopwatchEvent('sAdd'); - } - - $result = parent::sAdd($tag, $id, ...$other_values); - - if ($this->stopwatch) { - $e->stop(); - } - - return $result; - } } diff --git a/Cache/RedisLegacy.php b/Cache/RedisLegacy.php new file mode 100644 index 0000000..5ac090f --- /dev/null +++ b/Cache/RedisLegacy.php @@ -0,0 +1,89 @@ +callWithStopwatch('get', function () use ($key) { + return parent::get($key); + }); + } + + public function mGet(array $keys) + { + return $this->callWithStopwatch('mGet', function () use ($keys) { + return parent::mGet($keys); + }); + } + + public function exists($key, ...$otherKeys) + { + return $this->callWithStopwatch('exists', function () use ($key, $otherKeys) { + return parent::exists($key, ...$otherKeys); + }); + } + + public function set($key, $value, $options = null) + { + return $this->callWithStopwatch('set', function () use ($key, $value, $options) { + return parent::set($key, $value, $options); + }); + } + + public function setex($key, $expire, $value) + { + return $this->callWithStopwatch('setex', function () use ($key, $expire, $value) { + return parent::setex($key, $expire, $value); + }); + } + + public function mSetNx($keyValues) + { + return $this->callWithStopwatch('mSetNx', function () use ($keyValues) { + return parent::mSetNx($keyValues); + }); + } + + public function expire($key, $expire, $mode = null) + { + return $this->callWithStopwatch('expire', function () use ($key, $expire, $mode) { + if (2 === $this->expireMethodArgumentsCount) { + return parent::expire($key, $expire); + } + + return parent::expire($key, $expire, $mode); + }); + } + + public function exec() + { + return $this->callWithStopwatch('exec', function () { + return parent::exec(); + }); + } + + public function delete($key, ...$otherKeys) + { + return $this->callWithStopwatch('delete', function () use ($key, $otherKeys) { + return parent::delete($key, ...$otherKeys); + }); + } + + public function sMembers($key) + { + return $this->callWithStopwatch('sMembers', function () use ($key) { + return parent::sMembers($key); + }); + } + + public function sAdd($key, $value, ...$otherValues) + { + return $this->callWithStopwatch('sAdd', function () use ($key, $value, $otherValues) { + return parent::sAdd($key, $value, ...$otherValues); + }); + } +} diff --git a/Cache/RedisPhp8.php b/Cache/RedisPhp8.php new file mode 100644 index 0000000..52755b6 --- /dev/null +++ b/Cache/RedisPhp8.php @@ -0,0 +1,89 @@ +callWithStopwatch('get', function () use ($key) { + return parent::get($key); + }); + } + + public function mGet(array $keys): \Redis|array|false + { + return $this->callWithStopwatch('mGet', function () use ($keys) { + return parent::mGet($keys); + }); + } + + public function exists(mixed $key, mixed ...$otherKeys): \Redis|int|bool + { + return $this->callWithStopwatch('exists', function () use ($key, $otherKeys) { + return parent::exists($key, ...$otherKeys); + }); + } + + public function set(string $key, mixed $value, mixed $options = null): \Redis|string|bool + { + return $this->callWithStopwatch('set', function () use ($key, $value, $options) { + return parent::set($key, $value, $options); + }); + } + + public function setex(string $key, int $expire, mixed $value) + { + return $this->callWithStopwatch('setex', function () use ($key, $expire, $value) { + return parent::setex($key, $expire, $value); + }); + } + + public function mSetNx(array $keyValues): \Redis|bool + { + return $this->callWithStopwatch('mSetNx', function () use ($keyValues) { + return parent::mSetNx($keyValues); + }); + } + + public function expire(string $key, int $expire, ?string $mode = null): \Redis|bool + { + return $this->callWithStopwatch('expire', function () use ($key, $expire, $mode) { + if (2 === $this->expireMethodArgumentsCount) { + return parent::expire($key, $expire); + } + + return parent::expire($key, $expire, $mode); + }); + } + + public function exec(): \Redis|array|false + { + return $this->callWithStopwatch('exec', function () { + return parent::exec(); + }); + } + + public function delete(array|string $key, string ...$otherKeys): \Redis|int|false + { + return $this->callWithStopwatch('delete', function () use ($key, $otherKeys) { + return parent::delete($key, ...$otherKeys); + }); + } + + public function sMembers(string $key): \Redis|array|false + { + return $this->callWithStopwatch('sMembers', function () use ($key) { + return parent::sMembers($key); + }); + } + + public function sAdd(string $key, mixed $value, mixed ...$otherValues): \Redis|int|false + { + return $this->callWithStopwatch('sAdd', function () use ($key, $value, $otherValues) { + return parent::sAdd($key, $value, ...$otherValues); + }); + } +} diff --git a/Cache/RedisStopwatchTrait.php b/Cache/RedisStopwatchTrait.php new file mode 100644 index 0000000..63f54b8 --- /dev/null +++ b/Cache/RedisStopwatchTrait.php @@ -0,0 +1,68 @@ +serverName = $host . (6379 == $port ? '' : ':' . $port); + + $this->pconnect($host, $port, $timeout); + + $expireMethodReflection = new \ReflectionMethod(\Redis::class, 'expire'); + $this->expireMethodArgumentsCount = $expireMethodReflection->getNumberOfParameters(); + if ($this->expireMethodArgumentsCount < 2 || $this->expireMethodArgumentsCount > 3) { + throw new \RuntimeException( + 'Redis::expire method has wrong number of arguments ' . $this->expireMethodArgumentsCount . ' instead of 2 or 3' + ); + } + } + + public function setStopwatch(Stopwatch $stopwatch): void + { + $this->stopwatch = $stopwatch; + } + + public function setStopwatchTags(array $tags): void + { + $this->stopwatchAdditionalTags = $tags; + } + + protected function getStopwatchEvent($methodName) + { + $tags = $this->stopwatchAdditionalTags; + $tags['group'] = 'redis::' . $methodName; + + if ($this->serverName) { + $tags['server'] = $this->serverName; + } + + return $this->stopwatch->start($tags); + } + + protected function callWithStopwatch($methodName, callable $callback) + { + if ($this->stopwatch) { + $event = $this->getStopwatchEvent($methodName); + } + + $result = $callback(); + + if ($this->stopwatch) { + $event->stop(); + } + + return $result; + } +} diff --git a/DependencyInjection/Configuration.php b/DependencyInjection/Configuration.php index 763f664..189b21d 100644 --- a/DependencyInjection/Configuration.php +++ b/DependencyInjection/Configuration.php @@ -12,9 +12,6 @@ */ class Configuration implements ConfigurationInterface { - /** - * {@inheritDoc} - */ public function getConfigTreeBuilder(): TreeBuilder { $treeBuilder = new TreeBuilder('intaro_pinba'); diff --git a/DependencyInjection/IntaroPinbaExtension.php b/DependencyInjection/IntaroPinbaExtension.php index 5c61f11..e997958 100644 --- a/DependencyInjection/IntaroPinbaExtension.php +++ b/DependencyInjection/IntaroPinbaExtension.php @@ -14,9 +14,6 @@ */ class IntaroPinbaExtension extends Extension { - /** - * {@inheritDoc} - */ public function load(array $configs, ContainerBuilder $container): void { $configuration = new Configuration(); diff --git a/Logger/DbalLogger.php b/Logger/DbalLogger.php index 2945a17..3ca15a7 100644 --- a/Logger/DbalLogger.php +++ b/Logger/DbalLogger.php @@ -19,16 +19,13 @@ class DbalLogger implements SQLLogger * * @param Stopwatch $stopwatch A Stopwatch instance */ - public function __construct(Stopwatch $stopwatch = null, $host = null) + public function __construct(?Stopwatch $stopwatch = null, $host = null) { $this->stopwatch = $stopwatch; $this->databaseHost = $host; } - /** - * {@inheritdoc} - */ - public function startQuery($sql, array $params = null, array $types = null): void + public function startQuery($sql, ?array $params = null, ?array $types = null): void { if (null !== $this->stopwatch) { $tags = [ @@ -45,9 +42,6 @@ public function startQuery($sql, array $params = null, array $types = null): voi } } - /** - * {@inheritdoc} - */ public function stopQuery(): void { if (null !== $this->stopwatchEvent) { diff --git a/Stopwatch/Stopwatch.php b/Stopwatch/Stopwatch.php index b0b48c8..bb156e6 100644 --- a/Stopwatch/Stopwatch.php +++ b/Stopwatch/Stopwatch.php @@ -33,8 +33,7 @@ function_exists('pinba_timer_start') && function_exists('pinba_timer_add') && function_exists('pinba_get_info') && function_exists('pinba_timers_get') - && function_exists('pinba_flush') - ; + && function_exists('pinba_flush'); } public function disable(): void diff --git a/Twig/TimedTwigEngine.php b/Twig/TimedTwigEngine.php index 812d62d..75deac9 100644 --- a/Twig/TimedTwigEngine.php +++ b/Twig/TimedTwigEngine.php @@ -43,9 +43,6 @@ public function __construct(\Twig\Environment $environment, TemplateNameParserIn $this->stopwatch = $stopwatch; } - /** - * {@inheritdoc} - */ public function render($name, array $parameters = []) { $e = $this->stopwatch->start([ diff --git a/Twig/TimedTwigEnvironment.php b/Twig/TimedTwigEnvironment.php index 3b09b6b..19187a2 100644 --- a/Twig/TimedTwigEnvironment.php +++ b/Twig/TimedTwigEnvironment.php @@ -17,9 +17,6 @@ public function __construct(LoaderInterface $loader, $options = [], ?Stopwatch $ $this->stopwatch = $stopwatch; } - /** - * {@inheritdoc} - */ public function render($name, array $context = []): string { if (null !== $this->stopwatch) { diff --git a/composer.json b/composer.json index fa880d5..55134b9 100644 --- a/composer.json +++ b/composer.json @@ -9,34 +9,34 @@ }], "require": { "php": ">=7.4", - "symfony/framework-bundle" : "^4.0|^5.0|^6.0", - "symfony/yaml": "^4.0|^5.0|^6.0" + "symfony/framework-bundle" : "^4.0|^5.0|^6.0|^7.0", + "symfony/yaml": "^4.0|^5.0|^6.0|^7.0" }, "require-dev": { "doctrine/dbal": "^2|^3|^4", - "nyholm/symfony-bundle-test": "^2.0", + "nyholm/symfony-bundle-test": "^2.0|^3.1", "phpunit/phpunit": "^8.5|^9.5", - "symfony/phpunit-bridge": "^5.0|^6.0", - "symfony/config": "^4.4|^5.0|^6.0", - "symfony/dependency-injection": "^4.4|^5.0|^6.0", - "symfony/event-dispatcher": "^4.4|^5.0|^6.0", - "symfony/error-handler": "^4.4|^5.0|^6.0", - "symfony/http-kernel": "^4.4|^5.0|^6.0", - "symfony/http-foundation": "^4.4|^5.0|^6.0", + "symfony/phpunit-bridge": "^5.0|^6.0|^7.0", + "symfony/config": "^4.4|^5.0|^6.0|^7.0", + "symfony/dependency-injection": "^4.4|^5.0|^6.0|^7.0", + "symfony/event-dispatcher": "^4.4|^5.0|^6.0|^7.0", + "symfony/error-handler": "^4.4|^5.0|^6.0|^7.0", + "symfony/http-kernel": "^4.4|^5.0|^6.0|^7.0", + "symfony/http-foundation": "^4.4|^5.0|^6.0|^7.0", "symfony/deprecation-contracts": "^1.1|^2.0|^3", "symfony/event-dispatcher-contracts": "^1.1|^2.0|^3", - "symfony/routing": "^4.4|^5.0|^6.0", - "symfony/finder": "^4.4|^5.0|^6.0", - "symfony/filesystem": "^4.4|^5.0|^6.0", - "symfony/templating": "^4.4|^5.0|^6.0", + "symfony/routing": "^4.4|^5.0|^6.0|^7.0", + "symfony/finder": "^4.4|^5.0|^6.0|^7.0", + "symfony/filesystem": "^4.4|^5.0|^6.0|^7.0", + "symfony/templating": "^4.4|^5.0|^6.0|^7.0", "symfony/translation-contracts": "^1.1|^2.0", - "symfony/var-exporter": "^4.4|^5.0|^6.0", - "symfony/var-dumper": "^4.4|^5.0|^6.0", + "symfony/var-exporter": "^4.4|^5.0|^6.0|^7.0", + "symfony/var-dumper": "^4.4|^5.0|^6.0|^7.0", "psr/log": "~1.0|^2|^3", - "friendsofphp/php-cs-fixer": "3.4", + "friendsofphp/php-cs-fixer": "^3.4", "twig/twig": "^v2.14 || ^3.0", - "symfony/twig-bundle": "^4.4|^5.0|^6.0", - "symfony/twig-bridge": "^4.4|^5.0|^6.0", + "symfony/twig-bundle": "^4.4|^5.0|^6.0|^7.0", + "symfony/twig-bridge": "^4.4|^5.0|^6.0|^7.0", "phpstan/phpstan": "^1.10" }, "autoload": { diff --git a/docker-compose.yml b/docker-compose.yml index b45b1b1..f144651 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -8,7 +8,7 @@ services: build: context: . args: - PHP_IMAGE_TAG: ${PHP_IMAGE_TAG:-7.4} + PHP_IMAGE_TAG: ${PHP_IMAGE_TAG:-8.2} environment: - REDIS_HOST=redis volumes: diff --git a/meta/phpstan/redis.stub b/meta/phpstan/redis.stub new file mode 100644 index 0000000..1cbd7c5 --- /dev/null +++ b/meta/phpstan/redis.stub @@ -0,0 +1,97 @@ + $tags + */ + public function setStopwatchTags(array $tags): void + { + } + + public function get(string $key) + { + } + + /** + * @param array $keys + * + * @return \Redis|array|false + */ + public function mGet(array $keys) + { + } + + public function exists($key, ...$otherKeys) + { + } + + public function set(string $key, $value, $options = null) + { + } + + public function setex(string $key, int $expire, $value) + { + } + + /** + * @param array $keyValues + */ + public function mSetNx(array $keyValues) + { + } + + public function expire(string $key, int $expire, ?string $mode = null) + { + } + + /** + * @return \Redis|array|false + */ + public function exec() + { + } + + /** + * @param array|string $key + */ + public function delete($key, string ...$otherKeys) + { + } + + /** + * @return \Redis|array|false + */ + public function sMembers(string $key) + { + } + + public function sAdd(string $key, $value, ...$otherValues) + { + } + + public function multi($value = null) + { + } + + public function select(int $dbindex) + { + } + + public function flushDB($async = null) + { + } +} diff --git a/phpstan.neon.dist b/phpstan.neon.dist index b5940fa..d68300b 100644 --- a/phpstan.neon.dist +++ b/phpstan.neon.dist @@ -8,7 +8,11 @@ parameters: - Logger/DbalLogger.php # return type for get and mGet methods - Cache/Redis.php + - Cache/RedisLegacy.php + - Cache/RedisPhp8.php - vendor/ + scanFiles: + - meta/phpstan/redis.stub bootstrapFiles: - meta/phpstan/stub.php ignoreErrors: