cesargb / event-tracker
Track application events and send them to an analytics driver such as PostHog.
Requires
- php: ^8.4
- posthog/posthog-php: ^4.12
Requires (Dev)
- illuminate/support: ^12.0|^13.0
- larastan/larastan: ^3.0
- laravel/pint: ^1.27
- orchestra/testbench: ^10.0|^11.0
- phpunit/phpunit: ^13.1.6
Suggests
- illuminate/support: Required to use the Laravel integration (TrackerServiceProvider).
This package is auto-updated.
Last update: 2026-08-03 17:11:22 UTC
README
Send events to an analytics driver (currently PostHog) from plain PHP, or automatically from any event dispatched through Laravel's event system.
Installation
composer require cesargb/event-tracker
Plain PHP usage
use Cesargb\Event\Track; use Cesargb\Event\Track\Drivers\PostHogDriver; use Cesargb\Event\Track\Event; Track::init(new PostHogDriver('phc_your_api_key')); Track::capture(new Event( name: 'user_signup', userId: '42', timestamp: time(), properties: ['plan' => 'pro'], ));
Laravel usage
The service provider is auto-discovered. Publish the config file and set your PostHog API key:
php artisan vendor:publish --tag=event-tracker-config
POSTHOG_API_KEY=phc_your_api_key POSTHOG_HOST=https://eu.i.posthog.com # Optional: disable tracking entirely (e.g. in local/CI), defaults to true. EVENT_TRACKER_ENABLED=true
Auto-tracking events
Mark any class dispatched through Laravel's event system with the ShouldBeTracked marker interface and
it is sent automatically — no call to Track::capture() needed:
use Cesargb\Event\Track\Laravel\Contracts\ShouldBeTracked; class OrderShipped implements ShouldBeTracked { public function __construct(public readonly Order $order) {} } event(new OrderShipped($order));
This sends an order_shipped event with no properties and no user attribution. ShouldBeTracked has no
required methods — you implement only the ones you want to override:
| Method | Default when not implemented |
|---|---|
trackerName(): string |
Snake-case class basename (order_shipped) |
trackerProperties(): array |
[] |
trackerUserId(): ?string |
null — the event is anonymous/personless in PostHog |
trackerTimestamp(): int |
The current timestamp |
class OrderShipped implements ShouldBeTracked { public function __construct(public readonly Order $order) {} public function trackerProperties(): array { return ['total' => $this->order->total]; } public function trackerUserId(): ?string { return (string) $this->order->user_id; } }
If you don't override trackerUserId(), the event still reaches PostHog but can't be attributed to a
person — set it whenever you want the event tied to a user.
Tracking the authenticated user
For the common case of attributing an event to whoever is logged in, use the
TracksAuthenticatedUser trait instead of writing trackerUserId() by hand:
use Cesargb\Event\Track\Laravel\Concerns\TracksAuthenticatedUser; class OrderShipped implements ShouldBeTracked { use TracksAuthenticatedUser; public function __construct(public readonly Order $order) {} }
The id is read from the default guard when the event is captured. With no authenticated user
(console commands, queued jobs, guests) the event stays anonymous, exactly as if the trait
weren't there. A trackerUserId() declared on the event itself takes precedence over the trait.
Tracking constructor properties
For the common case of sending an event's own data as properties, use the
TracksConstructorProperties trait instead of writing trackerProperties() by hand:
use Cesargb\Event\Track\Laravel\Concerns\TracksConstructorProperties; class OrderShipped implements ShouldBeTracked { use TracksConstructorProperties; public function __construct( public readonly string $trackingNumber, #[\SensitiveParameter] public readonly string $customerEmail, private readonly Order $order, ) {} }
This sends ['tracking_number' => '...'] — only promoted, public constructor properties are
included, each renamed to snake_case:
| Excluded | Why |
|---|---|
private/protected promoted properties ($order above) |
Not part of the public API of the event |
A parameter marked #[\SensitiveParameter] ($customerEmail) |
Kept out of tracking on purpose |
| A public property that isn't a promoted constructor parameter | Out of scope for this trait |
A value with no safe scalar representation (a model, a closure, a pure enum, NAN/INF, a value nested more than 10 levels deep) |
Would leak more than intended or break the driver's JSON payload |
A few value types are cast rather than dropped: a backed enum becomes its scalar ->value, a
DateTimeInterface becomes an ISO-8601 string, and any Stringable object is cast with
(string). A trackerProperties() declared on the event itself takes precedence over the trait.
A failure while tracking an event (a misconfigured driver, a network error, a broken tracker method) is reported via Laravel's exception handler and swallowed — it never bubbles up to the code that dispatched the event.
Testing
composer test
vendor/bin/pint --test