google/cloud-spanner
Idiomatic PHP client for Google Cloud Spanner, a globally consistent relational database. Install via Composer and use gRPC to connect to instances/databases, run SQL queries with parameters, and benefit from V2 multiplexed sessions for efficient concurrent requests.
## Getting Started
### Minimal Setup
1. **Installation**:
```bash
composer require google/cloud-spanner
Ensure the grpc PHP extension is installed and enabled (required for gRPC communication).
Authentication: Configure credentials via environment variables (recommended) or service account key file:
putenv('GOOGLE_APPLICATION_CREDENTIALS=/path/to/key.json');
Follow the Authentication Guide.
First Query:
use Google\Cloud\Spanner\SpannerClient;
$spanner = new SpannerClient();
$db = $spanner->instance('your-instance')->database('your-database');
$result = $db->execute('SELECT * FROM Users WHERE id = @id', [
'parameters' => ['id' => 123]
]);
foreach ($result->rows() as $row) {
print_r($row);
}
execute() for reads/writes with parameterized queries.$transaction = $db->runTransaction(function ($transaction) {
$transaction->execute('UPDATE Accounts SET balance = balance - 100 WHERE id = @id', [
'parameters' => ['id' => 1]
]);
return $transaction->commit();
});
$db->executeDdl('CREATE TABLE Users (id INT64, name STRING(100))');
Connection Management:
SpannerClient instances (stateless) and cache database connections:
$spanner = new SpannerClient();
$db = $spanner->instance('instance')->database('db'); // Reusable
Query Patterns:
$result = $db->execute('SELECT * FROM Orders WHERE status = @status', [
'parameters' => ['status' => 'shipped']
]);
$db->executeUpdate('INSERT INTO Logs (message) VALUES (@message)', [
'parameters' => ['message' => 'Event occurred']
], ['batch' => true]);
Transaction Handling:
$db->runTransaction(function ($transaction) {
$transaction->execute('UPDATE Inventory SET quantity = quantity - 1 WHERE id = @id', [
'parameters' => ['id' => 100]
]);
return $transaction->commit();
});
$snapshot = $db->snapshot();
$result = $snapshot->execute('SELECT * FROM Products');
Async Operations:
executeAsync() for non-blocking queries (returns a Future):
$future = $db->executeAsync('SELECT * FROM LargeTable');
$result = $future->wait(); // Blocks until ready
Laravel Service Provider: Bind the client to the container for dependency injection:
$this->app->singleton(SpannerClient::class, function ($app) {
return new SpannerClient();
});
Inject into controllers:
public function __construct(private SpannerClient $spanner) {}
Query Builder Abstraction: Create a wrapper for Spanner-specific queries:
class SpannerQueryBuilder {
public function __construct(private SpannerDatabase $db) {}
public function findUser(int $id) {
return $this->db->execute('SELECT * FROM Users WHERE id = @id', [
'parameters' => ['id' => $id]
])->rows()->current();
}
}
Error Handling:
Catch Google\Cloud\Core\Exception\GoogleException for Spanner-specific errors:
try {
$db->execute('INVALID SQL');
} catch (GoogleException $e) {
Log::error('Spanner error: ' . $e->getMessage());
}
Configuration: Override defaults (e.g., cache, locking) via constructor options:
$spanner = new SpannerClient([
'cacheItemPool' => new FileSystemCacheItemPool('/custom/cache/path'),
'grpcOptions' => [
'keepalive_time_ms' => 60000, // 60s keepalive
]
]);
Session Expiry:
$db->session()->refresh(); // Force-refresh
gRPC Requirements:
grpc extension: Install via PECL or system package manager (e.g., sudo apt-get install php-grpc).keepalive_time_ms is 120s. Adjust if connections drop:
$spanner = new SpannerClient(['grpcOptions' => ['keepalive_time_ms' => 300000]]);
Parameter Binding:
null for nullable columns:
$db->execute('UPDATE Users SET email = @email WHERE id = @id', [
'parameters' => ['email' => null, 'id' => 1] // email is NULL
]);
INT64 vs. STRING). Cast values explicitly:
'parameters' => ['id' => (string) $userId] // Cast to STRING
Transactions and Locks:
READ_ONLY for analytics:
$db->runTransaction(function ($transaction) {
$transaction->setOptions(['isolation_level' => 'READ_ONLY']);
// ...
});
$db->execute('SELECT * FROM Inventory', [
'read_lock_mode' => 'READ_WRITE' // or 'STRONG', 'WEAK'
]);
Multiplexed Sessions:
sysvshm (Linux) or FileSystemCache. Customize if needed:
$spanner = new SpannerClient([
'cacheItemPool' => new RedisCacheItemPool($redisClient)
]);
sysvmsg/sysvsem locks. Fallback to flock on unsupported systems.Large Result Sets:
$result = $db->execute('SELECT * FROM LargeTable');
foreach ($result->rows() as $row) {
// Process row-by-row
}
OFFSET/LIMIT or application-side cursors.Enable gRPC Logging:
Add to php.ini or runtime:
grpc.verbose_logging = 1
Logs appear in stderr or PHP error logs.
Query Plan Analysis:
Use Spanner’s built-in EXPLAIN:
$plan = $db->execute('EXPLAIN SELECT * FROM Users');
print_r($plan->rows()->current());
Session Inspection: Check active sessions:
$sessions = $spanner->instance('instance')->sessions();
foreach ($sessions as $session) {
print_r($session->info());
}
Retry Logic:
Implement exponential backoff for transient errors (e.g., ABORTED):
use Google\Cloud\Core\Exception\GoogleException;
try {
$db->execute('SELECT * FROM Users');
} catch (GoogleException $e) {
if ($e->getCode() === 11) { // ABORTED
sleep(1); // Retry after delay
retry();
}
}
How can I help you explore Laravel packages today?