Usage
nebula()->timer($unique_id, $action, $category)
Parameters
$unique_id
(Required) (String) A unique ID for this specific timed task
Default: None
$action
(Optional) (String) A start/mark/stop action
Default: "start"
$category
(Optional) (String) Group timings together to be added together
Default: false
Parameter Notes
Actions currently include “start”, “mark” (with an alias of “once”), and “end” (with an alias of “stop”).
Although the $unique_id is required, if the ID is not unique a random number will be appended to it that begins with _d
On start actions, the used unique ID is returned so that the timer can be stopped later.
Examples
Time a single task
nebula()->timer('Example Task');
//...do something here
nebula()->timer('Example Task', 'end');
Time a task that may have multiple ends
$task_name = nebula()->timer('This can be a really long task name without needing to write it more than once');
//...do something here
nebula()->timer($task_name, 'end');
Categorize multiple similar tasks
foreach ( $big_list as $item ){
nebula()->timer('Big List Task (' . $item . ')', 'start', 'Big List');
//...do something here
nebula()->timer('Big List Task (' . $item . ')', 'end');
}
Combine timings from a single category
if ( !empty($this->server_timings['categories']) && !empty($this->server_timings['categories']['Big List']) ){
foreach ( $this->server_timings['categories']['Big List'] as $category => $times ){
$big_list_total = array_sum($times);
}
}
Mark a point in time with a single entry that immediately starts and stops.
nebula()->timer('Certain Point in Time', 'mark');
Additional Notes
Timings are stored in nebula()->server_timings and categories are separated into a nebula()->server_timings['categories'] array.
Timings will automatically appear for developers, but the query string ?timings must be used for non-developers (or logged-out users).
Server Timing API
Timings are sent via the server-timing HTTP header and can be viewed in Chrome DevTools under Network by clicking the page resource and choosing the Timing tab. It is important to note that these times are finalized before headers are sent.
Query Monitor Timings
If you have the Query Monitor plugin installed, Nebula timings are also sent to the Query Monitor Timings section:

WebPageTest.org Timings
This timing header is also available in other speed testing tools (like WebPageTest.org) that provide access to response headers.
JavaScript Console Table
Timings are also available in the JavaScript console within the Performance > Measurements group. Expand it to see all server timings as well as client-side timings as well in a sortable table. Timings are finalized again at the last possible moment to make sure they are complete.

Timings are available in the WordPress admin area too.
Source File
Located in /libs/Utilities/Utilities.php on line 1621.
3 Hooks
Find these filters and actions in the source code below to hook into them. Use do_action() and add_filter() in your functions file or plugin.
Filters
This function has no filter hooks available. Request one?
Actions
"qm/start""qm/stop"
"qm/stop"
Need a new action hook? Request one here.
public function timer($unique_id, $action='start', $category=false){
if ( $this->is_minimal_mode() ){return null;}
if ( $this->is_background_request() ){return null;}
//Ignore all timers for non-Developers
if ( !$this->is_dev() ){
if ( $this->is_plugin_active('query-monitor/query-monitor.php') && !current_user_can('view_query_monitor') ){
return null;
}
}
//If the timer array is getting too large, no more new timings
if ( is_array($this->server_timings) && count($this->server_timings) > 1000 ){
$this->server_timings = false; //Disable future timings
return null;
}
//Unique ID is required
if ( empty($unique_id) || in_array(strtolower($unique_id), array('start', 'stop', 'end')) ){
return null;
}
if ( $action === 'start' || $action === 'mark' || $action === 'once' ){
//Prevent duplicates by appending a random number to the ID (only when duplicate)
if ( !empty($this->server_timings[$unique_id]) ){
$unique_id .= '_d' . random_int(100000, 999999);
}
do_action('qm/start', $unique_id); //Inform Query Monitor as well
$this->server_timings[$unique_id] = array(
'start' => microtime(true),
'active' => true,
'category' => $category
);
//Add to array of this category (if categorization is used)
if ( !empty($category) ){
$this->server_timings['categories'][$category][] = 0; //Start with an empty time in this category to create it
}
//Immediately stop one-off timing marks
if ( $action === 'mark' || $action === 'once' ){
//Start and stop the Query Monitor timing so it appears without error
do_action('qm/stop', $unique_id); //Immediately end the Query Monitor timer so it appears without error
$this->server_timings[$unique_id]['end'] = $this->server_timings[$unique_id]['start']+0.001;
$this->server_timings[$unique_id]['time'] = 0.001; //Force non-empty time of 1 millisecond
$this->server_timings[$unique_id]['active'] = false;
}
return $unique_id; //Return the unique ID in case it was changed so that the 'end' call can know what to use
} elseif ( in_array(strtolower($action), array('stop', 'end')) ){
do_action('qm/stop', $unique_id); //Inform Query Monitor as well
if ( !empty($this->server_timings[$unique_id]['start']) ){ //Make sure this timer has started
$this->server_timings[$unique_id]['end'] = microtime(true);
$this->server_timings[$unique_id]['time'] = $this->server_timings[$unique_id]['end'] - $this->server_timings[$unique_id]['start'];
$this->server_timings[$unique_id]['active'] = false;
//Add to array of this category (if categorization is used)
$this_category = $this->server_timings[$unique_id]['category'];
//Add this individual time to the category if it exists
if ( !empty($this_category) ){
$this->server_timings['categories'][$this_category][] = $this->server_timings[$unique_id]['time'];
}
return true;
}
}
return null;
}
Override
This function can not be short-circuited with an override filter. Request one?

