diff --git a/phpunit/code/function-method-symbol-collision.php b/phpunit/code/function-method-symbol-collision.php index 03ff57f4..3b5c4d60 100644 --- a/phpunit/code/function-method-symbol-collision.php +++ b/phpunit/code/function-method-symbol-collision.php @@ -10,6 +10,11 @@ namespace Collision { } namespace Collision\Worker { + function validate(mixed $validater): mixed + { + return $validater; + } + function invoke_validate(mixed $validater): mixed { return validate($validater); diff --git a/phpunit/src/FunctionTest.php b/phpunit/src/FunctionTest.php index 3228438c..cdb88016 100644 --- a/phpunit/src/FunctionTest.php +++ b/phpunit/src/FunctionTest.php @@ -13,8 +13,8 @@ class FunctionTest extends \BaseTest $cppFile = $compiler->convertFile($testFile); $cpp = file_get_contents($cppFile); - $this->assertStringContainsString('php::call(', $cpp); - $this->assertStringNotContainsString('php_collision__worker__validate(validater)', $cpp); + $this->assertStringContainsString('php_collision__worker__NSE__validate(validater)', $cpp); + $this->assertStringContainsString('php_collision__NSE__worker__validate(', $cpp); } public function testReturnRef(): void diff --git a/src/CompilerBase.php b/src/CompilerBase.php index 2663aadf..96a301a3 100644 --- a/src/CompilerBase.php +++ b/src/CompilerBase.php @@ -196,6 +196,7 @@ class CompilerBase implements PropertyAccessContext public const string FUNC_MAP = 'func_map'; public const string PROP_MAP = 'property_map'; public const string NAMESPACE_SEPARATOR = '__'; + public const string NAMESPACE_END_MARKER = 'NSE'; public const string PREFIX = 'php_'; protected const string MULTI_RETURN_NAMESPACE = 'typephp::detail'; @@ -1018,7 +1019,14 @@ class CompilerBase implements PropertyAccessContext protected function getFunctionName(FunctionLike $v): string { - return $this->getNativeName($this->parseIdentifier($v->name), $this->namespace, $this->class); + if ($this->class !== '') { + return $this->getNativeClassMethodName( + $this->parseIdentifier($v->name), + $this->namespace, + $this->class, + ); + } + return $this->getNativeFunctionName($this->parseIdentifier($v->name), $this->namespace); } protected function getFullClassName(): string @@ -1141,6 +1149,29 @@ class CompilerBase implements PropertyAccessContext return implode(self::NAMESPACE_SEPARATOR, $names); } + protected function getNativeFunctionName(string $function, string $namespace = ''): string + { + $names = []; + if ($namespace !== '') { + $names[] = $this->escapeNamespace($namespace); + $names[] = self::NAMESPACE_END_MARKER; + } + $names[] = $this->escapeName($function); + return implode(self::NAMESPACE_SEPARATOR, $names); + } + + protected function getNativeClassMethodName(string $method, string $namespace, string $class): string + { + $names = []; + if ($namespace !== '') { + $names[] = $this->escapeNamespace($namespace); + } + $names[] = self::NAMESPACE_END_MARKER; + $names[] = $this->escapeClass($class); + $names[] = $this->escapeName($method); + return implode(self::NAMESPACE_SEPARATOR, $names); + } + protected function getClassId(string $className): int { if (isset($this->classMap[$className])) { @@ -2362,7 +2393,7 @@ class CompilerBase implements PropertyAccessContext if ($checkArgs) { $this->checkNativeCallArgs($expr, $methodDef->functionDef, $expr->args, $classDef->getNamespacedName() . '::' . $method); } - return $this->getNativeName($method, $classDef->namespace, $classDef->name); + return $this->getNativeClassMethodName($method, $classDef->namespace, $classDef->name); } protected function findNativeClassConst(NodeAbstract $expr, string $class, string $const): string|false @@ -2814,17 +2845,33 @@ class CompilerBase implements PropertyAccessContext // 绝对命名空间的函数 if ($funcName[0] == '\\') { $funcName = ltrim($funcName, '\\'); - $possibleFunctionNames = [$this->escapeName($funcName)]; + $separator = strrpos($funcName, '\\'); + if ($separator === false) { + $possibleFunctionNames = [$this->getNativeFunctionName($funcName)]; + } else { + $possibleFunctionNames = [$this->getNativeFunctionName( + substr($funcName, $separator + 1), + substr($funcName, 0, $separator), + )]; + } } else { - $possibleFunctionNames = [$this->escapeName($funcName)]; + $possibleFunctionNames = [$this->getNativeFunctionName($funcName)]; if (isset($this->useAliases[$funcName])) { - $possibleFunctionNames[] = $this->escapeName($this->escapeNamespace($this->useAliases[$funcName])); + $alias = $this->useAliases[$funcName]; + $separator = strrpos($alias, '\\'); + $possibleFunctionNames[] = $separator === false + ? $this->getNativeFunctionName($alias) + : $this->getNativeFunctionName(substr($alias, $separator + 1), substr($alias, 0, $separator)); } if ($this->namespace) { - $possibleFunctionNames[] = $this->escapeNamespace($this->namespace) . self::NAMESPACE_SEPARATOR . $this->escapeName($funcName); + $possibleFunctionNames[] = $this->getNativeFunctionName($funcName, $this->namespace); } if (isset($this->useFunctions[$funcName])) { - $possibleFunctionNames[] = $this->escapeNamespace($this->useFunctions[$funcName]); + $alias = $this->useFunctions[$funcName]; + $separator = strrpos($alias, '\\'); + $possibleFunctionNames[] = $separator === false + ? $this->getNativeFunctionName($alias) + : $this->getNativeFunctionName(substr($alias, $separator + 1), substr($alias, 0, $separator)); } // 复杂命名空间规则,组合命名空间 // 例子:use foo\bar; bar\fn(); @@ -2834,7 +2881,12 @@ class CompilerBase implements PropertyAccessContext if ($ns1[array_key_last($ns1)] === $ns2[array_key_first($ns2)]) { $ns = array_merge($ns1, $ns2); array_splice($ns, array_key_last($ns1) + 1); - $possibleFunctionNames[] = $this->escapeNamespace(implode('\\', $ns)); + $fullName = implode('\\', $ns); + $separator = strrpos($fullName, '\\'); + $possibleFunctionNames[] = $this->getNativeFunctionName( + substr($fullName, $separator + 1), + substr($fullName, 0, $separator), + ); break; } } diff --git a/src/Context/CompilationStateTrait.php b/src/Context/CompilationStateTrait.php index f6156e80..b02614c7 100644 --- a/src/Context/CompilationStateTrait.php +++ b/src/Context/CompilationStateTrait.php @@ -106,7 +106,7 @@ trait CompilationStateTrait protected function addFunction(string $name, FunctionDef $functionDef): void { - $this->symbols->putFunction($this->escapeFunction($name), $functionDef); + $this->symbols->putFunction($this->escapeNativeFunctionKey($name), $functionDef); } /** @@ -114,12 +114,32 @@ trait CompilationStateTrait */ protected function hasFunction(string $name): bool { - return $this->symbols->hasFunction($this->escapeFunction($name)); + return $this->symbols->hasFunction($this->escapeNativeFunctionKey($name)); } protected function getFunction(string $name): FunctionDef { - return $this->symbols->function($this->escapeFunction($name)); + return $this->symbols->function($this->escapeNativeFunctionKey($name)); + } + + /** Normalize PHP name segments without lowercasing the reserved ABI marker. */ + private function escapeNativeFunctionKey(string $name): string + { + $placeholder = "\x00"; + $leadingPlaceholder = "\x01"; + $name = str_replace( + self::NAMESPACE_SEPARATOR . self::NAMESPACE_END_MARKER . self::NAMESPACE_SEPARATOR, + self::NAMESPACE_SEPARATOR . $placeholder . self::NAMESPACE_SEPARATOR, + $name, + ); + if (str_starts_with($name, self::NAMESPACE_END_MARKER . self::NAMESPACE_SEPARATOR)) { + $name = $leadingPlaceholder . substr($name, strlen(self::NAMESPACE_END_MARKER)); + } + return str_replace( + [$placeholder, $leadingPlaceholder], + [self::NAMESPACE_END_MARKER, self::NAMESPACE_END_MARKER], + $this->escapeFunction($name), + ); } protected function addClass(string $name, ClassDef $classDef): void diff --git a/src/Parser/UniversalMethodCall.php b/src/Parser/UniversalMethodCall.php index 967c5886..11c7c3f2 100644 --- a/src/Parser/UniversalMethodCall.php +++ b/src/Parser/UniversalMethodCall.php @@ -392,7 +392,7 @@ trait UniversalMethodCall } $registry[$targetKey][$methodKey] = [ 'handler' => 'provider_extension', - 'fn' => $this->getNativeName($method->name, $provider->namespace, $provider->name), + 'fn' => $this->getNativeClassMethodName($method->name, $provider->namespace, $provider->name), 'class' => $provider->getNamespacedName(false), 'return_type' => $function->returnType, 'min_args' => max(0, $function->argCountRequired - 1), diff --git a/src/Translator.php b/src/Translator.php index 861279a9..b1f1c81b 100644 --- a/src/Translator.php +++ b/src/Translator.php @@ -2471,7 +2471,7 @@ CODE; protected function getNativeMethodName(ClassDef $classDef, MethodDef $methodDef): string { - return $this->getNativeName($methodDef->name, $classDef->namespace, $classDef->name); + return $this->getNativeClassMethodName($methodDef->name, $classDef->namespace, $classDef->name); } protected function parseDeclare(mixed $v): void @@ -4543,8 +4543,8 @@ CODE; $this->checkTraitMethodOverrideCompatibility($classDef, $methodDef, $classMethodName); $classDef->addMethod($methodDef); - $traitMethodNativeName = $this->getNativeName($traitMethodName, $traitDef->namespace, $traitDef->name); - $classMethodNativeName = $this->getNativeName($classMethodName, $classDef->namespace, $classDef->name); + $traitMethodNativeName = $this->getNativeClassMethodName($traitMethodName, $traitDef->namespace, $traitDef->name); + $classMethodNativeName = $this->getNativeClassMethodName($classMethodName, $classDef->namespace, $classDef->name); $argList = ['this_']; if ($methodDef->parentMethodCalls) { // Bind parent:: to the class that actually composes the trait. This @@ -4780,7 +4780,7 @@ CODE; { $name = $this->escapeZendFnName($functionDef->getNamespacedName()); $cppCode = 'ZEND_FUNCTION(' . $name . '){' . PHP_EOL; - $fn = self::PREFIX . $this->getNativeName($functionDef->name, $functionDef->namespace); + $fn = self::PREFIX . $this->getNativeFunctionName($functionDef->name, $functionDef->namespace); $cppCode .= $this->genWrapperFunctionArgs($fn, $functionDef, $functionDef->getNamespacedName()); return $cppCode; @@ -4792,9 +4792,9 @@ CODE; $fullName = ltrim($fullName, '\\'); $separator = strrpos($fullName, '\\'); if ($separator === false) { - return self::PREFIX . $this->getNativeName($fullName); + return self::PREFIX . $this->getNativeFunctionName($fullName); } - return self::PREFIX . $this->getNativeName( + return self::PREFIX . $this->getNativeFunctionName( substr($fullName, $separator + 1), substr($fullName, 0, $separator), );