tijmenwierenga / snowplow-tracker
Snowplow tracker for PHP powered applications
Requires
- php: ^8.1
- php-http/discovery: ^1.14
- php-http/httplug: ^2.2
- psr/http-client: ^1.0
- ramsey/collection: ^1.2.0
- ramsey/uuid: ^4.2.3
Requires (Dev)
- friendsofphp/php-cs-fixer: ^3.2
- infection/infection: ^0.25.3 || ^0.26.0
- nyholm/psr7: ^1.4
- phpunit/phpunit: ^9.5.5
- psalm/plugin-phpunit: ^0.16.1
- rector/rector: ^0.12.4
- symfony/http-client: ^5.3 || ^6.0
- symfony/var-dumper: ^5.3 || ^6.0
- vimeo/psalm: ^4.13
This package is auto-updated.
Last update: 2024-11-06 08:40:34 UTC
README
Snowplow Tracker
An alternative to the original Snowplow Tracker.
This Tracker provides:
- an object-oriented API for event tracking
- extension points in order to integrate your own domain logic
- abstractions to swap dependencies
Installation
With composer:
composer require tijmenwierenga/snowplow-tracker
Setup
The Snowplow Tracker is instantiable by providing an emitter and optionally additional configuration.
Currently, only a single emitter is available: the HttpClientEmitter
.
HttpClientEmitter
The HttpClientEmitter
sends the payload to a collector over HTTP.
If you want to use this emitter a PSR-7, PSR-17 and PSR-18 compliant implementation needs to be installed.
Popular PSR-18 compliant HTTP Clients are:
Popular PSR-7 compliant libraries are:
By default, the php-http/discovery
will discover the installed HTTP Client and Request Factory so no additional configuration is required.
If you wish to configure your HTTP client yourself you can pass in your own. Same goes for the Request Factory.
With auto-discovery:
<?php declare(strict_types=1); use TijmenWierenga\SnowplowTracker\Tracker; use TijmenWierenga\SnowplowTracker\Emitters\HttpClientEmitter; $emitter = new HttpClientEmitter('https://your-collector-uri') $tracker = new Tracker($emitter)
Without auto-discovery (with Symfony's HTTP client):
<?php declare(strict_types=1); use Symfony\Component\HttpClient\Psr18Client; use TijmenWierenga\SnowplowTracker\Emitters\HttpClientEmitter; use TijmenWierenga\SnowplowTracker\Tracker; // Instantiate your own HTTP Client $httpClient = new Psr18Client(); // Pass it to the emitter $emitter = new HttpClientEmitter('https://your-collector-uri', $httpClient); $tracker = new Tracker($emitter);
Tracker configuration
In order to customize the tracker's configuration you can pass an additional configuration object:
<?php declare(strict_types=1); use TijmenWierenga\SnowplowTracker\Tracker; use TijmenWierenga\SnowplowTracker\Config\Platform; use TijmenWierenga\SnowplowTracker\Config\TrackerConfig; $config = new TrackerConfig( Platform::WEB, // The platform you're sending events from 'My Tracker Name', // The name of your tracker 'my-app-id' // A unique identifier for your app ); $tracker = new Tracker(config: $config);
Handling failures
Whenever emitting an event fails, you don't want to lose the data.
Therefore, a TijmenWierenga\SnowplowTracker\Emitters\FallbackStrategy
exists in order to help you recover from failures.
The FallbackStrategy
is called whenever the TijmenWierenga\SnowplowTracker\Emitters\FailedToEmit
exception is raised.
By default, a void fallback strategy is used, which means nothing happens when an event failed emitting.
It's advised to implement an own implementation that stores the failed payloads in order to attempt to send the failed events at a later time.
Configure the fallback strategy as a constructor argument for the Tracker
:
<?php declare(strict_types=1); $fallbackStrategy = new MyFallbackStrategy(); $tracker = new Tracker(fallbackStrategy: $fallbackStrategy);
If a FailedToEmit
exception is raised, the Tracker
will rethrow the exception after calling the fallback strategy.
This could potentially lead to client-facing errors which may be undesirable.
This behaviour can be changed by setting the $throwOnError
constructor argument for the Tracker
:
<?php declare(strict_types=1); $tracker = new Tracker(throwOnError: false);
Please note that without a sufficient fallback strategy, ignoring exceptions will lead to data loss without anyone noticing.
Usage
Tracking events is done by calling the track()
method on the Tracker
instance:
This library implements 6 type of events.
Pageviews
<?php declare(strict_types=1); use TijmenWierenga\SnowplowTracker\Events\Pageview; $tracker->track(new Pageview( 'https://github.com/TijmenWierenga', 'Tijmen Wierenga (Tijmen Wierenga)', 'https://twitter.com/TijmenWierenga' ));
Page pings
<?php declare(strict_types=1); use TijmenWierenga\SnowplowTracker\Events\PagePing; $tracker->track(new PagePing( 0, // min horizontal offset 500, // max horizontal offset 250, // min vertical offset 300 // max vertical offset ));
Ecommerce transactions
<?php declare(strict_types=1); use TijmenWierenga\SnowplowTracker\Events\EcommerceTransaction; $tracker->track(new EcommerceTransaction( 'd85e7b63-c046-47ac-b9a9-039d33ef3b3b', // order ID 49.95, // total value ));
Transaction items
<?php declare(strict_types=1); use TijmenWierenga\SnowplowTracker\Events\TransactionItem; $tracker->track(new TransactionItem( 'd85e7b63-c046-47ac-b9a9-039d33ef3b3b', // order ID '48743-48284-24', // SKU 12.95, // price 4 // quantity ));
Structured events
<?php declare(strict_types=1); use TijmenWierenga\SnowplowTracker\Events\StructuredEvent; $tracker->track(new StructuredEvent( 'my-category', 'my-action' ));
Unstructured events
<?php declare(strict_types=1); use TijmenWierenga\SnowplowTracker\Events\UnstructuredEvent; use TijmenWierenga\SnowplowTracker\Schemas\Schema; use TijmenWierenga\SnowplowTracker\Schemas\Version; $tracker->track(new UnstructuredEvent( new Schema( 'com.snowplowanalytics.snowplow', 'ad_impression', new Version(1, 0, 0) ), [ 'impressionId' => 'dcefa2cc-9e82-4d7e-bbeb-eef0e9dad57d' ] ));
The Snowplow Tracker protocol
All events extend from a base event class which implements all properties currently available in the Snowplow Tracker Protocol.
These properties are publicly available in every event.
The example below shows how to add a userId
to an event to identify a user:
<?php declare(strict_types=1); use TijmenWierenga\SnowplowTracker\Events\Pageview; $pageviewEvent = new Pageview('https://github.com/TijmenWierenga'); $pageviewEvent->userId = 'TijmenWierenga';
Custom context
Sometimes you want to add additional context to an event.
Custom contexts are self-describing JSON schema's which can be implemented by creating a class that implements TijmenWierenga\SnowplowTracker\Events\Schemable
.
The example below shows an implementation of the existing Timing JSON Schema as defined by Snowplow Analytics.
<?php declare(strict_types=1); use TijmenWierenga\SnowplowTracker\Events\Schemable; use TijmenWierenga\SnowplowTracker\Schemas\Schema; use TijmenWierenga\SnowplowTracker\Schemas\Version; final class Timing implements Schemable { public function __construct( private readonly string $category, private readonly string $variable, private readonly int $timing, private readonly ?string $label = null ) { } public function getSchema(): Schema { return new Schema( 'com.snowplowanalytics.snowplow', 'timing', new Version(1, 0, 0) ); } public function getData(): array|string|int|float|bool|JsonSerializable { return array_filter([ 'category' => $this->category, 'variable' => $this->variable, 'timing' => $this->timing, 'label' => $this->label ]); } }
As an example, let's include context about the page load in a pageview event:
<?php declare(strict_types=1); use TijmenWierenga\SnowplowTracker\Events\Pageview; $pageviewEvent = new Pageview('https://github.com/TijmenWierenga'); $pageLoad = new Timing( 'pageLoad', 'ms', 21 ); $pageviewEvent->addContext($pageLoad);
Middleware
Middlewares provides a way to act on events that are tracked. Every piece of middleware is a callable that receives the event as an argument and must return the (modified) event to the next piece of middleware:
callable(Event $event): Event
This is incredibly useful when you want to add contextual information to every event.
As an example, middleware is added that adds the userId
of the currently authenticated user to the event.
<?php declare(strict_types=1); use TijmenWierenga\SnowplowTracker\Events\Event; use TijmenWierenga\SnowplowTracker\Events\Pageview; use TijmenWierenga\SnowplowTracker\Events\StructuredEvent; use TijmenWierenga\SnowplowTracker\Tracker; final class AddUserIdMiddleware { public function __construct( private readonly AuthenticatedUserIdProvider $userIdProvider ) { } public function __invoke(Event $event): Event { $event->userId = $this->userIdProvider->getUserId(); return $event; } } $addUserIdMiddleware = new AddUserIdMiddleware(/** ... */); $tracker = new Tracker(middleware: [$addUserIdMiddleware]); $pageviewEvent = new Pageview('https://github.com/TijmenWierenga'); $structuredEvent = new StructuredEvent('my-category', 'my-action'); $tracker->track($pageviewEvent); $tracker->track($structuredEvent);
In the example above both events will now have a userId
attached.