Skip to content

gen_stub.php support for @cvalue DocBlock annotation for backed enum cases #19426

@MarcelBolten

Description

@MarcelBolten

Description

I am trying to use a *.stub.php file to create an *_arginfo.h file which will bind third-party c enum values to a PHP backed enum.

My naïve approach was:

enum MyEnum: int {
    /**
     * @var int
     * @cvalue MY_C_ENUM_VALUE1
     */
    case Value1 = UNKNOWN;
}

but that did not work.

A rather verbose and unintuitive workaround is:

enum MyEnum: int {
    /**
     * @var int
     * @cvalue MY_C_ENUM_VALUE1
     */
    private const VALUE1 = UNKNOWN
    case Value1 = MyEnum::VALUE1;
}

Ideally, the type is inferred from the enum declaration but using @var works for me.

I'm using the 8.4 branch at commit 5295fc0 for my tests.

@kocsismate included because you are a volunteer for gen_stub.php.

Please find below a very simple implementation of what I try to achieve. While it does what I want it is not extensively tested and has probably side effects. If this is worth a PR please let me know. Any guidance on missing bits and pieces is very welcome. Obviously, tests have to be added.

---
 build/gen_stub.php | 62 +++++++++++++++++++++++++++++++++++++++++++---
 1 file changed, 58 insertions(+), 4 deletions(-)

diff --git a/build/gen_stub.php b/build/gen_stub.php
index f1d8b43862e..219c9af2cc4 100755
--- a/build/gen_stub.php
+++ b/build/gen_stub.php
@@ -3096,10 +3096,19 @@ public function __clone()
 class EnumCaseInfo {
     public string $name;
     public ?Expr $value;
+    public ?SimpleType $constType;
+    public ?string $cConstName;

-    public function __construct(string $name, ?Expr $value) {
+    public function __construct(
+        string $name,
+        ?Expr $value,
+        ?SimpleType $constType,
+        ?string $cConstName
+    ) {
         $this->name = $name;
         $this->value = $value;
+        $this->constType = $constType;
+        $this->cConstName = $cConstName;
     }

     /** @param array<string, ConstInfo> $allConstInfos */
@@ -3108,7 +3117,12 @@ public function getDeclaration(array $allConstInfos): string {
         if ($this->value === null) {
             $code = "\n\tzend_enum_add_case_cstr(class_entry, \"$escapedName\", NULL);\n";
         } else {
-            $value = EvaluatedValue::createFromExpression($this->value, null, null, $allConstInfos);
+            $value = EvaluatedValue::createFromExpression(
+                $this->value,
+                $this->constType,
+                $this->cConstName,
+                $allConstInfos
+            );

             $zvalName = "enum_case_{$escapedName}_value";
             $code = "\n" . $value->initializeZval($zvalName);
@@ -4444,6 +4458,43 @@ function parseConstLike(
     );
 }

+function parseEnumCase(
+    string $name,
+    ?Expr $value,
+    array $comments
+): EnumCaseInfo {
+    $cConstName = null;
+    $phpDocType = null;
+    if ($comments) {
+        $tags = parseDocComments($comments);
+        foreach ($tags as $tag) {
+            if ($tag->name === 'var') {
+                $phpDocType = $tag->getType();
+            } elseif ($tag->name === 'cvalue') {
+                $cConstName = $tag->value;
+            }
+        }
+    }
+
+    $constPhpDocType = $phpDocType
+        ? SimpleType::fromString($phpDocType)
+        : null;
+
+    // Toto: infer type from enum declaration
+    if ($cConstName && $constPhpDocType
+        && !($constPhpDocType->name === 'int' || $constPhpDocType->name === 'string')
+    ) {
+        throw new Exception("Backed enums must be of type int or string");
+    }
+
+    return new EnumCaseInfo(
+        $name,
+        $value,
+        $constPhpDocType,
+        $cConstName
+    );
+}
+
 /**
  * @param array<int, array<int, AttributeGroup> $attributes
  */
@@ -4810,8 +4861,11 @@ function handleStatements(FileInfo $fileInfo, array $stmts, PrettyPrinterAbstrac
                         $fileInfo->getMinimumPhpVersionIdCompatibility()
                     );
                 } else if ($classStmt instanceof Stmt\EnumCase) {
-                    $enumCaseInfos[] = new EnumCaseInfo(
-                        $classStmt->name->toString(), $classStmt->expr);
+                    $enumCaseInfos[] = parseEnumCase(
+                        $classStmt->name->toString(),
+                        $classStmt->expr,
+                        $classStmt->getComments()
+                    );
                 } else {
                     throw new Exception("Not implemented {$classStmt->getType()}");
                 }

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions