From 0eba3b72f953331aac3e8e7c094d0ada8867c0cf Mon Sep 17 00:00:00 2001 From: tianfenghan Date: Sun, 6 Sep 2026 17:27:11 +0800 Subject: [PATCH] feat(stdlib): lower SPL runtime calls directly --- phpunit/code/spl-runtime-direct-calls.php | 18 ++++++ phpunit/src/SplRuntimeOptimizerTest.php | 30 ++++++++++ src/Optimizer/FuncCallOptimizer.php | 39 ++++++++++++ tests/compiler/stdlib/spl-runtime-direct.phpt | 60 +++++++++++++++++++ 4 files changed, 147 insertions(+) create mode 100644 phpunit/code/spl-runtime-direct-calls.php create mode 100644 phpunit/src/SplRuntimeOptimizerTest.php create mode 100644 tests/compiler/stdlib/spl-runtime-direct.phpt diff --git a/phpunit/code/spl-runtime-direct-calls.php b/phpunit/code/spl-runtime-direct-calls.php new file mode 100644 index 00000000..208f78b8 --- /dev/null +++ b/phpunit/code/spl-runtime-direct-calls.php @@ -0,0 +1,18 @@ +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::iterator_count(')); + self::assertSame(2, substr_count($code, 'php::fn::iterator_to_array(')); + self::assertSame(1, substr_count($code, 'php::fn::spl_object_hash(')); + self::assertSame(1, substr_count($code, 'php::fn::spl_object_id(')); + self::assertSame(1, substr_count($code, 'php::fn::constant(')); + + // A mixed name must retain Zend's runtime parameter validation rather + // than being coerced into the String ABI used by the direct wrapper. + self::assertStringContainsString('php::call(', $code); + } +} diff --git a/src/Optimizer/FuncCallOptimizer.php b/src/Optimizer/FuncCallOptimizer.php index bf0ebd44..89b4fe8a 100644 --- a/src/Optimizer/FuncCallOptimizer.php +++ b/src/Optimizer/FuncCallOptimizer.php @@ -195,6 +195,45 @@ trait FuncCallOptimizer 'intrinsic' => true, ], + // SPL and runtime constant queries. These wrappers preserve + // Zend's runtime validation and iterator side effects while + // avoiding zend_call_function for ordinary positional calls. + 'iterator_count' => [ + 'args' => 'v', + 'minArgs' => 1, + 'maxArgs' => 1, + 'returnType' => Type::INT, + 'intrinsic' => true, + ], + 'iterator_to_array' => [ + 'args' => 'v_?b', + 'minArgs' => 1, + 'maxArgs' => 2, + 'returnType' => Type::ARRAY, + 'intrinsic' => true, + ], + 'spl_object_hash' => [ + 'args' => 'v', + 'minArgs' => 1, + 'maxArgs' => 1, + 'returnType' => Type::STR, + 'intrinsic' => true, + ], + 'spl_object_id' => [ + 'args' => 'v', + 'minArgs' => 1, + 'maxArgs' => 1, + 'returnType' => Type::INT, + 'intrinsic' => true, + ], + 'constant' => [ + 'args' => 's', + 'minArgs' => 1, + 'maxArgs' => 1, + 'returnType' => Type::ANY, + 'intrinsic' => true, + ], + 'strncmp' => ['constFold' => self::FOLD_CMP3], 'strncasecmp' => ['constFold' => self::FOLD_CMP3], 'explode' => [], diff --git a/tests/compiler/stdlib/spl-runtime-direct.phpt b/tests/compiler/stdlib/spl-runtime-direct.phpt new file mode 100644 index 00000000..418fa907 --- /dev/null +++ b/tests/compiler/stdlib/spl-runtime-direct.phpt @@ -0,0 +1,60 @@ +--TEST-- +SPL iterator, object identity, and constant functions use direct wrappers +--FILE-- + 'phpx', 4 => 42]); +} + +function main(): void +{ + var_dump(iterator_count(makeIterator())); + var_dump(iterator_to_array(makeIterator())); + var_dump(iterator_to_array(makeIterator(), false)); + + $object = new stdClass(); + var_dump(spl_object_id($object) > 0); + var_dump(strlen(spl_object_hash($object)) === 32); + var_dump(spl_object_id($object) === spl_object_id($object)); + + define('TYPEPHP_RUNTIME_FAST_CONSTANT', ['ok' => true]); + var_dump(constant('TYPEPHP_RUNTIME_FAST_CONSTANT')); + + try { + iterator_count(42); + } catch (TypeError $error) { + echo $error->getMessage(), "\n"; + } + + try { + spl_object_hash('invalid'); + } catch (TypeError $error) { + echo $error->getMessage(), "\n"; + } +} +?> +--EXPECT-- +int(2) +array(2) { + ["name"]=> + string(4) "phpx" + [4]=> + int(42) +} +array(2) { + [0]=> + string(4) "phpx" + [1]=> + int(42) +} +bool(true) +bool(true) +bool(true) +array(1) { + ["ok"]=> + bool(true) +} +iterator_count(): Argument #1 ($iterator) must be of type Traversable|array, int given +spl_object_hash(): Argument #1 ($object) must be of type object, string given