Skip to content

Commit

Permalink
Improved generator of GRPC proto files
Browse files Browse the repository at this point in the history
  • Loading branch information
meekstellar committed Nov 17, 2022
1 parent ee79cc9 commit 578702a
Show file tree
Hide file tree
Showing 11 changed files with 293 additions and 40 deletions.
26 changes: 22 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -933,6 +933,24 @@ return [
'binaryPath' => null,
// 'binaryPath' => __DIR__.'/../../protoc-gen-php-grpc',

/**
* Path, where generated DTO files put.
* Default: null
*/
'generatedPath' => null,

/**
* Base namespace for generated proto files.
* Default: null
*/
'namespace' => null,

/**
* Root path for all proto files in which imports will be searched.
* Default: null
*/
'servicesBasePath' => null

'services' => [
__DIR__.'/../../proto/echo.proto',
],
Expand All @@ -947,10 +965,10 @@ php app.php grpc:generate

#### Console commands

| Command | Description |
|--------------------------------------------|---------------------------------------------------------|
| grpc:services | List available GRPC services |
| grpc:generate {path=auto} {namespace=auto} | Generate GPRC service code using protobuf specification |
| Command | Description |
|--------------------------------------------|------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| grpc:services | List available GRPC services |
| grpc:generate {path=auto} {namespace=auto} | Generate GPRC service code using protobuf specification. By default `path` and `namespace` options are `auto`. Defined values from config will be used in `auto` mode, by default. |

#### Example GRPC service

Expand Down
4 changes: 4 additions & 0 deletions src/Bootloader/GRPCBootloader.php
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,10 @@ private function initGrpcConfig(): void
*/
'binaryPath' => null,

'generatedPath' => null,
'namespace' => null,
'servicesBasePath' => null,

'services' => [],

'interceptors' => [],
Expand Down
27 changes: 27 additions & 0 deletions src/Config/GRPCConfig.php
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,9 @@ final class GRPCConfig extends InjectableConfig

protected array $config = [
'binaryPath' => null,
'generatedPath' => null,
'namespace' => null,
'servicesBasePath' => null,
'services' => [],
'interceptors' => [],
];
Expand All @@ -24,6 +27,30 @@ public function getBinaryPath(): ?string
return $this->config['binaryPath'] ?? null;
}

/**
* Path, where generated DTO files put.
*/
public function getGeneratedPath(): ?string
{
return $this->config['generatedPath'] ?? null;
}

/**
* Base namespace for generated proto files.
*/
public function getNamespace(): ?string
{
return $this->config['namespace'] ?? null;
}

/**
* Root path for all proto files in which imports will be searched.
*/
public function getServicesBasePath(): ?string
{
return $this->config['servicesBasePath'] ?? null;
}

/**
* @return array<class-string<ServiceInterface>>
*/
Expand Down
26 changes: 20 additions & 6 deletions src/Console/Command/GRPC/GenerateCommand.php
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,9 @@
use Spiral\Console\Command;
use Spiral\Files\FilesInterface;
use Spiral\RoadRunnerBridge\Config\GRPCConfig;
use Spiral\RoadRunnerBridge\GRPC\CommandExecutor;
use Spiral\RoadRunnerBridge\GRPC\Exception\CompileException;
use Spiral\RoadRunnerBridge\GRPC\ProtocCommandBuilder;
use Spiral\RoadRunnerBridge\GRPC\ProtoCompiler;
use Spiral\RoadRunnerBridge\GRPC\ProtoRepository\ProtoFilesRepositoryInterface;

