Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
56 changes: 56 additions & 0 deletions assets/css/litespeed.css
Original file line number Diff line number Diff line change
Expand Up @@ -772,6 +772,62 @@ h3 .litespeed-learn-more {
padding: 0.5rem 0.75rem;
}

/* Object Cache benchmark — sits inside .litespeed-block. Theme-friendly outer
border (light grey that reads on both light and dark backgrounds via
currentColor + alpha) plus a slight header tint to distinguish the head
row from data. Background stays transparent so the parent block colour
shows through (no "lighter than parent" wash). */
.litespeed-oc-benchmark-table {
width: auto;
max-width: 100%;
margin: 0.75rem 0 0.75rem 0;
background: transparent;
border-collapse: collapse;
border: 1px solid rgba(127, 127, 127, 0.28);
border-radius: 4px;
overflow: hidden;
}

.litespeed-oc-benchmark-table th,
.litespeed-oc-benchmark-table td {
padding: 0.45rem 0.9rem;
text-align: left;
vertical-align: middle;
background: transparent;
border-bottom: 1px solid rgba(127, 127, 127, 0.18);
}

.litespeed-oc-benchmark-table th {
font-weight: 600;
white-space: nowrap;
background: rgba(127, 127, 127, 0.08);
border-bottom: 1px solid rgba(127, 127, 127, 0.28);
}

.litespeed-oc-benchmark-table tbody tr:last-child td {
border-bottom: 0;
}

.litespeed-oc-benchmark-fastest td {
font-weight: 700;
background: rgba(127, 127, 127, 0.05);
}

.litespeed-oc-bench-spinner {
display: inline-block;
width: 0.85em;
height: 0.85em;
vertical-align: -0.15em;
border: 2px solid currentColor;
border-right-color: transparent;
border-radius: 50%;
animation: litespeed-oc-bench-spin 0.7s linear infinite;
}

@keyframes litespeed-oc-bench-spin {
to { transform: rotate(360deg); }
}

