Skip to content

PHP SDK (Laravel + Symfony)

The Pionne SDK for PHP. Three usage paths: standalone, Laravel (auto-discovered service provider), Symfony (event subscriber).

Terminal window
composer require pionne/pionne
  • PHP_VERSION — PHP version
  • php_uname() — OS, hostname
  • Request URL, HTTP method, route name
  • User ID if authenticated (Laravel Auth::user(), Symfony Security::getUser())
  • DB queries (Laravel: via DB::listen(), Symfony: via Doctrine middleware)
  • Outbound HTTP requests (automatic Guzzle middleware)
  • Log::info() / error_log()

The service provider is auto-discovered. Configure via env vars:

.env
PIONNE_TOKEN=pio_live_…
PIONNE_ENVIRONMENT=production
PIONNE_RELEASE=1.0.0

And 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.

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 ...']);
  • 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;
}

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()):

Terminal window
PIONNE_GEOGRAPHY=true

At 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.

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.