Skip to main content

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.

Not the same as attlaz/project-base

This 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.