Download the PHP package snipform/php-sdk without Composer
On this page you can find all versions of the php package snipform/php-sdk. It is possible to download/install these versions without Composer. Possible dependencies are resolved automatically.
Download snipform/php-sdk
More information about snipform/php-sdk
Files in snipform/php-sdk
Package php-sdk
Short Description Official PHP SDK for the Snipform API
License MIT
Homepage https://snipform.io
Informations about the package php-sdk
SnipForm PHP SDK
Official PHP SDK for the SnipForm API. Eloquent-flavoured query builder over the V2 endpoints.
Quick start
Contents
- Data Objects (DTOs)
- Returning from a Laravel controller
- Property
- Signals query builder
- Periods
- Sessions
- Pages
- Metrics
- Short links
- Session actions
- Conversions
- Attribution
asRaw()— opt-out of typed objects- Laravel
- Auto-identify on login
Snipformfacade- Identify middleware (SPAs / token APIs)
- Manual setup —
Clientinjection SnipFormSessionMiddleware
- Authentication
- Error handling
- Configuration
- Tests
Data Objects (DTOs)
Every typed return object extends SnipForm\Data\SnipFormDTO — a pure typed value object with public readonly fields. Two helpers:
DTOs hold no original payload; if you need the raw API JSON, flip the resource into raw mode with asRaw().
Returning from a Laravel controller
DTOs and PaginatedCollection both implement JsonSerializable, so a Laravel (or any PSR-7) controller can return them directly:
A PaginatedCollection serializes as Laravel's standard pagination JSON (data, current_page, last_page, total, next_page_url, …). Only page 1 is serialized — iterate first if you want all pages.
Property
The token is scoped to a single SnipForm Property. Pull its identity + headline counts:
Signals query builder
The first argument to every where*() method is a public field id. Pass it as a SessionField enum case (IDE-discoverable, type-checked) or as a bare string (escape hatch, no SDK-side validation).
Field/subfield/type are resolved server-side from the id, so the wire stays small.
Type-safe operators: when you pass an enum case, the SDK validates the operator against the field's type and throws IncompatibleFieldOperator before HTTP:
SessionField cases are grouped by concern: entry/exit page, referrer, tags, geo, browser/device/OS, bot detection, channel + UTM attribution, acquisition value, short links, forms, events, session metrics. Bare string fallback covers anything new the server adds before the enum catches up.
| Method | Op | Use for |
|---|---|---|
where($id, $value) |
equals |
equality (array value = IN) |
orWhere(...) |
equals (where=or) |
OR clause |
whereNot(...) |
equals (not=true) |
negate |
orWhereNot(...) |
equals (where=or, not=true) |
OR negate |
whereStartsWith($id, $v) |
starts_with |
prefix |
whereContains($id, $v) |
contains |
substring |
whereRegex($id, $pat) |
regex |
regex |
whereGt / Gte / Lt / Lte |
gt / gte / lt / lte |
numeric comparison |
whereBetween($id, $a, $b) |
between |
numeric range |
whereExists($id) |
exists |
field is present |
whereNotExists($id) |
exists (not=true) |
field is absent |
Each clause posts as {id, op, value, where?, not?}. where and not are omitted at default values.
Periods
Use the typed shorthands for autocomplete, or pass a Period case to period().
Invalid period strings throw SnipForm\Exceptions\InvalidPeriodException immediately — no HTTP round-trip.
Sessions, lazy
->sessions() returns a PaginatedCollection you can iterate. Each iteration step pulls the next page transparently.
Pages — explicit pagination
->page($n) returns a Data\Page object that carries the page's items plus the full Laravel paginator meta and navigation methods. One HTTP call gives you both — no separate ->count().
Page is iterable, countable, and array-accessible — so existing foreach/count/$page[0] usage keeps working:
Returning a Page from a Laravel controller serializes it as the standard Laravel paginator JSON for that page:
Metrics
Returns a MetricsResult value object:
To reach trend data (previous-period, percent, difference), use asRaw().
Short links
Three resources: groups (folders), links (the short URLs themselves), and clicks (the redirect events). Scoped to the property your token belongs to.
Link groups
Links
Each link exposes a small accessor for utm values:
Clicks
Read-only — clicks are recorded server-side from short-link redirects. The fluent filter builder chains until you call ->all() or ->find():
Filters:
| Method | Effect |
|---|---|
forLink($id) |
scope to one short link |
forGroup($id) |
scope to a link group |
between($fromTs, $toTs) |
unix timestamp range |
since($fromTs) |
open-ended range |
usersOnly() |
exclude bot clicks |
botsOnly() |
only bot clicks |
perPage($n) |
page size, 1–100 |
Session actions
Three writes scoped to a single SignalSession: resolve a visitor's session id from their request, submit a custom event, and patch acquisition metadata.
Resolve
Looks up the SignalSession that belongs to a visitor — by hashing their IP + User-Agent + language with the same daily salt the JS tracker uses. The visitor must already have been tracked once today on this property for the lookup to find a match.
The SDK accepts a Symfony or Laravel Request and pulls those values for you. Pass $request from your controller:
Or pass values explicitly if you're not on a Symfony-flavoured framework:
$resolved->resolved is false if the visitor hasn't been tracked yet today on this property. Handle that case before chaining further writes.
Important: the
ipmust be the visitor's IP from your incoming request, not your server's outbound IP. Your framework's$request->getClientIp()/ equivalent does the right thing automatically (resolves through proxies / CDNs). The SDK does not inspect the transport-level IP of its own outbound call.
Event
Submit a custom event for a session. Identifies the target session in one of two ways:
Returns a typed Event value object.
Acquisition
Patch acquisition metadata onto a session. Partial — only supplied keys are written. Tags merge with existing tags (deduped); cost / value / currency overwrite.
Returns the resulting acquisition_meta array along with the session id.
Typical end-to-end flow
Conversions
Two surfaces:
- Definition CRUD — list / find / create / update / replaceSteps / publish / toggle / delete, plus a
schema()lookup that returns the catalog of valid trigger types. - Analytics reads —
->for($id)opens a fluent reader: summary, segments, cycles, sessions-at-step.
Schema
Inspect the catalog of trigger types, conversion types, segment dimensions, and valid match modes before building a config:
Definition CRUD
Create a conversion via the fluent builder — each step() opens a sub-builder whose ->on*() method commits the step and returns the parent for chaining:
Step trigger terminals:
| Method | Triggers when |
|---|---|
->onPageView($value, $match = 'contains', $field = 'path') |
Visitor reaches a page |
->onEntryPage($value, $match = 'contains', $field = 'entry_path') |
Session entered on a page |
->onEvent($name, $value = null, $valueMatch = 'exists') |
Custom event fires |
->onFormSubmit($snipFormId) |
A specific SnipForm submits |
->onShortLink($id, $scope = 'link') |
Session arrived via short link/group |
Each step builder also exposes ->optional() to mark the step is_required: false.
Patch existing definitions:
Analytics
->for($id) returns a ConversionAnalytics you chain a window onto, then call a terminal:
Slice by a flat dimension or a custom tag key:
Cycle through day/week/month buckets with deltas vs the prior bucket:
Drill into sessions that reached a specific funnel step:
Window setters: ->between($fromTs, $toTs) or ->since($fromTs) (open-ended to now). Both take unix timestamps.
Attribution
$client->attribution() exposes the SnipForm channel-attribution engine for diagnostics — useful for "Test attribution" buttons in a link builder UI, or for verifying that a campaign's UTM combo will land in the channel category you expect.
The engine runs the same code path that classifies real tracker sessions, so a positive preview is contractual: if the engine says messaging/WhatsApp now, a visitor landing with those tags in production will be classified the same way.
Click IDs + referrer
Both can be passed when you want to simulate something beyond bare UTMs — e.g. checking that a gclid lands in paid_search even if the merchant forgot to set utm_medium=cpc:
Preset catalog
presets() returns the canonical chip catalog the SnipForm app's link builder uses. Render the chip strip in your own link-creation UX so the UTM taxonomy stays consistent between SnipForm and your app:
asRaw() — opt-out of typed objects
Every resource (and every builder chain) supports ->asRaw(). Terminals return the underlying API array instead of hydrating a typed DTO. Useful when you want fields the SDK doesn't surface, or when you're forwarding API responses to a frontend that already expects the SnipForm JSON shape.
Each $client->resource() call returns a fresh instance, so flipping asRaw on one chain doesn't affect the next.
Laravel
Optional Laravel layer that ships in SnipForm\Laravel\*. The SDK doesn't depend on Laravel — these classes only load when illuminate/support is installed. Composer's package discovery wires the provider; everything is opt-in via config.
Auto-identify on login
The happy path: every time a user logs into your app, attach their session to a SnipForm Contact automatically. No code at the callsite.
1. Set the token in .env:
2. Add the Identifiable trait to your User model:
That's it. The provider registers a listener on Illuminate\Auth\Events\Login and fires identify against Snipform whenever a user authenticates.
What gets sent. Identifiable auto-derives the payload by probing common columns:
| Where | Columns checked |
|---|---|
external_id |
$user->getKey() |
email (top level) |
email |
traits.* |
first_name, last_name, phone, company, job_title, website, country, city |
traits.first_name / traits.last_name (fallback) |
name split on whitespace |
Override what's sent by adding hooks to your model:
Performance — three layers of "don't overburden the API":
afterResponsequeueing. The identify call runs after Laravel sends the response. Login is never blocked.- Cache-backed dedup. The provider wires Laravel's default cache as an atomic gate via
Cache::add. The first request per(user, payload)per TTL goes over the wire; the rest are no-ops. - Server-side idempotency. Even if you bypass the dedup, the SnipForm API merges traits in place — repeat calls with the same payload are a no-op database read.
Config knobs (all .env-driven):
Full control over the rest by publishing the config file:
Snipform facade
Explicit identify calls — useful for paths the Login event doesn't cover (impersonation, webhook handlers, side flows):
All identify calls honor the same dedup + queue config — Snipform::auth() called a hundred times in the same hour costs you one API call.
To register the Snipform alias globally (so you don't have to use it everywhere), add to config/app.php:
Identify middleware (SPAs / token APIs)
Some apps don't go through the form login flow — token-auth APIs (auth:sanctum, auth:api), SPAs that maintain a session via Sanctum's stateful cookie, etc. The Login event never fires in those cases.
The snipform.identify middleware (alias registered by the provider) runs identify against the auth user on every request it's applied to. Cheap to use because the dedup gate short-circuits repeats:
Per-request cost when the cache is warm: one cache hit. When the cache misses: one identify call, deferred until after the response is sent.
To target a non-default guard:
Manual setup — Client injection
If you'd rather skip the Laravel layer and reach for the raw SDK, the Client is still bound as a singleton:
Config also reads from the legacy config/services.php → snipform location for back-compat — apps that wired the SDK before the Laravel layer existed keep working untouched.
To opt out of the auto-registered provider entirely, add to your app's composer.json:
SnipFormSessionMiddleware
Older / lighter helper that just lifts the visitor's session id off the inbound request and stashes it on $request->attributes. Use it when you want the id without invoking the SDK on every request (analytics-only handlers, presence checks, conditional logging).
Pulls from, in priority order:
X-SnipForm-Session-Idheader (set bysignals.jsattachToFetch())snip_session_idform field (set byattachToForm())snip_session_idquery string param
Register on the web/api middleware group:
Then in any downstream controller:
The Session resource also accepts a Request directly — $snipform->session()->event($request, [...]) — and pulls the same fields. The middleware is purely a convenience for the "I want the id but don't need the SDK yet" case.
Authentication
The SDK takes a property-scoped Personal Access Token. Generate one in Property → Settings → API Tokens. Tokens carry scope (e.g. signals:read, conversions:write) — the SDK forwards them and the API enforces.
Error handling
ApiException exposes:
| Property | Type | Notes |
|---|---|---|
->status |
int | HTTP status code |
->errors |
array | Laravel-style ['field' => ['msg', ...]] — empty for non-validation errors |
->body |
array | Full unwrapped response body |
Configuration
Tests
The unit suite is hermetic — no network, no env config required:
There's also a tests/Integration suite that hits a live SnipForm deployment. Copy the env template, fill in a token + base URL, then:
Integration tests are skipped when SNIPFORM_TEST_TOKEN isn't set, so CI without secrets still runs unit-only.