PHP SDK (Laravel + Symfony)
The Pionne SDK for PHP. Three usage paths: standalone, Laravel (auto-discovered service provider), Symfony (event subscriber).
Install
Section titled “Install”composer require pionne/pionneAuto-context
Section titled “Auto-context”PHP_VERSION— PHP versionphp_uname()— OS, hostname- Request URL, HTTP method, route name
- User ID if authenticated (Laravel
Auth::user(), SymfonySecurity::getUser())
Auto-breadcrumbs
Section titled “Auto-breadcrumbs”- DB queries (Laravel: via
DB::listen(), Symfony: via Doctrine middleware) - Outbound HTTP requests (automatic Guzzle middleware)
Log::info()/error_log()
Frameworks
Section titled “Frameworks”The service provider is auto-discovered. Configure via env vars:
PIONNE_TOKEN=pio_live_…PIONNE_ENVIRONMENT=productionPIONNE_RELEASE=1.0.0And in config/services.php:
'pionne' => [ 'token' => env('PIONNE_TOKEN'), 'environment' => env('PIONNE_ENVIRONMENT', 'production'), 'release' => env('PIONNE_RELEASE'), 'sample_rate' => 1.0, 'scrub_pii' => true,],For Laravel 11+, hook into bootstrap/app.php:
use Pionne\Laravel\PionneHandler;
return Application::configure(basePath: dirname(__DIR__)) ->withExceptions(function (Exceptions $exceptions) { $exceptions->report(function (Throwable $e) { PionneHandler::report($e); }); })->create();For Laravel 10 and earlier, in App\Exceptions\Handler:
use Pionne\Pionne;
public function report(Throwable $e): void{ Pionne::captureException($e); parent::report($e);}Queue jobs are auto-instrumented — any unhandled exception in a Job is captured.
Enable the bundle in config/bundles.php:
return [ // ... Pionne\Symfony\PionneBundle::class => ['all' => true],];Configure in config/packages/pionne.yaml:
pionne: token: '%env(PIONNE_TOKEN)%' environment: '%env(PIONNE_ENVIRONMENT)%' release: '%env(PIONNE_RELEASE)%' sample_rate: 1.0 scrub_pii: trueAnd in .env:
PIONNE_TOKEN=pio_live_…PIONNE_ENVIRONMENT=productionPIONNE_RELEASE=1.0.0The bundle automatically registers an EventSubscriber on KernelEvents::EXCEPTION. Symfony Messenger messages are also auto-instrumented.
<?phprequire_once __DIR__ . '/vendor/autoload.php';
use Pionne\Pionne;
Pionne::init([ 'token' => 'pio_live_…', 'environment' => 'production', 'release' => '1.0.0',]);
try { doSomething();} catch (\Throwable $e) { Pionne::captureException($e);}Pionne::init(['token' => '…', 'environment' => '…']);Pionne::captureException($e, ['tags' => ['feature' => 'checkout']]);Pionne::captureMessage('Cache miss', ['level' => 'warning']);Pionne::setUser(['id' => 'user_42']);Pionne::setTags(['region' => 'eu-west-1']);Pionne::setEnabled(false);Pionne::addBreadcrumb(['category' => 'db', 'message' => 'SELECT ...']);Queue jobs
Section titled “Queue jobs”- Laravel queue: auto-instrumented (events
JobFailed) - Symfony Messenger: auto-instrumented (subscriber on
WorkerMessageFailedEvent) - Custom worker: wrap manually
try { $job->handle();} catch (\Throwable $e) { Pionne::captureException($e, [ 'tags' => ['job' => get_class($job)], 'contexts' => ['job' => ['attempts' => $job->attempts()]], ]); throw $e;}Geography (opt-in)
Section titled “Geography (opt-in)”Pionne can resolve the server’s approximate city/region/country and attach it to every event under contexts.geo. Off by default for privacy.
Pionne::init([ 'token' => 'pio_live_…', 'sendGeography' => true, // ← opt-in]);For Laravel, a single .env flag is enough (the service provider passes it to init()):
PIONNE_GEOGRAPHY=trueAt boot, a single cURL call to https://ipapi.co/json/ (4 s timeout) attaches contexts.geo = { city, region, country, country_code } to every subsequent event. If the lookup fails (egress firewall, rate-limit), the SDK silently keeps shipping events without geo. For a custom provider: 'geographyEndpoint' => 'https://geo.yourapi.com/'.
On an auto-scaled cluster, prefer setting the container region as a tag ('tags' => ['region' => getenv('AWS_REGION')]) rather than an IP lookup.
Anti-token-theft
Section titled “Anti-token-theft”Bundle ID pinning is mobile only (iOS/Android/RN/Flutter) — it protects against APK/IPA decompilation. On Laravel/Symfony, your token lives in .env (gitignored) or in a secrets manager: it’s never shipped to a client, so the threat doesn’t exist.
The “Bundle ID” field is hidden in the mobile dashboard for Laravel/Symfony projects — filling it manually would 403 every event (the SDK does not send app_id). To differentiate deployments, use tags:
Pionne::init([ 'token' => env('PIONNE_TOKEN'), 'tags' => [ 'deployment' => env('APP_DEPLOYMENT', 'prod'), 'region' => env('AWS_REGION', 'eu-west-3'), ],]);More details: Bundle ID Pinning → Backends without bundle_id.
See also
Section titled “See also”- SDK index — every Pionne SDK
- Ingest API — raw HTTP protocol