PHP
Two good paths: the libSQL extension (turso-client-php, a prebuilt native
extension for PHP 8.1–8.5) speaks Hrana with transactions and prepared
statements; or plain curl against the native JSON API — zero install, any
PHP 8.x, any shared host.
The libSQL extension
Install via the composer-distributed installer, which downloads a prebuilt
binary for your PHP version and registers it in php.ini:
composer global require darkterminal/turso-php-installer
turso-php-installer install # add -n --php-version=8.3 for non-interactive<?php
$db = new LibSQL("libsql:dbname=http://127.0.0.1:7775/app;authToken=your-token");
$db->execute('INSERT INTO users(name, balance) VALUES (?, ?)', ['ada', 100]);
$db->execute('INSERT INTO users(name, balance) VALUES (?, ?)', ['bob', 100]);
// An interactive transaction — both updates commit atomically.
$tx = $db->transaction();
$tx->execute('UPDATE users SET balance = balance - ? WHERE name = ?', [30, 'ada']);
$tx->execute('UPDATE users SET balance = balance + ? WHERE name = ?', [30, 'bob']);
$tx->commit();
$rows = $db->query('SELECT name, balance FROM users ORDER BY name')
->fetchArray(LibSQL::LIBSQL_ASSOC);
foreach ($rows as $row) {
echo "{$row['name']}: {$row['balance']}\n";
}
$db->close();The database is the URL path (/app); the token becomes
Authorization: Bearer. Prepared statements ($db->prepare) and
executeBatch work as documented in the
extension’s API reference.
Prebuilt coverage: linux-x86_64, macOS (arm64 + x86_64), Windows — but not
linux-aarch64. In an Apple Silicon Docker container, build/run the image with
--platform linux/amd64; on ARM Linux servers, use the curl path below.
Zero install: curl + the native JSON API
Works on every PHP 8.x with ext-curl (or Guzzle, same request):
<?php
function query(array $body): array
{
$ch = curl_init('http://127.0.0.1:7775/app/query');
curl_setopt_array($ch, [
CURLOPT_POST => true,
CURLOPT_POSTFIELDS => json_encode($body, JSON_THROW_ON_ERROR),
CURLOPT_HTTPHEADER => ['Authorization: Bearer your-token', 'Content-Type: application/json'],
CURLOPT_RETURNTRANSFER => true,
]);
$out = json_decode(curl_exec($ch), true, 512, JSON_THROW_ON_ERROR);
curl_close($ch);
if (isset($out['error'])) {
throw new RuntimeException($out['error']['message']);
}
return $out;
}
$rs = query(['sql' => 'SELECT name, balance FROM users WHERE id = ?', 'args' => [7]]);
// A batch runs as ONE all-or-nothing transaction:
query(['statements' => [
['sql' => 'UPDATE users SET balance = balance - ? WHERE name = ?', 'args' => [30, 'ada']],
['sql' => 'UPDATE users SET balance = balance + ? WHERE name = ?', 'args' => [30, 'bob']],
]]);Full shapes — including how batch errors come back — in the HTTP API reference.
Runnable versions
examples/clients/php-libsql
(with a ready-made Dockerfile) and
php-curl
run these exact flows against a real server in CI.