Skip to content

Leverage object_init_with_constructor() in zend_get_attribute_object() #14532

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 1 addition & 17 deletions Zend/tests/attributes/005_objects.phpt
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This change is due to:

php-src/Zend/zend_API.c

Lines 1872 to 1874 in bca0c08

/* Surprisingly, this is the only case where internal classes will allow to pass extra arguments
* However, if there are named arguments (and it is not empty),
* an Error must be thrown to be consistent with new ClassName() */

Original file line number Diff line number Diff line change
Expand Up @@ -72,19 +72,6 @@ try {

echo "\n";

#[Attribute]
class A4 { }

$ref = new \ReflectionFunction(#[A4(1)] function () { });

try {
$ref->getAttributes()[0]->newInstance();
} catch (\Error $e) {
var_dump('ERROR 5', $e->getMessage());
}

echo "\n";

class A5 { }

$ref = new \ReflectionFunction(#[A5] function () { });
Expand All @@ -111,10 +98,7 @@ string(7) "ERROR 3"
string(30) "Attribute class "A2" not found"

string(7) "ERROR 4"
string(48) "Attribute constructor of class A3 must be public"

string(7) "ERROR 5"
string(69) "Attribute class A4 does not have a constructor, cannot pass arguments"
string(51) "Call to private A3::__construct() from global scope"

string(7) "ERROR 6"
string(55) "Attempting to use non-attribute class "A5" as attribute"
10 changes: 5 additions & 5 deletions Zend/tests/prop_const_expr/attributes.phpt
Original file line number Diff line number Diff line change
Expand Up @@ -25,20 +25,20 @@ foreach ((new ReflectionClass(C::class))->getAttributes() as $reflectionAttribut
}

?>
--EXPECT--
object(Attr)#1 (1) {
--EXPECTF--
object(Attr)#%d (1) {
["value"]=>
string(1) "B"
}
object(Attr)#1 (1) {
object(Attr)#%d (1) {
["value"]=>
string(1) "C"
}
object(Attr)#1 (1) {
object(Attr)#%d (1) {
["value"]=>
string(1) "B"
}
object(Attr)#1 (1) {
object(Attr)#%d (1) {
["value"]=>
string(1) "C"
}
88 changes: 22 additions & 66 deletions Zend/zend_attributes.c
Original file line number Diff line number Diff line change
Expand Up @@ -214,18 +214,9 @@ ZEND_API zend_result zend_get_attribute_value(zval *ret, zend_attribute *attr, u
return SUCCESS;
}

