fix(php): 修复可选参数中的null值处理问题

- 在函数调用优化器中添加对可空参数的支持
- 修改buildArgList方法以接收和处理nullables数组
- 当参数允许null值时直接传递原始变量而非解析值
- 更新函数反射信息以包含参数可空性信息
- 调整测试用例以反映explode函数中null限制的行为变化
- 移除显式传入null时使用默认值的特殊处理逻辑
pull/2/head
韩天峰 2 months ago
parent f45dadc2f8
commit db5de84468
  1. 29
      src/Php/Optimizer/FuncCallOptimizer.php
  2. 5
      tests/aot/stdlib/null_optional_arg.phpt

@ -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);
}

@ -2,6 +2,7 @@
Passing null to optional parameters should use C++ default values
--FILE--
<?php
error_reporting(E_ALL & ~E_DEPRECATED);
// substr: null length should take rest of string, not return empty
$s = 'hello world';
@ -29,7 +30,7 @@ var_dump(strstr($s, 'o', null) === strstr($s, 'o'));
// str_repeat: ensure non-null still works
var_dump(str_repeat('ab', 3));
// explode with null limit should default to PHP_INT_MAX (no limit)
// explode with null limit: null coerces to 0 (limit=0: whole string as single element)
$arr = explode(' ', 'a b c d', null);
var_dump(count($arr));
@ -48,4 +49,4 @@ bool(true)
string(7) "o world"
bool(true)
string(6) "ababab"
int(4)
int(1)

Loading…
Cancel
Save