laracraft-tech/laravel-xhprof
Laravel package to integrate XHProf profiling into your app. Capture and store performance profiles for requests and jobs, view results via a simple UI, and analyze bottlenecks to optimize code and database queries.
Install the Package
composer require laracraft-tech/laravel-xhprof
php artisan vendor:publish --provider="LaracraftTech\Xhprof\XhprofServiceProvider" --tag="migrations"
php artisan migrate
Enable XHProf Extension
Add to your php.ini (CLI and web):
extension=xhprof.so
XHPROF_SAMPLE_RATE=10 # Adjust for balance between overhead and detail
Profile a Request
Append ?profile=1 to any route (e.g., http://your-app.test/api/orders?profile=1).
View results at /xhprof (default route).
Profile a CLI Command
use LaracraftTech\Xhprof\Facades\Xhprof;
Xhprof::start('cli-job');
// Your logic here
$profile = Xhprof::stop();
$profile->save(); // Stores in DB or file
/api/orders takes 800ms).?profile=1 and navigate to /xhprof.serialize(), Eloquent::hydrate()).collect() on large datasets).app/Http/Kernel.php:
protected $middlewareGroups = [
'web' => [
// ...
\LaracraftTech\Xhprof\Http\Middleware\Profile::class,
],
];
Route::middleware(['profile'])->group(function () {
Route::get('/api/orders', [OrderController::class, 'index']);
});
Configure in .env:
XHPROF_ENABLED=true
XHPROF_SKIP_URLS=*/health,*/admin/*
Or dynamically in code:
if (app()->environment('staging')) {
Xhprof::start('staging-request');
}
Wrap Artisan commands or queue jobs:
Xhprof::start('process-invoices');
Invoice::processAll();
$profile = Xhprof::stop();
$profile->save(); // Store for later analysis
Add to phpunit.xml:
<php>
<server name="XHPROF_ENABLED" value="true"/>
</php>
Test in FeatureTest:
public function test_api_performance()
{
$response = $this->get('/api/orders');
$this->assertLessThan(300, Xhprof::stop()->getWallTime(), "API too slow!");
}
feature/subscriptions").xhprof_runs table for structured queries (e.g., filter by URL, user agent).$slowRuns = \LaracraftTech\Xhprof\Models\Run::where('wall_time', '>', 500)->latest()->limit(10)->get();
storage/app/xhprof/ for large volumes..env:
XHPROF_STORAGE=file
XHPROF_FILE_PATH=storage/app/xhprof
Extend the Storage contract:
use LaracraftTech\Xhprof\Contracts\Storage;
class S3Storage implements Storage {
public function save(Run $run, $data) { /* ... */ }
public function list() { /* ... */ }
}
Bind in a service provider:
$this->app->bind(Storage::class, function () {
return new S3Storage();
});
/xhprof route for flame graphs.$profile = \LaracraftTech\Xhprof\Models\Run::find(1);
$data = $profile->data; // Decoded XHProf JSON
XHPROF_SAMPLE_RATE=5 # 5% sampling
XHPROF_ENABLED=false
XHProf not enabled.php -m | grep xhprof (CLI and web).xhprof is in docker-php-ext-install:
RUN docker-php-ext-install xhprof
xhprof_runs table grows uncontrollably.php artisan xhprof:prune --days=7
Schema::table('xhprof_runs', function (Blueprint $table) {
$table->timestamp('created_at')->useCurrent();
$table->index(['created_at']);
});
Profile middleware first in the stack:
$middleware = [
\LaracraftTech\Xhprof\Http\Middleware\Profile::class,
\App\Http\Middleware\TrustProxies::class,
// ...
];
v1.0.10+) or manually alter the schema:
ALTER TABLE xhprof_runs MODIFY data LONGTEXT;
XHPROF_SAMPLE_RATE (e.g., 20 for 20% sampling).XHPROF_ENABLED=false
XHPROF_SKIP_URLS is ignored.XHPROF_SKIP_URLS=*/health,*/admin/*
php artisan config:get xhprof.skip_urls.if (!Xhprof::isEnabled()) {
throw new \RuntimeException("XHProf is not enabled. Check extension and config.");
}
Inspect the stored profile data:
$run = \LaracraftTech\Xhprof\Models\Run::latest()->first();
$data = json_decode($run->data, true);
print_r($data['main()']['wt']); // Wall time
Use XHProf’s built-in markers:
Xhprof::start('custom-marker');
// Code to profile
Xhprof::stop('custom-marker');
Use the xhprof_diff tool (included with XHProf) to compare runs:
xhprof_diff run1.xhprof run2.xhprof
How can I help you explore Laravel packages today?