Plugin Directory

Changeset 3412283


Ignore:
Timestamp:
12/05/2025 02:32:34 PM (4 months ago)
Author:
loghin
Message:

1.2.996 Release

Location:
dynamic-front-end-heartbeat-control
Files:
35 added
14 edited

Legend:

Unmodified
Added
Removed
  • dynamic-front-end-heartbeat-control/trunk/admin/asset-manager.php

    r3310561 r3412283  
    2525            .collapsible-header .toggle-indicator{float:right;font-size:1.2em;line-height:1;font-weight:bold}
    2626            #dfehc-optimizer-form button{margin-right:5px;margin-bottom:5px}
    27             .database-health-status{display:inline-block;width:20px;height:20px;border-radius:50%;background-color:var(--c);animation:heartbeat 1s linear infinite;box-shadow:0 0 10px var(--c)}
    28             @keyframes heartbeat{50%{box-shadow:0 0 25px var(--c)}}
     27           .database-health-status{display:inline-block;width:22px;height:22px;border-radius:7px;background:radial-gradient(circle at 30% 22%,rgba(255,255,255,.5) 0,transparent 40%),radial-gradient(ellipse at top,rgba(255,255,255,.28) 12%,transparent 55%),linear-gradient(to bottom,rgba(255,255,255,.05),rgba(0,0,0,.22)),radial-gradient(ellipse at top,var(--c) 72%,transparent 73%),radial-gradient(ellipse at bottom,var(--c) 72%,transparent 73%);background-size:100% 100%,100% 38%,100% 100%,100% 38%,100% 38%;background-position:center,top,center,top,bottom;background-repeat:no-repeat;animation:heartbeat 2.3s ease-in-out infinite;box-shadow:0 0 8px var(--c),0 5px 12px rgba(0,0,0,.32)}
    2928            .dfehc-loader-overlay{display:none;position:fixed;top:0;left:0;width:100%;height:100%;background:rgba(255,255,255,.85);z-index:9999;text-align:center}
    3029            .dfehc-loader-content{position:absolute;top:50%;left:50%;transform:translate(-50%,-50%)}
  • dynamic-front-end-heartbeat-control/trunk/defibrillator/db-health.php

    r3396626 r3412283  
    1010}
    1111
    12 function dfehc_scoped_key(string $base): string {
    13     return $base . '_' . dfehc_blog_id();
    14 }
    15 
    16 function dfehc_cache_group(): string {
    17     return \defined('DFEHC_CACHE_GROUP') ? (string) \DFEHC_CACHE_GROUP : 'dfehc';
    18 }
    19 
    20 function dfehc_cache_get(string $key) {
    21     if (\function_exists('\wp_using_ext_object_cache') && \wp_using_ext_object_cache()) {
    22         return \wp_cache_get($key, dfehc_cache_group());
    23     }
    24     return \get_site_transient($key);
    25 }
    26 
    27 function dfehc_cache_set(string $key, $value, int $ttl): void {
    28     if (\function_exists('\wp_using_ext_object_cache') && \wp_using_ext_object_cache()) {
    29         \wp_cache_set($key, $value, dfehc_cache_group(), $ttl);
    30         return;
    31     }
    32     \set_site_transient($key, $value, $ttl);
     12if (!\function_exists(__NAMESPACE__ . '\\dfehc_host_token')) {
     13    function dfehc_host_token(): string {
     14        static $t = '';
     15        if ($t !== '') {
     16            return $t;
     17        }
     18        $host = @\php_uname('n')
     19            ?: (\defined('WP_HOME')
     20                ? WP_HOME
     21                : (\function_exists('\home_url') ? \home_url() : 'unknown'));
     22        $salt = \defined('DB_NAME') ? (string) DB_NAME : '';
     23        return $t = \substr(\md5((string) $host . $salt), 0, 10);
     24    }
     25}
     26
     27if (!\function_exists(__NAMESPACE__ . '\\dfehc_scoped_key')) {
     28    function dfehc_scoped_key(string $base): string {
     29        return $base . '_' . dfehc_blog_id() . '_' . dfehc_host_token();
     30    }
     31}
     32
     33if (!\function_exists(__NAMESPACE__ . '\\dfehc_cache_group')) {
     34    function dfehc_cache_group(): string {
     35        return \defined('DFEHC_CACHE_GROUP') ? (string) \DFEHC_CACHE_GROUP : 'dfehc';
     36    }
     37}
     38
     39if (!\function_exists(__NAMESPACE__ . '\\dfehc_cache_get')) {
     40    function dfehc_cache_get(string $key) {
     41        if (\function_exists('\wp_using_ext_object_cache') && \wp_using_ext_object_cache()) {
     42            return \wp_cache_get($key, dfehc_cache_group());
     43        }
     44        return \get_site_transient($key);
     45    }
     46}
     47
     48if (!\function_exists(__NAMESPACE__ . '\\dfehc_cache_set')) {
     49    function dfehc_cache_set(string $key, $value, int $ttl): void {
     50        $jitter = \function_exists('\random_int') ? \random_int(0, 5) : 0;
     51        $ttl    = \max(1, $ttl + $jitter);
     52
     53        if (\function_exists('\wp_using_ext_object_cache') && \wp_using_ext_object_cache()) {
     54            \wp_cache_set($key, $value, dfehc_cache_group(), $ttl);
     55            return;
     56        }
     57        \set_site_transient($key, $value, $ttl);
     58    }
    3359}
    3460
     
    7096    $sources = [];
    7197    $metrics = [
    72         'revision_count'          => 0,
    73         'trashed_posts_count'     => 0,
    74         'expired_transients_count'=> 0,
    75         'order_count'             => 0,
    76         'product_count'           => 0,
    77         'page_count'              => 0,
    78         'customer_count'          => 0,
    79         'user_count'              => 0,
    80         'db_size_mb'              => 0.0,
    81         'disk_free_space_mb'      => 0.0,
    82         'collected_at'            => \current_time('mysql'),
    83         'cache_backend'           => (\function_exists('\wp_using_ext_object_cache') && \wp_using_ext_object_cache()) ? 'object' : 'transient',
     98        'revision_count'           => 0,
     99        'trashed_posts_count'      => 0,
     100        'expired_transients_count' => 0,
     101        'order_count'              => 0,
     102        'product_count'            => 0,
     103        'page_count'               => 0,
     104        'customer_count'           => 0,
     105        'user_count'               => 0,
     106        'db_size_mb'               => 0.0,
     107        'disk_free_space_mb'       => 0.0,
     108        'collected_at'             => \current_time('mysql'),
     109        'cache_backend'            => (\function_exists('\wp_using_ext_object_cache') && \wp_using_ext_object_cache()) ? 'object' : 'transient',
    84110    ];
    85111
     
    160186
    161187    if ($toggles['db_size'] && $within_budget()) {
    162         $db_size_mb = dfehc_cache_get($db_size_key);
     188        $db_size_mb   = dfehc_cache_get($db_size_key);
    163189        $db_size_fail = dfehc_cache_get($db_size_fail_key);
     190
    164191        if ($db_size_mb === false && $db_size_fail === false) {
    165192            $prev = $wpdb->suppress_errors();
     
    236263        ]);
    237264        $estimate = 0.0;
    238         $estimate += $metrics['user_count']              * (float) ($multipliers['user'] ?? 0.03);
    239         $estimate += $metrics['order_count']             * (float) ($multipliers['order'] ?? 0.05);
    240         $estimate += $metrics['revision_count']          * (float) ($multipliers['revision'] ?? 0.05);
    241         $estimate += $metrics['trashed_posts_count']     * (float) ($multipliers['trash'] ?? 0.05);
    242         $estimate += $metrics['page_count']              * (float) ($multipliers['page'] ?? 0.10);
    243         $estimate += $metrics['expired_transients_count']* (float) ($multipliers['transient'] ?? 0.01);
     265        $estimate += $metrics['user_count']               * (float) ($multipliers['user'] ?? 0.03);
     266        $estimate += $metrics['order_count']              * (float) ($multipliers['order'] ?? 0.05);
     267        $estimate += $metrics['revision_count']           * (float) ($multipliers['revision'] ?? 0.05);
     268        $estimate += $metrics['trashed_posts_count']      * (float) ($multipliers['trash'] ?? 0.05);
     269        $estimate += $metrics['page_count']               * (float) ($multipliers['page'] ?? 0.10);
     270        $estimate += $metrics['expired_transients_count'] * (float) ($multipliers['transient'] ?? 0.01);
    244271        if ($estimate <= 0) {
    245272            $estimate = (float) ($multipliers['default'] ?? 500.0);
     
    277304        }
    278305        if ($disk_free_space_mb === null) {
    279             $fallback = (float) \apply_filters('dfehc_disk_free_default_mb', \round((50 * 1024 * 1024 * 1024) / 1024 / 1024, 2));
     306            $fallback = (float) \apply_filters(
     307                'dfehc_disk_free_default_mb',
     308                \round((50 * 1024 * 1024 * 1024) / 1024 / 1024, 2)
     309            );
    280310            $disk_free_space_mb = $fallback;
    281311            $sources['disk_free_space_mb'] = 'default';
     
    283313        $metrics['disk_free_space_mb'] = (float) $disk_free_space_mb;
    284314    } else {
    285         $metrics['disk_free_space_mb'] = (float) \apply_filters('dfehc_disk_free_default_mb', \round((50 * 1024 * 1024 * 1024) / 1024 / 1024, 2));
     315        $metrics['disk_free_space_mb'] = (float) \apply_filters(
     316            'dfehc_disk_free_default_mb',
     317            \round((50 * 1024 * 1024 * 1024) / 1024 / 1024, 2)
     318        );
    286319        $sources['disk_free_space_mb'] = $toggles['disk'] ? 'skipped' : 'disabled';
    287320    }
     
    336369    $thresholds = \apply_filters('dfehc_database_health_thresholds', $thresholds, $metrics);
    337370
    338     if (((int) ($metrics['revision_count'] ?? 0)) > (int) ($thresholds['revision'] ?? 0)) $conditions_met++;
     371    if (((int) ($metrics['revision_count'] ?? 0)) > (int) ($thresholds['revision'] ?? 0))   $conditions_met++;
    339372    if (((int) ($metrics['trashed_posts_count'] ?? 0)) > (int) ($thresholds['trash'] ?? 0)) $conditions_met++;
    340373    if (((int) ($metrics['expired_transients_count'] ?? 0)) > (int) ($thresholds['transients'] ?? 0)) $conditions_met++;
  • dynamic-front-end-heartbeat-control/trunk/defibrillator/load-estimator.php

    r3396626 r3412283  
    362362
    363363    private static function set_transient_noautoload(string $key, $value, int $ttl): void {
    364         $jitter = (\function_exists('random_int') ? \random_int(0, 5) : 0);
     364        $jitter = 0;
     365        if (\function_exists('random_int')) {
     366            try {
     367                $jitter = \random_int(0, 5);
     368            } catch (\Throwable $e) {
     369                $jitter = 0;
     370            }
     371        }
    365372        $ttl = \max(1, $ttl + $jitter);
    366373        if (\function_exists('wp_using_ext_object_cache') && \wp_using_ext_object_cache()) {
     
    388395
    389396    private static function set_site_transient_noautoload(string $key, $value, int $ttl): void {
    390         $jitter = (\function_exists('random_int') ? \random_int(0, 5) : 0);
     397        $jitter = 0;
     398        if (\function_exists('random_int')) {
     399            try {
     400                $jitter = \random_int(0, 5);
     401            } catch (\Throwable $e) {
     402                $jitter = 0;
     403            }
     404        }
    391405        $ttl = \max(1, $ttl + $jitter);
    392406        if (\function_exists('wp_using_ext_object_cache') && \wp_using_ext_object_cache()) {
  • dynamic-front-end-heartbeat-control/trunk/engine/interval-helper.php

    r3396626 r3412283  
    5454}
    5555
    56 if (!function_exists('dfehc_set_transient_noautoload')) {
    57     function dfehc_set_transient_noautoload(string $key, $value, int $expiration): void {
    58         $group = defined('DFEHC_CACHE_GROUP') ? DFEHC_CACHE_GROUP : 'dfehc';
    59         if (function_exists('wp_using_ext_object_cache') && wp_using_ext_object_cache()) {
    60             if (function_exists('wp_cache_add')) {
    61                 if (!wp_cache_add($key, $value, $group, $expiration)) {
    62                     wp_cache_set($key, $value, $group, $expiration);
    63                 }
    64             } else {
    65                 wp_cache_set($key, $value, $group, $expiration);
    66             }
    67             return;
    68         }
    69         set_transient($key, $value, $expiration);
    70         global $wpdb;
    71         if (!isset($wpdb->options)) return;
    72         $opt_key = "_transient_$key";
    73         $opt_key_to = "_transient_timeout_$key";
    74         $wpdb->suppress_errors(true);
    75         $autoload = $wpdb->get_var($wpdb->prepare("SELECT autoload FROM {$wpdb->options} WHERE option_name=%s LIMIT 1", $opt_key));
    76         if ($autoload === 'yes') {
    77             $wpdb->update($wpdb->options, ['autoload' => 'no'], ['option_name' => $opt_key, 'autoload' => 'yes'], ['%s'], ['%s','%s']);
    78         }
    79         $autoload_to = $wpdb->get_var($wpdb->prepare("SELECT autoload FROM {$wpdb->options} WHERE option_name=%s LIMIT 1", $opt_key_to));
    80         if ($autoload_to === 'yes') {
    81             $wpdb->update($wpdb->options, ['autoload' => 'no'], ['option_name' => $opt_key_to, 'autoload' => 'yes'], ['%s'], ['%s','%s']);
    82         }
    83         $wpdb->suppress_errors(false);
    84     }
    85 }
    86 
    8756if (!function_exists('dfehc_set_transient')) {
    8857    function dfehc_set_transient(string $key, float $value, float $interval): void {
    8958        $ttl = (int) apply_filters('dfehc_transient_ttl', max(60, (int) ceil($interval) * 2), $key, $value, $interval);
    90         $ttl += function_exists('random_int') ? random_int(0, 5) : 0;
     59        $jitter = 0;
     60        if (function_exists('random_int')) {
     61            try {
     62                $jitter = random_int(0, 5);
     63            } catch (\Throwable $e) {
     64                $jitter = 0;
     65            }
     66        }
     67        $ttl += $jitter;
    9168        dfehc_set_transient_noautoload($key, $value, $ttl);
    9269    }
     
    136113        $ttl_default = (int) dfehc_clamp($ttl_default, 60, 86400);
    137114        $ttl = (int) apply_filters('dfehc_ema_ttl', $ttl_default, $current, $ema);
    138         $ttl += function_exists('random_int') ? random_int(0, 5) : 0;
     115        $jitter = 0;
     116        if (function_exists('random_int')) {
     117            try {
     118                $jitter = random_int(0, 5);
     119            } catch (\Throwable $e) {
     120                $jitter = 0;
     121            }
     122        }
     123        $ttl += $jitter;
    139124        dfehc_set_transient_noautoload($key, $ema, $ttl);
    140125        return $ema;
     
    233218        if ($previous === false) {
    234219            $ttl = (int) apply_filters('dfehc_prev_interval_ttl', 1800);
    235             $ttl += function_exists('random_int') ? random_int(0, 5) : 0;
     220            $jitter = 0;
     221            if (function_exists('random_int')) {
     222                try {
     223                    $jitter = random_int(0, 5);
     224                } catch (\Throwable $e) {
     225                    $jitter = 0;
     226                }
     227            }
     228            $ttl += $jitter;
    236229            dfehc_set_transient_noautoload($key, $proposed, $ttl);
    237230            return $proposed;
     
    244237        $final     = dfehc_clamp($proposed, $lower, $upper);
    245238        $ttl = (int) apply_filters('dfehc_prev_interval_ttl', 1800);
    246         $ttl += function_exists('random_int') ? random_int(0, 5) : 0;
     239        $jitter = 0;
     240        if (function_exists('random_int')) {
     241            try {
     242                $jitter = random_int(0, 5);
     243            } catch (\Throwable $e) {
     244                $jitter = 0;
     245            }
     246        }
     247        $ttl += $jitter;
    247248        dfehc_set_transient_noautoload($key, $final, $ttl);
    248249        return $final;
  • dynamic-front-end-heartbeat-control/trunk/engine/server-load.php

    r3396626 r3412283  
    186186    $key = dfehc_key(DFEHC_SERVER_LOAD_CACHE_KEY);
    187187    $ttl = dfehc_server_load_ttl();
     188    $jitter = 0;
     189    if (function_exists('random_int')) {
     190        try {
     191            $jitter = random_int(0, 5);
     192        } catch (Throwable $e) {
     193            $jitter = 0;
     194        }
     195    }
     196    $ttl += $jitter;
    188197    try {
    189         $ttl += function_exists('random_int') ? random_int(0, 5) : 0;
    190198        if ($type === 'redis') {
    191199            $client->setex($key, $ttl, $value);
     
    224232            ];
    225233            $ttl = dfehc_server_load_ttl();
    226             $ttl += function_exists('random_int') ? random_int(0, 5) : 0;
     234            $jitter = 0;
     235            if (function_exists('random_int')) {
     236                try {
     237                    $jitter = random_int(0, 5);
     238                } catch (Throwable $e) {
     239                    $jitter = 0;
     240                }
     241            }
     242            $ttl += $jitter;
    227243            if (wp_using_ext_object_cache()) {
    228244                wp_cache_set($payloadKey, $payload, DFEHC_CACHE_GROUP, $ttl);
     
    287303}
    288304
    289 function dfehc_get_cpu_cores(): int
    290 {
    291     static $cores = null;
    292     if ($cores !== null) {
    293         return (int) $cores;
    294     }
    295     $override = getenv('DFEHC_CPU_CORES');
    296     if ($override && ctype_digit((string) $override) && (int) $override > 0) {
    297         $cores = (int) $override;
    298         return (int) $cores;
    299     }
    300     if (is_readable('/sys/fs/cgroup/cpu.max')) {
    301         $line = file_get_contents('/sys/fs/cgroup/cpu.max');
    302         if ($line !== false) {
    303             [$quota, $period] = explode(' ', trim($line));
    304             if ($quota !== 'max') {
    305                 $q = (int) $quota;
    306                 $p = (int) $period;
    307                 if ($q > 0 && $p > 0) {
    308                     $cores = max(1, (int) ceil($q / $p));
     305if (!function_exists('dfehc_get_cpu_cores')) {
     306    function dfehc_get_cpu_cores(): int
     307    {
     308        static $cores = null;
     309        if ($cores !== null) {
     310            return (int) $cores;
     311        }
     312        $override = getenv('DFEHC_CPU_CORES');
     313        if ($override && ctype_digit((string) $override) && (int) $override > 0) {
     314            $cores = (int) $override;
     315            return (int) $cores;
     316        }
     317        if (is_readable('/sys/fs/cgroup/cpu.max')) {
     318            $line = file_get_contents('/sys/fs/cgroup/cpu.max');
     319            if ($line !== false) {
     320                [$quota, $period] = explode(' ', trim($line));
     321                if ($quota !== 'max') {
     322                    $q = (int) $quota;
     323                    $p = (int) $period;
     324                    if ($q > 0 && $p > 0) {
     325                        $cores = max(1, (int) ceil($q / $p));
     326                        $cores = (int) apply_filters('dfehc_cpu_cores', (int) $cores);
     327                        return (int) $cores;
     328                    }
     329                }
     330            }
     331        }
     332        if (is_readable('/proc/self/cgroup')) {
     333            $content = file_get_contents('/proc/self/cgroup');
     334            if ($content !== false && preg_match('/^[0-9]+:[^:]*cpu[^:]*:(.+)$/m', $content, $m)) {
     335                $path = '/' . ltrim(trim($m[1]), '/');
     336                $base = '/sys/fs/cgroup' . $path;
     337                $quotaFile = "$base/cpu.cfs_quota_us";
     338                $periodFile = "$base/cpu.cfs_period_us";
     339                if (is_readable($quotaFile) && is_readable($periodFile)) {
     340                    $quota = (int) file_get_contents($quotaFile);
     341                    $period = (int) file_get_contents($periodFile);
     342                    if ($quota > 0 && $period > 0) {
     343                        $cores = max(1, (int) ceil($quota / $period));
     344                        $cores = (int) apply_filters('dfehc_cpu_cores', (int) $cores);
     345                        return (int) $cores;
     346                    }
     347                }
     348            }
     349        }
     350        if (is_readable('/sys/fs/cgroup/cpu/cpu.cfs_quota_us') && is_readable('/sys/fs/cgroup/cpu/cpu.cfs_period_us')) {
     351            $quota = (int) file_get_contents('/sys/fs/cgroup/cpu/cpu.cfs_quota_us');
     352            $period = (int) file_get_contents('/sys/fs/cgroup/cpu/cpu.cfs_period_us');
     353            if ($quota > 0 && $period > 0) {
     354                $cores = max(1, (int) ceil($quota / $period));
     355                $cores = (int) apply_filters('dfehc_cpu_cores', (int) $cores);
     356                return (int) $cores;
     357            }
     358        }
     359        $disabled = array_map('trim', explode(',', (string) ini_get('disable_functions')));
     360        if (function_exists('shell_exec') && !in_array('shell_exec', $disabled, true) && !ini_get('open_basedir')) {
     361            $n = shell_exec('nproc 2>/dev/null');
     362            if ($n && ctype_digit(trim((string) $n))) {
     363                $cores = max(1, (int) trim((string) $n));
     364                $cores = (int) apply_filters('dfehc_cpu_cores', (int) $cores);
     365                return (int) $cores;
     366            }
     367        }
     368        if (is_readable('/proc/cpuinfo')) {
     369            $info = file_get_contents('/proc/cpuinfo');
     370            if ($info !== false) {
     371                $cnt = preg_match_all('/^processor/m', $info);
     372                if ($cnt) {
     373                    $cores = (int) $cnt;
    309374                    $cores = (int) apply_filters('dfehc_cpu_cores', (int) $cores);
    310375                    return (int) $cores;
     
    312377            }
    313378        }
    314     }
    315     if (is_readable('/proc/self/cgroup')) {
    316         $content = file_get_contents('/proc/self/cgroup');
    317         if ($content !== false && preg_match('/^[0-9]+:[^:]*cpu[^:]*:(.+)$/m', $content, $m)) {
    318             $path = '/' . ltrim(trim($m[1]), '/');
    319             $base = '/sys/fs/cgroup' . $path;
    320             $quotaFile = "$base/cpu.cfs_quota_us";
    321             $periodFile = "$base/cpu.cfs_period_us";
    322             if (is_readable($quotaFile) && is_readable($periodFile)) {
    323                 $quota = (int) file_get_contents($quotaFile);
    324                 $period = (int) file_get_contents($periodFile);
    325                 if ($quota > 0 && $period > 0) {
    326                     $cores = max(1, (int) ceil($quota / $period));
    327                     $cores = (int) apply_filters('dfehc_cpu_cores', (int) $cores);
    328                     return (int) $cores;
    329                 }
    330             }
    331         }
    332     }
    333     if (is_readable('/sys/fs/cgroup/cpu/cpu.cfs_quota_us') && is_readable('/sys/fs/cgroup/cpu/cpu.cfs_period_us')) {
    334         $quota = (int) file_get_contents('/sys/fs/cgroup/cpu/cpu.cfs_quota_us');
    335         $period = (int) file_get_contents('/sys/fs/cgroup/cpu/cpu.cfs_period_us');
    336         if ($quota > 0 && $period > 0) {
    337             $cores = max(1, (int) ceil($quota / $period));
    338             $cores = (int) apply_filters('dfehc_cpu_cores', (int) $cores);
    339             return (int) $cores;
    340         }
    341     }
    342     $disabled = array_map('trim', explode(',', (string) ini_get('disable_functions')));
    343     if (function_exists('shell_exec') && !in_array('shell_exec', $disabled, true) && !ini_get('open_basedir')) {
    344         $n = shell_exec('nproc 2>/dev/null');
    345         if ($n && ctype_digit(trim((string) $n))) {
    346             $cores = max(1, (int) trim((string) $n));
    347             $cores = (int) apply_filters('dfehc_cpu_cores', (int) $cores);
    348             return (int) $cores;
    349         }
    350     }
    351     if (is_readable('/proc/cpuinfo')) {
    352         $info = file_get_contents('/proc/cpuinfo');
    353         if ($info !== false) {
    354             $cnt = preg_match_all('/^processor/m', $info);
    355             if ($cnt) {
    356                 $cores = (int) $cnt;
    357                 $cores = (int) apply_filters('dfehc_cpu_cores', (int) $cores);
    358                 return (int) $cores;
    359             }
    360         }
    361     }
    362     $cores = 1;
    363     $cores = (int) apply_filters('dfehc_cpu_cores', (int) $cores);
    364     return (int) $cores;
     379        $cores = 1;
     380        $cores = (int) apply_filters('dfehc_cpu_cores', (int) $cores);
     381        return (int) $cores;
     382    }
    365383}
    366384
     
    389407add_action('dfehc_log_server_load_hook', 'dfehc_log_server_load');
    390408
    391 function dfehc_get_server_load_ajax_handler(): void
    392 {
    393     $allow_public = apply_filters('dfehc_allow_public_server_load', false);
    394     if (!$allow_public) {
    395         $action = 'get_server_load';
    396         $nonce_action = 'dfehc-' . $action;
    397         $valid = function_exists('check_ajax_referer')
    398             ? check_ajax_referer($nonce_action, 'nonce', false)
    399             : wp_verify_nonce((string)($_POST['nonce'] ?? $_GET['nonce'] ?? ''), $nonce_action);
    400         if (!$valid) {
    401             wp_send_json_error(['message' => 'Invalid nonce.'], 403);
    402         }
    403         $cap = apply_filters('dfehc_required_capability', 'read');
    404         if (!current_user_can($cap)) {
    405             wp_send_json_error(['message' => 'Not authorised.'], 403);
    406         }
    407     } else {
    408         $ip = dfehc_client_ip();
    409         $rk = dfehc_key('dfehc_rl_' . md5($ip));
    410         $cnt = (int) get_transient($rk);
    411         $limit = (int) apply_filters('dfehc_public_rate_limit', 60);
    412         $win   = (int) apply_filters('dfehc_public_rate_window', 60);
    413         if ($cnt >= $limit) {
    414             wp_send_json_error(['message' => 'rate_limited'], 429);
    415         }
    416         set_transient($rk, $cnt + 1, $win);
    417     }
    418     nocache_headers();
    419     wp_send_json_success(dfehc_get_server_load_persistent());
     409if (!function_exists('dfehc_get_server_load_ajax_handler')) {
     410    function dfehc_get_server_load_ajax_handler(): void
     411    {
     412        $allow_public = apply_filters('dfehc_allow_public_server_load', false);
     413        if (!$allow_public) {
     414            $action = 'get_server_load';
     415            $nonce_action = 'dfehc-' . $action;
     416            $valid = function_exists('check_ajax_referer')
     417                ? check_ajax_referer($nonce_action, 'nonce', false)
     418                : wp_verify_nonce((string)($_POST['nonce'] ?? $_GET['nonce'] ?? ''), $nonce_action);
     419            if (!$valid) {
     420                wp_send_json_error(['message' => 'Invalid nonce.'], 403);
     421            }
     422            $cap = apply_filters('dfehc_required_capability', 'read');
     423            if (!current_user_can($cap)) {
     424                wp_send_json_error(['message' => 'Not authorised.'], 403);
     425            }
     426        } else {
     427            $ip = dfehc_client_ip();
     428            $rk = dfehc_key('dfehc_rl_' . md5($ip));
     429            $cnt = (int) get_transient($rk);
     430            $limit = (int) apply_filters('dfehc_public_rate_limit', 60);
     431            $win   = (int) apply_filters('dfehc_public_rate_window', 60);
     432            if ($cnt >= $limit) {
     433                wp_send_json_error(['message' => 'rate_limited'], 429);
     434            }
     435            set_transient($rk, $cnt + 1, $win);
     436        }
     437        nocache_headers();
     438        wp_send_json_success(dfehc_get_server_load_persistent());
     439    }
    420440}
    421441add_action('wp_ajax_get_server_load', 'dfehc_get_server_load_ajax_handler');
     
    454474    if (wp_using_ext_object_cache()) {
    455475        $ttl = dfehc_server_load_ttl();
    456         $ttl += function_exists('random_int') ? random_int(0, 5) : 0;
     476        $jitter = 0;
     477        if (function_exists('random_int')) {
     478            try {
     479                $jitter = random_int(0, 5);
     480            } catch (Throwable $e) {
     481                $jitter = 0;
     482            }
     483        }
     484        $ttl += $jitter;
    457485        wp_cache_set($key, $fresh, DFEHC_CACHE_GROUP, $ttl);
    458486    }
  • dynamic-front-end-heartbeat-control/trunk/engine/server-response.php

    r3396626 r3412283  
    126126        ];
    127127        $ttl = (int) apply_filters('dfehc_high_traffic_cache_expiration', 300);
    128         $ttl += function_exists('random_int') ? random_int(0, 5) : 0;
     128        $jitter = 0;
     129        if (function_exists('random_int')) {
     130            try {
     131                $jitter = random_int(0, 5);
     132            } catch (\Throwable $e) {
     133                $jitter = 0;
     134            }
     135        }
     136        $ttl += $jitter;
    129137        dfehc_set_transient_noautoload($cacheKey, $high, $ttl);
    130138        return $high;
    131139    }
    132140
    133     if (!dfehc_acquire_lock()) {
     141    if (!dfehc_rt_acquire_lock()) {
    134142        return array_merge($defaults, is_array($cached) ? $cached : []);
    135143    }
     
    155163            $results['timestamp'] = current_time('mysql');
    156164            $results['ts_unix'] = $now;
    157             $exp += function_exists('random_int') ? random_int(0, 5) : 0;
     165            $jitter = 0;
     166            if (function_exists('random_int')) {
     167                try {
     168                    $jitter = random_int(0, 5);
     169                } catch (\Throwable $e) {
     170                    $jitter = 0;
     171                }
     172            }
     173            $exp += $jitter;
    158174            dfehc_set_transient_noautoload($baselineKey, $results, $exp);
    159175            $baseline = $results;
     
    184200                $results['ts_unix'] = $now;
    185201                $exp = (int) apply_filters('dfehc_baseline_expiration', DFEHC_BASELINE_EXP);
    186                 $exp += function_exists('random_int') ? random_int(0, 5) : 0;
     202                $jitter = 0;
     203                if (function_exists('random_int')) {
     204                    try {
     205                        $jitter = random_int(0, 5);
     206                    } catch (\Throwable $e) {
     207                        $jitter = 0;
     208                    }
     209                }
     210                $exp += $jitter;
    187211                dfehc_set_transient_noautoload($baselineKey, $results, $exp);
    188212                $spike = 0.0;
     
    195219
    196220        if (abs($spike - $prev_spike) >= DFEHC_SPIKE_OPT_EPS) {
    197             $ttl = DFEHC_BASELINE_EXP + (function_exists('random_int') ? random_int(0, 5) : 0);
     221            $ttl = DFEHC_BASELINE_EXP;
     222            $jitter = 0;
     223            if (function_exists('random_int')) {
     224                try {
     225                    $jitter = random_int(0, 5);
     226                } catch (\Throwable $e) {
     227                    $jitter = 0;
     228                }
     229            }
     230            $ttl += $jitter;
    198231            dfehc_set_transient_noautoload($spikeKey, $spike, $ttl);
    199232        }
    200233
    201234        $exp = (int) apply_filters('dfehc_cache_expiration', 3 * MINUTE_IN_SECONDS);
    202         $exp += function_exists('random_int') ? random_int(0, 5) : 0;
     235        $jitter = 0;
     236        if (function_exists('random_int')) {
     237            try {
     238                $jitter = random_int(0, 5);
     239            } catch (\Throwable $e) {
     240                $jitter = 0;
     241            }
     242        }
     243        $exp += $jitter;
    203244        dfehc_set_transient_noautoload($cacheKey, $results, $exp);
    204245
    205246        return array_merge($defaults, $results);
    206247    } finally {
    207         dfehc_release_lock();
     248        dfehc_rt_release_lock();
    208249    }
    209250}
     
    324365                if ($head_supported === null) {
    325366                    $ttl = (int) apply_filters('dfehc_head_negative_ttl', DFEHC_HEAD_NEG_TTL);
    326                     $ttl += function_exists('random_int') ? random_int(0, 5) : 0;
     367                    $jitter = 0;
     368                    if (function_exists('random_int')) {
     369                        try {
     370                            $jitter = random_int(0, 5);
     371                        } catch (\Throwable $e) {
     372                            $jitter = 0;
     373                        }
     374                    }
     375                    $ttl += $jitter;
    327376                    dfehc_set_transient_noautoload($head_key, 0, $ttl);
    328377                }
     
    331380                if ($head_supported === null) {
    332381                    $ttl = (int) apply_filters('dfehc_head_positive_ttl', DFEHC_HEAD_POS_TTL);
    333                     $ttl += function_exists('random_int') ? random_int(0, 5) : 0;
     382                    $jitter = 0;
     383                    if (function_exists('random_int')) {
     384                        try {
     385                            $jitter = random_int(0, 5);
     386                        } catch (\Throwable $e) {
     387                            $jitter = 0;
     388                        }
     389                    }
     390                    $ttl += $jitter;
    334391                    dfehc_set_transient_noautoload($head_key, 1, $ttl);
    335392                }
     
    366423    } else {
    367424        $ttl = (int) apply_filters('dfehc_probe_fail_ttl', 60);
    368         $ttl += function_exists('random_int') ? random_int(0, 5) : 0;
     425        $jitter = 0;
     426        if (function_exists('random_int')) {
     427            try {
     428                $jitter = random_int(0, 5);
     429            } catch (\Throwable $e) {
     430                $jitter = 0;
     431            }
     432        }
     433        $ttl += $jitter;
    369434        dfehc_set_transient_noautoload($negKey, 1, $ttl);
    370435        $r['method'] = 'failed';
     
    402467}
    403468
    404 if (!function_exists('dfehc_acquire_lock')) {
    405     function dfehc_acquire_lock(): bool
     469if (!function_exists('dfehc_rt_acquire_lock')) {
     470    function dfehc_rt_acquire_lock(): bool
    406471    {
    407472        $key = dfehc_key('dfehc_measure_lock');
     
    434499}
    435500
    436 if (!function_exists('dfehc_release_lock')) {
    437     function dfehc_release_lock(): void
     501if (!function_exists('dfehc_rt_release_lock')) {
     502    function dfehc_rt_release_lock(): void
    438503    {
    439504        if (isset($GLOBALS['dfehc_rt_lock']) && $GLOBALS['dfehc_rt_lock'] instanceof WP_Lock) {
  • dynamic-front-end-heartbeat-control/trunk/engine/system-load-fallback.php

    r3396626 r3412283  
    3131if (!function_exists('dfehc_set_transient_noautoload')) {
    3232    function dfehc_set_transient_noautoload(string $key, $value, int $expiration): void {
    33         $jitter = function_exists('random_int') ? random_int(0, 5) : 0;
     33        $jitter = 0;
     34        if (function_exists('random_int')) {
     35            try {
     36                $jitter = random_int(0, 5);
     37            } catch (\Throwable $e) {
     38                $jitter = 0;
     39            }
     40        }
    3441        $expiration = max(1, $expiration + $jitter);
    3542        if (function_exists('wp_using_ext_object_cache') && wp_using_ext_object_cache()) {
  • dynamic-front-end-heartbeat-control/trunk/heartbeat-async.php

    r3396626 r3412283  
    22declare(strict_types=1);
    33
    4 define('DFEHC_LOAD_AVERAGES', 'dfehc_load_averages');
    5 define('DFEHC_SERVER_LOAD', 'dfehc_server_load');
    6 define('DFEHC_RECOMMENDED_INTERVAL', 'dfehc_recommended_interval');
    7 define('DFEHC_CAPABILITY', 'read');
    8 define('DFEHC_LOAD_LOCK_BASE', 'dfehc_compute_load_lock');
    9 define('DFEHC_CACHE_GROUP', 'dfehc');
     4if (!defined('DFEHC_LOAD_AVERAGES')) define('DFEHC_LOAD_AVERAGES', 'dfehc_load_averages');
     5if (!defined('DFEHC_SERVER_LOAD')) define('DFEHC_SERVER_LOAD', 'dfehc_server_load');
     6if (!defined('DFEHC_RECOMMENDED_INTERVAL')) define('DFEHC_RECOMMENDED_INTERVAL', 'dfehc_recommended_interval');
     7if (!defined('DFEHC_CAPABILITY')) define('DFEHC_CAPABILITY', 'read');
     8if (!defined('DFEHC_LOAD_LOCK_BASE')) define('DFEHC_LOAD_LOCK_BASE', 'dfehc_compute_load_lock');
     9if (!defined('DFEHC_CACHE_GROUP')) define('DFEHC_CACHE_GROUP', 'dfehc');
    1010
    1111function dfehc_max_server_load(): int { static $v; if ($v === null) { $v = (int) apply_filters('dfehc_max_server_load', 85); } return $v; }
     
    1515function dfehc_server_load_ttl(): int { static $v; if ($v === null) { $v = (int) apply_filters('dfehc_server_load_ttl', 180); } return $v; }
    1616
    17 function dfehc_host_token(): string
    18 {
    19     $host = @php_uname('n') ?: (defined('WP_HOME') ? WP_HOME : (function_exists('home_url') ? home_url() : 'unknown'));
    20     $salt = defined('DB_NAME') ? (string) DB_NAME : '';
    21     return substr(md5((string) $host . $salt), 0, 10);
    22 }
    23 
    24 function dfehc_scoped_key(string $base): string
    25 {
    26     $blog = function_exists('get_current_blog_id') ? (string) get_current_blog_id() : '0';
    27     return "{$base}_{$blog}_" . dfehc_host_token();
    28 }
    29 
    30 function dfehc_store_lockfree(string $key, $value, int $ttl): bool
    31 {
    32     if (function_exists('wp_cache_add') && wp_cache_add($key, $value, DFEHC_CACHE_GROUP, $ttl)) {
    33         return true;
    34     }
    35     return set_transient($key, $value, $ttl);
     17if (!function_exists('dfehc_host_token')) {
     18    function dfehc_host_token(): string
     19    {
     20        $host = @php_uname('n') ?: (defined('WP_HOME') ? WP_HOME : (function_exists('home_url') ? home_url() : 'unknown'));
     21        $salt = defined('DB_NAME') ? (string) DB_NAME : '';
     22        return substr(md5((string) $host . $salt), 0, 10);
     23    }
     24}
     25
     26if (!function_exists('dfehc_scoped_key')) {
     27    function dfehc_scoped_key(string $base): string
     28    {
     29        $blog = function_exists('get_current_blog_id') ? (string) get_current_blog_id() : '0';
     30        return "{$base}_{$blog}_" . dfehc_host_token();
     31    }
     32}
     33
     34if (!function_exists('dfehc_store_lockfree')) {
     35    function dfehc_store_lockfree(string $key, $value, int $ttl): bool
     36    {
     37        if (function_exists('wp_cache_add') && wp_cache_add($key, $value, DFEHC_CACHE_GROUP, $ttl)) {
     38            return true;
     39        }
     40        return set_transient($key, $value, $ttl);
     41    }
    3642}
    3743
     
    7278}
    7379
    74 function dfehc_set_transient_noautoload(string $key, $value, int $expiration): void
    75 {
    76     if (wp_using_ext_object_cache()) {
    77         if (function_exists('wp_cache_add')) {
    78             if (!wp_cache_add($key, $value, DFEHC_CACHE_GROUP, $expiration)) {
     80if (!function_exists('dfehc_set_transient_noautoload')) {
     81    function dfehc_set_transient_noautoload(string $key, $value, int $expiration): void
     82    {
     83        if (wp_using_ext_object_cache()) {
     84            if (function_exists('wp_cache_add')) {
     85                if (!wp_cache_add($key, $value, DFEHC_CACHE_GROUP, $expiration)) {
     86                    wp_cache_set($key, $value, DFEHC_CACHE_GROUP, $expiration);
     87                }
     88            } else {
    7989                wp_cache_set($key, $value, DFEHC_CACHE_GROUP, $expiration);
    8090            }
    81         } else {
    82             wp_cache_set($key, $value, DFEHC_CACHE_GROUP, $expiration);
    83         }
    84         return;
    85     }
    86     dfehc_store_lockfree($key, $value, $expiration);
    87     global $wpdb;
    88     $opt_key = "_transient_$key";
    89     $opt_key_to = "_transient_timeout_$key";
    90     $wpdb->suppress_errors(true);
    91     $autoload = $wpdb->get_var($wpdb->prepare("SELECT autoload FROM {$wpdb->options} WHERE option_name=%s LIMIT 1", $opt_key));
    92     if ($autoload === 'yes') {
    93         $wpdb->update($wpdb->options, ['autoload' => 'no'], ['option_name' => $opt_key, 'autoload' => 'yes'], ['%s'], ['%s','%s']);
    94     }
    95     $autoload_to = $wpdb->get_var($wpdb->prepare("SELECT autoload FROM {$wpdb->options} WHERE option_name=%s LIMIT 1", $opt_key_to));
    96     if ($autoload_to === 'yes') {
    97         $wpdb->update($wpdb->options, ['autoload' => 'no'], ['option_name' => $opt_key_to, 'autoload' => 'yes'], ['%s'], ['%s','%s']);
    98     }
    99     $wpdb->suppress_errors(false);
     91            return;
     92        }
     93        dfehc_store_lockfree($key, $value, $expiration);
     94        global $wpdb;
     95        $opt_key = "_transient_$key";
     96        $opt_key_to = "_transient_timeout_$key";
     97        $wpdb->suppress_errors(true);
     98        $autoload = $wpdb->get_var($wpdb->prepare("SELECT autoload FROM {$wpdb->options} WHERE option_name=%s LIMIT 1", $opt_key));
     99        if ($autoload === 'yes') {
     100            $wpdb->update($wpdb->options, ['autoload' => 'no'], ['option_name' => $opt_key, 'autoload' => 'yes'], ['%s'], ['%s','%s']);
     101        }
     102        $autoload_to = $wpdb->get_var($wpdb->prepare("SELECT autoload FROM {$wpdb->options} WHERE option_name=%s LIMIT 1", $opt_key_to));
     103        if ($autoload_to === 'yes') {
     104            $wpdb->update($wpdb->options, ['autoload' => 'no'], ['option_name' => $opt_key_to, 'autoload' => 'yes'], ['%s'], ['%s','%s']);
     105        }
     106        $wpdb->suppress_errors(false);
     107    }
    100108}
    101109
     
    442450}
    443451
    444 function dfehc_calculate_recommended_interval_user_activity(float $current_load): int
    445 {
    446     $key = dfehc_scoped_key(DFEHC_RECOMMENDED_INTERVAL);
    447     $interval = get_transient($key);
    448     if ($interval !== false) {
    449         return (int) $interval;
    450     }
    451     return $current_load >= dfehc_max_server_load() ? dfehc_max_interval() : dfehc_min_interval();
     452if (!function_exists('dfehc_calculate_recommended_interval_user_activity')) {
     453    function dfehc_calculate_recommended_interval_user_activity(float $current_load): float
     454    {
     455        $key = dfehc_scoped_key(DFEHC_RECOMMENDED_INTERVAL);
     456        $interval = get_transient($key);
     457        if ($interval !== false) {
     458            return (float) $interval;
     459        }
     460        return $current_load >= dfehc_max_server_load()
     461            ? (float) dfehc_max_interval()
     462            : (float) dfehc_min_interval();
     463    }
    452464}
    453465
  • dynamic-front-end-heartbeat-control/trunk/heartbeat-controller.php

    r3396626 r3412283  
    44Plugin URI: https://heartbeat.support
    55Description: 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.995
     6Version: 1.2.996
    77Author: Codeloghin
    88Author URI: https://codeloghin.com
     
    1111
    1212declare(strict_types=1);
     13
     14if (!defined('ABSPATH')) {
     15    exit;
     16}
    1317
    1418if (!defined('DFEHC_PLUGIN_PATH')) {
     
    96100}
    97101
     102add_action('wp_default_scripts', function (WP_Scripts $scripts) {
     103    if (is_admin() || wp_doing_ajax()) {
     104        return;
     105    }
     106
     107    if (isset($scripts->registered['heartbeat'])) {
     108        $scripts->registered['heartbeat']->src  = plugin_dir_url(__FILE__) . 'js/heartbeat.min.js';
     109        $scripts->registered['heartbeat']->ver  = '1.6.1';
     110        $scripts->registered['heartbeat']->deps = array('jquery');
     111    }
     112});
     113
    98114function dfehc_enqueue_scripts(): void
    99115{
    100     wp_register_script('dfehc-heartbeat', plugin_dir_url(__FILE__) . 'js/heartbeat.min.js', ['heartbeat'], '1.6.0', true);
    101     wp_enqueue_script('dfehc-heartbeat');
     116    if (is_admin() || wp_doing_ajax()) {
     117        return;
     118    }
     119
     120    wp_enqueue_script('heartbeat');
    102121
    103122    $load = function_exists('dfehc_get_server_load') ? dfehc_get_server_load() : null;
     
    113132    $ver  = defined('DFEHC_VERSION') ? (string) DFEHC_VERSION : (string) filemtime(__FILE__);
    114133
    115     wp_localize_script('dfehc-heartbeat', 'dfehc_heartbeat_vars', [
    116         'recommendedInterval'       => (float) $recommended,
    117         'heartbeat_control_enabled' => get_option('dfehc_heartbeat_control_enabled', '1'),
    118         'nonce'                     => wp_create_nonce(DFEHC_NONCE_ACTION),
    119         'ver'                       => $ver,
    120         'cache_bypass_rate'         => 0.05,
    121     ]);
     134    $min_interval_option = defined('DFEHC_OPTION_MIN_INTERVAL') ? (int) get_option(DFEHC_OPTION_MIN_INTERVAL, defined('DFEHC_DEFAULT_MIN_INTERVAL') ? DFEHC_DEFAULT_MIN_INTERVAL : DFEHC_MIN_INTERVAL) : (int) DFEHC_MIN_INTERVAL;
     135    $max_interval_option = defined('DFEHC_OPTION_MAX_INTERVAL') ? (int) get_option(DFEHC_OPTION_MAX_INTERVAL, defined('DFEHC_DEFAULT_MAX_INTERVAL') ? DFEHC_DEFAULT_MAX_INTERVAL : DFEHC_MAX_INTERVAL) : (int) DFEHC_MAX_INTERVAL;
     136    $max_server_load_option = defined('DFEHC_OPTION_MAX_SERVER_LOAD') ? (float) get_option(DFEHC_OPTION_MAX_SERVER_LOAD, defined('DFEHC_DEFAULT_MAX_SERVER_LOAD') ? DFEHC_DEFAULT_MAX_SERVER_LOAD : DFEHC_MAX_SERVER_LOAD) : (float) DFEHC_MAX_SERVER_LOAD;
     137
     138    wp_localize_script(
     139        'heartbeat',
     140        'dfehc_heartbeat_vars',
     141        array(
     142            'recommendedInterval'       => (float) $recommended,
     143            'heartbeat_control_enabled' => get_option('dfehc_heartbeat_control_enabled', '1'),
     144            'nonce'                     => wp_create_nonce(DFEHC_NONCE_ACTION),
     145            'ver'                       => $ver,
     146            'cache_bypass_rate'         => 0.05,
     147        )
     148    );
    122149}
    123150add_action('wp_enqueue_scripts', 'dfehc_enqueue_scripts');
     
    156183    $total_weight            = 0;
    157184    $offset                  = 0;
     185    $start_time              = microtime(true);
     186    $time_limit              = 1.5;
    158187    while (true) {
     188        if (microtime(true) - $start_time > $time_limit) {
     189            break;
     190        }
    159191        $userBatch = dfehc_get_users_in_batches($batch_size, $offset);
    160192        if (!$userBatch) {
     
    179211}
    180212
    181 function dfehc_get_recommended_heartbeat_interval_async()
     213function dfehc_get_recommended_heartbeat_interval_async(): float
    182214{
    183215    if (!class_exists('Heartbeat_Async') && file_exists(__DIR__ . '/heartbeat-async.php')) {
  • dynamic-front-end-heartbeat-control/trunk/js/heartbeat.js

    r3396626 r3412283  
    250250
    251251    try {
    252       const body = new URLSearchParams({ action: 'get_server_load' });
     252      const body = new URLSearchParams({ action: 'dfehc_update_heartbeat_interval' });
    253253      if (nonce) body.append('nonce', nonce);
    254254      const res = await fetchWithTimeout(url, {
  • dynamic-front-end-heartbeat-control/trunk/js/heartbeat.min.js

    r3396626 r3412283  
    1 ((wp)=>{const intervals={low:[15,30,60,120,180,240,300],medium:[30,60,120,180,240,300],high:[60,120,180,240,300]};const vars=typeof window.dfehc_heartbeat_vars==="object"?window.dfehc_heartbeat_vars:{};const clampNumber=(v,lo,hi,d)=>{const n=Number(v);if(!Number.isFinite(n))return d;return Math.min(hi,Math.max(lo,n))};const DEFAULT_MIN=15;const DEFAULT_MAX=300;const ABS_MIN=1;const ABS_MAX=3600;let MIN=clampNumber(vars.min_interval,ABS_MIN,ABS_MAX,DEFAULT_MIN);let MAX=clampNumber(vars.max_interval,MIN,ABS_MAX,DEFAULT_MAX);if(MIN>MAX){const t=MIN;MIN=MAX;MAX=t}const LOAD_CAP=Number.isFinite(vars.max_server_load)?Number(vars.max_server_load):85;const msOrAuto=(v,d)=>{const n=Number(v);if(!Number.isFinite(n))return d;return n<=600?n*1e3:n};const cacheTimeout=msOrAuto(vars.cache_duration,3e5);const cacheBypassRate=Math.min(1,Math.max(0,Number(vars.cache_bypass_rate)||.05));const sanitizeKey=(s)=>String(s||"").replace(/[^a-z0-9_.-]/gi,"_");const siteKey=sanitizeKey(vars.site_key||location.host);const localCacheKey=`dfehc_heartbeat_server_load:${siteKey}:${vars.ver||"1"}`;const memoryCache=Object.create(null);const SUPPORTS_BC=typeof window.BroadcastChannel==="function";const bc=SUPPORTS_BC?new BroadcastChannel("dfehc-heartbeat"):null;const TAB_ID=Math.random().toString(36).slice(2)+Date.now().toString(36);let isLeader=false;let lastLeaderBeat=0;const LEADER_TTL=5e3;const leaderKey=`${localCacheKey}:leader`;function readLeader(){try{const raw=localStorage.getItem(leaderKey);if(!raw)return null;const obj=JSON.parse(raw);if(!obj||typeof obj!=="object")return null;const ts=Number(obj.ts);const tab=String(obj.tab||"");if(!Number.isFinite(ts)||!tab)return null;return{ts,tab}}catch{return null}}function writeLeader(ts,tab){try{localStorage.setItem(leaderKey,JSON.stringify({ts,tab}))}catch{}}function clearLeader(tab){try{const cur=readLeader();if(!cur||cur.tab===tab)localStorage.removeItem(leaderKey)}catch{}}function tryBecomeLeader(){if(document.hidden)return false;const now=Date.now();const cur=readLeader();if(!cur||now-cur.ts>LEADER_TTL){writeLeader(now,TAB_ID);isLeader=true;lastLeaderBeat=now;if(bc)bc.postMessage({t:"leader",ts:now,tab:TAB_ID});return true}if(cur.tab===TAB_ID){isLeader=true;lastLeaderBeat=cur.ts;return true}return false}function renewLeadership(){if(!isLeader)return;const now=Date.now();if(now-lastLeaderBeat>2e3){writeLeader(now,TAB_ID);lastLeaderBeat=now}}function relinquishLeadership(){if(!isLeader)return;clearLeader(TAB_ID);isLeader=false}const getLocalCache=(key)=>{try{const raw=window.localStorage.getItem(key);if(!raw)return null;const parsed=JSON.parse(raw);if(!parsed||typeof parsed!=="object")return null;const timestamp=Number(parsed.timestamp);const data=parsed.data;if(!Number.isFinite(timestamp))return null;return Date.now()-timestamp<cacheTimeout?data:null}catch{try{window.localStorage.removeItem(key)}catch{}return null}};const setLocalCache=(key,data)=>{try{const jitterMs=Math.floor(Math.random()*5e3);window.localStorage.setItem(key,JSON.stringify({timestamp:Date.now()-jitterMs,data}))}catch{}};const nearestFrom=(value,list)=>list.reduce((best,v)=>Math.abs(v-value)<Math.abs(best-value)?v:best,list[0]);const trafficLevel=(load)=>load<=50?"low":load<=75?"medium":"high";const calcRecommendedIntervalFromLoad=(load)=>{const bucket=Math.max(0,Math.min(100,Math.round(load)));if(Object.prototype.hasOwnProperty.call(memoryCache,bucket))return memoryCache[bucket];const level=trafficLevel(load);const opts=intervals[level];const min=Math.max(MIN,opts[0]);const max=Math.min(MAX,opts[opts.length-1]);const ratio=Math.max(0,Math.min(1,load/LOAD_CAP));const raw=min+(max-min)*ratio;const clamped=Math.min(Math.max(raw,MIN),MAX);const snapped=nearestFrom(clamped,opts);memoryCache[bucket]=snapped;return snapped};const toNum=(v)=>{const n=Number(v);return Number.isFinite(n)?n:null};const coerceServerPayload=(val)=>{if(val&&typeof val==="object"){const interval=toNum(val.interval);const load=toNum(val.load);return{interval,load}}if(Number.isFinite(Number(val))){const n=Number(val);const mode=(vars.server_payload||"auto").toLowerCase();if(mode==="interval")return{interval:n,load:null};if(mode==="load")return{interval:null,load:n};if(n>=0&&n<=100)return{interval:null,load:n};if(n>100)return{interval:n,load:null}}return{interval:null,load:null}};const debounce=(fn,wait)=>{let t;return(...args)=>{clearTimeout(t);t=setTimeout(()=>fn(...args),wait)}};let lastInterval=null;const applyPresetThenExact=(secs)=>{const hb=wp&&wp.heartbeat;if(!hb||typeof hb.interval!=="function")return;if(typeof secs!=="number"||!Number.isFinite(secs))return;if(lastInterval!==null&&Math.round(lastInterval)===Math.round(secs))return;const preset=secs<=20?"fast":secs<=60?"standard":"slow";try{hb.interval(preset);hb.interval(secs)}catch{}lastInterval=secs};const setHeartbeatInterval=(secs)=>{let s=secs;if(s>=60){const jitter=Math.floor(Math.random()*3)-1;s=Math.max(MIN,Math.min(MAX,s+jitter))}applyPresetThenExact(s)};const debouncedSetInterval=debounce(setHeartbeatInterval,150);let failureCount=0;let nextRetryAt=0;const backoffMs=()=>Math.min(12e4,2e3*Math.pow(2,Math.min(failureCount,5)));const fetchWithTimeout=(url,opts,ms)=>{if("AbortController"in window){const ctrl=new AbortController;const t=setTimeout(()=>ctrl.abort(),ms);return fetch(url,{...opts,signal:ctrl.signal}).finally(()=>clearTimeout(t))}return Promise.race([fetch(url,opts),new Promise((_,rej)=>setTimeout(()=>rej(new Error("timeout")),ms))])};let reqSeq=0;const fetchServerData=async(nonce,rid)=>{if(Date.now()<nextRetryAt){const base=Number(vars.fallback_interval)||60;const jitter=Math.floor(Math.random()*7)-3;return{interval:Math.max(MIN,Math.min(MAX,base+jitter)),load:null,rid}}if(Math.random()>=cacheBypassRate){const cached=getLocalCache(localCacheKey);if(cached!==null)return{...coerceServerPayload(cached),rid}}const I_AM_LEADER=tryBecomeLeader();if(!I_AM_LEADER){const wait=Math.min(1500,Math.max(150,Math.floor(Math.random()*600)));await new Promise(r=>setTimeout(r,wait));const cached=getLocalCache(localCacheKey);if(cached!==null)return{...coerceServerPayload(cached),rid}}if(navigator.onLine===false){const base=Number(vars.fallback_interval)||60;const jitter=Math.floor(Math.random()*7)-3;return{interval:Math.max(MIN,Math.min(MAX,base+jitter)),load:null,rid}}const url=vars.ajax_url||typeof window.ajaxurl!=="undefined"?window.ajaxurl:`${location.origin.replace(/\/$/,"")}/wp-admin/admin-ajax.php`;try{const body=new URLSearchParams({action:"get_server_load"});if(nonce)body.append("nonce",nonce);const res=await fetchWithTimeout(url,{method:"POST",body,credentials:"same-origin",headers:{"Content-Type":"application/x-www-form-urlencoded; charset=UTF-8","Cache-Control":"no-store"}},6e3);if(!res.ok)throw new Error(String(res.status));const json=await res.json();if(!json||json.success!==true)throw new Error("bad payload");setLocalCache(localCacheKey,json.data);if(bc)bc.postMessage({t:"payload",data:json.data});failureCount=0;nextRetryAt=0;renewLeadership();return{...coerceServerPayload(json.data),rid}}catch{failureCount+=1;nextRetryAt=Date.now()+backoffMs();const base=Number(vars.fallback_interval)||60;const jitter=Math.floor(Math.random()*7)-3;relinquishLeadership();return{interval:Math.max(MIN,Math.min(MAX,base+jitter)),load:null,rid}}};const heartbeat={update(interval){debouncedSetInterval(interval)},updateUI(interval){const sel=document.querySelector("#dfehc-heartbeat-interval");if(sel)sel.value=String(interval);debouncedSetInterval(interval)},async init(nonce){const conn=navigator.connection||navigator.mozConnection||navigator.webkitConnection;const et=conn&&conn.effectiveType?String(conn.effectiveType):"";if("deviceMemory"in navigator&&navigator.deviceMemory<2)return;if(conn&&conn.saveData||et.startsWith("2g")||et.startsWith("slow-2g"))return;const myRid=++reqSeq;const payload=await fetchServerData(nonce,myRid);if(payload.rid!==reqSeq)return;const interval=payload.interval;const load=payload.load;const snapList=intervals[trafficLevel(typeof load==="number"?load:LOAD_CAP)]||intervals.low;const finalInterval=typeof interval==="number"?nearestFrom(Math.min(Math.max(interval,MIN),MAX),snapList):calcRecommendedIntervalFromLoad(typeof load==="number"?load:60);this.updateUI(finalInterval)}};if(bc){bc.onmessage=(e)=>{const m=e.data||{};if(m.t==="leader"){const tab=String(m.tab||"");if(tab&&tab!==TAB_ID)isLeader=false}if(m.t==="payload"){try{setLocalCache(localCacheKey,m.data)}catch{}Object.keys(memoryCache).forEach(k=>delete memoryCache[k]);const coerced=coerceServerPayload(m.data);const interval=coerced.interval;const load=coerced.load;const snapList=intervals[trafficLevel(typeof load==="number"?load:LOAD_CAP)]||intervals.low;const finalInterval=typeof interval==="number"?nearestFrom(Math.min(Math.max(interval,MIN),MAX),snapList):calcRecommendedIntervalFromLoad(typeof load==="number"?load:60);debouncedSetInterval(finalInterval)}}}window.addEventListener("storage",(e)=>{if(e.key!==localCacheKey||!e.newValue)return;try{const v=JSON.parse(e.newValue);if(!v||typeof v!=="object"||!("data"in v))return;Object.keys(memoryCache).forEach(k=>delete memoryCache[k]);const coerced=coerceServerPayload(v.data);const interval=coerced.interval;const load=coerced.load;const snapList=intervals[trafficLevel(typeof load==="number"?load:LOAD_CAP)]||intervals.low;const finalInterval=typeof interval==="number"?nearestFrom(Math.min(Math.max(interval,MIN),MAX),snapList):calcRecommendedIntervalFromLoad(typeof load==="number"?load:60);debouncedSetInterval(finalInterval)}catch{}});let memoVersion=String(vars.ver||"1");function maybeResetMemo(){const v=String(vars.ver||"1");if(v!==memoVersion){memoVersion=v;Object.keys(memoryCache).forEach(k=>delete memoryCache[k])}}function cleanup(){relinquishLeadership();if(bc&&typeof bc.close==="function"){try{bc.close()}catch{}}}document.addEventListener("visibilitychange",()=>{if(!document.hidden)nextRetryAt=0});window.addEventListener("online",()=>{nextRetryAt=0});window.addEventListener("pageshow",(e)=>{if(e.persisted)nextRetryAt=0});window.addEventListener("pagehide",cleanup,{once:true});window.addEventListener("beforeunload",cleanup,{once:true});document.addEventListener("DOMContentLoaded",()=>{maybeResetMemo();if((vars.heartbeat_control_enabled||"")!=="1")return;const nonce=vars.nonce;if("requestIdleCallback"in window){window.requestIdleCallback(()=>heartbeat.init(nonce))}else{setTimeout(()=>heartbeat.init(nonce),100)}const sel=document.querySelector("#dfehc-heartbeat-interval");if(sel){sel.addEventListener("change",function(){const val=parseInt(this.value,10);if(!Number.isNaN(val))heartbeat.update(Math.min(Math.max(val,MIN),MAX))})}})})(window.wp||{});
     1((wp)=>{const intervals={low:[15,30,60,120,180,240,300],medium:[30,60,120,180,240,300],high:[60,120,180,240,300]};const vars=typeof window.dfehc_heartbeat_vars==="object"?window.dfehc_heartbeat_vars:{};const clampNumber=(v,lo,hi,d)=>{const n=Number(v);if(!Number.isFinite(n))return d;return Math.min(hi,Math.max(lo,n))};const DEFAULT_MIN=15;const DEFAULT_MAX=300;const ABS_MIN=1;const ABS_MAX=3600;let MIN=clampNumber(vars.min_interval,ABS_MIN,ABS_MAX,DEFAULT_MIN);let MAX=clampNumber(vars.max_interval,MIN,ABS_MAX,DEFAULT_MAX);if(MIN>MAX){const t=MIN;MIN=MAX;MAX=t}const LOAD_CAP=Number.isFinite(vars.max_server_load)?Number(vars.max_server_load):85;const msOrAuto=(v,d)=>{const n=Number(v);if(!Number.isFinite(n))return d;return n<=600?n*1e3:n};const cacheTimeout=msOrAuto(vars.cache_duration,5*60*1e3);const cacheBypassRate=Math.min(1,Math.max(0,Number(vars.cache_bypass_rate)||.05));const sanitizeKey=s=>String(s||"").replace(/[^a-z0-9_.-]/gi,"_");const siteKey=sanitizeKey(vars.site_key||location.host);const localCacheKey=`dfehc_heartbeat_server_load:${siteKey}:${vars.ver||"1"}`;const memoryCache=Object.create(null);const SUPPORTS_BC=typeof window.BroadcastChannel==="function";const bc=SUPPORTS_BC?new BroadcastChannel("dfehc-heartbeat"):null;const TAB_ID=Math.random().toString(36).slice(2)+Date.now().toString(36);let isLeader=false;let lastLeaderBeat=0;const LEADER_TTL=5e3;const leaderKey=`${localCacheKey}:leader`;function readLeader(){try{const raw=localStorage.getItem(leaderKey);if(!raw)return null;const obj=JSON.parse(raw);if(!obj||typeof obj!=="object")return null;const ts=Number(obj.ts);const tab=String(obj.tab||"");if(!Number.isFinite(ts)||!tab)return null;return{ts,tab}}catch{return null}}function writeLeader(ts,tab){try{localStorage.setItem(leaderKey,JSON.stringify({ts,tab}))}catch{}}function clearLeader(tab){try{const cur=readLeader();if(!cur||cur.tab===tab)localStorage.removeItem(leaderKey)}catch{}}function tryBecomeLeader(){if(document.hidden)return false;const now=Date.now();const cur=readLeader();if(!cur||now-cur.ts>LEADER_TTL){writeLeader(now,TAB_ID);isLeader=true;lastLeaderBeat=now;if(bc)bc.postMessage({t:"leader",ts:now,tab:TAB_ID});return true}if(cur.tab===TAB_ID){isLeader=true;lastLeaderBeat=cur.ts;return true}return false}function renewLeadership(){if(!isLeader)return;const now=Date.now();if(now-lastLeaderBeat>2e3){writeLeader(now,TAB_ID);lastLeaderBeat=now}}function relinquishLeadership(){if(!isLeader)return;clearLeader(TAB_ID);isLeader=false}const getLocalCache=key=>{try{const raw=window.localStorage.getItem(key);if(!raw)return null;const parsed=JSON.parse(raw);if(!parsed||typeof parsed!=="object")return null;const timestamp=Number(parsed.timestamp);const data=parsed.data;if(!Number.isFinite(timestamp))return null;return Date.now()-timestamp<cacheTimeout?data:null}catch{try{window.localStorage.removeItem(key)}catch{}return null}};const setLocalCache=(key,data)=>{try{const jitterMs=Math.floor(Math.random()*5e3);window.localStorage.setItem(key,JSON.stringify({timestamp:Date.now()-jitterMs,data}))}catch{}};const nearestFrom=(value,list)=>list.reduce((best,v)=>Math.abs(v-value)<Math.abs(best-value)?v:best,list[0]);const trafficLevel=load=>load<=50?"low":load<=75?"medium":"high";const calcRecommendedIntervalFromLoad=load=>{const bucket=Math.max(0,Math.min(100,Math.round(load)));if(Object.prototype.hasOwnProperty.call(memoryCache,bucket))return memoryCache[bucket];const level=trafficLevel(load);const opts=intervals[level];const min=Math.max(MIN,opts[0]);const max=Math.min(MAX,opts[opts.length-1]);const ratio=Math.max(0,Math.min(1,load/LOAD_CAP));const raw=min+(max-min)*ratio;const clamped=Math.min(Math.max(raw,MIN),MAX);const snapped=nearestFrom(clamped,opts);memoryCache[bucket]=snapped;return snapped};const toNum=v=>{const n=Number(v);return Number.isFinite(n)?n:null};const coerceServerPayload=val=>{if(val&&typeof val==="object"){const interval=toNum(val.interval);const load=toNum(val.load);return{interval,load}}if(Number.isFinite(Number(val))){const n=Number(val);const mode=(vars.server_payload||"auto").toLowerCase();if(mode==="interval")return{interval:n,load:null};if(mode==="load")return{interval:null,load:n};if(n>=0&&n<=100)return{interval:null,load:n};if(n>100)return{interval:n,load:null}}return{interval:null,load:null}};const debounce=(fn,wait)=>{let t;return(...args)=>{clearTimeout(t);t=setTimeout(()=>fn(...args),wait)}};let lastInterval=null;const applyPresetThenExact=secs=>{const hb=wp&&wp.heartbeat;if(!hb||typeof hb.interval!=="function")return;if(typeof secs!=="number"||!Number.isFinite(secs))return;if(lastInterval!==null&&Math.round(lastInterval)===Math.round(secs))return;const preset=secs<=20?"fast":secs<=60?"standard":"slow";try{hb.interval(preset);hb.interval(secs)}catch{}lastInterval=secs};const setHeartbeatInterval=secs=>{let s=secs;if(s>=60){const jitter=Math.floor(Math.random()*3)-1;s=Math.max(MIN,Math.min(MAX,s+jitter))}applyPresetThenExact(s)};const debouncedSetInterval=debounce(setHeartbeatInterval,150);let failureCount=0;let nextRetryAt=0;const backoffMs=()=>Math.min(12e4,2e3*Math.pow(2,Math.min(failureCount,5)));const fetchWithTimeout=(url,opts,ms)=>{if("AbortController"in window){const ctrl=new AbortController;const t=setTimeout(()=>ctrl.abort(),ms);return fetch(url,{...opts,signal:ctrl.signal}).finally(()=>clearTimeout(t))}return Promise.race([fetch(url,opts),new Promise((_,rej)=>setTimeout(()=>rej(new Error("timeout")),ms))])};let reqSeq=0;const fetchServerData=async(nonce,rid)=>{if(Date.now()<nextRetryAt){const base=Number(vars.fallback_interval)||60;const jitter=Math.floor(Math.random()*7)-3;return{interval:Math.max(MIN,Math.min(MAX,base+jitter)),load:null,rid}}if(Math.random()>=cacheBypassRate){const cached=getLocalCache(localCacheKey);if(cached!==null)return{...coerceServerPayload(cached),rid}}const I_AM_LEADER=tryBecomeLeader();if(!I_AM_LEADER){const wait=Math.min(1500,Math.max(150,Math.floor(Math.random()*600)));await new Promise(r=>setTimeout(r,wait));const cached=getLocalCache(localCacheKey);if(cached!==null)return{...coerceServerPayload(cached),rid}}if(navigator.onLine===false){const base=Number(vars.fallback_interval)||60;const jitter=Math.floor(Math.random()*7)-3;return{interval:Math.max(MIN,Math.min(MAX,base+jitter)),load:null,rid}}const url=vars.ajax_url||typeof window.ajaxurl!=="undefined"?window.ajaxurl:`${location.origin.replace(/\/$/,"")}/wp-admin/admin-ajax.php`;try{const body=new URLSearchParams({action:"dfehc_update_heartbeat_interval"});if(nonce)body.append("nonce",nonce);const res=await fetchWithTimeout(url,{method:"POST",body,credentials:"same-origin",headers:{"Content-Type":"application/x-www-form-urlencoded; charset=UTF-8","Cache-Control":"no-store"}},6e3);if(!res.ok)throw new Error(String(res.status));const json=await res.json();if(!json||json.success!==true)throw new Error("bad payload");setLocalCache(localCacheKey,json.data);if(bc)bc.postMessage({t:"payload",data:json.data});failureCount=0;nextRetryAt=0;renewLeadership();return{...coerceServerPayload(json.data),rid}}catch{failureCount+=1;nextRetryAt=Date.now()+backoffMs();const base=Number(vars.fallback_interval)||60;const jitter=Math.floor(Math.random()*7)-3;relinquishLeadership();return{interval:Math.max(MIN,Math.min(MAX,base+jitter)),load:null,rid}}};const heartbeat={update(interval){debouncedSetInterval(interval)},updateUI(interval){const sel=document.querySelector("#dfehc-heartbeat-interval");if(sel)sel.value=String(interval);debouncedSetInterval(interval)},async init(nonce){const conn=navigator.connection||navigator.mozConnection||navigator.webkitConnection;const et=conn&&conn.effectiveType?String(conn.effectiveType):"";if("deviceMemory"in navigator&&navigator.deviceMemory<2)return;if(conn&&conn.saveData||et.startsWith("2g")||et.startsWith("slow-2g"))return;const myRid=++reqSeq;const payload=await fetchServerData(nonce,myRid);if(payload.rid!==reqSeq)return;const interval=payload.interval;const load=payload.load;const snapList=intervals[trafficLevel(typeof load==="number"?load:LOAD_CAP)]||intervals.low;const finalInterval=typeof interval==="number"?nearestFrom(Math.min(Math.max(interval,MIN),MAX),snapList):calcRecommendedIntervalFromLoad(typeof load==="number"?load:60);this.updateUI(finalInterval)}};if(bc){bc.onmessage=e=>{const m=e.data||{};if(m.t==="leader"){const tab=String(m.tab||"");if(tab&&tab!==TAB_ID)isLeader=false}if(m.t==="payload"){try{setLocalCache(localCacheKey,m.data)}catch{}Object.keys(memoryCache).forEach(k=>delete memoryCache[k]);const coerced=coerceServerPayload(m.data);const interval=coerced.interval;const load=coerced.load;const snapList=intervals[trafficLevel(typeof load==="number"?load:LOAD_CAP)]||intervals.low;const finalInterval=typeof interval==="number"?nearestFrom(Math.min(Math.max(interval,MIN),MAX),snapList):calcRecommendedIntervalFromLoad(typeof load==="number"?load:60);debouncedSetInterval(finalInterval)}}}window.addEventListener("storage",e=>{if(e.key!==localCacheKey||!e.newValue)return;try{const v=JSON.parse(e.newValue);if(!v||typeof v!=="object"||!("data"in v))return;Object.keys(memoryCache).forEach(k=>delete memoryCache[k]);const coerced=coerceServerPayload(v.data);const interval=coerced.interval;const load=coerced.load;const snapList=intervals[trafficLevel(typeof load==="number"?load:LOAD_CAP)]||intervals.low;const finalInterval=typeof interval==="number"?nearestFrom(Math.min(Math.max(interval,MIN),MAX),snapList):calcRecommendedIntervalFromLoad(typeof load==="number"?load:60);debouncedSetInterval(finalInterval)}catch{}});let memoVersion=String(vars.ver||"1");function maybeResetMemo(){const v=String(vars.ver||"1");if(v!==memoVersion){memoVersion=v;Object.keys(memoryCache).forEach(k=>delete memoryCache[k])}}function cleanup(){relinquishLeadership();if(bc&&typeof bc.close==="function"){try{bc.close()}catch{}}}document.addEventListener("visibilitychange",()=>{if(!document.hidden)nextRetryAt=0});window.addEventListener("online",()=>{nextRetryAt=0});window.addEventListener("pageshow",e=>{if(e.persisted)nextRetryAt=0});window.addEventListener("pagehide",cleanup,{once:true});window.addEventListener("beforeunload",cleanup,{once:true});document.addEventListener("DOMContentLoaded",()=>{maybeResetMemo();if((vars.heartbeat_control_enabled||"")!=="1")return;const nonce=vars.nonce;if("requestIdleCallback"in window){window.requestIdleCallback(()=>heartbeat.init(nonce))}else{setTimeout(()=>heartbeat.init(nonce),100)}const sel=document.querySelector("#dfehc-heartbeat-interval");if(sel){sel.addEventListener("change",function(){const val=parseInt(this.value,10);if(!Number.isNaN(val))heartbeat.update(Math.min(Math.max(val,MIN),MAX))})}})})(window.wp||{});
  • dynamic-front-end-heartbeat-control/trunk/readme.txt

    r3405181 r3412283  
    33Tested up to:      6.9
    44Requires PHP:      7.2
    5 Stable tag:        1.2.995
     5Stable tag:        1.2.996
    66License:           GPLv2 or later
    77License URI:       https://www.gnu.org/licenses/gpl-2.0.html
     
    1717For the best outcome it's recommended that you have a caching option configured as well as optimized pages(minified CSS, JavaScript, including optimized images).
    1818
    19 <strong>If you already have the plugin files on your device you can follow the steps:</strong>
    20 
    21 Step 1: Login to your WordPress Dashboard.
    22 
    23 Step 2: Click on "Plugins" on the left-hand side menu, then select "Add New".
    24 
    25 Step 3: Click on the "Upload Plugin" button at the top of the page.
    26 
    27 Step 4: Click on the "Choose File" button and browse to the location on your computer where the plugin file is saved. Select the plugin dynamic-heartbeat.zip file and click on "Open".
    28 
    29 Step 5: Once the plugin file is uploaded, click on the "Install Now" button.
    30 
    31 Step 6: After installation, click on the "Activate" button to activate the plugin.
    32 
    33 Step 7: Once activated, the plugin will begin working right away. Allow a few visits on your pages to let the plugin fully determine the best heartbeat interval. Make sure you clear your cache after activation.
    34 
    3519<strong>Additional important information:</strong>
    3620
    37 - Some caching plugins offer manual heartbeat control. For the best outcome, make sure that you only have this plugin controlling your heartbeat intervals automatically. This plugin is designed to integrate with any other plugins you might have installed. Compatible with other services, enhancing overall heartbeat performance and ensuring that you can leverage all the benefits that other plugins and CDNs bring to your site.
     21- Some caching plugins offer manual heartbeat control. For best results, allow this plugin to manage your heartbeat intervals automatically. You do not need to configure a manual frequency in any other plugin—feel free to use their other features, but avoid enabling their manual heartbeat settings.
    3822- Verified GDPR compliance: the plugin does not collect or store any personal data.
     23- Rigorously tested to ensure reliable performance during high-traffic surges.
    3924
    4025== Installation ==
     
    5237With a dynamic heartbeat frequency, your website can consistently operate at optimal performance. This is especially valuable for sites in highly competitive niches where every metric affects search rankings, ad performance, and even hosting costs. A real-time adaptive frequency ensures each visitor receives the most efficient heartbeat interval, reducing response times, improving page load speed, and minimizing unnecessary server load.
    5338
    54 = I have existing performance and SEO optimization plugins. Will this plugin cause any conflicts?
    55 Not at all. This plugin is built to enhance your website’s performance and is fully compatible with all major performance and SEO tools. The only potential overlap is that some of these plugins may include an option to set a manual heartbeat frequency. This is the only area where settings can conflict.
     39= How do I configure the plugin on my website? =
     40The plugin automatically delivers the most optimal heartbeat frequency by analyzing your hosting environment, website size, and real usage patterns. In most cases, no user intervention is required after activation.
    5641
    57 Even if both are enabled, your site will not crash. However, to ensure the heartbeat frequency remains dynamically managed, avoid enabling or configuring manual heartbeat frequency settings in other caching or performance plugins while using this plugin.
     42You can access Settings → DFEHC in the WordPress admin to view available configuration options, such as setting a manual heartbeat frequency or disabling the heartbeat entirely. For advanced users working on specific or unusual projects, the plugin also provides several filters that allow fine-tuning and precise control over the heartbeat pace.
     43
     44= I have existing performance and SEO optimization plugins. Will this plugin cause any conflicts? =
     45Not at all. This plugin is built to enhance your website’s performance and is fully compatible with all major performance and SEO tools. The only potential overlap is that some of these plugins may include an option to set a manual heartbeat frequency. This is the only area where settings can overlap.
     46
     47Even if both are enabled, your website will not crash. However, to ensure the heartbeat frequency remains dynamically managed, avoid enabling or configuring manual heartbeat frequency settings in other caching or performance plugins while using this plugin.
    5848
    5949== Changelog ==
     50
     51= 1.2.996 =
     52
     53* General enhancements.
    6054
    6155= 1.2.995 =
  • dynamic-front-end-heartbeat-control/trunk/visitor/cookie-helper.php

    r3396626 r3412283  
    4646}
    4747
     48if (!function_exists('dfehc_client_ip')) {
     49    function dfehc_client_ip(): string
     50    {
     51        $ip = (string) ($_SERVER['REMOTE_ADDR'] ?? '0.0.0.0');
     52        return (string) apply_filters('dfehc_client_ip', $ip);
     53    }
     54}
     55
    4856if (!function_exists('dfehc_set_transient_noautoload')) {
    4957    function dfehc_set_transient_noautoload(string $key, $value, int $expiration): void
    5058    {
    51         if (wp_using_ext_object_cache()) {
     59        $group = defined('DFEHC_CACHE_GROUP') ? DFEHC_CACHE_GROUP : 'dfehc';
     60
     61        if (function_exists('wp_using_ext_object_cache') && wp_using_ext_object_cache()) {
    5262            if (function_exists('wp_cache_add')) {
    53                 if (!wp_cache_add($key, $value, defined('DFEHC_CACHE_GROUP') ? DFEHC_CACHE_GROUP : 'dfehc', $expiration)) {
    54                     wp_cache_set($key, $value, defined('DFEHC_CACHE_GROUP') ? DFEHC_CACHE_GROUP : 'dfehc', $expiration);
     63                if (!wp_cache_add($key, $value, $group, $expiration)) {
     64                    wp_cache_set($key, $value, $group, $expiration);
    5565                }
    5666            } else {
    57                 wp_cache_set($key, $value, defined('DFEHC_CACHE_GROUP') ? DFEHC_CACHE_GROUP : 'dfehc', $expiration);
     67                wp_cache_set($key, $value, $group, $expiration);
    5868            }
    5969            return;
    6070        }
     71
    6172        set_transient($key, $value, $expiration);
     73
    6274        global $wpdb;
    6375        if (!isset($wpdb->options)) {
    6476            return;
    6577        }
    66         $opt_key = "_transient_$key";
     78        $opt_key    = "_transient_$key";
    6779        $opt_key_to = "_transient_timeout_$key";
    6880        $wpdb->suppress_errors(true);
    6981        $autoload = $wpdb->get_var($wpdb->prepare("SELECT autoload FROM {$wpdb->options} WHERE option_name=%s LIMIT 1", $opt_key));
    7082        if ($autoload === 'yes') {
    71             $wpdb->update($wpdb->options, ['autoload' => 'no'], ['option_name' => $opt_key, 'autoload' => 'yes'], ['%s'], ['%s','%s']);
     83            $wpdb->update(
     84                $wpdb->options,
     85                ['autoload' => 'no'],
     86                ['option_name' => $opt_key, 'autoload' => 'yes'],
     87                ['%s'],
     88                ['%s', '%s']
     89            );
    7290        }
    7391        $autoload_to = $wpdb->get_var($wpdb->prepare("SELECT autoload FROM {$wpdb->options} WHERE option_name=%s LIMIT 1", $opt_key_to));
    7492        if ($autoload_to === 'yes') {
    75             $wpdb->update($wpdb->options, ['autoload' => 'no'], ['option_name' => $opt_key_to, 'autoload' => 'yes'], ['%s'], ['%s','%s']);
     93            $wpdb->update(
     94                $wpdb->options,
     95                ['autoload' => 'no'],
     96                ['option_name' => $opt_key_to, 'autoload' => 'yes'],
     97                ['%s'],
     98                ['%s', '%s']
     99            );
    76100        }
    77101        $wpdb->suppress_errors(false);
     
    96120    }
    97121
    98     $len = strlen($ip_bin);
     122    $len      = strlen($ip_bin);
    99123    $max_bits = $len * 8;
    100124    if ($mask < 0 || $mask > $max_bits) {
     
    111135        return true;
    112136    }
    113     $ip_byte  = ord($ip_bin[$bytes]);
    114     $sub_byte = ord($sub_bin[$bytes]);
     137    $ip_byte   = ord($ip_bin[$bytes]);
     138    $sub_byte  = ord($sub_bin[$bytes]);
    115139    $mask_byte = (0xFF << (8 - $bits)) & 0xFF;
    116140    return ($ip_byte & $mask_byte) === ($sub_byte & $mask_byte);
     
    132156function dfehc_select_client_ip_from_xff(string $xff, array $trustedCidrs): ?string
    133157{
    134     $candidates = array_filter(array_map('trim', explode(',', $xff)), 'strlen');
     158    $candidates  = array_filter(array_map('trim', explode(',', $xff)), 'strlen');
    135159    $ipNonTrusted = null;
    136160    foreach ($candidates as $ip) {
     
    183207    }
    184208
    185     $ip     = dfehc_client_ip();
    186     $ipKeyScoped  = dfehc_scoped_key('dfehc_bad_ip_') . md5($ip ?: 'none');
    187     $group  = apply_filters('dfehc_cache_group', defined('DFEHC_CACHE_GROUP') ? DFEHC_CACHE_GROUP : 'dfehc');
     209    $ip    = dfehc_client_ip();
     210    $ipKeyScoped = dfehc_scoped_key('dfehc_bad_ip_') . md5($ip ?: 'none');
     211    $group = apply_filters('dfehc_cache_group', defined('DFEHC_CACHE_GROUP') ? DFEHC_CACHE_GROUP : 'dfehc');
    188212
    189213    if ($ip && function_exists('wp_using_ext_object_cache') && wp_using_ext_object_cache() && function_exists('wp_cache_get')) {
     
    212236    if (function_exists('wp_is_json_request') && wp_is_json_request()) return false;
    213237    if (function_exists('rest_get_url_prefix')) {
    214         $p = rest_get_url_prefix();
     238        $p   = rest_get_url_prefix();
    215239        $uri = $_SERVER['REQUEST_URI'] ?? '';
    216240        if ($p && strpos($uri, '/' . trim($p, '/') . '/') === 0) return false;
     
    231255    }
    232256
    233     $ip     = dfehc_client_ip();
    234     $group  = apply_filters('dfehc_cache_group', defined('DFEHC_CACHE_GROUP') ? DFEHC_CACHE_GROUP : 'dfehc');
    235     $maxRPM = (int) apply_filters('dfehc_max_rpm', 120);
     257    $ip    = dfehc_client_ip();
     258    $group = apply_filters('dfehc_cache_group', defined('DFEHC_CACHE_GROUP') ? DFEHC_CACHE_GROUP : 'dfehc');
     259    $maxRPM   = (int) apply_filters('dfehc_max_rpm', 120);
    236260    $badIpTtl = (int) apply_filters('dfehc_bad_ip_ttl', HOUR_IN_SECONDS);
    237261    $scopedVisitorKey = dfehc_scoped_key('dfehc_total_visitors');
     
    397421    }
    398422
    399     $cnt = (int) get_transient($scopedVisitorKey);
     423    $cnt  = (int) get_transient($scopedVisitorKey);
    400424    $vTtl = $lifetime + (function_exists('random_int') ? random_int(0, 5) : 0);
    401425    dfehc_set_transient_noautoload($scopedVisitorKey, $cnt + 1, $vTtl);
  • dynamic-front-end-heartbeat-control/trunk/visitor/manager.php

    r3396626 r3412283  
    228228        return;
    229229    }
    230     $prev = ignore_user_abort(true);
     230
     231    $prev = (bool) ignore_user_abort(true);
     232
    231233    if (function_exists('set_time_limit')) {
    232234        @set_time_limit(30);
    233235    }
     236
    234237    try {
    235238        global $wpdb;
     
    237240        $batch_size = (int) apply_filters('dfehc_cleanup_batch_size', $batch_size);
    238241        $batch_size = max(1, min(500, $batch_size));
    239         $ids = $wpdb->get_col($wpdb->prepare(
    240             "SELECT ID FROM $wpdb->users WHERE ID > %d ORDER BY ID ASC LIMIT %d",
    241             $last_id, $batch_size
    242         ));
     242
     243        $ids = $wpdb->get_col(
     244            $wpdb->prepare(
     245                "SELECT ID FROM $wpdb->users WHERE ID > %d ORDER BY ID ASC LIMIT %d",
     246                $last_id,
     247                $batch_size
     248            )
     249        );
     250
    243251        if (!$ids) {
    244252            update_option(dfehc_scoped_key('dfehc_last_cleanup_cron'), time(), false);
    245253            return;
    246254        }
     255
    247256        $cutoff = time() - (int) apply_filters('dfehc_activity_expiration', WEEK_IN_SECONDS);
     257
    248258        foreach ($ids as $id) {
    249259            $ts = (int) get_user_meta($id, $meta_key, true);
     
    252262            }
    253263        }
     264
    254265        if (count($ids) === $batch_size) {
    255             wp_schedule_single_event(time() + 15 + (function_exists('random_int') ? random_int(0, 5) : rand(0, 5)), 'dfehc_cleanup_user_activity', [end($ids), $batch_size]);
    256         }
     266            wp_schedule_single_event(
     267                time() + 15 + (function_exists('random_int') ? random_int(0, 5) : rand(0, 5)),
     268                'dfehc_cleanup_user_activity',
     269                [end($ids), $batch_size]
     270            );
     271        }
     272
    257273        update_option(dfehc_scoped_key('dfehc_last_cleanup_cron'), time(), false);
     274
    258275    } finally {
    259         ignore_user_abort($prev);
     276
     277        ignore_user_abort((bool) $prev);
     278
    260279        dfehc_release_lock($lock);
    261280    }
     
    444463}
    445464
     465function dfehc_ensure_reset_visitors_schedule(): void {
     466    if (!function_exists('wp_next_scheduled') || !function_exists('wp_schedule_event')) {
     467        return;
     468    }
     469    if (wp_next_scheduled('dfehc_reset_total_visitors_event')) {
     470        return;
     471    }
     472    $aligned = time() - time() % 300 + 300;
     473    $ts = $aligned + HOUR_IN_SECONDS;
     474    $ok = wp_schedule_event($ts, 'hourly', 'dfehc_reset_total_visitors_event');
     475    if (!$ok) {
     476        wp_schedule_single_event($ts, 'dfehc_reset_total_visitors_event');
     477    }
     478}
     479add_action('init', 'dfehc_ensure_reset_visitors_schedule', 10);
     480
    446481function dfehc_on_deactivate(): void {
    447482    wp_clear_scheduled_hook('dfehc_process_user_activity');
Note: See TracChangeset for help on using the changeset viewer.