diff --git a/.github/workflows/rector-cs.yml b/.github/workflows/rector-cs.yml new file mode 100644 index 0000000..9ecb99e --- /dev/null +++ b/.github/workflows/rector-cs.yml @@ -0,0 +1,27 @@ +name: Rector + PHP CS Fixer + +on: + pull_request: + paths: + - 'migrations/**' + - 'src/**' + - 'tests/**' + - 'rector.php' + - '.php-cs-fixer.dist.php' + - 'composer.json' + - '.github/workflows/rector-cs.yml' + +permissions: + contents: read + +concurrency: + group: ${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: true + +jobs: + rector-cs: + permissions: + contents: write # Required to commit formatting fixes back to the PR + 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 c017f76..0000000 --- a/.github/workflows/rector.yml +++ /dev/null @@ -1,23 +0,0 @@ -on: - pull_request: - paths-ignore: - - 'docs/**' - - 'README.md' - - 'CHANGELOG.md' - - '.gitignore' - - '.gitattributes' - - 'infection.json.dist' - - 'psalm.xml' - -name: rector - -permissions: - contents: read -jobs: - rector: - uses: yiisoft/actions/.github/workflows/rector.yml@master - with: - os: >- - ['ubuntu-latest'] - php: >- - ['8.3'] diff --git a/.php-cs-fixer.dist.php b/.php-cs-fixer.dist.php new file mode 100644 index 0000000..8cab8ec --- /dev/null +++ b/.php-cs-fixer.dist.php @@ -0,0 +1,23 @@ +in([ + __DIR__ . '/migrations', + __DIR__ . '/src', + __DIR__ . '/tests', +]); + +return ConfigBuilder::build() + ->setCacheFile(__DIR__ . '/runtime/.php-cs-fixer.cache') + ->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 c221a8a..74cbad4 100644 --- a/composer.json +++ b/composer.json @@ -38,6 +38,7 @@ "yiisoft/queue": "dev-master" }, "require-dev": { + "friendsofphp/php-cs-fixer": "^3.95", "maglnet/composer-require-checker": "^4.7", "phpunit/phpunit": "^10.5", "rector/rector": "^2.0.3", @@ -45,6 +46,7 @@ "spatie/phpunit-watcher": "^1.23", "vimeo/psalm": "^5.20", "yiisoft/cache": "^3.2", + "yiisoft/code-style": "^1.1", "yiisoft/db-sqlite": "^2.0" }, "suggest": { @@ -78,6 +80,8 @@ }, "scripts": { "test": "phpunit --testdox", - "test-watch": "phpunit-watcher watch" + "test-watch": "phpunit-watcher watch", + "rector": "rector", + "cs-fix": "php-cs-fixer fix" } } diff --git a/rector.php b/rector.php index 137e59d..f6ef473 100644 --- a/rector.php +++ b/rector.php @@ -2,30 +2,16 @@ declare(strict_types=1); -use Rector\CodeQuality\Rector\Class_\InlineConstructorDefaultToPropertyRector; use Rector\Config\RectorConfig; -use Rector\Php74\Rector\Closure\ClosureToArrowFunctionRector; -use Rector\Php81\Rector\Property\ReadOnlyPropertyRector; -use Rector\Php81\Rector\FuncCall\NullToStrictStringFuncCallArgRector; -use Rector\Set\ValueObject\LevelSetList; +use Yiisoft\CodeStyle\Rector\SetList; -return static function (RectorConfig $rectorConfig): void { - $rectorConfig->paths([ +return RectorConfig::configure() + ->withPaths([ + __DIR__ . '/migrations', __DIR__ . '/src', __DIR__ . '/tests', + ]) + ->withPhpSets(php81: true) + ->withSets([ + SetList::YII_CORE, ]); - - // register a single rule - $rectorConfig->rule(InlineConstructorDefaultToPropertyRector::class); - - // define sets of rules - $rectorConfig->sets([ - LevelSetList::UP_TO_PHP_81, - ]); - - $rectorConfig->skip([ - ClosureToArrowFunctionRector::class, - ReadOnlyPropertyRector::class, - NullToStrictStringFuncCallArgRector::class, - ]); -}; diff --git a/runtime/.gitignore b/runtime/.gitignore new file mode 100644 index 0000000..d6b7ef3 --- /dev/null +++ b/runtime/.gitignore @@ -0,0 +1,2 @@ +* +!.gitignore diff --git a/src/Adapter.php b/src/Adapter.php index bfc167f..65841ff 100644 --- a/src/Adapter.php +++ b/src/Adapter.php @@ -19,6 +19,13 @@ use Yiisoft\Mutex\MutexFactoryInterface; use Yiisoft\Mutex\MutexInterface; use Yiisoft\Queue\Provider\QueueProviderInterface; +use Exception; + +use function is_array; +use function is_resource; +use function is_string; + +use const SORT_ASC; final class Adapter implements AdapterInterface { @@ -39,6 +46,11 @@ final class Adapter implements AdapterInterface */ public $deleteReleased = true; + /** + * @var int reserve time + */ + private $reserveTime = 0; + public function __construct( private ConnectionInterface $db, private MessageSerializerInterface $serializer, @@ -123,17 +135,42 @@ public function withChannel(BackedEnum|string $channel): self return $new; } + /** + * Listens queue and runs each job. + * + * @param callable(MessageInterface): bool $handlerCallback The handler which will handle messages. Returns false if it cannot continue handling messages + * @param bool $repeat whether to continue listening when queue is empty. + * @param non-negative-int $timeout number of seconds to sleep before next iteration. + */ + public function run(callable $handlerCallback, bool $repeat, int $timeout = 0): void + { + while ($this->loop->canContinue()) { + if ($payload = $this->reserve()) { + if ($handlerCallback($this->serializer->unserialize($payload['job']))) { + $this->release($payload); + } + continue; + } + if (!$repeat) { + break; + } + if ($timeout > 0) { + sleep($timeout); + } + } + } + /** * Takes one message from waiting list and reserves it for handling. * - * @throws \Exception in case it hasn't waited the lock + * @throws Exception in case it hasn't waited the lock * @return array|null payload */ - protected function reserve(): array|null + protected function reserve(): ?array { // TWK TODO what is useMaster in Yii3 return $this->db->useMaster(function () { if (!$this->mutex->acquire($this->mutexTimeout)) { - throw new \Exception('Has not waited the lock.'); + throw new Exception('Has not waited the lock.'); } try { @@ -182,13 +219,13 @@ protected function release($payload): void if ($this->deleteReleased) { $this->db->createCommand()->delete( $this->tableName, - ['id' => $payload['id']] + ['id' => $payload['id']], )->execute(); } else { $this->db->createCommand()->update( $this->tableName, ['done_at' => time()], - ['id' => $payload['id']] + ['id' => $payload['id']], )->execute(); } } @@ -205,38 +242,8 @@ private function moveExpired(): void ['reserved_at' => null], '[[reserved_at]] < :time - [[ttr]] and [[reserved_at]] is not null and [[done_at]] is null', null, - [':time' => $this->reserveTime] + [':time' => $this->reserveTime], )->execute(); } } - - /** - * @var int reserve time - */ - private $reserveTime = 0; - - /** - * Listens queue and runs each job. - * - * @param callable(MessageInterface): bool $handlerCallback The handler which will handle messages. Returns false if it cannot continue handling messages - * @param bool $repeat whether to continue listening when queue is empty. - * @param non-negative-int $timeout number of seconds to sleep before next iteration. - */ - public function run(callable $handlerCallback, bool $repeat, int $timeout = 0): void - { - while ($this->loop->canContinue()) { - if ($payload = $this->reserve()) { - if ($handlerCallback($this->serializer->unserialize($payload['job']))) { - $this->release($payload); - } - continue; - } - if (!$repeat) { - break; - } - if ($timeout > 0) { - sleep($timeout); - } - } - } } diff --git a/tests/AdapterTest.php b/tests/AdapterTest.php index 211c7cc..22c2a81 100644 --- a/tests/AdapterTest.php +++ b/tests/AdapterTest.php @@ -18,6 +18,8 @@ use Yiisoft\Queue\Message\Serializer\MessageSerializerInterface; use Yiisoft\Queue\Provider\QueueProviderInterface; +use function in_array; + final class AdapterTest extends TestCase { public function testPushUsesMessageMetaAndReturnsIdEnvelope(): void