Skip to content

Commit abb5725

Browse files
committed
Allow for arbitrary (class) attributes in stubs
This can be easily extended to other types of attributes.
1 parent 1a3d836 commit abb5725

File tree

2 files changed

+42
-15
lines changed

2 files changed

+42
-15
lines changed

Zend/zend_builtin_functions_arginfo.h

Lines changed: 3 additions & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

build/gen_stub.php

Lines changed: 39 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -2272,8 +2272,8 @@ class ClassInfo {
22722272
public $isDeprecated;
22732273
/** @var bool */
22742274
public $isStrictProperties;
2275-
/** @var bool */
2276-
public $allowsDynamicProperties;
2275+
/** @var array{0: string, 1: \PhpParser\Node\Arg[]}[] */
2276+
public $attributes;
22772277
/** @var bool */
22782278
public $isNotSerializable;
22792279
/** @var Name[] */
@@ -2292,6 +2292,7 @@ class ClassInfo {
22922292
public $cond;
22932293

22942294
/**
2295+
* @param array{0: string, 1: \PhpParser\Node\Arg[]}[] $attributes
22952296
* @param Name[] $extends
22962297
* @param Name[] $implements
22972298
* @param ConstInfo[] $constInfos
@@ -2307,7 +2308,7 @@ public function __construct(
23072308
?SimpleType $enumBackingType,
23082309
bool $isDeprecated,
23092310
bool $isStrictProperties,
2310-
bool $allowsDynamicProperties,
2311+
array $attributes,
23112312
bool $isNotSerializable,
23122313
array $extends,
23132314
array $implements,
@@ -2324,7 +2325,7 @@ public function __construct(
23242325
$this->enumBackingType = $enumBackingType;
23252326
$this->isDeprecated = $isDeprecated;
23262327
$this->isStrictProperties = $isStrictProperties;
2327-
$this->allowsDynamicProperties = $allowsDynamicProperties;
2328+
$this->attributes = $attributes;
23282329
$this->isNotSerializable = $isNotSerializable;
23292330
$this->extends = $extends;
23302331
$this->implements = $implements;
@@ -2413,9 +2414,7 @@ function (Name $item) {
24132414
$code .= $property->getDeclaration($allConstInfos);
24142415
}
24152416

2416-
if ($this->allowsDynamicProperties) {
2417-
$code .= "\tzend_add_class_attribute(class_entry, zend_ce_allow_dynamic_properties->name, 0);\n";
2418-
}
2417+
$code .= generateAttributesCode($this->attributes, "zend_add_class_attribute(class_entry", "class_$escapedName", $allConstInfos);
24192418

24202419
if ($attributeInitializationCode = generateAttributeInitialization($this->funcInfos, $this->cond)) {
24212420
$code .= "\n" . $attributeInitializationCode;
@@ -2460,8 +2459,10 @@ private function getFlagsAsString(): string
24602459
$flags[] = "ZEND_ACC_NO_DYNAMIC_PROPERTIES";
24612460
}
24622461

2463-
if ($this->allowsDynamicProperties) {
2464-
$flags[] = "ZEND_ACC_ALLOW_DYNAMIC_PROPERTIES";
2462+
foreach ($this->attributes as list($name)) {
2463+
if ($name === "AllowDynamicProperties") {
2464+
$flags[] = "ZEND_ACC_ALLOW_DYNAMIC_PROPERTIES";
2465+
}
24652466
}
24662467

24672468
if ($this->isNotSerializable) {
@@ -2979,6 +2980,30 @@ public function getVariableName(): string {
29792980
}
29802981
}
29812982

2983+
/**
2984+
* @param array{0: string, 1: \PhpParser\Node\Arg[]}[] $attributes
2985+
* @param iterable<ConstInfo> $allConstInfos
2986+
*/
2987+
function generateAttributesCode(array $attributes, string $invocation, string $nameSuffix, iterable $allConstInfos): string {
2988+
$code = "";
2989+
foreach ($attributes as list($name, $args)) {
2990+
$escapedAttributeName = strtr($name, '\\', '_');
2991+
$code .= "\tzend_string *attribute_name_{$escapedAttributeName}_$nameSuffix = zend_string_init(\"" . addcslashes($name, "\\") . "\", " . strlen($name) . ", 1);\n";
2992+
$code .= "\t" . ($args ? "zend_attribute *attribute_{$escapedAttributeName}_$nameSuffix = " : "") . "$invocation, attribute_name_{$escapedAttributeName}_$nameSuffix, " . count($args) . ");\n";
2993+
$code .= "\tzend_string_release(attribute_name_{$escapedAttributeName}_$nameSuffix);\n";
2994+
foreach ($args as $i => $arg) {
2995+
$value = EvaluatedValue::createFromExpression($arg->value, null, null, $allConstInfos);
2996+
$zvalName = "attribute_{$escapedAttributeName}_class_{$escapedName}_arg$i";
2997+
$code .= $value->initializeZval($zvalName, $allConstInfos);
2998+
$code .= "\tZVAL_COPY_VALUE(&attribute_{$escapedAttributeName}_$nameSuffix.args[$i].value, &$zvalName);\n";
2999+
if ($arg->name) {
3000+
$code .= "\tattribute_{$escapedAttributeName}_$nameSuffix.args[$i].name = zend_string_init(\"{$arg->name->name}\", " . strlen($arg->name->name) . ", 1);\n";
3001+
}
3002+
}
3003+
}
3004+
return $code;
3005+
}
3006+
29823007
/** @return DocCommentTag[] */
29833008
function parseDocComment(DocComment $comment): array {
29843009
$commentText = substr($comment->getText(), 2, -2);
@@ -3286,6 +3311,7 @@ function parseClass(
32863311
$isStrictProperties = false;
32873312
$isNotSerializable = false;
32883313
$allowsDynamicProperties = false;
3314+
$attributes = [];
32893315

32903316
if ($comment) {
32913317
$tags = parseDocComment($comment);
@@ -3304,12 +3330,11 @@ function parseClass(
33043330

33053331
foreach ($class->attrGroups as $attrGroup) {
33063332
foreach ($attrGroup->attrs as $attr) {
3307-
switch ($attr->name->toCodeString()) {
3308-
case '\\AllowDynamicProperties':
3333+
$attributes[] = [$attr->name->toString(), $attr->args];
3334+
switch ($attr->name->toString()) {
3335+
case 'AllowDynamicProperties':
33093336
$allowsDynamicProperties = true;
33103337
break;
3311-
default:
3312-
throw new Exception("Unhandled attribute {$attr->name->toCodeString()}.");
33133338
}
33143339
}
33153340
}
@@ -3348,7 +3373,7 @@ function parseClass(
33483373
? SimpleType::fromNode($class->scalarType) : null,
33493374
$isDeprecated,
33503375
$isStrictProperties,
3351-
$allowsDynamicProperties,
3376+
$attributes,
33523377
$isNotSerializable,
33533378
$extends,
33543379
$implements,

0 commit comments

Comments
 (0)