bvanhoekelen/performance
A PHP/Laravel performance measurement package that profiles execution time, memory usage, and database queries, then presents a clear report to help you spot bottlenecks and optimize your application.
Install
composer require bvanhoekelen/performance
Laravel 5.5+ picks it up via package auto-discovery (PerformanceServiceProvider + Performance facade alias). On older Laravel or when auto-discovery is disabled, register manually in config/app.php:
'providers' => [
Bvanhoekelen\Performance\PerformanceServiceProvider::class,
],
'aliases' => [
'Performance' => Bvanhoekelen\Performance\Facades\Performance::class,
],
For plain Composer projects, just require 'vendor/autoload.php' and use the class directly.
Where to look first
start() / stop() / output() surface — the API is small, so five minutes there is enough.vendor/bvanhoekelen/performance/src — the facade and the Performance class are the whole story; there is no large config surface to learn.First use case
Wrap a slow controller action or Artisan command and see time, peak memory, and query count in one table:
use Bvanhoekelen\Performance\Facades\Performance;
Performance::start();
// ... the code you suspect is slow
Performance::stop()->output();
In a browser you get an HTML table; on the CLI (Artisan, queue worker) you get a text table. That single call is usually enough to answer "is this slow because of queries, memory, or just CPU?"
Block measurement around arbitrary code
The core pattern is start/stop around any region — a controller method, a service call, an Artisan command body, a loop iteration:
Performance::start();
$report = $this->reportBuilder->build($params);
Performance::stop()->output();
Named measurements for multi-segment requests
Pass a label to both calls to time several phases independently, then render them together:
Performance::start('validate');
$request->validate($rules);
Performance::stop('validate');
Performance::start('query');
$orders = Order::with('items')->where(...)->get();
Performance::stop('query');
Performance::start('render');
return view('orders.index', compact('orders'));
Performance::stop('render')->output();
This is the pattern worth standardizing on: it tells you immediately whether a regression is in validation, the ORM, or the view layer.
Closure form
For one-off probes, wrap the work instead of scattering start/stop calls:
Performance::measure(function () use ($payload) {
$this->importer->run($payload);
})->output();
Local-only middleware
The most useful day-to-day integration is a middleware that is registered only in local/staging. Start in handle(), stop and emit after the response is built (in terminate() or after the response object is returned), and gate it behind an env flag:
if (app()->environment('local') && config('app.performance_overlay')) {
Performance::start('request');
}
Console and queues
In Artisan commands and jobs, output() goes to stdout/log-friendly text, which makes it a cheap ad-hoc profiler for nightly imports and queue workloads. For queue workers, prefer emitting after the job rather than mid-job so timings reflect the whole unit of work.
Enable query logging when you need query detail
Query counts come from Laravel's query log, so confirm it is enabled for the code path you are measuring (DB::enableQueryLog() on the connection, or the equivalent config toggle). Without it, query numbers will read as zero and you will chase the wrong bottleneck.
Keep it out of production paths
Wire it behind APP_DEBUG / environment checks, or a config flag that defaults to false. Treat it as a development probe, not instrumentation.
output() echoes markup. In a JSON endpoint or behind a return response()->json(...) this corrupts the payload. Capture or restrict to local HTML responses, or route the table into a log entry instead.memory_get_peak_usage() only ever rises, so a "start vs stop" memory delta reflects the high-water mark, not what is live at stop time. Do not interpret a drop in memory as freed memory; compare separate requests instead.dont-discover, produce a "class not found" facade error. That is a registration problem, not a package bug.perf_probe('label', $callback)) that adds the environment gate, logging destination, and label conventions your team uses — keep that wrapper as the only import in application code so you can swap the backend later without touching call sites.How can I help you explore Laravel packages today?