A thin, no-magic PHP wrapper around the Consul HTTP API, built on top of Symfony HttpClient.
$kv = new Consul\Services\KV();
$kv->put('config/feature-flag', 'enabled');
echo $kv->get('config/feature-flag', ['raw' => true])->getBody(); // enabled- π§ The whole Consul CE HTTP API: 17 services, from KV to ACL, service mesh and operator endpoints
- ποΈ Key/Value store, sessions and transactions
- π©Ί Service discovery: agent, catalog, health and prepared queries
- π Distributed locks and semaphores, ready to use
- πͺΆ Lightweight: only depends on
symfony/http-clientandpsr/log - π Pluggable: bring your own HTTP client and PSR-3 logger
composer require friendsofphp/consul-php-sdkBy default, services talk to the agent on http://127.0.0.1:8500. The address
can be changed with the CONSUL_HTTP_ADDR environment variable (the same one
the Consul CLI uses), or with the base_uri option:
use Consul\Client;
use Consul\Services\KV;
$client = new Client(['base_uri' => 'https://consul.example.com:8500']);
$kv = new KV($client);The first argument of Client accepts any
Symfony HttpClient option,
which comes handy to send an ACL token:
$client = new Client([
'base_uri' => 'https://consul.example.com:8500',
'headers' => ['X-Consul-Token' => $token],
]);The token can also be set with the CONSUL_HTTP_TOKEN environment variable.
You can also pass a PSR-3 logger, and your own HttpClientInterface instance:
$client = new Client(logger: $logger, client: $httpClient);| Service | Consul API |
|---|---|
Consul\Services\ACL |
/v1/acl |
Consul\Services\Agent |
/v1/agent |
Consul\Services\Catalog |
/v1/catalog |
Consul\Services\Config |
/v1/config |
Consul\Services\Connect |
/v1/connect |
Consul\Services\Coordinate |
/v1/coordinate |
Consul\Services\DiscoveryChain |
/v1/discovery-chain |
Consul\Services\Event |
/v1/event |
Consul\Services\Health |
/v1/health |
Consul\Services\KV |
/v1/kv |
Consul\Services\Operator |
/v1/operator |
Consul\Services\Peering |
/v1/peering |
Consul\Services\PreparedQuery |
/v1/query |
Consul\Services\Session |
/v1/session |
Consul\Services\Snapshot |
/v1/snapshot |
Consul\Services\Status |
/v1/status |
Consul\Services\TXN |
/v1/txn |
All services follow the same convention:
$response = $service->method($mandatoryArgument, $someOptions);- Mandatory API arguments come first;
- Optional API arguments are passed in the
$optionsarray, with the same name as in the Consul documentation. Use an array for multi-valued arguments, e.g.['tag' => ['v1', 'primary']]; - Every method returns a
Consul\ConsulResponse, which exposesgetBody(),json(),getHeaders(),getStatusCode()andisSuccessful(); - A
4xxresponse throws aConsul\Exception\ClientException; - A
5xxresponse, or a network error, throws aConsul\Exception\ServerException.
Both exceptions implement Consul\Exception\ConsulExceptionInterface:
use Consul\Exception\ConsulExceptionInterface;
try {
$kv->get('does/not/exist');
} catch (ConsulExceptionInterface $e) {
// ...
}use Consul\Services\Agent;
use Consul\Services\Health;
$agent = new Agent();
$agent->registerService([
'ID' => 'api-1',
'Name' => 'api',
'Address' => '10.0.0.12',
'Port' => 8080,
'Check' => [
'HTTP' => 'http://10.0.0.12:8080/health',
'Interval' => '10s',
],
]);
// Later, find all healthy instances
$instances = (new Health())->service('api', ['passing' => true])->json();$kv = new Consul\Services\KV();
foreach ($kv->get('config/', ['recurse' => true])->json() as $entry) {
echo $entry['Key'], ' = ', base64_decode($entry['Value']), "\n";
}$txn = new Consul\Services\TXN();
$txn->put([
['KV' => ['Verb' => 'set', 'Key' => 'config/a', 'Value' => base64_encode('1')]],
['KV' => ['Verb' => 'set', 'Key' => 'config/b', 'Value' => base64_encode('2')]],
]);$snapshot = new Consul\Services\Snapshot();
file_put_contents('backup.snap', $snapshot->save()->getBody());
// Later...
$snapshot->restore(file_get_contents('backup.snap'));LockHandler takes a lock on a key, and releases it automatically at the end
of the script:
use Consul\Helper\LockHandler;
$lock = new LockHandler('locks/my-job');
if (!$lock->lock()) {
echo "The lock is already acquired by another node.\n";
exit(1);
}
// Do your job here...
$lock->release();MultiLockHandler locks all resources, or none of them:
use Consul\Helper\MultiLockHandler;
use Consul\Services\KV;
use Consul\Services\Session;
$lock = new MultiLockHandler(['resource1', 'resource2'], 60, new Session(), new KV(), 'my/lock/');
if ($lock->lock()) {
try {
// Do your job here...
// and call $lock->renew() before the TTL (60s) expires, if needed
} finally {
$lock->release();
}
}MultiSemaphore allows up to limit concurrent holders per resource. Each
Resource is defined by a name, the number of slots to acquire, and a limit:
use Consul\Helper\MultiSemaphore;
use Consul\Helper\MultiSemaphore\Resource;
use Consul\Services\KV;
use Consul\Services\Session;
$resources = [
new Resource('resource1', 2, 7),
new Resource('resource2', 3, 6),
new Resource('resource3', 1, 1),
];
$semaphore = new MultiSemaphore($resources, 60, new Session(), new KV(), 'my/semaphore');
if ($semaphore->acquire()) {
try {
// Do your job here...
} finally {
$semaphore->release();
}
}| Version | PHP | symfony/http-client |
|---|---|---|
| 5.4 | β₯ 8.2 | 6.4, 7.4, 8.1+ |
| 5.3 | β₯ 8.1 | 5.4, 6.4, 7.x, 8.x |
Looking for Guzzle support, or older versions of PHP? Check the CHANGELOG and this older README.
The test suite needs a Consul agent listening on localhost:8500 (or on
CONSUL_HTTP_ADDR), with ACLs enabled and root as management token. The
easiest way is to use Docker:
docker run -d --rm --name consul -p 8500:8500 \
-e CONSUL_LOCAL_CONFIG='{"acl":{"enabled":true,"default_policy":"allow","tokens":{"initial_management":"root"}}}' \
hashicorp/consulThen run:
composer install
vendor/bin/phpunitThis library is released under the MIT license.