diff --git a/src/Bootloader/GRPCBootloader.php b/src/Bootloader/GRPCBootloader.php index 464e82b..48e7982 100644 --- a/src/Bootloader/GRPCBootloader.php +++ b/src/Bootloader/GRPCBootloader.php @@ -9,11 +9,11 @@ use Spiral\Boot\KernelInterface; use Spiral\Config\ConfiguratorInterface; use Spiral\Config\Patch\Append; +use Spiral\Core\Attribute\Proxy; use Spiral\Core\Container\Autowire; use Spiral\Core\CoreInterceptorInterface; use Spiral\Core\FactoryInterface; use Spiral\Core\InterceptableCore; -use Spiral\Core\ScopeInterface; use Spiral\RoadRunner\GRPC\Invoker as BaseInvoker; use Spiral\RoadRunner\GRPC\InvokerInterface; use Spiral\RoadRunner\GRPC\Server; @@ -112,10 +112,9 @@ public function addGenerator(string|GeneratorInterface|Autowire $generator): voi private function initInvoker( GRPCConfig $config, - ContainerInterface $container, + #[Proxy] ContainerInterface $container, FactoryInterface $factory, BaseInvoker $invoker, - ScopeInterface $scope, ): InvokerInterface { $core = new InterceptableCore( new InvokerCore($invoker), @@ -129,7 +128,7 @@ private function initInvoker( $core->addInterceptor($interceptor); } - return new Invoker($core, $scope); + return new Invoker($core, $container); } private function initProtoFilesRepository(GRPCConfig $config): ProtoFilesRepositoryInterface diff --git a/src/GRPC/Interceptor/Invoker.php b/src/GRPC/Interceptor/Invoker.php index f0e2e0b..28a840b 100644 --- a/src/GRPC/Interceptor/Invoker.php +++ b/src/GRPC/Interceptor/Invoker.php @@ -5,6 +5,7 @@ namespace Spiral\RoadRunnerBridge\GRPC\Interceptor; use Google\Protobuf\Internal\Message; +use Psr\Container\ContainerInterface; use Spiral\Core\CoreInterface; use Spiral\Core\Scope; use Spiral\Core\ScopeInterface; @@ -24,16 +25,17 @@ final class Invoker implements InvokerInterface { public function __construct( private readonly CoreInterface $core, - private readonly ScopeInterface $scope, + private readonly ContainerInterface $container, ) { } public function invoke(ServiceInterface $service, Method $method, ContextInterface $ctx, ?string $input): string { $message = $this->makeInput($method, $input); + $scope = $this->container->get(ScopeInterface::class); /** @psalm-suppress InvalidArgument */ - return $this->scope->runScope( + return $scope->runScope( new Scope('grpc.request', [UnaryCallInterface::class => new UnaryCall($ctx, $method, $message)]), fn (): string => $this->core->callAction($service::class, $method->name, [ 'service' => $service, diff --git a/src/GRPC/ServiceLocator.php b/src/GRPC/ServiceLocator.php index 9667d5d..37ee61b 100644 --- a/src/GRPC/ServiceLocator.php +++ b/src/GRPC/ServiceLocator.php @@ -5,6 +5,9 @@ namespace Spiral\RoadRunnerBridge\GRPC; use Psr\Container\ContainerInterface; +use Spiral\Core\BinderInterface; +use Spiral\Core\Attribute\Proxy as ProxyAttribute; +use Spiral\Core\Config\Proxy; use Spiral\Core\Exception\Container\ContainerException; use Spiral\RoadRunner\GRPC\ServiceInterface; use Spiral\Tokenizer\ClassesInterface; @@ -13,7 +16,8 @@ final class ServiceLocator implements LocatorInterface { public function __construct( private readonly ClassesInterface $classes, - private readonly ContainerInterface $container + #[ProxyAttribute] private readonly ContainerInterface $container, + private readonly BinderInterface $binder, ) { } @@ -26,14 +30,19 @@ public function getServices(): array continue; } - try { - $instance = $this->container->get($service->getName()); - } catch (ContainerException) { - continue; - } - foreach ($service->getInterfaces() as $interface) { if ($interface->isSubclassOf(ServiceInterface::class)) { + $grpcRequest = $this->binder->getBinder('grpc.request'); + $grpcRequest->bind($interface->getName(), $service->getName()); + $grpcRequest->bind($service->getName(), $service->getName()); + $this->binder->bind($interface->getName(), new Proxy($interface->getName())); + + try { + $instance = $this->container->get($interface->getName()); + } catch (ContainerException) { + continue; + } + $result[$interface->getName()] = $instance; } } diff --git a/tests/app/config/tokenizer.php b/tests/app/config/tokenizer.php new file mode 100644 index 0000000..5f6b77e --- /dev/null +++ b/tests/app/config/tokenizer.php @@ -0,0 +1,10 @@ + [ + \dirname(__DIR__, 2) . '/app', + \dirname(__DIR__, 2) . '/generated', + ] +]; diff --git a/tests/generated/Service/PingService.php b/tests/generated/Service/PingService.php index e6b2b72..22b2c16 100644 --- a/tests/generated/Service/PingService.php +++ b/tests/generated/Service/PingService.php @@ -4,12 +4,25 @@ namespace Service; +use Psr\Container\ContainerInterface; +use Spiral\Core\Attribute\Scope; +use Spiral\Core\Internal\Introspector; use Spiral\RoadRunner\GRPC\ContextInterface; +#[Scope('grpc.request')] class PingService implements PingInterface { + public static array $scopes = []; + + public function __construct( + private readonly ContainerInterface $container, + ) { + } + public function Ping(ContextInterface $ctx, Message $in): Message { + self::$scopes = Introspector::scopeNames($this->container); + $out = new Message(); return $out->setMsg('PONG'); diff --git a/tests/src/GRPC/DispatcherTest.php b/tests/src/GRPC/DispatcherTest.php index fcf4daa..7c31764 100644 --- a/tests/src/GRPC/DispatcherTest.php +++ b/tests/src/GRPC/DispatcherTest.php @@ -4,6 +4,7 @@ namespace Spiral\Tests\GRPC; +use Service\PingService; use Spiral\App\GRPC\EchoService\Message; use Spiral\Boot\FinalizerInterface; use Spiral\RoadRunner\Payload; @@ -57,6 +58,35 @@ public function testServe(): void $this->serveDispatcher(Dispatcher::class); } + public function testGrpcScope(): void + { + $this->assertEquals([], PingService::$scopes); + + $worker = $this->mockContainer(WorkerInterface::class, Worker::class); + $this->getContainer()->bind(RoadRunnerMode::class, RoadRunnerMode::Grpc); + + $finalizer = $this->mockContainer(FinalizerInterface::class); + $finalizer->shouldReceive('finalize')->once()->with(false); + + $worker->shouldReceive('waitPayload')->once()->andReturn( + new Payload( + (new \Service\Message())->setMsg('PING')->serializeToString(), + json_encode(['service' => 'service.Ping', 'method' => 'Ping', 'context' => []]) + ) + ); + + $worker->shouldReceive('respond')->once()->withArgs(function (Payload $payload) { + $this->assertSame($payload->body, (new \Service\Message())->setMsg('PONG')->serializeToString()); + return true; + }); + + $worker->shouldReceive('waitPayload')->once()->with()->andReturnNull(); + + $this->getApp()->serve(); + + $this->assertEquals(['grpc.request', 'grpc', 'root'], PingService::$scopes); + } + protected function tearDown(): void { parent::tearDown();