Skip to content
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
260 changes: 173 additions & 87 deletions build/gen_stub.php
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}
}
Comment on lines +948 to +957
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why do you need this? I don't understand why the enum case needs to know about the name of the enum it's part of?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

All VariableLike classes do the same (check ClassConstName and PropertyName) because the class/enum name is needed in multiple places:

  • ClassConstName and PropertyName have a __toString() method where the classname is needed
  • The class/enum name is needed for all these classes to be able to generate the linkend attribute (check the ClassConstName::getFieldSynopsisDefaultLinkend() and the EnumCaseName::getEnumSynopsisLinkend() methods)

That said, the enum name could be stored in the EnumCaseInfo directly instead of EnumCaseName, but I figured it's a good idea to follow the other name classes.


interface FunctionOrMethodName {
public function getDeclaration(): string;
public function getArgInfoName(): string;
Expand Down Expand Up @@ -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<string, ConstInfo> $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 {
Expand All @@ -3309,6 +3322,60 @@ public function getDeclaration(array $allConstInfos): string {

return $code;
}

/** @param array<string, ConstInfo> $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<string, ConstInfo> $allConstInfos */
public function getEnumSynopsisLinkend(): string
{
$className = str_replace(["\\", "_"], ["-", "-"], $this->name->enum->toLowerString());

return "$className.cases." . strtolower(str_replace("_", "-", trim($this->name->case, "_")));
}

/** @param array<string, ConstInfo> $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
Expand Down Expand Up @@ -3748,8 +3815,12 @@ public function getClassSynopsisDocument(array $classMap, array $allConstInfos):
* @param array<string, ConstInfo> $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) {
Expand All @@ -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<string, Name> $parentsWithInheritedConstants */
$parentsWithInheritedConstants = [];
/** @var array<string, Name> $parentsWithInheritedProperties */
$parentsWithInheritedProperties = [];
/** @var array<int, array{name: Name, types: int[]}> $parentsWithInheritedMethods */
$parentsWithInheritedMethods = [];
/** @var array<string, Name> $parentsWithInheritedConstants */
$parentsWithInheritedConstants = [];
/** @var array<string, Name> $parentsWithInheritedProperties */
$parentsWithInheritedProperties = [];
/** @var array<int, array{name: Name, types: int[]}> $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 "));
Expand Down Expand Up @@ -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()}");
}
Expand Down
Loading