feat(php): 添加类构造函数初始化和清理功能

- 在ClassDef中添加ctorInit、ctorClean属性和propertyContext
- 修改编译器在对象创建时执行属性初始化代码
- 添加对默认数组属性的支持和测试
- 实现属性上下文管理以正确处理构造函数逻辑
- 更新预处理器和翻译器以支持新的属性处理机制
- 添加property_exists函数的相关测试用例
pull/1/head
韩天峰 5 months ago
parent fbb68ad689
commit 34e112d697
  1. 7
      src/Php/CompilerBase.php
  2. 6
      src/Php/Entity/ClassDef.php
  3. 5
      src/Php/Generator/Utils.php
  4. 2
      src/Php/Preprocessor.php
  5. 22
      src/Php/Translator.php
  6. 44
      tests/aot/object_property/default-array-001.phpt
  7. 60
      tests/zend/property_exists.phpt

@ -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) . '}))';

@ -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);
}

@ -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) . ' }';

@ -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');
}
}
}

@ -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

@ -0,0 +1,44 @@
--TEST--
default array property
--FILE--
<?php
class Test {
public $empty = array();
public $three = array(1, "b"=>"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"
}
}

@ -0,0 +1,60 @@
--TEST--
Testing property_exists()
--FILE--
<?php
class aParent {
public static function staticTest() {
$a = new A;
var_dump(property_exists($a, "prot"));
var_dump(property_exists($a, "prot2"));
var_dump(property_exists($a, "prot3"));
print "------------------\n";
var_dump(property_exists("A", "prot"));
var_dump(property_exists("A", "prot2"));
var_dump(property_exists("A", "prot3"));
print "------------------\n";
}
public function nonstaticTest() {
$a = new A;
var_dump(property_exists($a, "prot"));
var_dump(property_exists($a, "prot2"));
var_dump(property_exists($a, "prot3"));
print "------------------\n";
var_dump(property_exists("A", "prot"));
var_dump(property_exists("A", "prot2"));
var_dump(property_exists("A", "prot3"));
}
}
class A extends aParent {
static public $prot = "prot";
static protected $prot2 = "prot";
static private $prot3 = "prot";
}
function main() {
A::staticTest();
$a = new a;
$a->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)
Loading…
Cancel
Save