spiral/grpc-client
Powerful, extensible PHP gRPC client with a simple Guzzle-like API. Supports standalone use or Spiral integration, configurable via DTOs, includes common interceptors (timeouts, retries) and dedicated exceptions. Requires the PHP gRPC extension.
Install the package and PHP gRPC extension:
composer require spiral/grpc-client -W
pecl install grpc
Enable the extension in php.ini:
extension=grpc.so
Generate gRPC service interfaces (if not already done):
Use protoc with the protoc-gen-php-grpc plugin to generate PHP interfaces from .proto files.
Example:
protoc --php_out=. --grpc_out=. --plugin=protoc-gen-grpc=`which grpc_php_plugin` path/to/service.proto
Basic client usage (standalone):
use Spiral\Grpc\Client\GrpcClient;
$client = GrpcClient::create('localhost:9001');
$service = $client->service(\GRPC\MyService\MailSenderInterface::class);
$response = $service->sendMail($ctx, $request);
Integrate with Spiral Framework:
Add the bootloader to Kernel.php:
protected const LOAD = [
// ...
\Spiral\Grpc\Client\Bridge\GrpcClientBootloader::class,
];
Define a request DTO (auto-generated by protoc):
$request = (new \GRPC\MyService\SendMailRequest())
->setEmail('user@example.com')
->setSubject('Hello')
->setMessage('Test message');
Create a context (for metadata/headers):
use Spiral\Grpc\Client\Interceptor\Helper;
$ctx = Helper::withMetadata(new \Spiral\RoadRunner\GRPC\Context([]), [
'x-custom-header' => ['value'],
]);
Invoke the service method:
$response = $service->sendMail($ctx, $request);
Handle the response:
if ($response->getSuccess()) {
// Success logic
} else {
// Error handling (e.g., check $response->getError())
}
Use GrpcClientConfig for centralized configuration (recommended for Spiral apps):
use Spiral\Grpc\Client\Config\GrpcClientConfig;
use Spiral\Grpc\Client\Config\ServiceConfig;
use Spiral\Grpc\Client\Interceptor\SetTimeoutInterceptor;
$config = new GrpcClientConfig(
interceptors: [SetTimeoutInterceptor::createConfig(5_000)],
services: [
new ServiceConfig(
connections: ['service1:9001', 'service2:9001'],
interfaces: [\GRPC\MyService\MailSenderInterface::class],
),
],
);
Register in Spiral:
// In a bootloader or config file
$container->bind(GrpcClientInterface::class, fn() => new GrpcClient($config));
For dependency injection (e.g., in Spiral):
use Spiral\Grpc\Client\ServiceClientProvider;
$provider = new ServiceClientProvider($container, $config);
$mailSender = $provider->get(\GRPC\MyService\MailSenderInterface::class);
Usage in a service:
final class MailService {
public function __construct(
private \GRPC\MyService\MailSenderInterface $mailSender,
) {}
public function send(string $email): bool {
$request = (new \GRPC\MyService\SendMailRequest())->setEmail($email);
return $this->mailSender->sendMail(new \Spiral\RoadRunner\GRPC\Context([]), $request)->getSuccess();
}
}
Add interceptors for logging, retries, auth, etc.:
$client = GrpcClient::create('localhost:9001')
->withInterceptors([
SetTimeoutInterceptor::createConfig(10_000),
RetryInterceptor::createConfig(maximumAttempts: 3),
new AuthInterceptor($authToken), // Custom interceptor
]);
Custom Interceptor Example:
use Spiral\Grpc\Client\Interceptor\InterceptorInterface;
use Spiral\Grpc\Client\Interceptor\Helper;
final class AuthInterceptor implements InterceptorInterface {
public function __construct(private string $token) {}
public function intercept(CallContextInterface $context, HandlerInterface $handler): mixed {
$metadata = Helper::withMetadata($context, ['authorization' => [$this->token]]);
return $handler->handle($metadata);
}
}
Secure connections with TlsConfig:
use Spiral\Grpc\Client\Config\ConnectionConfig;
use Spiral\Grpc\Client\Config\TlsConfig;
$config = new ConnectionConfig(
'secure-service:9001',
tls: new TlsConfig(
certChain: '/path/to/cert.pem',
privateKey: '/path/to/key.pem',
),
);
Use multiple connections with interceptors like ConnectionsRotationInterceptor:
$client = GrpcClient::create([
'service1:9001',
'service2:9001',
])->withInterceptors([
ConnectionsRotationInterceptor::createConfig(),
]);
Handle server/client streaming:
// Client streaming
$stream = $client->service(\GRPC\StreamingService\ClientStreamerInterface::class);
$stream->send($request1);
$stream->send($request2);
$stream->finish();
$response = $stream->recv();
// Server streaming
$responseStream = $service->streamData($ctx, $request);
foreach ($responseStream as $response) {
// Process each response
}
Use gRPC-specific exceptions:
try {
$response = $service->sendMail($ctx, $request);
} catch (\GRPC\Exception $e) {
if ($e->getCode() === \GRPC\STATUS_UNAVAILABLE) {
// Retry or fallback logic
}
throw new \RuntimeException('gRPC call failed', 0, $e);
}
Missing gRPC Extension:
Class 'GRPC\ChannelCredentials' not found.pecl install grpc) and enable it in php.ini.Protobuf Generation Issues:
protoc is run with the correct plugins:
protoc --php_out=. --grpc_out=. --plugin=protoc-gen-grpc=`which grpc_php_plugin` service.proto
Interceptor Order Matters:
SetTimeoutInterceptor before RetryInterceptor to avoid infinite retries.Stateful Interceptors:
CallContext.TLS Certificate Paths:
TlsConfig may break in Docker/containerized environments.new TlsConfig(
certChain: getenv('GRPC_CERT_PATH'),
privateKey: getenv('GRPC_KEY_PATH'),
);
Context Propagation:
HandlerInterface.Helper::withMetadata() to propagate context:
$handler->handle(Helper::withMetadata($context, $updatedMetadata));
Service Interface Mismatch:
GRPC\Channel instead of the generated interface.\GRPC\MyService\MailSenderInterface).Enable gRPC Logging:
Add to php.ini:
grpc.verbose_logging = 1
Or set via code:
\GRPC\Channel::setDefaultOptions([
'grpc.default_metadata' => [['grpc-log-level', ['debug']]],
]);
Inspect Metadata:
Use Helper::getMetadata() to debug headers:
How can I help you explore Laravel packages today?