fix(runtime): correct property inheritance handling in object creation

- Fixed zend_update_property call to use correct class entry variable
- Refactored object creation logic to delegate to parent allocator when needed
- Added parentHasCustomCreateObjectOnPhp84 method to detect parent class properties
- Simplified create object handler implementation using typephp_create_object_with_defaults
- Updated property initialization to work properly with asymmetric visibility
- Modified test case to include options property verification in inheritance scenario
pull/34/head v0.4.0
韩天峰 1 month ago
parent 9009a0b5fa
commit 90b61fbc42
  1. 75
      src/Translator.php
  2. 12
      tests/compiler/object_property/default-expressions-inheritance.phpt

@ -1637,7 +1637,7 @@ CODE;
}
if ($property->arrayInitPlan) {
$init = "auto value = {$property->arrayInitPlan->expr};\n";
$init .= 'zend_update_property(obj->ce, obj, ' . $this->genZendStrl($property->name) . ", value.ptr());\n";
$init .= 'zend_update_property(' . $ce . ', obj, ' . $this->genZendStrl($property->name) . ", value.ptr());\n";
$init .= "php::throwErrorIfOccurred();\n";
$initBlock .= $this->wrapArrayInitPlan($property->arrayInitPlan, $init);
} else {
@ -1651,43 +1651,22 @@ CODE;
: $property->default;
$init = "do {\n";
$init .= "auto value = php::Var({$default});\n";
$init .= 'zend_update_property(obj->ce, obj, ' . $this->genZendStrl($property->name) . ", value.ptr());\n";
$init .= 'zend_update_property(' . $ce . ', obj, ' . $this->genZendStrl($property->name) . ", value.ptr());\n";
$init .= "php::throwErrorIfOccurred();\n";
$init .= "} while (0);\n";
$initBlock .= $init;
}
}
$buildCreateBody = function (bool $attachHandlers) use ($classDef, $className, $handlers, $ce, $initBlock): string {
$delegateToParentAllocator = $this->parentHasCustomCreateObjectOnPhp84($classDef);
$buildCreateBody = function () use ($classDef, $className, $handlers, $initBlock, $delegateToParentAllocator): string {
$body = $classDef->ctorInit;
if ($attachHandlers) {
// PHP < 8.4: the custom handlers are attached to the object
// AFTER the standard create_object, so object_properties_init
// runs with the standard handlers (no asymmetric check). Our
// explicit default inits run with the custom handlers
// attached, so we set EG(fake_scope) to the object's own class
// to satisfy asymmetric visibility for the class's own
// properties.
$body .= "auto obj = create_object_{$className}(class_type);\n";
$body .= "typephp_attach_property_handlers(obj, &{$handlers});\n";
$body .= "zend_class_entry *__typephp_saved_fake_scope = EG(fake_scope);\n";
$body .= "EG(fake_scope) = obj->ce;\n";
$body .= $initBlock;
$body .= "EG(fake_scope) = __typephp_saved_fake_scope;\n";
} else {
// PHP >= 8.4: the custom handlers live in
// default_object_handlers, so the object already carries the
// asymmetric write_property hook at creation time and
// object_properties_init would reject private(set)/protected(set)
// default values (including inherited ones). Create the object
// with the standard handlers, run the default initialization
// (no visibility check), then attach the custom handlers.
$body .= "auto obj = zend_objects_new(class_type);\n";
$body .= "obj->handlers = const_cast<zend_object_handlers *>(zend_get_std_object_handlers());\n";
$body .= "object_properties_init(obj, class_type);\n";
$body .= $initBlock;
$body .= "obj->handlers = &{$handlers};\n";
}
$body .= "auto obj = typephp_create_object_with_defaults(\n";
$body .= "class_type, create_object_{$className}, &{$handlers}, ";
$body .= ($delegateToParentAllocator ? 'true' : 'false') . ",\n";
$body .= "[&](zend_object *obj) {\n";
$body .= $initBlock;
$body .= "});\n";
$body .= $classDef->ctorClean;
return $body . "return obj;\n";
};
@ -1696,13 +1675,13 @@ CODE;
$code .= "#if (PHP_VERSION_ID < 80400)\n";
$code .= "create_object_{$className} = php_get_create_object_fn({$ce});\n";
$code .= "{$ce}->create_object = [](zend_class_entry *class_type) -> zend_object* {\n";
$code .= $buildCreateBody(true);
$code .= $buildCreateBody();
$code .= "};\n";
if ($classDef->requireCtor || $this->classHasAsymmetricOrHookedProperty($classDef)) {
$code .= "#else\n";
$code .= "create_object_{$className} = php_get_create_object_fn({$ce});\n";
$code .= "{$ce}->create_object = [](zend_class_entry *class_type) -> zend_object* {\n";
$code .= $buildCreateBody(false);
$code .= $buildCreateBody();
$code .= "};\n";
}
$code .= "#endif\n";
@ -1751,6 +1730,36 @@ CODE;
return false;
}
private function parentHasCustomCreateObjectOnPhp84(ClassDef $classDef): bool
{
if ($classDef->extends === '') {
return false;
}
if ($classDef->inheritedFromInternalClass) {
return true;
}
$parent = $this->getClassDef($classDef->extends);
while ($parent !== null) {
foreach ($parent->properties as $property) {
if (!$property->isStatic() && $property->default !== null) {
return true;
}
}
if ($this->classHasAsymmetricOrHookedProperty($parent)) {
return true;
}
if ($parent->extends === '') {
break;
}
if ($parent->inheritedFromInternalClass) {
return true;
}
$parent = $this->getClassDef($parent->extends);
}
return false;
}
protected function getRegisterClassFunction(string $name): string
{
return self::PREFIX . 'register_class_' . $name;

@ -18,6 +18,7 @@ class PropertyDefaultsParent
public int $sum = 1 + 2;
public float $ratio = 2;
protected string $label = self::LABEL_PREFIX . '-value';
protected array $options = ['mode' => 'parent'];
public static int $counter = GLOBAL_NUMBER + 1;
public static float $staticRatio = 3;
@ -25,6 +26,11 @@ class PropertyDefaultsParent
{
return $this->label;
}
public function options(): array
{
return $this->options;
}
}
class PropertyDefaultsChild extends PropertyDefaultsParent
@ -35,7 +41,7 @@ class PropertyDefaultsChild extends PropertyDefaultsParent
function main(): void
{
$value = new PropertyDefaultsChild();
var_dump($value->sum, $value->ratio, $value->label(), $value->traitValue);
var_dump($value->sum, $value->ratio, $value->label(), $value->options(), $value->traitValue);
var_dump(
PropertyDefaultsChild::$counter,
PropertyDefaultsChild::$staticRatio,
@ -47,6 +53,10 @@ function main(): void
int(3)
float(2)
string(12) "parent-value"
array(1) {
["mode"]=>
string(6) "parent"
}
int(11)
int(5)
float(3)

Loading…
Cancel
Save