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/dataauto_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 onlyallow127.0.0.1;deny all;indexindex.php;try_files$uri$uri/ /profiler/index.php?$query_string;location~\.php$ {fastcgi_pass unix:/run/php/php8.3-fpm.sock;fastcgi_indexindex.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.1DirectoryIndex 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:
usePHPDevsr\Profiler\Profiler;
$profiler = newProfiler(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();usePHPDevsr\Profiler\Storage\FileStorage;
$storage = newFileStorage('/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.