From 0e5b018c03c3397c74c93c4fa095d39be8140661 Mon Sep 17 00:00:00 2001 From: Sergei Predvoditelev Date: Fri, 10 Jul 2026 15:40:43 +0300 Subject: [PATCH 1/3] Improve code style --- .github/workflows/rector-cs.yml | 27 +++++++++++ .github/workflows/rector.yml | 31 ------------ .gitignore | 3 ++ .php-cs-fixer.dist.php | 22 +++++++++ .styleci.yml | 85 --------------------------------- composer.json | 9 +++- rector.php | 27 ++++------- 7 files changed, 70 insertions(+), 134 deletions(-) create mode 100644 .github/workflows/rector-cs.yml delete mode 100644 .github/workflows/rector.yml create mode 100644 .php-cs-fixer.dist.php delete mode 100644 .styleci.yml diff --git a/.github/workflows/rector-cs.yml b/.github/workflows/rector-cs.yml new file mode 100644 index 0000000..b0154f0 --- /dev/null +++ b/.github/workflows/rector-cs.yml @@ -0,0 +1,27 @@ +name: Rector + PHP CS Fixer + +on: + pull_request: + paths: + - 'config/**' + - 'src/**' + - 'tests/**' + - '.github/workflows/rector-cs.yml' + - 'composer.json' + - 'rector.php' + - '.php-cs-fixer.dist.php' + +permissions: + contents: read + +concurrency: + group: ${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: true + +jobs: + rector: + permissions: + contents: write # Required to commit automated Rector and CS fixes + uses: yiisoft/actions/.github/workflows/rector-cs.yml@master + with: + php: '8.1' diff --git a/.github/workflows/rector.yml b/.github/workflows/rector.yml deleted file mode 100644 index ce0a0e2..0000000 --- a/.github/workflows/rector.yml +++ /dev/null @@ -1,31 +0,0 @@ -on: - pull_request: - paths-ignore: - - 'docs/**' - - 'README.md' - - 'CHANGELOG.md' - - '.gitignore' - - '.gitattributes' - - 'infection.json.dist' - - 'psalm.xml' - -name: rector - -concurrency: - group: ${{ github.workflow }}-${{ github.ref }} - cancel-in-progress: true - -permissions: - contents: read - -jobs: - rector: - name: rector - runs-on: ubuntu-latest - steps: - - name: Checkout - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7 - with: - persist-credentials: false - - name: Rector - run: make rector diff --git a/.gitignore b/.gitignore index 8c81ab2..4bae101 100644 --- a/.gitignore +++ b/.gitignore @@ -29,3 +29,6 @@ phpunit.phar # phpunit cache .phpunit.result.cache /phpunit.cache + +# PHP CS Fixer +/.php-cs-fixer.cache diff --git a/.php-cs-fixer.dist.php b/.php-cs-fixer.dist.php new file mode 100644 index 0000000..3cb52f9 --- /dev/null +++ b/.php-cs-fixer.dist.php @@ -0,0 +1,22 @@ +in([ + __DIR__ . '/config', + __DIR__ . '/src', + __DIR__ . '/tests', +]); + +return ConfigBuilder::build() + ->setRiskyAllowed(true) + ->setParallelConfig(ParallelConfigFactory::detect()) + ->setRules([ + '@Yiisoft/Core' => true, + '@Yiisoft/Core:risky' => true, + ]) + ->setFinder($finder); diff --git a/.styleci.yml b/.styleci.yml deleted file mode 100644 index e121039..0000000 --- a/.styleci.yml +++ /dev/null @@ -1,85 +0,0 @@ -preset: psr12 -risky: true - -version: 8.2 - -finder: - exclude: - - docs - - vendor - -enabled: - - alpha_ordered_traits - - array_indentation - - array_push - - combine_consecutive_issets - - combine_consecutive_unsets - - combine_nested_dirname - - declare_strict_types - - dir_constant - - fully_qualified_strict_types - - function_to_constant - - hash_to_slash_comment - - is_null - - logical_operators - - magic_constant_casing - - magic_method_casing - - method_separation - - modernize_types_casting - - native_function_casing - - native_function_type_declaration_casing - - no_alias_functions - - no_empty_comment - - no_empty_phpdoc - - no_empty_statement - - no_extra_block_blank_lines - - no_short_bool_cast - - no_superfluous_elseif - - no_unneeded_control_parentheses - - no_unneeded_curly_braces - - no_unneeded_final_method - - no_unset_cast - - no_unused_imports - - no_unused_lambda_imports - - no_useless_else - - no_useless_return - - normalize_index_brace - - php_unit_dedicate_assert - - php_unit_dedicate_assert_internal_type - - php_unit_expectation - - php_unit_mock - - php_unit_mock_short_will_return - - php_unit_namespaced - - php_unit_no_expectation_annotation - - phpdoc_no_empty_return - - phpdoc_no_useless_inheritdoc - - phpdoc_order - - phpdoc_property - - phpdoc_scalar - - phpdoc_singular_inheritdoc - - phpdoc_trim - - phpdoc_trim_consecutive_blank_line_separation - - phpdoc_type_to_var - - phpdoc_types - - phpdoc_types_order - - print_to_echo - - regular_callable_call - - return_assignment - - self_accessor - - self_static_accessor - - set_type_to_cast - - short_array_syntax - - short_list_syntax - - simplified_if_return - - single_quote - - standardize_not_equals - - ternary_to_null_coalescing - - trailing_comma_in_multiline_array - - unalign_double_arrow - - unalign_equals - - empty_loop_body_braces - - integer_literal_case - - union_type_without_spaces - -disabled: - - function_declaration diff --git a/composer.json b/composer.json index b852981..1bf8eaa 100644 --- a/composer.json +++ b/composer.json @@ -37,12 +37,14 @@ "yiisoft/queue": "dev-master" }, "require-dev": { + "friendsofphp/php-cs-fixer": "^3.95", "maglnet/composer-require-checker": "^4.7", "phpunit/phpunit": "^10.5", - "rector/rector": "^1.0.3", + "rector/rector": "^2.5.5", "roave/infection-static-analysis-plugin": "^1.34", "spatie/phpunit-watcher": "^1.23", "vimeo/psalm": "^5.16", + "yiisoft/code-style": "^1.0", "yiisoft/test-support": "^3.0" }, "autoload": { @@ -69,5 +71,10 @@ "infection/extension-installer": true, "composer/package-versions-deprecated": true } + }, + "scripts": { + "cs-fix": "php-cs-fixer fix", + "psalm": "psalm", + "rector": "rector" } } diff --git a/rector.php b/rector.php index 90fea6b..54537c2 100644 --- a/rector.php +++ b/rector.php @@ -4,28 +4,21 @@ use Rector\CodeQuality\Rector\Class_\InlineConstructorDefaultToPropertyRector; use Rector\Config\RectorConfig; -use Rector\Php73\Rector\FuncCall\JsonThrowOnErrorRector; use Rector\Php74\Rector\Closure\ClosureToArrowFunctionRector; use Rector\Php81\Rector\Property\ReadOnlyPropertyRector; -use Rector\Set\ValueObject\LevelSetList; +use Rector\Php81\Rector\FuncCall\NullToStrictStringFuncCallArgRector; -return static function (RectorConfig $rectorConfig): void { - $rectorConfig->paths([ +return RectorConfig::configure() + ->withPaths([ __DIR__ . '/src', __DIR__ . '/tests', - ]); - - // register a single rule - $rectorConfig->rule(InlineConstructorDefaultToPropertyRector::class); - - // define sets of rules - $rectorConfig->sets([ - LevelSetList::UP_TO_PHP_81, - ]); - - $rectorConfig->skip([ + ]) + ->withPhpSets(php81: true) + ->withRules([ + InlineConstructorDefaultToPropertyRector::class, + ]) + ->withSkip([ ClosureToArrowFunctionRector::class, - JsonThrowOnErrorRector::class, ReadOnlyPropertyRector::class, + NullToStrictStringFuncCallArgRector::class, ]); -}; From b2a8b3e50b3d32b59f939ad4a51e23ecfa3ddc49 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Fri, 10 Jul 2026 12:41:28 +0000 Subject: [PATCH 2/3] Apply PHP CS Fixer and Rector changes (CI) --- config/di.php | 4 +- src/Adapter.php | 17 +++--- src/Exception/NotConnectedRedisException.php | 3 +- src/Message/Message.php | 2 +- src/QueueProvider.php | 59 +++++++++++-------- src/Reserve.php | 7 +-- tests/Integration/QueueProviderTest.php | 5 +- tests/Integration/QueueTest.php | 22 +++---- .../Support/ExtendedSimpleMessageHandler.php | 8 ++- tests/Support/FileHelper.php | 2 + tests/Support/IntegrationTestCase.php | 16 +++-- tests/Unit/QueueProviderTest.php | 17 +++--- tests/Unit/QueueTest.php | 4 +- 13 files changed, 97 insertions(+), 69 deletions(-) diff --git a/config/di.php b/config/di.php index 4a3459e..c1e4ab6 100644 --- a/config/di.php +++ b/config/di.php @@ -1,7 +1,9 @@ Yiisoft\Queue\Redis\QueueProvider::class, + QueueProviderInterface::class => QueueProvider::class, ]; diff --git a/src/Adapter.php b/src/Adapter.php index 707b5a0..2fbc640 100644 --- a/src/Adapter.php +++ b/src/Adapter.php @@ -12,15 +12,16 @@ use Yiisoft\Queue\Message\Serializer\MessageSerializerInterface; use Yiisoft\Queue\MessageStatus; +use function is_string; + final class Adapter implements AdapterInterface { public function __construct( private QueueProviderInterface $provider, private MessageSerializerInterface $serializer, private LoopInterface $loop, - private int $timeout = 3 - ) { - } + private int $timeout = 3, + ) {} public function runExisting(callable $handlerCallback): void { @@ -90,6 +91,11 @@ public function withChannel(BackedEnum|string $channel): AdapterInterface return $adapter; } + public function getChannel(): string + { + return $this->provider->getChannelName(); + } + private function reserve(): ?IdEnvelope { $reserve = $this->provider->reserve($this->timeout); @@ -100,9 +106,4 @@ private function reserve(): ?IdEnvelope $message = $this->serializer->unserialize($reserve->payload); return new IdEnvelope($message, $reserve->id); } - - public function getChannel(): string - { - return $this->provider->getChannelName(); - } } diff --git a/src/Exception/NotConnectedRedisException.php b/src/Exception/NotConnectedRedisException.php index 60e45ad..c91ebf8 100644 --- a/src/Exception/NotConnectedRedisException.php +++ b/src/Exception/NotConnectedRedisException.php @@ -5,8 +5,9 @@ namespace Yiisoft\Queue\Redis\Exception; use Yiisoft\FriendlyException\FriendlyExceptionInterface; +use RuntimeException; -class NotConnectedRedisException extends \RuntimeException implements FriendlyExceptionInterface +class NotConnectedRedisException extends RuntimeException implements FriendlyExceptionInterface { public function getName(): string { diff --git a/src/Message/Message.php b/src/Message/Message.php index c7bbd03..ae30867 100644 --- a/src/Message/Message.php +++ b/src/Message/Message.php @@ -21,7 +21,7 @@ public function __construct( private string $handlerName, private bool|int|float|string|array|null $payload, private array $meta, - private int $delay = 0 //delay in seconds + private int $delay = 0, //delay in seconds ) { if ($this->delay > 0) { $this->meta[DelayEnvelope::META_DELAY_SECONDS] = $delay; diff --git a/src/QueueProvider.php b/src/QueueProvider.php index 94fe4fa..65de2dc 100644 --- a/src/QueueProvider.php +++ b/src/QueueProvider.php @@ -7,6 +7,14 @@ use RedisException; use Yiisoft\Queue\Message\DelayEnvelope; use Yiisoft\Queue\Redis\Exception\NotConnectedRedisException; +use Redis; +use RuntimeException; + +use function is_array; +use function is_bool; +use function is_float; +use function is_int; +use function is_string; class QueueProvider implements QueueProviderInterface { @@ -21,10 +29,9 @@ class QueueProvider implements QueueProviderInterface * @throws RedisException */ public function __construct( - private \Redis $redis, //redis connection, - private string $channelName = self::DEFAULT_CHANNEL_NAME - ) { - } + private Redis $redis, //redis connection, + private string $channelName = self::DEFAULT_CHANNEL_NAME, + ) {} /** * @param array $meta @@ -114,22 +121,6 @@ public function delete(string $id): void $this->redis->hdel("$this->channelName.attempts", $id); } - /** - * @throws RedisException - */ - private function moveExpired(string $from): void - { - $now = time(); - $expired = $this->redis->zrevrangebyscore($from, (string) $now, '-inf'); - if (is_array($expired)) { - $this->redis->zremrangebyscore($from, '-inf', (string) $now); - /** @var string $id */ - foreach ($expired as $id) { - $this->redis->rpush("$this->channelName.waiting", $id); - } - } - } - /** * @throws RedisException */ @@ -141,7 +132,7 @@ public function getId(): int return $id; } - throw new \RuntimeException('Unable to get message id.'); + throw new RuntimeException('Unable to get message id.'); } /** @@ -164,18 +155,34 @@ public function withChannelName(string $channelName): QueueProviderInterface return new self($this->redis, $channelName); } + public function getChannelName(): string + { + return $this->channelName; + } + /** * @throws RedisException */ - private function checkConnection(): void + private function moveExpired(string $from): void { - if (!$this->redis->isConnected()) { - throw new NotConnectedRedisException('Redis is not connected.'); + $now = time(); + $expired = $this->redis->zrevrangebyscore($from, (string) $now, '-inf'); + if (is_array($expired)) { + $this->redis->zremrangebyscore($from, '-inf', (string) $now); + /** @var string $id */ + foreach ($expired as $id) { + $this->redis->rpush("$this->channelName.waiting", $id); + } } } - public function getChannelName(): string + /** + * @throws RedisException + */ + private function checkConnection(): void { - return $this->channelName; + if (!$this->redis->isConnected()) { + throw new NotConnectedRedisException('Redis is not connected.'); + } } } diff --git a/src/Reserve.php b/src/Reserve.php index cf40d5e..2e15c8f 100644 --- a/src/Reserve.php +++ b/src/Reserve.php @@ -7,8 +7,7 @@ final class Reserve { public function __construct( - readonly int $id, - readonly string $payload - ) { - } + public readonly int $id, + public readonly string $payload, + ) {} } diff --git a/tests/Integration/QueueProviderTest.php b/tests/Integration/QueueProviderTest.php index 1b529b5..523302e 100644 --- a/tests/Integration/QueueProviderTest.php +++ b/tests/Integration/QueueProviderTest.php @@ -8,6 +8,9 @@ use Yiisoft\Queue\Redis\Message\Message; use Yiisoft\Queue\Redis\QueueProvider; use Yiisoft\Queue\Redis\QueueProviderInterface; +use Redis; + +use const JSON_THROW_ON_ERROR; class QueueProviderTest extends TestCase { @@ -22,7 +25,7 @@ public function testGetId(QueueProvider $provider) public function test__construct(): QueueProvider { - $redis = new \Redis(); + $redis = new Redis(); $connected = $redis->connect('redis'); $this->assertTrue($connected); $provider = new QueueProvider($redis, 'test'); diff --git a/tests/Integration/QueueTest.php b/tests/Integration/QueueTest.php index 7a9b44b..b323303 100644 --- a/tests/Integration/QueueTest.php +++ b/tests/Integration/QueueTest.php @@ -19,6 +19,8 @@ use Yiisoft\Queue\Redis\Tests\Support\FileHelper; use Yiisoft\Queue\Redis\Tests\Support\IntegrationTestCase; +use const PHP_INT_MAX; + class QueueTest extends IntegrationTestCase { public function testRunExisting() @@ -30,7 +32,7 @@ public function testRunExisting() $queue = $this->getDefaultQueue($this->getAdapter()); $queue->push( - new Message('ext-simple', ['file_name' => $fileName, 'payload' => ['time' => $time]]) + new Message('ext-simple', ['file_name' => $fileName, 'payload' => ['time' => $time]]), ); self::assertNull($fileHelper->get($fileName)); @@ -79,7 +81,7 @@ public function testListen(): void $mockLoop->expects($this->exactly(3))->method('canContinue')->willReturn(true, true, false); $queueProvider = new QueueProvider( - $this->createConnection() + $this->createConnection(), ); $adapter = new Adapter( $queueProvider @@ -90,10 +92,10 @@ public function testListen(): void $queue = $this->getDefaultQueue($adapter); $queue->push( - new Message('ext-simple', ['file_name' => 'test-listen' . $time, 'payload' => ['time' => $time]]) + new Message('ext-simple', ['file_name' => 'test-listen' . $time, 'payload' => ['time' => $time]]), ); $queue->push( - new Message('ext-simple', ['file_name' => 'test-listen' . $time, 'payload' => ['time' => $time]]) + new Message('ext-simple', ['file_name' => 'test-listen' . $time, 'payload' => ['time' => $time]]), ); $queue->listen(); } @@ -104,17 +106,12 @@ public function testImmutable(): void $adapter = new Adapter( $queueProvider, $this->createMock(MessageSerializerInterface::class), - $this->createMock(LoopInterface::class) + $this->createMock(LoopInterface::class), ); self::assertNotSame($adapter, $adapter->withChannel('test')); } - private function getDefaultQueue(AdapterInterface $adapter): Queue - { - return $this->getQueueWithAdapter($adapter); - } - public function testAdapterStatusNotFound(): void { $adapter = $this->getAdapter(); @@ -147,4 +144,9 @@ public function testAdapterNullMessage() }); $this->assertTrue($notUseHandler); } + + private function getDefaultQueue(AdapterInterface $adapter): Queue + { + return $this->getQueueWithAdapter($adapter); + } } diff --git a/tests/Support/ExtendedSimpleMessageHandler.php b/tests/Support/ExtendedSimpleMessageHandler.php index 897cc75..acdd330 100644 --- a/tests/Support/ExtendedSimpleMessageHandler.php +++ b/tests/Support/ExtendedSimpleMessageHandler.php @@ -6,14 +6,16 @@ use Yiisoft\Queue\Message\MessageInterface; +use function is_array; +use function is_int; +use function is_string; + /** * Accepts any values from the queue and writes to the file */ final class ExtendedSimpleMessageHandler { - public function __construct(private FileHelper $fileHelper) - { - } + public function __construct(private FileHelper $fileHelper) {} public function handle(MessageInterface $message): void { diff --git a/tests/Support/FileHelper.php b/tests/Support/FileHelper.php index fbc32bb..0609167 100644 --- a/tests/Support/FileHelper.php +++ b/tests/Support/FileHelper.php @@ -6,6 +6,8 @@ use RuntimeException; +use function dirname; + final class FileHelper { /** diff --git a/tests/Support/IntegrationTestCase.php b/tests/Support/IntegrationTestCase.php index 60be450..404561c 100644 --- a/tests/Support/IntegrationTestCase.php +++ b/tests/Support/IntegrationTestCase.php @@ -27,18 +27,24 @@ use Yiisoft\Queue\Worker\Worker; use Yiisoft\Queue\Worker\WorkerInterface; use Yiisoft\Test\Support\Container\SimpleContainer; +use Redis; +use RuntimeException; + +use function is_array; +use function is_int; +use function is_string; /** * Test case for unit tests */ abstract class IntegrationTestCase extends TestCase { - protected Queue|null $queue = null; + public ?QueueProvider $queueProvider = null; + protected ?Queue $queue = null; protected ?WorkerInterface $worker = null; protected ?ContainerInterface $container = null; protected ?AdapterInterface $adapter = null; protected ?LoopInterface $loop = null; - public ?QueueProvider $queueProvider = null; protected function setUp(): void { @@ -98,7 +104,7 @@ protected function getMessageHandlers(): array && is_array($data['payload'] ?? null) && (is_int($data['payload']['time'] ?? null) || is_string($data['payload']['time'] ?? null)) ) { - throw new \RuntimeException((string) $data['payload']['time']); + throw new RuntimeException((string) $data['payload']['time']); } }, ]; @@ -166,9 +172,9 @@ protected function getQueueProvider(): QueueProvider ); } - protected function createConnection(): \Redis + protected function createConnection(): Redis { - $redis = new \Redis(); + $redis = new Redis(); $redis->connect('redis'); return $redis; } diff --git a/tests/Unit/QueueProviderTest.php b/tests/Unit/QueueProviderTest.php index a3bcdf9..7fd0bc8 100644 --- a/tests/Unit/QueueProviderTest.php +++ b/tests/Unit/QueueProviderTest.php @@ -8,12 +8,15 @@ use Yiisoft\Queue\Redis\Exception\NotConnectedRedisException; use Yiisoft\Queue\Redis\QueueProvider; use Yiisoft\Queue\Redis\QueueProviderInterface; +use PHPUnit\Framework\MockObject\Exception; +use Redis; +use RuntimeException; class QueueProviderTest extends TestCase { public function test__construct() { - $redis = $this->createMock(\Redis::class); + $redis = $this->createMock(Redis::class); $redis->method('isConnected')->willReturn(true); $provider = new QueueProvider($redis, 'test'); $this->assertInstanceOf(QueueProviderInterface::class, $provider); @@ -22,7 +25,7 @@ public function test__construct() /** * @depends test__construct - * @throws \PHPUnit\Framework\MockObject\Exception + * @throws Exception */ public function testImmutable(QueueProvider $provider): void { @@ -31,7 +34,7 @@ public function testImmutable(QueueProvider $provider): void public function testNotConnected(): void { - $redis = new \Redis(); + $redis = new Redis(); try { $provider = new QueueProvider($redis, 'test'); $provider->getId(); @@ -45,7 +48,7 @@ public function testNotConnected(): void public function testRedisException(): void { - $mock = $this->createMock(\Redis::class); + $mock = $this->createMock(Redis::class); $mock->method('brPop')->willReturn([1 => 1], [1 => '1']); $mock->method('isConnected')->willReturn(true); $mock->method('hget')->willReturn(null); @@ -58,17 +61,17 @@ public function testRedisException(): void $this->assertNull($provider->reserve()); $this->assertNull($provider->reserve()); - $this->expectException(\RuntimeException::class); + $this->expectException(RuntimeException::class); $provider->getId(); } /** - * @throws \PHPUnit\Framework\MockObject\Exception + * @throws Exception */ public function testGetChannelName(): void { // Тестируем значение по умолчанию - $redis = $this->createMock(\Redis::class); + $redis = $this->createMock(Redis::class); $redis->method('isConnected')->willReturn(true); $provider = new QueueProvider($redis); $this->assertEquals('yii-queue', $provider->getChannelName()); diff --git a/tests/Unit/QueueTest.php b/tests/Unit/QueueTest.php index cfa4712..d9e36f5 100644 --- a/tests/Unit/QueueTest.php +++ b/tests/Unit/QueueTest.php @@ -22,7 +22,7 @@ public function testImmutable(): void $adapter = new Adapter( $queueProvider, $this->createMock(MessageSerializerInterface::class), - $this->createMock(LoopInterface::class) + $this->createMock(LoopInterface::class), ); self::assertNotSame($adapter, $adapter->withChannel('test')); @@ -63,7 +63,7 @@ public function testGetChannel(): void $adapter = new Adapter( $queueProvider, $this->createMock(MessageSerializerInterface::class), - $this->createMock(LoopInterface::class) + $this->createMock(LoopInterface::class), ); $this->assertEquals($expectedChannelName, $adapter->getChannel()); From b4d2c03f2fc814e707008ec1c0211a7ca0e8bac8 Mon Sep 17 00:00:00 2001 From: Sergei Predvoditelev Date: Fri, 10 Jul 2026 15:42:18 +0300 Subject: [PATCH 3/3] fix --- config/di.php | 1 + rector.php | 1 + 2 files changed, 2 insertions(+) diff --git a/config/di.php b/config/di.php index c1e4ab6..83f150b 100644 --- a/config/di.php +++ b/config/di.php @@ -1,6 +1,7 @@ withPaths([ + __DIR__ . '/config', __DIR__ . '/src', __DIR__ . '/tests', ])