diff --git a/.env.sample b/.env.sample
index ce86754..d956ffc 100644
--- a/.env.sample
+++ b/.env.sample
@@ -1,8 +1,8 @@
# Debug mode disabled view cache and enabled higher verbosity.
-DEBUG = true
+DEBUG=true
# Set to application specific value, used to encrypt/decrypt cookies and etc.
-ENCRYPTER_KEY = {encrypt-key}
+ENCRYPTER_KEY={encrypt-key}
# Set to TRUE to disable confirmation in `migrate` commands.
-SAFE_MIGRATIONS = true
\ No newline at end of file
+SAFE_MIGRATIONS=true
\ No newline at end of file
diff --git a/.rr.yaml b/.rr.yaml
index 8489436..256a3a0 100644
--- a/.rr.yaml
+++ b/.rr.yaml
@@ -1,25 +1,29 @@
-# grpc service configuration.
-grpc:
- listen: tcp://0.0.0.0:50051
- proto: "proto/service.proto"
- workers.command: "php app.php"
- tls.key: "app.key"
- tls.cert: "app.crt"
+version: '2.7'
-# queue and jobs
-jobs:
- dispatch:
- app-job-*.pipeline: "local"
- pipelines:
- local:
- broker: "ephemeral"
- consume: ["local"]
+rpc:
+ listen: tcp://127.0.0.1:6001
- workers:
+server:
command: "php app.php"
- pool.numWorkers: 2
+ relay: pipes
-# control the max memory usage
-limit:
- services:
- grpc.maxMemory: 100
\ No newline at end of file
+# queue and jobs
+jobs:
+ consume: [ "local" ]
+ pool:
+ num_workers: 2
+ supervisor:
+ max_worker_memory: 100
+
+# grpc service configuration.
+grpc:
+ listen: "tcp://0.0.0.0:50051"
+ proto:
+ - "proto/service.proto"
+ tls:
+ key: "app.key"
+ cert: "app.crt"
+ pool:
+ num_workers: 2
+ supervisor:
+ max_worker_memory: 100
diff --git a/README.md b/README.md
index 2a9073b..59e7efe 100644
--- a/README.md
+++ b/README.md
@@ -13,7 +13,7 @@ Spiral Framework is a High-Performance PHP/Go Full-Stack framework and group of
Server Requirements
--------
Make sure that your server is configured with following PHP version and extensions:
-* PHP 7.2+, 64bit
+* PHP 8.0+, 64bit
* **mb-string** extension
* PDO Extension with desired database drivers
* [Install](https://github.com/protocolbuffers/protobuf/tree/master/php) `protobuf-ext` to gain higher performance.
@@ -48,11 +48,16 @@ In order to run GRPC server you must specify location of server key and certific
```yaml
grpc:
- listen: tcp://0.0.0.0:50051
- proto: "proto/service.proto"
- workers.command: "php app.php"
- tls.key: "app.key"
- tls.cert: "app.crt"
+ listen: "tcp://0.0.0.0:50051"
+ proto:
+ - "proto/service.proto"
+ tls:
+ key: "app.key"
+ cert: "app.crt"
+ pool:
+ num_workers: 2
+ supervisor:
+ max_worker_memory: 100
```
To issue local certificate:
@@ -64,13 +69,13 @@ $ openssl req -newkey rsa:2048 -nodes -keyout app.key -x509 -days 365 -out app.c
To start application server execute:
```
-$ ./spiral serve -v -d
+$ ./rr serve
```
On Windows:
```
-$ spiral.exe serve -v -d
+$ rr.exe serve
```
You can test your endpoints using any GRPC client. For example using [grpcui](https://github.com/fullstorydev/grpcui):
@@ -90,7 +95,7 @@ In order to compile protobuf declarations into service code make sure to install
To update or generate service code for your application run:
```
-$ php ./app.php grpc:generate proto/service.proto
+$ php ./app.php grpc:generate
```
Generated code will be available in `app/src/Service`. Implemented service will be automatically registered in your application.
diff --git a/app.php b/app.php
index 9926415..d624f98 100644
--- a/app.php
+++ b/app.php
@@ -30,9 +30,13 @@
//
// Initialize shared container, bindings, directories and etc.
//
-$app = App::init(['root' => __DIR__]);
+$app = App::create(
+ directories: ['root' => __DIR__]
+)->run();
-if ($app !== null) {
- $code = (int)$app->serve();
- exit($code);
+if ($app === null) {
+ exit(255);
}
+
+$code = (int)$app->serve();
+exit($code);
diff --git a/app/config/database.php b/app/config/database.php
index 522addb..382c601 100644
--- a/app/config/database.php
+++ b/app/config/database.php
@@ -9,6 +9,8 @@
declare(strict_types=1);
+use Cycle\Database\Config;
+
return [
/**
* Default database connection
@@ -36,10 +38,9 @@
* the driver class and its connection options.
*/
'drivers' => [
- 'sqlite' => [
- 'driver' => \Spiral\Database\Driver\SQLite\SQLiteDriver::class,
- 'connection' => 'sqlite:' . directory('root') . 'app.db',
- 'profiling' => true,
- ],
+ 'sqlite' => new Config\SQLiteDriverConfig(
+ connection: new Config\SQLite\MemoryConnectionConfig(),
+ queryCache: true
+ ),
],
];
diff --git a/app/config/grpc.php b/app/config/grpc.php
new file mode 100644
index 0000000..f07051b
--- /dev/null
+++ b/app/config/grpc.php
@@ -0,0 +1,13 @@
+ __DIR__ . '/../../protoc-gen-php-grpc',
+ 'services' => [
+ __DIR__ . '/../../proto/service.proto',
+ ],
+];
diff --git a/app/src/App.php b/app/src/App.php
index 8210c7a..314db18 100644
--- a/app/src/App.php
+++ b/app/src/App.php
@@ -13,8 +13,10 @@
use Spiral\Bootloader as Framework;
use Spiral\DotEnv\Bootloader as DotEnv;
+use Spiral\Cycle\Bootloader as CycleBridge;
use Spiral\Framework\Kernel;
use Spiral\Prototype\Bootloader as Prototype;
+use Spiral\RoadRunnerBridge\Bootloader as RoadRunnerBridge;
class App extends Kernel
{
@@ -31,20 +33,23 @@ class App extends Kernel
Framework\Security\EncrypterBootloader::class,
// Databases
- Framework\Database\DatabaseBootloader::class,
- Framework\Database\MigrationsBootloader::class,
+ CycleBridge\DatabaseBootloader::class,
+ CycleBridge\MigrationsBootloader::class,
// ORM
- Framework\Cycle\CycleBootloader::class,
- Framework\Cycle\ProxiesBootloader::class,
- Framework\Cycle\AnnotatedBootloader::class,
+ CycleBridge\SchemaBootloader::class,
+ CycleBridge\CycleOrmBootloader::class,
+ CycleBridge\AnnotatedBootloader::class,
+ CycleBridge\CommandBootloader::class,
// Dispatchers
- Framework\GRPC\GRPCBootloader::class,
- Framework\Jobs\JobsBootloader::class,
+ RoadRunnerBridge\GRPCBootloader::class,
+ RoadRunnerBridge\QueueBootloader::class,
// Framework commands
Framework\CommandBootloader::class,
+ CycleBridge\CommandBootloader::class,
+ RoadRunnerBridge\CommandBootloader::class,
// Debugging
Framework\DebugBootloader::class,
diff --git a/app/src/Command/GetBinaryCommand.php b/app/src/Command/GetBinaryCommand.php
new file mode 100644
index 0000000..43c8cb1
--- /dev/null
+++ b/app/src/Command/GetBinaryCommand.php
@@ -0,0 +1,237 @@
+os = new OperatingSystemOption($this);
+ $this->arch = new ArchitectureOption($this);
+ $this->version = new VersionFilterOption($this);
+ $this->location = new InstallationLocationOption($this);
+ $this->stability = new StabilityOption($this);
+ }
+
+ public function getDescription(): string
+ {
+ return 'Install or update protoc-gen-php-grpc binary';
+ }
+
+ public function execute(InputInterface $input, OutputInterface $output): int
+ {
+ $io = $this->io($input, $output);
+
+ $target = $this->location->get($input, $io);
+ $repository = $this->getRepository();
+
+ $output->writeln('');
+ $output->writeln(' Environment:');
+ $output->writeln(\sprintf(' - Version: %s', $this->version->get($input, $io)));
+ $output->writeln(\sprintf(' - Stability: %s', $this->stability->get($input, $io)));
+ $output->writeln(\sprintf(' - Operating System: %s', $this->os->get($input, $io)));
+ $output->writeln(\sprintf(' - Architecture: %s', $this->arch->get($input, $io)));
+ $output->writeln('');
+
+ // List of all available releases
+ $releases = $this->version->find($input, $io, $repository);
+
+ /**
+ * @var AssetInterface $asset
+ * @var ReleaseInterface $release
+ */
+ [$asset, $release] = $this->findAsset($repository, $releases, $input, $io);
+
+ // Installation
+ $output->writeln(
+ \sprintf(' - %s', $release->getRepositoryName()) .
+ \sprintf(' (%s):', $release->getVersion()) .
+ ' Downloading...'
+ );
+
+ if ($output->isVerbose()) {
+ $output->writeln(\sprintf(' -- %s', $asset->getName()));
+ }
+
+ // Install rr binary
+ $file = $this->installBinary($target, $release, $asset, $io, $output);
+
+ // Success
+ if ($file === null) {
+ $io->warning('protoc-gen-php-grpc has not been installed');
+
+ return 1;
+ }
+
+ return 0;
+ }
+
+ private function installBinary(
+ string $target,
+ ReleaseInterface $release,
+ AssetInterface $asset,
+ StyleInterface $io,
+ OutputInterface $out
+ ): ?\SplFileInfo {
+ $extractor = $this->assetToArchive($asset, $out)
+ ->extract([
+ 'protoc-gen-php-grpc.exe' => $target . '/protoc-gen-php-grpc.exe',
+ 'protoc-gen-php-grpc' => $target . '/protoc-gen-php-grpc',
+ ]);
+
+ $file = null;
+ while ($extractor->valid()) {
+ $file = $extractor->current();
+
+ if (!$this->checkExisting($file, $io)) {
+ $extractor->send(false);
+ continue;
+ }
+
+ // Success
+ $path = $file->getRealPath() ?: $file->getPathname();
+ $message = 'protoc-gen-php-grpc (%s) has been installed into %s';
+ $message = \sprintf($message, $release->getVersion(), $path);
+ $out->writeln($message);
+
+ $extractor->next();
+
+ if (!$file->isExecutable()) {
+ @chmod($file->getRealPath(), 0755);
+ }
+ }
+
+ return $file;
+ }
+
+ private function checkExisting(\SplFileInfo $bin, StyleInterface $io): bool
+ {
+ if (\is_file($bin->getPathname())) {
+ $io->warning('protoc-gen-php-grpc binary file already exists!');
+
+ if (!$io->confirm('Do you want overwrite it?', false)) {
+ $io->note('Skipping protoc-gen-php-grpc installation...');
+
+ return false;
+ }
+ }
+
+ return true;
+ }
+
+ private function findAsset(
+ RepositoryInterface $repo,
+ ReleasesCollection $releases,
+ InputInterface $in,
+ StyleInterface $io
+ ): array {
+ $osOption = $this->os->get($in, $io);
+ $archOption = $this->arch->get($in, $io);
+ $stabilityOption = $this->stability->get($in, $io);
+
+ /** @var ReleaseInterface[] $filtered */
+ $filtered = $releases
+ ->minimumStability($stabilityOption)
+ ->withAssets();
+
+ foreach ($filtered as $release) {
+ $asset = $release->getAssets()
+ ->filter(
+ static fn (AssetInterface $asset): bool =>
+ \str_starts_with($asset->getName(), 'protoc-gen-php-grpc')
+ )
+ ->whereArchitecture($archOption)
+ ->whereOperatingSystem($osOption)
+ ->first();
+
+ if ($asset === null) {
+ $io->warning(
+ \vsprintf('%s %s does not contain available assembly (further search in progress)', [
+ $repo->getName(),
+ $release->getVersion(),
+ ])
+ );
+
+ continue;
+ }
+
+ return [$asset, $release];
+ }
+
+ $message = \vsprintf(self::ERROR_ENVIRONMENT, [
+ $this->os->getName(),
+ $osOption,
+ $this->arch->getName(),
+ $archOption,
+ $this->stability->getName(),
+ $stabilityOption,
+ $this->version->choices($releases),
+ ]);
+
+ throw new \UnexpectedValueException($message);
+ }
+
+ private function assetToArchive(AssetInterface $asset, OutputInterface $out, string $temp = null): ArchiveInterface
+ {
+ $factory = new Factory();
+
+ $progress = new ProgressBar($out);
+ $progress->setFormat(' [%bar%] %percent:3s%% (%size%Kb/%total%Kb)');
+ $progress->setMessage('0.00', 'size');
+ $progress->setMessage('?.??', 'total');
+ $progress->display();
+
+ try {
+ return $factory->fromAsset($asset, function (int $size, int $total) use ($progress) {
+ if ($progress->getMaxSteps() !== $total) {
+ $progress->setMaxSteps($total);
+ }
+
+ if ($progress->getStartTime() === 0) {
+ $progress->start();
+ }
+
+ $progress->setMessage(\number_format($size / 1000, 2), 'size');
+ $progress->setMessage(\number_format($total / 1000, 2), 'total');
+
+ $progress->setProgress($size);
+ }, $temp);
+ } finally {
+ $progress->clear();
+ }
+ }
+}
diff --git a/app/src/Job/Ping.php b/app/src/Job/Ping.php
index 06fa222..cda2939 100644
--- a/app/src/Job/Ping.php
+++ b/app/src/Job/Ping.php
@@ -11,18 +11,14 @@
namespace App\Job;
-use Spiral\Jobs\JobHandler;
+use Spiral\Queue\JobHandler;
/**
* (QueueInterface)->push(new PingJob(["value"=>"my value"]));
*/
class Ping extends JobHandler
{
- /**
- * @param string $id
- * @param string $value
- */
- public function invoke(string $id, string $value)
+ public function invoke(string $id, string $value): void
{
// do something
error_log("pong by {$id}, value `{$value}`");
diff --git a/app/src/Service/Service.php b/app/src/Service/Service.php
index 0cbb5da..f1c2549 100644
--- a/app/src/Service/Service.php
+++ b/app/src/Service/Service.php
@@ -13,27 +13,16 @@
use App\Job\Ping;
use Spiral\Core\Container\SingletonInterface;
-use Spiral\GRPC;
-use Spiral\Jobs\QueueInterface;
+use Spiral\RoadRunner\GRPC;
+use Spiral\Queue\QueueInterface;
class Service implements ServiceInterface, SingletonInterface
{
- /** @var QueueInterface */
- private $queue;
-
- /**
- * @param QueueInterface $queue
- */
- public function __construct(QueueInterface $queue)
- {
- $this->queue = $queue;
+ public function __construct(
+ private QueueInterface $queue
+ ) {
}
- /**
- * @param GRPC\ContextInterface $ctx
- * @param Message\Message $in
- * @return Message\Message
- */
public function Welcome(GRPC\ContextInterface $ctx, Message\Message $in): Message\Message
{
$out = new Message\Message();
@@ -42,11 +31,6 @@ public function Welcome(GRPC\ContextInterface $ctx, Message\Message $in): Messag
return $out;
}
- /**
- * @param GRPC\ContextInterface $ctx
- * @param Message\Job $in
- * @return Message\JobID
- */
public function Schedule(GRPC\ContextInterface $ctx, Message\Job $in): Message\JobID
{
$id = $this->queue->push(Ping::class, ['value' => $in->getValue()]);
diff --git a/app/src/Service/ServiceInterface.php b/app/src/Service/ServiceInterface.php
index 3ed9e15..1326cdb 100644
--- a/app/src/Service/ServiceInterface.php
+++ b/app/src/Service/ServiceInterface.php
@@ -5,7 +5,7 @@
namespace App\Service;
-use Spiral\GRPC;
+use Spiral\RoadRunner\GRPC;
use App\Service\Message;
interface ServiceInterface extends GRPC\ServiceInterface
diff --git a/composer.json b/composer.json
index 51bf677..f586ee6 100644
--- a/composer.json
+++ b/composer.json
@@ -15,24 +15,19 @@
}
],
"require": {
- "php": ">=7.2",
- "spiral/framework": "^2.8",
- "spiral/jobs": "^2.0",
- "spiral/php-grpc": "^1.0",
- "spiral/roadrunner": "^1.4",
- "spiral/database": "^2.3",
- "spiral/migrations": "^2.0",
- "cycle/orm": "^1.0",
- "cycle/proxy-factory": "^1.0",
- "cycle/annotated": "^2.0",
- "cycle/migrations": "^1.0"
+ "php": ">=8.0",
+ "spiral/framework": "^2.13",
+ "spiral/queue": "^2.13",
+ "spiral/roadrunner-grpc": "^2.0",
+ "spiral/roadrunner-bridge": "^1.1",
+ "spiral/cycle-bridge": "^1.1"
},
"scripts": {
"post-create-project-cmd": [
"php -r \"copy('.env.sample', '.env');\"",
"php app.php encrypt:key -m .env",
"php app.php configure -vv",
- "spiral get-binary"
+ "rr get-binary"
]
},
"autoload": {
@@ -46,7 +41,10 @@
}
},
"config": {
- "sort-packages": true
+ "sort-packages": true,
+ "allow-plugins": {
+ "spiral/composer-publish-plugin": true
+ }
},
"minimum-stability": "dev",
"prefer-stable": true
diff --git a/docker-compose.yaml b/docker-compose.yaml
new file mode 100644
index 0000000..7e0b377
--- /dev/null
+++ b/docker-compose.yaml
@@ -0,0 +1,11 @@
+version: '3.5'
+
+services:
+ compiler:
+ image: spiralscout/php81-grpc:1.0.0
+ volumes:
+ - .:/app
+ command: >
+ bash -c "cd ./app
+ && [ -f ./protoc-gen-php-grpc ] || php ./app.php get-protoc-binary
+ && php ./app.php grpc:generate"