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 grpc PHP extension is installed (see gRPC guide).
Authentication:
Configure credentials via environment variables (GOOGLE_APPLICATION_CREDENTIALS) or service account JSON file.
Follow the Authentication guide.
First Query:
use Google\Cloud\Spanner\SpannerClient;
$spanner = new SpannerClient();
$db = $spanner->instance('your-instance')->database('your-database');
$rows = $db->execute('SELECT * FROM Users WHERE id = @id', ['parameters' => ['id' => 1]]);
foreach ($rows as $row) {
print_r($row);
}
$spanner->instance('id')->database('name')$db->execute('SQL', ['parameters' => [...]])$db->runInTransaction(fn($transaction) => $transaction->execute('SQL'))// Create
$db->execute('INSERT INTO Users (id, name) VALUES (@id, @name)', [
'parameters' => ['id' => 1, 'name' => 'John']
]);
// Read
$rows = $db->execute('SELECT * FROM Users WHERE id = @id', [
'parameters' => ['id' => 1]
]);
// Update
$db->execute('UPDATE Users SET name = @name WHERE id = @id', [
'parameters' => ['id' => 1, 'name' => 'Updated']
]);
// Delete
$db->execute('DELETE FROM Users WHERE id = @id', ['parameters' => ['id' => 1]]);
$db->runInTransaction(function ($transaction) {
$transaction->execute('INSERT INTO Orders (user_id, amount) VALUES (@user_id, @amount)', [
'parameters' => ['user_id' => 1, 'amount' => 100.00]
]);
$transaction->execute('UPDATE Users SET balance = balance - @amount WHERE id = @user_id', [
'parameters' => ['user_id' => 1, 'amount' => 100.00]
]);
});
$db->execute([
'sql' => [
'INSERT INTO Users (id, name) VALUES (@id1, @name1)',
'INSERT INTO Users (id, name) VALUES (@id2, @name2)'
],
'parameters' => [
['id1' => 2, 'name1' => 'Alice'],
['id2' => 3, 'name2' => 'Bob']
]
]);
$stmt = $db->prepare('SELECT * FROM Users WHERE id = @id');
$rows = $stmt->execute(['parameters' => ['id' => 1]]);
// Create table
$db->execute('CREATE TABLE Users (id INT64, name STRING(100))');
// Alter table
$db->execute('ALTER TABLE Users ADD COLUMN email STRING(255)');
// Drop table
$db->execute('DROP TABLE Users');
Use the vladimir-yuldashev/laravel-spanner package for seamless integration:
// config/database.php
'spanner' => [
'driver' => 'spanner',
'instance' => env('SPANNER_INSTANCE'),
'database' => env('SPANNER_DATABASE'),
'project_id' => env('GOOGLE_PROJECT_ID'),
],
Extend Laravel's query builder for Spanner-specific features:
use Google\Cloud\Spanner\SpannerClient;
class SpannerQueryBuilder extends \Illuminate\Database\Query\Builder {
protected function connect($dsn = null) {
$spanner = new SpannerClient();
$db = $spanner->instance(config('database.spanner.instance'))
->database(config('database.spanner.database'));
return new SpannerConnection($db);
}
}
For high-concurrency apps, customize the session cache:
use Symfony\Component\Cache\Adapter\RedisAdapter;
$cache = new RedisAdapter();
$spanner = new SpannerClient(['cacheItemPool' => $cache]);
Class 'GRPC\Channel' not foundgrpc extension and enable it in php.ini:
extension=grpc.so
Could not authenticate requestGOOGLE_APPLICATION_CREDENTIALS points to a valid service account JSON file.$spanner->instance('id')->database('name')->session()->refresh();
@param) are required; positional binding is not supported.// Correct
$db->execute('SELECT * FROM Users WHERE id = @id', ['parameters' => ['id' => 1]]);
// Incorrect (will fail)
$db->execute('SELECT * FROM Users WHERE id = ?', [1]);
$rows = $db->execute('SELECT * FROM LargeTable');
foreach ($rows as $row) {
// Process row-by-row
}
Add to php.ini:
grpc.verbose_logging = 1
Logs will appear in stderr.
Use EXPLAIN to analyze query performance:
$plan = $db->execute('EXPLAIN SELECT * FROM Users');
print_r($plan->metadata());
Enable transaction debug mode:
$db->runInTransaction(function ($transaction) {
$transaction->setDebugMode(true);
// Your transaction logic
});
Implement exponential backoff for transient errors:
use Google\Cloud\Core\Retry\RetryPolicy;
$retryPolicy = new RetryPolicy();
$retryPolicy->setMaxAttempts(3);
$retryPolicy->setInitialBackoff(100); // ms
$db->executeWithRetry('SELECT * FROM Users', ['parameters' => []], $retryPolicy);
Add request/response middleware for logging or metrics:
use Google\Cloud\Core\Grpc\Middleware\MiddlewareInterface;
class LoggingMiddleware implements MiddlewareInterface {
public function handle($request, callable $next) {
\Log::debug('Spanner request:', $request->getMetadata());
$response = $next($request);
\Log::debug('Spanner response:', $response->getMetadata());
return $response;
}
}
// Register middleware
$spanner = new SpannerClient([
'middleware' => [new LoggingMiddleware()]
]);
For distributed environments, replace the default lock with a custom implementation:
use Google\Cloud\Core\Lock\LockInterface;
class RedisLock implements LockInterface {
public function acquire(array $options = []) { /* ... */ }
public function release() { /* ... */ }
public function synchronize(callable $func, array $options = []) { /* ... */ }
}
$spanner->instance('id')->database('name', ['lock' => new RedisLock()]);
Extend result set processing:
use Google\Cloud\Spanner\Database;
class CustomDatabase extends Database {
public function execute($sql, array $options = []) {
$result
How can I help you explore Laravel packages today?