takt

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>
ChampDéfautRôle
domain(requis)Domaine du site
endpointhttps://taktlytics.com/api/eventEndpoint d’ingestion — par défaut l’origine Takt hébergée (un setup nu marche direct) ; passe /api/event pour un proxy same-origin
scriptOriginnullOrigine first-party pour servir le runtime + dériver l’endpoint (contourne les ad-blockers ; endpoint prime)
outboundfalseSuit les liens sortants (token outbound)
filesfalseSuit les téléchargements (token downloads)
fileExtensions[]Restreint les téléchargements à ces extensions (data-downloads-ext) ; vide = liste par défaut
taggedfalseSuit les éléments marqués data-takt-event (token tagged)
notFoundfalseSuit les pages 404 (token 404)
excludeLocalhosttrueIgnore les événements localhost
noncenullNonce CSP pour la balise <script>
sampleRatenullN’envoie que cette fraction (0–1) des hits (data-sample-rate)
trackQuerynullConserve 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
respectDntnullfalse cesse de respecter Do-Not-Track (data-respect-dnt)
enablednullfalse = coupe-circuit, snippet no-op (data-enabled)
scrubUrlnullFonction JS brute de réécriture d’URL — requiert Mode::Sdk
exclude[]Préfixes de chemin jamais suivis (borné au segment) — requiert Mode::Sdk
modeMode::InlineSource du runtime (voir ci-dessous)

enum Mode :

  • Inline — embarque le bundle takt.auto.js vendoré 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">.
  • Assetdefer src="/takt/takt.auto.js" pointant vers une copie auto-hébergée, ou {scriptOrigin}/takt/takt.auto.js si scriptOrigin est défini.
  • Sdk — émet un <script type="module">import{init}…;init({…})</script> qui boote le SDK complet. Seul mode capable d’exprimer scrubUrl ; le module est chargé depuis {scriptOrigin}/takt/takt.esm.js si scriptOrigin est défini, sinon depuis jsDelivr. Le paquet ne vendore que takt.auto.js : takt.esm.js n’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]',
);
Le suivi de navigation SPA est toujours actif (pas d'option 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$props est un array<string,scalar> dont chaque valeur est castée en chaîne (true part en '1')
  • pageview(?string $url = null, ?string $referrer = null): void
  • strict(): 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 202 signifie « accepté », pas « enregistré » : un User-Agent classé bot ou un opt-out (DNT / GPC) est jeté côté serveur, avec un 202 malgré tout.
L'URL de la page est requise sur chaque envoi : passe $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.
L'attribution visiteur est dérivée côté serveur de l'IP et de l'User-Agent. Sans 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.