feat(compiler): 支持函数别名和命名空间功能

- 实现了函数导入别名功能,解决命名冲突问题
- 添加了命名空间支持,包括嵌套命名空间解析
- 更新了函数定义实体类以支持命名空间属性
- 修改了编译器基础类中的函数解析逻辑
- 调整了扩展模板中函数注册的命名空间处理
- 优化了预处理器中的命名空间定义逻辑
- 完善了翻译器中函数包装器的命名空间支持
- 新增了函数别名查找和解析功能
- 添加了相关测试用例验证功能正确性
pull/1/head
韩天峰 7 months ago
parent ca4a34204c
commit 9118fc4e33
  1. 40
      src/Php/CompilerBase.php
  2. 9
      src/Php/Entity/FunctionDef.php
  3. 2
      src/Php/Preprocessor.php
  4. 22
      src/Php/Translator.php
  5. 5
      src/template/extension.cc.php
  6. 38
      tests/zend/str_offset_001.phpt
  7. 24
      tests/zend/try/try_finally_001.phpt
  8. 33
      tests/zend/use_function/alias.phpt

@ -106,7 +106,6 @@ class CompilerBase extends \PhpAot\Core\Translator
protected int $floatPrecision = 17; protected int $floatPrecision = 17;
protected bool $debugInfo = true; protected bool $debugInfo = true;
protected bool $noLiteralStrings = false; protected bool $noLiteralStrings = false;
protected bool $useCppNamespace = false;
protected string $file; protected string $file;
protected string $dir; protected string $dir;
@ -644,7 +643,7 @@ class CompilerBase extends \PhpAot\Core\Translator
} }
$fnName = $this->parseIdentifier($v->name); $fnName = $this->parseIdentifier($v->name);
$returnType = $v->returnType ? $this->getTypeFromZendType($this->parseIdentifier($v->returnType)) : self::TYPE_VOID; $returnType = $v->returnType ? $this->getTypeFromZendType($this->parseIdentifier($v->returnType)) : self::TYPE_VOID;
$functionDef = new FunctionDef($fnName, $returnType); $functionDef = new FunctionDef($fnName, $returnType, $this->namespace);
$this->functionDef = $functionDef; $this->functionDef = $functionDef;
$this->parseParams($v->params, $functionDef); $this->parseParams($v->params, $functionDef);
@ -1796,6 +1795,9 @@ class CompilerBase extends \PhpAot\Core\Translator
protected function findNativeFunction(string $fname): string|false protected function findNativeFunction(string $fname): string|false
{ {
$possibleFunctionNames = [$this->escapeName($fname)]; $possibleFunctionNames = [$this->escapeName($fname)];
if (isset($this->useAliases[$fname])) {
$possibleFunctionNames[] = $this->escapeName($this->escapeNamespace($this->useAliases[$fname]));
}
if ($this->namespace) { if ($this->namespace) {
$possibleFunctionNames[] = $this->escapeNamespace($this->namespace) . self::NAMESPACE_SEPARATOR . $fname; $possibleFunctionNames[] = $this->escapeNamespace($this->namespace) . self::NAMESPACE_SEPARATOR . $fname;
} }
@ -3102,8 +3104,8 @@ class CompilerBase extends \PhpAot\Core\Translator
$this->addLocalVar($exVar, self::TYPE_OBJECT); $this->addLocalVar($exVar, self::TYPE_OBJECT);
$code .= 'catch(zend_object *_ex) {' . PHP_EOL; $code .= 'catch(zend_object *_ex) {' . PHP_EOL;
$code .= $this->getIndent() . $exVar . ' = php::catchException();' . PHP_EOL;
if ($catches) { if ($catches) {
$code .= $this->getIndent() . $exVar . ' = php::catchException();' . PHP_EOL;
$this->indentLevel++; $this->indentLevel++;
foreach ($catches as $catch) { foreach ($catches as $catch) {
$code .= $this->parseCatch($catch, $exVar); $code .= $this->parseCatch($catch, $exVar);
@ -3111,6 +3113,7 @@ class CompilerBase extends \PhpAot\Core\Translator
$this->indentLevel--; $this->indentLevel--;
} }
$code .= '}' . PHP_EOL; $code .= '}' . PHP_EOL;
if ($finally) { if ($finally) {
$code .= $this->parseStmts($finally->stmts); $code .= $this->parseStmts($finally->stmts);
$code .= PHP_EOL; $code .= PHP_EOL;
@ -3267,28 +3270,21 @@ class CompilerBase extends \PhpAot\Core\Translator
protected function parseUse(mixed $v2): string protected function parseUse(mixed $v2): string
{ {
$code = ''; $code = '';
if ($this->useCppNamespace) { foreach ($v2->uses as $use) {
foreach ($v2->uses as $use) { $id = $this->parseIdentifier($use->name);
$code .= 'using ' . str_replace('\\', '::', $use->name->toString()) . ';' . PHP_EOL; if ($use->type === Node\Stmt\Use_::TYPE_FUNCTION) {
} $rpos = strrpos($id, '\\');
} else { $fn = substr($id, $rpos + 1);
foreach ($v2->uses as $use) { $ns = substr($id, 0, $rpos);
$id = $this->parseIdentifier($use->name); // fn => namespace
if ($use->type === Node\Stmt\Use_::TYPE_FUNCTION) { $this->useFunctions[$fn] = $ns;
$rpos = strrpos($id, '\\'); } else {
$fn = substr($id, $rpos + 1); $this->useNamespaces[] = $id;
$ns = substr($id, 0, $rpos); if ($use->alias) {
// fn => namespace $this->useAliases[$use->alias->toString()] = $id;
$this->useFunctions[$fn] = $ns;
} else {
$this->useNamespaces[] = $id;
if ($use->alias) {
$this->useAliases[$use->alias->toString()] = $id;
}
} }
} }
} }
return $code; return $code;
} }

@ -21,11 +21,18 @@ class FunctionDef
public array $argInfoList = []; public array $argInfoList = [];
public int $argCountRequired = 0; public int $argCountRequired = 0;
public string $params = ''; public string $params = '';
public string $namespace = '';
public bool $method = false; public bool $method = false;
public function __construct(string $name, string $returnType) public function __construct(string $name, string $returnType, string $namespace = '')
{ {
$this->name = $name; $this->name = $name;
$this->returnType = $returnType; $this->returnType = $returnType;
$this->namespace = $namespace;
}
public function getNamespacedName(): string
{
return $this->namespace ? $this->namespace . '\\' . $this->name : $this->name;
} }
} }

@ -130,7 +130,7 @@ class Preprocessor extends CompilerBase
protected function prepareNamespaceDef(Node\Stmt\Namespace_ $node): void protected function prepareNamespaceDef(Node\Stmt\Namespace_ $node): void
{ {
$this->resetNamespace(); $this->resetNamespace();
$this->namespace = $this->parseIdentifier($node->name); $this->namespace = $node->name ? $this->parseIdentifier($node->name) : '';
foreach ($node->stmts as $v2) { foreach ($node->stmts as $v2) {
$type2 = $v2->getType(); $type2 = $v2->getType();
switch ($type2) { switch ($type2) {

@ -587,25 +587,13 @@ class Translator extends Preprocessor
protected function parseNamespace(Node\Stmt\Namespace_ $node): string protected function parseNamespace(Node\Stmt\Namespace_ $node): string
{ {
$ns = $this->parseIdentifier($node->name); $ns = $node->name ? $this->parseIdentifier($node->name) : '';
$code = ''; $code = '';
$this->resetNamespace(); $this->resetNamespace();
if ($this->useCppNamespace) { $this->namespace = $ns;
$ns = explode('\\', $ns); $ns_end = '';
$ns = array_filter($ns, function ($v) {
return $v !== '';
});
foreach ($ns as $name) {
$code .= 'namespace ' . $name . ' {' . PHP_EOL;
}
$ns_end = str_repeat('}', count($ns));
$this->namespace = implode('::', $ns);
} else {
$this->namespace = $ns;
$ns_end = '';
}
foreach ($node->stmts as $v2) { foreach ($node->stmts as $v2) {
$type2 = $v2->getType(); $type2 = $v2->getType();
@ -984,9 +972,9 @@ class Translator extends Preprocessor
private function genFunctionWrapper(FunctionDef $functionDef): string private function genFunctionWrapper(FunctionDef $functionDef): string
{ {
$name = $functionDef->name; $name = $functionDef->namespace ? $functionDef->namespace . '_' . $functionDef->name : $functionDef->name;
$cppCode = 'ZEND_FUNCTION(' . $name . '){' . PHP_EOL; $cppCode = 'ZEND_FUNCTION(' . $name . '){' . PHP_EOL;
$fn = self::PREFIX . $this->getNativeName($functionDef->name); $fn = self::PREFIX . $this->getNativeName($functionDef->name, $functionDef->namespace);
$cppCode .= $this->genWrapperFunctionArgs($fn, $functionDef); $cppCode .= $this->genWrapperFunctionArgs($fn, $functionDef);
return $cppCode; return $cppCode;

@ -76,7 +76,12 @@ foreach ($this->functions as $functionDef):
continue; continue;
} }
?> ?>
<?php if ($functionDef->namespace): ?>
<?php $zif_name = $functionDef->namespace . '_' . $functionDef->name; ?>
ZEND_NAMED_FE("<?=$this->escapeString($functionDef->getNamespacedName())?>", ZEND_FN(<?= $zif_name ?>), arginfo_<?=$zif_name?>)
<?php else: ?>
ZEND_FE(<?=$functionDef->name?>, arginfo_<?=$functionDef->name?>) ZEND_FE(<?=$functionDef->name?>, arginfo_<?=$functionDef->name?>)
<?php endif; ?>
<?php endforeach;?> <?php endforeach;?>
ZEND_FE_END ZEND_FE_END
}; };

@ -0,0 +1,38 @@
--TEST--
string offset 001
--FILE--
<?php
// Test positive or null string offsets
function foo($x) {
var_dump($x);
}
function main() {
$str = "abc";
var_dump($str[0]);
var_dump($str[1]);
var_dump($str[2]);
var_dump($str[3]);
var_dump($str[1][0]);
foo($str[0]);
foo($str[1]);
foo($str[2]);
foo($str[3]);
foo($str[1][0]);
}
?>
--EXPECTF--
string(1) "a"
string(1) "b"
string(1) "c"
string(0) ""
string(1) "b"
string(1) "a"
string(1) "b"
string(1) "c"
string(0) ""
string(1) "b"

@ -0,0 +1,24 @@
--TEST--
Try finally (basic test)
--FILE--
<?php
function foo ($a) {
try {
throw new Exception("ex");
} finally {
var_dump($a);
}
}
function main() {
foo("finally");
}
?>
--EXPECTF--
string(7) "finally"
Fatal error: Uncaught Exception: ex %s
Stack trace:
#0 %stry_finally_001.php(%d): foo('finally')
#1 {main}
thrown in %stry_finally_001.php on line %d

@ -0,0 +1,33 @@
--TEST--
aliasing imported functions to resolve naming conflicts
--FILE--
<?php
namespace foo {
function baz() {
return 'foo.baz';
}
}
namespace bar {
function baz() {
return 'bar.baz';
}
}
namespace {
use function foo\baz as foo_baz,
bar\baz as bar_baz;
function main() {
var_dump(foo_baz());
var_dump(bar_baz());
echo "Done\n";
}
}
?>
--EXPECT--
string(7) "foo.baz"
string(7) "bar.baz"
Done
Loading…
Cancel
Save