static zend_result call_attribute_constructor(
zend_attribute *attr, zend_class_entry *ce, zend_object *obj,
zval *args, uint32_t argc, HashTable *named_params, zend_string *filename)
ZEND_API zend_result zend_get_attribute_object(zval *obj, zend_class_entry *attribute_ce, zend_attribute *attribute_data, zend_class_entry *scope, zend_string *filename)
{
zend_function *ctor = ce->constructor;
zend_execute_data *call = NULL;
ZEND_ASSERT(ctor != NULL);

if (!(ctor->common.fn_flags & ZEND_ACC_PUBLIC)) {
zend_throw_error(NULL, "Attribute constructor of class %s must be public", ZSTR_VAL(ce->name));
return FAILURE;
}

if (filename) {
/* Set up dummy call frame that makes it look like the attribute was invoked
Expand All @@ -244,7 +235,7 @@ static zend_result call_attribute_constructor(
opline = (zend_op*)(call + 1);
memset(opline, 0, sizeof(zend_op));
opline->opcode = ZEND_DO_FCALL;
opline->lineno = attr->lineno;
opline->lineno = attribute_data->lineno;

call->opline = opline;
call->call = NULL;
Expand All @@ -255,57 +246,17 @@ static zend_result call_attribute_constructor(
memset(call->func, 0, sizeof(zend_function));
call->func->type = ZEND_USER_FUNCTION;
call->func->op_array.fn_flags =
attr->flags & ZEND_ATTRIBUTE_STRICT_TYPES ? ZEND_ACC_STRICT_TYPES : 0;
attribute_data->flags & ZEND_ATTRIBUTE_STRICT_TYPES ? ZEND_ACC_STRICT_TYPES : 0;
call->func->op_array.fn_flags |= ZEND_ACC_CALL_VIA_TRAMPOLINE;
call->func->op_array.filename = filename;

EG(current_execute_data) = call;
}

zend_call_known_function(ctor, obj, obj->ce, NULL, argc, args, named_params);

if (filename) {
EG(current_execute_data) = call->prev_execute_data;
zend_vm_stack_free_call_frame(call);
}

if (EG(exception)) {
zend_object_store_ctor_failed(obj);
return FAILURE;
}

return SUCCESS;
}

static void attribute_ctor_cleanup(zval *obj, zval *args, uint32_t argc, HashTable *named_params)
{
if (obj) {
zval_ptr_dtor(obj);
}

if (args) {
uint32_t i;

for (i = 0; i < argc; i++) {
zval_ptr_dtor(&args[i]);
}

efree(args);
}

if (named_params) {
zend_array_destroy(named_params);
}
}

ZEND_API zend_result zend_get_attribute_object(zval *obj, zend_class_entry *attribute_ce, zend_attribute *attribute_data, zend_class_entry *scope, zend_string *filename)
{
zval *args = NULL;
HashTable *named_params = NULL;

if (SUCCESS != object_init_ex(obj, attribute_ce)) {
return FAILURE;
}
zend_result result = FAILURE;

uint32_t argc = 0;
if (attribute_data->argc) {
Expand All @@ -314,8 +265,8 @@ ZEND_API zend_result zend_get_attribute_object(zval *obj, zend_class_entry *attr
for (uint32_t i = 0; i < attribute_data->argc; i++) {
zval val;
if (FAILURE == zend_get_attribute_value(&val, attribute_data, i, scope)) {
attribute_ctor_cleanup(obj, args, argc, named_params);
return FAILURE;
result = FAILURE;
goto out;
}
if (attribute_data->args[i].name) {
if (!named_params) {
Expand All @@ -329,20 +280,25 @@ ZEND_API zend_result zend_get_attribute_object(zval *obj, zend_class_entry *attr
}
}

if (attribute_ce->constructor) {
if (FAILURE == call_attribute_constructor(attribute_data, attribute_ce, Z_OBJ_P(obj), args, argc, named_params, filename)) {
attribute_ctor_cleanup(obj, args, argc, named_params);
return FAILURE;
}
} else if (argc || named_params) {
attribute_ctor_cleanup(obj, args, argc, named_params);
zend_throw_error(NULL, "Attribute class %s does not have a constructor, cannot pass arguments", ZSTR_VAL(attribute_ce->name));
return FAILURE;
result = object_init_with_constructor(obj, attribute_ce, argc, args, named_params);

out:
for (uint32_t i = 0; i < argc; i++) {
zval_ptr_dtor(&args[i]);
}

attribute_ctor_cleanup(NULL, args, argc, named_params);
efree(args);

return SUCCESS;
if (named_params) {
zend_array_destroy(named_params);
}

if (filename) {
EG(current_execute_data) = call->prev_execute_data;
zend_vm_stack_free_call_frame(call);
}

return result;
}

static const char *target_names[] = {
Expand Down
14 changes: 7 additions & 7 deletions ext/reflection/tests/new_in_attributes.phpt
Original file line number Diff line number Diff line change
Expand Up @@ -36,34 +36,34 @@ $obj = $ra->newInstance();
var_dump($args, $obj);

?>
--EXPECT--
--EXPECTF--
array(2) {
[0]=>
NULL
[1]=>
object(stdClass)#3 (0) {
object(stdClass)#%d (0) {
}
}
object(MyAttribute)#4 (2) {
object(MyAttribute)#%d (2) {
["x"]=>
NULL
["y"]=>
object(stdClass)#5 (0) {
object(stdClass)#%d (0) {
}
}
bool(true)
bool(true)
array(2) {
["y"]=>
object(stdClass)#2 (0) {
object(stdClass)#%d (0) {
}
["x"]=>
NULL
}
object(MyAttribute)#10 (2) {
object(MyAttribute)#%d (2) {
["x"]=>
NULL
["y"]=>
object(stdClass)#11 (0) {
object(stdClass)#%d (0) {
}
}
Loading