Elgg  Version 4.3
Profiler.php
Go to the documentation of this file.
1 <?php
2 
3 namespace Elgg\Debug;
4 
6 use Elgg\Timer;
7 
13 class Profiler {
14 
15  protected $percentage_format = "%01.2f";
16  protected $duration_format = "%01.6f";
17  protected $minimum_percentage = 0.2;
18 
22  protected $total;
23 
31  public function __invoke(\Elgg\Hook $hook) {
32 
33  if (!_elgg_services()->config->enable_profiling) {
34  return;
35  }
36 
37  $profiler = new self();
38  $min_percentage = _elgg_services()->config->profiling_minimum_percentage;
39  if ($min_percentage !== null) {
40  $profiler->minimum_percentage = $min_percentage;
41  }
42 
43  $tree = $profiler->buildTree(_elgg_services()->timer);
44  $tree = $profiler->formatTree($tree);
45  $data = [
46  'tree' => $tree,
47  'total' => $tree['duration'] . " seconds",
48  ];
49 
50  $list = [];
51  $profiler->flattenTree($list, $tree);
52 
53  $root = Paths::project();
54  $list = array_map(function ($period) use ($root) {
55  $period['name'] = str_replace("Closure $root", "Closure ", $period['name']);
56  return "{$period['percentage']}% ({$period['duration']}) {$period['name']}";
57  }, $list);
58 
59  $data['list'] = $list;
60 
61  $html = $hook->getValue();
62  $html .= "<script>console.log(" . json_encode($data) . ");</script>";
63 
64  return $html;
65  }
66 
73  protected function buildTree(Timer $timer) {
74  $times = $timer->getTimes();
75 
76  if (!isset($times[Timer::MARKER_END])) {
77  $times[Timer::MARKER_END] = microtime(true);
78  }
79 
80  $begin = $this->findBeginTime($times);
81  $end = $this->findEndTime($times);
82  $this->total = $this->diffMicrotime($begin, $end);
83 
84  return $this->analyzePeriod('', $times);
85  }
86 
95  protected function flattenTree(array &$list = [], array $tree = [], $prefix = '') {
96  $is_root = empty($list);
97 
98  if (isset($tree['periods'])) {
99  foreach ($tree['periods'] as $period) {
100  $this->flattenTree($list, $period, "{$prefix} {$period['name']}");
101  }
102  unset($tree['periods']);
103  }
104  $tree['name'] = trim($prefix);
105  $list[] = $tree;
106 
107  if ($is_root) {
108  usort($list, function ($a, $b) {
109  if ($a['duration'] == $b['duration']) {
110  return 0;
111  }
112  return ($a['duration'] > $b['duration']) ? -1 : 1;
113  });
114  }
115  }
116 
123  protected function formatTree(array $tree) {
124  $tree['duration'] = sprintf($this->duration_format, $tree['duration']);
125  if (isset($tree['percentage'])) {
126  $tree['percentage'] = sprintf($this->percentage_format, $tree['percentage']);
127  }
128  if (isset($tree['periods'])) {
129  $tree['periods'] = array_map([$this, 'formatTree'], $tree['periods']);
130  }
131  return $tree;
132  }
133 
142  protected function analyzePeriod($name, array $times) {
143  $begin = $this->findBeginTime($times);
144  $end = $this->findEndTime($times);
145  if ($begin === false || $end === false) {
146  return false;
147  }
148  $has_own_markers = isset($times[Timer::MARKER_BEGIN]) && isset($times[Timer::MARKER_BEGIN]);
149  unset($times[Timer::MARKER_BEGIN], $times[Timer::MARKER_END]);
150 
151  $total = $this->diffMicrotime($begin, $end);
152  $ret = [
153  'name' => $name,
154  'percentage' => 100, // may be overwritten by parent
155  'duration' => $total,
156  ];
157 
158  foreach ($times as $times_key => $period) {
159  $period = $this->analyzePeriod($times_key, $period);
160  if ($period === false) {
161  continue;
162  }
163  $period['percentage'] = 100 * $period['duration'] / $this->total;
164  if ($period['percentage'] < $this->minimum_percentage) {
165  continue;
166  }
167  $ret['periods'][] = $period;
168  }
169 
170  if (isset($ret['periods'])) {
171  if (!$has_own_markers) {
172  // this is an aggregation of different non sequential timers (eg. SQL queries)
173  $ret['duration'] = 0;
174  foreach ($ret['periods'] as $period) {
175  $ret['duration'] += $period['duration'];
176  }
177  $ret['percentage'] = 100 * $ret['duration'] / $this->total;
178  }
179 
180  usort($ret['periods'], function ($a, $b) {
181  if ($a['duration'] == $b['duration']) {
182  return 0;
183  }
184  return ($a['duration'] > $b['duration']) ? -1 : 1;
185  });
186  }
187 
188  return $ret;
189  }
190 
197  protected function findBeginTime(array $times) {
198  if (isset($times[Timer::MARKER_BEGIN])) {
199  return $times[Timer::MARKER_BEGIN];
200  }
201  unset($times[Timer::MARKER_BEGIN], $times[Timer::MARKER_END]);
202  $first = reset($times);
203  if (is_array($first)) {
204  return $this->findBeginTime($first);
205  }
206  return false;
207  }
208 
216  protected function findEndTime(array $times) {
217  if (isset($times[Timer::MARKER_END])) {
218  return $times[Timer::MARKER_END];
219  }
220  unset($times[Timer::MARKER_BEGIN], $times[Timer::MARKER_END]);
221  $last = end($times);
222  if (is_array($last)) {
223  return $this->findEndTime($last);
224  }
225  return false;
226  }
227 
236  protected function diffMicrotime($start, $end) {
237  return (float) $end - $start;
238  }
239 }
static project()
Get the project root (where composer is installed) path with "/".
Definition: Paths.php:25
getTimes()
Get the tree of recorded start/end times.
Definition: Timer.php:52
if(!$user||!$user->canDelete()) $name
Definition: delete.php:22
if(elgg_trigger_plugin_hook('usersettings:save', 'user', $hooks_params, true)) foreach($request->validation() ->all() as $item) $data
Definition: save.php:53
Capture timing info for profiling.
Definition: Timer.php:10
analyzePeriod($name, array $times)
Analyze a time period.
Definition: Profiler.php:142
$html
Definition: section.php:10
findBeginTime(array $times)
Get the microtime start time.
Definition: Profiler.php:197
const MARKER_BEGIN
Definition: Timer.php:11
Models an event passed to hook handlers.
Definition: Hook.php:11
foreach($notification_settings as $purpose=> $prefered_methods) if((bool) elgg_get_config('enable_delayed_email')) $start
__invoke(\Elgg\Hook $hook)
Append a SCRIPT element to the page output.
Definition: Profiler.php:31
formatTree(array $tree)
Nicely format the elapsed time values.
Definition: Profiler.php:123
findEndTime(array $times)
Get the microtime end time.
Definition: Profiler.php:216
Analyzes duration of functions, queries, and processes.
Definition: Profiler.php:13
$timer
Definition: pending.php:29
flattenTree(array &$list=[], array $tree=[], $prefix= '')
Turn the tree of times into a sorted list.
Definition: Profiler.php:95
diffMicrotime($start, $end)
Calculate a precise time difference.
Definition: Profiler.php:236
_elgg_services()
Get the global service provider.
Definition: elgglib.php:638
const MARKER_END
Definition: Timer.php:12
$end
buildTree(Timer $timer)
Return a tree of time periods from a Timer.
Definition: Profiler.php:73