$has_phpredis, 'redis_socket' => $has_redis_socket, 'redis_any' => $has_phpredis || $has_redis_socket, 'memcached' => $has_memcached, 'memcache' => $has_memcache, 'apcu' => $has_apcu, ); } /** * Normalize TCP host:port or Unix domain socket path (stripping optional unix:// or tcp:// schemes). */ public static function normalize_endpoint( $raw_host, $raw_port, $default_port = 6379 ) { $host = trim( (string) $raw_host ); if ( stripos( $host, 'unix://' ) === 0 ) { $host = substr( $host, 7 ); } elseif ( stripos( $host, 'tcp://' ) === 0 ) { $host = substr( $host, 6 ); } if ( $host !== '' && ( strpos( $host, '/' ) === 0 || substr( strtolower( $host ), -5 ) === '.sock' || substr( strtolower( $host ), -7 ) === '.socket' ) ) { if ( strpos( $host, '/' ) !== 0 ) { $host = '/' . ltrim( $host, '/' ); } return array( 'host' => $host, 'port' => 0, 'is_socket' => true, ); } if ( preg_match( '/^([^:\[\]]+):(\d+)$/', $host, $m ) ) { $host = $m[1]; $raw_port = (int) $m[2]; } $port = absint( $raw_port ) > 0 ? absint( $raw_port ) : (int) $default_port; return array( 'host' => $host !== '' ? $host : '127.0.0.1', 'port' => $port, 'is_socket' => false, ); } /** * Check whether a filesystem path is permitted by PHP open_basedir before probing sockets. */ private static function is_path_allowed_by_open_basedir( $path ) { $obd = (string) ini_get( 'open_basedir' ); if ( trim( $obd ) === '' ) { return true; } $norm_target = wp_normalize_path( $path ); foreach ( explode( PATH_SEPARATOR, $obd ) as $allowed ) { $allowed = trim( $allowed ); if ( $allowed === '' ) { continue; } $norm_allowed = rtrim( wp_normalize_path( $allowed ), '/' ); if ( $norm_target === $norm_allowed || strpos( $norm_target, $norm_allowed . '/' ) === 0 ) { return true; } } return false; } /** * Auto-discover active Redis or Memcached Unix Domain Sockets on cPanel / CloudLinux / DirectAdmin / Linux VPS. */ public static function discover_local_sockets( $driver = 'redis' ) { $candidates = array(); $norm_abs = wp_normalize_path( ABSPATH ); $home_dir = ''; if ( preg_match( '#^(/home[^/]*/[^/]+|/var/www/vhosts/[^/]+|/Users/[^/]+)#', $norm_abs, $hm ) ) { $home_dir = $hm[1]; } elseif ( ! empty( $_SERVER['HOME'] ) ) { $home_dir = rtrim( wp_normalize_path( sanitize_text_field( wp_unslash( $_SERVER['HOME'] ) ) ), '/' ); } if ( $driver === 'memcached' || $driver === 'memcache' ) { if ( $home_dir !== '' ) { $candidates[] = $home_dir . '/.memcached/memcached.sock'; $candidates[] = $home_dir . '/memcached/memcached.sock'; $candidates[] = $home_dir . '/tmp/memcached.sock'; $candidates[] = $home_dir . '/.cagefs/tmp/memcached.sock'; } $candidates[] = '/var/run/memcached/memcached.sock'; $candidates[] = '/run/memcached/memcached.sock'; $candidates[] = '/tmp/memcached.sock'; } else { if ( defined( 'WP_REDIS_PATH' ) && is_string( WP_REDIS_PATH ) && WP_REDIS_PATH !== '' ) { $candidates[] = WP_REDIS_PATH; } if ( defined( 'WP_REDIS_HOST' ) && is_string( WP_REDIS_HOST ) && ( strpos( WP_REDIS_HOST, '/' ) === 0 || stripos( WP_REDIS_HOST, 'unix://' ) === 0 ) ) { $candidates[] = preg_replace( '#^unix://#i', '', WP_REDIS_HOST ); } if ( $home_dir !== '' ) { $candidates[] = $home_dir . '/.redis/redis.sock'; $candidates[] = $home_dir . '/redis/redis.sock'; $candidates[] = $home_dir . '/tmp/redis.sock'; $candidates[] = $home_dir . '/.cagefs/tmp/redis.sock'; $candidates[] = $home_dir . '/.cagefs/var/run/redis/redis.sock'; $candidates[] = $home_dir . '/logs/redis.sock'; } $candidates[] = '/var/run/redis/redis.sock'; $candidates[] = '/var/run/redis/redis-server.sock'; $candidates[] = '/run/redis/redis.sock'; $candidates[] = '/run/redis/redis-server.sock'; $candidates[] = '/tmp/redis.sock'; } $found = array(); foreach ( array_unique( $candidates ) as $sock ) { if ( $sock !== '' && self::is_path_allowed_by_open_basedir( $sock ) && @file_exists( $sock ) ) { // phpcs:ignore WordPress.PHP.NoSilencedErrors.Discouraged $found[] = $sock; } } return $found; } /** * Build connection settings array from saved aio_options or explicit override array. * * @param array|null $override Optional explicit form values. * @param bool $rotate_seed Force a brand-new namespace seed so stale backend keys are bypassed. */ public static function build_connection_params( $override = null, $rotate_seed = false ) { global $table_prefix; $opts = get_option( 'aio_options', array() ); $driver = isset( $override['driver'] ) ? sanitize_key( $override['driver'] ) : ( isset( $opts['optObjectCacheDriver'] ) ? sanitize_key( $opts['optObjectCacheDriver'] ) : 'redis' ); if ( ! in_array( $driver, array( 'redis', 'memcached', 'memcache', 'apcu' ), true ) ) { $driver = 'redis'; } $default_port = ( $driver === 'memcached' || $driver === 'memcache' ) ? 11211 : 6379; $raw_host = isset( $override['host'] ) ? sanitize_text_field( $override['host'] ) : ( isset( $opts['txtObjectCacheHost'] ) && $opts['txtObjectCacheHost'] !== '' ? (string) $opts['txtObjectCacheHost'] : '127.0.0.1' ); $raw_port = isset( $override['port'] ) ? absint( $override['port'] ) : ( isset( $opts['txtObjectCachePort'] ) ? absint( $opts['txtObjectCachePort'] ) : $default_port ); $ep = self::normalize_endpoint( $raw_host, $raw_port, $default_port ); $host = $ep['host']; $port = $ep['port']; $db_index = isset( $override['db'] ) ? min( 15, absint( $override['db'] ) ) : ( isset( $opts['txtObjectCacheDb'] ) ? min( 15, absint( $opts['txtObjectCacheDb'] ) ) : 0 ); $raw_pass = isset( $override['password'] ) ? (string) $override['password'] : ''; if ( $raw_pass !== '' && $raw_pass !== '********' ) { $password = $raw_pass; } else { $saved_enc = isset( $opts['txtObjectCachePassword'] ) ? (string) $opts['txtObjectCachePassword'] : ''; $password = class_exists( 'AIO_Crypto' ) ? AIO_Crypto::decrypt( $saved_enc ) : $saved_enc; } $site_hash = substr( md5( ABSPATH . ( defined( 'DB_NAME' ) ? DB_NAME : '' ) . (string) $table_prefix ), 0, 10 ); $seed = ''; if ( ! $rotate_seed ) { $existing = self::read_runtime_config(); if ( ! empty( $existing['seed'] ) && preg_match( '/^[a-f0-9]{6,16}$/', (string) $existing['seed'] ) && isset( $existing['driver'], $existing['host'], $existing['port'], $existing['database'] ) && (string) $existing['driver'] === $driver && (string) $existing['host'] === $host && (int) $existing['port'] === (int) $port && (int) $existing['database'] === (int) $db_index && ( isset( $existing['password'] ) ? (string) $existing['password'] : '' ) === (string) $password ) { $seed = (string) $existing['seed']; } } if ( $seed === '' ) { $seed = self::generate_seed(); } $plugin_file = defined( 'AIO_PATH' ) ? wp_normalize_path( AIO_PATH . 'aio-tools.php' ) : ''; return array( 'driver' => $driver, 'host' => $host, 'port' => $port, 'password' => $password, 'database' => $db_index, 'seed' => $seed, 'prefix' => 'aio_' . $site_hash . '_' . $seed . '_', 'timeout' => 0.25, 'plugin_file' => $plugin_file, ); } /** * Convert low-level socket/PhpRedis errors (e.g. "Connection refused") into clear, actionable guidance. */ public static function format_connection_error( $params, $raw_error = '' ) { $driver = isset( $params['driver'] ) ? strtoupper( (string) $params['driver'] ) : 'REDIS'; $host = isset( $params['host'] ) ? (string) $params['host'] : '127.0.0.1'; $port = isset( $params['port'] ) ? (int) $params['port'] : 6379; $is_socket = strpos( $host, '/' ) === 0; $err_clean = trim( (string) $raw_error ); if ( $is_socket ) { return sprintf( /* translators: 1: Unix socket path, 2: Backend name, 3: OS error detail */ __( 'Cannot connect to %2$s Unix Socket "%1$s" (%3$s). Please verify that the %2$s service is enabled in your Hosting Panel (cPanel / DirectAdmin) and the socket path is correct.', 'aio-tools' ), $host, $driver, $err_clean !== '' ? $err_clean : 'Socket unavailable' ); } if ( $err_clean === '' || stripos( $err_clean, 'Connection refused' ) !== false || stripos( $err_clean, 'actively refused' ) !== false || stripos( $err_clean, 'Failed to connect' ) !== false ) { return sprintf( /* translators: 1: Host IP/domain, 2: TCP Port, 3: Backend name */ __( 'Connection refused at %1$s:%2$d — PHP %3$s extension is installed, but the %3$s server service is not running on TCP port %2$d. Please enable %3$s in your Hosting Panel (cPanel / DirectAdmin / VPS), or enter your hosting Unix Socket path (e.g. /home/user/.redis/redis.sock).', 'aio-tools' ), $host, $port, $driver ); } return sprintf( '%s (%s:%d)', $err_clean, $host, $port ); } /** * Test configured backend, and if default localhost TCP port is refused on shared hosting, * automatically probe local Unix Domain Sockets (e.g. /home/user/.redis/redis.sock). */ public static function try_backend_with_socket_fallback( &$params ) { $res = self::test_backend( $params ); if ( ! empty( $res['ok'] ) ) { return $res; } $host_lower = strtolower( (string) $params['host'] ); if ( $params['driver'] !== 'apcu' && in_array( $host_lower, array( '127.0.0.1', 'localhost', '::1' ), true ) ) { $sockets = self::discover_local_sockets( $params['driver'] ); foreach ( $sockets as $sock_path ) { $cand = $params; $cand['host'] = $sock_path; $cand['port'] = 0; $probe = self::test_backend( $cand ); if ( ! empty( $probe['ok'] ) ) { $params = $cand; $probe['auto_socket'] = $sock_path; return $probe; } } } return $res; } /** * Test live connection roundtrip (write -> read -> delete) and collect server telemetry. */ public static function test_backend( $params ) { $driver = $params['driver']; $ep = self::normalize_endpoint( $params['host'], $params['port'], ( $driver === 'memcached' || $driver === 'memcache' ) ? 11211 : 6379 ); $host = $ep['host']; $port = (int) $ep['port']; $pass = (string) $params['password']; $db = (int) $params['database']; $t0 = microtime( true ); try { // 1. APCu if ( $driver === 'apcu' ) { if ( ! function_exists( 'apcu_store' ) || ! filter_var( ini_get( 'apc.enabled' ), FILTER_VALIDATE_BOOLEAN ) ) { return array( 'ok' => false, 'error' => __( 'APCu extension is not enabled on this PHP server.', 'aio-tools' ), ); } $tkey = $params['prefix'] . '_ping_' . wp_rand( 1000, 9999 ); apcu_store( $tkey, 'ok', 10 ); $val = apcu_fetch( $tkey ); apcu_delete( $tkey ); if ( $val !== 'ok' ) { return array( 'ok' => false, 'error' => __( 'APCu write/read verification failed.', 'aio-tools' ), ); } $sma = function_exists( 'apcu_sma_info' ) ? @apcu_sma_info( true ) : array(); // phpcs:ignore WordPress.PHP.NoSilencedErrors.Discouraged $info = function_exists( 'apcu_cache_info' ) ? @apcu_cache_info( true ) : array(); // phpcs:ignore WordPress.PHP.NoSilencedErrors.Discouraged $used = isset( $sma['seg_size'], $sma['avail_mem'] ) ? max( 0, (int) $sma['seg_size'] - (int) $sma['avail_mem'] ) : 0; return array( 'ok' => true, 'driver_used' => 'APCu Shared Memory', 'ping_ms' => round( ( microtime( true ) - $t0 ) * 1000, 2 ), 'memory_human' => $used > 0 ? size_format( $used, 1 ) : 'Active', 'keys_count' => isset( $info['num_entries'] ) ? (int) $info['num_entries'] : 0, ); } // 2. Memcached (modern extension) if ( $driver === 'memcached' ) { if ( ! class_exists( 'Memcached' ) ) { return array( 'ok' => false, 'error' => __( 'PHP Memcached extension is not installed on this server.', 'aio-tools' ), ); } $mc = new Memcached(); if ( defined( 'Memcached::OPT_CONNECT_TIMEOUT' ) ) { $mc->setOption( Memcached::OPT_CONNECT_TIMEOUT, 300 ); } $mc->addServer( $host, $ep['is_socket'] ? 0 : $port ); $tkey = $params['prefix'] . '_ping_' . wp_rand( 1000, 9999 ); $mc->set( $tkey, 'ok', 10 ); $val = $mc->get( $tkey ); $mc->delete( $tkey ); if ( $val !== 'ok' ) { return array( 'ok' => false, 'error' => self::format_connection_error( $params, 'Connection refused' ), ); } $stats_all = @$mc->getStats(); // phpcs:ignore WordPress.PHP.NoSilencedErrors.Discouraged $first = is_array( $stats_all ) ? reset( $stats_all ) : array(); $bytes = isset( $first['bytes'] ) ? (int) $first['bytes'] : 0; $items = isset( $first['curr_items'] ) ? (int) $first['curr_items'] : 0; return array( 'ok' => true, 'driver_used' => 'Memcached Extension', 'ping_ms' => round( ( microtime( true ) - $t0 ) * 1000, 2 ), 'memory_human' => size_format( $bytes, 1 ), 'keys_count' => $items, ); } // 3. Legacy Memcache if ( $driver === 'memcache' ) { if ( ! class_exists( 'Memcache' ) ) { return array( 'ok' => false, 'error' => __( 'PHP Memcache (legacy) extension is not installed on this server.', 'aio-tools' ), ); } $mc = new Memcache(); $connected = $ep['is_socket'] ? @$mc->connect( 'unix://' . $host, 0, 1 ) : @$mc->connect( $host, $port, 1 ); // phpcs:ignore WordPress.PHP.NoSilencedErrors.Discouraged if ( ! $connected ) { return array( 'ok' => false, 'error' => self::format_connection_error( $params, 'Connection refused' ), ); } $tkey = $params['prefix'] . '_ping_' . wp_rand( 1000, 9999 ); $mc->set( $tkey, 'ok', 0, 10 ); $val = $mc->get( $tkey ); $mc->delete( $tkey ); $stats = @$mc->getStats(); // phpcs:ignore WordPress.PHP.NoSilencedErrors.Discouraged $mc->close(); if ( $val !== 'ok' ) { return array( 'ok' => false, 'error' => __( 'Memcache verification failed.', 'aio-tools' ), ); } return array( 'ok' => true, 'driver_used' => 'Memcache Extension', 'ping_ms' => round( ( microtime( true ) - $t0 ) * 1000, 2 ), 'memory_human' => isset( $stats['bytes'] ) ? size_format( (int) $stats['bytes'], 1 ) : 'Active', 'keys_count' => isset( $stats['curr_items'] ) ? (int) $stats['curr_items'] : 0, ); } // 4. Redis: Prefer native PhpRedis if loaded, else Pure-PHP RESP Socket if ( class_exists( 'Redis' ) && method_exists( 'Redis', 'connect' ) ) { $r = new Redis(); $conn_err = ''; try { $ok = $ep['is_socket'] ? @$r->connect( $host, 0, 0.4 ) // phpcs:ignore WordPress.PHP.NoSilencedErrors.Discouraged : @$r->connect( $host, $port, 0.4 ); // phpcs:ignore WordPress.PHP.NoSilencedErrors.Discouraged } catch ( Throwable $re ) { $ok = false; $conn_err = $re->getMessage(); } if ( ! $ok ) { return array( 'ok' => false, 'error' => self::format_connection_error( $params, $conn_err !== '' ? $conn_err : 'Connection refused' ), ); } if ( $pass !== '' ) { $auth_ok = false; try { $auth_ok = (bool) @$r->auth( $pass ); // phpcs:ignore WordPress.PHP.NoSilencedErrors.Discouraged } catch ( Throwable $ae ) { $auth_ok = false; } if ( ! $auth_ok && strpos( $pass, ':' ) !== false ) { try { $auth_ok = (bool) @$r->auth( explode( ':', $pass, 2 ) ); // phpcs:ignore WordPress.PHP.NoSilencedErrors.Discouraged } catch ( Throwable $ae2 ) { $auth_ok = false; } } if ( ! $auth_ok ) { return array( 'ok' => false, 'error' => __( 'Redis authentication failed (invalid password).', 'aio-tools' ), ); } } if ( $db > 0 ) { @$r->select( $db ); // phpcs:ignore WordPress.PHP.NoSilencedErrors.Discouraged } $tkey = $params['prefix'] . '_ping_' . wp_rand( 1000, 9999 ); $r->setex( $tkey, 10, 'ok' ); $val = $r->get( $tkey ); $r->del( $tkey ); $info = @$r->info( 'memory' ); // phpcs:ignore WordPress.PHP.NoSilencedErrors.Discouraged $dbsize = (int) @$r->dbSize(); // phpcs:ignore WordPress.PHP.NoSilencedErrors.Discouraged $r->close(); if ( $val !== 'ok' ) { return array( 'ok' => false, 'error' => __( 'Redis read/write test failed.', 'aio-tools' ), ); } return array( 'ok' => true, 'driver_used' => 'PhpRedis (Native C)', 'ping_ms' => round( ( microtime( true ) - $t0 ) * 1000, 2 ), 'memory_human' => isset( $info['used_memory_human'] ) ? sanitize_text_field( $info['used_memory_human'] ) : 'Active', 'keys_count' => $dbsize, ); } // Fallback: Pure-PHP RESP Socket Client if ( ! function_exists( 'stream_socket_client' ) ) { return array( 'ok' => false, 'error' => __( 'Neither PhpRedis nor stream_socket_client is available.', 'aio-tools' ), ); } $endpoint = $ep['is_socket'] ? 'unix://' . $host : 'tcp://' . $host . ':' . $port; $errno = 0; $errstr = ''; $fp = @stream_socket_client( $endpoint, $errno, $errstr, 0.4 ); // phpcs:ignore WordPress.PHP.NoSilencedErrors.Discouraged if ( ! $fp ) { return array( 'ok' => false, 'error' => self::format_connection_error( $params, $errstr ? $errstr : 'Connection refused' ), ); } stream_set_timeout( $fp, 0, 400000 ); if ( $pass !== '' ) { $auth_res = self::resp_cmd( $fp, array( 'AUTH', $pass ) ); if ( is_wp_error( $auth_res ) && strpos( $pass, ':' ) !== false ) { $parts = explode( ':', $pass, 2 ); $auth_res = self::resp_cmd( $fp, array( 'AUTH', $parts[0], $parts[1] ) ); } if ( is_wp_error( $auth_res ) ) { fclose( $fp ); return array( 'ok' => false, 'error' => __( 'Redis authentication failed: ', 'aio-tools' ) . $auth_res->get_error_message(), ); } } if ( $db > 0 ) { self::resp_cmd( $fp, array( 'SELECT', (string) $db ) ); } $tkey = $params['prefix'] . '_ping_' . wp_rand( 1000, 9999 ); self::resp_cmd( $fp, array( 'SETEX', $tkey, '10', 'ok' ) ); $val = self::resp_cmd( $fp, array( 'GET', $tkey ) ); self::resp_cmd( $fp, array( 'DEL', $tkey ) ); $dbsize = self::resp_cmd( $fp, array( 'DBSIZE' ) ); $info = (string) self::resp_cmd( $fp, array( 'INFO', 'memory' ) ); fclose( $fp ); if ( $val !== 'ok' ) { return array( 'ok' => false, 'error' => __( 'Redis socket read/write verification failed.', 'aio-tools' ), ); } $mem_human = 'Active'; if ( preg_match( '/used_memory_human:([^\r\n]+)/', $info, $mm ) ) { $mem_human = trim( $mm[1] ); } return array( 'ok' => true, 'driver_used' => 'Redis (Pure-PHP Socket)', 'ping_ms' => round( ( microtime( true ) - $t0 ) * 1000, 2 ), 'memory_human' => $mem_human, 'keys_count' => is_int( $dbsize ) ? $dbsize : 0, ); } catch ( Throwable $e ) { return array( 'ok' => false, 'error' => self::format_connection_error( $params, $e->getMessage() ), ); } } /** * Minimal RESP2 protocol helper for admin connection testing. */ private static function resp_cmd( $fp, $args ) { $cmd = '*' . count( $args ) . "\r\n"; foreach ( $args as $arg ) { $s = (string) $arg; $cmd .= '$' . strlen( $s ) . "\r\n" . $s . "\r\n"; } if ( @fwrite( $fp, $cmd ) === false ) { // phpcs:ignore WordPress.PHP.NoSilencedErrors.Discouraged return new WP_Error( 'redis_write', 'Socket write error' ); } return self::resp_read( $fp ); } private static function resp_read( $fp ) { $line = @fgets( $fp ); // phpcs:ignore WordPress.PHP.NoSilencedErrors.Discouraged if ( $line === false || $line === '' ) { return new WP_Error( 'redis_eof', 'Empty response from Redis' ); } $type = $line[0]; $payload = rtrim( substr( $line, 1 ), "\r\n" ); if ( $type === '+' ) { return $payload; } if ( $type === '-' ) { return new WP_Error( 'redis_err', $payload ); } if ( $type === ':' ) { return (int) $payload; } if ( $type === '$' ) { $len = (int) $payload; if ( $len < 0 ) { return null; } $data = ''; $rem = $len + 2; while ( $rem > 0 && ! feof( $fp ) ) { $chunk = @fread( $fp, $rem ); // phpcs:ignore WordPress.PHP.NoSilencedErrors.Discouraged if ( $chunk === false || $chunk === '' ) { break; } $data .= $chunk; $rem -= strlen( $chunk ); } return substr( $data, 0, $len ); } if ( $type === '*' ) { $count = (int) $payload; if ( $count < 0 ) { return null; } $items = array(); for ( $i = 0; $i < $count; $i++ ) { $items[] = self::resp_read( $fp ); } return $items; } return null; } /** * Inspect current object-cache.php installation & live metrics. */ public static function get_status() { $dropin_path = self::get_dropin_path(); $exists = file_exists( $dropin_path ); $is_ours = false; $foreign_name = ''; if ( $exists && is_readable( $dropin_path ) ) { $head = (string) @file_get_contents( $dropin_path, false, null, 0, 2048 ); // phpcs:ignore WordPress.PHP.NoSilencedErrors.Discouraged $is_ours = strpos( $head, self::DROPIN_SIGNATURE ) !== false; if ( ! $is_ours ) { if ( stripos( $head, 'Redis Object Cache' ) !== false ) { $foreign_name = 'Redis Object Cache (Till Krüss)'; } elseif ( stripos( $head, 'LiteSpeed' ) !== false ) { $foreign_name = 'LiteSpeed Object Cache'; } elseif ( stripos( $head, 'W3 Total Cache' ) !== false ) { $foreign_name = 'W3 Total Cache'; } else { $foreign_name = __( 'External Object Cache Drop-in', 'aio-tools' ); } } } $params = self::build_connection_params(); $live_probe = array( 'ok' => false, 'driver_used' => strtoupper( $params['driver'] ), 'ping_ms' => null, 'memory_human' => '—', 'keys_count' => 0, ); if ( $exists && $is_ours ) { $live_probe = self::test_backend( $params ); } global $wp_object_cache; $hits = isset( $wp_object_cache->cache_hits ) ? (int) $wp_object_cache->cache_hits : 0; $misses = isset( $wp_object_cache->cache_misses ) ? (int) $wp_object_cache->cache_misses : 0; $total = $hits + $misses; $ratio = $total > 0 ? round( ( $hits / $total ) * 100, 1 ) : 0; return array( 'capabilities' => self::get_capabilities(), 'dropin' => array( 'exists' => $exists, 'is_ours' => $is_ours, 'foreign_name' => $foreign_name, 'ext_active' => function_exists( 'wp_using_ext_object_cache' ) && wp_using_ext_object_cache(), ), 'config' => array( 'driver' => $params['driver'], 'host' => $params['host'], 'port' => $params['port'], 'database' => $params['database'], 'detected_sockets' => self::discover_local_sockets( $params['driver'] ), ), 'telemetry' => array( 'connected' => ! empty( $live_probe['ok'] ), 'driver_used' => isset( $live_probe['driver_used'] ) ? $live_probe['driver_used'] : strtoupper( $params['driver'] ), 'ping_ms' => isset( $live_probe['ping_ms'] ) ? $live_probe['ping_ms'] : null, 'memory_human' => isset( $live_probe['memory_human'] ) ? $live_probe['memory_human'] : '—', 'keys_count' => isset( $live_probe['keys_count'] ) ? (int) $live_probe['keys_count'] : 0, 'hit_ratio' => $ratio, 'hits' => $hits, 'misses' => $misses, 'error' => isset( $live_probe['error'] ) ? $live_probe['error'] : '', ), ); } /** * Write static object-cache-config.php inside protected uploads/aio-cache/ directory. */ public static function write_runtime_config( $params ) { if ( class_exists( 'AIO_Dropin_Manager' ) ) { AIO_Dropin_Manager::write_runtime_config(); } $cfg_file = self::get_config_path(); $dir = dirname( $cfg_file ); if ( ! file_exists( $dir ) ) { wp_mkdir_p( $dir ); } if ( ! is_dir( $dir ) || ! is_writable( $dir ) ) { return false; } $export = "flush(); // phpcs:ignore WordPress.PHP.NoSilencedErrors.Discouraged } if ( method_exists( $GLOBALS['wp_object_cache'], 'disable_persistence' ) ) { @$GLOBALS['wp_object_cache']->disable_persistence(); // phpcs:ignore WordPress.PHP.NoSilencedErrors.Discouraged } } if ( function_exists( 'wp_using_ext_object_cache' ) ) { wp_using_ext_object_cache( false ); } $dropin_path = self::get_dropin_path(); if ( file_exists( $dropin_path ) && is_readable( $dropin_path ) ) { $head = (string) @file_get_contents( $dropin_path, false, null, 0, 2048 ); // phpcs:ignore WordPress.PHP.NoSilencedErrors.Discouraged if ( strpos( $head, self::DROPIN_SIGNATURE ) !== false ) { @unlink( $dropin_path ); // phpcs:ignore WordPress.PHP.NoSilencedErrors.Discouraged self::invalidate_php_file_cache( $dropin_path ); } } $cfg_path = self::get_config_path(); if ( file_exists( $cfg_path ) ) { @unlink( $cfg_path ); // phpcs:ignore WordPress.PHP.NoSilencedErrors.Discouraged self::invalidate_php_file_cache( $cfg_path ); } return true; } public function ajax_get_status() { check_ajax_referer( 'aio-settings-nonce', 'security' ); if ( ! current_user_can( 'manage_options' ) ) { wp_send_json_error( __( 'Unauthorized', 'aio-tools' ) ); } wp_send_json_success( self::get_status() ); } public function ajax_test_connection() { check_ajax_referer( 'aio-settings-nonce', 'security' ); if ( ! current_user_can( 'manage_options' ) ) { wp_send_json_error( __( 'Unauthorized', 'aio-tools' ) ); } // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized $raw_pass = isset( $_POST['password'] ) ? trim( (string) preg_replace( '/[\r\n\0]/', '', (string) wp_unslash( $_POST['password'] ) ) ) : ''; $override = array( 'driver' => isset( $_POST['driver'] ) ? sanitize_key( wp_unslash( $_POST['driver'] ) ) : 'redis', 'host' => isset( $_POST['host'] ) ? sanitize_text_field( wp_unslash( $_POST['host'] ) ) : '127.0.0.1', 'port' => isset( $_POST['port'] ) ? absint( $_POST['port'] ) : 6379, 'db' => isset( $_POST['db'] ) ? absint( $_POST['db'] ) : 0, 'password' => $raw_pass, ); $params = self::build_connection_params( $override ); $res = self::try_backend_with_socket_fallback( $params ); if ( ! empty( $res['ok'] ) ) { $res['resolved_driver'] = $params['driver']; $res['resolved_host'] = $params['host']; $res['resolved_port'] = $params['port']; $res['resolved_db'] = $params['database']; wp_send_json_success( $res ); } else { wp_send_json_error( isset( $res['error'] ) ? $res['error'] : __( 'Connection failed.', 'aio-tools' ) ); } } public function ajax_toggle_dropin() { check_ajax_referer( 'aio-settings-nonce', 'security' ); if ( ! current_user_can( 'manage_options' ) ) { wp_send_json_error( __( 'Unauthorized', 'aio-tools' ) ); } $enable = ! empty( $_POST['enable'] ); $force = ! empty( $_POST['force'] ); if ( ! $enable ) { self::remove_dropin(); $opts = get_option( 'aio_options', array() ); $opts['optEnableObjectCache'] = false; update_option( 'aio_options', $opts ); wp_send_json_success( array( 'message' => __( 'Disabled and removed AIO Object Cache Drop-in.', 'aio-tools' ), 'status' => self::get_status(), ) ); } // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized $raw_pass = isset( $_POST['password'] ) ? trim( (string) preg_replace( '/[\r\n\0]/', '', (string) wp_unslash( $_POST['password'] ) ) ) : ''; $override = array( 'driver' => isset( $_POST['driver'] ) ? sanitize_key( wp_unslash( $_POST['driver'] ) ) : 'redis', 'host' => isset( $_POST['host'] ) ? sanitize_text_field( wp_unslash( $_POST['host'] ) ) : '127.0.0.1', 'port' => isset( $_POST['port'] ) ? absint( $_POST['port'] ) : 6379, 'db' => isset( $_POST['db'] ) ? absint( $_POST['db'] ) : 0, 'password' => $raw_pass, ); // Always rotate seed when enabling so pre-existing/stale Redis keys are completely isolated $params = self::build_connection_params( $override, true ); // Pre-validate foreign drop-in and live backend (with cPanel/CloudLinux Unix Socket auto-discovery) BEFORE mutating DB $dropin_path = self::get_dropin_path(); if ( file_exists( $dropin_path ) && is_readable( $dropin_path ) && ! $force ) { $head = (string) @file_get_contents( $dropin_path, false, null, 0, 2048 ); // phpcs:ignore WordPress.PHP.NoSilencedErrors.Discouraged if ( strpos( $head, self::DROPIN_SIGNATURE ) === false ) { wp_send_json_error( __( 'Another object-cache.php drop-in already exists. Click Overwrite if you want AIO Tools to take over.', 'aio-tools' ) ); } } $probe = self::try_backend_with_socket_fallback( $params ); if ( empty( $probe['ok'] ) ) { wp_send_json_error( isset( $probe['error'] ) ? $probe['error'] : __( 'Backend connection failed.', 'aio-tools' ) ); } // Commit connection settings into MySQL BEFORE installing object-cache.php so first boot reads fresh DB state $opts = get_option( 'aio_options', array() ); $prev_enabled = ! empty( $opts['optEnableObjectCache'] ); $opts['optEnableObjectCache'] = true; $opts['optObjectCacheDriver'] = $params['driver']; $opts['txtObjectCacheHost'] = $params['host']; $opts['txtObjectCachePort'] = $params['port']; $opts['txtObjectCacheDb'] = $params['database']; if ( $override['password'] !== '********' ) { $opts['txtObjectCachePassword'] = ( $override['password'] !== '' && class_exists( 'AIO_Crypto' ) ) ? AIO_Crypto::encrypt( $override['password'] ) : $override['password']; } update_option( 'aio_options', $opts ); self::purge_plugin_options_cache(); $res = self::install_dropin( $params, $force ); if ( is_wp_error( $res ) ) { $opts['optEnableObjectCache'] = $prev_enabled; update_option( 'aio_options', $opts ); wp_send_json_error( $res->get_error_message() ); } $msg = sprintf( __( 'Object Cache Drop-in (%s) activated successfully!', 'aio-tools' ), $res['driver_used'] ); if ( ! empty( $probe['auto_socket'] ) ) { $msg .= ' (' . $probe['auto_socket'] . ')'; } wp_send_json_success( array( 'message' => $msg, 'resolved_driver' => $params['driver'], 'resolved_host' => $params['host'], 'resolved_port' => $params['port'], 'resolved_db' => $params['database'], 'status' => self::get_status(), ) ); } public function ajax_flush_cache() { check_ajax_referer( 'aio-settings-nonce', 'security' ); if ( ! current_user_can( 'manage_options' ) ) { wp_send_json_error( __( 'Unauthorized', 'aio-tools' ) ); } wp_cache_flush(); wp_send_json_success( array( 'message' => __( 'Persistent Object Cache flushed successfully!', 'aio-tools' ), 'status' => self::get_status(), ) ); } /** * Build standalone fail-open wp-content/object-cache.php Drop-in PHP code. */ private static function build_object_cache_dropin_code( $config_path ) { $escaped_cfg = addslashes( wp_normalize_path( $config_path ) ); return <<multisite = function_exists( 'is_multisite' ) && is_multisite(); \$this->blog_prefix = \$this->multisite ? ( (int) \$blog_id ) . ':' : ''; \$cfg_file = '{$escaped_cfg}'; if ( ! @is_file( \$cfg_file ) ) { return; } \$cfg = @include \$cfg_file; if ( ! is_array( \$cfg ) || empty( \$cfg['driver'] ) ) { return; } // Fail-open to RAM if AIO Tools plugin directory was manually removed via FTP/File Manager if ( ! empty( \$cfg['plugin_file'] ) && ! @is_file( (string) \$cfg['plugin_file'] ) ) { return; } \$this->prefix = ! empty( \$cfg['prefix'] ) ? (string) \$cfg['prefix'] : 'aio_'; \$this->hmac_key = hash( 'sha256', ( defined( 'AUTH_KEY' ) ? AUTH_KEY : '' ) . '|' . ( defined( 'SECURE_AUTH_KEY' ) ? SECURE_AUTH_KEY : '' ) . '|' . \$this->prefix . '|' . ABSPATH, true ); \$this->connect_backend( \$cfg ); // Self-healing guard: during plugin activation/deactivation/upgrade requests, bypass cached options // so WordPress always reads fresh active_plugins from MySQL and never hits a 0-rows-affected deadlock. if ( \$this->connected ) { \$script = isset( \$_SERVER['SCRIPT_NAME'] ) ? (string) \$_SERVER['SCRIPT_NAME'] : ''; \$action = isset( \$_REQUEST['action'] ) && \$_REQUEST['action'] !== '-1' ? (string) \$_REQUEST['action'] : ''; if ( \$action === '' && isset( \$_REQUEST['action2'] ) && \$_REQUEST['action2'] !== '-1' ) { \$action = (string) \$_REQUEST['action2']; } if ( \$action !== '' && ( substr( \$script, -12 ) === '/plugins.php' || substr( \$script, -11 ) === '/update.php' || substr( \$script, -16 ) === '/update-core.php' ) ) { \$lifecycle_actions = array( 'activate', 'deactivate', 'activate-selected', 'deactivate-selected', 'error_scrape', 'upload-plugin', 'upgrade-plugin', 'overwrite-plugin', 'delete-selected', ); if ( in_array( \$action, \$lifecycle_actions, true ) ) { \$this->delete( 'alloptions', 'options' ); \$this->delete( 'notoptions', 'options' ); \$this->delete( 'active_plugins', 'options' ); } } } } public function is_connected() { return (bool) \$this->connected; } public function disable_persistence() { \$this->close(); \$this->connected = false; \$this->client = null; } private function connect_backend( \$cfg ) { \$drv = (string) \$cfg['driver']; \$host = isset( \$cfg['host'] ) ? trim( (string) \$cfg['host'] ) : '127.0.0.1'; if ( stripos( \$host, 'unix://' ) === 0 ) { \$host = substr( \$host, 7 ); } elseif ( stripos( \$host, 'tcp://' ) === 0 ) { \$host = substr( \$host, 6 ); } \$is_sock = strpos( \$host, '/' ) === 0; \$port = \$is_sock ? 0 : ( isset( \$cfg['port'] ) ? (int) \$cfg['port'] : 6379 ); \$pass = isset( \$cfg['password'] ) ? (string) \$cfg['password'] : ''; \$db = isset( \$cfg['database'] ) ? (int) \$cfg['database'] : 0; \$timeout = isset( \$cfg['timeout'] ) ? (float) \$cfg['timeout'] : 0.25; try { if ( \$drv === 'apcu' && function_exists( 'apcu_fetch' ) && filter_var( ini_get( 'apc.enabled' ), FILTER_VALIDATE_BOOLEAN ) ) { \$this->driver = 'apcu'; \$this->connected = true; } elseif ( \$drv === 'memcached' && class_exists( 'Memcached' ) ) { \$mc = new Memcached(); if ( defined( 'Memcached::OPT_CONNECT_TIMEOUT' ) ) { \$mc->setOption( Memcached::OPT_CONNECT_TIMEOUT, 250 ); } \$mc->addServer( \$host, \$is_sock ? 0 : \$port ); \$this->client = \$mc; \$this->driver = 'memcached'; \$this->connected = true; } elseif ( \$drv === 'memcache' && class_exists( 'Memcache' ) ) { \$mc = new Memcache(); \$ok = \$is_sock ? @\$mc->connect( 'unix://' . \$host, 0, 1 ) : @\$mc->connect( \$host, \$port, 1 ); if ( \$ok ) { \$this->client = \$mc; \$this->driver = 'memcache'; \$this->connected = true; } } elseif ( \$drv === 'redis' ) { if ( class_exists( 'Redis' ) && method_exists( 'Redis', 'connect' ) ) { try { \$r = new Redis(); \$ok = \$is_sock ? @\$r->connect( \$host, 0, \$timeout ) : @\$r->connect( \$host, \$port, \$timeout ); if ( \$ok ) { \$auth_ok = true; if ( \$pass !== '' ) { try { \$auth_ok = (bool) @\$r->auth( \$pass ); } catch ( Throwable \$ae ) { \$auth_ok = false; } if ( ! \$auth_ok && strpos( \$pass, ':' ) !== false ) { try { \$auth_ok = (bool) @\$r->auth( explode( ':', \$pass, 2 ) ); } catch ( Throwable \$ae2 ) { \$auth_ok = false; } } } if ( \$auth_ok ) { if ( \$db > 0 ) { @\$r->select( \$db ); } if ( defined( 'Redis::OPT_SERIALIZER' ) && defined( 'Redis::SERIALIZER_NONE' ) ) { @\$r->setOption( Redis::OPT_SERIALIZER, Redis::SERIALIZER_NONE ); } if ( defined( 'Redis::OPT_READ_TIMEOUT' ) ) { @\$r->setOption( Redis::OPT_READ_TIMEOUT, max( 0.25, \$timeout ) ); } \$this->client = \$r; \$this->driver = 'phpredis'; \$this->connected = true; } } } catch ( Throwable \$re ) { \$this->connected = false; } } if ( ! \$this->connected && function_exists( 'stream_socket_client' ) ) { \$ep = \$is_sock ? 'unix://' . \$host : 'tcp://' . \$host . ':' . \$port; \$fp = @stream_socket_client( \$ep, \$eno, \$estr, \$timeout ); if ( \$fp ) { stream_set_timeout( \$fp, 0, 250000 ); \$auth_ok = true; if ( \$pass !== '' ) { \$res = \$this->resp_cmd( \$fp, array( 'AUTH', \$pass ) ); if ( \$res === false && strpos( \$pass, ':' ) !== false ) { \$parts = explode( ':', \$pass, 2 ); \$res = \$this->resp_cmd( \$fp, array( 'AUTH', \$parts[0], \$parts[1] ) ); } \$auth_ok = ( \$res !== false ); } if ( \$auth_ok ) { if ( \$db > 0 ) { \$this->resp_cmd( \$fp, array( 'SELECT', (string) \$db ) ); } \$this->client = \$fp; \$this->driver = 'redis_socket'; \$this->connected = true; } else { @fclose( \$fp ); } } } } if ( \$this->connected ) { \$v = \$this->backend_raw_get( \$this->prefix . '_gen' ); if ( is_numeric( \$v ) && (int) \$v > 0 ) { \$this->gen_version = (int) \$v; } else { \$this->gen_version = time(); \$this->backend_raw_set( \$this->prefix . '_gen', (string) \$this->gen_version, 2592000 ); } \$gmap_ok = false; \$gmap_key = \$this->prefix . 'g' . \$this->gen_version . ':_grp_map'; \$gmap_raw = \$this->backend_raw_get( \$gmap_key, \$gmap_ok ); if ( \$gmap_ok && is_string( \$gmap_raw ) && \$gmap_raw !== '' ) { \$unpacked_ok = false; \$gmap = \$this->unpack_payload( \$gmap_key, \$gmap_raw, \$unpacked_ok ); if ( \$unpacked_ok && is_array( \$gmap ) ) { \$this->group_versions = \$gmap; } } } } catch ( Throwable \$e ) { \$this->connected = false; } } private function resp_cmd( \$fp, \$args ) { if ( ! is_resource( \$fp ) ) { return false; } \$cmd = '*' . count( \$args ) . "\\r\\n"; foreach ( \$args as \$a ) { \$s = (string) \$a; \$cmd .= '\$' . strlen( \$s ) . "\\r\\n" . \$s . "\\r\\n"; } if ( @fwrite( \$fp, \$cmd ) === false ) { \$this->connected = false; return false; } return \$this->resp_read( \$fp ); } private function resp_read( \$fp ) { \$line = @fgets( \$fp ); if ( \$line === false || \$line === '' ) { \$this->connected = false; return false; } \$t = \$line[0]; \$p = rtrim( substr( \$line, 1 ), "\\r\\n" ); if ( \$t === '+' ) { return \$p; } if ( \$t === '-' ) { return false; } if ( \$t === ':' ) { return (int) \$p; } if ( \$t === '\$' ) { \$len = (int) \$p; if ( \$len < 0 ) { return null; } \$buf = ''; \$rem = \$len + 2; while ( \$rem > 0 && ! feof( \$fp ) ) { \$ch = @fread( \$fp, \$rem ); if ( \$ch === false || \$ch === '' ) { break; } \$buf .= \$ch; \$rem -= strlen( \$ch ); } return substr( \$buf, 0, \$len ); } if ( \$t === '*' ) { \$cnt = (int) \$p; if ( \$cnt < 0 ) { return null; } \$arr = array(); for ( \$i = 0; \$i < \$cnt; \$i++ ) { \$arr[] = \$this->resp_read( \$fp ); } return \$arr; } return null; } private function pack_payload( \$full_key, \$data ) { \$ser = serialize( \$data ); \$mac = substr( hash_hmac( 'sha256', \$full_key . '|' . \$ser, \$this->hmac_key, true ), 0, 16 ); return 'AIOM1:' . \$mac . \$ser; } private function unpack_payload( \$full_key, \$raw, &\$ok = false ) { \$ok = false; if ( ! is_string( \$raw ) || strlen( \$raw ) < 24 || strncmp( \$raw, 'AIOM1:', 6 ) !== 0 ) { return false; } \$mac = substr( \$raw, 6, 16 ); \$ser = substr( \$raw, 22 ); \$expected = substr( hash_hmac( 'sha256', \$full_key . '|' . \$ser, \$this->hmac_key, true ), 0, 16 ); if ( ! hash_equals( \$expected, \$mac ) ) { return false; } if ( \$ser === 'b:0;' ) { \$ok = true; return false; } try { \$tmp = @unserialize( \$ser ); if ( \$tmp !== false && ! ( is_object( \$tmp ) && get_class( \$tmp ) === '__PHP_Incomplete_Class' ) ) { \$ok = true; return \$tmp; } } catch ( Throwable \$e ) { \$ok = false; } return false; } private function is_valid_key( \$key ) { if ( is_int( \$key ) ) { return true; } if ( is_string( \$key ) && trim( \$key ) !== '' ) { return true; } return false; } private function safe_clone( \$val ) { if ( ! is_object( \$val ) ) { return \$val; } try { return clone \$val; } catch ( Throwable \$e ) { return \$val; } } private function build_key( \$key, \$group = 'default' ) { if ( empty( \$group ) ) { \$group = 'default'; } \$grp_prefix = isset( \$this->global_groups[ \$group ] ) ? '' : \$this->blog_prefix; \$grp_scope = \$grp_prefix . \$group; \$grp_ver = isset( \$this->group_versions[ \$grp_scope ] ) ? '.' . (int) \$this->group_versions[ \$grp_scope ] : ''; return \$this->prefix . 'g' . \$this->gen_version . ':' . \$grp_scope . \$grp_ver . ':' . \$key; } private function is_persistent( \$group ) { if ( empty( \$group ) ) { \$group = 'default'; } return \$this->connected && ! isset( \$this->non_persistent_groups[ \$group ] ); } private function backend_raw_get( \$full_key, &\$found = false ) { \$found = false; if ( ! \$this->connected ) { return false; } try { if ( \$this->driver === 'apcu' ) { \$ok = false; \$val = apcu_fetch( \$full_key, \$ok ); if ( \$ok ) { \$found = true; return \$val; } return false; } if ( \$this->driver === 'memcached' ) { \$raw = \$this->client->get( \$full_key ); if ( \$this->client->getResultCode() === Memcached::RES_SUCCESS ) { \$found = true; return \$raw; } return false; } if ( \$this->driver === 'memcache' ) { \$flags = false; \$raw = @\$this->client->get( \$full_key, \$flags ); if ( \$raw !== false || \$flags !== false ) { \$found = true; return \$raw; } return false; } if ( \$this->driver === 'phpredis' ) { \$raw = \$this->client->get( \$full_key ); if ( \$raw === false ) { return false; } \$found = true; return \$raw; } if ( \$this->driver === 'redis_socket' ) { \$raw = \$this->resp_cmd( \$this->client, array( 'GET', \$full_key ) ); if ( \$raw === null || \$raw === false ) { return false; } \$found = true; return \$raw; } } catch ( Throwable \$e ) { \$this->connected = false; } return false; } private function backend_raw_mget( array \$full_keys ) { \$results = array(); if ( ! \$this->connected || empty( \$full_keys ) ) { return \$results; } \$keys_list = array_values( \$full_keys ); try { if ( \$this->driver === 'apcu' ) { \$ok = false; \$vals = apcu_fetch( \$keys_list, \$ok ); return ( \$ok && is_array( \$vals ) ) ? \$vals : array(); } if ( \$this->driver === 'memcached' ) { \$vals = \$this->client->getMulti( \$keys_list ); return is_array( \$vals ) ? \$vals : array(); } if ( \$this->driver === 'phpredis' ) { \$vals = \$this->client->mGet( \$keys_list ); if ( is_array( \$vals ) ) { foreach ( \$keys_list as \$idx => \$fk ) { if ( isset( \$vals[ \$idx ] ) && \$vals[ \$idx ] !== false && is_string( \$vals[ \$idx ] ) ) { \$results[ \$fk ] = \$vals[ \$idx ]; } } } return \$results; } if ( \$this->driver === 'redis_socket' ) { \$vals = \$this->resp_cmd( \$this->client, array_merge( array( 'MGET' ), \$keys_list ) ); if ( is_array( \$vals ) ) { foreach ( \$keys_list as \$idx => \$fk ) { if ( isset( \$vals[ \$idx ] ) && \$vals[ \$idx ] !== null && \$vals[ \$idx ] !== false && is_string( \$vals[ \$idx ] ) ) { \$results[ \$fk ] = \$vals[ \$idx ]; } } } return \$results; } foreach ( \$keys_list as \$fk ) { \$ok = false; \$raw = \$this->backend_raw_get( \$fk, \$ok ); if ( \$ok && is_string( \$raw ) ) { \$results[ \$fk ] = \$raw; } } } catch ( Throwable \$e ) { \$this->connected = false; } return \$results; } private function backend_raw_add( \$full_key, \$serialized, \$expire = 0 ) { if ( ! \$this->connected ) { return false; } \$ttl = (int) \$expire; \$safe_ttl = \$ttl > 0 ? \$ttl : 604800; try { if ( \$this->driver === 'apcu' ) { return apcu_add( \$full_key, \$serialized, \$safe_ttl ); } if ( \$this->driver === 'memcached' ) { return \$this->client->add( \$full_key, \$serialized, \$safe_ttl > 2592000 ? time() + \$safe_ttl : \$safe_ttl ); } if ( \$this->driver === 'memcache' ) { return @\$this->client->add( \$full_key, \$serialized, 0, min( 2592000, \$safe_ttl ) ); } if ( \$this->driver === 'phpredis' ) { return (bool) \$this->client->set( \$full_key, \$serialized, array( 'nx', 'ex' => \$safe_ttl ) ); } if ( \$this->driver === 'redis_socket' ) { return \$this->resp_cmd( \$this->client, array( 'SET', \$full_key, \$serialized, 'EX', (string) \$safe_ttl, 'NX' ) ) === 'OK'; } } catch ( Throwable \$e ) { \$this->connected = false; } return false; } private function backend_raw_set( \$full_key, \$serialized, \$expire = 0 ) { if ( ! \$this->connected ) { return false; } \$ttl = (int) \$expire; if ( \$ttl < 0 ) { \$ttl = 0; } // Cap default boundless keys to 7 days so RAM stays healthy \$safe_ttl = \$ttl > 0 ? \$ttl : 604800; try { if ( \$this->driver === 'apcu' ) { return apcu_store( \$full_key, \$serialized, \$safe_ttl ); } if ( \$this->driver === 'memcached' ) { return \$this->client->set( \$full_key, \$serialized, \$safe_ttl > 2592000 ? time() + \$safe_ttl : \$safe_ttl ); } if ( \$this->driver === 'memcache' ) { return @\$this->client->set( \$full_key, \$serialized, 0, min( 2592000, \$safe_ttl ) ); } if ( \$this->driver === 'phpredis' ) { return \$this->client->setex( \$full_key, \$safe_ttl, \$serialized ); } if ( \$this->driver === 'redis_socket' ) { return \$this->resp_cmd( \$this->client, array( 'SETEX', \$full_key, (string) \$safe_ttl, \$serialized ) ) === 'OK'; } } catch ( Throwable \$e ) { \$this->connected = false; } return false; } private function backend_raw_del( \$full_key ) { if ( ! \$this->connected ) { return false; } try { if ( \$this->driver === 'apcu' ) { return apcu_delete( \$full_key ); } if ( \$this->driver === 'memcached' || \$this->driver === 'memcache' ) { return @\$this->client->delete( \$full_key ); } if ( \$this->driver === 'phpredis' ) { return (bool) \$this->client->del( \$full_key ); } if ( \$this->driver === 'redis_socket' ) { return (int) \$this->resp_cmd( \$this->client, array( 'DEL', \$full_key ) ) > 0; } } catch ( Throwable \$e ) { \$this->connected = false; } return false; } public function add( \$key, \$data, \$group = 'default', \$expire = 0 ) { if ( ! \$this->is_valid_key( \$key ) ) { return false; } if ( function_exists( 'wp_suspend_cache_addition' ) && wp_suspend_cache_addition() ) { return false; } if ( empty( \$group ) ) { \$group = 'default'; } \$ckey = \$this->build_key( \$key, \$group ); if ( array_key_exists( \$ckey, \$this->cache ) ) { return false; } if ( is_object( \$data ) ) { \$data = \$this->safe_clone( \$data ); } if ( \$this->is_persistent( \$group ) ) { try { \$payload = \$this->pack_payload( \$ckey, \$data ); if ( ! \$this->backend_raw_add( \$ckey, \$payload, (int) \$expire ) ) { return false; } } catch ( Throwable \$e ) { return false; } } \$this->cache[ \$ckey ] = \$data; return true; } public function replace( \$key, \$data, \$group = 'default', \$expire = 0 ) { if ( ! \$this->is_valid_key( \$key ) ) { return false; } if ( empty( \$group ) ) { \$group = 'default'; } \$ckey = \$this->build_key( \$key, \$group ); \$found = array_key_exists( \$ckey, \$this->cache ); if ( ! \$found && \$this->is_persistent( \$group ) ) { \$this->backend_raw_get( \$ckey, \$found ); } if ( ! \$found ) { return false; } return \$this->set( \$key, \$data, \$group, \$expire ); } public function set( \$key, \$data, \$group = 'default', \$expire = 0 ) { if ( ! \$this->is_valid_key( \$key ) ) { return false; } if ( empty( \$group ) ) { \$group = 'default'; } if ( is_object( \$data ) ) { \$data = \$this->safe_clone( \$data ); } \$ckey = \$this->build_key( \$key, \$group ); \$this->cache[ \$ckey ] = \$data; if ( \$this->is_persistent( \$group ) ) { // Never persist empty/corrupted alloptions into external memory if ( \$group === 'options' && (string) \$key === 'alloptions' && ( ! is_array( \$data ) || empty( \$data ) ) ) { return true; } try { \$payload = \$this->pack_payload( \$ckey, \$data ); \$this->backend_raw_set( \$ckey, \$payload, (int) \$expire ); } catch ( Throwable \$e ) { // Non-serializable object (e.g. Closure) remains safely in request RAM only } } return true; } public function get( \$key, \$group = 'default', \$force = false, &\$found = null ) { if ( ! \$this->is_valid_key( \$key ) ) { \$found = false; return false; } if ( empty( \$group ) ) { \$group = 'default'; } \$ckey = \$this->build_key( \$key, \$group ); if ( ! \$force && array_key_exists( \$ckey, \$this->cache ) ) { \$found = true; \$this->cache_hits++; return \$this->safe_clone( \$this->cache[ \$ckey ] ); } if ( \$this->is_persistent( \$group ) ) { \$ok = false; \$raw = \$this->backend_raw_get( \$ckey, \$ok ); if ( \$ok && is_string( \$raw ) ) { \$unser_ok = false; \$val = \$this->unpack_payload( \$ckey, \$raw, \$unser_ok ); if ( \$unser_ok && \$group === 'options' && (string) \$key === 'alloptions' && ( ! is_array( \$val ) || empty( \$val ) ) ) { \$unser_ok = false; } if ( \$unser_ok ) { \$this->cache[ \$ckey ] = \$val; \$found = true; \$this->cache_hits++; return \$this->safe_clone( \$val ); } // Evict corrupted, unsigned, or incomplete serialized entry automatically \$this->backend_raw_del( \$ckey ); } } \$found = false; \$this->cache_misses++; return false; } public function add_multiple( array \$data, \$group = 'default', \$expire = 0 ) { \$values = array(); foreach ( \$data as \$k => \$v ) { \$values[ \$k ] = \$this->add( \$k, \$v, \$group, \$expire ); } return \$values; } public function set_multiple( array \$data, \$group = 'default', \$expire = 0 ) { \$values = array(); foreach ( \$data as \$k => \$v ) { \$values[ \$k ] = \$this->set( \$k, \$v, \$group, \$expire ); } return \$values; } public function get_multiple( \$keys, \$group = 'default', \$force = false ) { if ( empty( \$group ) ) { \$group = 'default'; } \$values = array(); \$missing_map = array(); \$persistent = \$this->is_persistent( \$group ); foreach ( (array) \$keys as \$k ) { if ( ! \$this->is_valid_key( \$k ) ) { \$values[ \$k ] = false; continue; } \$ckey = \$this->build_key( \$k, \$group ); if ( ! \$force && array_key_exists( \$ckey, \$this->cache ) ) { \$this->cache_hits++; \$values[ \$k ] = \$this->safe_clone( \$this->cache[ \$ckey ] ); } elseif ( \$persistent ) { \$missing_map[ \$k ] = \$ckey; } else { \$this->cache_misses++; \$values[ \$k ] = false; } } if ( ! empty( \$missing_map ) ) { \$raw_map = \$this->backend_raw_mget( array_values( \$missing_map ) ); foreach ( \$missing_map as \$k => \$ckey ) { if ( isset( \$raw_map[ \$ckey ] ) && is_string( \$raw_map[ \$ckey ] ) ) { \$unser_ok = false; \$val = \$this->unpack_payload( \$ckey, \$raw_map[ \$ckey ], \$unser_ok ); if ( \$unser_ok && ! ( \$group === 'options' && (string) \$k === 'alloptions' && ( ! is_array( \$val ) || empty( \$val ) ) ) ) { \$this->cache[ \$ckey ] = \$val; \$this->cache_hits++; \$values[ \$k ] = \$this->safe_clone( \$val ); continue; } \$this->backend_raw_del( \$ckey ); } \$this->cache_misses++; \$values[ \$k ] = false; } } return \$values; } public function delete_multiple( array \$keys, \$group = 'default' ) { \$values = array(); foreach ( \$keys as \$k ) { \$values[ \$k ] = \$this->delete( \$k, \$group ); } return \$values; } public function delete( \$key, \$group = 'default' ) { if ( ! \$this->is_valid_key( \$key ) ) { return false; } if ( empty( \$group ) ) { \$group = 'default'; } \$ckey = \$this->build_key( \$key, \$group ); \$existed = array_key_exists( \$ckey, \$this->cache ); unset( \$this->cache[ \$ckey ] ); if ( \$this->is_persistent( \$group ) ) { \$del = \$this->backend_raw_del( \$ckey ); return \$existed || \$del; } return \$existed; } private function backend_raw_ttl( \$full_key ) { if ( ! \$this->connected ) { return 0; } try { if ( \$this->driver === 'phpredis' ) { \$ttl = (int) \$this->client->ttl( \$full_key ); return \$ttl > 0 ? \$ttl : 0; } if ( \$this->driver === 'redis_socket' ) { \$ttl = (int) \$this->resp_cmd( \$this->client, array( 'TTL', \$full_key ) ); return \$ttl > 0 ? \$ttl : 0; } } catch ( Throwable \$e ) { \$this->connected = false; } return 0; } public function incr( \$key, \$offset = 1, \$group = 'default' ) { if ( empty( \$group ) ) { \$group = 'default'; } \$found = false; \$val = \$this->get( \$key, \$group, false, \$found ); if ( ! \$found ) { return false; } \$rem_ttl = 0; if ( \$this->is_persistent( \$group ) ) { \$rem_ttl = \$this->backend_raw_ttl( \$this->build_key( \$key, \$group ) ); } \$new = max( 0, (int) \$val + (int) \$offset ); \$this->set( \$key, \$new, \$group, \$rem_ttl ); return \$new; } public function decr( \$key, \$offset = 1, \$group = 'default' ) { return \$this->incr( \$key, -1 * abs( (int) \$offset ), \$group ); } public function flush() { \$this->cache = array(); \$this->group_versions = array(); if ( \$this->connected ) { \$this->gen_version = max( \$this->gen_version + 1, time() ); \$this->backend_raw_set( \$this->prefix . '_gen', (string) \$this->gen_version, 2592000 ); } return true; } public function flush_group( \$group ) { if ( empty( \$group ) ) { \$group = 'default'; } \$grp_prefix = isset( \$this->global_groups[ \$group ] ) ? '' : \$this->blog_prefix; \$grp_scope = \$grp_prefix . \$group; \$old_key_pfx = \$this->build_key( '', \$group ); \$pfx_len = strlen( \$old_key_pfx ); foreach ( array_keys( \$this->cache ) as \$ck ) { if ( strncmp( (string) \$ck, \$old_key_pfx, \$pfx_len ) === 0 ) { unset( \$this->cache[ \$ck ] ); } } \$prev_ver = isset( \$this->group_versions[ \$grp_scope ] ) ? (int) \$this->group_versions[ \$grp_scope ] : 1; \$this->group_versions[ \$grp_scope ] = max( \$prev_ver + 1, time() ); if ( \$this->connected && \$this->is_persistent( \$group ) ) { \$gmap_key = \$this->prefix . 'g' . \$this->gen_version . ':_grp_map'; \$this->backend_raw_set( \$gmap_key, \$this->pack_payload( \$gmap_key, \$this->group_versions ), 2592000 ); } return true; } public function flush_runtime() { \$this->cache = array(); return true; } public function add_global_groups( \$groups ) { foreach ( (array) \$groups as \$g ) { \$this->global_groups[ \$g ] = true; } } public function add_non_persistent_groups( \$groups ) { foreach ( (array) \$groups as \$g ) { \$this->non_persistent_groups[ \$g ] = true; } } public function switch_to_blog( \$blog_id ) { \$this->blog_prefix = \$this->multisite ? ( (int) \$blog_id ) . ':' : ''; } public function reset() { \$this->cache = array(); } public function stats() { echo '

Cache Hits: ' . (int) \$this->cache_hits . '
'; echo 'Cache Misses: ' . (int) \$this->cache_misses . '

'; } public function close() { if ( \$this->driver === 'redis_socket' && is_resource( \$this->client ) ) { @fclose( \$this->client ); } elseif ( \$this->driver === 'phpredis' && is_object( \$this->client ) && method_exists( \$this->client, 'close' ) ) { try { @\$this->client->close(); } catch ( Throwable \$e ) { } } elseif ( \$this->driver === 'memcache' && is_object( \$this->client ) && method_exists( \$this->client, 'close' ) ) { try { @\$this->client->close(); } catch ( Throwable \$e ) { } } return true; } } function wp_cache_init() { \$GLOBALS['wp_object_cache'] = new AIO_Persistent_Object_Cache(); if ( function_exists( 'wp_using_ext_object_cache' ) ) { wp_using_ext_object_cache( \$GLOBALS['wp_object_cache']->is_connected() ); } } function wp_cache_add( \$key, \$data, \$group = '', \$expire = 0 ) { return \$GLOBALS['wp_object_cache']->add( \$key, \$data, \$group, (int) \$expire ); } function wp_cache_replace( \$key, \$data, \$group = '', \$expire = 0 ) { return \$GLOBALS['wp_object_cache']->replace( \$key, \$data, \$group, (int) \$expire ); } function wp_cache_set( \$key, \$data, \$group = '', \$expire = 0 ) { return \$GLOBALS['wp_object_cache']->set( \$key, \$data, \$group, (int) \$expire ); } function wp_cache_get( \$key, \$group = '', \$force = false, &\$found = null ) { return \$GLOBALS['wp_object_cache']->get( \$key, \$group, \$force, \$found ); } function wp_cache_get_multiple( \$keys, \$group = '', \$force = false ) { return \$GLOBALS['wp_object_cache']->get_multiple( \$keys, \$group, \$force ); } function wp_cache_delete( \$key, \$group = '' ) { return \$GLOBALS['wp_object_cache']->delete( \$key, \$group ); } function wp_cache_incr( \$key, \$offset = 1, \$group = '' ) { return \$GLOBALS['wp_object_cache']->incr( \$key, \$offset, \$group ); } function wp_cache_decr( \$key, \$offset = 1, \$group = '' ) { return \$GLOBALS['wp_object_cache']->decr( \$key, \$offset, \$group ); } function wp_cache_flush() { return \$GLOBALS['wp_object_cache']->flush(); } function wp_cache_flush_runtime() { return \$GLOBALS['wp_object_cache']->flush_runtime(); } function wp_cache_flush_group( \$group ) { return \$GLOBALS['wp_object_cache']->flush_group( \$group ); } function wp_cache_add_global_groups( \$groups ) { \$GLOBALS['wp_object_cache']->add_global_groups( \$groups ); } function wp_cache_add_non_persistent_groups( \$groups ) { \$GLOBALS['wp_object_cache']->add_non_persistent_groups( \$groups ); } function wp_cache_switch_to_blog( \$blog_id ) { \$GLOBALS['wp_object_cache']->switch_to_blog( \$blog_id ); } function wp_cache_close() { return \$GLOBALS['wp_object_cache']->close(); } function wp_cache_supports( \$feature ) { return in_array( \$feature, array( 'add_multiple', 'set_multiple', 'get_multiple', 'delete_multiple', 'flush_runtime', 'flush_group' ), true ); } function wp_cache_add_multiple( array \$data, \$group = '', \$expire = 0 ) { return \$GLOBALS['wp_object_cache']->add_multiple( \$data, \$group, (int) \$expire ); } function wp_cache_set_multiple( array \$data, \$group = '', \$expire = 0 ) { return \$GLOBALS['wp_object_cache']->set_multiple( \$data, \$group, (int) \$expire ); } function wp_cache_delete_multiple( array \$keys, \$group = '' ) { return \$GLOBALS['wp_object_cache']->delete_multiple( \$keys, \$group ); } PHP; } }