Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 10 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -28,8 +28,16 @@ RoadRunner includes PSR-7/PSR-17 compatible HTTP and HTTP/2 server and can be us
This repository contains commands to help you work with the RoadRunner, such as:

- `get-binary` (or `get`) - allows to install the latest version of the RoadRunner compatible with
your environment (operating system, processor architecture, runtime, etc...)

your environment (operating system, processor architecture, runtime, etc...).
Also, this command creates an example `.rr.yaml` configuration file. If don't use the command without additional options
`plugin` and `preset`, an example with a complete configuration file will be created.
Using the `plugin` option (shortcut `p`) can create an example configuration file with only plugins needed.
For example, with http plugin only: `get-binary -p http`, http and jobs: `get-binary -p http -p jobs`.
Available plugins: `amqp`, `beanstalk`, `boltdb`, `broadcast`, `endure`, `fileserver`, `grpc`, `http`, `jobs`, `kv`,
`logs`, `metrics`, `nats`, `redis`, `reload`, `rpc`, `server`, `service`, `sqs`, `status`, `tcp`, `temporal`, `websockets`.
Using the `preset` option can create an example configuration file with popular plugins for different typical tasks.
For example, with web preset: `get-binary --preset web`.
Available presets: `web` (contains plugins `http`, `jobs`).
- `versions` - displays a list of available RoadRunner binary versions.