/* =======================================
LAYOUT - block
======================================= */
Expand Down
2 changes: 1 addition & 1 deletion data/const.default.json
Original file line number Diff line number Diff line change
Expand Up @@ -104,7 +104,7 @@
"optm-exc": "",
"optm-guest_only": "1",
"object": "",
"object-kind": "",
"object-kind": "2",
"object-host": "localhost",
"object-port": "11211",
"object-life": "360",
Expand Down
3 changes: 3 additions & 0 deletions src/admin-display.cls.php
Original file line number Diff line number Diff line change
Expand Up @@ -973,6 +973,9 @@ public function form_action( $action = false, $type = false, $has_upload = false
echo '<div class="litespeed-relative">';
} else {
$current = isset( $_SERVER['REQUEST_URI'] ) ? esc_url_raw( wp_unslash( $_SERVER['REQUEST_URI'] ) ) : '';
// Drop transient UI query args so a POST to this URL doesn't re-trigger
// page-level actions (e.g. the Object Cache benchmark) on save.
$current = remove_query_arg( 'ls_oc_benchmark', $current );
if ( $has_upload ) {
echo '<form method="post" action="' . esc_url( $current ) . '" class="litespeed-relative" enctype="multipart/form-data">';
} else {
Expand Down
53 changes: 51 additions & 2 deletions src/admin-settings.cls.php
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,7 @@ public function save( $raw_data ) {

// Validate $child.
if ( self::O_CDN_MAPPING === $id ) {
if ( ! in_array( $child, [ self::CDN_MAPPING_URL, self::CDN_MAPPING_INC_IMG, self::CDN_MAPPING_INC_CSS, self::CDN_MAPPING_INC_JS, self::CDN_MAPPING_FILETYPE ], true ) ) {
if ( ! in_array( $child, [ self::CDN_MAPPING_URL, self::CDN_MAPPING_INC_IMG, self::CDN_MAPPING_INC_CSS, self::CDN_MAPPING_INC_JS, self::CDN_MAPPING_INC_DOCS, self::CDN_MAPPING_INC_FONTS, self::CDN_MAPPING_INC_MEDIA, self::CDN_MAPPING_FILETYPE ], true ) ) {
continue;
}
}
Expand Down Expand Up @@ -139,7 +139,7 @@ public function save( $raw_data ) {
$v = trailingslashit( $v );
}

if ( in_array( $child, [ self::CDN_MAPPING_INC_IMG, self::CDN_MAPPING_INC_CSS, self::CDN_MAPPING_INC_JS ], true ) ) {
if ( in_array( $child, [ self::CDN_MAPPING_INC_IMG, self::CDN_MAPPING_INC_CSS, self::CDN_MAPPING_INC_JS, self::CDN_MAPPING_INC_DOCS, self::CDN_MAPPING_INC_FONTS, self::CDN_MAPPING_INC_MEDIA ], true ) ) {
// Because these can't be auto detected in `config->update()`, need to format here.
$v = 'false' === $v ? 0 : (bool) $v;
}
Expand Down Expand Up @@ -312,6 +312,55 @@ function ( $arr ) {
}
}

// When Method = Automatic AND Object Cache is being kept on, detect a
// working backend now and persist the resolved host/port (and the
// concrete backend) so the user sees what was picked. If OC is off
// or detection fails, leave Auto as-is — runtime resolution handles it.
$kind_submitted = array_key_exists( self::O_OBJECT_KIND, $the_matrix );
$object_submitted = array_key_exists( self::O_OBJECT, $the_matrix );
$object_will_be_on = $object_submitted
? (bool) $the_matrix[ self::O_OBJECT ]
: (bool) $this->conf( self::O_OBJECT );

if (
$kind_submitted
&& Object_Cache::KIND_AUTO === (int) $the_matrix[ self::O_OBJECT_KIND ]
&& $object_will_be_on
) {
$current_host = array_key_exists( self::O_OBJECT_HOST, $the_matrix )
? (string) $the_matrix[ self::O_OBJECT_HOST ]
: (string) $this->conf( self::O_OBJECT_HOST );
$current_port = array_key_exists( self::O_OBJECT_PORT, $the_matrix )
? (int) $the_matrix[ self::O_OBJECT_PORT ]
: (int) $this->conf( self::O_OBJECT_PORT );

$detected = $this->cls( 'Object_Cache' )->auto_detect( [
'host' => $current_host,
'port' => $current_port,
] );

if ( $detected ) {
// Persist the detected host/port for visibility in the form, but
// keep Method = Automatic so each boot re-resolves against the
// live environment.
$the_matrix[ self::O_OBJECT_HOST ] = $detected['host'];
$the_matrix[ self::O_OBJECT_PORT ] = (int) $detected['port'];

$endpoint = '/' === substr( (string) $detected['host'], 0, 1 )
? $detected['host']
: $detected['host'] . ':' . (int) $detected['port'];

Admin_Display::success( sprintf(
/* translators: 1: backend name, 2: host:port or socket path */
__( 'Object Cache auto-detected: %1$s at %2$s.', 'litespeed-cache' ),
$detected['kind_label'],
$endpoint
) );
} else {
Admin_Display::error( __( 'Object Cache auto-detection could not find a working Redis/Valkey or Memcached connection. Method has been left as Automatic; runtime will keep retrying.', 'litespeed-cache' ) );
}
}

// id validation will be inside.
$this->cls( 'Conf' )->update_confs( $the_matrix );

Expand Down
8 changes: 8 additions & 0 deletions src/base.cls.php
Original file line number Diff line number Diff line change
Expand Up @@ -893,6 +893,14 @@ protected function _conf_multi_switch( $id ) {
return self::VAL_ON2;
}

// Object Cache Method is a tri-state: Memcached (0), Redis/Valkey (1),
// Auto (2). Without this registration the bool-default in
// $_default_options collapses Auto to true → 1 in type_casting() and
// the saved value silently becomes Redis on every save.
if ( self::O_OBJECT_KIND === $id ) {
return self::VAL_ON2;
}

return false;
}

Expand Down
31 changes: 31 additions & 0 deletions src/conf.cls.php
Original file line number Diff line number Diff line change
Expand Up @@ -472,6 +472,14 @@ private function _define_cache_on() {
*/
public function update_confs( $the_matrix = [] ) {
if ( $the_matrix ) {
// Drop the cached entries so update_option()'s no-op compare reads
// from MySQL — a stale alloptions blob can otherwise hide the write.
foreach ( array_keys( $the_matrix ) as $id ) {
wp_cache_delete( self::name( $id ), 'options' );
}
wp_cache_delete( 'alloptions', 'options' );
wp_cache_delete( 'notoptions', 'options' );

foreach ( $the_matrix as $id => $val ) {
$this->update( $id, $val );
}
Expand Down Expand Up @@ -510,6 +518,29 @@ public function update_confs( $the_matrix = [] ) {
// Update related files
$this->cls( 'Activation' )->update_files();

// Reset the Object_Cache singleton and bust its cached probes when any
// backend setting changes, so the same-request Status panel re-resolves.
$object_ids = [
self::O_OBJECT,
self::O_OBJECT_KIND,
self::O_OBJECT_HOST,
self::O_OBJECT_PORT,
self::O_OBJECT_LIFE,
self::O_OBJECT_PERSISTENT,
self::O_OBJECT_ADMIN,
self::O_OBJECT_DB_ID,
self::O_OBJECT_USER,
self::O_OBJECT_PSWD,
self::O_OBJECT_GLOBAL_GROUPS,
self::O_OBJECT_NON_PERSISTENT_GROUPS,
];
if ( array_intersect( $object_ids, $this->_updated_ids ) ) {
$this->cls( 'Object_Cache', true );
delete_transient( Object_Cache::TRANS_CONN_TEST );
delete_transient( Object_Cache::TRANS_AUTO_RESOLVED );
delete_transient( Object_Cache::TRANS_BENCHMARK );
}

/**
* CDN related actions - Cloudflare
*/
Expand Down
1 change: 1 addition & 0 deletions src/core.cls.php
Original file line number Diff line number Diff line change
Expand Up @@ -190,6 +190,7 @@ public function init() {
do_action( 'litespeed_init' );
add_action( 'wp_ajax_async_litespeed', 'LiteSpeed\Task::async_litespeed_handler' );
add_action( 'wp_ajax_nopriv_async_litespeed', 'LiteSpeed\Task::async_litespeed_handler' );
add_action( 'wp_ajax_litespeed_oc_benchmark', 'LiteSpeed\Object_Cache::ajax_benchmark' );

// In `after_setup_theme`, before `init` hook
$this->cls( 'Activation' )->auto_update();
Expand Down
Loading