7 private const REQUEST_DD =
'dbx|dbxPerformanceRequest';
8 private const TIMER_DD =
'dbx|dbxPerformanceTimer';
10 private array $configFileCache = array();
12 private function config_value(
string $key, $default =
'') {
13 $value =
dbx()->get_config(
'dbx', $key);
15 if ($value !==
'undef') {
19 $cfg = $this->config_file_values();
20 return $cfg[$key] ?? $default;
23 private function config_file_values(): array {
24 if ($this->configFileCache) {
25 return $this->configFileCache;
29 if (!is_file($file)) {
33 $loader =
static function ($file) {
39 $this->configFileCache = $loader($file);
40 return $this->configFileCache;
43 private function config_bool(
string $key, $default = 0):
bool {
44 $value = $this->config_value($key, $default);
45 return (
int) $value === 1;
48 private function normalize_level($value):
string {
49 $level = strtolower(trim((
string) $value));
50 if ($level ===
'details') {
54 return in_array($level, array(
'off',
'main',
'detail'),
true) ? $level :
'';
57 private function performance_level():
string {
58 $level = $this->normalize_level($this->config_value(
'performance_timer_level',
''));
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));
66 return ($request ||
$db) ?
'detail' :
'off';
69 private function is_enabled():
bool {
70 return $this->performance_level() !==
'off';
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-';
81 private function is_main_section(
string $section):
bool {
82 return in_array($section, array(
'system',
'js-total',
'db-total'),
true);
85 private function sample_rate():
int {
86 return max(1, (
int) $this->config_value(
'performance_timer_sample_rate', 1));
89 private function should_sample(
int $sampleRate):
bool {
90 if ($sampleRate <= 1) {
95 return random_int(1, $sampleRate) === 1;
96 }
catch (\Throwable $e) {
97 return mt_rand(1, $sampleRate) === 1;
101 private function ensure_tables():
bool {
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) {
110 private function ms($seconds):
int {
111 return max(0, (
int) round(((
float) $seconds) * 1000));
114 private function kb($bytes):
int {
115 return (
int) round(((
float) $bytes) / 1024);
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']);
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']));
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']);
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']));
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));
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));
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);
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');
166 'request_date' => $now,
167 'create_date' => $now,
168 'create_uid' => $uid,
169 'update_date' => $now,
170 'update_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,
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');
194 'request_id' => $requestId,
195 'request_date' => $requestDate,
196 'create_date' => $now,
197 'create_uid' => $uid,
198 'update_date' => $now,
199 'update_uid' => $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,
211 private function cleanup_old_rows():
void {
212 $days = (int) $this->config_value(
'performance_timer_keep_days', 14);
217 $cutoff = date(
'Y-m-d H:i:s', time() - ($days * 86400));
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) {
229 if ((int)
dbx()->get_system_var(
'dbx_ajax', 0,
'int') === 1) {
233 if ((
int)
dbx()->get_system_var(
'dbx_performance_timer_skip', 0,
'int') === 1) {
237 if ((
int) dbx()->get_request_var(
'dbx_sync', 1,
'int') === 0) {
241 if (!$this->is_enabled()) {
245 $sampleRate = $this->sample_rate();
246 if (!$this->should_sample($sampleRate)) {
250 global $dbx_run_timer;
251 if (!isset($dbx_run_timer) || !is_array($dbx_run_timer) || !$dbx_run_timer) {
255 dbx()->set_system_var(
'dbx_performance_timer_store', 1);
257 if (!$this->ensure_tables()) {
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);
267 $requestId = (
$db->insert(self::REQUEST_DD, $request, 0, 1, 1, 0) === 1) ?
$db->get_insert_id() : 0;
269 if ($requestId <= 0) {
274 foreach ($dbx_run_timer as $section => $timer) {
275 if (!is_array($timer)) {
279 $section = (string) $section;
280 if (!$detailEnabled && !$this->is_main_section($section)) {
284 $db->insert(self::TIMER_DD, $this->timer_record($requestId, $request[
'request_date'], $section, $timer, $sort), 0, 1, 1, 0);
288 $this->cleanup_old_rows();
290 }
catch (\Throwable $e) {
293 dbx()->set_system_var(
'dbx_performance_timer_store', 0);