Skip to content

Commit ab717aa

Browse files
committed
Partial attributes support
First need some APIs for internal calls with named attributes.
1 parent 41bec20 commit ab717aa

10 files changed

+116
-12
lines changed
Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
--TEST--
2+
Named params in attributes
3+
--FILE--
4+
<?php
5+
6+
<<Attribute>>
7+
class MyAttribute {
8+
public function __construct(
9+
public $a = 'a',
10+
public $b = 'b',
11+
public $c = 'c',
12+
) {}
13+
}
14+
15+
<<MyAttribute('A', c: 'C')>>
16+
class Test {}
17+
18+
$attr = (new ReflectionClass(Test::class))->getAttributes()[0];
19+
var_dump($attr->getName());
20+
var_dump($attr->getArguments());
21+
var_dump($attr->newInstance());
22+
23+
?>
24+
--EXPECT--
25+
string(11) "MyAttribute"
26+
array(2) {
27+
[0]=>
28+
string(1) "A"
29+
["c"]=>
30+
string(1) "C"
31+
}
32+
object(MyAttribute)#1 (3) {
33+
["a"]=>
34+
string(1) "A"
35+
["b"]=>
36+
string(1) "C"
37+
["c"]=>
38+
string(1) "c"
39+
}
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
--TEST--
2+
Named params in attributes: Duplicate named parameter error
3+
--FILE--
4+
<?php
5+
6+
<<MyAttribute(a: 'A', a: 'A')>>
7+
class Test {}
8+
9+
?>
10+
--EXPECTF--
11+
Fatal error: Duplicate named parameter $a in %s on line %d
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
--TEST--
2+
Named params in attributes: Positional after named error
3+
--FILE--
4+
<?php
5+
6+
<<Attribute>>
7+
class MyAttribute { }
8+
9+
<<MyAttribute(a: 'A', 'B')>>
10+
class Test {}
11+
12+
?>
13+
--EXPECTF--
14+
Fatal error: Cannot use positional argument after named argument in %s on line %d

Zend/zend_attributes.c

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -121,7 +121,8 @@ ZEND_API int zend_get_attribute_value(zval *ret, zend_attribute *attr, uint32_t
121121
return FAILURE;
122122
}
123123

124-
ZVAL_COPY_OR_DUP(ret, &attr->argv[i]);
124+
// TODO: Named args.
125+
ZVAL_COPY_OR_DUP(ret, &attr->args[i].value);
125126

