From 8b0c3d2b7243f0912a5c3b4eae964aedda9a8294 Mon Sep 17 00:00:00 2001 From: tianfenghan Date: Tue, 13 Jan 2026 16:52:31 +0800 Subject: [PATCH] =?UTF-8?q?refactor(php):=20=E9=87=8D=E6=9E=84PHP=E7=BC=96?= =?UTF-8?q?=E8=AF=91=E5=99=A8=E5=9F=BA=E7=A1=80=E5=8A=9F=E8=83=BD=E5=B9=B6?= =?UTF-8?q?=E4=BC=98=E5=8C=96=E4=BB=A3=E7=A0=81=E7=BB=93=E6=9E=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 移除不支持函数列表的硬编码定义 - 添加对不支持函数调用的运行时检查和错误报告 - 简化静态属性和类常量解析中的self关键字处理逻辑 - 修复类方法调用中self关键字的替换逻辑 - 将扩展模块入口声明从static改为全局作用域 - 重写gen_stub.php脚本的主函数结构和命令行参数处理 - 添加类信息和函数信息的独立生成选项 - 优化桩文件生成时的跳过提示输出 - 重构翻译器中的类解析方法实现 - 更新不支持函数列表包含更多动态函数 --- bin/gen_stub.php | 516 ++++++++++++++++++---------------- src/Php/CompilerBase.php | 22 +- src/Php/Translator.php | 34 ++- src/cpp/main.cc | 10 +- src/template/extension.cc.php | 2 +- 5 files changed, 311 insertions(+), 273 deletions(-) mode change 100644 => 100755 bin/gen_stub.php diff --git a/bin/gen_stub.php b/bin/gen_stub.php old mode 100644 new mode 100755 index 4c85073c..efdbc6ce --- a/bin/gen_stub.php +++ b/bin/gen_stub.php @@ -16,14 +16,6 @@ use PhpParser\Node\Stmt\Trait_; use PhpParser\PrettyPrinter\Standard; use PhpParser\PrettyPrinterAbstract; -error_reporting(E_ALL); -ini_set("precision", "-1"); -require __DIR__ . '/bootstrap.php'; - -$translator = new PhpAot\Php\Translator(ROOT_PATH); -$translator->setIndent("\t"); -$translator->setIndentLevel(1); - const PHP_70_VERSION_ID = 70000; const PHP_80_VERSION_ID = 80000; const PHP_81_VERSION_ID = 80100; @@ -94,6 +86,7 @@ function processStubFile(string $stubFile, Context $context, bool $includeOnly = $oldStubHash = extractStubHash($arginfoFile); if ($stubHash === $oldStubHash && !$context->forceParse) { /* Stub file did not change, do not regenerate. */ + echo "Skipping $stubFile, stub hash unchanged\n"; return null; } } @@ -4238,10 +4231,15 @@ class FileInfo { /** * @return iterable */ - public function getAllFuncInfos(): iterable { - yield from $this->funcInfos; - foreach ($this->classInfos as $classInfo) { - yield from $classInfo->funcInfos; + public function getAllFuncInfos(): iterable + { + global $genClass; + if ($genClass) { + foreach ($this->classInfos as $classInfo) { + yield from $classInfo->funcInfos; + } + } else { + yield from $this->funcInfos; } } @@ -5171,6 +5169,8 @@ function generateArgInfoCode( $generatedFuncInfos = []; + global $genClass; + $argInfoCode = generateCodeWithConditions( $fileInfo->getAllFuncInfos(), "\n", static function (FuncInfo $funcInfo) use (&$generatedFuncInfos, $fileInfo) { @@ -5221,14 +5221,16 @@ function generateArgInfoCode( $code .= generateFunctionEntries(null, $fileInfo->funcInfos); - foreach ($fileInfo->classInfos as $classInfo) { - $code .= generateFunctionEntries($classInfo->name, $classInfo->funcInfos, $classInfo->cond); + if ($genClass) { + foreach ($fileInfo->classInfos as $classInfo) { + $code .= generateFunctionEntries($classInfo->name, $classInfo->funcInfos, $classInfo->cond); + } } } $php80MinimumCompatibility = $fileInfo->getMinimumPhpVersionIdCompatibility() === null || $fileInfo->getMinimumPhpVersionIdCompatibility() >= PHP_80_VERSION_ID; - if ($fileInfo->generateClassEntries) { + if ($genClass and $fileInfo->generateClassEntries) { $declaredStrings = []; $attributeInitializationCode = generateFunctionAttributeInitialization($fileInfo->funcInfos, $allConstInfos, $fileInfo->getMinimumPhpVersionIdCompatibility(), null, $declaredStrings); $attributeInitializationCode .= generateGlobalConstantAttributeInitialization($fileInfo->constInfos, $allConstInfos, $fileInfo->getMinimumPhpVersionIdCompatibility(), null, $declaredStrings); @@ -6097,306 +6099,330 @@ function initPhpParser() { $isInitialized = true; } -$optind = null; -$options = getopt( - "fh", - [ - "force-regeneration", "parameter-stats", "help", "verify", "verify-manual", "replace-predefined-constants", - "generate-classsynopses", "replace-classsynopses", "generate-methodsynopses", "replace-methodsynopses", - "generate-optimizer-info", - ], - $optind -); - -$context = new Context; -$printParameterStats = isset($options["parameter-stats"]); -$verify = isset($options["verify"]); -$verifyManual = isset($options["verify-manual"]); -$replacePredefinedConstants = isset($options["replace-predefined-constants"]); -$generateClassSynopses = isset($options["generate-classsynopses"]); -$replaceClassSynopses = isset($options["replace-classsynopses"]); -$generateMethodSynopses = isset($options["generate-methodsynopses"]); -$replaceMethodSynopses = isset($options["replace-methodsynopses"]); -$generateOptimizerInfo = isset($options["generate-optimizer-info"]); -$context->forceRegeneration = isset($options["f"]) || isset($options["force-regeneration"]); -$context->forceParse = $context->forceRegeneration || $printParameterStats || $verify || $verifyManual || $replacePredefinedConstants || $generateClassSynopses || $generateOptimizerInfo || $replaceClassSynopses || $generateMethodSynopses || $replaceMethodSynopses; - -if (isset($options["h"]) || isset($options["help"])) { - die("\nUsage: gen_stub.php [ -f | --force-regeneration ] [ --replace-predefined-constants ] [ --generate-classsynopses ] [ --replace-classsynopses ] [ --generate-methodsynopses ] [ --replace-methodsynopses ] [ --parameter-stats ] [ --verify ] [ --verify-manual ] [ --generate-optimizer-info ] [ -h | --help ] [ name.stub.php | directory ] [ directory ]\n\n"); -} +function main() +{ + global $argv, $argc, $genClass, $translator; -$locations = array_slice($argv, $optind); -$locationCount = count($locations); -if ($replacePredefinedConstants && $locationCount < 2) { - die("At least one source stub path and a target manual directory has to be provided:\n./build/gen_stub.php --replace-predefined-constants ./ ../doc-en/\n"); -} -if ($replaceClassSynopses && $locationCount < 2) { - die("At least one source stub path and a target manual directory has to be provided:\n./build/gen_stub.php --replace-classsynopses ./ ../doc-en/\n"); -} -if ($generateMethodSynopses && $locationCount < 2) { - die("At least one source stub path and a target manual directory has to be provided:\n./build/gen_stub.php --generate-methodsynopses ./ ../doc-en/\n"); -} -if ($replaceMethodSynopses && $locationCount < 2) { - die("At least one source stub path and a target manual directory has to be provided:\n./build/gen_stub.php --replace-methodsynopses ./ ../doc-en/\n"); -} -if ($verifyManual && $locationCount < 2) { - die("At least one source stub path and a target manual directory has to be provided:\n./build/gen_stub.php --verify-manual ./ ../doc-en/\n"); -} -$manualTarget = null; -if ($replacePredefinedConstants || $replaceClassSynopses || $generateMethodSynopses || $replaceMethodSynopses || $verifyManual) { - $manualTarget = array_pop($locations); -} -if ($locations === []) { - $locations = ['.']; -} + error_reporting(E_ALL); + ini_set("precision", "-1"); + require __DIR__ . '/bootstrap.php'; -$fileInfos = []; -foreach (array_unique($locations) as $location) { - if (is_file($location)) { - // Generate single file. - $fileInfo = processStubFile($location, $context); - if ($fileInfo) { - $fileInfos[] = $fileInfo; - } - } else if (is_dir($location)) { - array_push($fileInfos, ...processDirectory($location, $context)); - } else { - echo "$location is neither a file nor a directory.\n"; - exit(1); - } -} + $translator = new PhpAot\Php\Translator(ROOT_PATH); + $translator->setIndent("\t"); + $translator->setIndentLevel(1); -if ($printParameterStats) { - $parameterStats = []; + $opt_index = 0; + $options = getopt( + "fh", + [ + "force-regeneration", "parameter-stats", "help", "verify", "verify-manual", "replace-predefined-constants", + "generate-classsynopses", "replace-classsynopses", "generate-methodsynopses", "replace-methodsynopses", + "generate-optimizer-info", "gen-class-info", "gen-func-info", + ], + $opt_index + ); - foreach ($fileInfos as $fileInfo) { - foreach ($fileInfo->getAllFuncInfos() as $funcInfo) { - foreach ($funcInfo->args as $argInfo) { - if (!isset($parameterStats[$argInfo->name])) { - $parameterStats[$argInfo->name] = 0; - } - $parameterStats[$argInfo->name]++; - } - } + $context = new Context; + $printParameterStats = isset($options["parameter-stats"]); + $verify = isset($options["verify"]); + $verifyManual = isset($options["verify-manual"]); + $replacePredefinedConstants = isset($options["replace-predefined-constants"]); + $generateClassSynopses = isset($options["generate-classsynopses"]); + $replaceClassSynopses = isset($options["replace-classsynopses"]); + $generateMethodSynopses = isset($options["generate-methodsynopses"]); + $replaceMethodSynopses = isset($options["replace-methodsynopses"]); + $generateOptimizerInfo = isset($options["generate-optimizer-info"]); + + if (isset($options["gen-class-info"])) { + $genClass = true; + } elseif (isset($options["gen-func-info"])) { + $genClass = false; + } else { + die("Please specify whether to generate class or function info\n"); } - arsort($parameterStats); - echo json_encode($parameterStats, JSON_PRETTY_PRINT), "\n"; -} - -/** @var array $classMap */ -$classMap = []; -/** @var array $funcMap */ -$funcMap = []; -/** @var array $aliasMap */ -$aliasMap = []; + $context->forceRegeneration = isset($options["f"]) || isset($options["force-regeneration"]); + $context->forceParse = $context->forceRegeneration || $printParameterStats || $verify || $verifyManual || $replacePredefinedConstants || $generateClassSynopses || $generateOptimizerInfo || $replaceClassSynopses || $generateMethodSynopses || $replaceMethodSynopses; -/** @var array $undocumentedConstMap */ -$undocumentedConstMap = []; -/** @var array $undocumentedClassMap */ -$undocumentedClassMap = []; -/** @var array $undocumentedFuncMap */ -$undocumentedFuncMap = []; -/** @var array $methodSynopsisWarnings */ -$methodSynopsisWarnings = []; + if (isset($options["h"]) || isset($options["help"])) { + die("\nUsage: gen_stub.php [ -f | --force-regeneration ] [ --replace-predefined-constants ] [ --generate-classsynopses ] [ --replace-classsynopses ] [ --generate-methodsynopses ] [ --replace-methodsynopses ] [ --parameter-stats ] [ --verify ] [ --verify-manual ] [ --generate-optimizer-info ] [ -h | --help ] [ name.stub.php | directory ] [ directory ]\n\n"); + } -foreach ($fileInfos as $fileInfo) { - foreach ($fileInfo->getAllFuncInfos() as $funcInfo) { - $funcMap[$funcInfo->name->__toString()] = $funcInfo; + $locations = array_slice($argv, $opt_index); + $locationCount = count($locations); + if ($replacePredefinedConstants && $locationCount < 2) { + die("At least one source stub path and a target manual directory has to be provided:\n./build/gen_stub.php --replace-predefined-constants ./ ../doc-en/\n"); + } + if ($replaceClassSynopses && $locationCount < 2) { + die("At least one source stub path and a target manual directory has to be provided:\n./build/gen_stub.php --replace-classsynopses ./ ../doc-en/\n"); + } + if ($generateMethodSynopses && $locationCount < 2) { + die("At least one source stub path and a target manual directory has to be provided:\n./build/gen_stub.php --generate-methodsynopses ./ ../doc-en/\n"); + } + if ($replaceMethodSynopses && $locationCount < 2) { + die("At least one source stub path and a target manual directory has to be provided:\n./build/gen_stub.php --replace-methodsynopses ./ ../doc-en/\n"); + } + if ($verifyManual && $locationCount < 2) { + die("At least one source stub path and a target manual directory has to be provided:\n./build/gen_stub.php --verify-manual ./ ../doc-en/\n"); + } + $manualTarget = null; + if ($replacePredefinedConstants || $replaceClassSynopses || $generateMethodSynopses || $replaceMethodSynopses || $verifyManual) { + $manualTarget = array_pop($locations); + } + if ($locations === []) { + $locations = ['.']; + } - // TODO: Don't use aliasMap for methodsynopsis? - if ($funcInfo->aliasType === "alias") { - $aliasMap[$funcInfo->alias->__toString()] = $funcInfo; + $fileInfos = []; + foreach (array_unique($locations) as $location) { + if (is_file($location)) { + // Generate single file. + $fileInfo = processStubFile($location, $context); + if ($fileInfo) { + $fileInfos[] = $fileInfo; + } + } else if (is_dir($location)) { + array_push($fileInfos, ...processDirectory($location, $context)); + } else { + echo "$location is neither a file nor a directory.\n"; + exit(1); } } - foreach ($fileInfo->classInfos as $classInfo) { - $classMap[$classInfo->name->__toString()] = $classInfo; + if ($printParameterStats) { + $parameterStats = []; - if ($classInfo->alias !== null) { - $classMap[$classInfo->alias] = $classInfo; + foreach ($fileInfos as $fileInfo) { + foreach ($fileInfo->getAllFuncInfos() as $funcInfo) { + foreach ($funcInfo->args as $argInfo) { + if (!isset($parameterStats[$argInfo->name])) { + $parameterStats[$argInfo->name] = 0; + } + $parameterStats[$argInfo->name]++; + } + } } + + arsort($parameterStats); + echo json_encode($parameterStats, JSON_PRETTY_PRINT), "\n"; } -} -if ($verify) { - $errors = []; + /** @var array $classMap */ + $classMap = []; + /** @var array $funcMap */ + $funcMap = []; + /** @var array $aliasMap */ + $aliasMap = []; - foreach ($funcMap as $aliasFunc) { - if (!$aliasFunc->alias || $aliasFunc->aliasType !== "alias") { - continue; - } + /** @var array $undocumentedConstMap */ + $undocumentedConstMap = []; + /** @var array $undocumentedClassMap */ + $undocumentedClassMap = []; + /** @var array $undocumentedFuncMap */ + $undocumentedFuncMap = []; + /** @var array $methodSynopsisWarnings */ + $methodSynopsisWarnings = []; - if (!isset($funcMap[$aliasFunc->alias->__toString()])) { - $errors[] = "Aliased function {$aliasFunc->alias}() cannot be found"; - continue; + foreach ($fileInfos as $fileInfo) { + foreach ($fileInfo->getAllFuncInfos() as $funcInfo) { + $funcMap[$funcInfo->name->__toString()] = $funcInfo; + + // TODO: Don't use aliasMap for methodsynopsis? + if ($funcInfo->aliasType === "alias") { + $aliasMap[$funcInfo->alias->__toString()] = $funcInfo; + } } - if (!$aliasFunc->verify) { - continue; + foreach ($fileInfo->classInfos as $classInfo) { + $classMap[$classInfo->name->__toString()] = $classInfo; + + if ($classInfo->alias !== null) { + $classMap[$classInfo->alias] = $classInfo; + } } + } - $aliasedFunc = $funcMap[$aliasFunc->alias->__toString()]; - $aliasedArgs = $aliasedFunc->args; - $aliasArgs = $aliasFunc->args; + if ($verify) { + $errors = []; - if ($aliasFunc->isInstanceMethod() !== $aliasedFunc->isInstanceMethod()) { - if ($aliasFunc->isInstanceMethod()) { - $aliasedArgs = array_slice($aliasedArgs, 1); + foreach ($funcMap as $aliasFunc) { + if (!$aliasFunc->alias || $aliasFunc->aliasType !== "alias") { + continue; } - if ($aliasedFunc->isInstanceMethod()) { - $aliasArgs = array_slice($aliasArgs, 1); + if (!isset($funcMap[$aliasFunc->alias->__toString()])) { + $errors[] = "Aliased function {$aliasFunc->alias}() cannot be found"; + continue; } - } - array_map( - function(?ArgInfo $aliasArg, ?ArgInfo $aliasedArg) use ($aliasFunc, $aliasedFunc, &$errors) { - if ($aliasArg === null) { - assert($aliasedArg !== null); - $errors[] = "{$aliasFunc->name}(): Argument \$$aliasedArg->name of aliased function {$aliasedFunc->name}() is missing"; - return null; - } + if (!$aliasFunc->verify) { + continue; + } - if ($aliasedArg === null) { - $errors[] = "{$aliasedFunc->name}(): Argument \$$aliasArg->name of alias function {$aliasFunc->name}() is missing"; - return null; - } + $aliasedFunc = $funcMap[$aliasFunc->alias->__toString()]; + $aliasedArgs = $aliasedFunc->args; + $aliasArgs = $aliasFunc->args; - if ($aliasArg->name !== $aliasedArg->name) { - $errors[] = "{$aliasFunc->name}(): Argument \$$aliasArg->name and argument \$$aliasedArg->name of aliased function {$aliasedFunc->name}() must have the same name"; - return null; + if ($aliasFunc->isInstanceMethod() !== $aliasedFunc->isInstanceMethod()) { + if ($aliasFunc->isInstanceMethod()) { + $aliasedArgs = array_slice($aliasedArgs, 1); } - if ($aliasArg->type != $aliasedArg->type) { - $errors[] = "{$aliasFunc->name}(): Argument \$$aliasArg->name and argument \$$aliasedArg->name of aliased function {$aliasedFunc->name}() must have the same type"; + if ($aliasedFunc->isInstanceMethod()) { + $aliasArgs = array_slice($aliasArgs, 1); } + } - if ($aliasArg->defaultValue !== $aliasedArg->defaultValue) { - $errors[] = "{$aliasFunc->name}(): Argument \$$aliasArg->name and argument \$$aliasedArg->name of aliased function {$aliasedFunc->name}() must have the same default value"; - } - }, - $aliasArgs, $aliasedArgs - ); + array_map( + function(?ArgInfo $aliasArg, ?ArgInfo $aliasedArg) use ($aliasFunc, $aliasedFunc, &$errors) { + if ($aliasArg === null) { + assert($aliasedArg !== null); + $errors[] = "{$aliasFunc->name}(): Argument \$$aliasedArg->name of aliased function {$aliasedFunc->name}() is missing"; + return null; + } + + if ($aliasedArg === null) { + $errors[] = "{$aliasedFunc->name}(): Argument \$$aliasArg->name of alias function {$aliasFunc->name}() is missing"; + return null; + } - $aliasedReturn = $aliasedFunc->return; - $aliasReturn = $aliasFunc->return; + if ($aliasArg->name !== $aliasedArg->name) { + $errors[] = "{$aliasFunc->name}(): Argument \$$aliasArg->name and argument \$$aliasedArg->name of aliased function {$aliasedFunc->name}() must have the same name"; + return null; + } + + if ($aliasArg->type != $aliasedArg->type) { + $errors[] = "{$aliasFunc->name}(): Argument \$$aliasArg->name and argument \$$aliasedArg->name of aliased function {$aliasedFunc->name}() must have the same type"; + } + + if ($aliasArg->defaultValue !== $aliasedArg->defaultValue) { + $errors[] = "{$aliasFunc->name}(): Argument \$$aliasArg->name and argument \$$aliasedArg->name of aliased function {$aliasedFunc->name}() must have the same default value"; + } + }, + $aliasArgs, $aliasedArgs + ); + + $aliasedReturn = $aliasedFunc->return; + $aliasReturn = $aliasFunc->return; + + if (!$aliasedFunc->name->isConstructor() && !$aliasFunc->name->isConstructor()) { + $aliasedReturnType = $aliasedReturn->type ?? $aliasedReturn->phpDocType; + $aliasReturnType = $aliasReturn->type ?? $aliasReturn->phpDocType; + if ($aliasReturnType != $aliasedReturnType) { + $errors[] = "{$aliasFunc->name}() and {$aliasedFunc->name}() must have the same return type"; + } + } - if (!$aliasedFunc->name->isConstructor() && !$aliasFunc->name->isConstructor()) { - $aliasedReturnType = $aliasedReturn->type ?? $aliasedReturn->phpDocType; - $aliasReturnType = $aliasReturn->type ?? $aliasReturn->phpDocType; - if ($aliasReturnType != $aliasedReturnType) { - $errors[] = "{$aliasFunc->name}() and {$aliasedFunc->name}() must have the same return type"; + $aliasedPhpDocReturnType = $aliasedReturn->phpDocType; + $aliasPhpDocReturnType = $aliasReturn->phpDocType; + if ($aliasedPhpDocReturnType != $aliasPhpDocReturnType && $aliasedPhpDocReturnType != $aliasReturn->type && $aliasPhpDocReturnType != $aliasedReturn->type) { + $errors[] = "{$aliasFunc->name}() and {$aliasedFunc->name}() must have the same PHPDoc return type"; } } - $aliasedPhpDocReturnType = $aliasedReturn->phpDocType; - $aliasPhpDocReturnType = $aliasReturn->phpDocType; - if ($aliasedPhpDocReturnType != $aliasPhpDocReturnType && $aliasedPhpDocReturnType != $aliasReturn->type && $aliasPhpDocReturnType != $aliasedReturn->type) { - $errors[] = "{$aliasFunc->name}() and {$aliasedFunc->name}() must have the same PHPDoc return type"; + echo implode("\n", $errors); + if (!empty($errors)) { + echo "\n"; + exit(1); } } - echo implode("\n", $errors); - if (!empty($errors)) { - echo "\n"; - exit(1); - } -} - -if ($replacePredefinedConstants || $verifyManual) { - $predefinedConstants = replacePredefinedConstants($manualTarget, $context->allConstInfos, $undocumentedConstMap); + if ($replacePredefinedConstants || $verifyManual) { + $predefinedConstants = replacePredefinedConstants($manualTarget, $context->allConstInfos, $undocumentedConstMap); - if ($replacePredefinedConstants) { - foreach ($predefinedConstants as $filename => $content) { - reportFilePutContents($filename, $content); + if ($replacePredefinedConstants) { + foreach ($predefinedConstants as $filename => $content) { + reportFilePutContents($filename, $content); + } } } -} -if ($generateClassSynopses) { - $classSynopsesDirectory = getcwd() . "/classsynopses"; + if ($generateClassSynopses) { + $classSynopsesDirectory = getcwd() . "/classsynopses"; - $classSynopses = generateClassSynopses($classMap, $context->allConstInfos); - if (!empty($classSynopses)) { - if (!file_exists($classSynopsesDirectory)) { - mkdir($classSynopsesDirectory); - } + $classSynopses = generateClassSynopses($classMap, $context->allConstInfos); + if (!empty($classSynopses)) { + if (!file_exists($classSynopsesDirectory)) { + mkdir($classSynopsesDirectory); + } - foreach ($classSynopses as $filename => $content) { - reportFilePutContents("$classSynopsesDirectory/$filename", $content); + foreach ($classSynopses as $filename => $content) { + reportFilePutContents("$classSynopsesDirectory/$filename", $content); + } } } -} -if ($replaceClassSynopses || $verifyManual) { - $classSynopses = replaceClassSynopses($manualTarget, $classMap, $context->allConstInfos, $undocumentedClassMap); + if ($replaceClassSynopses || $verifyManual) { + $classSynopses = replaceClassSynopses($manualTarget, $classMap, $context->allConstInfos, $undocumentedClassMap); - if ($replaceClassSynopses) { - foreach ($classSynopses as $filename => $content) { - reportFilePutContents($filename, $content); + if ($replaceClassSynopses) { + foreach ($classSynopses as $filename => $content) { + reportFilePutContents($filename, $content); + } } } -} -if ($generateMethodSynopses) { - $methodSynopses = generateMethodSynopses($funcMap); - if (!file_exists($manualTarget)) { - mkdir($manualTarget); - } + if ($generateMethodSynopses) { + $methodSynopses = generateMethodSynopses($funcMap); + if (!file_exists($manualTarget)) { + mkdir($manualTarget); + } - foreach ($methodSynopses as $filename => $content) { - $path = "$manualTarget/$filename"; + foreach ($methodSynopses as $filename => $content) { + $path = "$manualTarget/$filename"; - if (!file_exists($path)) { - if (!file_exists(dirname($path))) { - mkdir(dirname($path)); - } + if (!file_exists($path)) { + if (!file_exists(dirname($path))) { + mkdir(dirname($path)); + } - reportFilePutContents($path, $content); + reportFilePutContents($path, $content); + } } } -} -if ($replaceMethodSynopses || $verifyManual) { - $methodSynopses = replaceMethodSynopses($manualTarget, $funcMap, $verifyManual, $methodSynopsisWarnings, $undocumentedFuncMap); + if ($replaceMethodSynopses || $verifyManual) { + $methodSynopses = replaceMethodSynopses($manualTarget, $funcMap, $verifyManual, $methodSynopsisWarnings, $undocumentedFuncMap); - if ($replaceMethodSynopses) { - foreach ($methodSynopses as $filename => $content) { - reportFilePutContents($filename, $content); + if ($replaceMethodSynopses) { + foreach ($methodSynopses as $filename => $content) { + reportFilePutContents($filename, $content); + } } } -} -if ($generateOptimizerInfo) { - $filename = dirname(__FILE__, 2) . "/Zend/Optimizer/zend_func_infos.h"; - $optimizerInfo = generateOptimizerInfo($funcMap); + if ($generateOptimizerInfo) { + $filename = dirname(__FILE__, 2) . "/Zend/Optimizer/zend_func_infos.h"; + $optimizerInfo = generateOptimizerInfo($funcMap); - reportFilePutContents($filename, $optimizerInfo); -} + reportFilePutContents($filename, $optimizerInfo); + } -if ($verifyManual) { - foreach ($undocumentedConstMap as $constName => $info) { - if ($info->name instanceof ClassConstName || $info->isUndocumentable) { - continue; - } + if ($verifyManual) { + foreach ($undocumentedConstMap as $constName => $info) { + if ($info->name instanceof ClassConstName || $info->isUndocumentable) { + continue; + } - echo "Warning: Missing predefined constant for $constName\n"; - } + echo "Warning: Missing predefined constant for $constName\n"; + } - foreach ($methodSynopsisWarnings as $warning) { - echo "Warning: $warning\n"; - } + foreach ($methodSynopsisWarnings as $warning) { + echo "Warning: $warning\n"; + } - foreach ($undocumentedClassMap as $className => $info) { - if (!$info->isUndocumentable) { - echo "Warning: Missing class synopsis for $className\n"; + foreach ($undocumentedClassMap as $className => $info) { + if (!$info->isUndocumentable) { + echo "Warning: Missing class synopsis for $className\n"; + } } - } - foreach ($undocumentedFuncMap as $functionName => $info) { - if (!$info->isUndocumentable) { - echo "Warning: Missing method synopsis for $functionName()\n"; + foreach ($undocumentedFuncMap as $functionName => $info) { + if (!$info->isUndocumentable) { + echo "Warning: Missing method synopsis for $functionName()\n"; + } } } -} \ No newline at end of file +} + +main(); \ No newline at end of file diff --git a/src/Php/CompilerBase.php b/src/Php/CompilerBase.php index 2b556303..577e640a 100644 --- a/src/Php/CompilerBase.php +++ b/src/Php/CompilerBase.php @@ -53,13 +53,7 @@ class CompilerBase extends \PhpAot\Core\Translator 'float' => self::TYPE_FLOAT, 'bool' => self::TYPE_BOOL, ]; - protected array $reservedNames; - - protected array $unsupportedFunctions = [ - 'compact', - 'extract' - ]; protected array $globalHeaders = [ 'phpx.h', 'phpx_helper.h', @@ -135,7 +129,6 @@ class CompilerBase extends \PhpAot\Core\Translator $climate = new CLImate(); $this->climate = $climate; // $this->noLiteralStrings = $climate->arguments->get('no-literal-strings'); - $this->noLiteralStrings = true; } public function setPhpxDir($dir): void @@ -1295,6 +1288,9 @@ class CompilerBase extends \PhpAot\Core\Translator $name = ''; } elseif ($expr->name->getType() === 'Name') { $name = $this->parseIdentifier($expr->name); + if (in_array($name, $this->unsupportedFunctions)) { + $this->fatalError($expr, 'Unsupported function: `' . $name . '`'); + } $nativeFn = $this->findNativeFunction($name); if ($nativeFn) { return self::PREFIX . $nativeFn . '(' . $this->parseCallArgs($expr->args, $name) . ')'; @@ -2106,10 +2102,7 @@ class CompilerBase extends \PhpAot\Core\Translator } elseif ($var instanceof Node\Expr\ArrayDimFetch) { return $this->parseIdentifier($var->var) . ".offsetExists(" . $this->parseIdentifier($var->dim) . ')'; } elseif ($var instanceof Node\Expr\StaticPropertyFetch) { - $class = $this->parseIdentifier($var->class); - $prop = $var->name; - $class = $class === 'self' ? $this->class : $class; - return 'php::hasStaticProperty("' . $class . '", ' . $this->identifierToStr($prop) . ')'; + return 'php::hasStaticProperty(' . $this->identifierToStr($var->class) . ', ' . $this->identifierToStr($var->name) . ')'; } elseif ($var instanceof Node\Expr\PropertyFetch) { $object = $var->var; $prop = $var->name; @@ -2228,6 +2221,9 @@ class CompilerBase extends \PhpAot\Core\Translator } return $id; } else { + if ($id === 'self') { + $id = $this->class; + } return '"'. $id . '"'; } } @@ -2245,8 +2241,9 @@ class CompilerBase extends \PhpAot\Core\Translator $fn = 'php::concat({' . $this->identifierToStr($expr->class). ', "::", ' . $this->identifierToStr($expr->name) . '})'; } else { $class = $this->parseIdentifier($expr->class); + $class = $class === 'self' ? $this->class : $class; $method = $this->parseIdentifier($expr->name); - $fn = '"'. $class . '::' . $method . '"'; + $fn = '"' . $class . '::' . $method . '"'; } if (empty($expr->args)) { return 'php::call(' . $fn . ')'; @@ -2263,6 +2260,7 @@ class CompilerBase extends \PhpAot\Core\Translator protected function parseClassConstFetch(Node\Expr\ClassConstFetch $expr): string { $class = $this->parseIdentifier($expr->class); + $class = ($class === 'self' or $class === 'this_') ? $this->class : $class; $const = $this->parseIdentifier($expr->name); return 'php::constant("' . $class . '::' . $const . '")'; } diff --git a/src/Php/Translator.php b/src/Php/Translator.php index 0d6d9d03..3de04a79 100644 --- a/src/Php/Translator.php +++ b/src/Php/Translator.php @@ -9,6 +9,13 @@ use PhpParser\NodeTraverser; class Translator extends Preprocessor { protected bool $verbose = false; + protected array $unsupportedFunctions = [ + 'compact', + 'extract', + 'func_num_args', + 'func_get_arg', + 'func_get_args', + ]; public function __construct(string $rootPath) { @@ -358,21 +365,26 @@ class Translator extends Preprocessor return $code; } + protected function genClassStubFile(Node\Stmt\Class_ $class, string $file): void + { + $genStubCmd = PHP_BINARY. ' ' . $this->rootPath . '/bin/gen_stub.php --gen-class-info -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); + if (!str_starts_with($output, "Saved")) { + $this->fatalError($class, "failed to generate arginfo header file: `$headerFile`, output: $output"); + } + $this->localHeaders[] = $headerFile; + $this->stubFileIncluded = true; + } + protected function parseClass(Node\Stmt\Class_ $class): string { $this->class = $this->parseIdentifier($class->name); if (!$this->stubFileIncluded) { - $genStubCmd = PHP_BINARY. ' ' . $this->rootPath . '/bin/gen_stub.php -f ' . $this->file; - $output = shell_exec($genStubCmd); - $this->climate->info('generate stub file: ' . $this->file); - $this->climate->comment($genStubCmd); - $stubFilenameWithoutExtension = str_replace([".stub.php", '.php'], "", $this->file); - $headerFile = $this->getArgInfoHeaderFile($stubFilenameWithoutExtension, true); - if (!str_starts_with($output, "Saved")) { - $this->fatalError($class, "failed to generate arginfo header file: `$headerFile`, output: $output"); - } - $this->localHeaders[] = $headerFile; - $this->stubFileIncluded = true; + $this->genClassStubFile($class, $this->file); } $this->classDef = new ClassDef(); diff --git a/src/cpp/main.cc b/src/cpp/main.cc index f9aaa84d..e2111494 100644 --- a/src/cpp/main.cc +++ b/src/cpp/main.cc @@ -9,9 +9,7 @@ extern php::Var argv; extern void php_app_init(); extern void php_app_clean(); -BEGIN_EXTERN_C() -extern zend_module_entry *get_module(); -END_EXTERN_C() +extern zend_module_entry app_module_entry; static void throw_exception(zend_object *ex) { zend_bailout(); @@ -21,10 +19,14 @@ int main(int cpp_argc, char **cpp_argv) { php_embed_init(cpp_argc, cpp_argv); zend_throw_exception_hook = throw_exception; - if (zend_register_module_ex(get_module(), MODULE_TEMPORARY) == NULL) { + if (zend_register_module_ex(&app_module_entry, MODULE_TEMPORARY) == NULL) { zend_error(E_ERROR, "Failed to register module"); } + if (zend_startup_module_ex(&app_module_entry) == FAILURE) { + zend_error(E_ERROR, "Failed to startup module"); + } + int rc = 0; #if PPROF_ON ProfilerStart("myapp.prof"); diff --git a/src/template/extension.cc.php b/src/template/extension.cc.php index 3cf9ceb7..319d07d9 100644 --- a/src/template/extension.cc.php +++ b/src/template/extension.cc.php @@ -104,7 +104,7 @@ foreach ($this->nativeConstants as $name => $constant): } -static zend_module_entry app_module_entry = { +zend_module_entry app_module_entry = { STANDARD_MODULE_HEADER, "app", ext_functions,