diff --git a/bin/gen_stub.php b/bin/gen_stub.php index 5a902958..4f724d4a 100755 --- a/bin/gen_stub.php +++ b/bin/gen_stub.php @@ -80,7 +80,8 @@ function processStubFile(string $stubFile, Context $context, bool $includeOnly = if (!$includeOnly) { global $translator; $stubFilenameWithoutExtension = $translator->getArgInfoStubFilename($stubFile); - $arginfoFile = $translator->getArgInfoHeaderFile($stubFilenameWithoutExtension); + $arginfoFile = $context->objectFile; + var_dump($arginfoFile); $legacyFile = "{$stubFilenameWithoutExtension}_legacy_arginfo.h"; $stubCode = file_get_contents($stubFile); @@ -174,6 +175,7 @@ class Context { public array $allConstInfos = []; /** @var FileInfo[] */ public array $parsedFiles = []; + public string $objectFile = ''; } class ArrayType extends SimpleType { @@ -6171,7 +6173,7 @@ function main() $opt_index = 0; $options = getopt( - "fh", + "fho:", [ "force-regeneration", "parameter-stats", "help", "verify", "verify-manual", "replace-predefined-constants", "generate-classsynopses", "replace-classsynopses", "generate-methodsynopses", "replace-methodsynopses", @@ -6192,6 +6194,7 @@ function main() $generateOptimizerInfo = isset($options["generate-optimizer-info"]); $context->forceRegeneration = isset($options["f"]) || isset($options["force-regeneration"]); + $context->objectFile = $options["o"] ?? ''; $context->forceParse = $context->forceRegeneration || $printParameterStats || $verify || $verifyManual || $replacePredefinedConstants || $generateClassSynopses || $generateOptimizerInfo || $replaceClassSynopses || $generateMethodSynopses || $replaceMethodSynopses; if (isset($options["h"]) || isset($options["help"])) { diff --git a/src/Php/CompilerBase.php b/src/Php/CompilerBase.php index 190e1a1e..b678acd8 100644 --- a/src/Php/CompilerBase.php +++ b/src/Php/CompilerBase.php @@ -1512,7 +1512,6 @@ class CompilerBase extends \PhpAot\Core\Translator if ($this->isNativeFunction($name)) { return $this->nativeFunctions[$name]->returnType; } - return $this->detectFuncCallReturnType($name); case 'Expr_New': return self::TYPE_OBJECT; @@ -1945,28 +1944,35 @@ class CompilerBase extends \PhpAot\Core\Translator */ protected function findNativeFunction(string $fname): string|false { - $possibleFunctionNames = [$this->escapeName($fname)]; - if (isset($this->useAliases[$fname])) { - $possibleFunctionNames[] = $this->escapeName($this->escapeNamespace($this->useAliases[$fname])); - } - if ($this->namespace) { - $possibleFunctionNames[] = $this->escapeNamespace($this->namespace) . self::NAMESPACE_SEPARATOR . $fname; - } - if (isset($this->useFunctions[$fname])) { - $possibleFunctionNames[] = $this->escapeNamespace($this->useFunctions[$fname]) . self::NAMESPACE_SEPARATOR . $fname; - } - // 复杂命名空间规则,组合命名空间 - // 例子:use foo\bar; bar\fn(); - foreach ($this->useNamespaces as $use) { - $ns1 = explode('\\', $use); - $ns2 = explode('\\', $fname); - 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)); - break; + // 绝对命名空间的函数 + if ($fname[0] == '\\') { + $fname = ltrim($fname, '\\'); + $possibleFunctionNames = [$this->escapeName($fname)]; + } else { + $possibleFunctionNames = [$this->escapeName($fname)]; + if (isset($this->useAliases[$fname])) { + $possibleFunctionNames[] = $this->escapeName($this->escapeNamespace($this->useAliases[$fname])); + } + if ($this->namespace) { + $possibleFunctionNames[] = $this->escapeNamespace($this->namespace) . self::NAMESPACE_SEPARATOR . $fname; + } + if (isset($this->useFunctions[$fname])) { + $possibleFunctionNames[] = $this->escapeNamespace($this->useFunctions[$fname]) . self::NAMESPACE_SEPARATOR . $fname; + } + // 复杂命名空间规则,组合命名空间 + // 例子:use foo\bar; bar\fn(); + foreach ($this->useNamespaces as $use) { + $ns1 = explode('\\', $use); + $ns2 = explode('\\', $fname); + 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)); + break; + } } } + foreach ($possibleFunctionNames as $name) { // 在预处理阶段检测到函数声明,但是未定义,说明在当前文件,但是顺序错误 // 跳过,稍后再处理 @@ -1992,12 +1998,11 @@ class CompilerBase extends \PhpAot\Core\Translator protected function parseFuncCall(Node\Expr\FuncCall $expr, bool $silent = false): string { $call = ''; - $placeHolder = ''; if ($this->isVarExpr($expr->name)) { $fn = $this->parseIdentifier($expr->name); $placeHolder = $fn; $name = ''; - } elseif ($expr->name->getType() === 'Name') { + } elseif ($expr->name->getType() === 'Name' or $expr->name->getType() === 'Name_FullyQualified') { $name = $this->parseIdentifier($expr->name); if (in_array($name, $this->unsupportedFunctions)) { $this->fatalError($expr, 'Unsupported function: `' . $name . '`'); diff --git a/src/Php/Translator.php b/src/Php/Translator.php index d808d211..b72fda4a 100644 --- a/src/Php/Translator.php +++ b/src/Php/Translator.php @@ -27,6 +27,7 @@ class Translator extends Preprocessor { use MagicMethodDetector; protected string $targetName = 'app'; + protected array $sourceDirs = []; protected bool $verbose = false; protected array $phpSrcFiles = []; protected array $argInfoHeaderFiles = []; @@ -152,6 +153,7 @@ class Translator extends Preprocessor $list = $this->getFilesFromDir($path); $targetName = basename($path); $this->setTargetName($targetName); + $this->sourceDirs[] = $path; } else { $ext = pathinfo($path, PATHINFO_EXTENSION); if ($ext === 'yml') { @@ -160,6 +162,7 @@ class Translator extends Preprocessor $list = [$path]; $targetName = FileScanner::getFileName($path); $this->setTargetName($targetName); + $this->sourceDirs[] = dirname($path); } else { $this->error('Unsupported file type: ' . $path); } @@ -340,9 +343,16 @@ class Translator extends Preprocessor public function getArgInfoHeaderFile(string $stubFilenameWithoutExtension, bool $relative = false): string { - $basename = self::PREFIX . basename($stubFilenameWithoutExtension); - $basename = $this->escapeFileName($basename); - $absPath = $this->getIncludeDir() . "/{$basename}_arginfo.h"; + foreach ($this->sourceDirs as $srcDir) { + if (str_starts_with($stubFilenameWithoutExtension, $srcDir)) { + $filePath = ltrim($this->removeCommonPrefix($srcDir, $stubFilenameWithoutExtension), '/'); + break; + } + } + + $filename = self::PREFIX . str_replace('/', '_', $filePath); + $filename = $this->escapeFileName($filename); + $absPath = $this->getIncludeDir() . "/{$filename}_arginfo.h"; if ($relative) { return ltrim($this->removeCommonPrefix($this->getIncludeDir(), $absPath), '/'); } @@ -414,9 +424,11 @@ class Translator extends Preprocessor } if (is_file($realPath)) { $list[] = $realPath; + $this->sourceDirs[] = basename($realPath); } else { - $tmp = $this->getFilesFromDir($realPath); + $tmp = $this->getFilesFromDir($realPath); $list = array_merge($list, $tmp); + $this->sourceDirs[] = $realPath; } } } else { @@ -646,12 +658,14 @@ class Translator extends Preprocessor protected function genStubFile(string $file): void { - $genStubCmd = PHP_BINARY . ' ' . $this->rootPath . '/bin/gen_stub.php -f ' . $file; - $output = shell_exec($genStubCmd); - $this->climate->info('generate stub file: ' . $file); - $this->climate->comment($genStubCmd); $stubFilenameWithoutExtension = str_replace(['.stub.php', '.php'], '', $file); $headerFile = $this->getArgInfoHeaderFile($stubFilenameWithoutExtension, true); + + $genStubCmd = PHP_BINARY . ' ' . $this->rootPath . '/bin/gen_stub.php -f -o ' . $this->getIncludeDir() . '/' . $headerFile . ' ' . $file; + $output = shell_exec($genStubCmd); + $this->climate->info('generate stub file: ' . $file); + $this->climate->comment($genStubCmd); + if (!str_contains($output, 'Saved')) { $this->error("failed to generate arginfo header file: `{$headerFile}`, output: {$output}"); } diff --git a/tests/aot/fn-call-001.phpt b/tests/aot/fn-call-001.phpt new file mode 100644 index 00000000..3503c537 --- /dev/null +++ b/tests/aot/fn-call-001.phpt @@ -0,0 +1,16 @@ +--TEST-- +fn call 001 +--FILE-- + +--EXPECT-- +string(6) "\Event"