diff --git a/run-tests.php b/run-tests.php index b23d9784..a9003db8 100755 --- a/run-tests.php +++ b/run-tests.php @@ -4210,9 +4210,6 @@ function compile_php_file(string $file): string } system('./bin/compiler.php ' . $file); $binary_file = str_replace('-', '_', basename($file, '.php')); - if (is_numeric($binary_file)) { - $binary_file = 'app_' . $binary_file; - } if (!file_exists($binary_file)) { throw new Exception('Compilation failed'); } diff --git a/src/Php/ArgInfo.php b/src/Php/ArgInfo.php index f2ba9e6a..03d34579 100644 --- a/src/Php/ArgInfo.php +++ b/src/Php/ArgInfo.php @@ -8,11 +8,14 @@ namespace PhpAot\Php; +use PhpParser\Node\Expr; + class ArgInfo { public string $name; public string $type; public string $default = ''; + public ?Expr $defaultValue = null; public bool $byRef = false; public bool $variadic = false; public bool $property = false; diff --git a/src/Php/CompilerBase.php b/src/Php/CompilerBase.php index f55c77b9..6034396b 100644 --- a/src/Php/CompilerBase.php +++ b/src/Php/CompilerBase.php @@ -995,6 +995,7 @@ class CompilerBase extends \PhpAot\Core\Translator if ($param->default) { $defaultValueCount++; $argInfo->default = $this->parseParamDefaultValue($param->default); + $argInfo->defaultValue = $param->default; } $functionDef->argInfoList[] = $argInfo; } @@ -2246,6 +2247,12 @@ class CompilerBase extends \PhpAot\Core\Translator } // 对 key 进行排序,确保参数顺序正确 if ($hasNamedArg) { + // 命名参数中间存在空洞,需要使用默认参数填充 + foreach ($functionDef->argInfoList as $k => $argInfo) { + if (!isset($args[$k])) { + $args[$k] = new Node\Arg($argInfo->defaultValue); + } + } ksort($args); } diff --git a/tests/aot/named_args/001.phpt b/tests/aot/named_args/001.phpt new file mode 100644 index 00000000..613acf4a --- /dev/null +++ b/tests/aot/named_args/001.phpt @@ -0,0 +1,53 @@ +--TEST-- +Named Arguments - PHP 8+ function call syntax +--FILE-- + $name, + 'email' => $email, + 'age' => $age, + 'active' => $active, + ]; +} + +function main() { + // Test basic named arguments + $user1 = create_user( + name: 'John Doe', + email: 'john@example.com', + age: 25 + ); + var_dump($user1); + + // Test skipping optional parameters + $user2 = create_user( + name: 'Jane Smith', + email: 'jane@example.com' + ); + var_dump($user2); +} +?> +--EXPECT-- +array(4) { + ["name"]=> + string(8) "John Doe" + ["email"]=> + string(16) "john@example.com" + ["age"]=> + int(25) + ["active"]=> + bool(true) +} +array(4) { + ["name"]=> + string(10) "Jane Smith" + ["email"]=> + string(16) "jane@example.com" + ["age"]=> + int(18) + ["active"]=> + bool(true) +} + diff --git a/tests/aot/named_args/002.phpt b/tests/aot/named_args/002.phpt new file mode 100644 index 00000000..18c03fe4 --- /dev/null +++ b/tests/aot/named_args/002.phpt @@ -0,0 +1,35 @@ +--TEST-- +Named Arguments - PHP 8+ function call syntax +--FILE-- + $name, + 'email' => $email, + 'age' => $age, + 'active' => $active, + ]; +} + +function main() { + // Test all defaults + $user3 = create_user( + name: 'Bob', + email: 'bob@example.com', + active: false + ); + var_dump($user3); +} +?> +--EXPECT-- +array(4) { + ["name"]=> + string(3) "Bob" + ["email"]=> + string(15) "bob@example.com" + ["age"]=> + int(18) + ["active"]=> + bool(false) +} diff --git a/tests/aot/named-args.phpt b/tests/aot/named_args/003.phpt similarity index 100% rename from tests/aot/named-args.phpt rename to tests/aot/named_args/003.phpt diff --git a/tests/aot/named_args/004.phpt b/tests/aot/named_args/004.phpt new file mode 100644 index 00000000..06469d74 --- /dev/null +++ b/tests/aot/named_args/004.phpt @@ -0,0 +1,117 @@ +--TEST-- +Named Arguments - PHP 8+ function call syntax +--FILE-- + $table, + 'fields' => $fields, + 'where' => $where, + 'limit' => $limit, + ]; +} + +function main() { + // Test named arguments with math + var_dump(multiply(a: 5, b: 10)); + var_dump(multiply(a: 5, b: 10, scale: 2)); + var_dump(multiply(scale: 3, a: 4, b: 6)); + + // Test all optional with named + var_dump(greet()); + var_dump(greet(greeting: 'Hi')); + var_dump(greet(name: 'Alice')); + var_dump(greet(punctuation: '?', greeting: 'How are you', name: '')); + + // Test mixed positional and named (positional must come first) + var_dump(format_string('[', 'content', ']')); + var_dump(format_string('[', 'content', suffix: ')')); + + // Test build query with various combinations + $query1 = build_query(table: 'users'); + var_dump($query1); + + $query2 = build_query( + table: 'products', + fields: ['id', 'name', 'price'], + where: 'active = 1' + ); + var_dump($query2); + + $query3 = build_query( + limit: 10, + table: 'orders', + where: 'status = "pending"' + ); + var_dump($query3); +} +?> +--EXPECT-- +int(50) +int(100) +int(72) +string(13) "Hello, World!" +string(10) "Hi, World!" +string(13) "Hello, Alice!" +string(14) "How are you, ?" +string(9) "[content]" +string(9) "[content)" +array(4) { + ["table"]=> + string(5) "users" + ["fields"]=> + array(1) { + [0]=> + string(1) "*" + } + ["where"]=> + string(0) "" + ["limit"]=> + int(100) +} +array(4) { + ["table"]=> + string(8) "products" + ["fields"]=> + array(3) { + [0]=> + string(2) "id" + [1]=> + string(4) "name" + [2]=> + string(5) "price" + } + ["where"]=> + string(10) "active = 1" + ["limit"]=> + int(100) +} +array(4) { + ["table"]=> + string(6) "orders" + ["fields"]=> + array(1) { + [0]=> + string(1) "*" + } + ["where"]=> + string(18) "status = "pending"" + ["limit"]=> + int(10) +} \ No newline at end of file diff --git a/tests/aot/named_args/005.phpt b/tests/aot/named_args/005.phpt new file mode 100644 index 00000000..0951c57d --- /dev/null +++ b/tests/aot/named_args/005.phpt @@ -0,0 +1,20 @@ +--TEST-- +Named Arguments - PHP 8+ function call syntax +--FILE-- +>')); +} +?> +--EXPECT-- +string(9) "[content]" +string(9) "[content)" +string(8) "<>"