From ac0d7f61510604938a86c893c056100f0ca4383a Mon Sep 17 00:00:00 2001 From: tianfenghan Date: Tue, 30 Jun 2026 11:41:16 +0800 Subject: [PATCH] =?UTF-8?q?feat(php):=20=E6=B7=BB=E5=8A=A0=E5=BF=85?= =?UTF-8?q?=E9=9C=80=E5=8F=82=E6=95=B0=E8=AE=A1=E6=95=B0=E6=A3=80=E6=9F=A5?= =?UTF-8?q?=E5=8A=9F=E8=83=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 在生成包装函数参数时添加必需参数数量验证 - 实现参数不足时抛出 ArgumentCountError 异常 - 为可空参数移除默认 null 值传递逻辑 - 添加测试用例验证可空参数无默认值时仍需传入 - 生成详细的错误消息包含函数名和期望参数数量 --- src/Php/Translator.php | 30 +++++++++++++++---- .../nullable-required-param-check.phpt | 23 ++++++++++++++ 2 files changed, 48 insertions(+), 5 deletions(-) create mode 100644 tests/aot/type_decl/nullable-required-param-check.phpt diff --git a/src/Php/Translator.php b/src/Php/Translator.php index 70ab0473..9439af7b 100644 --- a/src/Php/Translator.php +++ b/src/Php/Translator.php @@ -2850,10 +2850,13 @@ CODE; return $code; } - protected function genWrapperFunctionArgs(string $fn, FunctionDef $functionDef): string + protected function genWrapperFunctionArgs(string $fn, FunctionDef $functionDef, string $displayName): string { $cppCode = ''; $callParams = ''; + if ($functionDef->argCountRequired > 0) { + $cppCode .= $this->genWrapperRequiredArgCountCheck($functionDef, $displayName); + } foreach ($functionDef->argInfoList as $k => $argInfo) { $var = 'arg_' . $argInfo->name; if ($argInfo->variadic) { @@ -2874,8 +2877,6 @@ CODE; } else { if ($argInfo->byRef) { $argExpr = 'php::getCallArgByRef(' . $k . ')'; - } elseif ($argInfo->nullable) { - $argExpr = 'php::getCallArg(' . $k . ', php::null)'; } else { $argExpr = 'php::getCallArg(' . $k . ')'; } @@ -2905,13 +2906,32 @@ CODE; return $cppCode; } + private function genWrapperRequiredArgCountCheck(FunctionDef $functionDef, string $displayName): string + { + $required = $functionDef->argCountRequired; + $message = 'php::concat({' + . 'php::Str(' . $this->genCharPtr('Too few arguments to function ' . $displayName . '(), ', true) . '), ' + . 'php::toString(php::getCallArgNum()), ' + . 'php::Str(' . $this->genCharPtr(' passed and exactly ' . $required . ' expected', true) . ')' + . '})'; + + $code = $this->getIndent() . 'if (UNEXPECTED(php::getCallArgNum() < ' . $required . ')) {' . PHP_EOL; + $this->indentLevel++; + $code .= $this->getIndent() . 'php::throwException(zend_ce_argument_count_error, (' . $message . ').toCString());' . PHP_EOL; + $code .= $this->getIndent() . 'return;' . PHP_EOL; + $this->indentLevel--; + $code .= $this->getIndent() . '}' . PHP_EOL; + + return $code; + } + protected function genMethodWrapper(ClassDef $classDef, MethodDef $methodDef): string { $name = $classDef->getNamespacedName(); $cppCode = 'ZEND_METHOD(' . $name . ', ' . $methodDef->name . '){' . PHP_EOL; $cppCode .= $this->getIndent() . self::TYPE_OBJECT . ' this_(&execute_data->This);' . PHP_EOL; $fn = self::PREFIX . $this->getNativeMethodName($classDef, $methodDef); - $cppCode .= $this->genWrapperFunctionArgs($fn, $methodDef->functionDef); + $cppCode .= $this->genWrapperFunctionArgs($fn, $methodDef->functionDef, $classDef->getNamespacedName(false) . '::' . $methodDef->name); return $cppCode; } @@ -3733,7 +3753,7 @@ CODE; $name = $this->escapeZendFnName($functionDef->getNamespacedName()); $cppCode = 'ZEND_FUNCTION(' . $name . '){' . PHP_EOL; $fn = self::PREFIX . $this->getNativeName($functionDef->name, $functionDef->namespace); - $cppCode .= $this->genWrapperFunctionArgs($fn, $functionDef); + $cppCode .= $this->genWrapperFunctionArgs($fn, $functionDef, $functionDef->getNamespacedName()); return $cppCode; } diff --git a/tests/aot/type_decl/nullable-required-param-check.phpt b/tests/aot/type_decl/nullable-required-param-check.phpt new file mode 100644 index 00000000..e9bc531e --- /dev/null +++ b/tests/aot/type_decl/nullable-required-param-check.phpt @@ -0,0 +1,23 @@ +--TEST-- +Nullable parameter without default is still required +--FILE-- +getMessage()); + } +} +?> +--EXPECT-- +string(18) "ArgumentCountError" +string(84) "Too few arguments to function expect_nullable_int(), 0 passed and exactly 1 expected"