Weave Code
Weaver
Helps Laravel developers discover, compare, and choose open-source packages. See popularity, security, maintainers, and scores at a glance to make better decisions.
Feedback
Share your thoughts, report bugs, or suggest improvements.
Subject
Message

Xhprof Profiler Bundle Laravel Package

zim32/xhprof-profiler-bundle

Symfony bundle that integrates XHProf profiling into the Web Debug Toolbar and Profiler, helping you capture and inspect function call graphs, timings, and performance data to identify bottlenecks in PHP applications.

View on GitHub
Deep Wiki
Context7

Getting Started

This is a Symfony bundle, so map your Laravel instincts before you start: a bundle is a package plus a ServiceProvider, config/packages/*.yaml is config/*.php, and the Symfony Web Profiler toolbar is the analog of Laravel Telescope/Debugbar. The bundle does one thing: it registers an XHProf data collector so a per-request function-level profile shows up as a panel in the debug toolbar and profiler.

Prerequisites, in order:

  1. Install the PHP extension, not just the package. This bundle only presents data; it does not collect it. You need the open-source tideways_xhprof PECL extension:

    pecl install tideways_xhprof
    # then add extension=tideways_xhprof.so to your php.ini
    php -m | grep tideways_xhprof   # must print a line
    

    Do not confuse this with the commercial tideways extension (SaaS APM) — that one is a different extension and will not satisfy this bundle.

  2. Require it as a dev dependency:

    composer require --dev zim32/xhprof-profiler-bundle
    

    With Symfony Flex it should be registered automatically in config/bundles.php. If not, add the bundle class from the package's src/ namespace manually — and only inside the dev/test arrays.

  3. Confirm the config root. Because this package is small and thinly documented, dump the reference before guessing:

    php bin/console config:dump-reference | grep -i xhprof
    

    Then check the bundle's DependencyInjection/Configuration.php in vendor/zim32/ for the exact key, default flags, and enable switch.

Where to look first: the package README (short), then DependencyInjection/Configuration.php for options, then whatever EventSubscriber/DataCollector class exists — that tells you exactly when tideways_xhprof_enable() fires and where results are stored. Don't trust blog posts; this package is too obscure for them to be accurate.

First use case: open a slow page in dev with the toolbar visible, click the XHProf panel, and read the function table — inclusive vs. exclusive wall time, CPU time, and memory per function. You are looking for the function that owns the most exclusive time, not the one at the top of the inclusive list (that's always the kernel).

Implementation Patterns

Dev-only gating is the whole pattern. Never enable this in production: the extension adds meaningful overhead and the panel exposes internal call structure. Keep the bundle registration in config/bundles.php limited to ['dev', 'test' => true] and, if the bundle has an enabled flag, drive it from %kernel.debug% or an env var in config/packages/dev/.

Always-on in dev vs. on-demand. The low-friction mode is to let it profile every request in dev — you get a panel on whatever page is slow without re-running anything. If the overhead bothers you or profiles get noisy, gate collection behind a header or query param in a small kernel-request listener that calls tideways_xhprof_enable(...) yourself and skips the bundle's automatic start.

Use it as a complement, not a replacement. Symfony's built-in panels already give you the request timeline, DB queries, and Twig render costs. XHProf's value is the call tree: which internal function inside your service is burning CPU, whether array_merge in a loop or an accidental O(n²) is the culprit. Read the timeline panel first to find the slow phase, then XHProf to find the function.

Before/after comparison is the workflow. Profile the endpoint, note inclusive CPU/wall time for the suspect function, change the code, re-profile, compare the same row. Treat the numbers as relative, not absolute — they vary run to run. If you need durable storage and side-by-side diffs across many runs, this bundle is the wrong tool; reach for XHGui or a profiler that persists runs.

CLI profiling needs a manual shim. The bundle is built around the HTTP kernel and the profiler, so console commands generally won't get a panel. Wrap the work yourself:

tideways_xhprof_enable(TIDEWAYS_XHPROF_FLAGS_CPU | TIDEWAYS_XHPROF_FLAGS_MEMORY);
// ... run the command logic ...
$data = tideways_xhprof_disable();
file_put_contents('/tmp/profile.json', json_encode($data));

Then feed the JSON into a viewer (or tideways_xhprof's callgraph tooling). This is usually the highest-value use of XHProf for queue workers and import scripts, and it's the part the bundle doesn't cover.

Override the panel if the default view is thin. Symfony lets you override bundle templates by placing a file at templates/bundles/<BundleName>/.... If the shipped panel lacks a column you want (peak memory, call counts), copy the template and extend it. Likewise, you can decorate the bundle's data collector service in config/services.yaml to post-process or filter the raw data before rendering.

Gotchas and Tips

  • Missing extension fails quietly or fatally. If tideways_xhprof_enable is undefined, you get a fatal error in the listener or, worse, an empty panel with no explanation. First debugging step is always php -m | grep tideways.
  • The panel disappears when the profiler stops collecting. If framework.profiler.collect: false (common on staging-ish dev setups) or the web_profiler toolbar is disabled, the collector may never run. Check both before blaming the bundle.
  • Sub-requests and ESI can double-count. If the listener enables profiling on every request event, a sub-request may re-enable the extension mid-request or clobber the parent's data. If numbers look impossible (2x expected time), guard with a static "already started" flag in your own listener.
  • tideways vs tideways_xhprof. Two different PECL extensions. Installing the commercial one does not give you these functions, and having both loaded can produce confusing function tables. Confirm which .so is in php -m.
  • Watch the overhead in tests. If the bundle is registered in the test env, functional tests get slower and memory-heavy profiles they don't need. Either scope it to dev only or disable collection in config/packages/test/.
  • Expect config-key and version drift. With a project this small (few stars, sparse docs), the config key, flag defaults, and Symfony version constraint may not match whatever tutorial you find. Trust config:dump-reference and the composer.json constraints over any external source. If your Symfony major version isn't in the constraint but the code is trivial, composer require --dev --ignore-platform-reqs or a local fork is a reasonable escape hatch — just pin it.
  • Don't expect fixes upstream. No dependents and minimal activity means you are effectively the maintainer. Keep the bundle isolated to dev, pin a version in composer.lock, and if you need real extensions (custom metrics, persistence), fork rather than wait.
  • Raw tideways_xhprof_disable() output is large. On a heavy request it can be tens of MB of arrays. If you're dumping to JSON for later analysis, gzip it and prune to functions above a time threshold; otherwise you'll fill /tmp fast.
Weaver

How can I help you explore Laravel packages today?

Conversation history is not saved when not logged in.
Prompt
Add packages to context
No packages found.
jinas/dhivatar
store-doc/laravel-storage-driver
plus-it/laravel-translation-manager
diephp/perhaps
diephp/telegraph
osmaviation/spreadsheet
microsoft/fluentui-system-icons
mmccook/php-json-canonicalization-scheme
middlewares/firewall
qubiqx/montapacking
nyoncode/laravel-package-console
pnscripts/filament-broadquill-lite
pnscripts/laravel-sentfold-lite
vleap/mx-sdk-laravel-internal
twidpay/utils
htetmyathlaing/scout-elasticsearch-driver
artisan-toolbox/front-awesome
ls/client-assistant
users-au/laravel-client
cgv/localized-resource