PluginProbe
xSpeed Cache: AI-Powered Performance Hub with MCP, Caching & CDN / 1.4.2
xSpeed Cache: AI-Powered Performance Hub with MCP, Caching & CDN v1.4.2
1.4.2 1.4.1 1.4.0 1.3.7 1.3.6 1.3.5 1.3.4 1.3.3 1.3.2 1.3.1 1.3.0 1.2.4 trunk 1.0.0 1.0.1 1.0.2 1.0.3 1.0.4 1.0.5 1.0.6 1.0.7 1.0.8 1.0.9 1.1.0 1.1.1 All 36 releases
← All changes | includes/class-object-cache.php +33 -17 1.4.0 → 1.4.2 View file →
@@ -228,10 +228,19 @@
228 228 $prefix = self::effective_salt( $opts );
229 229 $timeout = self::int( $opts, 'connection_timeout', 1 );
230 230 $persist = ! empty( $opts['persistent'] );
231 231
232 - $lines[] = "define( 'WP_REDIS_HOST', '" . self::esc( $host ) . "' );";
233 - $lines[] = "define( 'WP_REDIS_PORT', " . $port . ' );';
232 + // A socket is written the way Redis Object Cache expects one,
233 + // scheme plus path, so the snippet works with that plugin too.
234 + // xSpeed's drop-in reads the same pair. (#566)
235 + $socket = Redis_Client::socket_path( $host );
236 + if ( '' !== $socket ) {
237 + $lines[] = "define( 'WP_REDIS_SCHEME', 'unix' );";
238 + $lines[] = "define( 'WP_REDIS_PATH', '" . self::esc( $socket ) . "' );";
239 + } else {
240 + $lines[] = "define( 'WP_REDIS_HOST', '" . self::esc( $host ) . "' );";
241 + $lines[] = "define( 'WP_REDIS_PORT', " . $port . ' );';
242 + }
234 243 // Emit the ACL username only when set (Redis 6+). The drop-in
235 244 // reads it; an empty user keeps the legacy default-user behavior.
236 245 if ( '' !== $user ) {
237 246 $lines[] = "define( 'WP_REDIS_USER', '" . self::esc( $user ) . "' );";
@@ -337,13 +346,21 @@
337 346 $timeout = self::int( $opts, 'connection_timeout', 1 );
338 347 $user = self::str( $opts, 'redis_user', '' );
339 348 $pass = self::str( $opts, 'redis_password', '' );
340 349 $db = self::int( $opts, 'redis_database', 0 );
350 + // A socket path has no port. phpredis opens a socket only when the
351 + // port is below 1, and the messages should name the path the user
352 + // typed rather than "path:6379". (#566)
353 + $socket = Redis_Client::socket_path( $host );
354 + $where = '' !== $socket ? $socket : "{$host}:{$port}";
341 355
342 356 if ( class_exists( '\\Redis' ) ) {
343 - $redis = new \Redis();
344 - if ( ! @$redis->connect( $host, $port, $timeout ) ) {
345 - return self::test_result( false, $backend, "Could not connect to Redis at {$host}:{$port}." );
357 + $redis = new \Redis();
358 + $connected = '' !== $socket
359 + ? @$redis->connect( $socket, 0, $timeout )
360 + : @$redis->connect( $host, $port, $timeout );
361 + if ( ! $connected ) {
362 + return self::test_result( false, $backend, "Could not connect to Redis at {$where}." );
346 363 }
347 364 // Redis 6+ ACL: when a username is set, authenticate as that user
348 365 // (phpredis ≥ 5.3 accepts ['user'=>..,'pass'=>..]); otherwise keep
349 366 // the legacy password-only form that authenticates as `default`.
@@ -356,9 +373,9 @@
356 373 }
357 374 $pong = @$redis->ping();
358 375 $ok = ( '+PONG' === $pong || true === $pong || 'PONG' === $pong );
359 376 if ( ! $ok ) {
360 - return self::test_result( false, $backend, "Redis at {$host}:{$port} did not respond to PING.", $start );
377 + return self::test_result( false, $backend, "Redis at {$where} did not respond to PING.", $start );
361 378 }
362 379 // Write-verification: PING only proves auth, not that the user can
363 380 // STORE data. ACL-namespaced hosts (xCloud) restrict a user to a
364 381 // key pattern (~redis:<id>:*); a SET outside it is NOPERM-denied and
@@ -370,17 +387,17 @@
370 387 $set = @$redis->set( $probe, '1', 5 ); // phpcs:ignore WordPress.PHP.NoSilencedErrors.Discouraged -- NOPERM/denied is the negative answer we report, not a fatal.
371 388 $got = @$redis->get( $probe ); // phpcs:ignore WordPress.PHP.NoSilencedErrors.Discouraged -- same.
372 389 @$redis->del( $probe ); // phpcs:ignore WordPress.PHP.NoSilencedErrors.Discouraged -- best-effort cleanup.
373 390 if ( ! $set || '1' !== (string) $got ) {
374 - return self::test_result( false, $backend, self::write_denied_message( $opts, $host, $port ), $start );
391 + return self::test_result( false, $backend, self::write_denied_message( $opts, $where ), $start );
375 392 }
376 - return self::test_result( true, $backend, self::with_prefix_advisory( "Connected to Redis at {$host}:{$port} (phpredis).", $opts ), $start );
393 + return self::test_result( true, $backend, self::with_prefix_advisory( "Connected to Redis at {$where} (phpredis).", $opts ), $start );
377 394 }
378 395
379 396 // Pure-PHP fallback — our own client, zero dependencies.
380 397 $rc = new Redis_Client( $host, $port, (float) $timeout, false );
381 398 if ( ! $rc->connect() ) {
382 - return self::test_result( false, $backend, "Could not connect to Redis at {$host}:{$port}." );
399 + return self::test_result( false, $backend, "Could not connect to Redis at {$where}." );
383 400 }
384 401 // Authenticate when a user OR a password is set. Gating on password
385 402 // alone skipped auth for the "ACL user + empty password" case, which
386 403 // then failed later at PING with a misleading message. (FBS-83118 OC-1)
@@ -394,9 +411,9 @@
394 411 $pong = $rc->ping();
395 412 $ok = ( is_string( $pong ) && false !== stripos( $pong, 'PONG' ) );
396 413 if ( ! $ok ) {
397 414 $rc->close();
398 - return self::test_result( false, $backend, "Redis at {$host}:{$port} did not respond to PING.", $start );
415 + return self::test_result( false, $backend, "Redis at {$where} did not respond to PING.", $start );
399 416 }
400 417 // Write-verification round-trip — same rationale as the phpredis path
401 418 // above. (FBS-83118 OC-2)
402 419 $probe = self::probe_key( $opts );
@@ -404,11 +421,11 @@
404 421 $got = $rc->get( $probe );
405 422 $rc->del( $probe );
406 423 $rc->close();
407 424 if ( ! $set || '1' !== (string) $got ) {
408 - return self::test_result( false, $backend, self::write_denied_message( $opts, $host, $port ), $start );
425 + return self::test_result( false, $backend, self::write_denied_message( $opts, $where ), $start );
409 426 }
410 - return self::test_result( true, $backend, self::with_prefix_advisory( "Connected to Redis at {$host}:{$port} (built-in client).", $opts ), $start );
427 + return self::test_result( true, $backend, self::with_prefix_advisory( "Connected to Redis at {$where} (built-in client).", $opts ), $start );
411 428 } catch ( \Throwable $e ) {
412 429 return self::test_result( false, $backend, 'Connection error: ' . $e->getMessage() );
413 430 }
414 431 }
@@ -636,19 +653,18 @@
636 653 * Message for a connect-OK-but-write-denied result. Points ACL/namespaced
637 654 * hosts at the fix (match the key prefix to the host's Redis Object Cache
638 655 * Key), which is exactly the xCloud failure mode. (FBS-83118 OC-2)
639 656 *
640 - * @param array $opts Settings array.
641 - * @param string $host Redis host.
642 - * @param int $port Redis port.
657 + * @param array $opts Settings array.
658 + * @param string $where host:port, or the socket path.
643 659 * @return string
644 660 */
645 - private static function write_denied_message( array $opts, string $host, int $port ): string {
661 + private static function write_denied_message( array $opts, string $where ): string {
646 662 $has_prefix = '' !== self::str( $opts, 'key_prefix', '' );
647 663 $hint = $has_prefix
648 664 ? 'The Redis user may lack write permission for this key prefix (NOPERM).'
649 665 : 'On ACL/namespaced Redis (e.g. xCloud), set Cache key prefix to the host\'s "Redis Object Cache Key" so writes land in the permitted namespace.';
650 - return "Connected to Redis at {$host}:{$port}, but the cache could not store data. {$hint}";
666 + return "Connected to Redis at {$where}, but the cache could not store data. {$hint}";
651 667 }
652 668
653 669 /**
654 670 * Full plug-and-play enable: test → write wp-config constants → install