| @@ -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 |