Skip to content
Daniel Carbone edited this page Nov 6, 2026 · 1 revision

KV

$consul->KV wraps Consul's KV Store API.

The examples below assume $consul was built as shown in Getting Started.

Read and write

use DCarbone\PHPConsulAPI\KV\KVPair;

[$wm, $err] = $consul->KV->Put(p: new KVPair(Key: 'app/config/db', Value: 'postgres://localhost'));
if (null !== $err) {
    throw new RuntimeException((string)$err);
}

[$pair, $qm, $err] = $consul->KV->Get(key: 'app/config/db');
echo $pair->Value; // postgres://localhost

A missing key is not an error. Get returns null for the value and null for the error:

[$pair, $qm, $err] = $consul->KV->Get(key: 'does/not/exist');
// $pair === null, $err === null

KVPair::$Value is the raw (already base64-decoded) string. Flags, CreateIndex, ModifyIndex and LockIndex are Brick\Math\BigInteger values (see Big Integers).

Listing

[$pairs, $qm, $err] = $consul->KV->List(prefix: 'app/');
foreach ($pairs as $p) {
    echo "{$p->Key} = {$p->Value}\n";
}

// Keys only.  The second argument is either a separator or a QueryOptions.
[$keys, $qm, $err] = $consul->KV->Keys(prefix: 'app/', separatorOrOpts: '/');

Check-and-set

CAS uses the pair's ModifyIndex. It returns false (with no error) if the key changed since you read it:

[$pair] = $consul->KV->Get(key: 'app/config/db');
$pair->setValue(Value: 'postgres://db.internal');

[$updated, $wm, $err] = $consul->KV->CAS(p: $pair); // true
[$updated, $wm, $err] = $consul->KV->CAS(p: $pair); // false, the index is now stale

DeleteCAS works the same way for deletes.

Locks

Pair AcquireBool / ReleaseBool with a Session:

use DCarbone\PHPConsulAPI\Consul;
use DCarbone\PHPConsulAPI\KV\KVPair;
use DCarbone\PHPConsulAPI\Session\SessionEntry;

[$sid, $wm, $err] = $consul->Session->Create(sessionEntry: new SessionEntry(
    Name: 'my-lock',
    TTL: '15s',
    Behavior: Consul::SessionBehaviorRelease,
));

[$acquired, $wm, $err] = $consul->KV->AcquireBool(p: new KVPair(Key: 'locks/leader', Value: 'me', Session: $sid));
if ($acquired) {
    // ... do leader work, calling $consul->Session->Renew(id: $sid) before the TTL expires ...
    $consul->KV->ReleaseBool(p: new KVPair(Key: 'locks/leader', Session: $sid));
}

$consul->Session->Destroy(id: $sid);

Acquire / Release are the Go-style variants. They return only [$wm, $err], so you can't tell whether the lock was taken.

Blocking queries

Pass the previous LastIndex back as WaitIndex. The call blocks until the key changes or WaitTime elapses:

use DCarbone\PHPConsulAPI\QueryOptions;

[$pair, $qm, $err] = $consul->KV->Get(key: 'app/config/db');
while (true) {
    [$pair, $qm, $err] = $consul->KV->Get(
        key: 'app/config/db',
        opts: new QueryOptions(WaitIndex: $qm->LastIndex, WaitTime: '5m'),
    );
    // ... react to the change ...
}

See Query and Write Options for details.

Transactions

KV->Txn runs several KV operations atomically. The response is not destructurable; read its properties instead:

use DCarbone\PHPConsulAPI\Txn\KVOp;
use DCarbone\PHPConsulAPI\Txn\KVTxnOp;
use DCarbone\PHPConsulAPI\Txn\TxnOp;

$resp = $consul->KV->Txn(
    null,
    new TxnOp(KV: new KVTxnOp(Verb: KVOp::KVSet, Key: 'txn/a', Value: 'one')),
    new TxnOp(KV: new KVTxnOp(Verb: KVOp::KVSet, Key: 'txn/b', Value: 'two')),
);

if (null !== $resp->Err) {
    // transport / HTTP error
} elseif (!$resp->OK) {
    foreach ($resp->KVTxnResponse->Errors as $e) {
        echo "op {$e->OpIndex}: {$e->What}\n";
    }
} else {
    echo count($resp->KVTxnResponse->Results) . " results\n";
}

For transactions that also touch nodes, services or checks, use the Txn client.

See Responses and Errors for how to work with the response objects.

Clone this wiki locally