From 684c47458b9c529d40568b09197aaa50896b9c7e Mon Sep 17 00:00:00 2001 From: tianfenghan Date: Sun, 6 Sep 2026 16:00:58 +0800 Subject: [PATCH] feat(stdlib): lower core datetime calls directly --- phpunit/code/datetime-direct-calls.php | 10 +++++++ phpunit/src/DatetimeOptimizerTest.php | 26 ++++++++++++++++ src/Optimizer/FuncCallOptimizer.php | 35 ++++++++++++++++++++-- tests/compiler/stdlib/datetime-direct.phpt | 18 +++++++++++ 4 files changed, 86 insertions(+), 3 deletions(-) create mode 100644 phpunit/code/datetime-direct-calls.php create mode 100644 phpunit/src/DatetimeOptimizerTest.php create mode 100644 tests/compiler/stdlib/datetime-direct.phpt diff --git a/phpunit/code/datetime-direct-calls.php b/phpunit/code/datetime-direct-calls.php new file mode 100644 index 00000000..6785d49a --- /dev/null +++ b/phpunit/code/datetime-direct-calls.php @@ -0,0 +1,10 @@ +addFiles([$source]); + $compiler->prepareFile($source); + $generated = $compiler->convertFile($source); + $code = file_get_contents($generated); + + self::assertIsString($code); + self::assertSame(1, substr_count($code, 'php::fn::time(')); + self::assertSame(2, substr_count($code, 'php::fn::date(')); + self::assertSame(1, substr_count($code, 'php::fn::gmdate(')); + self::assertStringNotContainsString('get_persistent_func', $code); + self::assertStringNotContainsString('php::call(', $code); + } +} diff --git a/src/Optimizer/FuncCallOptimizer.php b/src/Optimizer/FuncCallOptimizer.php index 7dc70374..4e49b066 100644 --- a/src/Optimizer/FuncCallOptimizer.php +++ b/src/Optimizer/FuncCallOptimizer.php @@ -81,9 +81,9 @@ trait FuncCallOptimizer 'version_compare', 'gettype', 'is_array', 'is_string', 'is_object', 'is_resource', 'is_scalar', 'is_numeric', 'is_countable', 'is_iterable', - 'array_is_list', 'is_dir', 'is_file', 'file_exists', 'realpath', 'time', + 'array_is_list', 'is_dir', 'is_file', 'file_exists', 'realpath', 'in_array', 'array_search', - 'date', 'strtotime', 'md5', 'sha1', 'hash', 'print_r', + 'strtotime', 'md5', 'sha1', 'hash', 'print_r', 'base64_encode', 'base64_decode', 'urlencode', 'urldecode', 'rawurlencode', 'rawurldecode', 'json_encode', 'json_decode', 'serialize', 'unserialize', @@ -121,6 +121,31 @@ trait FuncCallOptimizer 'str_ends_with' => [], 'str_contains' => [], + // Date/time core functions. Keep their ABI explicit so these + // calls never depend on runtime Reflection metadata or fall back + // to php::call() when the arguments have proven scalar types. + 'time' => [ + 'args' => '', + 'minArgs' => 0, + 'maxArgs' => 0, + 'returnType' => Type::INT, + 'intrinsic' => true, + ], + 'date' => [ + 'args' => 's_?i', + 'minArgs' => 1, + 'maxArgs' => 2, + 'returnType' => Type::STR, + 'intrinsic' => true, + ], + 'gmdate' => [ + 'args' => 's_?i', + 'minArgs' => 1, + 'maxArgs' => 2, + 'returnType' => Type::STR, + 'intrinsic' => true, + ], + 'strncmp' => ['constFold' => self::FOLD_CMP3], 'strncasecmp' => ['constFold' => self::FOLD_CMP3], 'explode' => [], @@ -361,7 +386,11 @@ trait FuncCallOptimizer if ($argCount < $minArgs) { $this->fatalError($expr, "{$name}() expects at least {$minArgs} argument(s), {$argCount} given"); } - if ($maxArgs > 0 && $argCount > $maxArgs) { + // An explicit zero is a real zero-argument limit (for example + // time()). Reflection lookup failure also uses zero as its + // unknown sentinel, so only enforce that implicit value when it + // is greater than zero. + if ((array_key_exists('maxArgs', $config) || $maxArgs > 0) && $argCount > $maxArgs) { $this->fatalError($expr, "{$name}() expects at most {$maxArgs} argument(s), {$argCount} given"); } } diff --git a/tests/compiler/stdlib/datetime-direct.phpt b/tests/compiler/stdlib/datetime-direct.phpt new file mode 100644 index 00000000..c5e5261b --- /dev/null +++ b/tests/compiler/stdlib/datetime-direct.phpt @@ -0,0 +1,18 @@ +--TEST-- +date, gmdate and time use direct PHPX wrappers +--FILE-- + 0); +} +?> +--EXPECT-- +string(19) "1970-01-01 08:00:00" +string(19) "1970-01-01 00:00:00" +bool(true)