refactor(php): 重构编译器内部函数和类管理机制

- 移除 CompilerTest 单例模式,改为 create 方法创建实例
- 将 nativeFunctions 重命名为 functions 并统一使用 addFunction/hasFunction/getFunction 方法管理
- 将 nativeConstants 重命名为 constants 并统一使用相应方法管理
- 将 nativeClasses 相关方法重构为 getClass/hasClass 等统一命名方式
- 添加 ConstantDef 实体类用于常量定义管理
- 优化函数查找逻辑,使用 escapeFunction 进行名称转换
- 重构预处理器中的函数注册逻辑
- 更新模板文件中的变量引用以匹配新的属性命名
- 添加重复函数、类的测试用例并移除旧的错误测试文件
pull/1/head
韩天峰 5 months ago
parent 76c1104234
commit e508b6de4f
  1. 5
      phpunit/bootstrap.php
  2. 0
      phpunit/code/duplicate_01.php
  3. 14
      phpunit/code/duplicate_02.php
  4. 12
      phpunit/code/duplicate_03.php
  5. 38
      phpunit/src/DuplicateTest.php
  6. 23
      phpunit/src/ErrorTest.php
  7. 158
      src/Php/CompilerBase.php
  8. 12
      src/Php/CompilerTest.php
  9. 2
      src/Php/Preprocessor.php
  10. 14
      src/Php/Translator.php
  11. 9
      src/template/extension.cc.php

@ -1,9 +1,4 @@
<?php
use PhpAot\Php\CompilerTest;
require __DIR__ . '/../bin/bootstrap.php';
CompilerTest::getInstance(ROOT_PATH);

@ -0,0 +1,14 @@
<?php
function foo1()
{
}
function foo1()
{
}
function main()
{
}

@ -0,0 +1,12 @@
<?php
class Foo1
{
}
class Foo1
{
}
function main()
{
}

@ -0,0 +1,38 @@
<?php
use PhpAot\Php\CompilerTest;
use PhpAot\Php\Exception\TestError;
use PHPUnit\Framework\TestCase;
class DuplicateTest extends TestCase
{
private function exec(string $expected, string $file): void
{
try {
$compiler = CompilerTest::create(ROOT_PATH);
$testFile = __DIR__ . '/../code/' . $file;
$compiler->addFiles([$testFile]);
$compiler->convert($testFile);
} catch (TestError $exception) {
$this->assertStringContainsString($expected, $exception->getMessage());
return;
}
$this->fail();
}
public function testStaticVar()
{
$this->exec('Duplicate static variable', 'duplicate_01.php');
}
public function testFunction()
{
$this->exec('Duplicate function', 'duplicate_02.php');
}
public function testClass()
{
$this->exec('Duplicate class', 'duplicate_03.php');
}
}

@ -1,23 +0,0 @@
<?php
use PhpAot\Php\CompilerTest;
use PhpAot\Php\Exception\TestError;
use PHPUnit\Framework\TestCase;
class ErrorTest extends TestCase
{
public function testDuplicateStaticVar()
{
try {
$o = CompilerTest::getInstance();
$file = __DIR__ . '/../code/duplicate.php';
$o->addFiles([$file]);
$o->convert($file);
} catch (TestError $exception) {
$this->assertStringContainsString('Duplicate static variable', $exception->getMessage());
return;
}
$this->fail();
}
}

