diff --git a/build/gen_stub.php b/build/gen_stub.php index 3bf9fb995dce4..6c2f058655ea6 100755 --- a/build/gen_stub.php +++ b/build/gen_stub.php @@ -2217,10 +2217,10 @@ public function enterNode(Node $expr) $isUnknownConstValue = false; $evaluator = new ConstExprEvaluator( - static function (Expr $expr) use ($allConstInfos, &$isUnknownConstValue) { - // $expr is a ConstFetch with a name of a C macro here + function (Expr $expr) use ($allConstInfos, &$isUnknownConstValue) { + // $expr is a ConstFetch with a name of a C macro here. Class constants are not supported yet if (!$expr instanceof Expr\ConstFetch) { - throw new Exception($this->getVariableTypeName() . " " . $this->name->__toString() . " has an unsupported value"); + throw new Exception("Cannot evaluate expression"); } $constName = $expr->name->__toString(); @@ -2379,7 +2379,7 @@ abstract protected function getFieldSynopsisDefaultLinkend(): string; abstract protected function getFieldSynopsisName(): string; /** @param array $allConstInfos */ - abstract protected function getFieldSynopsisValueString(array $allConstInfos): ?string; + abstract public function getFieldSynopsisValueString(array $allConstInfos): ?string; abstract public function discardInfoForOldPhpVersions(?int $minimumPhpVersionIdCompatibility): void; @@ -2604,7 +2604,7 @@ protected function getFieldSynopsisName(): string } /** @param array $allConstInfos */ - protected function getFieldSynopsisValueString(array $allConstInfos): ?string + public function getFieldSynopsisValueString(array $allConstInfos): ?string { $value = EvaluatedValue::createFromExpression($this->value, null, $this->cValue, $allConstInfos); if ($value->isUnknownConstValue) { @@ -2939,7 +2939,7 @@ protected function getFieldSynopsisName(): string } /** @param array $allConstInfos */ - protected function getFieldSynopsisValueString(array $allConstInfos): ?string + public function getFieldSynopsisValueString(array $allConstInfos): ?string { return $this->defaultValueString; } @@ -3061,10 +3061,12 @@ public function __clone() class EnumCaseInfo { public string $name; public ?Expr $value; + public ?string $valueString; - public function __construct(string $name, ?Expr $value) { + public function __construct(string $name, ?Expr $value, ?string $valueString) { $this->name = $name; $this->value = $value; + $this->valueString = $valueString; } /** @param array $allConstInfos */ @@ -3082,6 +3084,51 @@ public function getDeclaration(array $allConstInfos): string { return $code; } + + /** @param array $allConstInfos */ + public function getEnumItemElement(DOMDocument $doc, array $allConstInfos): DOMElement + { + $enumItemElement = $doc->createElement("enumitem"); + + $enumItemElement->appendChild(new DOMText("\n ")); + $enumIdentifierElement = $doc->createElement("enumidentifier"); + $enumIdentifierElement->appendChild(new DOMText($this->name)); + $enumItemElement->appendChild($enumIdentifierElement); + + $valueString = $this->getEnumValueString($allConstInfos); + if ($valueString) { + $enumValueElement = $doc->createElement("enumvalue"); + $enumItemElement->appendChild(new DOMText("\n ")); + $initializerElement = $doc->createElement("initializer", $valueString); + $enumValueElement->appendChild($initializerElement); + $enumItemElement->appendChild($enumValueElement); + } + + $enumItemElement->appendChild(new DOMText("\n ")); + + return $enumItemElement; + } + + /** @param array $allConstInfos */ + protected function getEnumValueString(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; + } } class AttributeInfo { @@ -3468,17 +3515,36 @@ public function getClassSynopsisDocument(array $classMap, array $allConstInfos): * @param array $allConstInfos */ public function getClassSynopsisElement(DOMDocument $doc, array $classMap, array $allConstInfos): ?DOMElement { + $isEnum = $this->type === "enum"; + + $classSynopsis = $doc->createElement($isEnum ? "enumsynopsis" : "classsynopsis"); + $synopsisInfoName = $isEnum ? "synopsisinfo" : "classsynopsisinfo"; - $classSynopsis = $doc->createElement("classsynopsis"); - $classSynopsis->setAttribute("class", $this->type === "interface" ? "interface" : "class"); + if (!$isEnum) { + $classSynopsis->setAttribute("class", $this->type); + } $exceptionOverride = $this->type === "class" && $this->isException($classMap) ? "exception" : null; + $classSynopsis->appendChild(new DOMText("\n ")); + $ooElement = self::createOoElement($doc, $this, $exceptionOverride, true, null, 4); - if (!$ooElement) { - return null; + if ($ooElement) { + $classSynopsis->appendChild($ooElement); + } + + if ($isEnum) { + $enumNameElement = $doc->createElement("enumname"); + $enumNameElement->appendChild(new DOMText($this->name->toString())); + $classSynopsis->appendChild($enumNameElement); + + if ($this->enumBackingType) { + $classSynopsis->appendChild(new DOMText("\n ")); + $enumNameElement = $doc->createElement("modifier"); + $enumNameElement->setAttribute("role", "enum_backing_type"); + $enumNameElement->appendChild(new DOMText($this->enumBackingType->name)); + $classSynopsis->appendChild($enumNameElement); + } } - $classSynopsis->appendChild(new DOMText("\n ")); - $classSynopsis->appendChild($ooElement); foreach ($this->extends as $k => $parent) { $parentInfo = $classMap[$parent->toString()] ?? null; @@ -3502,6 +3568,11 @@ public function getClassSynopsisElement(DOMDocument $doc, array $classMap, array $classSynopsis->appendChild($ooElement); } + // Enums implicitly implement either UnitEnum or BackEnum. This way inherited methods can be displayed. + if ($isEnum) { + $this->implements[] = new Name\FullyQualified($this->enumBackingType ? "BackedEnum" : "UnitEnum"); + } + foreach ($this->implements as $k => $interface) { $interfaceInfo = $classMap[$interface->toString()] ?? null; if (!$interfaceInfo) { @@ -3535,13 +3606,14 @@ public function getClassSynopsisElement(DOMDocument $doc, array $classMap, array $doc, $classSynopsis, $parentsWithInheritedConstants, + $synopsisInfoName, "&Constants;", "&InheritedConstants;" ); if (!empty($this->constInfos)) { $classSynopsis->appendChild(new DOMText("\n\n ")); - $classSynopsisInfo = $doc->createElement("classsynopsisinfo", "&Constants;"); + $classSynopsisInfo = $doc->createElement($synopsisInfoName, "&Constants;"); $classSynopsisInfo->setAttribute("role", "comment"); $classSynopsis->appendChild($classSynopsisInfo); @@ -3552,9 +3624,22 @@ public function getClassSynopsisElement(DOMDocument $doc, array $classMap, array } } + if (!empty($this->enumCaseInfos)) { + $classSynopsis->appendChild(new DOMText("\n\n ")); + $classSynopsisInfo = $doc->createElement($synopsisInfoName, "&EnumCases;"); + $classSynopsisInfo->setAttribute("role", "comment"); + $classSynopsis->appendChild($classSynopsisInfo); + + foreach ($this->enumCaseInfos as $enumCaseInfo) { + $classSynopsis->appendChild(new DOMText("\n ")); + $enumItemElement = $enumCaseInfo->getEnumItemElement($doc, $allConstInfos); + $classSynopsis->appendChild($enumItemElement); + } + } + if (!empty($this->propertyInfos)) { $classSynopsis->appendChild(new DOMText("\n\n ")); - $classSynopsisInfo = $doc->createElement("classsynopsisinfo", "&Properties;"); + $classSynopsisInfo = $doc->createElement($synopsisInfoName, "&Properties;"); $classSynopsisInfo->setAttribute("role", "comment"); $classSynopsis->appendChild($classSynopsisInfo); @@ -3569,13 +3654,14 @@ public function getClassSynopsisElement(DOMDocument $doc, array $classMap, array $doc, $classSynopsis, $parentsWithInheritedProperties, + $synopsisInfoName, "&Properties;", "&InheritedProperties;" ); if (!empty($this->funcInfos)) { $classSynopsis->appendChild(new DOMText("\n\n ")); - $classSynopsisInfo = $doc->createElement("classsynopsisinfo", "&Methods;"); + $classSynopsisInfo = $doc->createElement($synopsisInfoName, "&Methods;"); $classSynopsisInfo->setAttribute("role", "comment"); $classSynopsis->appendChild($classSynopsisInfo); @@ -3612,7 +3698,7 @@ public function getClassSynopsisElement(DOMDocument $doc, array $classMap, array if (!empty($parentsWithInheritedMethods)) { $classSynopsis->appendChild(new DOMText("\n\n ")); - $classSynopsisInfo = $doc->createElement("classsynopsisinfo", "&InheritedMethods;"); + $classSynopsisInfo = $doc->createElement($synopsisInfoName, "&InheritedMethods;"); $classSynopsisInfo->setAttribute("role", "comment"); $classSynopsis->appendChild($classSynopsisInfo); @@ -3650,8 +3736,7 @@ private static function createOoElement( ): ?DOMElement { $indentation = str_repeat(" ", $indentationLevel); - if ($classInfo->type !== "class" && $classInfo->type !== "interface") { - echo "Class synopsis generation is not implemented for " . $classInfo->type . "\n"; + if ($classInfo->type === "enum") { return null; } @@ -3745,6 +3830,8 @@ private function collectInheritedMembers( ); } + $isEnum = $this->type === "enum"; + foreach ($this->implements as $parent) { $parentInfo = $classMap[$parent->toString()] ?? null; if (!$parentInfo) { @@ -3758,13 +3845,25 @@ private function collectInheritedMembers( $unusedParentsWithInheritedProperties = []; $unusedParentsWithInheritedMethods = []; - $parentInfo->collectInheritedMembers( - $parentsWithInheritedConstants, - $unusedParentsWithInheritedProperties, - $unusedParentsWithInheritedMethods, - $hasConstructor, - $classMap - ); + if ($isEnum) { + $parentInfo->collectInheritedMembers( + $parentsWithInheritedConstants, + $unusedParentsWithInheritedProperties, + // We only want to collect inherited methods in case of enums + $parentsWithInheritedMethods, + $hasConstructor, + $classMap + ); + } else { + $parentInfo->collectInheritedMembers( + $parentsWithInheritedConstants, + $unusedParentsWithInheritedProperties, + // We only want to collect inherited methods in case of enums + $unusedParentsWithInheritedMethods, + $hasConstructor, + $classMap + ); + } } } @@ -3886,14 +3985,14 @@ public function __clone() /** * @param Name[] $parents */ - private function appendInheritedMemberSectionToClassSynopsis(DOMDocument $doc, DOMElement $classSynopsis, array $parents, string $label, string $inheritedLabel): void + private function appendInheritedMemberSectionToClassSynopsis(DOMDocument $doc, DOMElement $classSynopsis, array $parents, string $synopsisInfoName, string $label, string $inheritedLabel): void { if (empty($parents)) { return; } $classSynopsis->appendChild(new DOMText("\n\n ")); - $classSynopsisInfo = $doc->createElement("classsynopsisinfo", "$inheritedLabel"); + $classSynopsisInfo = $doc->createElement($synopsisInfoName, "$inheritedLabel"); $classSynopsisInfo->setAttribute("role", "comment"); $classSynopsis->appendChild($classSynopsisInfo); @@ -4738,7 +4837,10 @@ function handleStatements(FileInfo $fileInfo, array $stmts, PrettyPrinterAbstrac ); } else if ($classStmt instanceof Stmt\EnumCase) { $enumCaseInfos[] = new EnumCaseInfo( - $classStmt->name->toString(), $classStmt->expr); + $classStmt->name->toString(), + $classStmt->expr, + $classStmt->expr ? $prettyPrinter->prettyPrintExpr($classStmt->expr) : null + ); } else { throw new Exception("Not implemented {$classStmt->getType()}"); } diff --git a/classsynopses/random-intervalboundary.xml b/classsynopses/random-intervalboundary.xml new file mode 100644 index 0000000000000..cfe3156993b0a --- /dev/null +++ b/classsynopses/random-intervalboundary.xml @@ -0,0 +1,41 @@ + + + Random\IntervalBoundary + int + + + implements + BackedEnum + + + &Constants; + + const + int + Random\IntervalBoundary::ClosedC + 2 + + + &EnumCases; + + ClosedOpen + 1 + + + ClosedClosed + 2 + + + OpenClosed + 3 + + + OpenOpen + 4 + + + &InheritedMethods; + + + + diff --git a/ext/random/random.stub.php b/ext/random/random.stub.php index 1b40294e60f41..49d615765a9e1 100644 --- a/ext/random/random.stub.php +++ b/ext/random/random.stub.php @@ -155,11 +155,12 @@ public function __serialize(): array {} public function __unserialize(array $data): void {} } - enum IntervalBoundary { - case ClosedOpen; - case ClosedClosed; - case OpenClosed; - case OpenOpen; + enum IntervalBoundary: int { + const int ClosedC = 2; + case ClosedOpen = 1; + case ClosedClosed = IntervalBoundary::ClosedC; + case OpenClosed = 3; + case OpenOpen = 4; } /** diff --git a/ext/random/random_arginfo.h b/ext/random/random_arginfo.h index 82df476ed125a..96c4e93e8e4e8 100644 --- a/ext/random/random_arginfo.h +++ b/ext/random/random_arginfo.h @@ -1,5 +1,5 @@ /* This is a generated file, edit the .stub.php file instead. - * Stub hash: 35cb16abb3392bd257a43cc675cad4f5af5549c1 */ + * Stub hash: e676dc318d339a4ae6539c494e642bfceae90738 */ ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_lcg_value, 0, 0, IS_DOUBLE, 0) ZEND_END_ARG_INFO() @@ -338,15 +338,29 @@ static zend_class_entry *register_class_Random_Randomizer(void) static zend_class_entry *register_class_Random_IntervalBoundary(void) { - zend_class_entry *class_entry = zend_register_internal_enum("Random\\IntervalBoundary", IS_UNDEF, class_Random_IntervalBoundary_methods); + zend_class_entry *class_entry = zend_register_internal_enum("Random\\IntervalBoundary", IS_LONG, class_Random_IntervalBoundary_methods); - zend_enum_add_case_cstr(class_entry, "ClosedOpen", NULL); + zval const_ClosedC_value; + ZVAL_LONG(&const_ClosedC_value, 2); + zend_string *const_ClosedC_name = zend_string_init_interned("ClosedC", sizeof("ClosedC") - 1, 1); + zend_declare_typed_class_constant(class_entry, const_ClosedC_name, &const_ClosedC_value, ZEND_ACC_PUBLIC, NULL, (zend_type) ZEND_TYPE_INIT_MASK(MAY_BE_LONG)); + zend_string_release(const_ClosedC_name); - zend_enum_add_case_cstr(class_entry, "ClosedClosed", NULL); + zval enum_case_ClosedOpen_value; + ZVAL_LONG(&enum_case_ClosedOpen_value, 1); + zend_enum_add_case_cstr(class_entry, "ClosedOpen", &enum_case_ClosedOpen_value); - zend_enum_add_case_cstr(class_entry, "OpenClosed", NULL); + zval enum_case_ClosedClosed_value; + ZVAL_LONG(&enum_case_ClosedClosed_value, 2); + zend_enum_add_case_cstr(class_entry, "ClosedClosed", &enum_case_ClosedClosed_value); - zend_enum_add_case_cstr(class_entry, "OpenOpen", NULL); + zval enum_case_OpenClosed_value; + ZVAL_LONG(&enum_case_OpenClosed_value, 3); + zend_enum_add_case_cstr(class_entry, "OpenClosed", &enum_case_OpenClosed_value); + + zval enum_case_OpenOpen_value; + ZVAL_LONG(&enum_case_OpenOpen_value, 4); + zend_enum_add_case_cstr(class_entry, "OpenOpen", &enum_case_OpenOpen_value); return class_entry; }