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