@@ -2273,6 +2273,8 @@ class ClassInfo {
2273
2273
/** @var bool */
2274
2274
public $ isStrictProperties ;
2275
2275
/** @var bool */
2276
+ public $ allowsDynamicProperties ;
2277
+ /** @var bool */
2276
2278
public $ isNotSerializable ;
2277
2279
/** @var Name[] */
2278
2280
public $ extends ;
@@ -2305,6 +2307,7 @@ public function __construct(
2305
2307
?SimpleType $ enumBackingType ,
2306
2308
bool $ isDeprecated ,
2307
2309
bool $ isStrictProperties ,
2310
+ bool $ allowsDynamicProperties ,
2308
2311
bool $ isNotSerializable ,
2309
2312
array $ extends ,
2310
2313
array $ implements ,
@@ -2321,6 +2324,7 @@ public function __construct(
2321
2324
$ this ->enumBackingType = $ enumBackingType ;
2322
2325
$ this ->isDeprecated = $ isDeprecated ;
2323
2326
$ this ->isStrictProperties = $ isStrictProperties ;
2327
+ $ this ->allowsDynamicProperties = $ allowsDynamicProperties ;
2324
2328
$ this ->isNotSerializable = $ isNotSerializable ;
2325
2329
$ this ->extends = $ extends ;
2326
2330
$ this ->implements = $ implements ;
@@ -2409,6 +2413,10 @@ function (Name $item) {
2409
2413
$ code .= $ property ->getDeclaration ($ allConstInfos );
2410
2414
}
2411
2415
2416
+ if ($ this ->allowsDynamicProperties ) {
2417
+ $ code .= "\tzend_add_class_attribute(class_entry, zend_ce_allow_dynamic_properties->name, 0); \n" ;
2418
+ }
2419
+
2412
2420
if ($ attributeInitializationCode = generateAttributeInitialization ($ this ->funcInfos , $ this ->cond )) {
2413
2421
$ code .= "\n" . $ attributeInitializationCode ;
2414
2422
}
@@ -2452,6 +2460,10 @@ private function getFlagsAsString(): string
2452
2460
$ flags [] = "ZEND_ACC_NO_DYNAMIC_PROPERTIES " ;
2453
2461
}
2454
2462
2463
+ if ($ this ->allowsDynamicProperties ) {
2464
+ $ flags [] = "ZEND_ACC_ALLOW_DYNAMIC_PROPERTIES " ;
2465
+ }
2466
+
2455
2467
if ($ this ->isNotSerializable ) {
2456
2468
$ flags [] = "ZEND_ACC_NOT_SERIALIZABLE " ;
2457
2469
}
@@ -3273,6 +3285,7 @@ function parseClass(
3273
3285
$ isDeprecated = false ;
3274
3286
$ isStrictProperties = false ;
3275
3287
$ isNotSerializable = false ;
3288
+ $ allowsDynamicProperties = false ;
3276
3289
3277
3290
if ($ comment ) {
3278
3291
$ tags = parseDocComment ($ comment );
@@ -3289,6 +3302,22 @@ function parseClass(
3289
3302
}
3290
3303
}
3291
3304
3305
+ foreach ($ class ->attrGroups as $ attrGroup ) {
3306
+ foreach ($ attrGroup ->attrs as $ attr ) {
3307
+ switch ($ attr ->name ->toCodeString ()) {
3308
+ case '\\AllowDynamicProperties ' :
3309
+ $ allowsDynamicProperties = true ;
3310
+ break ;
3311
+ default :
3312
+ throw new Exception ("Unhandled attribute {$ attr ->name ->toCodeString ()}. " );
3313
+ }
3314
+ }
3315
+ }
3316
+
3317
+ if ($ isStrictProperties && $ allowsDynamicProperties ) {
3318
+ throw new Exception ("A class may not have '@strict-properties' and '#[ \\AllowDynamicProperties]' at the same time. " );
3319
+ }
3320
+
3292
3321
$ extends = [];
3293
3322
$ implements = [];
3294
3323
@@ -3319,6 +3348,7 @@ function parseClass(
3319
3348
? SimpleType::fromNode ($ class ->scalarType ) : null ,
3320
3349
$ isDeprecated ,
3321
3350
$ isStrictProperties ,
3351
+ $ allowsDynamicProperties ,
3322
3352
$ isNotSerializable ,
3323
3353
$ extends ,
3324
3354
$ implements ,
0 commit comments