friendsofphp / consul-php-sdk
SDK to talk with consul.io API
Requires
- php: >=8.2
- psr/log: ^2|^3
- symfony/http-client: ^6.4|^7.4|^8.1
Requires (Dev)
- phpunit/phpunit: ^11.5|^12.0|^13.0
Suggests
None
Provides
None
Conflicts
None
Replaces
None
README
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
β¨ Features
- π§ 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
π¦ Installation
composer require friendsofphp/consul-php-sdk
π Usage
Configuration
By 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);
Available services
| 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 |
Conventions
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) { // ... }
π³ Cookbook
Register a service with a health check
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();
Read many keys at once
$kv = new Consul\Services\KV(); foreach ($kv->get('config/', ['recurse' => true])->json() as $entry) { echo $entry['Key'], ' = ', base64_decode($entry['Value']), "\n"; }
Run a transaction
$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')]], ]);
Back up the cluster
$snapshot = new Consul\Services\Snapshot(); file_put_contents('backup.snap', $snapshot->save()->getBody()); // Later... $snapshot->restore(file_get_contents('backup.snap'));
Acquire an exclusive lock
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();
Lock many resources at once
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(); } }
Use a distributed semaphore
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(); } }
π§© Compatibility
| 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.
π§ͺ Running the test suite
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/consul
Then run:
composer install vendor/bin/phpunit
π License
This library is released under the MIT license.