google/grpc-gcp
GCP-specific extensions for gRPC, providing additional features and supporting infrastructure such as end-to-end tests and benchmarks for accessing Google Cloud APIs with gRPC client libraries. See the src directory for extension implementations.
## Getting Started
### Minimal Steps to First Use
1. **Install Dependencies**:
- Ensure PHP 8.1+ and required extensions (`grpc`, `protobuf`) are installed via PECL:
```bash
pecl install grpc protobuf
```
- Add to `php.ini`:
```ini
extension=grpc.so
extension=protobuf.so
```
2. **Generate gRPC Clients**:
- Clone the [googleapis](https://github.com/googleapis/googleapis) repo and generate PHP clients:
```bash
git clone https://github.com/googleapis/googleapis.git
cd googleapis
make LANGUAGE=php OUTPUT=./generated
```
- Move generated files to `app/Generated/Google/Cloud/` in your Laravel project.
3. **Set Up Credentials**:
- Download a service account key from GCP Console and set the environment variable:
```bash
export GOOGLE_APPLICATION_CREDENTIALS=/path/to/key.json
```
- Alternatively, configure credentials in Laravel’s `.env`:
```env
GOOGLE_APPLICATION_CREDENTIALS=/path/to/key.json
```
4. **First gRPC Call in Laravel**:
- Create a service class (e.g., `app/Services/GcpFirestoreService.php`):
```php
use Google\Cloud\Firestore\V1beta1\FirestoreClient;
use Google\Cloud\Firestore\V1beta1\ListDocumentsRequest;
use Google\Auth\ApplicationDefaultCredentials;
class GcpFirestoreService {
public function listDocuments(string $projectId): array
{
$host = "firestore.googleapis.com";
$credentials = \Grpc\ChannelCredentials::createSsl();
$auth = ApplicationDefaultCredentials::getCredentials();
$opts = [
'credentials' => $credentials,
'update_metadata' => $auth->getUpdateMetadataFunc(),
];
$client = new FirestoreClient($host, $opts);
$request = new ListDocumentsRequest();
$request->setParent("projects/{$projectId}/databases/(default)/documents");
list($response, $error) = $client->ListDocuments($request)->wait();
return $response->getDocuments();
}
}
```
5. **Use in Laravel**:
- Bind the service in `AppServiceProvider`:
```php
public function register()
{
$this->app->singleton(GcpFirestoreService::class, function ($app) {
return new GcpFirestoreService();
});
}
```
- Call from a controller:
```php
use App\Services\GcpFirestoreService;
public function index(GcpFirestoreService $service)
{
$documents = $service->listDocuments(config('services.gcp.project_id'));
return response()->json($documents);
}
```
---
## Implementation Patterns
### 1. **Service Abstraction Layer**
Leverage Laravel’s **Service Container** to abstract gRPC clients into reusable services:
```php
// app/Services/GcpPubSubService.php
class GcpPubSubService {
protected $client;
public function __construct()
{
$this->client = new \Google\Cloud\PubSub\V1\PublisherClient();
}
public function publish(string $topic, string $message): void
{
$topicName = $this->client->topicName(
config('services.gcp.project_id'),
$topic
);
$this->client->publish($topicName, $message);
}
}
AppServiceProvider:
$this->app->singleton(GcpPubSubService::class, function ($app) {
return new GcpPubSubService();
});
$this->pubSubService->publish('orders', json_encode($order));
Use gRPC’s client interceptors to add middleware (e.g., logging, OpenTelemetry):
use Grpc\UnaryUnaryClientInterceptor;
use Psr\Log\LoggerInterface;
class LoggingInterceptor implements UnaryUnaryClientInterceptor
{
protected $logger;
public function __construct(LoggerInterface $logger)
{
$this->logger = $logger;
}
public function intercept($method, $request, $details, $handler)
{
$this->logger->info("gRPC Call: {$details->getMethodName()}");
return $handler($method, $request, $details);
}
}
$interceptor = new LoggingInterceptor(app(LoggerInterface::class));
$opts['interceptors'] = [$interceptor];
Handle server-side streaming (e.g., Firestore real-time updates) with Laravel Events:
// app/Services/GcpFirestoreStreamService.php
class GcpFirestoreStreamService {
public function listenForUpdates(string $collectionPath, callable $callback)
{
$client = new FirestoreClient($host, $opts);
$request = new ListDocumentsRequest();
$request->setParent($collectionPath);
$stream = $client->ListDocuments($request);
foreach ($stream as $response) {
foreach ($response->getDocuments() as $doc) {
$callback($doc);
}
}
}
}
$service->listenForUpdates(
"projects/{$projectId}/databases/(default)/documents/users",
fn($doc) => event(new UserUpdated($doc))
);
Use exponential backoff for transient failures (e.g., gRPC timeouts):
use Grpc\UnaryUnaryClientInterceptor;
use Google\Rpc\Status;
class RetryInterceptor implements UnaryUnaryClientInterceptor
{
public function intercept($method, $request, $details, $handler)
{
$attempts = 0;
$maxAttempts = 3;
$baseDelay = 100; // ms
while ($attempts < $maxAttempts) {
try {
return $handler($method, $request, $details);
} catch (RpcException $e) {
if ($e->getCode() !== Status::UNAVAILABLE) {
throw $e;
}
$attempts++;
usleep($baseDelay * pow(2, $attempts) * 1000);
}
}
throw new \RuntimeException("Max retries exceeded");
}
}
Use gRPC for Pub/Sub-backed queues:
// config/queue.php
'connections' => [
'gcp-pubsub' => [
'driver' => 'gcp-pubsub',
'project_id' => env('GCP_PROJECT_ID'),
'topic' => env('GCP_QUEUE_TOPIC'),
],
],
// app/Providers/QueueServiceProvider.php
public function boot()
{
Queue::extend('gcp-pubsub', function ($app) {
return new GcpPubSubQueueService(
new \Google\Cloud\PubSub\V1\PublisherClient()
);
});
}
Protobuf Generation Failures:
grpc_php_plugin fails to compile proto files due to missing dependencies.protoc and grpc_php_plugin are in PATH and all .proto files include syntax = "proto3";.googleapis Makefile to auto-resolve dependencies:
make LANGUAGE=php OUTPUT=./generated
Credential Handling:
ApplicationDefaultCredentials fails silently if GOOGLE_APPLICATION_CREDENTIALS is misconfigured.$auth = ApplicationDefaultCredentials::getCredentials();
if (!$auth) {
throw new \RuntimeException("GCP credentials not found");
}
.env for flexibility:
GCP_CREDENTIALS_PATH=/path/to/key.json
putenv("GOOGLE_APPLICATION_CREDENTIALS={$this->app['env']->get('GCP_CREDENTIALS_PATH')}");
Shared Memory Leaks:
$opts['disable_shared_memory'] = true;
PHP 8.1+ Deprecations:
Serializable interface changes break generated classes.google/grpc-gcp v0.3.0+ and regenerate proto files.Streaming Timeouts:
How can I help you explore Laravel packages today?