Testing:
Expand Down
4 changes: 3 additions & 1 deletion composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -19,11 +19,13 @@
"require": {
"php": ">=7.4",
"ext-json": "*",
"composer/semver": "^3.2",
"spiral/roadrunner-worker": ">=2.0.2",
"spiral/tokenizer": "^2.13 || ^3.0",
"symfony/console": "^4.4|^5.0|^6.0",
"symfony/http-client": "^4.4|^5.0|^6.0",
"symfony/polyfill-php80": "^1.22",
"composer/semver": "^3.2"
"symfony/yaml": "^5.4 || ^6.0"
},
"require-dev": {
"jetbrains/phpstorm-attributes": "^1.0",
Expand Down
79 changes: 79 additions & 0 deletions src/Configuration/Generator.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
<?php

declare(strict_types=1);

namespace Spiral\RoadRunner\Console\Configuration;

use Spiral\RoadRunner\Console\Configuration\Section\Rpc;
use Spiral\RoadRunner\Console\Configuration\Section\SectionInterface;
use Spiral\RoadRunner\Console\Configuration\Section\Version;
use Symfony\Component\Yaml\Yaml;

class Generator
{
/** @var SectionInterface[] */
protected array $sections = [];

/** @psalm-var non-empty-array<class-string<SectionInterface>> */
protected const REQUIRED_SECTIONS = [
Version::class,
Rpc::class,
];

public function generate(Plugins $plugins): string
{
$this->collectSections($plugins->getPlugins());

return $this->getHeaderComment() . PHP_EOL . Yaml::dump($this->getContent(), 10);
}

protected function getContent(): array
{
$content = [];
foreach ($this->sections as $section) {
$content += $section->render();
}

return $content;
}

protected function collectSections(array $plugins): void
{
$sections = \array_merge(self::REQUIRED_SECTIONS, $plugins);

foreach ($sections as $section) {
$this->fromSection(new $section());
}
}

/** @psalm-return non-empty-array<SectionInterface> */
protected function fromSection(SectionInterface $section): void
{
if (!isset($this->sections[\get_class($section)])) {
$this->sections[\get_class($section)] = $section;
}

foreach ($section->getRequired() as $required) {
$this->fromSection(new $required());
}
}

protected function getHeaderComment(): string
{
$comment = [
'########################################################################################',
'# THIS IS SAMPLE OF THE CONFIGURATION #',
'# IT\'S NOT A DEFAULT CONFIGURATION, IT\'S JUST A SIMPLE SAMPLE #',
'# MORE DOCS CAN BE FOUND HERE: <https://roadrunner.dev/docs/intro-config> #',
'########################################################################################',
'',
'# Hint: RR will replace any config options using reference to environment variables,',
'# eg.: `option_key: ${ENVIRONMENT_VARIABLE_NAME}`.',
'',
'# Important: TCP port numbers for each plugin (rpc, http, etc) must be unique!',
''
];

return \implode(PHP_EOL, $comment);
}
}
89 changes: 89 additions & 0 deletions src/Configuration/Plugins.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
<?php

declare(strict_types=1);

namespace Spiral\RoadRunner\Console\Configuration;

use Spiral\RoadRunner\Console\Configuration\Section\SectionInterface;
use Spiral\Tokenizer\ClassLocator;
use Symfony\Component\Finder\Finder;

final class Plugins
{
/**
* @var string[]
*
* Requested plugins in a shortname format.
*/
private array $requestedPlugins;

/**
* @psalm-var non-empty-array<class-string<SectionInterface>>
*
* All plugins.
*/
private array $available;

private function __construct(array $plugins)
{
$this->available = $this->getAvailable();
$this->requestedPlugins = $plugins;
}

public static function fromPlugins(array $plugins): self
{
return new self($plugins);
}

public static function fromPreset(string $preset): self
{
$plugins = [];
switch ($preset) {
case Presets::WEB_PRESET_NANE:
$plugins = Presets::WEB_PLUGINS;
}

return new self(\array_map(function (string $plugin) {
return $plugin::getShortName();
}, $plugins));
}

public function getPlugins(): array
{
if ($this->requestedPlugins === []) {
return $this->available;
}

$plugins = [];
foreach ($this->available as $plugin) {
if (\in_array($plugin::getShortName(), $this->requestedPlugins, true)) {
$plugins[] = $plugin;
}
}

return $plugins;
}

private function getAvailable(): array
{
$finder = new Finder();
$finder->files()->in(__DIR__ . '/Section')->name('*.php');

$locator = new ClassLocator($finder);

/** @var SectionInterface[] $available */
$available = [];
foreach ($locator->getClasses() as $class) {
if ($this->isPlugin($class)) {
$available[] = $class->getName();
}
}

return $available;
}

private function isPlugin(\ReflectionClass $class): bool
{
return $class->implementsInterface(SectionInterface::class) && !$class->isAbstract() && !$class->isInterface();
}
}
18 changes: 18 additions & 0 deletions src/Configuration/Presets.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
<?php

declare(strict_types=1);

namespace Spiral\RoadRunner\Console\Configuration;

use Spiral\RoadRunner\Console\Configuration\Section\Http;
use Spiral\RoadRunner\Console\Configuration\Section\Jobs;

final class Presets
{
public const WEB_PRESET_NANE = 'web';

public const WEB_PLUGINS = [
Http::class,
Jobs::class
];
}
15 changes: 15 additions & 0 deletions src/Configuration/Section/AbstractSection.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
<?php

declare(strict_types=1);

namespace Spiral\RoadRunner\Console\Configuration\Section;

abstract class AbstractSection implements SectionInterface
{
public function getRequired(): array
{
return [];
}

abstract public function render(): array;
}
24 changes: 24 additions & 0 deletions src/Configuration/Section/Amqp.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
<?php

declare(strict_types=1);

namespace Spiral\RoadRunner\Console\Configuration\Section;

final class Amqp extends AbstractSection
{
private const NAME = 'amqp';

public function render(): array
{
return [
self::NAME => [
'addr' => 'amqp://guest:guest@127.0.0.1:5672/'
]
];
}

public static function getShortName(): string
{
return self::NAME;
}
}
25 changes: 25 additions & 0 deletions src/Configuration/Section/Beanstalk.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
<?php

declare(strict_types=1);

namespace Spiral\RoadRunner\Console\Configuration\Section;

final class Beanstalk extends AbstractSection
{
private const NAME = 'beanstalk';

public function render(): array
{
return [
self::NAME => [
'addr' => 'tcp://127.0.0.1:11300',
'timeout' => '10s'
]
];
}

public static function getShortName(): string
{
return self::NAME;
}
}
24 changes: 24 additions & 0 deletions src/Configuration/Section/Boltdb.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
<?php

declare(strict_types=1);

namespace Spiral\RoadRunner\Console\Configuration\Section;

final class Boltdb extends AbstractSection
{
private const NAME = 'boltdb';

public function render(): array
{
return [
self::NAME => [
'permissions' => 0777
]
];
}

public static function getShortName(): string
{
return self::NAME;
}
}
55 changes: 55 additions & 0 deletions src/Configuration/Section/Broadcast.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
<?php

declare(strict_types=1);

namespace Spiral\RoadRunner\Console\Configuration\Section;

final class Broadcast extends AbstractSection
{
private const NAME = 'broadcast';

public function render(): array
{
return [
self::NAME => [
'default' => [
'driver' => 'memory',
'config' => []
],
'default-redis' => [
'driver' => 'redis',
'config' => [
'addrs' => [
'localhost:6379'
],
'master_name' => '',
'username' => '',
'password' => '',
'db' => 0,
'sentinel_password' => '',
'route_by_latency' => false,
'route_randomly' => false,
'dial_timeout' => 0,
'max_retries' => 1,
'min_retry_backoff' => 0,
'max_retry_backoff' => 0,
'pool_size' => 0,
'min_idle_conns' => 0,
'max_conn_age' => 0,
'read_timeout' => 0,
'write_timeout' => 0,
'pool_timeout' => 0,
'idle_timeout' => 0,
'idle_check_freq' => 0,
'read_only' => false
]
]
]
];
}

public static function getShortName(): string
{
return self::NAME;
}
}
26 changes: 26 additions & 0 deletions src/Configuration/Section/Endure.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
<?php

declare(strict_types=1);

namespace Spiral\RoadRunner\Console\Configuration\Section;

final class Endure extends AbstractSection
{
private const NAME = 'endure';

public function render(): array
{
return [
self::NAME => [
'grace_period' => '30s',
'print_graph' => false,
'log_level' => 'error'
]
];
}

public static function getShortName(): string
{
return self::NAME;
}
}
Loading