Symfony
Le bridge Symfony vskstudio/takt-symfony expose le cœur PHP sous forme de bundle : une fonction Twig pour le snippet et un service Takt autowiré pour le S2S. Compatible Symfony 6.4 / 7.x, PHP 8.1+.
composer require vskstudio/takt-symfony Le paquet n’expose pas de recette Flex : enregistre le bundle à la main.
// config/bundles.php
return [
// …
Vskstudio\Takt\Symfony\TaktBundle::class => ['all' => true],
]; Le TaktBundle enregistre ensuite ses services depuis la config sémantique : le SnippetRenderer derrière la fonction Twig, et le client S2S Takt. Ce dernier est RequestStack-aware : il forwarde l’IP et l’User-Agent de la requête courante pour l’attribution.
Cette page est la référence des options. Pour l’intégration de bout en bout et ses pièges (placement du snippet sous Turbo, arbitrage contrôleur de retour / webhook pour les conversions) : voir le guide analytics sans cookie dans une application Symfony.
Configuration
config/packages/takt.yaml :
takt:
domain: 'exemple.fr'
endpoint: 'https://taktlytics.com'
api_key: '%env(TAKT_API_KEY)%' # S2S uniquement: omets la clé sinon
mode: inline
outbound: true
files: true
tagged: true
not_found: true
file_extensions: ['pdf', 'zip'] Un %env(...)% doit correspondre à une variable déclarée : sans elle, le conteneur lève EnvNotFoundException sur toutes les pages qui rendent le snippet.
# .env
TAKT_API_KEY=tk_… Référence des clés
| Clé | Défaut | Rôle |
|---|---|---|
domain | '' | Domaine du site (attribut data-domain, et domaine porté par chaque event S2S) |
endpoint | https://taktlytics.com | Cible d’ingestion : l’origine ou l’URL de collecte complète, voir ci-dessous |
script_origin | null | Origine first-party qui sert le runtime et dont l’endpoint est dérivé (contourne les ad-blockers) ; endpoint prime |
api_key | null | Clé d’API scope=ingest, utilisée uniquement par le client S2S |
mode | inline | Source du runtime : inline, cdn, asset ou sdk |
outbound | false | Suit les liens sortants (token outbound) |
files | false | Suit les téléchargements (token downloads) |
tagged | false | Suit les éléments marqués data-takt-event (token tagged) |
not_found | false | Suit les pages 404 (token 404) |
file_extensions | [] | Restreint les téléchargements comptés ; vide = liste par défaut du tracker |
exclude_localhost | true | Ignore les hits émis depuis localhost : la raison la plus fréquente d’un « rien ne remonte » en dev |
nonce | null | Nonce CSP posé sur la balise <script> (indispensable en mode: inline sous CSP stricte) |
redact_routes | [] | Routes sensibles envoyées sous la forme du motif, requiert mode: sdk (voir Masquage de routes) |
route_templates | false | Envoie chaque page sous la forme du chemin de sa route, requiert mode: sdk |
redact_undecodable_paths | true | Chemins illisibles comparés aux routes au lieu de /[redacted] ; événements S2S seulement |
sample_rate | null | N’envoie qu’une fraction (0 à 1) des hits |
track_query | null | Conserve la query string + le hash (défaut : strippés) |
query_params | [] | Allowlist de paramètres conservés quand track_query est off |
exclude | [] | Préfixes de chemins jamais suivis (requiert mode: sdk) |
respect_dnt | null | false cesse de respecter Do-Not-Track |
enabled | null | false = coupe-circuit, snippet no-op |
scrub_url | null | Fonction JS brute de réécriture d’URL (requiert mode: sdk) |
Les cinq dernières options avancées valent null par défaut (« non définie » : le défaut du tracker s’applique) ; ne les pose que pour une valeur non-défaut, sample_rate: 0.5 jette la moitié du trafic et enabled: false coupe toute la mesure.
Les modes :
inline: embarque le bundletakt.auto.jsvendoré dans la balise (zéro requête en plus).cdn: loader<script defer src="https://cdn.jsdelivr.net/npm/@vskstudio/[email protected]/dist/takt.auto.js">.asset: loader vers/takt/takt.auto.js, une copie que tu héberges toi-même (voir le callout en fin de page).sdk:<script type="module">qui boote le SDK complet.
mode n’est pas validé : une valeur inconnue retombe silencieusement sur inline.
La clé endpoint
La même clé alimente deux consommateurs : le snippet Twig, qui a besoin de l’URL de collecte complète, et le service Takt (S2S), qui a besoin de l’origine à laquelle il ajoute /api/event. Le bundle accepte donc les deux formes et les normalise pour chacun :
Valeur d’endpoint | Le snippet POSTe sur | Le service S2S POSTe sur |
|---|---|---|
https://taktlytics.com (défaut) | https://taktlytics.com/api/event | https://taktlytics.com/api/event |
https://taktlytics.com/api/event | https://taktlytics.com/api/event | https://taktlytics.com/api/event |
/api/event (proxy same-origin) | /api/event sur ton domaine | chemin relatif : aucun envoi n’aboutit |
Une valeur qui porte un autre chemin est prise telle quelle comme URL de collecte : c’est le cas d’un proxy first-party servi depuis ton propre domaine. Un chemin relatif ne convient qu’au snippet ; si tu envoies aussi des événements serveur, garde une URL absolue.
script_origin ne sert à dériver l’endpoint du snippet qu’avec l’endpoint hébergé (sous l’une ou l’autre forme ci-dessus) : le tracker POSTe alors sur {script_origin}/api/event. Avec toute autre valeur, l’endpoint est émis en data-endpoint et prime.
mode: sdk
mode: sdk charge le SDK en module ES. C’est le seul mode capable d’exprimer scrub_url, une fonction JS brute de réécriture d’URL injectée verbatim dans la page, et exclude, les préfixes de chemins jamais suivis. Le snippet minimal n’embarque ni l’un ni l’autre : les poser dans un autre mode lève une exception au rendu plutôt que de les laisser filer silencieusement. Réserve scrub_url au dev : ne la construis jamais à partir d’entrées utilisateur.
takt:
mode: sdk
exclude: ['/app', '/compte']
scrub_url: "(u) => u.split('#')[0]" redact_routes et route_templates suivent la même règle, voir Masquage de routes.
Fonction Twig takt()
Rends le snippet dans le <head> de ton template de base :
<head>
<meta charset="utf-8">
{{ takt() }}
</head> takt() ne prend aucun argument : l’autocapture (outbound, files, tagged, not_found) et toutes les autres options se règlent uniquement dans config/packages/takt.yaml. Un argument passé depuis un template est ignoré sans erreur. Il n’y a pas de surcharge par template.
Service Takt autowiré (serveur-à-serveur)
Type-hinte simplement Vskstudio\Takt\Takt, l’autowiring fait le reste :
namespace App\Controller;
use Symfony\Component\HttpFoundation\Response;
use Vskstudio\Takt\Revenue;
use Vskstudio\Takt\Takt;
final class CheckoutController
{
public function __construct(private readonly Takt $takt) {}
public function success(): Response
{
$this->takt->event('Purchase', ['plan' => 'pro'], new Revenue(amount: '29', currency: 'EUR'));
return new Response('Merci');
}
} L’envoi est fire-and-forget : un 202 vaut succès, et les erreurs de transport sont avalées pour ne jamais casser la requête. $this->takt->strict() renvoie une instance qui lève sur tout statut différent de 202 (utile en test). pageview() et withVisitor() sont détaillés sur la page cœur PHP.
Request::getClientIp(), qui n'honore X-Forwarded-For que si framework.trusted_proxies est configuré. Derrière un CDN ou un reverse proxy non déclaré, tous les events S2S sont attribués à l'IP du proxy.mode: asset, le loader pointe vers /takt/takt.auto.js, mais le bundle ne publie aucun asset : copie toi-même vendor/vskstudio/takt-core-php/resources/takt.auto.js dans public/takt/takt.auto.js. En mode: sdk avec script_origin, le module est chargé depuis /takt/takt.esm.js sur cette origine. Ce fichier n'est pas vendoré non plus, c'est à toi de le servir.Masquage de routes
Le nettoyage des URLs retire la query et le hash, mais le chemin part en clair : /verify/abc123 transmet le jeton. Depuis la 0.6.0 (cœur PHP 0.6), deux nœuds de configuration remplacent les chemins réels par le motif de la route, dans le snippet takt() comme dans le service Takt autowiré (voir Masquage de routes). Côté snippet, ils requièrent mode: sdk : dans un autre mode, le SnippetRenderer lève une exception, puisque le snippet minimal ne sait pas masquer.
redact_routes
takt:
mode: sdk
redact_routes: ['/verify/{token}', '/reset/{code}'] Un chemin qui correspond à l’un des motifs part sous la forme du motif, tous les autres gardent leur chemin réel. Les motifs acceptent la syntaxe Symfony ({token}, {page?}) comme [p], [[p]], [...p], (groupe), :p, :p?, * et ** (détail sur la page cœur PHP). Les contraintes et valeurs par défaut en ligne sont retirées du motif envoyé ({id<\d+>?1} devient {id}).
Le SDK navigateur ne lit pas les accolades : {token} lui est transmis sous la forme [token]. Une pageview du navigateur remonte donc /verify/[token], alors qu’un événement envoyé par le service Takt remonte /verify/{token}. Écris /verify/[token] si les deux doivent tomber sur la même ligne.
route_templates
takt:
mode: sdk
route_templates: true Chaque page part sous la forme du chemin de sa route : /users/42 devient /users/{id}. Le bundle enregistre alors un service privé Vskstudio\Takt\Symfony\RouteTemplate, qui résout le _route de la requête principale via le routeur (Route::getPath()). Ce chemin est rendu dans le snippet takt() et sert de route par défaut à chaque pageview() et event() du service Takt autowiré. Il n’est pas traduit en crochets : navigateur et serveur remontent tous deux /users/{id}. Une requête sans route correspondante garde son chemin réel, toujours soumis à redact_routes.
route_templates convient à une application privée ; sur un site public, il fusionne tous tes articles en une seule ligne du classement Pages.
redact_undecodable_paths
takt:
redact_undecodable_paths: false Booléen, true par défaut, depuis le bundle 0.7.0 : un chemin illisible (échappement % mal formé ou UTF-8 invalide une fois décodé) part en /[redacted] dès qu’une route est configurée. Avec false, il est comparé aux routes comme les autres (voir Chemins illisibles). Le nœud ne règle que le service Takt autowiré (événements serveur) ; le snippet takt() garde le défaut, masqué.
Gabarit explicite
Chaque appel serveur peut choisir son propre gabarit, qui prime sur le défaut :
$this->takt->event('Verified', url: $request->getUri(), route: '/verify/{token}');
$this->takt->withRoute('/factures/{id}')->pageview($request->getUri()); withRoute() renvoie une instance dont tous les appels partent sous ce gabarit ; il accepte aussi une closure, évaluée à l’envoi. Détail des arguments sur la page cœur PHP.