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/.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(); 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/README.md b/README.md index f7b02b4..b24337d 100644 --- a/README.md +++ b/README.md @@ -12,49 +12,56 @@ 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 package provides complete Laravel integration with RoadRunner, offering: + +- 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) -> **Note:** There is an article that explains all the RoadRunner -> plugins: https://butschster.medium.com/roadrunner-an-underrated-powerhouse-for-php-applications-46410b0abc +> [!TIP] +> [There is an article][rr-plugins-article] that explains all the RoadRunner plugins. -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. +## Table of Contents -Our **Laravel Bridge** solves this problem by taking a different approach: +- [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) +- [Support](#support) +- [License](#license) -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 +## Get Started -**This way, you get the best of both worlds:** Octane's state management and RoadRunner's full plugin ecosystem. +### 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 - -After package installation, you can download and install [RoadRunner][roadrunner] binary -using [DLoad][dload] package: +Download and install RoadRunner binary using DLoad: -```bash +```shell ./vendor/bin/dload get rr ``` -### Basic Configuration (.rr.yaml) +### Configuration Create a `.rr.yaml` configuration file in your project root: @@ -65,7 +72,6 @@ rpc: server: command: 'php vendor/bin/rr-worker start' - relay: pipes http: address: 0.0.0.0:8080 @@ -82,49 +88,30 @@ http: forbid: [ ".php" ] ``` -## RoadRunner Worker Configuration - -You can configure workers in `config/roadrunner.php` file in the `workers` section: - -```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; +### Starting the Server -return [ - // ... other configuration options ... +Start the RoadRunner server with: - '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, +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 @@ -148,16 +135,17 @@ 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. +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' => [ @@ -166,7 +154,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' => [ @@ -192,10 +180,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 @@ -203,6 +188,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][roadrunner-docs-jobs]. + ### gRPC Plugin The gRPC plugin enables serving gRPC services with your Laravel application. @@ -263,36 +251,54 @@ 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: +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. -```shell script -./rr serve +### 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, + Mode::MODE_GRPC => GrpcWorker::class, + Mode::MODE_TEMPORAL => TemporalWorker::class, + ], +]; ``` -## Custom Workers +### Creating Custom Workers -You can create your own custom workers by implementing the `Spiral\RoadRunnerLaravel\WorkerInterface`: +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; @@ -304,30 +310,46 @@ 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 } } ``` -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, ], ]; ``` +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. @@ -374,6 +396,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/ @@ -389,3 +413,11 @@ 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 + +[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 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 + + - - - - + + + - - - - - + + +