webspark / profiling-php
Lightweight profiling package for PHP projects.
Requires
- php: ^7.4|^8.0|^8.1|^8.2|^8.3
- ext-json: *
Requires (Dev)
- pestphp/pest: 2.34.7
- pestphp/pest-plugin-type-coverage: 2.8.1
README
Navigation
Introduction
This package provides a lightweight PHP profiler for speed and timings profiling. It can be used to measure the time of your code execution and to see how much time it takes on your code parts. It can be useful to find bottlenecks in your code and to optimize it.
The package provides two types of profiling:
- Speed profiling - to measure the time of your code execution.
- Timings profiling - to measure the time of your code parts.
The package is easy to use and has a simple configuration. It can be used in any PHP project.
How it works
How it works Speed profiling.
Profiling with SpeedProfiler
is based on using a destructor as the end point of the action. Profiling of the code execution speed starts during the initialization of a new instance of SpeedProfiler
and ends when the instance is cleared from RAM. In PHP, the destructor is called automatically during memory clearing, and memory clearing occurs when the process exits a method and the variables in the method are no longer needed to process the code.
How it works Timings profiling.
Timing profiling is based on the use of the Server-Timing
header, which is recognized by the browser and can be displayed in a readable form. This allows real-time profiling directly in the browser.
Installation
You can install the package via composer:
composer require webspark/profiling-php
Notice: Profiler can't be used for the multithreading process because of provider singleton realization.
Configuration
1. Processors.
By default, speed profiler will use InMemoryProfilingProcessor
to collect profiling logs. This will give you profiling statistics on the current page load.
To collect historical logs of all actions you need to create your own processor or use one proposed by us. You can use DailyLogProfilingProcessor
for storing profiling logs in a daily logger file. The daily log processor has implemented log file rotation, the default value is 7 days.
Note: processors are used only for speed profiling.
To set custom processor you need to set it in provider:
SpeedProfilingProvider::getInstance()
->setProcessor(new DailyLogProfilingProcessor(<set log file name>, <set log file folder>, <set rotation days>));
2. Profiling Config.
By default, for profiling we use actions, that take time (latency in milliseconds) more than 1 second (1000 milliseconds). You can override profiling configuration by setting up the custom config object:
SpeedProfilingProvider::getInstance()
->setConfig(new ProfilingConfig(['latency' => '2000'])); // to set latency in milliseconds
TimingsProfilingProvider::getInstance()
->setConfig(new ProfilingConfig(['latency' => '2000'])); // to set latency in milliseconds
Usage
Speed profiling.
Speed profiling is a useful tool to see how much time it takes on your code execution. You can store measurements in log files and check an execution statistic.
1. Speed profiling global actions.
To add global action profiling you should add new SpeedProfiler()
to upper entry point of your application. It will start speed profiling for every request to your project. For example in index.php:
require_once __DIR__ . '/vendor/autoload.php';
$profiler = new SpeedProfiler('Index action');
... // project logic
exit;
2. Speed profiling of specific places.
To record specific method you need to create an instance of SpeedProfiler
class at the start of this method. It will record the time spent on the method when process will exit out from the method. You can use it in any place of your code.
class Example
{
public function execute(): void
{
$profiler = new SpeedProfiler('Example execute action.');
... // some logic
}
}
3. Speed profiling checkoint
method.
In process of recording the time spent, you can record parts of the method with checkoint
method from the SpeedProfiler
class. This method will record partial time spent from the SpeedProfiler
initialisation point.
class Example
{
public function execute(): bool
{
$profiler = new SpeedProfiler('Example execute action.');
... // some logic
$profiler->checkpoint('First part of logic');
... // some logic
$profiler->checkpoint('Second part of logic');
... // some logic
return true; // method profiling finished
}
}
4. Speed profiler statistic.
Profiling statistic have fields: max
- the largest time of all logged requests, avg
- the average time of all logged action requests, med
- the median time of all logged action requests without 10% from the max execution and 10% from the min execution, min
- the smaller time of all logged action requests, total
- total time spent on action by the period, call
- count of logged action requests and action
- the name of action.
To view profiler statistic you can use SpeedProfilerStatisticService
to generate statistic based on profiling logs. You can specify date (by default the all logged dates are in format Y-m-d
), limit rows (by default is 10) and sorting (by default sorting by max time).
Sorting can be: max (from larger max time to smaller), min (from larger min time to smaller), avg (from larger avg time to smaller), med (from larger med time to smaller), total (from larger total time to smaller) and calls (from larger count of calls to smaller).
$filters = new ProfilerStatisticFilters(
20, // by default is 10
date('Y-m-d'), //by default the all logged dates in format `Y-m-d`
new StatisticSortingType(StatisticSortingType::CALLS), //by default StatisticSortingType::MAX
);
$statisticList = (new SpeedProfilerStatisticService())->generate($filters);
Timings profiling.
Timings profiling is a useful tool to see how much time it takes on your code parts in real time. You can see measurement results in browser in network in section Timing.
To enable timings in browser you should add headers with server timings to your response. You can do it with steps:
1. Start timings collecting.
You need to start automatic timings collecting after your application has bootstrapped. It will stop Bootstrap timestamp and start App timestamp. For example in index.php:
require_once __DIR__ . '/vendor/autoload.php';
TimingsProfilingProvider::getInstance()->init();
... // project logic
By default, you will have timings Bootstrap
(framework initialization) and App
(time from route action was detected to exit from application). We will use request started time from global object $_SERVER['REQUEST_TIME_FLOAT']
you can override it. For example:
TimingsProfilingProvider::getInstance()->init($requestStarted);
2. Set timings header to response before your application exit.
To view measured timings in browser, you need to send a header with measurements from provider. You can do it before your application exit. For example in index.php:
header(TimingsProfilingProvider::getInstance()->header());
3. Additional timing measurements.
To view an additional measurements in browser you need to record them. We have a few ways how to do it.
- Using the
TimingsProfilingProvider
class. For example:
public function execute(): void
{
TimingsProfilingProvider::getInstance()->start('Method execute');
... // some logic
TimingsProfilingProvider::getInstance()->stop('Method execute');
}
You also can just start timing. It will stop when you ask headers from provider and will include all time from starting point. For example:
public function execute(): void
{
TimingsProfilingProvider::getInstance()->start('Execute');
... // some logic
}
- Using methods
setDuration
fromTimingsProfilingProvider
. This method will record measurement with given duration. If timing measurement with this name already exists, it will increment existed duration. The better place to use it is to measure repetitive actions, like time, that spent on database queries. It will increase previously finished measurements. For example:
$startPoint = microtime(true);
... // some logic
TimingsProfilingProvider::getInstance()
->setDuration('Measurement', (microtime(true) - $startPoint) * 1000);
You can also provide callback instead of duration value, to measure the execution time of the callback. For example:
TimingsProfilingProvider::getInstance()
->setDuration('Timing name', static function () {
... // some logic here
});
- Using
TimingsProfiler
class to add your timings - it will stop measurement on profiler destructor, when script leaves a method. For example:
class Example
{
public function execute(): void
{
$profiler = new TimingsProfiler('Method execute');
... // some logic
}
}
All timings you will also have in speed profiler logs - it is helpful thing to understand how much time was taken on code parts for long term action.
Testing
Execute tests:
composer test
Execute tests with coverage:
composer test-coverage
Changelog
Please see CHANGELOG for more information on what has changed recently.
License
The MIT License (MIT). Please see License File for more information.