From db5de844688726c195135da0d33c6bc99fdf31b6 Mon Sep 17 00:00:00 2001 From: tianfenghan Date: Wed, 17 Jun 2026 18:15:57 +0800 Subject: [PATCH] =?UTF-8?q?fix(php):=20=E4=BF=AE=E5=A4=8D=E5=8F=AF?= =?UTF-8?q?=E9=80=89=E5=8F=82=E6=95=B0=E4=B8=AD=E7=9A=84null=E5=80=BC?= =?UTF-8?q?=E5=A4=84=E7=90=86=E9=97=AE=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 在函数调用优化器中添加对可空参数的支持 - 修改buildArgList方法以接收和处理nullables数组 - 当参数允许null值时直接传递原始变量而非解析值 - 更新函数反射信息以包含参数可空性信息 - 调整测试用例以反映explode函数中null限制的行为变化 - 移除显式传入null时使用默认值的特殊处理逻辑 --- src/Php/Optimizer/FuncCallOptimizer.php | 29 ++++++++++++++----------- tests/aot/stdlib/null_optional_arg.phpt | 5 +++-- 2 files changed, 19 insertions(+), 15 deletions(-) diff --git a/src/Php/Optimizer/FuncCallOptimizer.php b/src/Php/Optimizer/FuncCallOptimizer.php index 1407da95..f04e1f5d 100644 --- a/src/Php/Optimizer/FuncCallOptimizer.php +++ b/src/Php/Optimizer/FuncCallOptimizer.php @@ -248,7 +248,8 @@ trait FuncCallOptimizer } } - $args = $this->buildArgList($expr, $argTypeStr, $defaults); + $nullables = $refInfo['nullables'] ?? []; + $args = $this->buildArgList($expr, $argTypeStr, $defaults, $nullables); return $target . '(' . implode(', ', $args) . ')'; } @@ -264,10 +265,11 @@ trait FuncCallOptimizer $ref = Reflection::getFunction($funcName); if (!$ref) { - return $this->_autoArgTypes[$funcName] = ['args' => '', 'variadic' => false, 'variadicType' => '', 'minArgs' => 0, 'maxArgs' => 0]; + return $this->_autoArgTypes[$funcName] = ['args' => '', 'variadic' => false, 'variadicType' => '', 'minArgs' => 0, 'maxArgs' => 0, 'nullables' => []]; } $types = []; + $nullables = []; $variadic = false; $variadicType = ''; foreach ($ref->getParameters() as $param) { @@ -281,6 +283,7 @@ trait FuncCallOptimizer $char = self::ARG_OPTIONAL . $char; } $types[] = $char; + $nullables[] = $param->allowsNull(); } return $this->_autoArgTypes[$funcName] = [ @@ -289,6 +292,7 @@ trait FuncCallOptimizer 'variadicType' => $variadicType, 'minArgs' => $ref->getNumberOfRequiredParameters(), 'maxArgs' => $ref->getNumberOfParameters(), + 'nullables' => $nullables, ]; } @@ -375,7 +379,7 @@ trait FuncCallOptimizer return $this->convertArrayExpr($raw); } - protected function buildArgList(Node\Expr\FuncCall $expr, string $argTypeStr, array $defaults = []): array + protected function buildArgList(Node\Expr\FuncCall $expr, string $argTypeStr, array $defaults = [], array $nullables = []): array { if ($argTypeStr === '') { return []; @@ -387,23 +391,22 @@ trait FuncCallOptimizer foreach ($types as $i => $type) { $optional = ($type[0] ?? '') === self::ARG_OPTIONAL; + $nullable = $nullables[$i] ?? false; + + // Missing optional arg — use configured default or skip (C++ default handles it) if ($optional && $argCount <= $i) { if (isset($defaults[$i])) { $args[] = $defaults[$i]; } continue; } - // When null is explicitly passed to an optional parameter, skip it - // so the C++ default value takes effect (PHP null = "use default"). - if ($optional && $argCount > $i && isset($expr->args[$i])) { - $argVal = $expr->args[$i]->value; - if ($argVal instanceof Node\Expr\ConstFetch && strtolower($argVal->name->toString()) === 'null') { - if (isset($defaults[$i])) { - $args[] = $defaults[$i]; - } - continue; - } + + // Nullable param — pass raw Variant; C++ function checks isNull() at runtime + if ($nullable) { + $args[] = $this->getArg($expr, $i); + continue; } + $args[] = $this->resolveArg($expr, $i, $type); } diff --git a/tests/aot/stdlib/null_optional_arg.phpt b/tests/aot/stdlib/null_optional_arg.phpt index 902320d9..338a0122 100644 --- a/tests/aot/stdlib/null_optional_arg.phpt +++ b/tests/aot/stdlib/null_optional_arg.phpt @@ -2,6 +2,7 @@ Passing null to optional parameters should use C++ default values --FILE--