PHP client
The official PHP client for the Attlaz API, published to Packagist as
attlaz/client. It handles authentication, token
refresh and pagination, so you work with objects instead of HTTP.
Requires PHP 8.2 or newer.
attlaz/project-baseThis is the client for calling the Attlaz API from your own application. If you are writing flow
code that runs on Attlaz, you want attlaz/project-base
instead — it already gives you a configured client.
Install
composer require attlaz/client
Authenticate
Two ways, matching the two ways to get a token described in Authentication.
With client credentials — for a server-to-server integration. A token is minted on the first request:
use Attlaz\Client;
$client = new Client();
$client->authWithClient('<client_id>', '<client_secret>');
With a token you already have:
$client = new Client();
$client->authWithToken('<access_token>');
Reusing a token across requests
PHP builds a fresh Client for every web request, so client credentials mean a token request each
time. To avoid that, store the token somewhere you control — Redis, APCu, a session — and hand it
back with authWithToken():
$token = $cache->get('attlaz_token');
$client = new Client();
if ($token !== null) {
$client->authWithToken($token);
} else {
$client->authWithClient('<client_id>', '<client_secret>');
}
The client does not cache tokens to disk. It used to, and that cache was removed: the file landed in the working directory with default permissions, which is not somewhere credentials belong. Where a token is kept, and for how long, is a decision that belongs to your application.
To store one after the client has minted it, read it back with getAccessToken(), which returns an
Attlaz\Model\AccessToken — the token string plus when it expires, so you can cache it for exactly
as long as it is good for:
use Attlaz\Model\AccessToken;
$token = $client->getAccessToken();
if ($token !== null && !$token->hasExpired(60)) {
$cache->set('attlaz_token', $token->getToken(), $token->getExpires() - \time());
}
hasExpired() takes an optional margin in seconds, so a token with moments left counts as expired
rather than failing partway through a request. A token with no known expiry — one you supplied
yourself — reports false, since only you know its lifetime.
Make a call
Endpoints hang off the client, one accessor per area:
$flows = $client->getFlowEndpoint()->getFlows('<project_id>');
A method that fetches one thing returns null when it does not exist, rather than throwing.
Pagination
List endpoints are cursor-paginated and return a CollectionResult — the page plus whether more
exist:
use Attlaz\Model\CursorPagination;
$pagination = new CursorPagination();
$pagination->limit = 100;
$page = $client->getStorageEndpoint()->getItems('<project_environment_id>', 'cache', $pagination);
$page->getData();
$page->hasMore;
To walk every page, use LoadAllHelper:
use Attlaz\Helper\LoadAllHelper;
$all = LoadAllHelper::loadAll(
fn(CursorPagination $pagination) => $client->getStorageEndpoint()->getItems('<id>', 'cache', $pagination),
);
It stops at 50,000 records and throws — it is for collections you know are small. It also throws if a page comes back empty while still reporting that more results exist: the cursor moves on from the last record, so there is no way to page past an empty one, and stopping with an error beats looping on the same request.
Errors
Failed requests throw Attlaz\Model\Exception\RequestException, which carries the HTTP status in
$httpCode:
use Attlaz\Model\Exception\RequestException;
try {
$client->getFlowEndpoint()->getFlows('<project_id>');
} catch (RequestException $exception) {
$exception->httpCode; // e.g. 403
}
Timeouts
A request is given 80 seconds, and 30 seconds to establish the connection. Both are configurable:
$client->setTimeout(30);
$client->setConnectTimeout(5);
Earlier versions disabled both, so a request against an unreachable host blocked the process
indefinitely and setTimeout() had no effect on API calls.
Reporting data-quality problems
Open a scan, report what you found in as many batches as you like, then complete it — completing is what resolves problems that were checked and are no longer there:
use Attlaz\DataQuality\Model\ReportEntity;
use Attlaz\DataQuality\Model\ReportProblem;
$quality = $client->getQualityEndpoint();
$scan = $quality->openScan('<dataset_id>', 'my-checks');
$entity = new ReportEntity('product', 'SKU-1');
$entity->name = 'Blue lamp';
$entity->problems = [new ReportProblem('shop.ean_invalid_checkdigit', 'ean')];
$entity->passed = ['shop.name_too_short'];
$quality->reportToScan('<dataset_id>', $scan->id, [$entity]);
$quality->completeScan('<dataset_id>', $scan->id);
If you already have everything in memory, reportProblems() does all three in one call. Both are
bound by the request size limit — batch into a scan for anything
larger than a few thousand entities.