dbXapp 2.0
RAD, CMS, Module und Runtime-IDE fuer dbXapp
Loading...
Searching...
No Matches
dbxPerformanceTimer.class.php
Go to the documentation of this file.
1<?php
2
3dbx()->use_system_class('dbxDD');
4
6
7 private const REQUEST_DD = 'dbx|dbxPerformanceRequest';
8 private const TIMER_DD = 'dbx|dbxPerformanceTimer';
9
10 private array $configFileCache = array();
11
12 private function config_value(string $key, $default = '') {
13 $value = dbx()->get_config('dbx', $key);
14
15 if ($value !== 'undef') {
16 return $value;
17 }
18
19 $cfg = $this->config_file_values();
20 return $cfg[$key] ?? $default;
21 }
22
23 private function config_file_values(): array {
24 if ($this->configFileCache) {
25 return $this->configFileCache;
26 }
27
28 $file = dbx_os_path_file(dbx_get_base_dir() . 'dbx/modules/dbx/cfg/config.php');
29 if (!is_file($file)) {
30 return array();
31 }
32
33 $loader = static function ($file) {
34 $config = array();
35 include $file;
36 return is_array($config) ? $config : array();
37 };
38
39 $this->configFileCache = $loader($file);
40 return $this->configFileCache;
41 }
42
43 private function config_bool(string $key, $default = 0): bool {
44 $value = $this->config_value($key, $default);
45 return (int) $value === 1;
46 }
47
48 private function normalize_level($value): string {
49 $level = strtolower(trim((string) $value));
50 if ($level === 'details') {
51 $level = 'detail';
52 }
53
54 return in_array($level, array('off', 'main', 'detail'), true) ? $level : '';
55 }
56
57 private function performance_level(): string {
58 $level = $this->normalize_level($this->config_value('performance_timer_level', ''));
59 if ($level !== '') {
60 return $level;
61 }
62
63 $request = $this->config_bool('performance_timer_request', $this->config_value('performance_timer', 0));
64 $db = $this->config_bool('performance_timer_db', $this->config_value('performance_timer_detail', 0));
65
66 return ($request || $db) ? 'detail' : 'off';
67 }
68
69 private function is_enabled(): bool {
70 return $this->performance_level() !== 'off';
71 }
72
73 private function is_db_section(string $section): bool {
74 return $section === 'db-total'
75 || $section === 'db-select'
76 || $section === 'db-save'
77 || substr($section, 0, 10) === 'db-select-'
78 || substr($section, 0, 8) === 'db-save-';
79 }
80
81 private function is_main_section(string $section): bool {
82 return in_array($section, array('system', 'js-total', 'db-total'), true);
83 }
84
85 private function sample_rate(): int {
86 return max(1, (int) $this->config_value('performance_timer_sample_rate', 1));
87 }
88
89 private function should_sample(int $sampleRate): bool {
90 if ($sampleRate <= 1) {
91 return true;
92 }
93
94 try {
95 return random_int(1, $sampleRate) === 1;
96 } catch (\Throwable $e) {
97 return mt_rand(1, $sampleRate) === 1;
98 }
99 }
100
101 private function ensure_tables(): bool {
102 try {
103 $dd = dbx()->get_system_obj('dbxDD');
104 return $dd->create_db_tab(self::REQUEST_DD) && $dd->create_db_tab(self::TIMER_DD);
105 } catch (\Throwable $e) {
106 return false;
107 }
108 }
109
110 private function ms($seconds): int {
111 return max(0, (int) round(((float) $seconds) * 1000));
112 }
113
114 private function kb($bytes): int {
115 return (int) round(((float) $bytes) / 1024);
116 }
117
118 private function timer_ms(array $timer): int {
119 if (isset($timer['time']) && is_numeric($timer['time']) && (float) $timer['time'] >= 0) {
120 return $this->ms($timer['time']);
121 }
122
123 if (isset($timer['start_time'], $timer['end_time']) && is_numeric($timer['start_time']) && is_numeric($timer['end_time'])) {
124 return $this->ms(max(0, (float) $timer['end_time'] - (float) $timer['start_time']));
125 }
126
127 return 0;
128 }
129
130 private function timer_memory_kb(array $timer): int {
131 if (isset($timer['memory']) && is_numeric($timer['memory']) && (float) $timer['memory'] >= 0) {
132 return $this->kb($timer['memory']);
133 }
134
135 if (isset($timer['start_memory'], $timer['end_memory']) && is_numeric($timer['start_memory']) && is_numeric($timer['end_memory'])) {
136 return $this->kb(max(0, (float) $timer['end_memory'] - (float) $timer['start_memory']));
137 }
138
139 return 0;
140 }
141
142 private function timer_end_memory_mb(array $timer): int {
143 if (isset($timer['end_memory']) && is_numeric($timer['end_memory']) && (float) $timer['end_memory'] >= 0) {
144 return max(0, (int) ceil(((float) $timer['end_memory']) / 1048576));
145 }
146
147 if (isset($timer['start_memory']) && is_numeric($timer['start_memory']) && (float) $timer['start_memory'] >= 0) {
148 return max(0, (int) ceil(((float) $timer['start_memory']) / 1048576));
149 }
150
151 return 0;
152 }
153
154 private function trim_text($value, int $length): string {
155 $value = str_replace(array("\r", "\n", "\t"), ' ', (string) $value);
156 return substr($value, 0, $length);
157 }
158
159 private function request_record(array $timers, int $sampleRate): array {
160 $system = $timers['system'] ?? array();
161 $uid = (int) dbx()->user();
162 $modul = (string) dbx()->get_system_var('dbx_modul', dbx()->get_system_var('dbx_activ_modul', 'dbx'));
163 $now = date('Y-m-d H:i:s');
164
165 return array(
166 'request_date' => $now,
167 'create_date' => $now,
168 'create_uid' => $uid,
169 'update_date' => $now,
170 'update_uid' => $uid,
171 'owner' => $uid,
172 'uid' => $uid,
173 'session_id' => $this->trim_text(session_id(), 128),
174 'modul' => $this->trim_text($modul, 80),
175 'run1' => $this->trim_text(dbx()->get_system_var('dbx_run1', ''), 80),
176 'run2' => $this->trim_text(dbx()->get_system_var('dbx_run2', ''), 80),
177 'ajax' => (int) dbx()->get_system_var('dbx_ajax', 0, 'int'),
178 'sync' => (int) dbx()->get_request_var('dbx_sync', 1, 'int'),
179 'method' => $this->trim_text($_SERVER['REQUEST_METHOD'] ?? '', 12),
180 'uri' => $this->trim_text($_SERVER['REQUEST_URI'] ?? '', 1200),
181 'total_time_ms' => $this->timer_ms($system),
182 'total_memory_kb' => $this->timer_memory_kb($system),
183 'peak_memory_mb' => $this->timer_end_memory_mb($system),
184 'timer_count' => count($timers),
185 'sample_rate' => $sampleRate,
186 );
187 }
188
189 private function timer_record(int $requestId, string $requestDate, string $section, array $timer, int $sortOrder): array {
190 $uid = (int) dbx()->user();
191 $now = date('Y-m-d H:i:s');
192
193 return array(
194 'request_id' => $requestId,
195 'request_date' => $requestDate,
196 'create_date' => $now,
197 'create_uid' => $uid,
198 'update_date' => $now,
199 'update_uid' => $uid,
200 'owner' => $uid,
201 'sort_order' => $sortOrder,
202 'section' => $this->trim_text($section, 80),
203 'info' => $this->trim_text($timer['info'] ?? '', 160),
204 'time_ms' => $this->timer_ms($timer),
205 'memory_kb' => $this->timer_memory_kb($timer),
206 'start_memory_kb' => isset($timer['start_memory']) && is_numeric($timer['start_memory']) ? $this->kb($timer['start_memory']) : 0,
207 'end_memory_kb' => isset($timer['end_memory']) && is_numeric($timer['end_memory']) ? $this->kb($timer['end_memory']) : 0,
208 );
209 }
210
211 private function cleanup_old_rows(): void {
212 $days = (int) $this->config_value('performance_timer_keep_days', 14);
213 if ($days <= 0) {
214 return;
215 }
216
217 $cutoff = date('Y-m-d H:i:s', time() - ($days * 86400));
218
219 try {
220 $db = dbx()->get_system_obj('dbxDB');
221 $db->delete(self::TIMER_DD, "request_date < '" . $cutoff . "'", 0, 0);
222 $db->delete(self::REQUEST_DD, "request_date < '" . $cutoff . "'", 0, 0);
223 } catch (\Throwable $e) {
224 return;
225 }
226 }
227
228 public function store(): int {
229 if ((int) dbx()->get_system_var('dbx_ajax', 0, 'int') === 1) {
230 return 0;
231 }
232
233 if ((int) dbx()->get_system_var('dbx_performance_timer_skip', 0, 'int') === 1) {
234 return 0;
235 }
236
237 if ((int) dbx()->get_request_var('dbx_sync', 1, 'int') === 0) {
238 return 0;
239 }
240
241 if (!$this->is_enabled()) {
242 return 0;
243 }
244
245 $sampleRate = $this->sample_rate();
246 if (!$this->should_sample($sampleRate)) {
247 return 0;
248 }
249
250 global $dbx_run_timer;
251 if (!isset($dbx_run_timer) || !is_array($dbx_run_timer) || !$dbx_run_timer) {
252 return 0;
253 }
254
255 dbx()->set_system_var('dbx_performance_timer_store', 1);
256 try {
257 if (!$this->ensure_tables()) {
258 return 0;
259 }
260
261 $performanceLevel = $this->performance_level();
262 $detailEnabled = $performanceLevel === 'detail';
263 $db = dbx()->get_system_obj('dbxDB');
264 $request = $this->request_record($dbx_run_timer, $sampleRate);
265 $requestId = 0;
266
267 $requestId = ($db->insert(self::REQUEST_DD, $request, 0, 1, 1, 0) === 1) ? $db->get_insert_id() : 0;
268
269 if ($requestId <= 0) {
270 return 0;
271 }
272
273 $sort = 0;
274 foreach ($dbx_run_timer as $section => $timer) {
275 if (!is_array($timer)) {
276 continue;
277 }
278
279 $section = (string) $section;
280 if (!$detailEnabled && !$this->is_main_section($section)) {
281 continue;
282 }
283
284 $db->insert(self::TIMER_DD, $this->timer_record($requestId, $request['request_date'], $section, $timer, $sort), 0, 1, 1, 0);
285 $sort++;
286 }
287
288 $this->cleanup_old_rows();
289 return $requestId;
290 } catch (\Throwable $e) {
291 return 0;
292 } finally {
293 dbx()->set_system_var('dbx_performance_timer_store', 0);
294 }
295 }
296}
$cfg
$config['version']
Definition config.php:2
dbx_os_path_file($path_file)
Definition index.php:164
if( $syncRequest)
Definition index.php:520
dbx_get_base_dir($cut_Data=0)
Definition index.php:157
DBX schema administration.
$_SERVER['REQUEST_URI']