diff --git a/build/gen_stub.php b/build/gen_stub.php index 75f4b12f95721..d1034e9a07116 100755 --- a/build/gen_stub.php +++ b/build/gen_stub.php @@ -945,6 +945,17 @@ public function getDeclarationName(): string } } +class EnumCaseName { + public /* readonly */ Name $enum; + public /* readonly */ string $case; + + public function __construct(Name $enum, string $case) + { + $this->enum = $enum; + $this->case = $case; + } +} + interface FunctionOrMethodName { public function getDeclaration(): string; public function getArgInfoName(): string; @@ -3286,17 +3297,19 @@ protected function addModifiersToFieldSynopsis(DOMDocument $doc, DOMElement $fie } class EnumCaseInfo { - private /* readonly */ string $name; + private /* readonly */ EnumCaseName $name; private /* readonly */ ?Expr $value; + private /* readonly */ ?string $valueString; - public function __construct(string $name, ?Expr $value) { + public function __construct(EnumCaseName $name, ?Expr $value, ?string $valueString) { $this->name = $name; $this->value = $value; + $this->valueString = $valueString; } /** @param array $allConstInfos */ public function getDeclaration(array $allConstInfos): string { - $escapedName = addslashes($this->name); + $escapedName = addslashes($this->name->case); if ($this->value === null) { $code = "\n\tzend_enum_add_case_cstr(class_entry, \"$escapedName\", NULL);\n"; } else { @@ -3309,6 +3322,60 @@ public function getDeclaration(array $allConstInfos): string { return $code; } + + /** @param array $allConstInfos */ + public function getEnumSynopsisItemElement(DOMDocument $doc, array $allConstInfos, int $indentationLevel): DOMElement + { + $indentation = str_repeat(" ", $indentationLevel); + + $itemElement = $doc->createElement("enumitem"); + + $identifierElement = $doc->createElement("enumidentifier", $this->name->case); + $identifierElement->setAttribute("linkend", $this->getEnumSynopsisLinkend()); + + $itemElement->appendChild(new DOMText("\n$indentation ")); + $itemElement->appendChild($identifierElement); + + $valueString = $this->getEnumSynopsisValueString($allConstInfos); + if ($valueString) { + $itemElement->appendChild(new DOMText("\n$indentation ")); + $valueElement = $doc->createElement("enumvalue", $valueString); + $itemElement->appendChild($valueElement); + } + + $itemElement->appendChild(new DOMText("\n$indentation")); + + return $itemElement; + } + + /** @param array $allConstInfos */ + public function getEnumSynopsisLinkend(): string + { + $className = str_replace(["\\", "_"], ["-", "-"], $this->name->enum->toLowerString()); + + return "$className.cases." . strtolower(str_replace("_", "-", trim($this->name->case, "_"))); + } + + /** @param array $allConstInfos */ + public function getEnumSynopsisValueString(array $allConstInfos): ?string + { + if ($this->value === null) { + return null; + } + + $value = EvaluatedValue::createFromExpression($this->value, null, null, $allConstInfos); + if ($value->isUnknownConstValue) { + return null; + } + + if ($value->originatingConsts) { + return implode("\n", array_map(function (ConstInfo $const) use ($allConstInfos) { + return $const->getFieldSynopsisValueString($allConstInfos); + }, $value->originatingConsts)); + } + + return $this->valueString; + } } // Instances of AttributeInfo are immutable and do not need to be cloned @@ -3748,8 +3815,12 @@ public function getClassSynopsisDocument(array $classMap, array $allConstInfos): * @param array $allConstInfos */ public function getClassSynopsisElement(DOMDocument $doc, array $classMap, array $allConstInfos): ?DOMElement { - $classSynopsis = $doc->createElement("classsynopsis"); - $classSynopsis->setAttribute("class", $this->type === "interface" ? "interface" : "class"); + if ($this->type === "enum") { + $classSynopsis = $doc->createElement("enumsynopsis"); + } else { + $classSynopsis = $doc->createElement("classsynopsis"); + $classSynopsis->setAttribute("class", $this->type === "interface" ? "interface" : "class"); + } $namespace = $this->getNamespace(); if ($namespace) { @@ -3769,108 +3840,120 @@ public function getClassSynopsisElement(DOMDocument $doc, array $classMap, array $classSynopsisIndentation = str_repeat(" ", $classSynopsisIndentationLevel); } - $exceptionOverride = $this->type === "class" && $this->isException($classMap) ? "exception" : null; - $ooElement = self::createOoElement($doc, $this, $exceptionOverride, true, null, $classSynopsisIndentationLevel + 1); - if (!$ooElement) { - return null; - } $classSynopsis->appendChild(new DOMText("\n$classSynopsisIndentation ")); - $classSynopsis->appendChild($ooElement); - foreach ($this->extends as $k => $parent) { - $parentInfo = $classMap[$parent->toString()] ?? null; - if ($parentInfo === null) { - throw new Exception("Missing parent class " . $parent->toString()); - } + if ($this->type === "enum") { + $enumName = $doc->createElement("enumname", $this->getClassName()); + $classSynopsis->appendChild($enumName); - $ooElement = self::createOoElement( - $doc, - $parentInfo, - null, - false, - $k === 0 ? "extends" : null, - $classSynopsisIndentationLevel + 1 - ); + foreach ($this->enumCaseInfos as $enumCaseInfo) { + $classSynopsis->appendChild(new DOMText("\n$classSynopsisIndentation ")); + $enumItemElement = $enumCaseInfo->getEnumSynopsisItemElement($doc, $allConstInfos, $classSynopsisIndentationLevel + 1); + $classSynopsis->appendChild($enumItemElement); + } + } else { + $exceptionOverride = $this->type === "class" && $this->isException($classMap) ? "exception" : null; + $ooElement = self::createOoElement($doc, $this, $exceptionOverride, true, null, $classSynopsisIndentationLevel + 1); if (!$ooElement) { return null; } - - $classSynopsis->appendChild(new DOMText("\n\n$classSynopsisIndentation ")); $classSynopsis->appendChild($ooElement); - } - foreach ($this->implements as $k => $interface) { - $interfaceInfo = $classMap[$interface->toString()] ?? null; - if (!$interfaceInfo) { - throw new Exception("Missing implemented interface " . $interface->toString()); + foreach ($this->extends as $k => $parent) { + $parentInfo = $classMap[$parent->toString()] ?? null; + if ($parentInfo === null) { + throw new Exception("Missing parent class " . $parent->toString()); + } + + $ooElement = self::createOoElement( + $doc, + $parentInfo, + null, + false, + $k === 0 ? "extends" : null, + $classSynopsisIndentationLevel + 1 + ); + if (!$ooElement) { + return null; + } + + $classSynopsis->appendChild(new DOMText("\n\n$classSynopsisIndentation ")); + $classSynopsis->appendChild($ooElement); } - $ooElement = self::createOoElement($doc, $interfaceInfo, null, false, $k === 0 ? "implements" : null, $classSynopsisIndentationLevel + 1); - if (!$ooElement) { - return null; + foreach ($this->implements as $k => $interface) { + $interfaceInfo = $classMap[$interface->toString()] ?? null; + if (!$interfaceInfo) { + throw new Exception("Missing implemented interface " . $interface->toString()); + } + + $ooElement = self::createOoElement($doc, $interfaceInfo, null, false, $k === 0 ? "implements" : null, $classSynopsisIndentationLevel + 1); + if (!$ooElement) { + return null; + } + $classSynopsis->appendChild(new DOMText("\n\n$classSynopsisIndentation ")); + $classSynopsis->appendChild($ooElement); } - $classSynopsis->appendChild(new DOMText("\n\n$classSynopsisIndentation ")); - $classSynopsis->appendChild($ooElement); - } - /** @var array $parentsWithInheritedConstants */ - $parentsWithInheritedConstants = []; - /** @var array $parentsWithInheritedProperties */ - $parentsWithInheritedProperties = []; - /** @var array $parentsWithInheritedMethods */ - $parentsWithInheritedMethods = []; + /** @var array $parentsWithInheritedConstants */ + $parentsWithInheritedConstants = []; + /** @var array $parentsWithInheritedProperties */ + $parentsWithInheritedProperties = []; + /** @var array $parentsWithInheritedMethods */ + $parentsWithInheritedMethods = []; - $this->collectInheritedMembers( - $parentsWithInheritedConstants, - $parentsWithInheritedProperties, - $parentsWithInheritedMethods, - $this->hasConstructor(), - $classMap - ); + $this->collectInheritedMembers( + $parentsWithInheritedConstants, + $parentsWithInheritedProperties, + $parentsWithInheritedMethods, + $this->hasConstructor(), + $classMap + ); - $this->appendInheritedMemberSectionToClassSynopsis( - $doc, - $classSynopsis, - $parentsWithInheritedConstants, - "&Constants;", - "&InheritedConstants;", - $classSynopsisIndentationLevel + 1 - ); + $this->appendInheritedMemberSectionToClassSynopsis( + $doc, + $classSynopsis, + $parentsWithInheritedConstants, + "&Constants;", + "&InheritedConstants;", + $classSynopsisIndentationLevel + 1 + ); - if (!empty($this->constInfos)) { - $classSynopsis->appendChild(new DOMText("\n\n$classSynopsisIndentation ")); - $classSynopsisInfo = $doc->createElement("classsynopsisinfo", "&Constants;"); - $classSynopsisInfo->setAttribute("role", "comment"); - $classSynopsis->appendChild($classSynopsisInfo); + if (!empty($this->constInfos)) { + $classSynopsis->appendChild(new DOMText("\n\n$classSynopsisIndentation ")); + $classSynopsisInfo = $doc->createElement("classsynopsisinfo", "&Constants;"); + $classSynopsisInfo->setAttribute("role", "comment"); + $classSynopsis->appendChild($classSynopsisInfo); - foreach ($this->constInfos as $constInfo) { - $classSynopsis->appendChild(new DOMText("\n$classSynopsisIndentation ")); - $fieldSynopsisElement = $constInfo->getFieldSynopsisElement($doc, $allConstInfos, $classSynopsisIndentationLevel + 1); - $classSynopsis->appendChild($fieldSynopsisElement); + foreach ($this->constInfos as $constInfo) { + $classSynopsis->appendChild(new DOMText("\n$classSynopsisIndentation ")); + $fieldSynopsisElement = $constInfo->getFieldSynopsisElement($doc, $allConstInfos, $classSynopsisIndentationLevel + 1); + $classSynopsis->appendChild($fieldSynopsisElement); + } } - } - if (!empty($this->propertyInfos)) { - $classSynopsis->appendChild(new DOMText("\n\n$classSynopsisIndentation ")); - $classSynopsisInfo = $doc->createElement("classsynopsisinfo", "&Properties;"); - $classSynopsisInfo->setAttribute("role", "comment"); - $classSynopsis->appendChild($classSynopsisInfo); + if (!empty($this->propertyInfos)) { + $classSynopsis->appendChild(new DOMText("\n\n$classSynopsisIndentation ")); + $classSynopsisInfo = $doc->createElement("classsynopsisinfo", "&Properties;"); + $classSynopsisInfo->setAttribute("role", "comment"); + $classSynopsis->appendChild($classSynopsisInfo); - foreach ($this->propertyInfos as $propertyInfo) { - $classSynopsis->appendChild(new DOMText("\n$classSynopsisIndentation ")); - $fieldSynopsisElement = $propertyInfo->getFieldSynopsisElement($doc, $allConstInfos, $classSynopsisIndentationLevel + 1); - $classSynopsis->appendChild($fieldSynopsisElement); + foreach ($this->propertyInfos as $propertyInfo) { + $classSynopsis->appendChild(new DOMText("\n$classSynopsisIndentation ")); + $fieldSynopsisElement = $propertyInfo->getFieldSynopsisElement($doc, $allConstInfos, $classSynopsisIndentationLevel + 1); + $classSynopsis->appendChild($fieldSynopsisElement); + } } - } - $this->appendInheritedMemberSectionToClassSynopsis( - $doc, - $classSynopsis, - $parentsWithInheritedProperties, - "&Properties;", - "&InheritedProperties;", - $classSynopsisIndentationLevel + 1 - ); + $this->appendInheritedMemberSectionToClassSynopsis( + $doc, + $classSynopsis, + $parentsWithInheritedProperties, + "&Properties;", + "&InheritedProperties;", + $classSynopsisIndentationLevel + 1 + ); + } if (!empty($this->funcInfos)) { $classSynopsis->appendChild(new DOMText("\n\n$classSynopsisIndentation ")); @@ -4491,7 +4574,10 @@ private function handleStatements(array $stmts, PrettyPrinterAbstract $prettyPri ); } else if ($classStmt instanceof Stmt\EnumCase) { $enumCaseInfos[] = new EnumCaseInfo( - $classStmt->name->toString(), $classStmt->expr); + new EnumCaseName($className, $classStmt->name->toString()), + $classStmt->expr, + $classStmt->expr ? $prettyPrinter->prettyPrintExpr($classStmt->expr) : null, + ); } else { throw new Exception("Not implemented {$classStmt->getType()}"); }