Changeset 3424156
- Timestamp:
- 12/20/2025 11:11:10 AM (3 months ago)
- Location:
- dynamic-front-end-heartbeat-control
- Files:
-
- 35 added
- 11 edited
-
tags/1.2.997 (added)
-
tags/1.2.997/LICENSE (added)
-
tags/1.2.997/admin (added)
-
tags/1.2.997/admin/affix.php (added)
-
tags/1.2.997/admin/ajax-handler.php (added)
-
tags/1.2.997/admin/asset-manager.php (added)
-
tags/1.2.997/admin/heartbeat-config.php (added)
-
tags/1.2.997/admin/unclogger-menu.php (added)
-
tags/1.2.997/css (added)
-
tags/1.2.997/css/dfhcsl-admin.css (added)
-
tags/1.2.997/defibrillator (added)
-
tags/1.2.997/defibrillator/cli-helper.php (added)
-
tags/1.2.997/defibrillator/db-health.php (added)
-
tags/1.2.997/defibrillator/load-estimator.php (added)
-
tags/1.2.997/defibrillator/rest-api.php (added)
-
tags/1.2.997/defibrillator/unclogger-db.php (added)
-
tags/1.2.997/defibrillator/unclogger.php (added)
-
tags/1.2.997/engine (added)
-
tags/1.2.997/engine/interval-helper.php (added)
-
tags/1.2.997/engine/server-load.php (added)
-
tags/1.2.997/engine/server-response.php (added)
-
tags/1.2.997/engine/system-load-fallback.php (added)
-
tags/1.2.997/heartbeat-async.php (added)
-
tags/1.2.997/heartbeat-controller.php (added)
-
tags/1.2.997/js (added)
-
tags/1.2.997/js/chart.js (added)
-
tags/1.2.997/js/dfhcsl-admin.js (added)
-
tags/1.2.997/js/heartbeat.js (added)
-
tags/1.2.997/js/heartbeat.min.js (added)
-
tags/1.2.997/readme.txt (added)
-
tags/1.2.997/settings.php (added)
-
tags/1.2.997/visitor (added)
-
tags/1.2.997/visitor/cookie-helper.php (added)
-
tags/1.2.997/visitor/manager.php (added)
-
tags/1.2.997/widget.php (added)
-
trunk/defibrillator/cli-helper.php (modified) (4 diffs)
-
trunk/defibrillator/load-estimator.php (modified) (20 diffs)
-
trunk/engine/server-load.php (modified) (9 diffs)
-
trunk/engine/server-response.php (modified) (3 diffs)
-
trunk/engine/system-load-fallback.php (modified) (13 diffs)
-
trunk/heartbeat-async.php (modified) (5 diffs)
-
trunk/heartbeat-controller.php (modified) (5 diffs)
-
trunk/readme.txt (modified) (2 diffs)
-
trunk/visitor/cookie-helper.php (modified) (9 diffs)
-
trunk/visitor/manager.php (modified) (23 diffs)
-
trunk/widget.php (modified) (5 diffs)
Legend:
- Unmodified
- Added
- Removed
-
dynamic-front-end-heartbeat-control/trunk/defibrillator/cli-helper.php
r3396626 r3424156 84 84 class DFEHC_CLI_Command { 85 85 86 private function key_candidates(string $base): array { 87 $keys = []; 88 $base = (string) $base; 89 if ($base !== '') { 90 $keys[] = $base; 91 } 92 $keys[] = dfehc_scope_key($base); 93 if (\function_exists('\dfehc_scoped_key')) { 94 $keys[] = \dfehc_scoped_key($base); 95 } 96 if (\function_exists('\dfehc_key')) { 97 $keys[] = \dfehc_key($base); 98 } 99 $keys = \array_values(\array_unique(\array_filter($keys, 'strlen'))); 100 return $keys; 101 } 102 103 private function delete_candidate_keys(string $base): void { 104 $keys = $this->key_candidates($base); 105 foreach ($keys as $k) { 106 \delete_transient($k); 107 if (\is_multisite()) { 108 \delete_site_transient($k); 109 } 110 } 111 } 112 113 private function set_candidate_keys(string $base, $value, int $ttl): void { 114 $keys = $this->key_candidates($base); 115 foreach ($keys as $k) { 116 if (\function_exists('\dfehc_set_transient_noautoload')) { 117 \dfehc_set_transient_noautoload($k, $value, $ttl); 118 } else { 119 \set_transient($k, $value, $ttl); 120 } 121 } 122 } 123 86 124 public function recalc_interval(array $args = [], array $assoc_args = []) { 87 125 if (!\function_exists('\dfehc_get_server_load')) { … … 106 144 107 145 $baseKey = \defined('DFEHC_RECOMMENDED_INTERVAL') ? \DFEHC_RECOMMENDED_INTERVAL : 'dfehc_recommended_interval'; 108 $key = dfehc_scope_key($baseKey); 109 110 if (\function_exists('\dfehc_set_transient_noautoload')) { 111 \dfehc_set_transient_noautoload($key, $interval, 5 * MINUTE_IN_SECONDS); 112 } else { 113 \set_transient($key, $interval, 5 * MINUTE_IN_SECONDS); 114 } 146 $ttl = 5 * MINUTE_IN_SECONDS; 147 148 $this->set_candidate_keys((string) $baseKey, $interval, $ttl); 115 149 116 150 \WP_CLI::success("Heartbeat interval recalculated and cached: {$interval}s"); … … 162 196 try { 163 197 foreach ($bases as $base) { 164 $key = dfehc_scope_key($base); 165 \delete_transient($key); 166 \delete_site_transient($key); 198 $this->delete_candidate_keys((string) $base); 167 199 } 168 200 \delete_option('dfehc_stale_total_visitors'); … … 201 233 202 234 $baseKey = \defined('DFEHC_RECOMMENDED_INTERVAL') ? \DFEHC_RECOMMENDED_INTERVAL : 'dfehc_recommended_interval'; 203 $key = dfehc_scope_key($baseKey); 204 $interval = \get_transient($key); 235 $interval = \get_transient(dfehc_scope_key((string) $baseKey)); 236 237 if (($interval === false || $interval === null) && \function_exists('\dfehc_scoped_key')) { 238 $interval = \get_transient(\dfehc_scoped_key((string) $baseKey)); 239 } 240 if (($interval === false || $interval === null) && \function_exists('\dfehc_key')) { 241 $interval = \get_transient(\dfehc_key((string) $baseKey)); 242 } 205 243 206 244 $heartbeat_enabled = !\get_option('dfehc_disable_heartbeat'); -
dynamic-front-end-heartbeat-control/trunk/defibrillator/load-estimator.php
r3412283 r3424156 31 31 $duration = 0.5; 32 32 } 33 33 34 $suffix = self::scope_suffix(); 34 35 $baselineT = self::get_baseline_transient_name($suffix); 35 36 $cacheTtl = (int) \apply_filters('dfehc_load_cache_ttl', 90); 37 if ($cacheTtl < 1) { 38 $cacheTtl = 1; 39 } 36 40 $cacheKey = self::get_cache_key($suffix); 41 37 42 $cached = self::get_scoped_transient($cacheKey); 38 if ($cached !== false && $cached !== null) { 39 return $cached; 40 } 43 if ($cached !== false && $cached !== null && \is_numeric($cached)) { 44 return (float) $cached; 45 } 46 41 47 $sysAvg = self::try_sys_getloadavg(); 42 48 if ($sysAvg !== null) { … … 44 50 return $sysAvg; 45 51 } 52 46 53 $baseline = self::get_baseline_value($baselineT); 47 if ($baseline === false || $baseline === null || !\is_numeric($baseline) || $baseline <=0) {54 if ($baseline === false || $baseline === null || !\is_numeric($baseline) || (float) $baseline <= 0.0) { 48 55 $baseline = self::maybe_calibrate($baselineT, $duration); 49 56 } 57 $baseline = (float) $baseline; 58 if ($baseline <= 0.0) { 59 $baseline = 1.0; 60 } 61 50 62 $loopsPerSec = self::run_loop_avg($duration); 51 63 if ($loopsPerSec <= 0) { 52 64 return false; 53 65 } 66 54 67 $loadRatio = ($baseline * 0.125) / \max($loopsPerSec, 1); 55 68 $loadPercent = \round(\min(100.0, \max(0.0, $loadRatio * 100.0)), 2); 56 69 $loadPercent = (float) \apply_filters('dfehc_computed_load_percent', $loadPercent, $baseline, $loopsPerSec); 70 57 71 self::update_spike_score($loadPercent, $suffix); 58 72 self::set_scoped_transient_noautoload($cacheKey, $loadPercent, $cacheTtl); 73 59 74 return $loadPercent; 60 75 } … … 84 99 return; 85 100 } 101 86 102 $suffix = self::scope_suffix(); 87 103 $seenKey = 'dfehc_seen_recently_' . $suffix; 88 104 $ttl = (int) \apply_filters('dfehc_seen_recently_ttl', 60); 105 if ($ttl < 1) { 106 $ttl = 1; 107 } 89 108 if (\get_transient($seenKey) !== false) { 90 109 return; … … 101 120 $cdKey = 'dfehc_cron_cal_cd_' . $suffix; 102 121 $cdTtl = (int) \apply_filters('dfehc_cron_calibration_cooldown', 300); 122 if ($cdTtl < 1) { 123 $cdTtl = 1; 124 } 103 125 if (\get_transient($cdKey) !== false) { 104 126 return; … … 118 140 $raw = (float) $avg[0]; 119 141 $raw = (float) \apply_filters('dfehc_raw_sys_load', $raw); 142 120 143 $cores = 0; 121 144 if (\defined('DFEHC_CPU_CORES')) { 122 145 $cores = (int) DFEHC_CPU_CORES; 123 } elseif (\function_exists('\dfehc_get_cpu_cores')) {124 $cores = (int) \dfehc_get_cpu_cores();125 146 } elseif (\function_exists('dfehc_get_cpu_cores')) { 126 147 $cores = (int) \dfehc_get_cpu_cores(); … … 130 151 $cores = 1; 131 152 } 153 132 154 return \min(100.0, \round(($raw / $cores) * 100.0, 2)); 133 155 } … … 148 170 } 149 171 $duration += \mt_rand(0, 2) * 0.001; 172 150 173 $warm = self::now(); 151 174 for ($i = 0; $i < 1000; $i++) { $warm += 0; } 175 152 176 $start = self::now(); 153 177 $end = $start + $duration; 154 178 $cnt = 0; 155 179 $cap = (int) \apply_filters('dfehc_loop_iteration_cap', 10000000); 180 if ($cap < 1000) { 181 $cap = 1000; 182 } 156 183 $now = $start; 184 157 185 while ($now < $end && $cnt < $cap) { 158 186 ++$cnt; 159 187 $now = self::now(); 160 188 } 189 161 190 $elapsed = $now - $start; 162 191 return $elapsed > 0 ? $cnt / $elapsed : 0.0; … … 178 207 $lockKey = 'dfehc_calibrating_' . $suffix; 179 208 $lock = self::acquire_lock($lockKey, 30); 209 if (!$lock) { 210 $existing = self::get_baseline_value($baselineT); 211 if ($existing !== false && $existing !== null && \is_numeric($existing) && (float) $existing > 0.0) { 212 return (float) $existing; 213 } 214 } 215 180 216 $baseline = self::run_loop_avg($duration); 181 217 if ($baseline <= 0.0) { 182 218 $baseline = 1.0; 183 219 } 220 184 221 if ($lock) { 185 222 $exp = (int) \apply_filters('dfehc_baseline_expiration', 7 * DAY_IN_SECONDS); 223 if ($exp < 60) { 224 $exp = 60; 225 } 186 226 self::set_baseline_value($baselineT, $baseline, $exp); 187 self::release_lock($lock, $lockKey); 188 } 227 self::release_lock($lock); 228 } 229 189 230 return $baseline; 190 231 } … … 192 233 private static function update_spike_score(float $loadPercent, string $suffix): void { 193 234 $spikeKey = self::get_spike_key($suffix); 194 $score = (float) self::get_scoped_transient($spikeKey); 235 $scoreRaw = self::get_scoped_transient($spikeKey); 236 $score = \is_numeric($scoreRaw) ? (float) $scoreRaw : 0.0; 237 195 238 $decay = (float) \apply_filters('dfehc_spike_decay', 0.5); 196 239 $increment = (float) \apply_filters('dfehc_spike_increment', 1.0); … … 198 241 $trigger = (float) \apply_filters('dfehc_spike_trigger', 90.0); 199 242 $resetCdTtl = (int) \apply_filters('dfehc_baseline_reset_cooldown', 3600); 243 if ($resetCdTtl < 1) { 244 $resetCdTtl = 1; 245 } 200 246 $resetCdKey = self::BASELINE_RESET_CD_PREFIX . $suffix; 201 247 $scoreMax = (float) \apply_filters('dfehc_spike_score_max', 20.0); 248 202 249 if ($loadPercent > $trigger) { 203 250 $score += $increment * (1 + (($loadPercent - $trigger) / 20.0)); … … 205 252 $score = \max(0.0, $score - $decay); 206 253 } 254 207 255 $score = \min($scoreMax, \max(0.0, $score)); 256 208 257 if ($score >= $threshold) { 209 258 if (\get_transient($resetCdKey) === false) { … … 215 264 } 216 265 } 266 217 267 self::set_scoped_transient_noautoload($spikeKey, $score, (int) \apply_filters('dfehc_spike_score_ttl', HOUR_IN_SECONDS)); 218 268 } … … 222 272 $baselineT = self::get_baseline_transient_name($suffix); 223 273 $existing = self::get_baseline_value($baselineT); 224 if ($existing !== false && $existing !== null && \is_numeric($existing) && $existing > 0) { 225 return; 226 } 274 if ($existing !== false && $existing !== null && \is_numeric($existing) && (float) $existing > 0.0) { 275 return; 276 } 277 227 278 $lockKey = 'dfehc_calibrating_' . $suffix; 228 279 $lock = self::acquire_lock($lockKey, 30); … … 230 281 return; 231 282 } 232 $duration = (float) \apply_filters('dfehc_loop_duration', 0.025); 233 if (!\is_finite($duration) || $duration <= 0.0) { 234 $duration = 0.025; 235 } 236 if ($duration < 0.01) { 237 $duration = 0.01; 238 } 239 if ($duration > 0.5) { 240 $duration = 0.5; 241 } 242 $baseline = self::run_loop_avg($duration); 243 if ($baseline <= 0.0) { 244 $baseline = 1.0; 245 } 246 $exp = (int) \apply_filters('dfehc_baseline_expiration', 7 * DAY_IN_SECONDS); 247 self::set_baseline_value($baselineT, $baseline, $exp); 248 self::release_lock($lock, $lockKey); 283 284 try { 285 $duration = (float) \apply_filters('dfehc_loop_duration', 0.025); 286 if (!\is_finite($duration) || $duration <= 0.0) { 287 $duration = 0.025; 288 } 289 if ($duration < 0.01) { 290 $duration = 0.01; 291 } 292 if ($duration > 0.5) { 293 $duration = 0.5; 294 } 295 296 $baseline = self::run_loop_avg($duration); 297 if ($baseline <= 0.0) { 298 $baseline = 1.0; 299 } 300 301 $exp = (int) \apply_filters('dfehc_baseline_expiration', 7 * DAY_IN_SECONDS); 302 if ($exp < 60) { 303 $exp = 60; 304 } 305 self::set_baseline_value($baselineT, $baseline, $exp); 306 } finally { 307 self::release_lock($lock); 308 } 249 309 } 250 310 251 311 private static function acquire_lock(string $key, int $ttl) { 312 $ttl = $ttl < 1 ? 1 : $ttl; 313 $scopedKey = $key; 314 252 315 if (\class_exists('\WP_Lock')) { 253 $lock = new \WP_Lock($ key, $ttl);316 $lock = new \WP_Lock($scopedKey, $ttl); 254 317 return $lock->acquire() ? $lock : null; 255 318 } 256 if (\function_exists('wp_cache_add') && \wp_cache_add($ key, 1, DFEHC_CACHE_GROUP, $ttl)) {257 return (object) ['type' => 'cache', 'key' => $ key];258 } 259 if (\get_transient($ key) !== false) {319 if (\function_exists('wp_cache_add') && \wp_cache_add($scopedKey, 1, DFEHC_CACHE_GROUP, $ttl)) { 320 return (object) ['type' => 'cache', 'key' => $scopedKey]; 321 } 322 if (\get_transient($scopedKey) !== false) { 260 323 return null; 261 324 } 262 if (\set_transient($ key, 1, $ttl)) {263 return (object) ['type' => 'transient', 'key' => $ key];325 if (\set_transient($scopedKey, 1, $ttl)) { 326 return (object) ['type' => 'transient', 'key' => $scopedKey]; 264 327 } 265 328 return null; 266 329 } 267 330 268 private static function release_lock($lock , string $key): void {331 private static function release_lock($lock): void { 269 332 if ($lock instanceof \WP_Lock) { 270 333 $lock->release(); … … 273 336 if (\is_object($lock) && isset($lock->type, $lock->key)) { 274 337 if ($lock->type === 'cache') { 275 \wp_cache_delete($key, DFEHC_CACHE_GROUP); 338 if (\function_exists('wp_cache_delete')) { 339 \wp_cache_delete((string) $lock->key, DFEHC_CACHE_GROUP); 340 } 276 341 return; 277 342 } 278 343 if ($lock->type === 'transient') { 279 \delete_transient( $key);344 \delete_transient((string) $lock->key); 280 345 return; 281 346 } … … 292 357 $url = \defined('WP_HOME') && WP_HOME ? WP_HOME : (\function_exists('home_url') ? \home_url() : ''); 293 358 $parts = \parse_url((string) $url); 294 $host = $parts['host'] ?? ($url ?: 'unknown'); 359 $host = \is_array($parts) ? ($parts['host'] ?? '') : ''; 360 if (!$host) { 361 $host = $url ?: 'unknown'; 362 } 295 363 } 296 364 $salt = \defined('DB_NAME') ? (string) DB_NAME : ''; … … 326 394 327 395 private static function set_baseline_value(string $name, $value, int $exp): void { 396 if ($exp < 1) { 397 $exp = 1; 398 } 328 399 if (\is_multisite()) { 329 400 self::set_site_transient_noautoload($name, $value, $exp); … … 346 417 347 418 private static function set_scoped_transient_noautoload(string $key, $value, int $ttl): void { 419 if ($ttl < 1) { 420 $ttl = 1; 421 } 348 422 if (\is_multisite()) { 349 423 self::set_site_transient_noautoload($key, $value, $ttl); … … 371 445 } 372 446 $ttl = \max(1, $ttl + $jitter); 447 373 448 if (\function_exists('wp_using_ext_object_cache') && \wp_using_ext_object_cache()) { 374 449 \wp_cache_set($key, $value, DFEHC_CACHE_GROUP, $ttl); 375 450 return; 376 451 } 452 377 453 \set_transient($key, $value, $ttl); 454 378 455 global $wpdb; 379 if (!isset($wpdb->options)) { 380 return; 381 } 456 if (!isset($wpdb) || !\is_object($wpdb) || !isset($wpdb->options)) { 457 return; 458 } 459 382 460 $opt_key = "_transient_$key"; 383 461 $opt_key_to = "_transient_timeout_$key"; 384 462 $wpdb->suppress_errors(true); 385 $autoload = $wpdb->get_var($wpdb->prepare("SELECT autoload FROM {$wpdb->options} WHERE option_name=%s LIMIT 1", $opt_key)); 386 if ($autoload === 'yes') { 387 $wpdb->update($wpdb->options, ['autoload' => 'no'], ['option_name' => $opt_key, 'autoload' => 'yes'], ['%s'], ['%s','%s']); 388 } 389 $autoload_to = $wpdb->get_var($wpdb->prepare("SELECT autoload FROM {$wpdb->options} WHERE option_name=%s LIMIT 1", $opt_key_to)); 390 if ($autoload_to === 'yes') { 391 $wpdb->update($wpdb->options, ['autoload' => 'no'], ['option_name' => $opt_key_to, 'autoload' => 'yes'], ['%s'], ['%s','%s']); 392 } 393 $wpdb->suppress_errors(false); 463 try { 464 $autoload = $wpdb->get_var($wpdb->prepare("SELECT autoload FROM {$wpdb->options} WHERE option_name=%s LIMIT 1", $opt_key)); 465 if ($autoload === 'yes') { 466 $wpdb->update($wpdb->options, ['autoload' => 'no'], ['option_name' => $opt_key, 'autoload' => 'yes'], ['%s'], ['%s','%s']); 467 } 468 $autoload_to = $wpdb->get_var($wpdb->prepare("SELECT autoload FROM {$wpdb->options} WHERE option_name=%s LIMIT 1", $opt_key_to)); 469 if ($autoload_to === 'yes') { 470 $wpdb->update($wpdb->options, ['autoload' => 'no'], ['option_name' => $opt_key_to, 'autoload' => 'yes'], ['%s'], ['%s','%s']); 471 } 472 } finally { 473 $wpdb->suppress_errors(false); 474 } 394 475 } 395 476 … … 404 485 } 405 486 $ttl = \max(1, $ttl + $jitter); 487 406 488 if (\function_exists('wp_using_ext_object_cache') && \wp_using_ext_object_cache()) { 407 489 \wp_cache_set($key, $value, DFEHC_CACHE_GROUP, $ttl); 408 490 return; 409 491 } 492 410 493 \set_site_transient($key, $value, $ttl); 411 494 } -
dynamic-front-end-heartbeat-control/trunk/engine/server-load.php
r3412883 r3424156 68 68 function dfehc_client_ip(): string 69 69 { 70 $ip = (string)($_SERVER['REMOTE_ADDR'] ?? '0.0.0.0'); 70 $remote = (string) ($_SERVER['REMOTE_ADDR'] ?? ''); 71 $remote = $remote !== '' ? $remote : '0.0.0.0'; 72 73 $is_valid_ip = static function (string $ip, bool $publicOnly): bool { 74 $ip = trim($ip); 75 if ($ip === '') return false; 76 77 if ($publicOnly) { 78 return filter_var($ip, FILTER_VALIDATE_IP, FILTER_FLAG_NO_PRIV_RANGE | FILTER_FLAG_NO_RES_RANGE) !== false; 79 } 80 return filter_var($ip, FILTER_VALIDATE_IP) !== false; 81 }; 82 83 $public_only = (bool) apply_filters('dfehc_client_ip_public_only', true); 84 71 85 $trusted = (array) apply_filters('dfehc_trusted_proxies', []); 72 if ($trusted && in_array($ip, $trusted, true)) { 73 $headers = (array) apply_filters('dfehc_proxy_ip_headers', ['HTTP_X_FORWARDED_FOR', 'HTTP_CF_CONNECTING_IP', 'HTTP_X_REAL_IP']); 86 $trusted = array_values(array_filter(array_map('trim', $trusted), 'strlen')); 87 $remote_is_trusted = $trusted && in_array($remote, $trusted, true); 88 89 if ($remote_is_trusted) { 90 $headers = (array) apply_filters( 91 'dfehc_proxy_ip_headers', 92 ['HTTP_CF_CONNECTING_IP', 'HTTP_TRUE_CLIENT_IP', 'HTTP_X_REAL_IP', 'HTTP_X_FORWARDED_FOR'] 93 ); 94 74 95 foreach ($headers as $h) { 75 if (!empty($_SERVER[$h])) { 76 $raw = (string) $_SERVER[$h]; 96 $h = (string) $h; 97 if ($h === '' || empty($_SERVER[$h])) { 98 continue; 99 } 100 101 $raw = (string) $_SERVER[$h]; 102 103 if ($h === 'HTTP_X_FORWARDED_FOR') { 77 104 $parts = array_map('trim', explode(',', $raw)); 78 $cand = end($parts); 79 if ($cand) { 105 106 foreach ($parts as $cand) { 107 if ($is_valid_ip($cand, $public_only)) { 108 return (string) apply_filters('dfehc_client_ip', $cand); 109 } 110 } 111 foreach ($parts as $cand) { 112 if ($is_valid_ip($cand, false)) { 113 return (string) apply_filters('dfehc_client_ip', $cand); 114 } 115 } 116 } else { 117 $cand = trim($raw); 118 if ($is_valid_ip($cand, $public_only) || $is_valid_ip($cand, false)) { 80 119 return (string) apply_filters('dfehc_client_ip', $cand); 81 120 } … … 83 122 } 84 123 } 85 return (string) apply_filters('dfehc_client_ip', $ip); 124 125 if (!$is_valid_ip($remote, false)) { 126 $remote = '0.0.0.0'; 127 } 128 129 return (string) apply_filters('dfehc_client_ip', $remote); 86 130 } 87 131 } … … 119 163 { 120 164 static $cached = null; 121 static $ts = 0; 165 static $last_probe_ts = 0; 166 122 167 $retryAfter = (int) apply_filters('dfehc_cache_retry_after', 60); 123 if ($cached !== null && ($cached['type'] !== 'none' || $ts > time() - $retryAfter)) { 124 return $cached; 125 } 126 $ts = time(); 168 $retryAfter = max(0, $retryAfter); 169 170 if (is_array($cached) && isset($cached['type'])) { 171 if ($cached['type'] !== 'none') { 172 return $cached; 173 } 174 if ($retryAfter > 0 && (time() - (int) $last_probe_ts) < $retryAfter) { 175 return $cached; 176 } 177 } 178 179 $last_probe_ts = time(); 180 127 181 global $wp_object_cache; 128 182 if (is_object($wp_object_cache) && isset($wp_object_cache->redis) && $wp_object_cache->redis instanceof Redis) { … … 132 186 return $cached = ['client' => $wp_object_cache->mc, 'type' => 'memcached']; 133 187 } 188 134 189 if (class_exists('Redis')) { 135 190 try { … … 138 193 $pass = apply_filters('dfehc_redis_auth', getenv('REDIS_PASSWORD') ?: null); 139 194 $user = apply_filters('dfehc_redis_user', getenv('REDIS_USERNAME') ?: null); 140 if ($user && $pass && method_exists($redis, 'auth')) { 141 $redis->auth([$user, $pass]); 142 } elseif ($pass && method_exists($redis, 'auth')) { 143 $redis->auth($pass); 144 } 195 196 if (method_exists($redis, 'auth')) { 197 if ($user && $pass) { 198 $ok = $redis->auth([$user, $pass]); 199 if ($ok === false) { 200 throw new RuntimeException('Redis auth failed'); 201 } 202 } elseif ($pass) { 203 $ok = $redis->auth($pass); 204 if ($ok === false) { 205 throw new RuntimeException('Redis auth failed'); 206 } 207 } 208 } 209 145 210 return $cached = ['client' => $redis, 'type' => 'redis']; 146 211 } … … 151 216 } 152 217 } 218 153 219 if (class_exists('Memcached')) { 154 220 try { … … 157 223 $mc->addServer(dfehc_get_memcached_server(), dfehc_get_memcached_port()); 158 224 } 225 159 226 $user = getenv('MEMCACHED_USERNAME'); 160 227 $pass = getenv('MEMCACHED_PASSWORD'); … … 163 230 $mc->setSaslAuthData($user, $pass); 164 231 } 232 165 233 $versions = $mc->getVersion(); 166 234 $first = is_array($versions) ? reset($versions) : false; 167 $ok = $first && $first !== '0.0.0' ;235 $ok = $first && $first !== '0.0.0' && $first !== '0.0.0.0'; 168 236 if ($ok) { 169 237 return $cached = ['client' => $mc, 'type' => 'memcached']; … … 175 243 } 176 244 } 245 177 246 return $cached = ['client' => null, 'type' => 'none']; 178 247 } -
dynamic-front-end-heartbeat-control/trunk/engine/server-response.php
r3412283 r3424156 70 70 71 71 if (!function_exists('dfehc_client_ip')) { 72 function dfehc_client_ip(): string { 73 $ip = (string)($_SERVER['REMOTE_ADDR'] ?? '0.0.0.0'); 72 function dfehc_client_ip(): string 73 { 74 $remote = (string) ($_SERVER['REMOTE_ADDR'] ?? ''); 75 $remote = $remote !== '' ? $remote : '0.0.0.0'; 76 77 $is_valid_ip = static function (string $ip, bool $publicOnly): bool { 78 $ip = trim($ip); 79 if ($ip === '') return false; 80 if ($publicOnly) { 81 return filter_var($ip, FILTER_VALIDATE_IP, FILTER_FLAG_NO_PRIV_RANGE | FILTER_FLAG_NO_RES_RANGE) !== false; 82 } 83 return filter_var($ip, FILTER_VALIDATE_IP) !== false; 84 }; 85 86 $public_only = (bool) apply_filters('dfehc_client_ip_public_only', true); 87 74 88 $trusted = (array) apply_filters('dfehc_trusted_proxies', []); 75 if ($trusted && in_array($ip, $trusted, true)) { 76 $headers = (array) apply_filters('dfehc_proxy_ip_headers', ['HTTP_X_FORWARDED_FOR', 'HTTP_CF_CONNECTING_IP', 'HTTP_X_REAL_IP']); 89 $trusted = array_values(array_filter(array_map('trim', $trusted), 'strlen')); 90 $remote_is_trusted = $trusted && in_array($remote, $trusted, true); 91 92 if ($remote_is_trusted) { 93 $headers = (array) apply_filters( 94 'dfehc_proxy_ip_headers', 95 ['HTTP_CF_CONNECTING_IP', 'HTTP_TRUE_CLIENT_IP', 'HTTP_X_REAL_IP', 'HTTP_X_FORWARDED_FOR'] 96 ); 97 77 98 foreach ($headers as $h) { 78 if (!empty($_SERVER[$h])) { 79 $raw = (string) $_SERVER[$h]; 99 $h = (string) $h; 100 if ($h === '' || empty($_SERVER[$h])) { 101 continue; 102 } 103 104 $raw = (string) $_SERVER[$h]; 105 106 if ($h === 'HTTP_X_FORWARDED_FOR') { 80 107 $parts = array_map('trim', explode(',', $raw)); 81 $cand = end($parts); 82 if ($cand) { 108 109 foreach ($parts as $cand) { 110 if ($is_valid_ip($cand, $public_only)) { 111 return (string) apply_filters('dfehc_client_ip', $cand); 112 } 113 } 114 foreach ($parts as $cand) { 115 if ($is_valid_ip($cand, false)) { 116 return (string) apply_filters('dfehc_client_ip', $cand); 117 } 118 } 119 } else { 120 $cand = trim($raw); 121 if ($is_valid_ip($cand, $public_only) || $is_valid_ip($cand, false)) { 83 122 return (string) apply_filters('dfehc_client_ip', $cand); 84 123 } … … 86 125 } 87 126 } 88 return (string) apply_filters('dfehc_client_ip', $ip); 127 128 if (!$is_valid_ip($remote, false)) { 129 $remote = '0.0.0.0'; 130 } 131 132 return (string) apply_filters('dfehc_client_ip', $remote); 89 133 } 90 134 } … … 177 221 } 178 222 179 $results['baseline_used'] = $baseline;223 $results['baseline_used'] = is_array($baseline); 180 224 181 225 if (is_array($baseline) && $results['method'] === 'http_loopback' && isset($results['main_response_ms'], $baseline['main_response_ms'])) { -
dynamic-front-end-heartbeat-control/trunk/engine/system-load-fallback.php
r3412283 r3424156 3 3 4 4 defined('DFEHC_SERVER_LOAD_TTL') || define('DFEHC_SERVER_LOAD_TTL', 60); 5 defined('DFEHC_SENTINEL_NO_LOAD') || define('DFEHC_SENTINEL_NO_LOAD', 0.404);5 defined('DFEHC_SENTINEL_NO_LOAD') || define('DFEHC_SENTINEL_NO_LOAD', -1.0); 6 6 defined('DFEHC_SYSTEM_LOAD_KEY') || define('DFEHC_SYSTEM_LOAD_KEY', 'dfehc_system_load_avg'); 7 7 defined('DFEHC_CACHE_GROUP') || define('DFEHC_CACHE_GROUP', 'dfehc'); … … 40 40 } 41 41 $expiration = max(1, $expiration + $jitter); 42 42 43 if (function_exists('wp_using_ext_object_cache') && wp_using_ext_object_cache()) { 43 44 wp_cache_set($key, $value, DFEHC_CACHE_GROUP, $expiration); 44 45 return; 45 46 } 47 46 48 set_transient($key, $value, $expiration); 47 if (isset($GLOBALS['wpdb'])) { 48 global $wpdb; 49 if (!isset($wpdb->options)) { 50 return; 51 } 52 $wpdb->suppress_errors(true); 53 $wpdb->query($wpdb->prepare("UPDATE {$wpdb->options} SET autoload='no' WHERE option_name=%s AND autoload='yes' LIMIT 1", "_transient_$key")); 54 $wpdb->query($wpdb->prepare("UPDATE {$wpdb->options} SET autoload='no' WHERE option_name=%s AND autoload='yes' LIMIT 1", "_transient_timeout_$key")); 55 $wpdb->suppress_errors(false); 56 } 49 50 if (!isset($GLOBALS['wpdb'])) { 51 return; 52 } 53 global $wpdb; 54 if (!isset($wpdb->options)) { 55 return; 56 } 57 58 $opt_key = "_transient_$key"; 59 $opt_key_to = "_transient_timeout_$key"; 60 61 $wpdb->suppress_errors(true); 62 63 $autoload = $wpdb->get_var($wpdb->prepare("SELECT autoload FROM {$wpdb->options} WHERE option_name=%s LIMIT 1", $opt_key)); 64 if ($autoload === 'yes') { 65 $wpdb->update($wpdb->options, ['autoload' => 'no'], ['option_name' => $opt_key, 'autoload' => 'yes'], ['%s'], ['%s', '%s']); 66 } 67 68 $autoload_to = $wpdb->get_var($wpdb->prepare("SELECT autoload FROM {$wpdb->options} WHERE option_name=%s LIMIT 1", $opt_key_to)); 69 if ($autoload_to === 'yes') { 70 $wpdb->update($wpdb->options, ['autoload' => 'no'], ['option_name' => $opt_key_to, 'autoload' => 'yes'], ['%s'], ['%s', '%s']); 71 } 72 73 $wpdb->suppress_errors(false); 57 74 } 58 75 } … … 65 82 return ''; 66 83 } 84 67 85 $can_proc = function_exists('proc_open') && !in_array('proc_open', $disabled, true); 68 86 if ($can_proc) { … … 70 88 $process = @proc_open($cmd, $descriptorspec, $pipes); 71 89 if (!is_resource($process)) return ''; 72 foreach ($pipes as $p) { @stream_set_blocking($p, false); @stream_set_timeout($p, (int)ceil($timeoutSec)); } 73 $out = ''; $err = ''; 90 91 foreach ($pipes as $p) { 92 @stream_set_blocking($p, false); 93 @stream_set_timeout($p, (int) ceil($timeoutSec)); 94 } 95 96 $out = ''; 74 97 $start = microtime(true); 75 98 $spins = 0; 99 76 100 while (true) { 77 $out .= @stream_get_contents($pipes[1]) ?: ''; 78 $err .= @stream_get_contents($pipes[2]) ?: ''; 101 $out .= (string) (@stream_get_contents($pipes[1]) ?: ''); 102 @stream_get_contents($pipes[2]); 103 79 104 $status = @proc_get_status($process); 80 if (!$status || !$status['running']) break; 81 if ((microtime(true) - $start) > $timeoutSec) { @proc_terminate($process); break; } 105 if (!$status || empty($status['running'])) { 106 break; 107 } 108 109 if ((microtime(true) - $start) > $timeoutSec) { 110 @proc_terminate($process); 111 break; 112 } 113 82 114 $spins++; 83 115 usleep($spins > 10 ? 25000 : 10000); 84 116 } 85 foreach ($pipes as $p) { @fclose($p); } 117 118 foreach ($pipes as $p) { 119 @fclose($p); 120 } 86 121 @proc_close($process); 122 87 123 return trim($out); 88 124 } 125 89 126 $can_shell = function_exists('shell_exec') && !in_array('shell_exec', $disabled, true); 90 127 if ($can_shell) { 91 128 return trim((string) @shell_exec($cmd)); 92 129 } 130 93 131 return ''; 94 132 } … … 98 136 function dfehc_get_cpu_cores(): int { 99 137 static $cached = null; 100 if ($cached !== null) return $cached; 138 if ($cached !== null) return (int) $cached; 139 101 140 $tkey = dfehc_scoped_key('dfehc_cpu_cores'); 141 102 142 if (function_exists('wp_using_ext_object_cache') && wp_using_ext_object_cache()) { 103 143 $oc = wp_cache_get($tkey, DFEHC_CACHE_GROUP); … … 106 146 } 107 147 } 148 108 149 $tc = get_transient($tkey); 109 150 if ($tc !== false && (int) $tc > 0) { 110 151 return $cached = (int) $tc; 111 152 } 153 112 154 $disabled = array_map('trim', explode(',', (string) ini_get('disable_functions'))); 155 113 156 if (PHP_OS_FAMILY !== 'Windows' && function_exists('shell_exec') && !in_array('shell_exec', $disabled, true) && !ini_get('open_basedir')) { 114 157 $val = trim((string) @shell_exec('getconf _NPROCESSORS_ONLN 2>/dev/null')); … … 119 162 } 120 163 } 164 121 165 if (PHP_OS_FAMILY === 'Windows' && !ini_get('open_basedir')) { 122 $csv = dfehc_exec_with_timeout('typeperf -sc 1 "\Processor(_Total)\% Processor Time" 2>NUL', 1.5);123 if ($csv !== '') {124 $wval = dfehc_exec_with_timeout('wmic cpu get NumberOfLogicalProcessors /value 2>NUL', 1.0);125 if ($wval && preg_match('/NumberOfLogicalProcessors=(\d+)/i', $wval, $m) && (int) $m[1] > 0) {126 $cores = (int) $m[1];127 dfehc_set_transient_noautoload($tkey, $cores, (int) apply_filters('dfehc_cpu_cores_ttl', DAY_IN_SECONDS));128 return $cached = $cores;129 }130 }131 166 $wout = dfehc_exec_with_timeout('wmic cpu get NumberOfLogicalProcessors /value 2>NUL', 1.0); 132 if ($wout && preg_match('/NumberOfLogicalProcessors=(\d+)/i', $wout, $m 2) && (int) $m2[1] > 0) {133 $cores = (int) $m 2[1];167 if ($wout && preg_match('/NumberOfLogicalProcessors=(\d+)/i', $wout, $m) && (int) $m[1] > 0) { 168 $cores = (int) $m[1]; 134 169 dfehc_set_transient_noautoload($tkey, $cores, (int) apply_filters('dfehc_cpu_cores_ttl', DAY_IN_SECONDS)); 135 170 return $cached = $cores; 136 171 } 137 172 } 173 138 174 if (is_readable('/proc/cpuinfo')) { 139 175 $cnt = preg_match_all('/^processor/m', (string) @file_get_contents('/proc/cpuinfo')); … … 144 180 } 145 181 } 182 146 183 dfehc_set_transient_noautoload($tkey, 1, (int) apply_filters('dfehc_cpu_cores_ttl', DAY_IN_SECONDS)); 147 184 return $cached = 1; … … 153 190 $ttl = (int) apply_filters('dfehc_system_load_ttl', DFEHC_SERVER_LOAD_TTL); 154 191 $key = dfehc_scoped_key(DFEHC_SYSTEM_LOAD_KEY); 192 193 $as_percent = false; 194 155 195 if (function_exists('wp_using_ext_object_cache') && wp_using_ext_object_cache()) { 156 196 $vc = wp_cache_get($key, DFEHC_CACHE_GROUP); … … 161 201 } 162 202 } 203 163 204 $cache = get_transient($key); 164 205 if ($cache !== false) { … … 174 215 if (function_exists('dfehc_get_server_load')) { 175 216 $val = dfehc_get_server_load(); 176 if ($val !== DFEHC_SENTINEL_NO_LOAD) { 177 dfehc_set_transient_noautoload($key, (float) $val, $ttl); 178 $ratio = (float) $val; 179 $as_percent = (bool) apply_filters('dfehc_system_load_return_percent', false, $ratio); 180 return $as_percent ? (float) round($ratio * 100, 2) : $ratio; 181 } 182 } 183 184 if (function_exists('sys_getloadavg')) { 217 if (is_numeric($val)) { 218 $v = (float) $val; 219 if ($v !== (float) DFEHC_SENTINEL_NO_LOAD && $v >= 0.0) { 220 if ($v > 1.0) { 221 $raw = $v / 100.0; 222 $normalized_ratio = true; 223 $source = 'dfehc_get_server_load_percent'; 224 } else { 225 $raw = $v; 226 $normalized_ratio = true; 227 $source = 'dfehc_get_server_load_ratio'; 228 } 229 } 230 } 231 } 232 233 if ($raw === null && function_exists('sys_getloadavg')) { 185 234 $arr = @sys_getloadavg(); 186 235 if ($arr && isset($arr[0])) { … … 255 304 if ($raw === null) { 256 305 $sentinel_ttl = (int) apply_filters('dfehc_sentinel_ttl', 5); 257 dfehc_set_transient_noautoload($key, DFEHC_SENTINEL_NO_LOAD, $sentinel_ttl);306 dfehc_set_transient_noautoload($key, (float) DFEHC_SENTINEL_NO_LOAD, $sentinel_ttl); 258 307 $ratio = (float) DFEHC_SENTINEL_NO_LOAD; 259 308 $as_percent = (bool) apply_filters('dfehc_system_load_return_percent', false, $ratio); … … 273 322 } 274 323 324 if (!is_finite($ratio)) { 325 $ratio = (float) DFEHC_SENTINEL_NO_LOAD; 326 } 327 275 328 dfehc_set_transient_noautoload($key, $ratio, $ttl); 329 276 330 $as_percent = (bool) apply_filters('dfehc_system_load_return_percent', false, $ratio); 277 331 return $as_percent ? (float) round($ratio * 100, 2) : (float) $ratio; -
dynamic-front-end-heartbeat-control/trunk/heartbeat-async.php
r3412936 r3424156 9 9 if (!defined('DFEHC_CACHE_GROUP')) define('DFEHC_CACHE_GROUP', 'dfehc'); 10 10 11 function dfehc_max_server_load(): int { static $v; if ($v === null) { $v = (int) apply_filters('dfehc_max_server_load', 85); } return $v; } 12 function dfehc_min_interval(): int { static $v; if ($v === null) { $v = (int) apply_filters('dfehc_min_interval', 15); } return $v; } 13 function dfehc_max_interval(): int { static $v; if ($v === null) { $v = (int) apply_filters('dfehc_max_interval', 300); } return $v; } 14 function dfehc_fallback_interval(): int { static $v; if ($v === null) { $v = (int) apply_filters('dfehc_fallback_interval', 60); } return $v; } 11 if (!function_exists('dfehc_max_server_load')) { 12 function dfehc_max_server_load(): int { static $v; if ($v === null) { $v = (int) apply_filters('dfehc_max_server_load', 85); } return $v; } 13 } 14 if (!function_exists('dfehc_min_interval')) { 15 function dfehc_min_interval(): int { static $v; if ($v === null) { $v = (int) apply_filters('dfehc_min_interval', 15); } return $v; } 16 } 17 if (!function_exists('dfehc_max_interval')) { 18 function dfehc_max_interval(): int { static $v; if ($v === null) { $v = (int) apply_filters('dfehc_max_interval', 300); } return $v; } 19 } 20 if (!function_exists('dfehc_fallback_interval')) { 21 function dfehc_fallback_interval(): int { static $v; if ($v === null) { $v = (int) apply_filters('dfehc_fallback_interval', 60); } return $v; } 22 } 15 23 16 24 if (!function_exists('dfehc_host_token')) { 17 25 function dfehc_host_token(): string 18 26 { 27 static $t = ''; 28 if ($t !== '') return $t; 19 29 $host = @php_uname('n') ?: (defined('WP_HOME') ? WP_HOME : (function_exists('home_url') ? home_url() : 'unknown')); 20 30 $salt = defined('DB_NAME') ? (string) DB_NAME : ''; 21 return substr(md5((string) $host . $salt), 0, 10);31 return $t = substr(md5((string) $host . $salt), 0, 10); 22 32 } 23 33 } … … 72 82 dfehc_store_lockfree($key, $value, $expiration); 73 83 global $wpdb; 84 if (!isset($wpdb) || !is_object($wpdb) || !isset($wpdb->options)) { 85 return; 86 } 74 87 $opt_key = "_transient_$key"; 75 88 $opt_key_to = "_transient_timeout_$key"; … … 262 275 } 263 276 264 p rotectedfunction run_action(): void277 public function run_action(): void 265 278 { 266 279 $lock = dfehc_acquire_lock('dfehc_async_run', 30); … … 430 443 add_action('dfehc_prune_logs_hook', 'dfehc_prune_server_load_logs'); 431 444 432 if (!wp_next_scheduled('dfehc_prune_logs_hook')) { 445 function dfehc_schedule_prune_logs(): void 446 { 447 if (!function_exists('wp_next_scheduled') || !function_exists('wp_schedule_event')) { 448 return; 449 } 450 if (wp_next_scheduled('dfehc_prune_logs_hook')) { 451 return; 452 } 433 453 $t = strtotime('today 03:00'); 434 454 if ($t < time()) { … … 437 457 wp_schedule_event($t, 'dfehc_daily', 'dfehc_prune_logs_hook'); 438 458 } 459 460 add_action('init', 'dfehc_schedule_prune_logs', 1); 439 461 440 462 add_filter('dfehc_required_capability', function () { return 'manage_options'; }); -
dynamic-front-end-heartbeat-control/trunk/heartbeat-controller.php
r3412970 r3424156 4 4 Plugin URI: https://heartbeat.support 5 5 Description: An enhanced solution to optimize the performance of your WordPress website. Stabilize your website's load averages and enhance the browsing experience for visitors during high-traffic fluctuations. 6 Version: 1.2.99 6.26 Version: 1.2.997 7 7 Author: Codeloghin 8 8 Author URI: https://codeloghin.com … … 59 59 require_once DFEHC_PLUGIN_PATH . 'settings.php'; 60 60 61 function dfehc_scoped_tkey(string $base): string 62 { 63 if (function_exists('dfehc_scoped_key')) { 64 return dfehc_scoped_key($base); 65 } 66 $bid = function_exists('get_current_blog_id') ? (string) get_current_blog_id() : '0'; 67 $host = @php_uname('n') ?: (defined('WP_HOME') ? WP_HOME : (function_exists('home_url') ? home_url() : 'unknown')); 68 $salt = defined('DB_NAME') ? (string) DB_NAME : ''; 69 $tok = substr(md5((string) $host . $salt), 0, 10); 70 return "{$base}_{$bid}_{$tok}"; 71 } 72 73 function dfehc_set_transient_noautoload(string $key, $value, int $expiration): void 74 { 75 $jitter = function_exists('random_int') ? random_int(0, 5) : 0; 76 $expiration = max(1, $expiration + $jitter); 77 if (function_exists('wp_using_ext_object_cache') && wp_using_ext_object_cache()) { 78 wp_cache_set($key, $value, DFEHC_CACHE_GROUP, $expiration); 79 return; 80 } 81 set_transient($key, $value, $expiration); 82 if (isset($GLOBALS['wpdb'])) { 83 global $wpdb; 84 if (!isset($wpdb->options)) { 61 if (!function_exists('dfehc_scoped_tkey')) { 62 function dfehc_scoped_tkey(string $base): string 63 { 64 if (function_exists('dfehc_scoped_key')) { 65 return dfehc_scoped_key($base); 66 } 67 $bid = function_exists('get_current_blog_id') ? (string) get_current_blog_id() : '0'; 68 $host = @php_uname('n') ?: (defined('WP_HOME') ? WP_HOME : (function_exists('home_url') ? home_url() : 'unknown')); 69 $salt = defined('DB_NAME') ? (string) DB_NAME : ''; 70 $tok = substr(md5((string) $host . $salt), 0, 10); 71 return "{$base}_{$bid}_{$tok}"; 72 } 73 } 74 75 if (!function_exists('dfehc_set_transient_noautoload')) { 76 function dfehc_set_transient_noautoload(string $key, $value, int $expiration): void 77 { 78 $jitter = 0; 79 if (function_exists('random_int')) { 80 try { 81 $jitter = random_int(0, 5); 82 } catch (\Throwable $e) { 83 $jitter = 0; 84 } 85 } 86 $expiration = max(1, $expiration + $jitter); 87 88 if (function_exists('wp_using_ext_object_cache') && wp_using_ext_object_cache()) { 89 wp_cache_set($key, $value, DFEHC_CACHE_GROUP, $expiration); 85 90 return; 86 91 } 87 $opt_key_val = '_transient_' . $key; 88 $opt_key_to = '_transient_timeout_' . $key; 89 $wpdb->suppress_errors(true); 90 $autoload = $wpdb->get_var($wpdb->prepare("SELECT autoload FROM {$wpdb->options} WHERE option_name=%s LIMIT 1", $opt_key_val)); 91 if ($autoload === 'yes') { 92 $wpdb->query($wpdb->prepare("UPDATE {$wpdb->options} SET autoload='no' WHERE option_name=%s AND autoload='yes' LIMIT 1", $opt_key_val)); 93 } 94 $autoload_to = $wpdb->get_var($wpdb->prepare("SELECT autoload FROM {$wpdb->options} WHERE option_name=%s LIMIT 1", $opt_key_to)); 95 if ($autoload_to === 'yes') { 96 $wpdb->query($wpdb->prepare("UPDATE {$wpdb->options} SET autoload='no' WHERE option_name=%s AND autoload='yes' LIMIT 1", $opt_key_to)); 97 } 98 $wpdb->suppress_errors(false); 92 93 set_transient($key, $value, $expiration); 94 95 if (isset($GLOBALS['wpdb'])) { 96 global $wpdb; 97 if (!isset($wpdb) || !is_object($wpdb) || !isset($wpdb->options)) { 98 return; 99 } 100 $opt_key_val = '_transient_' . $key; 101 $opt_key_to = '_transient_timeout_' . $key; 102 $wpdb->suppress_errors(true); 103 $autoload = $wpdb->get_var($wpdb->prepare("SELECT autoload FROM {$wpdb->options} WHERE option_name=%s LIMIT 1", $opt_key_val)); 104 if ($autoload === 'yes') { 105 $wpdb->query($wpdb->prepare("UPDATE {$wpdb->options} SET autoload='no' WHERE option_name=%s AND autoload='yes' LIMIT 1", $opt_key_val)); 106 } 107 $autoload_to = $wpdb->get_var($wpdb->prepare("SELECT autoload FROM {$wpdb->options} WHERE option_name=%s LIMIT 1", $opt_key_to)); 108 if ($autoload_to === 'yes') { 109 $wpdb->query($wpdb->prepare("UPDATE {$wpdb->options} SET autoload='no' WHERE option_name=%s AND autoload='yes' LIMIT 1", $opt_key_to)); 110 } 111 $wpdb->suppress_errors(false); 112 } 99 113 } 100 114 } … … 223 237 class Dfehc_Get_Recommended_Heartbeat_Interval_Async extends Heartbeat_Async 224 238 { 239 protected $action = 'dfehc_get_recommended_interval_async'; 240 225 241 public function dispatch(): void 226 242 { 227 $this->action = 'dfehc_get_recommended_interval_async';228 229 243 if (has_action($this->action)) { 230 244 do_action($this->action); … … 236 250 } 237 251 238 p rotectedfunction run_action(): void252 public function run_action(): void 239 253 { 240 254 $lock = function_exists('dfehc_acquire_lock') ? dfehc_acquire_lock('dfehc_interval_calculation_lock', (int) DFEHC_LOCK_TTL) : null; … … 296 310 { 297 311 check_ajax_referer(DFEHC_NONCE_ACTION, 'nonce'); 298 $ip = (string) ($_SERVER['REMOTE_ADDR'] ?? '0.0.0.0');312 $ip = function_exists('dfehc_client_ip') ? (string) dfehc_client_ip() : (string) ($_SERVER['REMOTE_ADDR'] ?? '0.0.0.0'); 299 313 $rlk = dfehc_scoped_tkey('dfehc_rl_' . md5($ip)); 300 314 $cnt = (int) get_transient($rlk); -
dynamic-front-end-heartbeat-control/trunk/readme.txt
r3412883 r3424156 3 3 Tested up to: 6.9 4 4 Requires PHP: 7.2 5 Stable tag: 1.2.99 6.25 Stable tag: 1.2.997 6 6 License: GPLv2 or later 7 7 License URI: https://www.gnu.org/licenses/gpl-2.0.html … … 48 48 49 49 == Changelog == 50 51 = 1.2.997 = 52 53 * General enhancements. 50 54 51 55 = 1.2.996.2 = -
dynamic-front-end-heartbeat-control/trunk/visitor/cookie-helper.php
r3412283 r3424156 14 14 ]); 15 15 $tokens = array_map( 16 static fn(string $s): string => 17 '(?<![A-Za-z0-9])' . preg_quote($s, '/') . '(?![A-Za-z0-9])', 16 static function (string $s): string { 17 return '(?<![A-Za-z0-9])' . preg_quote($s, '/') . '(?![A-Za-z0-9])'; 18 }, 18 19 $sigs 19 20 ); … … 49 50 function dfehc_client_ip(): string 50 51 { 51 $ip = (string) ($_SERVER['REMOTE_ADDR'] ?? '0.0.0.0'); 52 return (string) apply_filters('dfehc_client_ip', $ip); 52 $remote = (string) ($_SERVER['REMOTE_ADDR'] ?? ''); 53 $remote = trim($remote); 54 55 $trustedCidrs = (array) apply_filters('dfehc_trusted_proxies', []); 56 $isTrustedRemote = false; 57 if ($remote !== '' && $trustedCidrs) { 58 foreach ($trustedCidrs as $cidr) { 59 if (is_string($cidr) && dfehc_ip_in_cidr($remote, $cidr)) { 60 $isTrustedRemote = true; 61 break; 62 } 63 } 64 } 65 66 $cand = null; 67 68 if ($isTrustedRemote) { 69 $headers = (array) apply_filters('dfehc_proxy_ip_headers', ['HTTP_X_FORWARDED_FOR', 'HTTP_CF_CONNECTING_IP', 'HTTP_X_REAL_IP']); 70 foreach ($headers as $h) { 71 if (empty($_SERVER[$h])) { 72 continue; 73 } 74 $raw = trim((string) $_SERVER[$h]); 75 if ($raw === '') { 76 continue; 77 } 78 if ($h === 'HTTP_X_FORWARDED_FOR') { 79 $picked = dfehc_select_client_ip_from_xff($raw, $trustedCidrs); 80 if ($picked !== null) { 81 $cand = $picked; 82 break; 83 } 84 continue; 85 } 86 $raw = preg_replace('/%[0-9A-Za-z.\-]+$/', '', $raw); 87 if ($raw !== '' && filter_var($raw, FILTER_VALIDATE_IP)) { 88 $cand = $raw; 89 break; 90 } 91 } 92 } 93 94 if ($cand === null) { 95 $cand = $remote !== '' && filter_var($remote, FILTER_VALIDATE_IP) ? $remote : '0.0.0.0'; 96 } 97 98 return (string) apply_filters('dfehc_client_ip', $cand); 53 99 } 54 100 } … … 73 119 74 120 global $wpdb; 75 if (!isset($wpdb ->options)) {121 if (!isset($wpdb) || !is_object($wpdb) || !isset($wpdb->options)) { 76 122 return; 77 123 } … … 79 125 $opt_key_to = "_transient_timeout_$key"; 80 126 $wpdb->suppress_errors(true); 81 $autoload = $wpdb->get_var($wpdb->prepare("SELECT autoload FROM {$wpdb->options} WHERE option_name=%s LIMIT 1", $opt_key)); 82 if ($autoload === 'yes') { 83 $wpdb->update( 84 $wpdb->options, 85 ['autoload' => 'no'], 86 ['option_name' => $opt_key, 'autoload' => 'yes'], 87 ['%s'], 88 ['%s', '%s'] 89 ); 90 } 91 $autoload_to = $wpdb->get_var($wpdb->prepare("SELECT autoload FROM {$wpdb->options} WHERE option_name=%s LIMIT 1", $opt_key_to)); 92 if ($autoload_to === 'yes') { 93 $wpdb->update( 94 $wpdb->options, 95 ['autoload' => 'no'], 96 ['option_name' => $opt_key_to, 'autoload' => 'yes'], 97 ['%s'], 98 ['%s', '%s'] 99 ); 100 } 101 $wpdb->suppress_errors(false); 127 try { 128 $autoload = $wpdb->get_var($wpdb->prepare("SELECT autoload FROM {$wpdb->options} WHERE option_name=%s LIMIT 1", $opt_key)); 129 if ($autoload === 'yes') { 130 $wpdb->update( 131 $wpdb->options, 132 ['autoload' => 'no'], 133 ['option_name' => $opt_key, 'autoload' => 'yes'], 134 ['%s'], 135 ['%s', '%s'] 136 ); 137 } 138 $autoload_to = $wpdb->get_var($wpdb->prepare("SELECT autoload FROM {$wpdb->options} WHERE option_name=%s LIMIT 1", $opt_key_to)); 139 if ($autoload_to === 'yes') { 140 $wpdb->update( 141 $wpdb->options, 142 ['autoload' => 'no'], 143 ['option_name' => $opt_key_to, 'autoload' => 'yes'], 144 ['%s'], 145 ['%s', '%s'] 146 ); 147 } 148 } finally { 149 $wpdb->suppress_errors(false); 150 } 102 151 } 103 152 } … … 263 312 if ($ip && function_exists('wp_using_ext_object_cache') && wp_using_ext_object_cache() && function_exists('wp_cache_incr')) { 264 313 $rpmKey = dfehc_scoped_key('dfehc_iprpm_') . md5($ip); 265 $rpmTtl = 60 + (function_exists('random_int') ? random_int(0, 5) : 0); 314 $rpmTtl = 60; 315 if (function_exists('random_int')) { 316 try { 317 $rpmTtl += random_int(0, 5); 318 } catch (\Throwable $e) { 319 } 320 } 266 321 if (false === wp_cache_add($rpmKey, 0, $group, $rpmTtl)) { 267 322 wp_cache_set($rpmKey, (int) (wp_cache_get($rpmKey, $group) ?: 0), $group, $rpmTtl); … … 305 360 $existing = $_COOKIE[$name] ?? ''; 306 361 $val = $existing; 362 307 363 if (!preg_match('/^[A-Fa-f0-9]{32,64}$/', (string) $val)) { 308 364 if (function_exists('random_bytes')) { 309 $val = bin2hex(random_bytes(16)); 365 try { 366 $val = bin2hex(random_bytes(16)); 367 } catch (\Throwable $e) { 368 $val = bin2hex(pack('H*', substr(sha1(uniqid((string) mt_rand(), true)), 0, 32))); 369 } 310 370 } else { 311 371 $val = bin2hex(pack('H*', substr(sha1(uniqid((string) mt_rand(), true)), 0, 32))); … … 346 406 347 407 if (function_exists('wp_using_ext_object_cache') && wp_using_ext_object_cache() && function_exists('wp_cache_incr')) { 348 $vTtl = $lifetime + (function_exists('random_int') ? random_int(0, 5) : 0); 408 $vTtl = $lifetime; 409 if (function_exists('random_int')) { 410 try { 411 $vTtl += random_int(0, 5); 412 } catch (\Throwable $e) { 413 } 414 } 349 415 if (false === wp_cache_add($scopedVisitorKey, 0, $group, $vTtl)) { 350 416 wp_cache_set($scopedVisitorKey, (int) (wp_cache_get($scopedVisitorKey, $group) ?: 0), $group, $vTtl); … … 393 459 } 394 460 if ($client) { 395 $client->incr($scopedVisitorKey); 396 $client->expire($scopedVisitorKey, $lifetime); 397 return; 461 try { 462 $client->incr($scopedVisitorKey); 463 $client->expire($scopedVisitorKey, $lifetime); 464 return; 465 } catch (\Throwable $e) { 466 } 398 467 } 399 468 … … 412 481 } 413 482 if ($mem) { 414 $valInc = $mem->increment($scopedVisitorKey, 1); 415 if ($valInc === false) { 416 $mem->set($scopedVisitorKey, 1, $lifetime); 417 } else { 418 $mem->touch($scopedVisitorKey, $lifetime); 419 } 420 return; 483 try { 484 $valInc = $mem->increment($scopedVisitorKey, 1); 485 if ($valInc === false) { 486 $mem->set($scopedVisitorKey, 1, $lifetime); 487 } else { 488 if (method_exists($mem, 'touch')) { 489 $mem->touch($scopedVisitorKey, $lifetime); 490 } else { 491 $mem->set($scopedVisitorKey, (int) $valInc, $lifetime); 492 } 493 } 494 return; 495 } catch (\Throwable $e) { 496 } 421 497 } 422 498 423 499 $cnt = (int) get_transient($scopedVisitorKey); 424 $vTtl = $lifetime + (function_exists('random_int') ? random_int(0, 5) : 0); 500 $vTtl = $lifetime; 501 if (function_exists('random_int')) { 502 try { 503 $vTtl += random_int(0, 5); 504 } catch (\Throwable $e) { 505 } 506 } 425 507 dfehc_set_transient_noautoload($scopedVisitorKey, $cnt + 1, $vTtl); 426 508 } -
dynamic-front-end-heartbeat-control/trunk/visitor/manager.php
r3412283 r3424156 24 24 function dfehc_set_transient_noautoload(string $key, $value, int $expiration): void { 25 25 $group = defined('DFEHC_CACHE_GROUP') ? DFEHC_CACHE_GROUP : 'dfehc'; 26 26 27 if (function_exists('wp_using_ext_object_cache') && wp_using_ext_object_cache()) { 27 28 if (function_exists('wp_cache_add')) { … … 34 35 return; 35 36 } 37 36 38 set_transient($key, $value, $expiration); 39 37 40 global $wpdb; 38 if (!isset($wpdb ->options)) return;41 if (!isset($wpdb) || !is_object($wpdb) || !isset($wpdb->options)) return; 39 42 $opt_key = "_transient_$key"; 40 43 $opt_key_to = "_transient_timeout_$key"; 41 44 $wpdb->suppress_errors(true); 42 $autoload = $wpdb->get_var($wpdb->prepare("SELECT autoload FROM {$wpdb->options} WHERE option_name=%s LIMIT 1", $opt_key)); 43 if ($autoload === 'yes') { 44 $wpdb->update($wpdb->options, ['autoload' => 'no'], ['option_name' => $opt_key, 'autoload' => 'yes'], ['%s'], ['%s','%s']); 45 } 46 $autoload_to = $wpdb->get_var($wpdb->prepare("SELECT autoload FROM {$wpdb->options} WHERE option_name=%s LIMIT 1", $opt_key_to)); 47 if ($autoload_to === 'yes') { 48 $wpdb->update($wpdb->options, ['autoload' => 'no'], ['option_name' => $opt_key_to, 'autoload' => 'yes'], ['%s'], ['%s','%s']); 49 } 50 $wpdb->suppress_errors(false); 45 try { 46 $autoload = $wpdb->get_var($wpdb->prepare("SELECT autoload FROM {$wpdb->options} WHERE option_name=%s LIMIT 1", $opt_key)); 47 if ($autoload === 'yes') { 48 $wpdb->update($wpdb->options, ['autoload' => 'no'], ['option_name' => $opt_key, 'autoload' => 'yes'], ['%s'], ['%s','%s']); 49 } 50 $autoload_to = $wpdb->get_var($wpdb->prepare("SELECT autoload FROM {$wpdb->options} WHERE option_name=%s LIMIT 1", $opt_key_to)); 51 if ($autoload_to === 'yes') { 52 $wpdb->update($wpdb->options, ['autoload' => 'no'], ['option_name' => $opt_key_to, 'autoload' => 'yes'], ['%s'], ['%s','%s']); 53 } 54 } finally { 55 $wpdb->suppress_errors(false); 56 } 51 57 } 52 58 } … … 98 104 return null; 99 105 } 106 } 107 if (!function_exists('dfehc_release_lock')) { 100 108 function dfehc_release_lock($lock): void { 101 109 if ($lock instanceof WP_Lock) { … … 105 113 if (is_object($lock) && isset($lock->cache_key)) { 106 114 $group = $lock->cache_group ?? apply_filters('dfehc_cache_group', defined('DFEHC_CACHE_GROUP') ? DFEHC_CACHE_GROUP : 'dfehc'); 107 wp_cache_delete($lock->cache_key, $group); 115 if (function_exists('wp_cache_delete')) { 116 wp_cache_delete($lock->cache_key, $group); 117 } 108 118 return; 109 119 } … … 121 131 122 132 function dfehc_add_intervals(array $s): array { 123 $s['dfehc_5_minutes'] ??= ['interval' => 300, 'display' => __('Every 5 minutes (DFEHC)', 'dfehc')]; 124 $s['dfehc_daily'] ??= ['interval' => DAY_IN_SECONDS, 'display' => __('Daily (DFEHC)', 'dfehc')]; 133 if (!isset($s['dfehc_5_minutes'])) { 134 $s['dfehc_5_minutes'] = ['interval' => 300, 'display' => __('Every 5 minutes (DFEHC)', 'dfehc')]; 135 } 136 if (!isset($s['dfehc_daily'])) { 137 $s['dfehc_daily'] = ['interval' => DAY_IN_SECONDS, 'display' => __('Daily (DFEHC)', 'dfehc')]; 138 } 125 139 return $s; 126 140 } … … 129 143 function dfehc_schedule_user_activity_processing(): void { 130 144 $lock = dfehc_acquire_lock('dfehc_cron_sched_lock', 15); 145 if (!$lock) { 146 return; 147 } 148 131 149 $aligned = time() - time() % 300 + 300; 132 if (!get_option('dfehc_activity_cron_scheduled') && !wp_next_scheduled('dfehc_process_user_activity')) { 133 $ok = wp_schedule_event($aligned, 'dfehc_5_minutes', 'dfehc_process_user_activity'); 134 if ($ok) { 135 update_option('dfehc_activity_cron_scheduled', 1, false); 136 } else { 137 wp_schedule_single_event($aligned, 'dfehc_process_user_activity'); 138 } 139 } 140 if (!wp_next_scheduled('dfehc_cleanup_user_activity')) { 141 $args = [0, (int) apply_filters('dfehc_cleanup_batch_size', 75)]; 142 $ok2 = wp_schedule_event($aligned + 300, 'dfehc_daily', 'dfehc_cleanup_user_activity', $args); 143 if (!$ok2) { 144 wp_schedule_single_event($aligned + 300, 'dfehc_cleanup_user_activity', $args); 145 } 146 } 147 if ($lock) { 150 151 try { 152 if (!get_option('dfehc_activity_cron_scheduled') && !wp_next_scheduled('dfehc_process_user_activity')) { 153 $ok = wp_schedule_event($aligned, 'dfehc_5_minutes', 'dfehc_process_user_activity'); 154 if ($ok && !is_wp_error($ok)) { 155 update_option('dfehc_activity_cron_scheduled', 1, false); 156 } else { 157 wp_schedule_single_event($aligned, 'dfehc_process_user_activity'); 158 } 159 } 160 161 if (!wp_next_scheduled('dfehc_cleanup_user_activity')) { 162 $args = [0, (int) apply_filters('dfehc_cleanup_batch_size', 75)]; 163 $ok2 = wp_schedule_event($aligned + 300, 'dfehc_daily', 'dfehc_cleanup_user_activity', $args); 164 if (!$ok2 || is_wp_error($ok2)) { 165 wp_schedule_single_event($aligned + 300, 'dfehc_cleanup_user_activity', $args); 166 } 167 } 168 } finally { 148 169 dfehc_release_lock($lock); 149 170 } … … 157 178 } 158 179 159 $prev = (bool) ignore_user_abort(true);180 $prev = (bool) ignore_user_abort(true); 160 181 161 182 try { … … 174 195 } 175 196 global $wpdb; 197 if (!isset($wpdb) || !is_object($wpdb) || !isset($wpdb->users)) { 198 return; 199 } 176 200 $meta_key = (string) apply_filters('dfehc_last_activity_meta_key', 'last_activity_time'); 177 201 $batch = (int) apply_filters('dfehc_activity_processing_batch_size', 75); … … 193 217 $max_writes = (int) apply_filters('dfehc_activity_max_writes_per_run', 500); 194 218 foreach ($ids as $id) { 195 if (!get_user_meta( $id, $meta_key, true)) {196 update_user_meta( $id, $meta_key, $now);219 if (!get_user_meta((int) $id, $meta_key, true)) { 220 update_user_meta((int) $id, $meta_key, $now); 197 221 $written++; 198 222 if ($written >= $max_writes) { … … 201 225 } 202 226 } 203 update_option($last_id_opt, end($ids), false); 227 $last = end($ids); 228 update_option($last_id_opt, $last ? (int) $last : (int) $last_id, false); 204 229 update_option(dfehc_scoped_key('dfehc_last_activity_cron'), time(), false); 205 230 } … … 211 236 static $cache = []; 212 237 $meta_key = (string) apply_filters('dfehc_last_activity_meta_key', 'last_activity_time'); 213 $uid = get_current_user_id(); 238 $uid = (int) get_current_user_id(); 239 if ($uid <= 0) { 240 return; 241 } 214 242 $now = time(); 215 243 $interval = (int) apply_filters('dfehc_activity_update_interval', 900); … … 229 257 } 230 258 231 $prev = (bool) ignore_user_abort(true);259 $prev = (bool) ignore_user_abort(true); 232 260 233 261 if (function_exists('set_time_limit')) { … … 237 265 try { 238 266 global $wpdb; 267 if (!isset($wpdb) || !is_object($wpdb) || !isset($wpdb->users)) { 268 return; 269 } 239 270 $meta_key = (string) apply_filters('dfehc_last_activity_meta_key', 'last_activity_time'); 240 271 $batch_size = (int) apply_filters('dfehc_cleanup_batch_size', $batch_size); … … 244 275 $wpdb->prepare( 245 276 "SELECT ID FROM $wpdb->users WHERE ID > %d ORDER BY ID ASC LIMIT %d", 246 $last_id,247 $batch_size277 (int) $last_id, 278 (int) $batch_size 248 279 ) 249 280 ); … … 257 288 258 289 foreach ($ids as $id) { 290 $id = (int) $id; 259 291 $ts = (int) get_user_meta($id, $meta_key, true); 260 292 if ($ts && $ts < $cutoff) { … … 264 296 265 297 if (count($ids) === $batch_size) { 298 $delay = 15; 299 if (function_exists('random_int')) { 300 try { 301 $delay += random_int(0, 5); 302 } catch (\Throwable $e) { 303 $delay += rand(0, 5); 304 } 305 } else { 306 $delay += rand(0, 5); 307 } 266 308 wp_schedule_single_event( 267 time() + 15 + (function_exists('random_int') ? random_int(0, 5) : rand(0, 5)),309 time() + $delay, 268 310 'dfehc_cleanup_user_activity', 269 [ end($ids),$batch_size]311 [(int) end($ids), (int) $batch_size] 270 312 ); 271 313 } … … 274 316 275 317 } finally { 276 277 ignore_user_abort((bool) $prev); 278 318 ignore_user_abort((bool) $prev); 279 319 dfehc_release_lock($lock); 280 320 } … … 286 326 $grp = apply_filters('dfehc_cache_group', defined('DFEHC_CACHE_GROUP') ? DFEHC_CACHE_GROUP : 'dfehc'); 287 327 $ttl = (int) apply_filters('dfehc_total_visitors_ttl', HOUR_IN_SECONDS); 288 $ttl += function_exists('random_int') ? random_int(0, 5) : 0; 328 if (function_exists('random_int')) { 329 try { 330 $ttl += random_int(0, 5); 331 } catch (\Throwable $e) { 332 } 333 } 289 334 290 335 if (function_exists('wp_using_ext_object_cache') && wp_using_ext_object_cache() && function_exists('wp_cache_incr')) { … … 327 372 } 328 373 if ($redis) { 329 $redis->incr($key); 330 $redis->expire($key, $ttl); 331 return; 374 try { 375 $redis->incr($key); 376 $redis->expire($key, $ttl); 377 return; 378 } catch (\Throwable $e) { 379 } 332 380 } 333 381 … … 343 391 } 344 392 if ($mem) { 345 $inc = $mem->increment($key, 1); 346 if ($inc === false) { 347 $mem->set($key, 1, $ttl); 348 } else { 349 $mem->touch($key, $ttl); 350 } 351 return; 393 try { 394 $inc = $mem->increment($key, 1); 395 if ($inc === false) { 396 $mem->set($key, 1, $ttl); 397 } else { 398 if (method_exists($mem, 'touch')) { 399 $mem->touch($key, $ttl); 400 } else { 401 $mem->set($key, (int) $inc, $ttl); 402 } 403 } 404 return; 405 } catch (\Throwable $e) { 406 } 352 407 } 353 408 354 409 $cnt = (int) get_transient($key); 355 if ($cnt === 0) {356 dfehc_set_transient_noautoload($key, 0, $ttl);357 $cnt = 0;358 }359 410 dfehc_set_transient_noautoload($key, $cnt + 1, $ttl); 360 411 } … … 396 447 } 397 448 398 $regen_ttl = MINUTE_IN_SECONDS + (function_exists('random_int') ? random_int(0, 5) : 0); 449 $regen_ttl = (int) MINUTE_IN_SECONDS; 450 if (function_exists('random_int')) { 451 try { 452 $regen_ttl += random_int(0, 5); 453 } catch (\Throwable $e) { 454 } 455 } 399 456 dfehc_set_transient_noautoload($regen_key, true, $regen_ttl); 457 400 458 $total = dfehc_safe_cache_get('dfehc_total_visitors'); 459 401 460 $ttl = (int) apply_filters('dfehc_visitors_cache_ttl', 10 * MINUTE_IN_SECONDS); 402 $ttl += function_exists('random_int') ? random_int(0, 5) : 0; 461 if (function_exists('random_int')) { 462 try { 463 $ttl += random_int(0, 5); 464 } catch (\Throwable $e) { 465 } 466 } 403 467 dfehc_set_transient_noautoload($cache_key, (int) $total, $ttl); 404 468 update_option($stale_opt, (int) $total, false); … … 453 517 if (!wp_next_scheduled('dfehc_reset_total_visitors_event')) { 454 518 $ok = wp_schedule_event($aligned + HOUR_IN_SECONDS, 'hourly', 'dfehc_reset_total_visitors_event'); 455 if (!$ok ) {519 if (!$ok || is_wp_error($ok)) { 456 520 wp_schedule_single_event($aligned + HOUR_IN_SECONDS, 'dfehc_reset_total_visitors_event'); 457 521 } … … 473 537 $ts = $aligned + HOUR_IN_SECONDS; 474 538 $ok = wp_schedule_event($ts, 'hourly', 'dfehc_reset_total_visitors_event'); 475 if (!$ok ) {539 if (!$ok || is_wp_error($ok)) { 476 540 wp_schedule_single_event($ts, 'dfehc_reset_total_visitors_event'); 477 541 } -
dynamic-front-end-heartbeat-control/trunk/widget.php
r3303997 r3424156 26 26 if ($heartbeat_status === false) { 27 27 $heartbeat_status = get_option('dfehc_disable_heartbeat') ? 'Stopped' : dfehc_get_server_health_status($server_load); 28 set_transient('dfehc_heartbeat_health_status', $heartbeat_status, 60);28 set_transient('dfehc_heartbeat_health_status', $heartbeat_status, 20); 29 29 } 30 30 … … 59 59 } 60 60 61 $min_interval = (int) get_option('dfehc_min_interval', 15); 62 $max_interval = (int) get_option('dfehc_max_interval', 300); 63 $max_response_time = (float) get_option('dfehc_max_response_time', 5.0); 64 $ema_alpha = (float) get_option('dfehc_ema_alpha', 0.35); 65 66 $min_interval = max(1, $min_interval); 67 $max_interval = max($min_interval, $max_interval); 68 $max_response_time = max(0.1, $max_response_time); 69 $ema_alpha = max(0.01, min(1.0, $ema_alpha)); 70 71 $glow_rgb = '0, 204, 0'; 72 if ($status_color === 'yellow') $glow_rgb = '255, 204, 0'; 73 elseif ($status_color === 'red') $glow_rgb = '204, 0, 0'; 74 elseif ($status_color === 'black') $glow_rgb = '0, 0, 0'; 75 76 $server_load_text = ''; 77 if (is_numeric($server_load)) { 78 $server_load_text = (string) round((float) $server_load, 2); 79 } elseif (is_scalar($server_load) && $server_load !== null) { 80 $server_load_text = (string) $server_load; 81 } else { 82 $server_load_text = function_exists('wp_json_encode') ? (string) wp_json_encode($server_load) : (string) json_encode($server_load); 83 } 84 85 $response_seconds = 0.0; 86 if (is_numeric($server_response_time)) { 87 $response_seconds = (float) $server_response_time; 88 } elseif (is_array($server_response_time)) { 89 if (isset($server_response_time['main_response_ms']) && is_numeric($server_response_time['main_response_ms'])) { 90 $response_seconds = ((float) $server_response_time['main_response_ms']) / 1000.0; 91 } elseif (isset($server_response_time['response_time']) && is_numeric($server_response_time['response_time'])) { 92 $response_seconds = (float) $server_response_time['response_time']; 93 } elseif (isset($server_response_time['response_ms']) && is_numeric($server_response_time['response_ms'])) { 94 $response_seconds = ((float) $server_response_time['response_ms']) / 1000.0; 95 } 96 } elseif (is_object($server_response_time)) { 97 $arr = (array) $server_response_time; 98 if (isset($arr['main_response_ms']) && is_numeric($arr['main_response_ms'])) { 99 $response_seconds = ((float) $arr['main_response_ms']) / 1000.0; 100 } elseif (isset($arr['response_time']) && is_numeric($arr['response_time'])) { 101 $response_seconds = (float) $arr['response_time']; 102 } elseif (isset($arr['response_ms']) && is_numeric($arr['response_ms'])) { 103 $response_seconds = ((float) $arr['response_ms']) / 1000.0; 104 } 105 } 106 $response_seconds = max(0.0, $response_seconds); 107 $response_display = (string) round($response_seconds, 3); 108 109 $recommended_interval_text = (string) round((float) $recommended_interval, 2); 110 111 $ajax_url = function_exists('admin_url') ? admin_url('admin-ajax.php') : ''; 112 $ajax_nonce = function_exists('wp_create_nonce') ? wp_create_nonce('dfehc_widget_stats') : ''; 113 61 114 echo "<style> 62 115 .heartbeat { … … 87 140 0%, 100% { box-shadow: 0 0 5px {$status_color}, 0 0 10px {$status_color}; } 88 141 } 142 143 .dfehc-pulse-wrap { 144 --size: 30px; 145 --pulse-color: {$status_color}; 146 --glow-rgb: {$glow_rgb}; 147 display: flex; 148 justify-content: center; 149 align-items: center; 150 margin: 20px auto; 151 width: var(--size); 152 height: var(--size); 153 } 154 155 .dfehc-pulse { 156 position: relative; 157 width: var(--size); 158 height: var(--size); 159 border-radius: 50%; 160 background: var(--pulse-color); 161 overflow: hidden; 162 animation: dfehc-heartbeat 2s ease-in-out infinite, {$animation_name} 2s linear infinite; 163 } 164 165 .dfehc-pulse::before { 166 content: ''; 167 position: absolute; 168 top: 50%; 169 left: 50%; 170 width: 190%; 171 height: 190%; 172 transform: translate(-50%, -50%) scale(0.9); 173 border-radius: 50%; 174 background: radial-gradient(circle, rgba(var(--glow-rgb), 0.22) 0%, rgba(var(--glow-rgb), 0.10) 34%, rgba(var(--glow-rgb), 0) 70%); 175 pointer-events: none; 176 opacity: 0.55; 177 filter: blur(0.2px); 178 animation: dfehc-glow-sync 2s ease-in-out infinite; 179 } 180 181 .dfehc-spark { 182 position: absolute; 183 top: 50%; 184 left: 50%; 185 width: 4%; 186 height: 4%; 187 border-radius: 50%; 188 background: radial-gradient(circle at center, #ffffff 0%, #eaffea 70%, #d4ffd4 100%); 189 box-shadow: 0 0 12px 2px #ffffff, 0 0 24px 6px rgba(var(--glow-rgb), 0.35); 190 transform-origin: 0 0; 191 animation: dfehc-orbit var(--duration,6s) linear forwards, dfehc-flash var(--duration,6s) ease-in-out forwards; 192 pointer-events: none; 193 } 194 195 @keyframes dfehc-heartbeat { 196 0% { transform: scale(1); } 197 25% { transform: scale(1.1); } 198 50% { transform: scale(0.96); } 199 75% { transform: scale(1.05); } 200 100% { transform: scale(1); } 201 } 202 203 @keyframes dfehc-glow-sync { 204 0% { transform: translate(-50%, -50%) scale(0.92); opacity: 0.35; } 205 18% { transform: translate(-50%, -50%) scale(1.18); opacity: 0.90; } 206 34% { transform: translate(-50%, -50%) scale(1.02); opacity: 0.55; } 207 208 64% { transform: translate(-50%, -50%) scale(1.12); opacity: 0.78; } 209 100% { transform: translate(-50%, -50%) scale(0.92); opacity: 0.35; } 210 } 211 212 @keyframes dfehc-orbit { 213 from { transform: rotate(0deg) translate(var(--radius, 0px)) scale(1); } 214 to { transform: rotate(360deg) translate(var(--radius, 0px)) scale(1); } 215 } 216 217 @keyframes dfehc-flash { 218 0%, 95%, 100% { opacity: 0; box-shadow: 0 0 8px 2px #ffffff, 0 0 16px 4px rgba(var(--glow-rgb), 0.30); } 219 45%, 55% { opacity: 0.7; box-shadow: 0 0 14px 3px #ffffff, 0 0 24px 6px rgba(var(--glow-rgb), 0.45); } 220 } 221 222 .dfehc-matrix { 223 width: 100%; 224 max-width: 520px; 225 margin: 14px auto 0; 226 border-radius: 10px; 227 border: 1px solid rgba(0,0,0,0.10); 228 background: rgba(255,255,255,0.75); 229 box-shadow: 0 8px 20px rgba(0,0,0,0.06); 230 overflow: hidden; 231 cursor: pointer; 232 user-select: none; 233 -webkit-tap-highlight-color: transparent; 234 transition: transform 120ms ease, box-shadow 120ms ease, background 120ms ease, opacity 120ms ease; 235 } 236 237 .dfehc-matrix:hover { 238 box-shadow: 0 10px 24px rgba(0,0,0,0.08); 239 transform: translateY(-1px); 240 } 241 242 .dfehc-matrix:active { 243 transform: translateY(0px) scale(0.99); 244 opacity: 0.98; 245 } 246 247 .dfehc-matrix.is-loading { 248 opacity: 0.65; 249 } 250 251 .dfehc-matrix-inner { 252 display: grid; 253 grid-template-columns: 1fr 1fr 1fr; 254 gap: 0; 255 } 256 257 .dfehc-matrix-cell { 258 padding: 10px 12px; 259 text-align: center; 260 } 261 262 .dfehc-matrix-cell + .dfehc-matrix-cell { 263 border-left: 1px solid rgba(0,0,0,0.08); 264 } 265 .dfehc-pulse.dfehc-ack { 266 animation-duration: 1.35s, 1.35s; 267 } 268 269 .dfehc-matrix-label { 270 font-size: 11px; 271 letter-spacing: 0.08em; 272 text-transform: uppercase; 273 color: rgba(0,0,0,0.55); 274 line-height: 1.2; 275 margin-bottom: 6px; 276 } 277 278 .dfehc-matrix-value { 279 font-size: 16px; 280 font-weight: 700; 281 color: #111; 282 line-height: 1.2; 283 font-variant-numeric: tabular-nums; 284 word-break: break-word; 285 } 286 287 .dfehc-matrix-unit { 288 font-size: 11px; 289 font-weight: 600; 290 color: rgba(0,0,0,0.55); 291 margin-left: 6px; 292 } 293 294 @media (max-width: 520px) { 295 .dfehc-matrix-inner { 296 grid-template-columns: 1fr; 297 } 298 .dfehc-matrix-cell + .dfehc-matrix-cell { 299 border-left: none; 300 border-top: 1px solid rgba(0,0,0,0.08); 301 } 302 } 303 304 @media (prefers-reduced-motion: reduce) { 305 .dfehc-pulse, .dfehc-pulse::before, .dfehc-spark { animation: none !important; } 306 .dfehc-matrix { transition: none !important; } 307 } 89 308 </style>"; 90 309 91 echo "<p style='text-align: center; font-size: 24px; margin-top: 20px;'>Systolic Pressure: <strong>{$server_load}</strong></p>"; 92 echo "<div style='width: 30px; height: 30px; background-color: {$status_color}; margin: 20px auto; border-radius: 50%; animation: {$animation_name} 2s infinite;'></div>"; 310 echo "<div class='dfehc-pulse-wrap'><div class='dfehc-pulse' aria-label='Heartbeat status indicator' style='margin-top: 20px;'></div></div>"; 93 311 echo "<p style='text-align: center; font-size: 24px; margin-top: 20px;'>Heartbeat: <strong>{$heartbeat_status}</strong></p>"; 94 312 313 echo "<div class='dfehc-matrix' id='dfehc-matrix' role='button' tabindex='0' aria-label='Refresh current heartbeat metrics' style='margin-bottom: 20px;'> 314 <div class='dfehc-matrix-inner'> 315 <div class='dfehc-matrix-cell'> 316 <div class='dfehc-matrix-label'>Current Load</div> 317 <div class='dfehc-matrix-value' id='dfehc-stat-load'>{$server_load_text}</div> 318 </div> 319 <div class='dfehc-matrix-cell'> 320 <div class='dfehc-matrix-label'>Response Time</div> 321 <div class='dfehc-matrix-value'><span id='dfehc-stat-rt'>{$response_display}</span><span class='dfehc-matrix-unit'>s</span></div> 322 </div> 323 <div class='dfehc-matrix-cell'> 324 <div class='dfehc-matrix-label'>Interval</div> 325 <div class='dfehc-matrix-value'><span id='dfehc-stat-int'>{$recommended_interval_text}</span><span class='dfehc-matrix-unit'>s</span></div> 326 </div> 327 </div> 328 </div>"; 329 330 echo "<script> 331 (function() { 332 var pulse = document.querySelector('.dfehc-pulse'); 333 var box = document.getElementById('dfehc-matrix'); 334 var elLoad = document.getElementById('dfehc-stat-load'); 335 var elRt = document.getElementById('dfehc-stat-rt'); 336 var elInt = document.getElementById('dfehc-stat-int'); 337 338 if (pulse) { 339 pulse.style.cursor = 'pointer'; 340 pulse.setAttribute('role', 'button'); 341 pulse.setAttribute('tabindex', '0'); 342 pulse.setAttribute('aria-label', 'Refresh heartbeat animation'); 343 } 344 345 window.DFEHC_METRICS = window.DFEHC_METRICS || {}; 346 window.DFEHC_METRICS.recommended_interval = " . json_encode((float) $recommended_interval) . "; 347 window.DFEHC_METRICS.server_response_time = " . json_encode((float) $response_seconds) . "; 348 window.DFEHC_METRICS.min_interval = " . json_encode((int) $min_interval) . "; 349 window.DFEHC_METRICS.max_interval = " . json_encode((int) $max_interval) . "; 350 window.DFEHC_METRICS.max_response_time = " . json_encode((float) $max_response_time) . "; 351 window.DFEHC_METRICS.ema_alpha = " . json_encode((float) $ema_alpha) . "; 352 353 function clamp(v, lo, hi) { 354 v = Number(v); 355 if (!Number.isFinite(v)) return lo; 356 return Math.max(lo, Math.min(hi, v)); 357 } 358 359 function getSeed() { 360 try { 361 if (window.crypto && window.crypto.getRandomValues) { 362 var a = new Uint32Array(1); 363 window.crypto.getRandomValues(a); 364 return a[0] >>> 0; 365 } 366 } catch (e) {} 367 return (Date.now() >>> 0) ^ ((Math.random() * 0xffffffff) >>> 0); 368 } 369 370 function mulberry32(seed) { 371 var t = seed >>> 0; 372 return function() { 373 t += 0x6D2B79F5; 374 var x = t; 375 x = Math.imul(x ^ (x >>> 15), x | 1); 376 x ^= x + Math.imul(x ^ (x >>> 7), x | 61); 377 return ((x ^ (x >>> 14)) >>> 0) / 4294967296; 378 }; 379 } 380 381 var rng = mulberry32(getSeed()); 382 function rnd() { return rng(); } 383 384 function jitterFactor(pct) { 385 var p = clamp(pct, 0, 0.75); 386 return 1 + ((rnd() * 2 - 1) * p); 387 } 388 389 var emaStress = null; 390 function ema(next, alpha) { 391 var a = clamp(alpha, 0.01, 1.0); 392 if (emaStress === null) { 393 emaStress = next; 394 return emaStress; 395 } 396 emaStress = a * next + (1 - a) * emaStress; 397 return emaStress; 398 } 399 400 function getMetrics() { 401 var m = (window.DFEHC_METRICS && typeof window.DFEHC_METRICS === 'object') ? window.DFEHC_METRICS : {}; 402 var minInterval = clamp(m.min_interval != null ? m.min_interval : 15, 1, 3600); 403 var maxInterval = clamp(m.max_interval != null ? m.max_interval : 300, minInterval, 36000); 404 var recInterval = clamp(m.recommended_interval != null ? m.recommended_interval : maxInterval, minInterval, maxInterval); 405 406 var rt = Number(m.server_response_time != null ? m.server_response_time : 0); 407 if (!Number.isFinite(rt)) rt = 0; 408 409 var maxRT = clamp(m.max_response_time != null ? m.max_response_time : 5.0, 0.1, 120); 410 411 if (rt > (maxRT * 3) && rt <= 60000) rt = rt / 1000; 412 rt = clamp(rt, 0, 600); 413 414 return { minInterval: minInterval, maxInterval: maxInterval, recInterval: recInterval, rt: rt, maxRT: maxRT }; 415 } 416 417 function computeStress() { 418 var mm = getMetrics(); 419 var intervalNorm = (mm.recInterval - mm.minInterval) / Math.max(1e-9, (mm.maxInterval - mm.minInterval)); 420 intervalNorm = clamp(intervalNorm, 0, 1); 421 var activity = clamp(1 - intervalNorm, 0, 1); 422 var rtNorm = clamp(mm.rt / mm.maxRT, 0, 1); 423 424 var raw = clamp((0.65 * activity) + (0.35 * rtNorm), 0, 1); 425 var alpha = (window.DFEHC_METRICS && window.DFEHC_METRICS.ema_alpha != null) ? window.DFEHC_METRICS.ema_alpha : 0.35; 426 var smoothed = ema(raw, alpha); 427 428 return clamp(smoothed, 0, 1); 429 } 430 431 function pickSpawnDelayMs(stress) { 432 var minMs = 900; 433 var maxMs = 9500; 434 var base = maxMs - (stress * (maxMs - 1800)); 435 return clamp(base * jitterFactor(0.18), minMs, maxMs); 436 } 437 438 function pickSparkDurationSec(stress) { 439 var minS = 2.6; 440 var maxS = 8.4; 441 var base = maxS - (stress * (maxS - 3.2)); 442 return clamp(base * jitterFactor(0.12), minS, maxS); 443 } 444 445 function pickRadiusPx(stress) { 446 var w = pulse ? (pulse.clientWidth || 30) : 30; 447 var lo = 0.15; 448 var hi = 0.35; 449 var bias = lo + (hi - lo) * (0.25 + 0.75 * stress); 450 var pct = clamp(bias * jitterFactor(0.10), lo, hi); 451 return w * pct; 452 } 453 454 var sparkTimer = null; 455 var firstTimer = null; 456 var ackTimer = null; 457 458 function clearTimers() { 459 if (sparkTimer) { window.clearTimeout(sparkTimer); sparkTimer = null; } 460 if (firstTimer) { window.clearTimeout(firstTimer); firstTimer = null; } 461 if (ackTimer) { window.clearTimeout(ackTimer); ackTimer = null; } 462 } 463 464 function removeSparks() { 465 if (!pulse) return; 466 var sparks = pulse.querySelectorAll('.dfehc-spark'); 467 for (var i = 0; i < sparks.length; i++) { 468 if (sparks[i] && sparks[i].parentNode) sparks[i].parentNode.removeChild(sparks[i]); 469 } 470 } 471 472 function restartCssAnimations() { 473 if (!pulse) return; 474 var prev = pulse.style.animation; 475 pulse.style.animation = 'none'; 476 pulse.offsetHeight; 477 pulse.style.animation = prev || ''; 478 } 479 480 function ackBeat() { 481 if (!pulse) return; 482 pulse.classList.add('dfehc-ack'); 483 if (ackTimer) window.clearTimeout(ackTimer); 484 ackTimer = window.setTimeout(function() { 485 if (pulse) pulse.classList.remove('dfehc-ack'); 486 }, 220); 487 } 488 489 function spawnSpark() { 490 if (!pulse) return; 491 492 var stress = computeStress(); 493 494 var spark = document.createElement('span'); 495 spark.className = 'dfehc-spark'; 496 497 var duration = pickSparkDurationSec(stress).toFixed(2) + 's'; 498 var radiusPx = pickRadiusPx(stress).toFixed(2) + 'px'; 499 500 spark.style.setProperty('--duration', duration); 501 spark.style.setProperty('--radius', radiusPx); 502 503 pulse.appendChild(spark); 504 505 spark.addEventListener('animationend', function() { 506 if (spark && spark.parentNode) spark.parentNode.removeChild(spark); 507 }); 508 509 sparkTimer = window.setTimeout(spawnSpark, pickSpawnDelayMs(stress)); 510 } 511 512 function startSparks() { 513 if (!pulse) return; 514 clearTimers(); 515 var s = computeStress(); 516 var firstDelay = clamp((800 + (1 - s) * 2400) * jitterFactor(0.22), 400, 4200); 517 firstTimer = window.setTimeout(spawnSpark, firstDelay); 518 } 519 520 function refreshAnimation() { 521 rng = mulberry32(getSeed()); 522 emaStress = null; 523 removeSparks(); 524 restartCssAnimations(); 525 ackBeat(); 526 startSparks(); 527 } 528 529 startSparks(); 530 531 if (pulse) { 532 pulse.addEventListener('click', function() { 533 refreshAnimation(); 534 }); 535 536 pulse.addEventListener('keydown', function(e) { 537 var k = e.key || e.keyCode; 538 if (k === 'Enter' || k === ' ' || k === 13 || k === 32) { 539 e.preventDefault(); 540 refreshAnimation(); 541 } 542 }); 543 } 544 545 var ajaxUrl = " . json_encode((string) $ajax_url) . "; 546 var ajaxNonce = " . json_encode((string) $ajax_nonce) . "; 547 var inFlight = false; 548 549 function parseResponseSeconds(payload) { 550 if (payload == null) return 0; 551 if (typeof payload === 'number' && isFinite(payload)) return Math.max(0, payload); 552 if (typeof payload === 'string') { 553 var n = Number(payload); 554 if (isFinite(n)) return Math.max(0, n); 555 return 0; 556 } 557 if (typeof payload === 'object') { 558 if (payload.main_response_ms != null && isFinite(Number(payload.main_response_ms))) return Math.max(0, Number(payload.main_response_ms) / 1000); 559 if (payload.response_ms != null && isFinite(Number(payload.response_ms))) return Math.max(0, Number(payload.response_ms) / 1000); 560 if (payload.response_time != null && isFinite(Number(payload.response_time))) return Math.max(0, Number(payload.response_time)); 561 } 562 return 0; 563 } 564 565 function parseLoad(payload) { 566 if (payload == null) return ''; 567 if (typeof payload === 'number' && isFinite(payload)) return String(Math.round(payload * 100) / 100); 568 if (typeof payload === 'string') { 569 var n = Number(payload); 570 if (isFinite(n)) return String(Math.round(n * 100) / 100); 571 return payload; 572 } 573 if (typeof payload === 'object') { 574 if (payload.load != null && isFinite(Number(payload.load))) return String(Math.round(Number(payload.load) * 100) / 100); 575 if (payload.server_load != null && isFinite(Number(payload.server_load))) return String(Math.round(Number(payload.server_load) * 100) / 100); 576 } 577 try { return JSON.stringify(payload); } catch (e) { return ''; } 578 } 579 580 function parseInterval(payload) { 581 var n = Number(payload); 582 if (isFinite(n)) return Math.max(0, n); 583 return 0; 584 } 585 586 function updateUI(data) { 587 if (elLoad && data && data.server_load != null) elLoad.textContent = parseLoad(data.server_load); 588 if (elRt && data && data.server_response_time != null) { 589 var s = parseResponseSeconds(data.server_response_time); 590 elRt.textContent = (Math.round(s * 1000) / 1000).toFixed(3); 591 window.DFEHC_METRICS.server_response_time = s; 592 } 593 if (elInt && data && data.recommended_interval != null) { 594 var iv = parseInterval(data.recommended_interval); 595 elInt.textContent = (Math.round(iv * 100) / 100).toFixed(2); 596 window.DFEHC_METRICS.recommended_interval = iv; 597 } 598 } 599 600 function refreshStats() { 601 if (!ajaxUrl || inFlight) return; 602 inFlight = true; 603 if (box) box.classList.add('is-loading'); 604 605 var fd = new FormData(); 606 fd.append('action', 'dfehc_widget_refresh_stats'); 607 fd.append('_ajax_nonce', ajaxNonce); 608 609 fetch(ajaxUrl, { method: 'POST', credentials: 'same-origin', body: fd }) 610 .then(function(r) { return r.json(); }) 611 .then(function(json) { 612 if (json && json.success && json.data) updateUI(json.data); 613 }) 614 .catch(function() {}) 615 .finally(function() { 616 inFlight = false; 617 if (box) box.classList.remove('is-loading'); 618 }); 619 } 620 621 function onActivate(e) { 622 if (e && e.type === 'keydown') { 623 var k = e.key || e.keyCode; 624 if (k !== 'Enter' && k !== ' ' && k !== 13 && k !== 32) return; 625 e.preventDefault(); 626 } 627 refreshStats(); 628 } 629 630 if (box) { 631 box.addEventListener('click', onActivate); 632 box.addEventListener('keydown', onActivate); 633 } 634 })(); 635 </script>"; 95 636 $labels = []; 96 637 $data = []; … … 103 644 foreach ($load_logs as $log) { 104 645 if ($log['timestamp'] >= $timestamp && $log['timestamp'] < ($timestamp + $interval)) { 105 $load_sum += $log['load'];646 $load_sum += is_numeric($log['load']) ? (float) $log['load'] : 0; 106 647 $count++; 107 648 } … … 148 689 </script>'; 149 690 } 691 add_action('wp_ajax_dfehc_widget_refresh_stats', function() { 692 if (!current_user_can('manage_options')) { 693 wp_send_json_error(['message' => 'forbidden'], 403); 694 } 695 check_ajax_referer('dfehc_widget_stats'); 696 697 $server_load = dfehc_get_server_load(); 698 $server_response_time = dfehc_get_server_response_time(); 699 $recommended_interval = dfehc_calculate_recommended_interval_user_activity($server_load); 700 701 $response_seconds = 0.0; 702 if (is_numeric($server_response_time)) { 703 $response_seconds = (float) $server_response_time; 704 } elseif (is_array($server_response_time)) { 705 if (isset($server_response_time['main_response_ms']) && is_numeric($server_response_time['main_response_ms'])) { 706 $response_seconds = ((float) $server_response_time['main_response_ms']) / 1000.0; 707 } elseif (isset($server_response_time['response_ms']) && is_numeric($server_response_time['response_ms'])) { 708 $response_seconds = ((float) $server_response_time['response_ms']) / 1000.0; 709 } elseif (isset($server_response_time['response_time']) && is_numeric($server_response_time['response_time'])) { 710 $response_seconds = (float) $server_response_time['response_time']; 711 } 712 } elseif (is_object($server_response_time)) { 713 $arr = (array) $server_response_time; 714 if (isset($arr['main_response_ms']) && is_numeric($arr['main_response_ms'])) { 715 $response_seconds = ((float) $arr['main_response_ms']) / 1000.0; 716 } elseif (isset($arr['response_ms']) && is_numeric($arr['response_ms'])) { 717 $response_seconds = ((float) $arr['response_ms']) / 1000.0; 718 } elseif (isset($arr['response_time']) && is_numeric($arr['response_time'])) { 719 $response_seconds = (float) $arr['response_time']; 720 } 721 } 722 $response_seconds = max(0.0, $response_seconds); 723 724 wp_send_json_success([ 725 'server_load' => $server_load, 726 'server_response_time' => $response_seconds, 727 'recommended_interval' => (float) $recommended_interval, 728 ]); 729 }); 730 150 731 151 732 function dfehc_add_heartbeat_health_dashboard_widget() {
Note: See TracChangeset
for help on using the changeset viewer.