feat(extension): 添加类构造器和属性默认值支持

- 在扩展模板中添加类构造器创建对象函数声明
- 实现类构造器中的属性初始化逻辑
- 将静态属性列表重命名为默认静态属性列表
- 添加类属性默认值列表用于存储实例属性初始值
- 更新类信息数组结构添加classDef引用
- 移除构造函数检查逻辑并优化方法包装生成
- 在类包装生成中添加属性默认值处理逻辑
- 添加测试用例验证可空类型属性功能
pull/1/head
韩天峰 5 months ago
parent e7f2760d93
commit 68a9a782e8
  1. 45
      src/Php/Translator.php
  2. 25
      src/template/extension.cc.php
  3. 40
      tests/aot/prop-003.phpt

@ -32,7 +32,10 @@ class Translator extends Preprocessor
protected array $phpSrcFiles = [];
protected array $argInfoHeaderFiles = [];
protected array $registerSymbols = [];
protected array $staticPropertyList = [];
// 类静态属性初始值
protected array $defaultStaticPropertyList = [];
// 类属性初始值
protected array $defaultPropertyList = [];
protected bool $useRegisterSymbolsFn = false;
public function __construct(string $rootPath)
@ -715,6 +718,7 @@ class Translator extends Preprocessor
}
$this->classCeInfo[$ce] = [
'classDef' => $classDef,
'deps' => $deps,
'func' => $this->getRegisterClassFunction($classDef->getNamespacedName()),
'args' => $this->getRegisterClassFunctionArgs($classDef),
@ -866,9 +870,6 @@ class Translator extends Preprocessor
}
}
$code = $this->genNativeMethod($methodCodes);
if ($this->classDef->requireCtor and !$this->classDef->hasMethod('__construct')) {
$this->fatalError($class, "Class `{$this->class}` uses a non-empty array as the default value for an property, and the constructor must be set.");
}
$this->resetClass();
return $code;
@ -937,32 +938,15 @@ class Translator extends Preprocessor
protected function genMethodWrapper(ClassDef $classDef, MethodDef $methodDef): string
{
$name = $classDef->getNamespacedName();
$fullClassName = $classDef->getNamespacedName(false);
$cppCode = 'ZEND_METHOD(' . $name . ', ' . $methodDef->name . '){' . PHP_EOL;
$cppCode .= $this->getIndent() . self::TYPE_OBJECT . ' this_(&execute_data->This);' . PHP_EOL;
foreach ($classDef->properties as $property) {
if ($property->type === self::TYPE_ARRAY and $property->default and $property->default !== self::TYPE_ARRAY . '{}') {
if ($property->isStatic()) {
$prop = new \stdClass();
$prop->class = $fullClassName;
$prop->name = $property->name;
$prop->default = $property->default;
$this->staticPropertyList[$name . '::' . $property->name] = $prop;
} else {
$propOffset = $this->getPropertyOffset($fullClassName, $property->name);
$cppCode .= $this->getIndent() . 'this_.attr(' . $propOffset . ') = ' . $property->default . ';' . PHP_EOL;
}
}
}
$fn = self::PREFIX . $this->getNativeMethodName($classDef, $methodDef);
$cppCode .= $this->genWrapperFunctionArgs($fn, $methodDef->functionDef);
return $cppCode;
}
protected function getClassRegisterCeFunc(ClassDef|InterfaceDef $classDef): void
protected function getClassRegisterCeFunc(ClassDef|InterfaceDef $classDef): string
{
$cppCode = '';
$name = $classDef->getNamespacedName();
@ -971,6 +955,8 @@ class Translator extends Preprocessor
$cppCode .= 'zend_class_entry *' . $this->getRegisterClassFunction($name) . '(' . $argsDef . ') {' . PHP_EOL;
$cppCode .= $this->getIndent() . 'return register_class_' . $name . '(' . $param . ');' . PHP_EOL;
$cppCode .= '}' . PHP_EOL . PHP_EOL;
return $cppCode;
}
protected function genClassWrapper(ClassDef|InterfaceDef $classDef): string
@ -979,6 +965,21 @@ class Translator extends Preprocessor
// 接口没有方法实体
if ($classDef instanceof ClassDef) {
foreach ($classDef->properties as $property) {
if ($property->type === self::TYPE_ARRAY and $property->default and $property->default !== self::TYPE_ARRAY . '{}') {
$fullClassName = $classDef->getNamespacedName(false);
$fullPropName = $fullClassName . '::' . $property->name;
if ($property->isStatic()) {
$prop = new \stdClass();
$prop->class = $fullClassName;
$prop->name = $property->name;
$prop->default = $property->default;
$this->defaultStaticPropertyList[$fullPropName] = $prop;
} else {
$this->defaultPropertyList[$fullPropName] = $property->default;
}
}
}
$methods = $classDef->methods;
foreach ($methods as $methodDef) {
$cppCode .= $this->genMethodWrapper($classDef, $methodDef);

@ -79,9 +79,12 @@ foreach ($this->nativeConstants as $name => $const):
<?=$const->type?> <?=$name?>;
<?php endforeach; ?>
// class array constants
// class
<?php
foreach ($this->classes as $classDef) :
if ($classDef->requireCtor) {
echo "static zend_object* (*create_object_" . $classDef->getNamespacedName() . ")(zend_class_entry *class_type);";
}
foreach ($classDef->constants as $constant) :
if ($constant->type === Translator::TYPE_ARRAY) :
$constName = Translator::PREFIX . $this->getNativeName($constant->name, $classDef->namespace, $classDef->name);
@ -119,6 +122,24 @@ foreach ($this->classCeList as $ce):
$info = $this->classCeInfo[$ce] ?? $this->getInternalCeInfo($ce);
?>
<?=$ce?> = <?= $info['func'] ?>(<?= $info['args'] ?>);
<?php
if ($info['classDef'] and $info['classDef']->requireCtor):
$className = $info['classDef']->getNamespacedName();
?>
create_object_<?=$className?> = <?=$ce?>->create_object ? create_object_<?=$className?> : zend_objects_new;
<?=$ce?>->create_object = [](zend_class_entry *class_type) -> zend_object* {
auto obj = create_object_<?= $className ?>(class_type);
<?php foreach ($info['classDef']->properties as $property):
$fullPropName = $info['classDef']->getNamespacedName(true) . '::' . $property->name;
?>
<?php if (isset($this->defaultPropertyList[$fullPropName])): ?>
auto value = <?=$this->defaultPropertyList[$fullPropName]?>;
zend_update_property_ex(obj->ce, obj, <?=$this->getLiteralString($property->name)?>.str(), value.ptr());
<?php endif; ?>
<?php endforeach; ?>
return obj;
};
<?php endif; ?>
<?php endforeach; ?>
// register symbols
@ -149,7 +170,7 @@ foreach ($this->globalVars as $name => $type):
// static property
<?php
foreach ($this->staticPropertyList as $prop):
foreach ($this->defaultStaticPropertyList as $prop):
?>
php::setStaticProperty(<?=$this->genCharPtr($prop->class, true)?>, <?=$this->genCharPtr($prop->name)?>, <?=$prop->default?>);
<?php

@ -0,0 +1,40 @@
--TEST--
Property with nullable type
--FILE--
<?php
class Response
{
public $phrases = [
100 => 'Continue',
101 => 'Switching Protocols',
102 => 'Processing', // WebDAV; RFC 2518
103 => 'Early Hints', // RFC 8297
];
static public $methods = ['GET', 'PUT', 'POST',];
public function __construct() {
}
public function getPhrase() {
return $this->phrases[200] ?? 'unknown';
}
}
function main() {
$resp = new Response;
var_dump($resp->phrases[100]);
var_dump(Response::$methods);
}
?>
--EXPECT--
string(8) "Continue"
array(3) {
[0]=>
string(3) "GET"
[1]=>
string(3) "PUT"
[2]=>
string(4) "POST"
}
Loading…
Cancel
Save