PHP
Le cœur PHP vskstudio/takt-core-php (Packagist, v0.5.0) est agnostique au framework. Il couvre deux besoins côté serveur : rendre le snippet du runtime navigateur dans ton HTML, et envoyer des événements serveur-à-serveur (S2S) directement depuis PHP. Les bridges Laravel et Symfony s'appuient dessus.
composer require vskstudio/takt-core-php PHP 8.1+. SnippetRenderer n’a besoin d’aucune dépendance HTTP : cette ligne suffit pour rendre le snippet.
Le client S2S Takt découvre en revanche son transport via PSR-18 / PSR-17 (php-http/discovery) et le paquet ne déclare que les interfaces — tu apportes l’implémentation :
composer require vskstudio/takt-core-php guzzlehttp/guzzle
# ou : composer require vskstudio/takt-core-php symfony/http-client nyholm/psr7 Sans implémentation PSR-18 / PSR-17 installée, new Takt(...) lève dès la construction (la découverte a lieu dans le constructeur, hors du filet fire-and-forget).
SnippetRenderer — le runtime navigateur
SnippetRenderer produit le bloc <script> à déposer dans le <head>. Il se configure avec un objet Options en lecture seule.
use Vskstudio\Takt\SnippetRenderer;
use Vskstudio\Takt\Options;
use Vskstudio\Takt\Mode;
$renderer = new SnippetRenderer(new Options(
domain: 'exemple.fr',
outbound: true,
files: true,
tagged: true,
notFound: true,
fileExtensions: ['pdf', 'zip'],
));
echo $renderer->render(); // à placer dans <head> | Champ | Défaut | Rôle |
|---|---|---|
domain | (requis) | Domaine du site |
endpoint | https://taktlytics.com/api/event | Endpoint d’ingestion — par défaut l’origine Takt hébergée (un setup nu marche direct) ; passe /api/event pour un proxy same-origin |
scriptOrigin | null | Origine first-party pour servir le runtime + dériver l’endpoint (contourne les ad-blockers ; endpoint prime) |
outbound | false | Suit les liens sortants (token outbound) |
files | false | Suit les téléchargements (token downloads) |
fileExtensions | [] | Restreint les téléchargements à ces extensions (data-downloads-ext) ; vide = liste par défaut |
tagged | false | Suit les éléments marqués data-takt-event (token tagged) |
notFound | false | Suit les pages 404 (token 404) |
excludeLocalhost | true | Ignore les événements localhost |
nonce | null | Nonce CSP pour la balise <script> |
sampleRate | null | N’envoie que cette fraction (0–1) des hits (data-sample-rate) |
trackQuery | null | Conserve la query string + le hash (data-track-query) ; off = strippé |
queryParams | [] | Allowlist de paramètres de query conservés (data-query-params) — sans effet si trackQuery est actif, qui garde tout |
respectDnt | null | false cesse de respecter Do-Not-Track (data-respect-dnt) |
enabled | null | false = coupe-circuit, snippet no-op (data-enabled) |
scrubUrl | null | Fonction JS brute de réécriture d’URL — requiert Mode::Sdk |
exclude | [] | Préfixes de chemin jamais suivis (borné au segment) — requiert Mode::Sdk |
mode | Mode::Inline | Source du runtime (voir ci-dessous) |
enum Mode :
Inline— embarque le bundletakt.auto.jsvendoré dans la balise (zéro requête en plus, compatible CSP avec un nonce).Cdn— émet un loader<script defer src="https://cdn.jsdelivr.net/npm/@vskstudio/[email protected]/dist/takt.auto.js">.Asset—defer src="/takt/takt.auto.js"pointant vers une copie auto-hébergée, ou{scriptOrigin}/takt/takt.auto.jssiscriptOriginest défini.Sdk— émet un<script type="module">import{init}…;init({…})</script>qui boote le SDK complet. Seul mode capable d’exprimerscrubUrl; le module est chargé depuis{scriptOrigin}/takt/takt.esm.jssiscriptOriginest défini, sinon depuis jsDelivr. Le paquet ne vendore quetakt.auto.js:takt.esm.jsn’est pas fourni, c’est à toi de le déposer à cette URL, sinon le module renvoie un 404 et rien n’est mesuré.
L’autocapture est opt-in : outbound, files, tagged et notFound ajoutent chacun un token à un unique attribut data-auto que lit le bundle takt.auto.js ; fileExtensions restreint les téléchargements comptés.
Options avancées
Chaque option avancée vaut null par défaut (« non définie » — le défaut du tracker s’applique) ; seule une valeur non-défaut est rendue. sampleRate, trackQuery, queryParams, respectDnt et enabled se reflètent en attributs data-* dans les modes Inline, Cdn et Asset (parité snippet, voir Configuration) ; en Mode::Sdk aucun attribut data-* n’est émis, tout part dans l’objet passé à init(). scrubUrl est une fonction JS brute injectée verbatim dans la page : elle ne s’exprime qu’en Mode::Sdk (la passer dans un autre mode lève une exception) et reste contrôlée par le dev — ne la construis jamais à partir d’entrées utilisateur. exclude vit lui aussi uniquement dans le SDK complet (le snippet minimal ≤ 1 kB ne l’embarque pas) : il requiert Mode::Sdk et lève une exception dans les autres modes plutôt que de laisser filer silencieusement des chemins qu’on croyait exclus.
new Options(
domain: 'exemple.fr',
mode: Mode::Sdk,
sampleRate: 0.5,
queryParams: ['utm_source'],
exclude: ['/app', '/account'],
scrubUrl: '(u) => u.split("?")[0]',
); spa). Le respect de Do-Not-Track est actif par défaut ; ne passe respectDnt: false (ou data-respect-dnt="false") que si tu sais exactement pourquoi.Options::fromArray(array $a): self construit les options depuis un tableau et accepte en plus les alias snake_case (exclude_localhost, not_found, file_extensions, sample_rate, track_query, query_params, respect_dnt, scrub_url) — c’est ce qui rend les bridges Laravel et Symfony configurables par fichier de config. Deux constantes évitent de recopier les URLs : Options::HOSTED_ORIGIN (https://taktlytics.com) et Options::HOSTED_ENDPOINT (https://taktlytics.com/api/event). Si tu assembles toi-même une balise inline, SnippetRenderer::neutralizeScriptClose(string $js): string est publique : elle échappe les fermetures de balise script contenues dans un bundle.
Takt — le client serveur-à-serveur
Takt poste des événements sur POST /api/event avec une clé d’API portant la permission events:write (préfixe takt_ik_), liée au site dont le domaine correspond exactement à domain — sinon l’ingest répond 401. Idéal pour tracer des actions qui n’ont pas lieu dans le navigateur (webhook de paiement, job en file…).
L’endpoint du client S2S est une origine de base, jamais un chemin : /api/event y est ajouté à chaque envoi. Ne recopie donc pas le défaut du tableau SnippetRenderer ci-dessus, qui est une URL complète — il donnerait https://taktlytics.com/api/event/api/event. Passe Options::HOSTED_ORIGIN, ou l’origine de ton proxy first-party si tu en sers un.
use Vskstudio\Takt\Options;
use Vskstudio\Takt\Revenue;
use Vskstudio\Takt\Takt;
// Forwarde l'IP + l'User-Agent du visiteur réel pour l'attribution
// (adapte la lecture de la requête à ton framework) :
$takt = (new Takt(
endpoint: Options::HOSTED_ORIGIN, // https://taktlytics.com
domain: 'exemple.fr',
apiKey: $_ENV['TAKT_API_KEY'],
))->withVisitor($_SERVER['REMOTE_ADDR'] ?? null, $_SERVER['HTTP_USER_AGENT'] ?? null);
$takt->event('Signup', ['plan' => 'pro'], null, 'https://exemple.fr/inscription');
$takt->event(
'Purchase',
['plan' => 'pro'],
new Revenue(amount: '29.00', currency: 'EUR'),
'https://exemple.fr/merci',
);
$takt->pageview('https://exemple.fr/merci'); __construct(string $endpoint, string $domain, ?string $apiKey = null, ?ClientInterface $httpClient = null, ?RequestFactoryInterface $requestFactory = null, ?StreamFactoryInterface $streamFactory = null)— les trois derniers paramètres injectent le transport PSR (indispensable pour tester) ; laissés ànull, ils sont découverts.withVisitor(?string $ip, ?string $userAgent): self— lie le visiteur réel avant l’envoi (renvoie une instance depuis laquelle envoyer)event(string $name, array $props = [], ?Revenue $revenue = null, ?string $url = null, ?string $referrer = null): void—$propsest unarray<string,scalar>dont chaque valeur est castée en chaîne (truepart en'1')pageview(?string $url = null, ?string $referrer = null): voidstrict(): self— renvoie un clone qui lève sur erreur de transport ou sur toute réponse ≠202(pratique en test)- Fire-and-forget : en mode par défaut, erreurs de transport comme réponses d’erreur sont avalées (l’analytics ne doit jamais casser l’app). Un
202signifie « accepté », pas « enregistré » : un User-Agent classé bot ou un opt-out (DNT / GPC) est jeté côté serveur, avec un202malgré tout.
$url en URL absolue http(s). Omise, elle part vide, l'ingest répond 400 et l'événement n'est pas enregistré — en mode par défaut le rejet est avalé, l'appelant ne voit rien.withVisitor(), les events S2S sont attribués au serveur applicatif, pas au visiteur réel — forwarde l'IP / l'User-Agent depuis la requête courante.Revenue est un value object en lecture seule (amount, currency) : le montant est une chaîne décimale, la devise un code à 3 lettres majuscules (miroir du SDK JS). Les deux sont validés à la construction — '29,00' ou 'eur' lèvent une InvalidArgumentException.