@ -11,6 +11,7 @@ namespace PhpAot\Php;
use League\CLImate\CLImate;
use PhpAot\Php\Context\FunctionContext;
use PhpAot\Php\Entity\ClassDef;
use PhpAot\Php\Entity\ConstantDef;
use PhpAot\Php\Entity\FunctionDef;
use PhpAot\Php\Entity\InterfaceDef;
use PhpAot\Php\Entity\MethodDef;
@ -136,15 +137,7 @@ class CompilerBase extends \PhpAot\Core\Translator
'php_aot_helper.h',
];
protected array $localHeaders = [];
/**
* 存储所有函数、类方法的定义,key 是 native name,命名空间需要转为 `_`,并且必须为小写
* @var array<string, FunctionDef>
*/
protected array $nativeFunctions = [];
protected array $internalFunctions = [];
protected array $nativeConstants = [];
/**
* 存储所有函数、类方法的声明,key 是 符号名称,Value 是函数、类方法所在的文件名称
* @var array<string, string>
@ -185,28 +178,28 @@ class CompilerBase extends \PhpAot\Core\Translator
protected string $class = '';
protected string $parentClass = '';
protected string $interface = '';
/**
* key 类名,包含命名空间
* @var array<string, ClassDef>
*/
protected array $classes = [];
/**
* @var array<string, InterfaceDef>
*/
protected array $interfaces = [];
/**
* 存储所有函数、类方法的定义,key 是 native name,命名空间需要转为 `_`,并且必须为小写
* @var array<string, FunctionDef>
*/
protected array $functions = [];
/**
* key 类名,包含命名空间
* @var array<string, ClassDef>
*/
protected array $classes = [];
/**
* @var array<string, ConstantDef>
*/
protected array $constants = [];
/**
* @var array<string, ClassDef>
*/
protected array $classesDefineInFile = [];
/**
* @var array<string, InterfaceDef>
*/
@ -837,10 +830,10 @@ class CompilerBase extends \PhpAot\Core\Translator
$this->resetFunction();
$this->function = $this->parseIdentifier($v->name);
$name = $this->getFunctionName($v);
if (!empty($this->nativeFunctions[$name])) {
$this->functionDef = $this->nativeFunctions[$name];
if ($this->hasFunction($name)) {
$this->functionDef = $this->getFunction($name);
} else {
$this->nativeFunctions[$name] = $this->parseFunctionDecl($v);
$this->addFunction($name, $this->parseFunctionDecl($v));
if (isset($this->redoAfterDeclare[$name])) {
unset($this->redoAfterDeclare[$name]);
$this->climate->cyan('Received redo request, retrying...');
@ -856,7 +849,6 @@ class CompilerBase extends \PhpAot\Core\Translator
$this->methodDef->functionDef = $this->functionDef;
}
} else {
$this->functions[$name] = $this->functionDef;
$this->functionDefineInFile[$name] = $this->functionDef;
}
@ -1568,10 +1560,10 @@ class CompilerBase extends \PhpAot\Core\Translator
protected function addStaticVar(string $name, string $type): void
{
$this->context->staticVars[$name] = $type;
if ($this->hasStaticVar($name)) {
$this->error('Duplicate static variable `$' . $name . '`');
}
$this->context->staticVars[$name] = $type;
$this->addGlobalVar($this->getStaticVarName($name), $type);
}
@ -1594,21 +1586,11 @@ class CompilerBase extends \PhpAot\Core\Translator
$this->globalVars[$name] = $type;
}
protected function addClass(string $name, ClassDef $classDef): void
{
$this->classes[$this->escapeClass($name)] = $classDef;
}
protected function addObject(string $name, string $class): void
{
$this->context->objects[$name] = $class;
}
protected function addFunction(string $name, FunctionDef $functionDef): void
{
$this->functions[$this->escapeFunction($name)] = $functionDef;
}
protected function hasVar(string $name): bool
{
return $this->hasLocalVar($name) || $this->hasGlobalVar($name) || $this->hasStaticVar($name);
@ -1619,39 +1601,45 @@ class CompilerBase extends \PhpAot\Core\Translator
return isset($this->context->localVars[$name]);
}
protected function addFunction(string $name, FunctionDef $functionDef): void
{
$this->functions[$this->escapeFunction($name)] = $functionDef;
}
/**
* @param string $name 必须传入带有完整命名空间的类名,将会自动转义为 native name
*/
protected function hasNativeClass(string $name): bool
protected function hasFunction(string $name): bool
{
return array_key_exists($this->escapeClass($name), $this->classes);
return array_key_exists($this->escapeFunction($name), $this->functions);
}
protected function getNativeClass(string $name): ClassDef
protected function getFunction(string $name): FunctionDef
{
return $this->classes[$this->escapeClass($name)];
return $this->functions[$this->escapeFunction($name)];
}
/**
* @param string $name 必须传入带有完整命名空间的类名,将会自动转义为 native name
*/
protected function hasNativeFunction(string $name): bool
protected function addClass(string $name, ClassDef $classDef): void
{
return array_key_exists($this->escapeFunction($name), $this->nativeFunctions);
$this->classes[$this->escapeClass($name)] = $classDef;
}
protected function getNativeFunction(string $name): FunctionDef
protected function getClass(string $name): ClassDef
{
return $this->nativeFunctions[$this->escapeFunction($name)];
return $this->classes[$this->escapeClass($name)];
}
protected function hasClass(string $name): bool
{
return array_key_exists($this->escapeClass($name), $this->classes);
}
protected function checkNativeFunction(string $name): void
protected function checkFunction(string $name): void
{
// 在预处理阶段检测到函数声明,但是未定义,说明在当前文件,但是顺序错误
// 跳过,稍后再处理
if (isset($this->symbolDeclInFile[$name])
and $this->symbolDeclInFile[$name] === $this->file
and !$this->hasNativeFunction($name)) {
and !$this->hasFunction($name)) {
$this->redoAfterDeclare[$name] = true;
throw new Skip();
}
@ -1670,11 +1658,11 @@ class CompilerBase extends \PhpAot\Core\Translator
protected function getNativeMethod(CallLike $expr, string $class, string $method, bool $checkArgs = true): string|false
{
if (!$this->hasNativeClass($class)) {
if (!$this->hasClass($class)) {
return false;
}
$classDef = $this->getClassDef($class);
$classDef = $this->getClass($class);
$methodDef = null;
// 递归查找,若子类中未定义方法,则尝试查找父类是否存在此方法
while ($classDef) {
@ -1682,7 +1670,7 @@ class CompilerBase extends \PhpAot\Core\Translator
if (!$classDef->extends) {
return false;
}
if (!$this->hasNativeClass($classDef->extends)) {
if (!$this->hasClass($classDef->extends)) {
if ($classDef->inheritedFromInternalClass) {
if (!Reflection::hasMethod($classDef->extends, $method) and !Reflection::hasMethod($classDef->extends, $method . '__call')) {
$this->fatalError($expr, 'Class `' . $classDef->getNamespacedName() . '` inherits from a internal class, but the class `' .
@ -1694,7 +1682,7 @@ class CompilerBase extends \PhpAot\Core\Translator
}
return false;
}
$classDef = $this->getClassDef($classDef->extends);
$classDef = $this->getClass($classDef->extends);
} else {
$methodDef = $classDef->getMethod($method);
break;
@ -1714,13 +1702,13 @@ class CompilerBase extends \PhpAot\Core\Translator
return $this->getNativeName($method, $classDef->namespace, $classDef->name);
}
protected function getNativeClassConst(NodeAbstract $expr, string $class, string $const): string|false
protected function getClassConst(NodeAbstract $expr, string $class, string $const): string|false
{
if (!$this->hasNativeClass($class)) {
if (!$this->hasClass($class)) {
return false;
}
$classDef = $this->getClassDef($class);
$classDef = $this->getClass($class);
$constDef = null;
// 递归查找,若子类中未定义方法,则尝试查找父类是否存在此方法
while ($classDef) {
@ -1728,10 +1716,10 @@ class CompilerBase extends \PhpAot\Core\Translator
if (!$classDef->extends) {
return false;
}
if (!$this->hasNativeClass($classDef->extends)) {
if (!$this->hasClass($classDef->extends)) {
return false;
}
$classDef = $this->getClassDef($classDef->extends);
$classDef = $this->getClass($classDef->extends);
} else {
$constDef = $classDef->getConstant($const);
break;
@ -1747,11 +1735,6 @@ class CompilerBase extends \PhpAot\Core\Translator
}
}
protected function getClassDef(string $name): ClassDef
{
return $this->classes[$this->escapeClass($name)];
}
protected function resetReturnType(Node\Stmt\Return_ $node, string $type): void
{
$oriType = $this->functionDef->returnType;
@ -1816,8 +1799,8 @@ class CompilerBase extends \PhpAot\Core\Translator
case 'Expr_FuncCall':
if ($this->isNameExpr($expr->name)) {
$name = $this->parseIdentifier($expr->name);
if ($this->hasNativeFunction($name)) {
return $this->getNativeFunction($name)->returnType;
if ($this->hasFunction($name)) {
return $this->getFunction($name)->returnType;
}
return $this->detectFuncCallReturnType($name);
}
@ -1828,7 +1811,7 @@ class CompilerBase extends \PhpAot\Core\Translator
$method = $this->parseIdentifier($expr->name);
$nativeFunc = $this->findNativeMethod($expr, $object, $method);
if ($nativeFunc) {
$funcDef = $this->nativeFunctions[$nativeFunc];
$funcDef = $this->functions[$nativeFunc];
// 此函数尚未完成解析,仅经过初步的预处理,无法获得准确的返回值类型
if ($funcDef->completed) {
return $funcDef->returnType;
@ -2323,8 +2306,8 @@ class CompilerBase extends \PhpAot\Core\Translator
if (str_contains($nativeFunc, '\\')) {
$nativeFunc = $this->escapeNamespace($nativeFunc);
}
$this->checkNativeFunction($nativeFunc);
if ($this->hasNativeFunction($nativeFunc)) {
$this->checkFunction($nativeFunc);
if ($this->hasFunction($nativeFunc)) {
return $nativeFunc;
}
}
@ -2351,7 +2334,7 @@ class CompilerBase extends \PhpAot\Core\Translator
$nativeFn = $this->findNativeFunction($name);
if ($nativeFn) {
$expr->setAttribute('nativeCall', $nativeFn);
$this->checkNativeCallArgs($expr, $this->getNativeFunction($nativeFn), $expr->args, $name);
$this->checkNativeCallArgs($expr, $this->getFunction($nativeFn), $expr->args, $name);
try {
return self::PREFIX . $nativeFn . '(' . $this->parseNativeCallArgs($expr->args, $nativeFn) . ')';
} catch (PlaceHolder) {
@ -2387,7 +2370,7 @@ class CompilerBase extends \PhpAot\Core\Translator
protected function parseNativeCallArgs(array $callArgs, string $nativeFunc): string
{
$argList = [];
$functionDef = $this->nativeFunctions[$nativeFunc];
$functionDef = $this->functions[$nativeFunc];
$args = [];
$hasNamedArg = false;
// 对命名参数进行重排
@ -2966,8 +2949,8 @@ class CompilerBase extends \PhpAot\Core\Translator
$cePtr = 'php_get_called_ce(this_)';
} else {
$className = $this->getNamespacedClassName($className);
if ($this->hasNativeClass($className)) {
$classDef = $this->getClassDef($className);
if ($this->hasClass($className)) {
$classDef = $this->getClass($className);
if ($classDef->flags & Modifiers::ABSTRACT) {
$this->fatalError($expr, "abstract class `{$className}` cannot be instantiated");
}
@ -3113,10 +3096,10 @@ class CompilerBase extends \PhpAot\Core\Translator
protected function getArgInfo(Node $arg, string $funcName, int $index): ArgInfo
{
if (!isset($this->nativeFunctions[$funcName])) {
if (!isset($this->functions[$funcName])) {
$this->fatalError($arg, "Function `{$funcName}` is undefined, you must adjust the order of function definition");
}
$funcDef = $this->nativeFunctions[$funcName];
$funcDef = $this->functions[$funcName];
if (!array_key_exists($index, $funcDef->argInfoList)) {
$this->fatalError($arg, "Argument `{$index}` of function `{$funcName}` not found");
}
@ -3727,9 +3710,8 @@ class CompilerBase extends \PhpAot\Core\Translator
return $left . ' = ' . $object . '.attrRef(' . $prop . ')';
}
} else {
$this->fatalError($expr, 'Cannot assign reference to non-variable');
}
$this->fatalError($expr, 'Cannot assign reference to non-variable');
}
protected function parseMethodCall(Node\Expr\MethodCall $expr): string
@ -3863,7 +3845,7 @@ class CompilerBase extends \PhpAot\Core\Translator
if ($callScope) {
$nativeFunc = $this->getNativeMethod($expr, $class, $method);
// 存在 Native 类,但是没有找到方法,可能是动态调用
if (!$nativeFunc and $this->hasNativeClass($class) and $this->getNativeMethod($expr, $class, '__callStatic', false)) {
if (!$nativeFunc and $this->hasClass($class) and $this->getNativeMethod($expr, $class, '__callStatic', false)) {
$dynamicCall = true;
}
@ -3922,11 +3904,11 @@ class CompilerBase extends \PhpAot\Core\Translator
}
$fullName = $this->getNamespacedClassName($class);
if (!$this->hasNativeClass($fullName)) {
if (!$this->hasClass($fullName)) {
return null;
}
$classDef = $this->getClassDef($fullName);
$classDef = $this->getClass($fullName);
if ($classDef->hasProperty($prop)) {
$propDef = $classDef->getProperty($prop);
if ($propDef->isStatic()) {
@ -3947,8 +3929,8 @@ class CompilerBase extends \PhpAot\Core\Translator
$scope = $this->class ? ltrim($namespace . '\\' . $class, '\\') : '';
while (true) {
if ($this->hasNativeClass($findClass)) {
$classDef = $this->getClassDef($findClass);
if ($this->hasClass($findClass)) {
$classDef = $this->getClass($findClass);
if ($classDef->hasProperty($property)) {
$propertyDef = $classDef->getProperty($property);
if ($propertyDef->isPublic()) {
@ -4021,13 +4003,13 @@ class CompilerBase extends \PhpAot\Core\Translator
return '"' . $this->escapeString($class) . '"';
}
if (($self or $this->isNameExpr($expr->class)) and $this->isIdExpr($expr->name)) {
if ($this->hasNativeClass($class)) {
$classDef = $this->getClassDef($class);
if ($this->hasClass($class)) {
$classDef = $this->getClass($class);
if ($classDef->enum) {
$ce = $this->getClassEntryPtr($class);
return 'php::getEnumCase(' . $ce . ', ' . $this->getLiteralString($const) . ')';
}
$nativeConst = $this->getNativeClassConst($expr, $class, $const);
$nativeConst = $this->getClassConst($expr, $class, $const);
if ($nativeConst) {
return $nativeConst;
}
@ -4157,12 +4139,12 @@ class CompilerBase extends \PhpAot\Core\Translator
$constInfo->type = $this->detectStrValueType($value);
$constInfo->namespace = $this->namespace;
$constInfo->name = $name;
$this->nativeConstants[$this->escapeNamespace($name)] = $constInfo;
$this->constants[$this->escapeNamespace($name)] = $constInfo;
}
protected function hasConstant(string $name): bool
{
return isset($this->nativeConstants[$this->escapeNamespace($name)]);
return isset($this->constants[$this->escapeNamespace($name)]);
}
protected function getConstant(string $name): string
@ -4172,7 +4154,7 @@ class CompilerBase extends \PhpAot\Core\Translator
protected function getConstantType(string $name): string
{
return $this->nativeConstants[$this->escapeNamespace($name)]->type;
return $this->constants[$this->escapeNamespace($name)]->type;
}
protected function detectStrValueType(mixed $constant): string
@ -4314,7 +4296,7 @@ class CompilerBase extends \PhpAot\Core\Translator
$class = $this->context->objects[$object];
$nativeFunc = $this->getNativeMethod($expr, $class, $method);
// 存在 Native 类,但是没有找到方法,可能是动态调用
if (!$nativeFunc and $this->hasNativeClass($class) and $this->getNativeMethod($expr, $class, '__call', false)) {
if (!$nativeFunc and $this->hasClass($class) and $this->getNativeMethod($expr, $class, '__call', false)) {
throw new DynamicCall();
}
}
@ -4329,8 +4311,8 @@ class CompilerBase extends \PhpAot\Core\Translator
return false;
}
if ($nativeFunc) {
$this->checkNativeFunction($nativeFunc);
if ($this->hasNativeFunction($nativeFunc)) {
$this->checkFunction($nativeFunc);
if ($this->hasFunction($nativeFunc)) {
return $nativeFunc;
}
}
@ -4460,7 +4442,7 @@ class CompilerBase extends \PhpAot\Core\Translator
}
if ($this->isCallExpr($expr->expr)) {
$nativeCall = $expr->expr->getAttribute('nativeCall');
if ($nativeCall and $this->nativeFunctions[$nativeCall]->returnType === self::TYPE_VOID) {
if ($nativeCall and $this->functions[$nativeCall]->returnType === self::TYPE_VOID) {
return $beforeCode . PHP_EOL . $code . ';' . PHP_EOL . 'return ' . self::VALUE_NULL . ';';
}
}

@ -10,14 +10,10 @@ namespace PhpAot\Php;
class CompilerTest extends Translator
{
protected static ?self $instance = null;
public static function getInstance(string $rootPath = ''): CompilerTest
public static function create(string $rootPath = ''): CompilerTest
{
if (!self::$instance) {
self::$instance = new self($rootPath);
self::$instance->forTest = true;
}
return self::$instance;
$instance = new self($rootPath);
$instance->forTest = true;
return $instance;
}
}

@ -195,7 +195,7 @@ class Preprocessor extends CompilerBase
{
$name = $this->getFunctionName($v);
if ($this->stubFile) {
$this->nativeFunctions[$name] = $this->parseFunctionDecl($v);
$this->addFunction($name, $this->parseFunctionDecl($v));
} else {
$this->symbolDeclInFile[strtolower($name)] = $this->file;
}

@ -244,7 +244,7 @@ class Translator extends Preprocessor
public function genExtension(string $file): void
{
if ($this->buildMode == 'bin') {
if (!isset($this->nativeFunctions['main'])) {
if (!$this->hasFunction('main')) {
$this->climate->red('When the build mode is a binary executable file, the `main()` function must be defined');
exit(1);
}
@ -437,7 +437,7 @@ class Translator extends Preprocessor
$literalStringsCount = count($this->literalStrings);
$code .= 'extern ' . self::TYPE_STR . ' ' . self::LITERAL_STRINGS . '[' . $literalStringsCount . '];' . PHP_EOL;
foreach ($this->nativeFunctions as $name => $func) {
foreach ($this->functions as $name => $func) {
$code .= 'extern ' . $func->returnType . ' ' . self::PREFIX . $name . '(';
$list = [];
if ($func->method) {
@ -462,7 +462,7 @@ class Translator extends Preprocessor
}
$code .= PHP_EOL;
foreach ($this->nativeConstants as $name => $constant) {
foreach ($this->constants as $name => $constant) {
$code .= 'extern ' . $constant->type . ' ' . $name . ';' . PHP_EOL;
}
$this->writeFile($file, $code);
@ -881,8 +881,8 @@ class Translator extends Preprocessor
}
$fullName = $this->getFullClassName();
if ($this->hasNativeClass($fullName)) {
$this->classDef = $this->getClassDef($fullName);
if ($this->hasClass($fullName)) {
$this->classDef = $this->getClass($fullName);
} else {
$this->classDef = new ClassDef($this->class, $flags, $this->namespace);
$this->addClass($fullName, $this->classDef);
@ -894,8 +894,8 @@ class Translator extends Preprocessor
if ($extends) {
$parentClass = $this->getParentClass($class->extends);
if ($this->hasNativeClass($parentClass)) {
$parent = $this->getClassDef($parentClass);
if ($this->hasClass($parentClass)) {
$parent = $this->getClass($parentClass);
if ($parent->flags & Modifiers::FINAL) {
$this->fatalError($class, "Class `{$this->class}` cannot extend final class `{$parentClass}`");
}

@ -74,7 +74,7 @@ foreach ($this->literalStrings as $str => $index):
// constants
<?php
foreach ($this->nativeConstants as $name => $const):
foreach ($this->constants as $name => $const):
?>
<?=$const->type?> <?=$name?>;
<?php endforeach; ?>
@ -103,6 +103,9 @@ foreach ($this->functions as $functionDef):
if ($this->buildMode === 'ext' and $functionDef->name === 'main') {
continue;
}
if ($functionDef->method) {
continue;
}
$zif_name = $this->escapeZendFnName($functionDef->getNamespacedName());
?>
<?php if ($functionDef->namespace): ?>
@ -132,7 +135,7 @@ foreach ($this->registerSymbols as $registerSymbolFn):
void php_app_init() {
// register constants
<?php
foreach ($this->nativeConstants as $name => $const):
foreach ($this->constants as $name => $const):
?>
<?=$name?> = <?= $const->value ?>;
php::define("<?=$this->escapeString($const->name)?>", <?= $name ?>);
@ -166,7 +169,7 @@ foreach ($this->globalVars as $name => $type) :
php::unsetGlobal("<?=$name?>");
<?php endforeach; ?>
<?php
foreach ($this->nativeConstants as $name => $const):
foreach ($this->constants as $name => $const):
if ($const->type !== Translator::TYPE_VAR) {
continue;
}

Loading…
Cancel
Save