fix: Accept null for $seeds in RedisCluster::__construct

The stub declares $seeds as ?array but the C code used format
specifier 'a' (non-nullable) instead of 'a!' in
zend_parse_method_parameters. This caused new RedisCluster(null, null)
to throw TypeError instead of RedisClusterException, contradicting
the declared type signature.

Also treat z_seeds == NULL the same as ZEND_NUM_ARGS() < 2 so that
explicitly passing null falls through to INI-based seed loading,
matching the behaviour when the argument is omitted entirely.

Fixes GH-2810.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
derrickschoen
2026-02-25 17:17:18 -05:00
committed by Michael Grunder
parent 741abf09ec
commit 409508afa2
2 changed files with 36 additions and 2 deletions
+2 -2
View File
@@ -353,7 +353,7 @@ PHP_METHOD(RedisCluster, __construct) {
// Parse arguments
if (zend_parse_method_parameters(ZEND_NUM_ARGS(), getThis(),
"Os!|addbza!", &object, redis_cluster_ce, &name,
"Os!|a!ddbza!", &object, redis_cluster_ce, &name,
&name_len, &z_seeds, &timeout, &read_timeout,
&persistent, &z_auth, &context) == FAILURE)
{
@@ -361,7 +361,7 @@ PHP_METHOD(RedisCluster, __construct) {
}
/* If we've got a string try to load from INI */
if (ZEND_NUM_ARGS() < 2) {
if (ZEND_NUM_ARGS() < 2 || z_seeds == NULL) {
if (name_len == 0) { // Require a name
CLUSTER_THROW_EXCEPTION("You must specify a name or pass seeds!", 0);
}
+34
View File
@@ -70,6 +70,40 @@ class Redis_Cluster_Test extends Redis_Test {
public function testSession_noUnlockOfOtherProcess() { $this->markTestSkipped(); }
public function testSession_lockWaitTime() { $this->markTestSkipped(); }
/* Regression test for GH #2810 */
public function testConstructNullSeeds() {
/* new RedisCluster(null, null) must not throw TypeError.
* $seeds is declared ?array so null is a valid argument. */
$thrown = false;
try {
new RedisCluster(null, null);
} catch (\Throwable $e) {
$thrown = true;
$this->assertFalse($e instanceof \TypeError);
}
$this->assertTrue($thrown);
/* Passing an empty array must also not throw TypeError (control). */
$thrown = false;
try {
new RedisCluster(null, []);
} catch (\Throwable $e) {
$thrown = true;
$this->assertFalse($e instanceof \TypeError);
}
$this->assertTrue($thrown);
/* Both (null) and (null, null) mean "no name, no seeds" and must
* produce the same exception type. */
$ex1 = $ex2 = null;
try { new RedisCluster(null); }
catch (\Throwable $e) { $ex1 = get_class($e); }
try { new RedisCluster(null, null); }
catch (\Throwable $e) { $ex2 = get_class($e); }
$this->assertTrue($ex1 !== null);
$this->assertEquals($ex1, $ex2);
}
private function loadSeedsFromHostPort($host, $port) {
try {
$rc = new RedisCluster(NULL, ["$host:$port"], 1, 1, true, $this->getAuth());