126127
if (Z_TYPE_P(ret) == IS_CONSTANT_AST) {
127128
if (SUCCESS != zval_update_constant_ex(ret, scope)) {
@@ -182,7 +183,10 @@ static zend_always_inline void free_attribute(zend_attribute *attr, int persiste
182183
zend_string_release(attr->lcname);
183184

184185
for (i = 0; i < attr->argc; i++) {
185-
zval_ptr_dtor(&attr->argv[i]);
186+
if (attr->args[i].name) {
187+
zend_string_release(attr->args[i].name);
188+
}
189+
zval_ptr_dtor(&attr->args[i].value);
186190
}
187191

188192
pefree(attr, persistent);
@@ -219,7 +223,8 @@ ZEND_API zend_attribute *zend_add_attribute(HashTable **attributes, zend_bool pe
219223

220224
/* Initialize arguments to avoid partial initialization in case of fatal errors. */
221225
for (uint32_t i = 0; i < argc; i++) {
222-
ZVAL_UNDEF(&attr->argv[i]);
226+
attr->args[i].name = NULL;
227+
ZVAL_UNDEF(&attr->args[i].value);
223228
}
224229

225230
zend_hash_next_index_insert_ptr(*attributes, attr);

Zend/zend_attributes.h

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -30,19 +30,25 @@
3030
#define ZEND_ATTRIBUTE_IS_REPEATABLE (1<<6)
3131
#define ZEND_ATTRIBUTE_FLAGS ((1<<7) - 1)
3232

33-
#define ZEND_ATTRIBUTE_SIZE(argc) (sizeof(zend_attribute) + sizeof(zval) * (argc) - sizeof(zval))
33+
#define ZEND_ATTRIBUTE_SIZE(argc) \
34+
(sizeof(zend_attribute) + sizeof(zend_attribute_arg) * (argc) - sizeof(zend_attribute_arg))
3435

3536
BEGIN_EXTERN_C()
3637

3738
extern ZEND_API zend_class_entry *zend_ce_attribute;
3839

40+
typedef struct {
41+
zend_string *name;
42+
zval value;
43+
} zend_attribute_arg;
44+
3945
typedef struct _zend_attribute {
4046
zend_string *name;
4147
zend_string *lcname;
4248
/* Parameter offsets start at 1, everything else uses 0. */
4349
uint32_t offset;
4450
uint32_t argc;
45-
zval argv[1];
51+
zend_attribute_arg args[1];
4652
} zend_attribute;
4753

4854
typedef struct _zend_internal_attribute {

Zend/zend_compile.c

Lines changed: 22 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5812,13 +5812,33 @@ static void zend_compile_attributes(HashTable **attributes, zend_ast *ast, uint3
58125812
if (args) {
58135813
ZEND_ASSERT(args->kind == ZEND_AST_ARG_LIST);
58145814

5815+
zend_bool uses_named_args = 0;
58155816
for (j = 0; j < args->children; j++) {
5816-
if (args->child[j]->kind == ZEND_AST_UNPACK) {
5817+
zend_ast *arg_ast = args->child[j];
5818+
5819+
if (arg_ast->kind == ZEND_AST_UNPACK) {
58175820
zend_error_noreturn(E_COMPILE_ERROR,
58185821
"Cannot use unpacking in attribute argument list");
58195822
}
58205823

5821-
zend_const_expr_to_zval(&attr->argv[j], args->child[j]);
5824+
if (arg_ast->kind == ZEND_AST_NAMED_ARG) {
5825+
attr->args[j].name = zend_string_copy(zend_ast_get_str(arg_ast->child[0]));
5826+
arg_ast = arg_ast->child[1];
5827+
uses_named_args = 1;
5828+
5829+
for (uint32_t k = 0; k < j; k++) {
5830+
if (attr->args[k].name &&
5831+
zend_string_equals(attr->args[k].name, attr->args[j].name)) {
5832+
zend_error_noreturn(E_COMPILE_ERROR, "Duplicate named parameter $%s",
5833+
ZSTR_VAL(attr->args[j].name));
5834+
}
5835+
}
5836+
} else if (uses_named_args) {
5837+
zend_error_noreturn(E_COMPILE_ERROR,
5838+
"Cannot use positional argument after named argument");
5839+
}
5840+
5841+
zend_const_expr_to_zval(&attr->args[j].value, arg_ast);
58225842
}
58235843
}
58245844
}

ext/opcache/zend_file_cache.c

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -415,7 +415,8 @@ static void zend_file_cache_serialize_attribute(zval *zv,
415415
SERIALIZE_STR(attr->lcname);
416416

417417
for (i = 0; i < attr->argc; i++) {
418-
zend_file_cache_serialize_zval(&attr->argv[i], script, info, buf);
418+
SERIALIZE_STR(attr->args[i].name);
419+
zend_file_cache_serialize_zval(&attr->args[i].value, script, info, buf);
419420
}
420421
}
421422

@@ -1178,7 +1179,8 @@ static void zend_file_cache_unserialize_attribute(zval *zv, zend_persistent_scri
11781179
UNSERIALIZE_STR(attr->lcname);
11791180

11801181
for (i = 0; i < attr->argc; i++) {
1181-
zend_file_cache_unserialize_zval(&attr->argv[i], script, buf);
1182+
UNSERIALIZE_STR(attr->args[i].name);
1183+
zend_file_cache_unserialize_zval(&attr->args[i].value, script, buf);
11821184
}
11831185
}
11841186

ext/opcache/zend_persist.c

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -278,7 +278,8 @@ static HashTable *zend_persist_attributes(HashTable *attributes)
278278
zend_accel_store_interned_string(copy->lcname);
279279

280280
for (i = 0; i < copy->argc; i++) {
281-
zend_persist_zval(&copy->argv[i]);
281+
zend_accel_store_interned_string(copy->args[i].name);
282+
zend_persist_zval(&copy->args[i].value);
282283
}
283284

284285
ZVAL_PTR(v, copy);

ext/opcache/zend_persist_calc.c

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -165,7 +165,8 @@ static void zend_persist_attributes_calc(HashTable *attributes)
165165
ADD_INTERNED_STRING(attr->lcname);
166166

167167
for (i = 0; i < attr->argc; i++) {
168-
zend_persist_zval_calc(&attr->argv[i]);
168+
ADD_INTERNED_STRING(attr->args[i].name);
169+
zend_persist_zval_calc(&attr->args[i].value);
169170
}
170171
} ZEND_HASH_FOREACH_END();
171172
}

ext/reflection/php_reflection.c

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6494,7 +6494,12 @@ ZEND_METHOD(ReflectionAttribute, getArguments)
64946494
RETURN_THROWS();
64956495
}
64966496

6497-
add_next_index_zval(return_value, &tmp);
6497+
if (attr->data->args[i].name) {
6498+
/* We ensured at compile-time that there are no duplicate parameter names. */
6499+
zend_hash_add_new(Z_ARRVAL_P(return_value), attr->data->args[i].name, &tmp);
6500+
} else {
6501+
add_next_index_zval(return_value, &tmp);
6502+
}
64986503
}
64996504
}
65006505
/* }}} */

0 commit comments

Comments
 (0)