From 52dfb7d3627450f07354d6365de7a019fd272591 Mon Sep 17 00:00:00 2001 From: roxblnfk Date: Thu, 8 May 2025 11:01:59 +0400 Subject: [PATCH 01/32] Fix readme --- README.md | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 6a00836..f7b02b4 100644 --- a/README.md +++ b/README.md @@ -48,10 +48,10 @@ php artisan vendor:publish --provider='Spiral\RoadRunnerLaravel\ServiceProvider' ## Usage After package installation, you can download and install [RoadRunner][roadrunner] binary -using Composer script: +using [DLoad][dload] package: ```bash -composer get:rr +./vendor/bin/dload get rr ``` ### Basic Configuration (.rr.yaml) @@ -266,7 +266,7 @@ return [ Download Temporal binary for development purposes using the following command: ```bash -composer get:temporal +./vendor/bin/dload get temporal ``` To start the Temporal server, you can use the following command: @@ -378,6 +378,8 @@ MIT License (MIT). Please see [`LICENSE`](./LICENSE) for more information. [getcomposer]:https://getcomposer.org/download/ +[dload]:https://github.com/php-internal/dload + [roadrunner]:https://github.com/roadrunner-server/roadrunner [roadrunner_config]:https://github.com/roadrunner-server/roadrunner/blob/master/.rr.yaml From b5fedc0b869dad2503123daf5609084a4917a474 Mon Sep 17 00:00:00 2001 From: roxblnfk Date: Thu, 8 May 2025 15:39:47 +0400 Subject: [PATCH 02/32] Add CS fixer CI workflow --- .github/workflows/cs-fix.yml | 12 ++++++++++++ .php-cs-fixer.dist.php | 1 + 2 files changed, 13 insertions(+) create mode 100644 .github/workflows/cs-fix.yml diff --git a/.github/workflows/cs-fix.yml b/.github/workflows/cs-fix.yml new file mode 100644 index 0000000..0395b27 --- /dev/null +++ b/.github/workflows/cs-fix.yml @@ -0,0 +1,12 @@ +on: + push: + branches: + - '*' + +name: Fix Code Style + +jobs: + cs-fix: + permissions: + contents: write + uses: spiral/gh-actions/.github/workflows/cs-fix.yml@master diff --git a/.php-cs-fixer.dist.php b/.php-cs-fixer.dist.php index 9fd31dc..4d3b491 100644 --- a/.php-cs-fixer.dist.php +++ b/.php-cs-fixer.dist.php @@ -10,4 +10,5 @@ ->include(__DIR__ . '/config') ->include(__DIR__ . '/tests') ->include(__DIR__ . '/rector.php') + ->allowRisky(false) ->build(); From 1d6b4202d879879b17442ad5b499e00b37361e9d Mon Sep 17 00:00:00 2001 From: roxblnfk Date: Thu, 8 May 2025 16:02:56 +0400 Subject: [PATCH 03/32] Update metafiles --- .editorconfig | 2 +- .gitattributes | 7 +- Makefile | 2 +- composer.json | 194 +++++++++++++++++++++++------------------------ phpunit.xml.dist | 33 ++++---- 5 files changed, 120 insertions(+), 118 deletions(-) diff --git a/.editorconfig b/.editorconfig index 0dd272d..7fbab54 100644 --- a/.editorconfig +++ b/.editorconfig @@ -8,7 +8,7 @@ indent_style = space indent_size = 4 trim_trailing_whitespace = true -[*.{yml, yaml, sh, conf, neon*}] +[*.{yml,yaml,sh,conf,neon*}] indent_size = 2 [Makefile] diff --git a/.gitattributes b/.gitattributes index b35bb1f..6cf3d8c 100644 --- a/.gitattributes +++ b/.gitattributes @@ -1,9 +1,10 @@ * text=auto -/.github export-ignore +/.* export-ignore /tests export-ignore /[Dd]ocker* export-ignore -/.* export-ignore -/phpunit.xml* export-ignore +/*.xml export-ignore +/*.xml.dist export-ignore /phpstan.* export-ignore /Makefile export-ignore +/rector.php export-ignore diff --git a/Makefile b/Makefile index d10e286..d38f564 100644 --- a/Makefile +++ b/Makefile @@ -29,7 +29,7 @@ test: ## Execute php tests and linters docker-compose run $(RUN_APP_ARGS) app composer test test-cover: ## Execute php tests with coverage - docker-compose run --rm --user "0:0" -e 'XDEBUG_MODE=coverage' app sh -c 'docker-php-ext-enable xdebug && su $(shell whoami) -s /bin/sh -c "composer phpunit-cover"' + docker-compose run --rm --user "0:0" -e 'XDEBUG_MODE=coverage' app sh -c 'docker-php-ext-enable xdebug && su $(shell whoami) -s /bin/sh -c "composer phpunit:cover"' shell: ## Start shell into container with php docker-compose run $(RUN_APP_ARGS) app sh diff --git a/composer.json b/composer.json index 61e412b..8dfce17 100644 --- a/composer.json +++ b/composer.json @@ -1,103 +1,103 @@ { - "name": "roadrunner-php/laravel-bridge", - "type": "library", - "description": "Laravel integration for RoadRunner with support for HTTP, Jobs, gRPC, and Temporal plugins - going beyond Octane's capabilities", - "keywords": [ - "laravel", - "bridge", - "roadrunner", - "temporal", - "grpc", - "queue", - "cache", - "http" - ], - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/roadrunner-server" - } - ], - "license": "MIT", - "authors": [ - { - "name": "butschster", - "homepage": "https://github.com/butschster" + "name": "roadrunner-php/laravel-bridge", + "type": "library", + "description": "Laravel integration for RoadRunner with support for HTTP, Jobs, gRPC, and Temporal plugins - going beyond Octane's capabilities", + "keywords": [ + "laravel", + "bridge", + "roadrunner", + "temporal", + "grpc", + "queue", + "cache", + "http" + ], + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/roadrunner-server" + } + ], + "license": "MIT", + "authors": [ + { + "name": "butschster", + "homepage": "https://github.com/butschster" + }, + { + "name": "roxblnfk", + "homepage": "https://github.com/roxblnfk" + }, + { + "name": "tarampampam", + "homepage": "https://github.com/tarampampam" + } + ], + "require": { + "php": "^8.2", + "spiral/roadrunner-kv": "^4.0", + "spiral/roadrunner-jobs": "^4.0", + "spiral/roadrunner-grpc": "^3.5", + "laravel/octane": "^2.9", + "spiral/roadrunner-http": "^3.0", + "spiral/roadrunner-worker": "^3.0", + "temporal/sdk": "^2.0", + "internal/dload": "^1.1" }, - { - "name": "roxblnfk", - "homepage": "https://github.com/roxblnfk" + "require-dev": { + "laravel/framework": "^12.0", + "spiral/code-style": "^2.2.2", + "rector/rector": "^2.0", + "guzzlehttp/guzzle": "^7.0", + "mockery/mockery": "^1.6", + "phpstan/phpstan": "^2.1", + "phpunit/phpunit": "^10.0" }, - { - "name": "tarampampam", - "homepage": "https://github.com/tarampampam" - } - ], - "require": { - "php": "^8.2", - "spiral/roadrunner-kv": "^4.0", - "spiral/roadrunner-jobs": "^4.0", - "spiral/roadrunner-grpc": "^3.5", - "laravel/octane": "^2.9", - "spiral/roadrunner-http": "^3.0", - "spiral/roadrunner-worker": "^3.0", - "temporal/sdk": "^2.0", - "internal/dload": "^1.1" - }, - "require-dev": { - "laravel/framework": "^12.0", - "spiral/code-style": "^2.2.2", - "rector/rector": "^2.0", - "guzzlehttp/guzzle": "^7.0", - "mockery/mockery": "^1.6", - "phpstan/phpstan": "^2.1", - "phpunit/phpunit": "^10.0" - }, - "autoload": { - "psr-4": { - "Spiral\\RoadRunnerLaravel\\": "src/" - } - }, - "autoload-dev": { - "psr-4": { - "Spiral\\RoadRunnerLaravel\\Tests\\": "tests/" - } - }, - "bin": [ - "bin/rr-worker" - ], - "scripts": { - "get:rr": "dload get rr", - "get:temporal": "dload get temporal", - "get:protoc": "dload get protoc protoc-gen-php-grpc", - "cs-check": "vendor/bin/php-cs-fixer fix --dry-run", - "cs-fix": "vendor/bin/php-cs-fixer fix", - "refactor": "rector process --config=rector.php", - "refactor:ci": "rector process --config=rector.php --dry-run --ansi", - "phpunit": "@php ./vendor/bin/phpunit --no-coverage", - "phpunit-cover": "@php ./vendor/bin/phpunit", - "phpstan": "@php ./vendor/bin/phpstan analyze -c ./phpstan.neon.dist --no-progress --ansi", - "test": [ - "@phpstan", - "@phpunit" + "autoload": { + "psr-4": { + "Spiral\\RoadRunnerLaravel\\": "src/" + } + }, + "autoload-dev": { + "psr-4": { + "Spiral\\RoadRunnerLaravel\\Tests\\": "tests/" + } + }, + "bin": [ + "bin/rr-worker" ], - "test-cover": [ - "@phpstan", - "@phpunit-cover" - ] - }, - "extra": { - "laravel": { - "providers": [ - "Spiral\\RoadRunnerLaravel\\ServiceProvider", - "Spiral\\RoadRunnerLaravel\\Queue\\QueueServiceProvider", - "Spiral\\RoadRunnerLaravel\\Cache\\CacheServiceProvider", - "Spiral\\RoadRunnerLaravel\\Temporal\\TemporalServiceProvider" - ] + "scripts": { + "get:rr": "dload get rr", + "get:temporal": "dload get temporal", + "get:protoc": "dload get protoc protoc-gen-php-grpc", + "cs:check": "php-cs-fixer fix --dry-run", + "cs:fix": "php-cs-fixer fix", + "refactor": "rector process --config=rector.php", + "refactor:ci": "rector process --config=rector.php --dry-run --ansi", + "phpunit": "phpunit --no-coverage", + "phpunit-cover": "phpunit", + "phpstan": "phpstan analyze -c ./phpstan.neon.dist --no-progress --ansi", + "test": "phpunit --color=always --testdox", + "test:unit": "phpunit --color=always --testsuite=Unit", + "test:feat": "phpunit --color=always --testsuite=Feature", + "test-cover": [ + "phpstan", + "@putenv XDEBUG_MODE=coverage", + "phpunit --coverage-clover=runtime/phpunit/logs/clover.xml --color=always" + ] + }, + "extra": { + "laravel": { + "providers": [ + "Spiral\\RoadRunnerLaravel\\ServiceProvider", + "Spiral\\RoadRunnerLaravel\\Queue\\QueueServiceProvider", + "Spiral\\RoadRunnerLaravel\\Cache\\CacheServiceProvider", + "Spiral\\RoadRunnerLaravel\\Temporal\\TemporalServiceProvider" + ] + } + }, + "support": { + "issues": "https://github.com/roadrunner-php/laravel-bridge/issues", + "source": "https://github.com/roadrunner-php/laravel-bridge" } - }, - "support": { - "issues": "https://github.com/roadrunner-php/laravel-bridge/issues", - "source": "https://github.com/roadrunner-php/laravel-bridge" - } } diff --git a/phpunit.xml.dist b/phpunit.xml.dist index 1680c26..5cb43d4 100644 --- a/phpunit.xml.dist +++ b/phpunit.xml.dist @@ -2,8 +2,12 @@ - + cacheResultFile="runtime/phpunit/result.cache" + colors="true" + stderr="true" + displayDetailsOnTestsThatTriggerWarnings="true" + displayDetailsOnTestsThatTriggerDeprecations="true" +> ./tests/Unit @@ -12,25 +16,22 @@ ./tests/Feature - - + - ./src + src - ./vendor - ./tests + tests + + - - - - + + + - - - - - + + + From 39d328ef1a186018181671cbe64f5cc5083fc6be Mon Sep 17 00:00:00 2001 From: roxblnfk Date: Thu, 8 May 2025 16:44:01 +0400 Subject: [PATCH 04/32] First Readme fix iteration --- README.md | 39 ++++++++++++++++++++++++--------------- 1 file changed, 24 insertions(+), 15 deletions(-) diff --git a/README.md b/README.md index f7b02b4..801a17e 100644 --- a/README.md +++ b/README.md @@ -12,26 +12,31 @@ Easy way for connecting [RoadRunner][roadrunner] and [Laravel][laravel] applicat ## Why Use This Package? -Laravel provides the [Octane](https://laravel.com/docs/12.x/octane) package which partially supports RoadRunner as an -application server, but RoadRunner offers much more than just HTTP capabilities. It also includes Jobs, Temporal, gRPC, -and other plugins. +This library offers the most complete integration of Laravel with RoadRunner. +You should definitely use this package if: -![RoadRunner](https://github.com/user-attachments/assets/609d2e29-b6af-478b-b350-1d27b77ed6fb) +- You want to use not only HTTP but also other RoadRunner plugins like gRPC, Queue, KV, and others. +- You want to use Temporal. +- You want to control RoadRunner configuration. -> **Note:** There is an article that explains all the RoadRunner -> plugins: https://butschster.medium.com/roadrunner-an-underrated-powerhouse-for-php-applications-46410b0abc +![RoadRunner](https://github.com/user-attachments/assets/609d2e29-b6af-478b-b350-1d27b77ed6fb) -The main limitation of Octane is that it has a built-in worker only for the HTTP plugin and doesn't provide the ability -to create additional workers for other RoadRunner plugins, restricting its use to just the HTTP plugin. +> [!TIP] +> [There is an article][rr-plugins-article] that explains all the RoadRunner plugins: -Our **Laravel Bridge** solves this problem by taking a different approach: -1. We include `laravel/octane` in our package and reuse its **SDK** for clearing the state of Laravel applications -2. We add support for running and configuring multiple workers for different RoadRunner plugins -3. By reusing Octane's functionality for state clearing, we automatically support all third-party packages that are - compatible with Octane +Table of content: -**This way, you get the best of both worlds:** Octane's state management and RoadRunner's full plugin ecosystem. +- [Installation](#installation) +- [Usage](#usage) +- [RoadRunner Worker Configuration](#roadrunner-worker-configuration) +- [How It Works](#how-it-works) +- [Supported Plugins](#supported-plugins) + - [HTTP Plugin](#http-plugin) + - [Jobs (Queue) Plugin](#jobs-queue-plugin) + - [gRPC Plugin](#grpc-plugin) + - [Temporal](#temporal) +- [Custom Workers] ## Installation @@ -65,7 +70,7 @@ rpc: server: command: 'php vendor/bin/rr-worker start' - relay: pipes + relay: pipes http: address: 0.0.0.0:8080 @@ -389,3 +394,7 @@ MIT License (MIT). Please see [`LICENSE`](./LICENSE) for more information. [laravel_events]:https://laravel.com/docs/events [roadrunner-binary-releases]:https://github.com/roadrunner-server/roadrunner/releases + +[octane]https://laravel.com/docs/12.x/octane + +[rr-plugins-article]https://butschster.medium.com/roadrunner-an-underrated-powerhouse-for-php-applications-46410b0abc From d191d586f7c09eac9d6a9af277f9266dd6bb5186 Mon Sep 17 00:00:00 2001 From: roxblnfk Date: Thu, 8 May 2025 17:56:27 +0400 Subject: [PATCH 05/32] Second Readme fix iteration --- README.md | 126 ++++++++++++++++++++++++------------------------------ 1 file changed, 56 insertions(+), 70 deletions(-) diff --git a/README.md b/README.md index 801a17e..7fe36f6 100644 --- a/README.md +++ b/README.md @@ -22,44 +22,47 @@ You should definitely use this package if: ![RoadRunner](https://github.com/user-attachments/assets/609d2e29-b6af-478b-b350-1d27b77ed6fb) > [!TIP] -> [There is an article][rr-plugins-article] that explains all the RoadRunner plugins: +> [There is an article][rr-plugins-article] that explains all the RoadRunner plugins. +## Table of Contents -Table of content: - -- [Installation](#installation) -- [Usage](#usage) -- [RoadRunner Worker Configuration](#roadrunner-worker-configuration) +- [Get Started](#get-started) + - [Installation](#installation) + - [Configuration](#configuration) + - [Starting the Server](#starting-the-server) - [How It Works](#how-it-works) - [Supported Plugins](#supported-plugins) - [HTTP Plugin](#http-plugin) - [Jobs (Queue) Plugin](#jobs-queue-plugin) - [gRPC Plugin](#grpc-plugin) - [Temporal](#temporal) -- [Custom Workers] +- [Custom Workers](#custom-workers) +- [Support](#support) +- [License](#license) + +## Get Started + +### Installation -## Installation +First, install the Laravel Bridge package via Composer: -```shell script +```shell composer require roadrunner-php/laravel-bridge ``` -After that you can "publish" package configuration file (`./config/roadrunner.php`) using next command: +Publish the configuration file: -```shell script +```shell php artisan vendor:publish --provider='Spiral\RoadRunnerLaravel\ServiceProvider' --tag=config ``` -## Usage +Download and install RoadRunner binary using DLoad: -After package installation, you can download and install [RoadRunner][roadrunner] binary -using [DLoad][dload] package: - -```bash +```shell ./vendor/bin/dload get rr ``` -### Basic Configuration (.rr.yaml) +### Configuration Create a `.rr.yaml` configuration file in your project root: @@ -70,7 +73,6 @@ rpc: server: command: 'php vendor/bin/rr-worker start' - relay: pipes http: address: 0.0.0.0:8080 @@ -87,50 +89,27 @@ http: forbid: [ ".php" ] ``` -## RoadRunner Worker Configuration +### Starting the Server -You can configure workers in `config/roadrunner.php` file in the `workers` section: +Start the RoadRunner server with: -```php -use Spiral\RoadRunner\Environment\Mode; -use Spiral\RoadRunnerLaravel\Grpc\GrpcWorker; -use Spiral\RoadRunnerLaravel\Http\HttpWorker; -use Spiral\RoadRunnerLaravel\Queue\QueueWorker; -use Spiral\RoadRunnerLaravel\Temporal\TemporalWorker; - -return [ - // ... other configuration options ... - - 'workers' => [ - Mode::MODE_HTTP => HttpWorker::class, - Mode::MODE_JOBS => QueueWorker::class, - Mode::MODE_GRPC => GrpcWorker::class, - Mode::MODE_TEMPORAL => TemporalWorker::class, - ], -]; +```shell +./rr serve ``` -As you can see, there are several predefined workers for HTTP, Jobs, gRPC, and Temporal. Feel free to replace any of -them with your implementation if needed. Or create your own worker, for example, -for [Centrifugo](https://docs.roadrunner.dev/docs/plugins/centrifuge), [TCP](https://docs.roadrunner.dev/docs/plugins/tcp) -or any other plugin. - ## How It Works -In the server section of the RoadRunner config, we specify the command to start our worker: +RoadRunner creates a worker pool by executing the command specified in the server configuration: ```yaml server: command: 'php vendor/bin/rr-worker start' - relay: pipes ``` -When RoadRunner server creates a worker pool for a specific plugin, it exposes an environment variable `RR_MODE` that -indicates which plugin is being used. Our worker checks this variable to determine which Worker class should handle the -request based on the configuration in `roadrunner.php`. +When RoadRunner creates a worker pool for a specific plugin, it sets the `RR_MODE` environment variable to indicate which plugin is being used. +The Laravel Bridge checks this variable to determine which Worker class should handle the request based on your configuration. -The selected worker starts listening for requests from the RoadRunner server and handles them using the Octane worker, -which clears the application state after each task (request, command, etc.). +The selected worker then listens for requests from the RoadRunner server and handles them using the Octane worker, which clears the application state after each task (request, command, etc.). ## Supported Plugins @@ -153,16 +132,15 @@ http: forbid: [ ".php" ] ``` -> **Note:** Read more about the HTTP plugin in -> the [RoadRunner documentation][https://docs.roadrunner.dev/docs/http/http]. +> **Note:** Read more about the HTTP plugin in the [RoadRunner documentation](https://docs.roadrunner.dev/docs/http/http). ### Jobs (Queue) Plugin -The Queue plugin allows you to use RoadRunner as a queue driver for Laravel. +The Queue plugin allows you to use RoadRunner as a queue driver for Laravel without additional services like Redis or a database. #### Configuration -First, add the Queue Service Provider in your `config/app.php`: +First, add the Queue Service Provider in `config/app.php`: ```php 'providers' => [ @@ -171,7 +149,7 @@ First, add the Queue Service Provider in your `config/app.php`: ], ``` -Then, configure a new connection in your `config/queue.php`: +Then, configure a new connection in `config/queue.php`: ```php 'connections' => [ @@ -197,10 +175,7 @@ jobs: config: { } ``` -> **Note:** Read more about the Jobs plugin in -> the [RoadRunner documentation][https://docs.roadrunner.dev/docs/queues-and-jobs/overview-queues]. - -Don't forget to set the `QUEUE_CONNECTION` environment variable in your `.env` file: +Set the `QUEUE_CONNECTION` environment variable in your `.env` file: ```dotenv QUEUE_CONNECTION=roadrunner @@ -208,6 +183,9 @@ QUEUE_CONNECTION=roadrunner That's it! You can now dispatch jobs to the RoadRunner queue without any additional services like Redis or Database. +> [!TIP] +> Read more about the Jobs plugin in the [RoadRunner documentation](https://docs.roadrunner.dev/docs/queues-and-jobs/overview-queues). + ### gRPC Plugin The gRPC plugin enables serving gRPC services with your Laravel application. @@ -268,36 +246,44 @@ return [ ]; ``` -Download Temporal binary for development purposes using the following command: +Download Temporal binary for development: ```bash ./vendor/bin/dload get temporal ``` -To start the Temporal server, you can use the following command: +Start the Temporal dev server: ```bash ./temporal server start-dev --log-level error --color always ``` -#### Useful links +#### Useful Links - [PHP SDK on GitHub](https://github.com/temporalio/sdk-php) - [PHP SDK docs](https://docs.temporal.io/develop/php/) - [Code samples](https://github.com/temporalio/samples-php) - [Taxi service sample](https://github.com/butschster/podlodka-taxi-service) -## Starting RoadRunner Server +## Custom Workers -To start the RoadRunner server: +As you can see, there are several predefined workers for HTTP, Jobs, gRPC, and Temporal in `'workers'` section of `config/roadrunner.php`. +You can replace them with your own implementations if needed or create your own workers for other plugins, for example, +for [Centrifugo](https://docs.roadrunner.dev/docs/plugins/centrifuge), [TCP](https://docs.roadrunner.dev/docs/plugins/tcp) +or any other plugin. -```shell script -./rr serve +```php +return [ + 'workers' => [ + Mode::MODE_HTTP => HttpWorker::class, + Mode::MODE_JOBS => QueueWorker::class, + Mode::MODE_GRPC => GrpcWorker::class, + Mode::MODE_TEMPORAL => TemporalWorker::class, + ], +]; ``` -## Custom Workers - -You can create your own custom workers by implementing the `Spiral\RoadRunnerLaravel\WorkerInterface`: +To create your own custom workers start from implementing the `Spiral\RoadRunnerLaravel\WorkerInterface`: ```php namespace App\Workers; @@ -395,6 +381,6 @@ MIT License (MIT). Please see [`LICENSE`](./LICENSE) for more information. [roadrunner-binary-releases]:https://github.com/roadrunner-server/roadrunner/releases -[octane]https://laravel.com/docs/12.x/octane +[octane]:https://laravel.com/docs/12.x/octane -[rr-plugins-article]https://butschster.medium.com/roadrunner-an-underrated-powerhouse-for-php-applications-46410b0abc +[rr-plugins-article]:https://butschster.medium.com/roadrunner-an-underrated-powerhouse-for-php-applications-46410b0abc From e4525f806b3080a82a1194a245f8014f73102530 Mon Sep 17 00:00:00 2001 From: roxblnfk Date: Thu, 8 May 2025 18:05:20 +0400 Subject: [PATCH 06/32] Third Readme fix iteration --- README.md | 32 +++++++++++++++++++++++++------- 1 file changed, 25 insertions(+), 7 deletions(-) diff --git a/README.md b/README.md index 7fe36f6..92c533c 100644 --- a/README.md +++ b/README.md @@ -267,13 +267,19 @@ Start the Temporal dev server: ## Custom Workers -As you can see, there are several predefined workers for HTTP, Jobs, gRPC, and Temporal in `'workers'` section of `config/roadrunner.php`. -You can replace them with your own implementations if needed or create your own workers for other plugins, for example, -for [Centrifugo](https://docs.roadrunner.dev/docs/plugins/centrifuge), [TCP](https://docs.roadrunner.dev/docs/plugins/tcp) -or any other plugin. +The RoadRunner Laravel Bridge comes with several predefined workers for common plugins, +but you can easily create your own custom workers for any RoadRunner plugin. +This section explains how to create and register custom workers in your application. + +### Understanding Workers + +Workers are responsible for handling requests from the RoadRunner server and processing them in your Laravel application. +The predefined workers are configured in the `config/roadrunner.php` file: ```php return [ + // ... other configuration options ... + 'workers' => [ Mode::MODE_HTTP => HttpWorker::class, Mode::MODE_JOBS => QueueWorker::class, @@ -283,7 +289,10 @@ return [ ]; ``` -To create your own custom workers start from implementing the `Spiral\RoadRunnerLaravel\WorkerInterface`: +### Creating Custom Workers + +To create a custom worker, you need to implement the `Spiral\RoadRunnerLaravel\WorkerInterface`. +This interface has a single method, `start()`, which is called when the worker is started by the RoadRunner server: ```php namespace App\Workers; @@ -300,12 +309,21 @@ class CustomWorker implements WorkerInterface } ``` -Then register it in the `config/roadrunner.php`: +### Registering Custom Workers + +After creating your custom worker, you need to register it in the `config/roadrunner.php` file: ```php return [ + // ... other configuration options ... + 'workers' => [ - 'custom' => \App\Workers\CustomWorker::class, + // Existing workers + Mode::MODE_HTTP => HttpWorker::class, + Mode::MODE_JOBS => QueueWorker::class, + + // Your custom worker for a custom or built-in plugin + 'custom_plugin' => \App\Workers\CustomWorker::class, ], ]; ``` From af87a044a9dcd758e1b4b2cfec9fb9be2d55330a Mon Sep 17 00:00:00 2001 From: roxblnfk Date: Thu, 8 May 2025 18:22:46 +0400 Subject: [PATCH 07/32] Fourth Readme fix iteration --- README.md | 38 +++++++++++++++++++++++++++++--------- 1 file changed, 29 insertions(+), 9 deletions(-) diff --git a/README.md b/README.md index 92c533c..b549900 100644 --- a/README.md +++ b/README.md @@ -106,10 +106,14 @@ server: command: 'php vendor/bin/rr-worker start' ``` -When RoadRunner creates a worker pool for a specific plugin, it sets the `RR_MODE` environment variable to indicate which plugin is being used. -The Laravel Bridge checks this variable to determine which Worker class should handle the request based on your configuration. +When RoadRunner creates a worker pool for a specific plugin, +it sets the `RR_MODE` environment variable to indicate which plugin is being used. +The Laravel Bridge checks this variable to determine +which Worker class should handle the request based on your configuration. -The selected worker then listens for requests from the RoadRunner server and handles them using the Octane worker, which clears the application state after each task (request, command, etc.). +The selected worker then listens for requests from the RoadRunner server +and handles them using the [Octane][octane] worker, +which clears the application state after each task (request, command, etc.). ## Supported Plugins @@ -132,11 +136,13 @@ http: forbid: [ ".php" ] ``` -> **Note:** Read more about the HTTP plugin in the [RoadRunner documentation](https://docs.roadrunner.dev/docs/http/http). +> [!TIP] +> Read more about the HTTP plugin in the [RoadRunner documentation][roadrunner-docs-http]. ### Jobs (Queue) Plugin -The Queue plugin allows you to use RoadRunner as a queue driver for Laravel without additional services like Redis or a database. +The Queue plugin allows you to use RoadRunner as a queue driver for Laravel +without additional services like Redis or a database. #### Configuration @@ -184,7 +190,7 @@ QUEUE_CONNECTION=roadrunner That's it! You can now dispatch jobs to the RoadRunner queue without any additional services like Redis or Database. > [!TIP] -> Read more about the Jobs plugin in the [RoadRunner documentation](https://docs.roadrunner.dev/docs/queues-and-jobs/overview-queues). +> Read more about the Jobs plugin in the [RoadRunner documentation][roadrunner-docs-jobs]. ### gRPC Plugin @@ -273,7 +279,8 @@ This section explains how to create and register custom workers in your applicat ### Understanding Workers -Workers are responsible for handling requests from the RoadRunner server and processing them in your Laravel application. +Workers are responsible for handling requests from the RoadRunner server +and processing them in your Laravel application. The predefined workers are configured in the `config/roadrunner.php` file: ```php @@ -304,7 +311,8 @@ class CustomWorker implements WorkerInterface { public function start(WorkerOptionsInterface $options): void { - // Your custom worker implementation + // Your worker implementation goes here + // This method should handle requests from the RoadRunner server } } ``` @@ -328,15 +336,21 @@ return [ ]; ``` +The key in the `workers` array should match the value of the `RR_MODE` environment variable +set by the RoadRunner server for your plugin. + ## Support -If you find this package helpful, please consider giving it a star on GitHub. Your support helps make the project more visible to other developers who might benefit from it! +If you find this package helpful, please consider giving it a star on GitHub. +Your support helps make the project more visible to other developers who might benefit from it! [![Issues][badge_issues]][link_issues] [![Issues][badge_pulls]][link_pulls] If you find any package errors, please, [make an issue][link_create_issue] in a current repository. +You can also [sponsor this project][link_sponsor] to help ensure its continued development and maintenance. + ## License MIT License (MIT). Please see [`LICENSE`](./LICENSE) for more information. @@ -383,6 +397,8 @@ MIT License (MIT). Please see [`LICENSE`](./LICENSE) for more information. [link_pulls]:https://github.com/roadrunner-php/laravel-bridge/pulls +[link_sponsor]:https://github.com/sponsors/roadrunner-server + [link_license]:https://github.com/roadrunner-php/laravel-bridge/blob/master/LICENSE [getcomposer]:https://getcomposer.org/download/ @@ -399,6 +415,10 @@ MIT License (MIT). Please see [`LICENSE`](./LICENSE) for more information. [roadrunner-binary-releases]:https://github.com/roadrunner-server/roadrunner/releases +[roadrunner-docs-jobs]:https://docs.roadrunner.dev/docs/queues-and-jobs/overview-queues + +[roadrunner-docs-http]:https://docs.roadrunner.dev/docs/http/http + [octane]:https://laravel.com/docs/12.x/octane [rr-plugins-article]:https://butschster.medium.com/roadrunner-an-underrated-powerhouse-for-php-applications-46410b0abc From 23ca5a04417a5d3ef422ad9ba0842a8a08cffa09 Mon Sep 17 00:00:00 2001 From: roxblnfk Date: Thu, 8 May 2025 18:44:07 +0400 Subject: [PATCH 08/32] Simplify docs --- README.md | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/README.md b/README.md index b549900..b24337d 100644 --- a/README.md +++ b/README.md @@ -12,12 +12,11 @@ Easy way for connecting [RoadRunner][roadrunner] and [Laravel][laravel] applicat ## Why Use This Package? -This library offers the most complete integration of Laravel with RoadRunner. -You should definitely use this package if: +This package provides complete Laravel integration with RoadRunner, offering: -- You want to use not only HTTP but also other RoadRunner plugins like gRPC, Queue, KV, and others. -- You want to use Temporal. -- You want to control RoadRunner configuration. +- Support for HTTP and other RoadRunner plugins like gRPC, Queue, KeyValue, and more. +- [Temporal](https://temporal.io/) integration +- Full RoadRunner configuration control ![RoadRunner](https://github.com/user-attachments/assets/609d2e29-b6af-478b-b350-1d27b77ed6fb) From 992282c4fa1daa1b544f7e2bc23868583d1be726 Mon Sep 17 00:00:00 2001 From: butschster Date: Thu, 15 May 2025 17:41:56 +0200 Subject: [PATCH 09/32] feat: Add NamedArgumentConstructor to AssignWorker attribute --- src/Temporal/Attribute/AssignWorker.php | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/Temporal/Attribute/AssignWorker.php b/src/Temporal/Attribute/AssignWorker.php index be1b594..5870f8e 100644 --- a/src/Temporal/Attribute/AssignWorker.php +++ b/src/Temporal/Attribute/AssignWorker.php @@ -4,7 +4,9 @@ namespace Spiral\RoadRunnerLaravel\Temporal\Attribute; -#[\Attribute(\Attribute::TARGET_CLASS | \Attribute::IS_REPEATABLE)] +use Doctrine\Common\Annotations\Annotation\NamedArgumentConstructor; + +#[\Attribute(\Attribute::TARGET_CLASS | \Attribute::IS_REPEATABLE), NamedArgumentConstructor] final readonly class AssignWorker { /** From a8faa7d624c2e65d8379daeffb8eec996b4c529c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?P=D0=B0ramtamt=C4=81m?= <7326800+tarampampam@users.noreply.github.com> Date: Wed, 28 May 2025 11:41:03 +0400 Subject: [PATCH 10/32] Update bug_report.md --- .github/ISSUE_TEMPLATE/bug_report.md | 1 - 1 file changed, 1 deletion(-) diff --git a/.github/ISSUE_TEMPLATE/bug_report.md b/.github/ISSUE_TEMPLATE/bug_report.md index 99aea44..0a1c52b 100644 --- a/.github/ISSUE_TEMPLATE/bug_report.md +++ b/.github/ISSUE_TEMPLATE/bug_report.md @@ -2,7 +2,6 @@ name: Bug report about: Create a report to help us improve labels: type:bug -assignees: tarampampam --- ## Describe the bug From 42384a1b33124cc2844b704485e3650eab2c2061 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?P=D0=B0ramtamt=C4=81m?= <7326800+tarampampam@users.noreply.github.com> Date: Wed, 28 May 2025 11:41:16 +0400 Subject: [PATCH 11/32] Update feature_request.md --- .github/ISSUE_TEMPLATE/feature_request.md | 1 - 1 file changed, 1 deletion(-) diff --git a/.github/ISSUE_TEMPLATE/feature_request.md b/.github/ISSUE_TEMPLATE/feature_request.md index 1f76be3..b534773 100644 --- a/.github/ISSUE_TEMPLATE/feature_request.md +++ b/.github/ISSUE_TEMPLATE/feature_request.md @@ -2,7 +2,6 @@ name: Feature request about: Suggest an idea for this package labels: type:enhancement -assignees: tarampampam --- ### Is your feature request related to a problem? From 995a8c4ebcb60ddd150ea5ec99cc54cb3ac4b363 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?P=D0=B0ramtamt=C4=81m?= <7326800+tarampampam@users.noreply.github.com> Date: Wed, 28 May 2025 11:41:26 +0400 Subject: [PATCH 12/32] Update question.md --- .github/ISSUE_TEMPLATE/question.md | 1 - 1 file changed, 1 deletion(-) diff --git a/.github/ISSUE_TEMPLATE/question.md b/.github/ISSUE_TEMPLATE/question.md index 14ff0c6..da4617f 100644 --- a/.github/ISSUE_TEMPLATE/question.md +++ b/.github/ISSUE_TEMPLATE/question.md @@ -2,7 +2,6 @@ name: Question about: Ask anything labels: type:question -assignees: tarampampam --- > If you have any questions feel free to ask From 2a113101b354d7d76143386e9a4c95d6607ebe13 Mon Sep 17 00:00:00 2001 From: Alexander Date: Tue, 10 Jun 2025 17:58:23 +0500 Subject: [PATCH 13/32] The "withHeader" method of the "\Spiral\RoadRunner\Jobs\Task\WritableHeadersInterface" interface expects the type "string|iterable", "int" is passed --- src/Queue/RoadRunnerJob.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Queue/RoadRunnerJob.php b/src/Queue/RoadRunnerJob.php index 24364d5..7d8aeea 100644 --- a/src/Queue/RoadRunnerJob.php +++ b/src/Queue/RoadRunnerJob.php @@ -53,7 +53,7 @@ protected function failed($e): void $attempts = $this->attempts(); $this->task - ->withHeader('attempts', $attempts + 1) + ->withHeader('attempts', (string) ++$attempts) ->fail($e->getMessage()); parent::failed($e); From 4889d7f34bcea50513030ff4a9eeaff1ccadbf32 Mon Sep 17 00:00:00 2001 From: Alexander Date: Tue, 10 Jun 2025 18:05:47 +0500 Subject: [PATCH 14/32] The "withHeader" method of the "\Spiral\RoadRunner\Jobs\Task\WritableHeadersInterface" interface expects the type "string|iterable", "int" is passed --- CHANGELOG.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 59007c3..2dcb611 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,10 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog][keepachangelog] and this project adheres to [Semantic Versioning][semver]. +### Fixed + +- The "withHeader" method of the "\Spiral\RoadRunner\Jobs\Task\WritableHeadersInterface" interface expects the type "string|iterable", "int" is passed + ## v5.12.0 ### Added From edb9cce88c09a8ac8aef1ccf1f75adb55985d729 Mon Sep 17 00:00:00 2001 From: Alexander Date: Thu, 26 Jun 2025 12:50:16 +0500 Subject: [PATCH 15/32] Tasks were stuck in case of an error, the "release" method did not return them to the queue. The "calculateBackoff" method incorrectly took the index "$job->attempts()" --- src/Queue/QueueWorker.php | 7 +++---- src/Queue/RoadRunnerJob.php | 12 ++++++++++++ 2 files changed, 15 insertions(+), 4 deletions(-) diff --git a/src/Queue/QueueWorker.php b/src/Queue/QueueWorker.php index 488dfbc..dcc965e 100644 --- a/src/Queue/QueueWorker.php +++ b/src/Queue/QueueWorker.php @@ -187,8 +187,7 @@ protected function failJob(RoadRunnerJob $job, \Throwable $e): void protected function maxAttemptsExceededException(RoadRunnerJob $job): MaxAttemptsExceededException { return new MaxAttemptsExceededException( - $job->resolveName( - ) . ' has been attempted too many times or run too long. The job may have previously timed out.', + $job->resolveName() . ' has been attempted too many times or run too long. The job may have previously timed out.', ); } @@ -267,9 +266,9 @@ protected function calculateBackoff(RoadRunnerJob $job, WorkerOptions $options): ',', \method_exists($job, 'backoff') && !\is_null($job->backoff()) ? $job->backoff() - : $options->backoff, + : (string) $options->backoff, ); - return (int) ($backoff[$job->attempts() - 1] ?? last($backoff)); + return (int) ($backoff[$job->attempts()] ?? last($backoff)); } } diff --git a/src/Queue/RoadRunnerJob.php b/src/Queue/RoadRunnerJob.php index 7d8aeea..8a4d8ba 100644 --- a/src/Queue/RoadRunnerJob.php +++ b/src/Queue/RoadRunnerJob.php @@ -48,6 +48,18 @@ public function fire(): void $this->task->complete(); } + public function release($delay = 0): void + { + $attempts = $this->attempts(); + + $this->task + ->withDelay($delay) + ->withHeader('attempts', (string) ++$attempts) + ->requeue('release'); + + parent::release($delay); + } + protected function failed($e): void { $attempts = $this->attempts(); From 2f3f5be0b977005ac0f99ecaccc2d1cc7685bceb Mon Sep 17 00:00:00 2001 From: Alexander Date: Thu, 26 Jun 2025 12:51:22 +0500 Subject: [PATCH 16/32] Tasks were stuck in case of an error, the "release" method did not return them to the queue. The "calculateBackoff" method incorrectly took the index "$job->attempts()" --- CHANGELOG.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 2dcb611..a1192b2 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,8 @@ The format is based on [Keep a Changelog][keepachangelog] and this project adher ### Fixed +- Tasks were stuck in case of an error, the "release" method did not return them to the queue. +- The "calculateBackoff" method incorrectly took the index "$job->attempts()" - The "withHeader" method of the "\Spiral\RoadRunner\Jobs\Task\WritableHeadersInterface" interface expects the type "string|iterable", "int" is passed ## v5.12.0 From 37e16deaca5d67ee1d288de9f9a2996d30755466 Mon Sep 17 00:00:00 2001 From: Thai Nguyen Hung Date: Thu, 26 Jun 2025 16:17:49 +0700 Subject: [PATCH 17/32] feat: add gRPC client support and configuration --- composer.json | 3 +- config/roadrunner.php | 25 +++++++++++++++ src/ServiceProvider.php | 69 +++++++++++++++++++++++++++++++++++++++++ 3 files changed, 96 insertions(+), 1 deletion(-) diff --git a/composer.json b/composer.json index 8dfce17..35a0f59 100644 --- a/composer.json +++ b/composer.json @@ -42,7 +42,8 @@ "spiral/roadrunner-http": "^3.0", "spiral/roadrunner-worker": "^3.0", "temporal/sdk": "^2.0", - "internal/dload": "^1.1" + "internal/dload": "^1.1", + "spiral/grpc-client": "^1.0.0-rc1" }, "require-dev": { "laravel/framework": "^12.0", diff --git a/config/roadrunner.php b/config/roadrunner.php index 14bb231..77837ae 100644 --- a/config/roadrunner.php +++ b/config/roadrunner.php @@ -18,6 +18,31 @@ 'services' => [ // GreeterInterface::class => new Greeter::class, ], + 'clients' => [ + 'interceptors' => [ + // LoggingInterceptor::class, + ], + 'services' => [ + // [ + // 'connection' => 'my-grpc-server:9002', + // 'interfaces' => [ + // GreeterInterface::class, + // ], + // ], + // [ + // 'connection' => 'my-secure-grpc-server:9002', + // 'interfaces' => [ + // GreeterInterface::class, + // ], + // 'tls' => [ + // 'rootCerts' => '/path/to/ca.pem', + // 'privateKey' => '/path/to/client.key', + // 'certChain' => '/path/to/client.crt', + // 'serverName' => 'my.grpc.server', + // ], + // ], + ], + ], ], 'temporal' => [ diff --git a/src/ServiceProvider.php b/src/ServiceProvider.php index 8bf06ec..8dcdcdf 100644 --- a/src/ServiceProvider.php +++ b/src/ServiceProvider.php @@ -6,6 +6,12 @@ use Spiral\Attributes\AttributeReader; use Spiral\Attributes\ReaderInterface; +use Spiral\Core\FactoryInterface; +use Spiral\Grpc\Client\Config\ConnectionConfig; +use Spiral\Grpc\Client\Config\GrpcClientConfig; +use Spiral\Grpc\Client\Config\ServiceConfig; +use Spiral\Grpc\Client\Config\TlsConfig; +use Spiral\Grpc\Client\ServiceClientProvider; final class ServiceProvider extends \Illuminate\Support\ServiceProvider { @@ -23,6 +29,7 @@ public function register(): void { $this->app->singleton(ReaderInterface::class, AttributeReader::class); $this->initializeConfigs(); + $this->initializeGrpcClientServices(); } protected function initializeConfigs(): void @@ -33,4 +40,66 @@ protected function initializeConfigs(): void \realpath(self::getConfigPath()) => config_path(\basename(self::getConfigPath())), ], 'config'); } + + protected function initializeGrpcClientServices(): void + { + $this->app->singleton(FactoryInterface::class, fn() => new class implements FactoryInterface { + /** + * @param class-string $class + * @param array $parameters + */ + public function make(string $class, array $parameters = []): object + { + return new $class(...array_values($parameters)); + } + }); + $this->app->singleton(ServiceClientProvider::class, function () { + $toNonEmptyStringOrNull = static fn($value): ?string => (is_string($value) && $value !== '') ? $value : null; + /** + * @var array, + * tls?: array{ + * rootCerts?: non-empty-string|null, + * privateKey?: non-empty-string|null, + * certChain?: non-empty-string|null, + * serverName?: non-empty-string|null + * } + * }> + */ + $rawServices = config('roadrunner.grpc.clients.services', []); + $services = collect($rawServices); + $serviceConfigs = []; + foreach ($services as $service) { + $tls = null; + if (isset($service['tls'])) { + $tlsConfig = $service['tls']; + $tls = new TlsConfig( + $toNonEmptyStringOrNull($tlsConfig['rootCerts'] ?? null), + $toNonEmptyStringOrNull($tlsConfig['privateKey'] ?? null), + $toNonEmptyStringOrNull($tlsConfig['certChain'] ?? null), + $toNonEmptyStringOrNull($tlsConfig['serverName'] ?? null), + ); + } + /** @var non-empty-string $connection */ + $connection = $service['connection']; + /** @var list $interfaces */ + $interfaces = $service['interfaces']; + $serviceConfigs[] = new ServiceConfig( + connections: new ConnectionConfig($connection, $tls), + interfaces: $interfaces, + ); + } + + /** @var array|\Spiral\Core\Container\Autowire<\Spiral\Interceptors\InterceptorInterface>|\Spiral\Interceptors\InterceptorInterface> $interceptors */ + $interceptors = config('roadrunner.grpc.clients.interceptors', []); + $config = new GrpcClientConfig( + interceptors: $interceptors, + services: $serviceConfigs, + ); + + return new ServiceClientProvider($config, $this->app->make(FactoryInterface::class)); + }); + + } } From cef137d225f83a69f5c7ec452125169059d245b4 Mon Sep 17 00:00:00 2001 From: Thai Nguyen Hung Date: Thu, 26 Jun 2025 16:24:00 +0700 Subject: [PATCH 18/32] docs: update README to include gRPC Client usage and configuration details --- README.md | 61 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 61 insertions(+) diff --git a/README.md b/README.md index b24337d..abbb585 100644 --- a/README.md +++ b/README.md @@ -34,6 +34,7 @@ This package provides complete Laravel integration with RoadRunner, offering: - [HTTP Plugin](#http-plugin) - [Jobs (Queue) Plugin](#jobs-queue-plugin) - [gRPC Plugin](#grpc-plugin) + - [gRPC Client](#grpc-client) - [Temporal](#temporal) - [Custom Workers](#custom-workers) - [Support](#support) @@ -219,6 +220,66 @@ return [ ]; ``` +#### gRPC Client Usage + +The package also allows your Laravel application to act as a gRPC client, making requests to external gRPC services. + +##### Client Configuration + +Add your gRPC client configuration to `config/roadrunner.php`: + +```php +return [ + // ... other configuration + 'grpc' => [ + // ... server config + 'clients' => [ + 'services' => [ + [ + 'connection' => '127.0.0.1:9001', // gRPC server address + 'interfaces' => [ + \App\Grpc\EchoServiceInterface::class, + ], + // 'tls' => [ ... ] // Optional TLS configuration + ], + ], + // 'interceptors' => [ ... ] // Optional interceptors + ], + ], +]; +``` + +##### Using the gRPC Client in Laravel + +You can inject `Spiral\Grpc\Client\ServiceClientProvider` into your services or controllers to obtain a gRPC client instance: + +```php +use Spiral\Grpc\Client\ServiceClientProvider; +use App\Grpc\EchoServiceInterface; +use App\Grpc\EchoRequest; + +class GrpcController extends Controller +{ + public function callService(ServiceClientProvider $provider) + { + /** @var EchoServiceInterface $client */ + $client = $provider->get(EchoServiceInterface::class); + + $request = new EchoRequest(); + $request->setMessage('Hello from client!'); + + $response = $client->Echo($request); + + return $response->getMessage(); + } +} +``` + +> **Note:** +> - Make sure you have generated the PHP classes from your `.proto` files (using `protoc`). +> - The `connection` and `interfaces` must match the service you want to call. +> - You can configure multiple gRPC client services as needed. + ### Temporal Temporal is a workflow engine that enables orchestration of microservices and provides sophisticated workflow From 6fb1adf089661e545316fc57d6e1c8f52d68ff9c Mon Sep 17 00:00:00 2001 From: Thai Nguyen Hung Date: Thu, 26 Jun 2025 16:32:23 +0700 Subject: [PATCH 19/32] chore: update CHANGELOG to include gRPC client support --- CHANGELOG.md | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index a1192b2..e5aeaaf 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,12 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog][keepachangelog] and this project adheres to [Semantic Versioning][semver]. +## UNRELEASED + +### Added + +- gRPC client support + ### Fixed - Tasks were stuck in case of an error, the "release" method did not return them to the queue. From 1aa2969fbec07e2bf529afa89145e8429ca34a11 Mon Sep 17 00:00:00 2001 From: Thai Nguyen Hung Date: Sat, 28 Jun 2025 14:08:35 +0700 Subject: [PATCH 20/32] refactor: enhance FactoryInterface implementation to utilize the service container for object creation --- src/ServiceProvider.php | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/src/ServiceProvider.php b/src/ServiceProvider.php index 8dcdcdf..54d21e7 100644 --- a/src/ServiceProvider.php +++ b/src/ServiceProvider.php @@ -4,6 +4,7 @@ namespace Spiral\RoadRunnerLaravel; +use Illuminate\Contracts\Container\Container; use Spiral\Attributes\AttributeReader; use Spiral\Attributes\ReaderInterface; use Spiral\Core\FactoryInterface; @@ -43,14 +44,18 @@ protected function initializeConfigs(): void protected function initializeGrpcClientServices(): void { - $this->app->singleton(FactoryInterface::class, fn() => new class implements FactoryInterface { + $this->app->singleton(FactoryInterface::class, fn() => new class($this->app) implements FactoryInterface { + public function __construct( + private readonly Container $container, + ) {} + /** * @param class-string $class * @param array $parameters */ public function make(string $class, array $parameters = []): object { - return new $class(...array_values($parameters)); + return $this->container->make($class, $parameters); } }); $this->app->singleton(ServiceClientProvider::class, function () { From f72cb2f7b240f88debecdc3886c61455f6ca67c1 Mon Sep 17 00:00:00 2001 From: Eldario Date: Thu, 10 Jul 2025 14:50:47 +0500 Subject: [PATCH 21/32] Fix pipeline stats on push message --- CHANGELOG.md | 5 +++++ src/Queue/RoadRunnerQueue.php | 2 +- 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index a1192b2..e646550 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,12 +4,17 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog][keepachangelog] and this project adheres to [Semantic Versioning][semver]. +## Unreleased + ### Fixed +- Check pipeline stats on message push [#147] - Tasks were stuck in case of an error, the "release" method did not return them to the queue. - The "calculateBackoff" method incorrectly took the index "$job->attempts()" - The "withHeader" method of the "\Spiral\RoadRunner\Jobs\Task\WritableHeadersInterface" interface expects the type "string|iterable", "int" is passed +[#147]:https://github.com/roadrunner-php/laravel-bridge/issues/147 + ## v5.12.0 ### Added diff --git a/src/Queue/RoadRunnerQueue.php b/src/Queue/RoadRunnerQueue.php index 27424b7..23d3ff7 100644 --- a/src/Queue/RoadRunnerQueue.php +++ b/src/Queue/RoadRunnerQueue.php @@ -120,7 +120,7 @@ private function getStats(?string $queue = null): Stat /** @var Stat $stat */ foreach ($stats as $stat) { - if ($stat->getQueue() === $queue) { + if ($stat->getPipeline() === $queue) { return $stat; } } From f364b5f88331028dc6d0e13f656ca60cb9e37c87 Mon Sep 17 00:00:00 2001 From: Alexander Date: Fri, 11 Jul 2025 14:00:17 +0500 Subject: [PATCH 22/32] Edit the $ttl calculation for the RoadRunnerStore, the time calculation takes place inside the spiral/roadrunner-kv package --- CHANGELOG.md | 1 + src/Cache/RoadRunnerStore.php | 31 ++----------------------------- 2 files changed, 3 insertions(+), 29 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index a1192b2..e25c15c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,7 @@ The format is based on [Keep a Changelog][keepachangelog] and this project adher ### Fixed +- Edit the $ttl calculation for the RoadRunnerStore, the time calculation takes place inside the spiral/roadrunner-kv package - Tasks were stuck in case of an error, the "release" method did not return them to the queue. - The "calculateBackoff" method incorrectly took the index "$job->attempts()" - The "withHeader" method of the "\Spiral\RoadRunner\Jobs\Task\WritableHeadersInterface" interface expects the type "string|iterable", "int" is passed diff --git a/src/Cache/RoadRunnerStore.php b/src/Cache/RoadRunnerStore.php index 9239e52..dded3bc 100644 --- a/src/Cache/RoadRunnerStore.php +++ b/src/Cache/RoadRunnerStore.php @@ -6,13 +6,10 @@ use Illuminate\Cache\TaggableStore; use Illuminate\Contracts\Cache\LockProvider; -use Illuminate\Support\InteractsWithTime; use Spiral\RoadRunner\KeyValue\StorageInterface; final class RoadRunnerStore extends TaggableStore implements LockProvider { - use InteractsWithTime; - public function __construct(private StorageInterface $storage, private string $prefix = '') {} public function get($key) @@ -39,7 +36,7 @@ public function many(array $keys) public function put($key, $value, $seconds) { - return $this->storage->set($this->prefix . $key, $value, $this->calculateExpiration($seconds)); + return $this->storage->set($this->prefix . $key, $value, $seconds); } public function putMany(array $values, $seconds) @@ -52,7 +49,7 @@ public function putMany(array $values, $seconds) return $this->storage->setMultiple( $prefixedValues, - $this->calculateExpiration($seconds), + $seconds, ); } @@ -90,28 +87,4 @@ public function getPrefix() { return $this->prefix; } - - /** - * Set the cache key prefix. - */ - public function setPrefix(string $prefix): void - { - $this->prefix = !empty($prefix) ? $prefix . ':' : ''; - } - - /** - * Get the expiration time of the key. - */ - protected function calculateExpiration(int $seconds): int - { - return $this->toTimestamp($seconds); - } - - /** - * Get the UNIX timestamp for the given number of seconds. - */ - protected function toTimestamp(int $seconds): int - { - return $seconds > 0 ? $this->availableAt($seconds) : 0; - } } From 6bc7ac4971a3b077fe4b01c4c62b657ef21d2a26 Mon Sep 17 00:00:00 2001 From: Adrien Brignon Date: Fri, 11 Jul 2025 14:07:05 +0200 Subject: [PATCH 23/32] feat: add `setPrefix` method on `RoadRunnerStore` class --- CHANGELOG.md | 1 + src/Cache/RoadRunnerStore.php | 12 +++++++++++- 2 files changed, 12 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 58b95b2..3f5b1fa 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -24,6 +24,7 @@ The format is based on [Keep a Changelog][keepachangelog] and this project adher ### Added +- Method `setPrefix` on the `\Spiral\RoadRunnerLaravel\Cache\RoadRunnerStore` class - Support for `v3.x` of `spiral/roadrunner-http` package [#125] [#125]:https://github.com/roadrunner-php/laravel-bridge/issues/125 diff --git a/src/Cache/RoadRunnerStore.php b/src/Cache/RoadRunnerStore.php index dded3bc..347988c 100644 --- a/src/Cache/RoadRunnerStore.php +++ b/src/Cache/RoadRunnerStore.php @@ -10,7 +10,12 @@ final class RoadRunnerStore extends TaggableStore implements LockProvider { - public function __construct(private StorageInterface $storage, private string $prefix = '') {} + private $prefix; + + public function __construct(private StorageInterface $storage, string $prefix = '') + { + $this->setPrefix($prefix); + } public function get($key) { @@ -87,4 +92,9 @@ public function getPrefix() { return $this->prefix; } + + public function setPrefix($prefix) + { + $this->prefix = $prefix; + } } From 6eeac38ab5fd5fcdd76ff178b83f597dc9fb4f0a Mon Sep 17 00:00:00 2001 From: Adrien Brignon Date: Fri, 11 Jul 2025 15:02:27 +0200 Subject: [PATCH 24/32] style: added type hints for prefix --- src/Cache/RoadRunnerStore.php | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/Cache/RoadRunnerStore.php b/src/Cache/RoadRunnerStore.php index 347988c..90af3fd 100644 --- a/src/Cache/RoadRunnerStore.php +++ b/src/Cache/RoadRunnerStore.php @@ -10,7 +10,7 @@ final class RoadRunnerStore extends TaggableStore implements LockProvider { - private $prefix; + private string $prefix; public function __construct(private StorageInterface $storage, string $prefix = '') { @@ -88,12 +88,12 @@ public function flush() return $this->storage->clear(); } - public function getPrefix() + public function getPrefix(): string { return $this->prefix; } - public function setPrefix($prefix) + public function setPrefix(string $prefix): void { $this->prefix = $prefix; } From 5e4a4453b6f06b6db3d754a71d093285b159a606 Mon Sep 17 00:00:00 2001 From: Henrik Malmberg Date: Wed, 23 Jul 2025 12:31:23 +0200 Subject: [PATCH 25/32] feat: enhance RoadRunnerQueue and RoadRunnerConnector with options handling --- src/Queue/RoadRunnerConnector.php | 20 ++++++++++++++++++++ src/Queue/RoadRunnerQueue.php | 4 +++- 2 files changed, 23 insertions(+), 1 deletion(-) diff --git a/src/Queue/RoadRunnerConnector.php b/src/Queue/RoadRunnerConnector.php index 86cffbd..70c8f4b 100644 --- a/src/Queue/RoadRunnerConnector.php +++ b/src/Queue/RoadRunnerConnector.php @@ -10,6 +10,10 @@ use Spiral\Goridge\RPC\RPC; use Spiral\RoadRunner\Environment; use Spiral\RoadRunner\Jobs\Jobs; +use Spiral\RoadRunner\Jobs\KafkaOptions; +use Spiral\RoadRunner\Jobs\Options; +use Spiral\RoadRunner\Jobs\OptionsInterface; +use Spiral\RoadRunner\Jobs\Queue\Driver; final class RoadRunnerConnector implements ConnectorInterface { @@ -25,7 +29,23 @@ public function connect(array $config): Queue return new RoadRunnerQueue( new Jobs($rpc), $rpc, + $this->getOptions($config['options'] ?? []), $config['queue'], ); } + + private function getOptions(array $config): OptionsInterface + { + $options = new Options( + $config['delay'] ?? 0, + $config['priority'] ?? 0, + $config['auto_ack'] ?? false + ); + + return match ($config['driver'] ?? null) { + Driver::Kafka => KafkaOptions::from($options) + ->withTopic($config['topic'] ?? 'default'), + default => $options, + }; + } } diff --git a/src/Queue/RoadRunnerQueue.php b/src/Queue/RoadRunnerQueue.php index 23d3ff7..6253249 100644 --- a/src/Queue/RoadRunnerQueue.php +++ b/src/Queue/RoadRunnerQueue.php @@ -12,6 +12,7 @@ use RoadRunner\Jobs\DTO\V1\Stats; use Spiral\Goridge\RPC\RPCInterface; use Spiral\RoadRunner\Jobs\Jobs; +use Spiral\RoadRunner\Jobs\OptionsInterface; use Spiral\RoadRunner\Jobs\QueueInterface; final class RoadRunnerQueue extends Queue implements QueueContract @@ -19,6 +20,7 @@ final class RoadRunnerQueue extends Queue implements QueueContract public function __construct( private readonly Jobs $jobs, private readonly RPCInterface $rpc, + private readonly OptionsInterface $options, private readonly string $default = 'default', ) {} @@ -103,7 +105,7 @@ private function laterRaw( private function getQueue(?string $queue = null): QueueInterface { - $queue = $this->jobs->connect($queue ?? $this->default); + $queue = $this->jobs->connect($queue ?? $this->default, $this->options); if (!$this->getStats($queue->getName())->getReady()) { $queue->resume(); From c26cf5660177eae7c5bacf0372f1edfa2237cc99 Mon Sep 17 00:00:00 2001 From: Henrik Malmberg Date: Wed, 23 Jul 2025 13:02:21 +0200 Subject: [PATCH 26/32] feat: implement HasQueueOptions interface and refactor RoadRunnerQueue for options handling --- src/Queue/Contract/HasQueueOptions.php | 8 +++++ src/Queue/RoadRunnerConnector.php | 21 +----------- src/Queue/RoadRunnerQueue.php | 44 ++++++++++++++++++++++---- 3 files changed, 46 insertions(+), 27 deletions(-) create mode 100644 src/Queue/Contract/HasQueueOptions.php diff --git a/src/Queue/Contract/HasQueueOptions.php b/src/Queue/Contract/HasQueueOptions.php new file mode 100644 index 0000000..9e8ce13 --- /dev/null +++ b/src/Queue/Contract/HasQueueOptions.php @@ -0,0 +1,8 @@ +getOptions($config['options'] ?? []), $config['queue'], + $config['options'] ?? [], ); } - - private function getOptions(array $config): OptionsInterface - { - $options = new Options( - $config['delay'] ?? 0, - $config['priority'] ?? 0, - $config['auto_ack'] ?? false - ); - - return match ($config['driver'] ?? null) { - Driver::Kafka => KafkaOptions::from($options) - ->withTopic($config['topic'] ?? 'default'), - default => $options, - }; - } } diff --git a/src/Queue/RoadRunnerQueue.php b/src/Queue/RoadRunnerQueue.php index 6253249..255f202 100644 --- a/src/Queue/RoadRunnerQueue.php +++ b/src/Queue/RoadRunnerQueue.php @@ -12,16 +12,20 @@ use RoadRunner\Jobs\DTO\V1\Stats; use Spiral\Goridge\RPC\RPCInterface; use Spiral\RoadRunner\Jobs\Jobs; +use Spiral\RoadRunner\Jobs\KafkaOptions; +use Spiral\RoadRunner\Jobs\Options; use Spiral\RoadRunner\Jobs\OptionsInterface; +use Spiral\RoadRunner\Jobs\Queue\Driver; use Spiral\RoadRunner\Jobs\QueueInterface; +use Spiral\RoadRunnerLaravel\Queue\Contract\HasQueueOptions; final class RoadRunnerQueue extends Queue implements QueueContract { public function __construct( private readonly Jobs $jobs, private readonly RPCInterface $rpc, - private readonly OptionsInterface $options, private readonly string $default = 'default', + private readonly array $defaultOptions = [], ) {} public function push($job, $data = '', $queue = null): string @@ -31,13 +35,13 @@ public function push($job, $data = '', $queue = null): string $this->createPayload($job, $queue, $data), $queue, null, - fn($payload, $queue) => $this->pushRaw($payload, $queue), + fn($payload, $queue) => $this->pushRaw($payload, $queue, $this->getJobOverrideOptions($job)), ); } public function pushRaw($payload, $queue = null, array $options = []): string { - $queue = $this->getQueue($queue); + $queue = $this->getQueue($queue, $options); $task = $queue->dispatch( $queue @@ -54,7 +58,7 @@ public function later($delay, $job, $data = '', $queue = null): string $this->createPayload($job, $queue, $data), $queue, $delay, - fn($payload, $queue) => $this->laterRaw($delay, $payload, $queue), + fn($payload, $queue) => $this->laterRaw($delay, $payload, $queue, $this->getJobOverrideOptions($job)), ); } @@ -90,8 +94,9 @@ private function laterRaw( \DateTimeInterface|\DateInterval|int $delay, array $payload, ?string $queue = null, + array $options = [] ): string { - $queue = $this->getQueue($queue); + $queue = $this->getQueue($queue, $options); $task = $queue->dispatch( $queue @@ -103,9 +108,9 @@ private function laterRaw( return $task->getId(); } - private function getQueue(?string $queue = null): QueueInterface + private function getQueue(?string $queue = null, array $options = []): QueueInterface { - $queue = $this->jobs->connect($queue ?? $this->default, $this->options); + $queue = $this->jobs->connect($queue ?? $this->default, $this->getQueueOptions($options)); if (!$this->getStats($queue->getName())->getReady()) { $queue->resume(); @@ -129,4 +134,29 @@ private function getStats(?string $queue = null): Stat return new Stat(); } + + private function getJobOverrideOptions(string|object $job): array + { + if ($job instanceof HasQueueOptions) { + return $job->queueOptions(); + } + + return []; + } + + private function getQueueOptions(array $overrides = []): OptionsInterface + { + $config = array_merge($this->defaultOptions, $overrides); + $options = new Options( + $config['delay'] ?? 0, + $config['priority'] ?? 0, + $config['auto_ack'] ?? false + ); + + return match ($config['driver'] ?? null) { + Driver::Kafka => KafkaOptions::from($options) + ->withTopic($config['topic'] ?? 'default'), + default => $options, + }; + } } From 3e5149db9115e665040034d7b3d77ea0579dc882 Mon Sep 17 00:00:00 2001 From: Henrik Malmberg Date: Wed, 23 Jul 2025 13:10:32 +0200 Subject: [PATCH 27/32] update CHANGELOG to include changes --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 3f5b1fa..167c72c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,7 @@ The format is based on [Keep a Changelog][keepachangelog] and this project adher ### Added - gRPC client support +- Added support to set queue options globally or per job [#158] ## Unreleased From 6bd9276826bc5303891b410f423033f5f8768999 Mon Sep 17 00:00:00 2001 From: Henrik Malmberg Date: Wed, 23 Jul 2025 13:25:26 +0200 Subject: [PATCH 28/32] fix: change queueOptions method visibility to public in HasQueueOptions interface --- src/Queue/Contract/HasQueueOptions.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Queue/Contract/HasQueueOptions.php b/src/Queue/Contract/HasQueueOptions.php index 9e8ce13..941ffd2 100644 --- a/src/Queue/Contract/HasQueueOptions.php +++ b/src/Queue/Contract/HasQueueOptions.php @@ -4,5 +4,5 @@ interface HasQueueOptions { - function queueOptions(): array; + public function queueOptions(): array; } From 70048d1eca862a2a081e482d2cb8d1f062e96b53 Mon Sep 17 00:00:00 2001 From: Henrik Malmberg Date: Wed, 23 Jul 2025 13:28:34 +0200 Subject: [PATCH 29/32] feat: handle cases where $job is a string --- src/Queue/RoadRunnerQueue.php | 137 ++++++++++++++++++---------------- 1 file changed, 73 insertions(+), 64 deletions(-) diff --git a/src/Queue/RoadRunnerQueue.php b/src/Queue/RoadRunnerQueue.php index 255f202..7aa1af6 100644 --- a/src/Queue/RoadRunnerQueue.php +++ b/src/Queue/RoadRunnerQueue.php @@ -26,7 +26,8 @@ public function __construct( private readonly RPCInterface $rpc, private readonly string $default = 'default', private readonly array $defaultOptions = [], - ) {} + ) { + } public function push($job, $data = '', $queue = null): string { @@ -51,40 +52,75 @@ public function pushRaw($payload, $queue = null, array $options = []): string return $task->getId(); } - public function later($delay, $job, $data = '', $queue = null): string + private function getQueue(?string $queue = null, array $options = []): QueueInterface { - return $this->enqueueUsing( - $job, - $this->createPayload($job, $queue, $data), - $queue, - $delay, - fn($payload, $queue) => $this->laterRaw($delay, $payload, $queue, $this->getJobOverrideOptions($job)), - ); + $queue = $this->jobs->connect($queue ?? $this->default, $this->getQueueOptions($options)); + + if (!$this->getStats($queue->getName())->getReady()) { + $queue->resume(); + } + + return $queue; } - public function pop($queue = null): void + private function getQueueOptions(array $overrides = []): OptionsInterface { - throw new \BadMethodCallException('Pop is not supported'); + $config = array_merge($this->defaultOptions, $overrides); + $options = new Options( + $config['delay'] ?? 0, + $config['priority'] ?? 0, + $config['auto_ack'] ?? false + ); + + return match ($config['driver'] ?? null) { + Driver::Kafka => KafkaOptions::from($options) + ->withTopic($config['topic'] ?? 'default'), + default => $options, + }; } - public function size($queue = null): int + private function getStats(?string $queue = null): Stat { - $stats = $this->getStats($queue); + $queue ??= $this->default; - return $stats->getActive() + $stats->getDelayed(); + $stats = $this->rpc->call('jobs.Stat', new Stats(), Stats::class)->getStats(); + + /** @var Stat $stat */ + foreach ($stats as $stat) { + if ($stat->getPipeline() === $queue) { + return $stat; + } + } + + return new Stat(); } - /** - * Get the "available at" UNIX timestamp. - * @param mixed $delay - */ - protected function availableAt($delay = 0): int + private function getJobOverrideOptions(string|object $job): array { - $delay = $this->parseDateInterval($delay); + if (is_string($job) && class_exists($job)) { + $job = app($job); - return $delay instanceof \DateTimeInterface - ? Carbon::parse($delay)->diffInSeconds() - : $delay; + if ($job instanceof HasQueueOptions) { + return $job->queueOptions(); + } + } + + if ($job instanceof HasQueueOptions) { + return $job->queueOptions(); + } + + return []; + } + + public function later($delay, $job, $data = '', $queue = null): string + { + return $this->enqueueUsing( + $job, + $this->createPayload($job, $queue, $data), + $queue, + $delay, + fn($payload, $queue) => $this->laterRaw($delay, $payload, $queue, $this->getJobOverrideOptions($job)), + ); } /** @@ -108,55 +144,28 @@ private function laterRaw( return $task->getId(); } - private function getQueue(?string $queue = null, array $options = []): QueueInterface - { - $queue = $this->jobs->connect($queue ?? $this->default, $this->getQueueOptions($options)); - - if (!$this->getStats($queue->getName())->getReady()) { - $queue->resume(); - } - - return $queue; - } - - private function getStats(?string $queue = null): Stat + /** + * Get the "available at" UNIX timestamp. + * @param mixed $delay + */ + protected function availableAt($delay = 0): int { - $queue ??= $this->default; - - $stats = $this->rpc->call('jobs.Stat', new Stats(), Stats::class)->getStats(); - - /** @var Stat $stat */ - foreach ($stats as $stat) { - if ($stat->getPipeline() === $queue) { - return $stat; - } - } + $delay = $this->parseDateInterval($delay); - return new Stat(); + return $delay instanceof \DateTimeInterface + ? Carbon::parse($delay)->diffInSeconds() + : $delay; } - private function getJobOverrideOptions(string|object $job): array + public function pop($queue = null): void { - if ($job instanceof HasQueueOptions) { - return $job->queueOptions(); - } - - return []; + throw new \BadMethodCallException('Pop is not supported'); } - private function getQueueOptions(array $overrides = []): OptionsInterface + public function size($queue = null): int { - $config = array_merge($this->defaultOptions, $overrides); - $options = new Options( - $config['delay'] ?? 0, - $config['priority'] ?? 0, - $config['auto_ack'] ?? false - ); + $stats = $this->getStats($queue); - return match ($config['driver'] ?? null) { - Driver::Kafka => KafkaOptions::from($options) - ->withTopic($config['topic'] ?? 'default'), - default => $options, - }; + return $stats->getActive() + $stats->getDelayed(); } } From b82fdb514a95c1cd50046124fbff9ccea4b9ed0d Mon Sep 17 00:00:00 2001 From: Henrik Malmberg Date: Wed, 23 Jul 2025 13:31:57 +0200 Subject: [PATCH 30/32] feat: update options handling in RoadRunnerQueue to use default values from OptionsInterface --- src/Queue/RoadRunnerQueue.php | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/src/Queue/RoadRunnerQueue.php b/src/Queue/RoadRunnerQueue.php index 7aa1af6..6c5bf0a 100644 --- a/src/Queue/RoadRunnerQueue.php +++ b/src/Queue/RoadRunnerQueue.php @@ -13,6 +13,7 @@ use Spiral\Goridge\RPC\RPCInterface; use Spiral\RoadRunner\Jobs\Jobs; use Spiral\RoadRunner\Jobs\KafkaOptions; +use Spiral\RoadRunner\Jobs\KafkaOptionsInterface; use Spiral\RoadRunner\Jobs\Options; use Spiral\RoadRunner\Jobs\OptionsInterface; use Spiral\RoadRunner\Jobs\Queue\Driver; @@ -67,14 +68,14 @@ private function getQueueOptions(array $overrides = []): OptionsInterface { $config = array_merge($this->defaultOptions, $overrides); $options = new Options( - $config['delay'] ?? 0, - $config['priority'] ?? 0, - $config['auto_ack'] ?? false + $config['delay'] ?? OptionsInterface::DEFAULT_DELAY, + $config['priority'] ?? OptionsInterface::DEFAULT_PRIORITY, + $config['auto_ack'] ?? OptionsInterface::DEFAULT_AUTO_ACK, ); return match ($config['driver'] ?? null) { Driver::Kafka => KafkaOptions::from($options) - ->withTopic($config['topic'] ?? 'default'), + ->withTopic($config['topic'] ?? ($this->defaultOptions['topic'] ?? '')), default => $options, }; } From 9fe83780ec65ee036ef0487ec628930c46e55e01 Mon Sep 17 00:00:00 2001 From: Henrik Malmberg Date: Mon, 28 Jul 2025 08:53:50 +0200 Subject: [PATCH 31/32] fix: update queueOptions method return type to OptionsInterface in HasQueueOptions --- src/Queue/Contract/HasQueueOptions.php | 4 +++- src/Queue/RoadRunnerQueue.php | 10 ++++------ 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/src/Queue/Contract/HasQueueOptions.php b/src/Queue/Contract/HasQueueOptions.php index 941ffd2..85f7c64 100644 --- a/src/Queue/Contract/HasQueueOptions.php +++ b/src/Queue/Contract/HasQueueOptions.php @@ -2,7 +2,9 @@ namespace Spiral\RoadRunnerLaravel\Queue\Contract; +use Spiral\RoadRunner\Jobs\OptionsInterface; + interface HasQueueOptions { - public function queueOptions(): array; + public function queueOptions(): OptionsInterface; } diff --git a/src/Queue/RoadRunnerQueue.php b/src/Queue/RoadRunnerQueue.php index 6c5bf0a..1718420 100644 --- a/src/Queue/RoadRunnerQueue.php +++ b/src/Queue/RoadRunnerQueue.php @@ -13,7 +13,6 @@ use Spiral\Goridge\RPC\RPCInterface; use Spiral\RoadRunner\Jobs\Jobs; use Spiral\RoadRunner\Jobs\KafkaOptions; -use Spiral\RoadRunner\Jobs\KafkaOptionsInterface; use Spiral\RoadRunner\Jobs\Options; use Spiral\RoadRunner\Jobs\OptionsInterface; use Spiral\RoadRunner\Jobs\Queue\Driver; @@ -100,14 +99,13 @@ private function getJobOverrideOptions(string|object $job): array { if (is_string($job) && class_exists($job)) { $job = app($job); - - if ($job instanceof HasQueueOptions) { - return $job->queueOptions(); - } } if ($job instanceof HasQueueOptions) { - return $job->queueOptions(); + $options = $job->queueOptions(); + if ($options instanceof Options) { + return $options->toArray(); + } } return []; From 231a23f80ef41f201013768b857e418931ea9e24 Mon Sep 17 00:00:00 2001 From: github-actions Date: Fri, 8 Aug 2025 07:52:31 +0000 Subject: [PATCH 32/32] style(php-cs-fixer): fix coding standards --- src/Queue/RoadRunnerQueue.php | 77 +++++++++++++++++------------------ 1 file changed, 38 insertions(+), 39 deletions(-) diff --git a/src/Queue/RoadRunnerQueue.php b/src/Queue/RoadRunnerQueue.php index 1718420..6f5f388 100644 --- a/src/Queue/RoadRunnerQueue.php +++ b/src/Queue/RoadRunnerQueue.php @@ -26,8 +26,7 @@ public function __construct( private readonly RPCInterface $rpc, private readonly string $default = 'default', private readonly array $defaultOptions = [], - ) { - } + ) {} public function push($job, $data = '', $queue = null): string { @@ -52,6 +51,42 @@ public function pushRaw($payload, $queue = null, array $options = []): string return $task->getId(); } + public function later($delay, $job, $data = '', $queue = null): string + { + return $this->enqueueUsing( + $job, + $this->createPayload($job, $queue, $data), + $queue, + $delay, + fn($payload, $queue) => $this->laterRaw($delay, $payload, $queue, $this->getJobOverrideOptions($job)), + ); + } + + public function pop($queue = null): void + { + throw new \BadMethodCallException('Pop is not supported'); + } + + public function size($queue = null): int + { + $stats = $this->getStats($queue); + + return $stats->getActive() + $stats->getDelayed(); + } + + /** + * Get the "available at" UNIX timestamp. + * @param mixed $delay + */ + protected function availableAt($delay = 0): int + { + $delay = $this->parseDateInterval($delay); + + return $delay instanceof \DateTimeInterface + ? Carbon::parse($delay)->diffInSeconds() + : $delay; + } + private function getQueue(?string $queue = null, array $options = []): QueueInterface { $queue = $this->jobs->connect($queue ?? $this->default, $this->getQueueOptions($options)); @@ -111,17 +146,6 @@ private function getJobOverrideOptions(string|object $job): array return []; } - public function later($delay, $job, $data = '', $queue = null): string - { - return $this->enqueueUsing( - $job, - $this->createPayload($job, $queue, $data), - $queue, - $delay, - fn($payload, $queue) => $this->laterRaw($delay, $payload, $queue, $this->getJobOverrideOptions($job)), - ); - } - /** * Push a raw job onto the queue after a delay. */ @@ -129,7 +153,7 @@ private function laterRaw( \DateTimeInterface|\DateInterval|int $delay, array $payload, ?string $queue = null, - array $options = [] + array $options = [], ): string { $queue = $this->getQueue($queue, $options); @@ -142,29 +166,4 @@ private function laterRaw( return $task->getId(); } - - /** - * Get the "available at" UNIX timestamp. - * @param mixed $delay - */ - protected function availableAt($delay = 0): int - { - $delay = $this->parseDateInterval($delay); - - return $delay instanceof \DateTimeInterface - ? Carbon::parse($delay)->diffInSeconds() - : $delay; - } - - public function pop($queue = null): void - { - throw new \BadMethodCallException('Pop is not supported'); - } - - public function size($queue = null): int - { - $stats = $this->getStats($queue); - - return $stats->getActive() + $stats->getDelayed(); - } }