From 34e112d6976f40ce11dd318ded48cbe0485abcd7 Mon Sep 17 00:00:00 2001 From: tianfenghan Date: Tue, 7 Apr 2026 20:55:54 +0800 Subject: [PATCH] =?UTF-8?q?feat(php):=20=E6=B7=BB=E5=8A=A0=E7=B1=BB?= =?UTF-8?q?=E6=9E=84=E9=80=A0=E5=87=BD=E6=95=B0=E5=88=9D=E5=A7=8B=E5=8C=96?= =?UTF-8?q?=E5=92=8C=E6=B8=85=E7=90=86=E5=8A=9F=E8=83=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 在ClassDef中添加ctorInit、ctorClean属性和propertyContext - 修改编译器在对象创建时执行属性初始化代码 - 添加对默认数组属性的支持和测试 - 实现属性上下文管理以正确处理构造函数逻辑 - 更新预处理器和翻译器以支持新的属性处理机制 - 添加property_exists函数的相关测试用例 --- src/Php/CompilerBase.php | 7 ++- src/Php/Entity/ClassDef.php | 6 ++ src/Php/Generator/Utils.php | 5 ++ src/Php/Preprocessor.php | 2 +- src/Php/Translator.php | 22 ++++++- .../object_property/default-array-001.phpt | 44 ++++++++++++++ tests/zend/property_exists.phpt | 60 +++++++++++++++++++ 7 files changed, 139 insertions(+), 7 deletions(-) create mode 100644 tests/aot/object_property/default-array-001.phpt create mode 100644 tests/zend/property_exists.phpt diff --git a/src/Php/CompilerBase.php b/src/Php/CompilerBase.php index 3b39f89b..a49b6e2b 100644 --- a/src/Php/CompilerBase.php +++ b/src/Php/CompilerBase.php @@ -3131,7 +3131,8 @@ class CompilerBase extends \PhpAot\Core\Translator $fullName = $this->getNamespacedClassName($ns[0]); $ce = $this->getClassEntryPtr($fullName); return 'php::constant(' . $ce . ', ' . $this->getLiteralString($ns[1]) . ')'; - } elseif (isset($this->useAliases[$name])) { + } + if (isset($this->useAliases[$name])) { $name = $this->useAliases[$name]; } else { $fullName = $this->getNamespacedClassName($name); @@ -4251,10 +4252,10 @@ class CompilerBase extends \PhpAot\Core\Translator return $code; } - protected function parseShellExec(Node\Expr\ShellExec $expr): string + protected function parseShellExec(Expr\ShellExec $expr): string { $list = []; - foreach($expr->parts as $part) { + foreach ($expr->parts as $part) { $list[] = $this->identifierToStr($part); } return 'php::shell_exec(php::concat({' . implode(', ', $list) . '}))'; diff --git a/src/Php/Entity/ClassDef.php b/src/Php/Entity/ClassDef.php index 74591693..a6dd8412 100644 --- a/src/Php/Entity/ClassDef.php +++ b/src/Php/Entity/ClassDef.php @@ -8,6 +8,8 @@ namespace PhpAot\Php\Entity; +use PhpAot\Php\Context\FunctionContext; + class ClassDef extends ClassLikeDef { /** @@ -30,10 +32,14 @@ class ClassDef extends ClassLikeDef public bool $enum = false; public int $flags; public bool $inheritedFromInternalClass = false; + public string $ctorInit = ''; + public string $ctorClean = ''; + public FunctionContext $propertyContext; public function __construct(string $name, int $flags, string $namespace = '') { $this->flags = $flags; + $this->propertyContext = new FunctionContext(); parent::__construct($name, $namespace); } diff --git a/src/Php/Generator/Utils.php b/src/Php/Generator/Utils.php index fb3aa1a3..c0323b25 100644 --- a/src/Php/Generator/Utils.php +++ b/src/Php/Generator/Utils.php @@ -18,6 +18,11 @@ trait Utils return '"' . ($escape ? $this->escapeString($str) : $str) . '"'; } + protected function genZendStrl(string $char): string + { + return 'ZEND_STRL(' . $this->genCharPtr($char) . ')'; + } + protected function genArray(array $elements): string { return CompilerBase::TYPE_ARRAY . '{' . implode(', ', $elements) . ' }'; diff --git a/src/Php/Preprocessor.php b/src/Php/Preprocessor.php index 71443254..7763b5ed 100644 --- a/src/Php/Preprocessor.php +++ b/src/Php/Preprocessor.php @@ -277,7 +277,7 @@ class Preprocessor extends CompilerBase if ($this->method === '__construct') { foreach ($v->params as $param) { if ($param->isPromoted()) { - $this->fatalError($v, "Cannot declare promoted property in an abstract constructor"); + $this->fatalError($v, 'Cannot declare promoted property in an abstract constructor'); } } } diff --git a/src/Php/Translator.php b/src/Php/Translator.php index 12fb9717..29a3af34 100644 --- a/src/Php/Translator.php +++ b/src/Php/Translator.php @@ -522,14 +522,19 @@ class Translator extends Preprocessor $className = $classDef->getNamespacedName(); $code .= "create_object_{$className} = php_get_create_object_fn({$ce});\n"; $code .= "{$ce}->create_object = [](zend_class_entry *class_type) -> zend_object* {\n"; + $code .= $classDef->ctorInit; $code .= "auto obj = create_object_{$className}(class_type);\n"; + $code .= "object_properties_init(obj, class_type);\n"; foreach ($classDef->properties as $property) { $fullPropName = $classDef->getNamespacedName() . '::' . $property->name; if (isset($this->defaultPropertyList[$fullPropName])) { + $code .= "do {\n"; $code .= "auto value = {$this->defaultPropertyList[$fullPropName]};\n"; - $code .= 'zend_update_property_ex(obj->ce, obj, ' . $this->getLiteralString($property->name) . ".str(), value.ptr());\n"; + $code .= 'zend_update_property(obj->ce, obj, ' . $this->genZendStrl($property->name) . ", value.ptr());\n"; + $code .= "} while(0);\n"; } } + $code .= $classDef->ctorClean; $code .= "return obj;\n};\n"; } } @@ -936,6 +941,13 @@ class Translator extends Preprocessor } } $code = $this->genNativeMethod($methodCodes); + + $oriCtx = $this->context; + $this->context = $this->classDef->propertyContext; + $this->classDef->ctorInit .= $this->genLocalVarDecl() . $this->parseBeforeStmtLines(); + $this->classDef->ctorClean .= $this->parseAfterStmtLines(); + $this->context = $oriCtx; + $this->resetClass(); return $code; @@ -1150,19 +1162,23 @@ class Translator extends Preprocessor protected function parsePropertyDef(Node\Stmt\Property $v): void { + $oriCtx = $this->context; + $this->context = $this->classDef->propertyContext; $flags = $this->parseModifiers($v->flags); $type = $this->parseTypeDecl($v->type, self::DECL_TYPE_OF_PROPERTY); foreach ($v->props as $prop) { $propDef = new PropertyDef($this->parseIdentifier($prop->name), $flags, $type); if ($prop->default) { - if ($prop->default->getType() == 'Expr_Array' and count($prop->default->items) > 0) { + $propDef->default = $this->parseIdentifier($prop->default); + if ($prop->default->getType() == 'Expr_Array') { $propDef->type = self::TYPE_ARRAY; } - $propDef->default = $this->parseIdentifier($prop->default); } $this->classDef->properties[$propDef->name] = $propDef; } + + $this->context = $oriCtx; } protected function parseModifiers(int $flags): int diff --git a/tests/aot/object_property/default-array-001.phpt b/tests/aot/object_property/default-array-001.phpt new file mode 100644 index 00000000..f17f8023 --- /dev/null +++ b/tests/aot/object_property/default-array-001.phpt @@ -0,0 +1,44 @@ +--TEST-- +default array property +--FILE-- +"c", 3=>array()); + + function bar() { + echo __METHOD__; + } + + public $four = array('hello' => 'world', 'bar', ); +} + +function main() { + $obj = new Test; + var_dump(get_object_vars($obj)); +} +?> +--EXPECT-- +array(3) { + ["empty"]=> + array(0) { + } + ["three"]=> + array(3) { + [0]=> + int(1) + ["b"]=> + string(1) "c" + [3]=> + array(0) { + } + } + ["four"]=> + array(2) { + ["hello"]=> + string(5) "world" + [0]=> + string(3) "bar" + } +} diff --git a/tests/zend/property_exists.phpt b/tests/zend/property_exists.phpt new file mode 100644 index 00000000..a0b47815 --- /dev/null +++ b/tests/zend/property_exists.phpt @@ -0,0 +1,60 @@ +--TEST-- +Testing property_exists() +--FILE-- +nonstaticTest(); +} + +?> +--EXPECT-- +bool(true) +bool(true) +bool(true) +------------------ +bool(true) +bool(true) +bool(true) +------------------ +bool(true) +bool(true) +bool(true) +------------------ +bool(true) +bool(true) +bool(true)