PHP Sampling Profiler using the Excimer extension, with automatic per-request collection and a built-in flamegraph dashboard.
- Zero-code instrumentation – add one line to
php.iniand every request is profiled automatically. - Flamegraph dashboard – interactive SVG flamegraph with zoom, tooltips, and frame highlighting.
- Endpoint ranking – requests grouped by URI path, sorted by total sample count.
- Merged view – all requests for the same endpoint are merged into a single flamegraph.
- Export JSON – download the merged folded-stacks profile for offline analysis.
- Automatic cleanup – keeps the newest 10 000 profiles; older files are pruned on each request.
| Requirement | Version |
|---|---|
| PHP | ≥ 8.3 |
| ext-excimer | any |
composer require phpdevsr/php-profilerOr clone / install directly to /opt/php-profiler:
git clone https://github.com/PHPDevsr/php-profiler /opt/php-profilerauto_prepend_file = /opt/php-profiler/profiler.phpOptionally override the data directory:
; defaults to /opt/php-profiler/data
auto_prepend_file = /opt/php-profiler/profiler.phpOr via an environment variable:
PHP_PROFILER_DATA_DIR=/var/lib/php-profiler/data# Serve the dashboard at /profiler/
location /profiler/ {
alias /opt/php-profiler/dashboard/;
# Restrict to localhost only
allow 127.0.0.1;
deny all;
index index.php;
try_files $uri $uri/ /profiler/index.php?$query_string;
location ~ \.php$ {
fastcgi_pass unix:/run/php/php8.3-fpm.sock;
fastcgi_index index.php;
include fastcgi_params;
fastcgi_param SCRIPT_FILENAME $request_filename;
}
}Apache example:
Alias /profiler /opt/php-profiler/dashboard
<Directory /opt/php-profiler/dashboard>
Options -Indexes
AllowOverride None
Require ip 127.0.0.1
DirectoryIndex index.php
</Directory>http://127.0.0.1/profiler/
Make a few HTTP requests to your application, then refresh the dashboard to see endpoint rankings and flamegraphs.
You can also use the Profiler class directly in your code:
use PHPDevsr\Profiler\Profiler;
$profiler = new Profiler(period: 0.01); // 10 ms sampling interval
$profiler->start();
// … code to profile …
$profiler->stop();
// Raw folded-stacks string (Excimer format, compatible with flamegraph tools)
$folded = $profiler->getFoldedStacks();
// Parsed log: array of ['stack' => string[], 'count' => int]
$log = $profiler->getLog();use PHPDevsr\Profiler\Storage\FileStorage;
$storage = new FileStorage('/var/lib/php-profiler/data');
$storage->save([
'id' => uniqid('', true),
'timestamp' => microtime(true),
'endpoint' => '/api/users',
'method' => 'GET',
'duration_ms' => 45.2,
'sample_count' => 12,
'folded_stacks' => $profiler->getFoldedStacks(),
]);
// Endpoint statistics (sorted by total samples, descending)
$stats = $storage->getEndpointStats();
// Profiles for one endpoint
$profiles = $storage->findByEndpoint('/api/users');
// Delete oldest files when total exceeds $maxFiles
$storage->cleanup(maxFiles: 10_000);# Run tests
composer test
# Static analysis
composer phpstan
# Rector dry-run
composer rectorMIT — see LICENSE.