aeatech/web-snapshot-profiler-xhprof-bundle
Symfony bundle for production-friendly web profiling with XHProf and XHGui. Capture snapshot profiles per request, control sampling via route/header/request param matchers, and configure flags for memory/internal function data. Requires PHP 8.2+ and ext-xhprof.
Install Dependencies
pecl install xhprof
docker run -d -p 8080:80 --name xhgui xhgui/xhgui:0.22.1
Ensure extension=xhprof.so is in your php.ini.
Install the Bundle
composer require aeatech/web-snapshot-profiler-xhprof-bundle
Enable in config/bundles.php:
AEATech\WebSnapshotProfilerXhprofBundle\AEATechWebSnapshotProfilerXhprofBundle::class => ['dev' => true, 'prod' => true],
Configure
Set is_profiling_enabled: true in config/packages/aea_tech_web_snapshot_profiler_xhprof.yaml and define AEA_TECH_WEB_SNAPSHOT_PROFILER_XHPROF_XHGUI in .env:
AEA_TECH_WEB_SNAPSHOT_PROFILER_XHPROF_XHGUI=http://localhost:8080
First Use Case Trigger a profile by visiting a route or manually in code:
$profiler = $this->container->get('aea_tech_web_snapshot_profiler_xhprof.profiler');
$profiler->start('my_custom_snapshot_name');
// ... code to profile ...
$profiler->stop();
Conditional Profiling Enable profiling only for specific routes or environments:
# config/packages/aea_tech_web_snapshot_profiler_xhprof.yaml
aea_tech_web_snapshot_profiler_xhprof:
is_profiling_enabled: '%kernel.debug%' # Only in dev
Automatic Profiling for Critical Paths Use middleware to profile high-traffic routes:
// src/Kernel.php
protected function build(Request $request): Response
{
if ($request->isMethod('GET') && $request->getPathInfo() === '/api/heavy-endpoint') {
$profiler = $this->getContainer()->get('aea_tech_web_snapshot_profiler_xhprof.profiler');
$profiler->start('heavy_endpoint');
}
// ...
}
Custom Snapshot Naming Override snapshot naming logic in a service:
// src/Service/ProfilerService.php
public function start(string $name): void
{
$profiler = $this->container->get('aea_tech_web_snapshot_profiler_xhprof.profiler');
$profiler->start(sprintf('user_%s_%s', auth()->id(), $name));
}
Integration with Symfony Profiler Correlate XHProf snapshots with Symfony’s profiler toolbar:
// src/EventListener/ProfilerListener.php
public function onKernelRequest(GetResponseEvent $event)
{
if ($event->isMasterRequest() && $this->profiler->isStarted()) {
$token = $event->getRequest()->query->get('_profiler');
$this->profiler->setMetadata(['_profiler_token' => $token]);
}
}
DBAL calls:
$profiler->start('query_execution');
$result = $connection->executeQuery('SELECT ...');
$profiler->stop();
Guzzle or Symfony HttpClient:
$profiler->start('http_request');
$client->request('GET', 'https://api.example.com');
$profiler->stop();
// src/Command/ProcessDataCommand.php
protected function execute(InputInterface $input, OutputInterface $output)
{
$profiler = $this->getContainer()->get('aea_tech_web_snapshot_profiler_xhprof.profiler');
$profiler->start('cron_process_data');
// ... task logic ...
$profiler->stop();
}
XHProf Extension Conflicts
xhprof is not loaded in php.ini for CLI (use php -n for web requests only).xdebug: Disable xdebug when profiling with XHProf to avoid overhead:
XDEBUG_MODE=off
Snapshot Overwrite Risks
app_version + timestamp) may overwrite snapshots. Customize naming to avoid collisions:
aea_tech_web_snapshot_profiler_xhprof:
app_version: 'v1.0.0'
Or use a UUID:
$profiler->start('snapshot_' . Str::uuid()->toString());
XHGUI Connection Issues
import_uri in config matches XHGUI’s base URL.xhprof_runs_dir is writable by the web server.Performance Overhead
aea_tech_web_snapshot_profiler_xhprof:
is_profiling_enabled: '%kernel.debug%'
Missing Snapshots
Check XHGUI logs (/var/log/xhgui.log) and PHP errors (storage/logs/). Ensure the profiler is started before the code to profile:
// WRONG: Profiler started after DB call
$connection->executeQuery('SELECT * FROM users');
$profiler->start('query');
// CORRECT: Profiler started before
$profiler->start('query');
$connection->executeQuery('SELECT * FROM users');
Corrupted Snapshots If snapshots are empty or malformed:
xhprof.rm -rf xhgui/xhprof_runs/*).Custom Profiler Service Extend the profiler to add metadata:
// src/Service/CustomProfiler.php
class CustomProfiler extends \AEATech\WebSnapshotProfilerXhprofBundle\Profiler
{
public function start(string $name, array $metadata = []): void
{
$this->metadata = array_merge($this->metadata, $metadata);
parent::start($name);
}
}
Register as a service in config/services.yaml:
services:
AEATech\WebSnapshotProfilerXhprofBundle\Profiler:
alias: App\Service\CustomProfiler
Post-Processing Snapshots
Hook into the post_stop event to process snapshots:
// src/EventListener/SnapshotListener.php
public function onPostStop(PostStopEvent $event)
{
$snapshot = $event->getSnapshot();
if ($snapshot->getDuration() > 1.0) { // >1s
$this->notifyTeam($snapshot);
}
}
Bind the listener in services.yaml:
services:
App\EventListener\SnapshotListener:
tags:
- { name: kernel.event_listener, event: aea_tech_web_snapshot_profiler_xhprof.post_stop }
Dynamic Profiling Triggers
Use Symfony’s EventDispatcher to start/stop profiling based on events:
// src/EventListener/RouteListener.php
public function onKernelRequest(GetResponseEvent $event)
{
if ($event->getRequest()->attributes->get('_route') === 'slow_route') {
$this->profiler->start('slow_route');
}
}
How can I help you explore Laravel packages today?