/Docs

Symfony integration

This guide targets Symfony 6.4+ running on PHP 8.2 or later. The avsbhq/avsb-phppackage includes a Symfony Bundle that registers the SDK in Symfony's dependency-injection container, wires per-request context scoping as an event subscriber, and exposes the client for injection into controllers and services. By the end you will be reading flags via constructor injection and tracking events with zero boilerplate.

1

Install

terminal
bash
1composer require avsbhq/avsb-php
2

Obtain your SDK key

Open your A vs B project, go to Settings → Environments, and copy the Server SDK key. Store it in your .env file and reference it from the bundle config:

.env
bash
1AVSB_SDK_KEY=sdk_production_...
3

Enable the bundle

config/bundles.php
php
1<?php
2
3return [
4 // ... existing bundles ...
5 Avsb\Symfony\AvsbBundle::class => ['all' => true],
6];
4

Configure the bundle

config/packages/avsb.yaml
yaml
1avsb:
2 sdk_key: '%env(AVSB_SDK_KEY)%'
3
4 # Optional: build the EvalContext from the current Request.
5 # This callable is called once per request by the event subscriber.
6 context_from: App\Avsb\RequestContextFactory

Create the context factory class referenced above:

src/Avsb/RequestContextFactory.php
php
1<?php
2
3namespace App\Avsb;
4
5use Avsb\Symfony\ContextFactoryInterface;
6use Symfony\Component\HttpFoundation\Request;
7
8final class RequestContextFactory implements ContextFactoryInterface
9{
10 public function fromRequest(Request $request): array
11 {
12 $user = $request->attributes->get('_user');
13
14 return [
15 'kind' => 'user',
16 'key' => $user?->getId() ?? 'anon',
17 'plan' => $user?->getPlan() ?? 'free',
18 ];
19 }
20}
5

Inject the client into a controller

Type-hint Avsb\Client in your controller constructor. Symfony autowires the request-scoped instance that the bundle registered.

src/Controller/CheckoutController.php
php
1<?php
2
3namespace App\Controller;
4
5use Avsb\Client as AvsbClient;
6use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
7use Symfony\Component\HttpFoundation\JsonResponse;
8use Symfony\Component\Routing\Annotation\Route;
9
10final class CheckoutController extends AbstractController
11{
12 public function __construct(private readonly AvsbClient $avsb) {}
13
14 #[Route('/checkout', name: 'checkout')]
15 public function show(): JsonResponse
16 {
17 $showNew = $this->avsb->getBoolFlag('checkout-v2', false);
18 $theme = $this->avsb->getStringFlag('ui-theme', 'default');
19
20 return $this->json([
21 'showNewCheckout' => $showNew,
22 'theme' => $theme,
23 ]);
24 }
25}
6

Track an event

php
1$this->avsb->track('purchase', ['value' => 149.99, 'properties' => ['currency' => 'usd']]);
7

Identify a user

The injected $avsb client is already scoped to the context built by your RequestContextFactory. If you need to evaluate against a different identity mid-controller — for example after a manual token exchange — inject Avsb\AvsbServer and call forUser:

php
1use Avsb\AvsbServer;
2
3// In your service / controller
4public function __construct(private readonly AvsbServer $server) {}
5
6// Inside a method
7$scoped = $this->server->forUser([
8 'kind' => 'user',
9 'key' => $user->getId(),
10 'plan' => $user->getPlan(),
11]);
12$showNew = $scoped->getBoolFlag('checkout-v2', false);

Using flags in Twig templates

The bundle registers a Twig extension that exposes the avsb_flagfunction and avsb global:

templates/checkout/show.html.twig
python
1{# avsb_flag(key, defaultValue) returns the typed value #}
2{% if avsb_flag('checkout-v2', false) %}
3 {{ include('checkout/_new.html.twig') }}
4{% else %}
5 {{ include('checkout/_legacy.html.twig') }}
6{% endif %}

Graceful shutdown

The bundle registers a kernel terminate listener that flushes pending events after the response is sent (kernel.terminate event). For CLI commands and Symfony Messenger workers, the bundle also listens to the console.terminate event. No additional configuration is required.

Testing

tests/Controller/CheckoutControllerTest.php
php
1<?php
2
3namespace App\Tests\Controller;
4
5use Avsb\Symfony\Testing\AvsbTestClient;
6use Symfony\Bundle\FrameworkBundle\Test\WebTestCase;
7
8final class CheckoutControllerTest extends WebTestCase
9{
10 public function testShowsNewCheckout(): void
11 {
12 $client = static::createClient();
13
14 // Override flags inside the test container
15 AvsbTestClient::flagOn('checkout-v2');
16
17 $client->request('GET', '/checkout');
18
19 $this->assertResponseIsSuccessful();
20 $data = json_decode($client->getResponse()->getContent(), true);
21 $this->assertTrue($data['showNewCheckout']);
22 }
23}
Test service decoration
AvsbTestClient decorates the Avsb\Client service definition in the test environment. It stores overrides in memory and resets them after each test — no network calls are made.

What's next