Expand All @@ -31,16 +33,20 @@ public function perform(
$binaryPath = $config->getBinaryPath();

if ($binaryPath !== null && !\file_exists($binaryPath)) {
$this->sprintf('<error>PHP Server plugin binary `%s` not found.</error>', $binaryPath);
$this->sprintf(
'<error>Protoc plugin binary `%s` was not found. Use command `./vendor/bin/rr download-protoc-binary` to download it.`</error>',
$binaryPath
);

return self::FAILURE;
}

$compiler = new ProtoCompiler(
$this->getPath($kernel),
$this->getNamespace($kernel),
$this->getPath($kernel, $config->getGeneratedPath()),
$this->getNamespace($kernel, $config->getNamespace()),
$files,
$binaryPath
new ProtocCommandBuilder($files, $config, $binaryPath),
new CommandExecutor()
);

foreach ($repository->getProtos() as $protoFile) {
Expand Down Expand Up @@ -81,13 +87,17 @@ public function perform(
/**
* Get or detect base source code path. By default fallbacks to kernel location.
*/
protected function getPath(KernelInterface $kernel): string
protected function getPath(KernelInterface $kernel, ?string $generatedPath): string
{
$path = $this->argument('path');
if ($path !== 'auto') {
return $path;
}

if ($generatedPath !== null) {
return $generatedPath;
}

$r = new \ReflectionObject($kernel);

return \dirname($r->getFileName());
Expand All @@ -96,13 +106,17 @@ protected function getPath(KernelInterface $kernel): string
/**
* Get or detect base namespace. By default fallbacks to kernel namespace.
*/
protected function getNamespace(KernelInterface $kernel): string
protected function getNamespace(KernelInterface $kernel, ?string $protoNamespace): string
{
$namespace = $this->argument('namespace');
if ($namespace !== 'auto') {
return $namespace;
}

if ($protoNamespace !== null) {
return $protoNamespace;
}

return (new \ReflectionObject($kernel))->getNamespaceName();
}
}
28 changes: 28 additions & 0 deletions src/GRPC/CommandExecutor.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
<?php

declare(strict_types=1);

namespace Spiral\RoadRunnerBridge\GRPC;

use Spiral\RoadRunnerBridge\GRPC\Exception\CompileException;

/**
* @internal
*/
final class CommandExecutor
{
public function execute(string $command): string
{
\exec(
$command,
$output,
$exitCode
);

if ($exitCode !== 0) {
throw new CompileException(\implode("\n", $output), $exitCode);
}

return \trim(\implode("\n", $output), "\n ,");
}
}
37 changes: 7 additions & 30 deletions src/GRPC/ProtoCompiler.php
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@

/**
* Compiles GRPC protobuf declaration and moves files into proper location.
*
* @internal
*/
final class ProtoCompiler
{
Expand All @@ -18,7 +20,8 @@ public function __construct(
private readonly string $basePath,
string $baseNamespace,
private readonly FilesInterface $files,
private readonly ?string $protocBinaryPath = null
private readonly ProtocCommandBuilder $commandBuilder,
private readonly CommandExecutor $executor
) {
$this->baseNamespace = \str_replace('\\', '/', \rtrim($baseNamespace, '\\'));
}
Expand All @@ -30,25 +33,10 @@ public function compile(string $protoFile): array
{
$tmpDir = $this->tmpDir();

\exec(
\sprintf(
'protoc %s --php_out=%s --php-grpc_out=%s -I %s %s 2>&1',
$this->protocBinaryPath ? '--plugin=' . $this->protocBinaryPath : '',
\escapeshellarg($tmpDir),
\escapeshellarg($tmpDir),
\escapeshellarg(dirname($protoFile)),
\implode(' ', \array_map('escapeshellarg', $this->getProtoFiles($protoFile)))
),
$output,
$exitCode
$output = $this->executor->execute(
$this->commandBuilder->build(\dirname($protoFile), $tmpDir)
);

if ($exitCode !== 0) {
throw new CompileException(\implode("\n", $output), $exitCode);
}

$output = \trim(\implode("\n", $output), "\n ,");

if ($output !== '') {
$this->files->deleteDirectory($tmpDir);
throw new CompileException($output);
Expand All @@ -68,7 +56,7 @@ public function compile(string $protoFile): array
private function copy(string $tmpDir, string $file): string
{
$source = \ltrim($this->files->relativePath($file, $tmpDir), '\\/');
if (str_starts_with($source, $this->baseNamespace)) {
if (\str_starts_with($source, $this->baseNamespace)) {
$source = \ltrim(\substr($source, \strlen($this->baseNamespace)), '\\/');
}

Expand All @@ -87,15 +75,4 @@ private function tmpDir(): string

return $this->files->normalizePath($directory, true);
}

/**
* Include all proto files from the directory.
*/
private function getProtoFiles(string $protoFile): array
{
return \array_filter(
$this->files->getFiles(\dirname($protoFile)),
static fn (string $file) => str_contains($file, '.proto')
);
}
}
58 changes: 58 additions & 0 deletions src/GRPC/ProtocCommandBuilder.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
<?php

declare(strict_types=1);

namespace Spiral\RoadRunnerBridge\GRPC;

use Spiral\Files\FilesInterface;
use Spiral\RoadRunnerBridge\Config\GRPCConfig;

/**
* @internal
*/
final class ProtocCommandBuilder
{
public function __construct(
private readonly FilesInterface $files,
private readonly GRPCConfig $config,
private readonly string $protocBinaryPath
) {
}

public function build(string $protoDir, string $tmpDir): string
{
return \sprintf(
'protoc %s --php_out=%s --php-grpc_out=%s%s %s 2>&1',
$this->protocBinaryPath ? '--plugin=' . $this->protocBinaryPath : '',
\escapeshellarg($tmpDir),
\escapeshellarg($tmpDir),
$this->buildDirs($protoDir),
\implode(' ', \array_map('escapeshellarg', $this->getProtoFiles($protoDir)))
);
}

/**
* Include all proto files from the directory.
*/
private function getProtoFiles(string $protoDir): array
{
return \array_filter(
$this->files->getFiles($protoDir),
static fn(string $file) => \str_ends_with($file, '.proto')
);
}

private function buildDirs(string $protoDir): string
{
$dirs = \array_filter([
$this->config->getServicesBasePath(),
$protoDir,
]);

if ($dirs === []) {
return '';
}

return ' -I=' . \implode(' -I=', \array_map('escapeshellarg', $dirs));
}
}
4 changes: 4 additions & 0 deletions tests/app/config/grpc.php
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,10 @@
*/
'binaryPath' => directory('app') . '../protoc-gen-php-grpc',

'generatedPath' => null,
'namespace' => null,
'servicesBasePath' => directory('app') . 'proto',

'services' => [
directory('app') . 'proto/echo.proto',
directory('app') . 'proto/foo.proto',
Expand Down
3 changes: 3 additions & 0 deletions tests/src/Bootloader/GRPCBootloaderTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,9 @@ public function testConfigShouldBeDefined(): void

$this->assertSame([
'binaryPath' => $this->getDirectoryByAlias('app') . '../protoc-gen-php-grpc',
'generatedPath' => null,
'namespace' => null,
'servicesBasePath' => $this->getDirectoryByAlias('app') . 'proto',
'services' => [
$this->getDirectoryByAlias('app') . 'proto/echo.proto',
$this->getDirectoryByAlias('app') . 'proto/foo.proto',
Expand Down
48 changes: 48 additions & 0 deletions tests/src/Config/GRPCConfigTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -56,4 +56,52 @@ public function testGetNotExistsInterceptors(): void

$this->assertSame([], $config->getInterceptors());
}

public function testGetGeneratedPath(): void
{
$config = new GRPCConfig([
'generatedPath' => 'foo',
]);

$this->assertSame('foo', $config->getGeneratedPath());
}

public function testGetNonExistsGeneratedPath(): void
{
$config = new GRPCConfig();

$this->assertNull($config->getGeneratedPath());
}

public function testGetNamespace(): void
{
$config = new GRPCConfig([
'namespace' => 'foo',
]);

$this->assertSame('foo', $config->getNamespace());
}

public function testGetNonExistsNamespace(): void
{
$config = new GRPCConfig();

$this->assertNull($config->getNamespace());
}

public function testGetServicesBasePath(): void
{
$config = new GRPCConfig([
'servicesBasePath' => 'foo',
]);

$this->assertSame('foo', $config->getServicesBasePath());
}

public function testGetNonExistsServicesBasePath(): void
{
$config = new GRPCConfig();

$this->assertNull($config->getServicesBasePath());
}
}
Loading

0 comments on commit 578702a

Please sign in to comment.