PHP code example of xpiston / openfga

1. Go to this page and download the library: Download xpiston/openfga library. Choose the download type require.

2. Extract the ZIP file and open the index.php.

3. Add this code to the index.php.
    
        
<?php
require_once('vendor/autoload.php');

/* Start to develop here. Best regards https://php-download.com/ */

    

xpiston / openfga example snippets


use Piston\OpenFga\OpenFga;

$fga = OpenFga::at('http://localhost:8080');

// Once, in a deployment script.
$store = $fga->stores()->create('my-app');
$fga = $fga->withStore($store->id);

$modelId = $fga->models()->write(file_get_contents('authorization-model.fga'));

$fga = OpenFga::at('http://localhost:8080', store: $storeId, model: $modelId);

$fga->grant('user:anne', 'owner', 'document:budget');

$fga->check('user:anne', 'viewer', 'document:budget');   // true - owner implies viewer
$fga->check('user:bob',  'viewer', 'document:budget');   // false

use Piston\OpenFga\Dsl\Dsl;

$model = Dsl::file(__DIR__.'/authorization-model.fga');

$fga->models()->write($model);            // or pass the raw text, it is detected
echo Dsl::toJson($dslText);               // DSL  -> JSON
echo Dsl::stringify($jsonFromTheServer);  // JSON -> DSL

// One yes/no. Cheap. Safe on the request path.
$fga->check('user:anne', 'viewer', 'document:budget');

// Many, in one call, answered in the order asked. What a list page needs.
$fga->query()->batchCheck([
    TupleKey::of('user:anne', 'viewer', 'document:1'),
    TupleKey::of('user:anne', 'viewer', 'document:2'),
]);

// "Which documents may Anne see?" Expensive, and capped - read on.
$fga->query()->listObjects('user:anne', 'viewer', 'document');

// The same, streamed: no cap, first result arrives immediately.
foreach ($fga->query()->streamListObjects('user:anne', 'viewer', 'document') as $document) {
    // ...
}

// "Who may see this document?" One kind of user per call - see below.
$fga->query()->listUsers('document:budget', 'viewer', 'user');          // subjects
$fga->query()->listUsers('document:budget', 'viewer', 'group#member');  // usersets

// Why. A tree, for debugging rather than for serving.
$fga->query()->expand('document:budget', 'viewer');

$tree = $fga->query()->expand('folder:budgets', 'viewer');

$tree->users();     // ['user:bob']            - assigned directly
$tree->usersets();  // ['folder:budgets#owner'] - and this, still to follow

use Piston\OpenFga\Model\TupleKey;

// One at a time.
$fga->grant('user:anne', 'owner', 'document:budget');
$fga->revoke('user:anne', 'owner', 'document:budget');

// Or atomically, writes and deletes together.
$fga->tuples()->write(
    writes: [TupleKey::of('user:anne', 'member', 'group:eng')],
    deletes: [TupleKey::of('user:bob', 'member', 'group:eng')],
);

'user:anne'             // a subject
'group:eng#member'      // a userset: whoever is `member` of group:eng
'user:*'                // everybody of that type - read it twice

foreach ($fga->tuples()->readAll('document:budget') as $tuple) {
    echo $tuple;   // document:budget#viewer@user:anne
}

foreach ($fga->tuples()->changesSince($savedToken) as $change) {
    $change->isWrite() ? $index->add($change->key) : $index->remove($change->key);
}

$fga->tuples()->grant('user:dana', 'viewer', 'document:q4', 'non_expired_grant', [
    'grant_time' => '2026-10-13T09:00:00Z',
    'grant_duration' => '1h',
]);

$fga->check('user:dana', 'viewer', 'document:q4', ['current_time' => '2026-10-13T09:30:00Z']); // true
$fga->check('user:dana', 'viewer', 'document:q4', ['current_time' => '2026-10-13T11:00:00Z']); // false

$fga->check('user:zoe', 'viewer', 'folder:budgets');   // false

$fga->check('user:zoe', 'viewer', 'folder:budgets', contextualTuples: [
    TupleKey::of('user:zoe', 'member', 'group:finance'),
]);                                                    // true

$fga->check('user:zoe', 'viewer', 'folder:budgets');   // false again - nothing was written

OpenFga::at($url, store: $storeId, model: $modelId);   // do this

use Piston\OpenFga\Exception\OpenFgaException;    // the interface, for one catch
use Piston\OpenFga\Exception\ValidationError;     // 400/422 - usually a tuple that does not fit the model
use Piston\OpenFga\Exception\Unauthenticated;     // 401/403 - *this SDK* may not talk to OpenFGA
use Piston\OpenFga\Exception\NotFound;            // 404
use Piston\OpenFga\Exception\RateLimited;         // 429
use Piston\OpenFga\Exception\TransportFailed;     // the network, or a non-JSON body
use Piston\OpenFga\Exception\InvalidArgument;     // your mistake, before any request

try {
    $fga->check('user:anne', 'nonexistent', 'document:budget');
} catch (ValidationError $e) {
    $e->errorCode();   // 'validation_error'
    $e->apiMessage();  // "invalid relation: relation 'document#nonexistent' not found"
}

use Piston\OpenFga\Bridge\Laravel\Concerns\AuthorizedByOpenFga;
use Piston\OpenFga\Contracts\FgaObject;

final class Document extends Model implements FgaObject
{
    use AuthorizedByOpenFga;      // `document:<key>`, or set $fgaType
}

Gate::allows('viewer', $document);
$this->authorize('can_read', $document);

Route::get('/documents/{document}', ShowDocument::class)
    ->middleware('fga:viewer,document');

public function show(OpenFga $fga, Document $document)
{
    abort_unless($fga->check("user:{$request->user()->id}", 'viewer', $document->fgaObject()), 403);
}

// config/bundles.php
return [
    Piston\OpenFga\Bridge\Symfony\OpenFgaBundle::class => ['all' => true],
];

#[IsGranted('viewer', subject: 'document')]
public function show(Document $document): Response

// or
$this->denyAccessUnlessGranted('can_read', $document);