Download the PHP package mirafive/sdk-php without Composer
On this page you can find all versions of the php package mirafive/sdk-php. It is possible to download/install these versions without Composer. Possible dependencies are resolved automatically.
Download mirafive/sdk-php
More information about mirafive/sdk-php
Files in mirafive/sdk-php
Package sdk-php
Short Description PHP SDK for MIRA FIVE, privacy-first analytics: server-side events and feature flags.
License MIT
Homepage https://mirafive.io
Informations about the package sdk-php
MIRA FIVE for PHP
Privacy-first analytics and feature flags for PHP servers: send events from your backend and evaluate flags in-process, hosted in the EU.
Install
PHP 8.3 or newer with ext-json. ext-curl is used when present, otherwise PHP streams; you can also send through your own PSR-18 client. There are no required Composer dependencies.
Using a framework? Take mirafive/sdk-laravel or mirafive/sdk-symfony: they wire this SDK into the container and flush after the response.
Quickstart
Create a server source in MIRA FIVE and put its secret key in the environment:
For events that must be recorded exactly once, such as a payment webhook that may be delivered twice, send them immediately with an idempotency key:
The same key always maps to the same batch, so a repeat is stored once.
Consent & privacy
MIRA FIVE has two collection modes:
- Full (
Mode::Full, the default) may carryuserId,anonymousIdandsessionId. Use it for people who consented, or where you already hold another lawful basis for the processing. - Consentless (
Mode::Consentless) carries no identifiers at all. Passing one throws anInvalidArgumentException, so a misconfiguration shows up on the first call instead of as refused batches.
Rules that hold in both modes:
- Never put personal data in event names or properties. No e-mail addresses, names, phone numbers or free text a person typed. Event names are labels such as
signup, notsignup [email protected]. userIdis pseudonymous. Pass your own internal id (u_42, a UUID), never an e-mail address.- The secret key stays on the server. It never goes into HTML, JavaScript or a mobile app. Browsers use the public website key with the browser SDK.
API reference
MiraFive\Mira
| Member | Behaviour |
|---|---|
track(string $name, ?string $userId = null, ?string $anonymousId = null, ?string $sessionId = null, array $properties = [], DateTimeInterface\|int\|null $time = null, ?array $page = null): void |
Buffers one event. time is a DateTimeInterface or epoch milliseconds; it defaults to now. page takes url, title, referrer. |
identify(string $userId, array $traits = [], ?string $anonymousId = null, DateTimeInterface\|int\|null $time = null): void |
Buffers $identify: the person's traits, and a link from the browser's anonymous id when given. Full mode only. |
send(array $events, ?string $idempotencyKey = null): Receipt |
Sends 1–1000 events now as one batch. The idempotency key, when given, must not be empty. Each event is an array with name and optionally userId, anonymousId, sessionId, properties, time, page, id. Throws MiraError. |
flush(): void |
Sends the buffer, or hands it to handOff. Never throws; failures go to onError, the logger, or error_log(). |
deliverPrepared(string $body): Receipt |
Sends a batch a handOff received, with the usual retries and the refused-event recovery below, under this client's key. Throws MiraError. |
flags(): Flags\MiraFlags |
The flags of this source, sharing key, host, transport and cache. |
The buffer is also sent when flushAt is reached, when the Mira object is destroyed, and once in a shutdown function at the end of the request (unless flushOnShutdown: false).
Defaults. flushAt 100 events, timeoutMs 5,000 per attempt, connectTimeoutMs 1,000, flushDeadlineMs 3,000, maxRetries 2, maxRetryAfterMs 3,000. They are lower than the Node server SDK's (10 s timeout, 3 retries) because delivery usually runs inside a PHP web request. For flags: refreshSeconds 30, a 1,500 ms document fetch, a 500 ms segment lookup and a 1,000 ms connect timeout.
Outages. A flush never takes longer than flushDeadlineMs: each attempt's timeout shrinks to what is left, and a wait that would pass the deadline ends the flush. After a flush fails for a retryable reason, flushes skip the network for 30 s and drop their events; the first skip is reported to onError. With a PSR-16 cache, that 30 s pause holds for every PHP process, so an outage costs one request a timeout rather than every request. send() and deliverPrepared() are not paused; they always try.
Refused events. When the collector refuses a buffered batch with validation_failed, the events its errors name are reported to onError and dropped, and the rest is resent under a new batch id derived from the old one (the same input always gives the same id, so a queue job that runs twice is still stored once). When an error names no event, the whole batch is reported and dropped.
Delivery. Batches go to POST {host}/v1/batch as JSON with the secret key as a bearer token, at most 1000 events and 1 MiB each (larger buffers are split). Retries use full-jitter exponential backoff (100 ms base, 1 s cap), honour Retry-After, and resend the byte-identical body under the same batch id, so a retry is never counted twice.
Empty objects. A PHP [] is sent as a JSON list; pass new stdClass where you mean {}.
Input checks. Input the collector would refuse throws an InvalidArgumentException immediately, because one bad event would otherwise cost every event in its batch: names of 1–128 characters without surrounding whitespace, $ names other than the reserved ones ($pageview, $autocapture, $identify, $search, $install_check, $exposure), blank or overlong ids, properties that are a list, nest deeper than 5 levels, carry more than 64 values or encode to more than 32 KB. Page fields that are too long are shortened instead.
Queues, frameworks and tests
- Deliver from a queue. With
handOff, every buffered flush (explicit, atflushAt, on shutdown) passes the encoded batch to your closure instead of sending it. Put the body on a queue; the worker calls$mira->deliverPrepared($body)on its ownMira, so the key never travels in the message. The body is final: retries resend it byte for byte under its batch id, so a job that runs twice is stored once.send()ignoreshandOffand always sends immediately. - Flush on terminate. Frameworks pass
flushOnShutdown: falseand callflush()after the response. - Local and test environments.
enabled: falsesends nothing and needs no key, but refuses the same input as production.track()keeps nothing,send()anddeliverPrepared()return a local receipt with every event accepted, and flags answer their fallbacks.
MiraFive\Receipt
batch (string), accepted (int), dropped (int), reason (bot, install_check, ingestion_paused, allowance_exhausted or null). dropped > 0 with a reason means nothing was kept; the answer is still final.
MiraFive\MiraError
Extends RuntimeException.
| Property | Meaning |
|---|---|
errorCode |
The protocol code: validation_failed, unauthorized, rate_limited, payload_too_large, sink_unavailable, …, plus network_error, timeout and unexpected |
status |
HTTP status, or null when no answer came. getCode() returns it too (0 without one) |
retryable |
Whether trying again later can succeed |
retryAfterMs |
From Retry-After, when sent |
errors |
For validation_failed: up to 10 ['path' => …, 'message' => …] |
Transports
MiraFive\Http\CurlTransport (default, reuses one connection per request), MiraFive\Http\StreamTransport (no extensions needed) and MiraFive\Http\Psr18Transport:
A PSR-18 client applies its own timeout. Keep it short: delivery runs inside your request.
Flags
for() takes userId (the unit of flags assigned by signed-in person), anonymousId (MIRA FIVE's anonymous id from the browser SDK, the unit of flags assigned by browser) and properties (facts your rules test; they stay in memory and are never sent). Reads are synchronous and never throw: without a document, or for an unknown key, they answer your fallback.
Consent and opt-out (FLAGS.md §5.1). Leave a scope out of consent when your own lawful basis applies; set it to false when the person declined:
| Input | Effect |
|---|---|
consent: ['experiments' => false] |
The anonymous id is not used, so flags assigned by browser answer their default. Every experiment answers its default (NOT_ALLOWED) and nobody is counted |
consent: ['targeting' => false] |
No segment lookup; segment conditions are false (NOT_ALLOWED) |
optedOut: true |
No unit at all, no segment lookup, no exposure, whatever consent says. Fixed values and property rules still apply |
The document. MiraFlags fetches GET {host}/v1/flags on first use and again on a read once it is older than refreshSeconds (default 30, at least 10), revalidating with If-None-Match. PHP-FPM starts every request with an empty process, so pass a PSR-16 cache: it shares the document (and failed fetches), segment memberships, the lookup pause and the hourly exposure marks between requests. Without one, each request fetches the document once and an experiment may be counted once per request.
Segments. When a flag tests segments, for() asks POST {host}/v1/flags/segments about the unit once per minute, with a short timeout. If the lookup fails, lookups pause for 30 s (or as long as Retry-After asks), segment conditions count as false and evaluate() reports MEMBERSHIP_UNAVAILABLE.
Snapshots. $flags->snapshot() returns the document in use as the JSON string MIRA FIVE sent (or null). Store it at build or deploy time and pass it back as document: to start from it while MIRA FIVE is unreachable. A document that cannot be read is reported to onError and ignored.
Experiments. enabled, variant and config send one $exposure per person, experiment and variant for experiments counted on your server. evaluate() never counts anyone. Experiments counted in the browser answer their default on the server (NOT_ALLOWED).
Bootstrap. Hand the server's answers to the browser SDK so the first paint shows the right variant:
Only flags your website reads are included, and every <, >, &, U+2028 and U+2029 is escaped, so no value can end the script. Never let a shared cache store such a page.
Reasons (MiraFive\Flags\Reason): STATIC, TARGETING_MATCH, SPLIT, DEFAULT, DISABLED, ERROR. Error codes (MiraFive\Flags\ErrorCode): UNSUPPORTED (the flag needs a newer SDK), NOT_READY, FLAG_NOT_FOUND, MEMBERSHIP_UNAVAILABLE, NOT_ALLOWED.
Troubleshooting
- Nothing arrives. Pass
onError(or a logger) and look aterrorCode. Without either, failures go toerror_log(). Then check the key and host with an install check (below). unauthorized. The key is missing, wrong or revoked.MIRAFIVE_SECRET_KEYmust hold the secret key of a server source.website_key_as_bearer. You passed the public website key. Server code needs the secret key.InvalidArgumentException: A consentless client may not send userId. The client is inMode::Consentless; drop the identifiers or useMode::Fullwhere you have consent.- Events arrive only at the end of a long job, or never, in a worker. Octane, RoadRunner, Swoole and queue workers run for many requests, so the shutdown flush only fires when the worker stops. Call
$mira->flush()after each request or job (the Laravel and Symfony packages do). - Slow responses when MIRA FIVE is unreachable. A flush is capped at
flushDeadlineMs(3 s), and after a failure flushes skip the network for 30 s. Pass a PSR-16cacheso that pause covers every PHP-FPM process, or lowerflushDeadlineMs. For no delivery work in the request at all, usehandOffwith a queue. - Flags always answer the fallback. Look at
$flags->status()and$user->evaluate($key)->errorCode:NOT_READYmeans no document yet (seeonError),FLAG_NOT_FOUNDthat the flag is not served to this source.
For AI agents
(a) A prompt for an agent adding MIRA FIVE to a plain PHP application:
(b) Facts for agents:
- Package
mirafive/sdk-php, namespaceMiraFive. Classes:MiraFive\Mira,MiraFive\Mode(Full,Consentless),MiraFive\Receipt,MiraFive\MiraError,MiraFive\Flags\MiraFlags,MiraFive\Flags\UserFlags,MiraFive\Flags\Evaluation,MiraFive\Http\Psr18Transport. - Environment:
MIRAFIVE_SECRET_KEY(required),MIRAFIVE_HOST(optional, defaulthttps://events.mirafive.io). - The secret key never goes to a browser. Browsers use the website key with
@mirafive/sdk-browseror the hosted tracker. track()andidentify()buffer; the buffer is flushed on shutdown (once), on destruction, atflushAtevents, or byflush().send()is immediate and throwsMiraError.- Ids are strings: cast integer ids with
(string). Under PHP-FPM pass a PSR-16cache. track()/flush()never throw for transport reasons. Invalid input and identifiers in consentless mode throwInvalidArgumentException.MiraError::$errorCodeholds the protocol code;getCode()is the HTTP status.- Verify a setup with
$mira->send([['name' => '$install_check']]): the receipt'sreasonisinstall_check. - Framework packages:
mirafive/sdk-laravel(facadeMiraFive\Laravel\Facades\Mira, flush on terminating) andmirafive/sdk-symfony(MiraFive\Symfony\MiraFiveBundle, flush onkernel.terminate).
License
MIT, see LICENSE. Copyright (c) 2026 Cloo GmbH.
All versions of sdk-php with dependencies
ext-json Version *