--- /dev/null
+diff --git a/build/gen_stub.php b/build/gen_stub.php
+index 5f74d26dbc..71265c12fa 100755
+--- a/build/gen_stub.php
++++ b/build/gen_stub.php
+@@ -733,10 +733,6 @@ class ArgInfo {
+
+ private function setTypes(?Type $type, ?Type $phpDocType): void
+ {
+- if ($phpDocType !== null && Type::equals($type, $phpDocType)) {
+- throw new Exception('PHPDoc param type "' . $phpDocType->__toString() . '" is unnecessary');
+- }
+-
+ $this->type = $type;
+ $this->phpDocType = $phpDocType;
+ }
+@@ -793,7 +789,7 @@ class FunctionName implements FunctionOrMethodName {
+ }
+
+ public function getDeclarationName(): string {
+- return $this->name->getLast();
++ return strtr($this->name->toString(), "\\", "_");
+ }
+
+ public function getDeclaration(): string {
+@@ -910,10 +906,6 @@ class ReturnInfo {
+
+ private function setTypes(?Type $type, ?Type $phpDocType, bool $tentativeReturnType): void
+ {
+- if ($phpDocType !== null && Type::equals($type, $phpDocType)) {
+- throw new Exception('PHPDoc return type "' . $phpDocType->__toString() . '" is unnecessary');
+- }
+-
+ $this->type = $type;
+ $this->phpDocType = $phpDocType;
+ $this->tentativeReturnType = $tentativeReturnType;
+@@ -1152,8 +1144,8 @@ class FuncInfo {
+ if ($namespace) {
+ // Render A\B as "A\\B" in C strings for namespaces
+ return sprintf(
+- "\tZEND_NS_FE(\"%s\", %s, %s)\n",
+- addslashes($namespace), $declarationName, $this->getArgInfoName());
++ "\tZEND_NS_RAW_FENTRY(\"%s\", \"%s\", ZEND_FN(%s), %s, 0)\n",
++ addslashes($namespace), substr((string)$this->name, strlen($namespace)+1), $declarationName, $this->getArgInfoName());
+ } else {
+ return sprintf("\tZEND_FE(%s, %s)\n", $declarationName, $this->getArgInfoName());
+ }
+@@ -1398,6 +1390,8 @@ class PropertyInfo
+ public $defaultValueString;
+ /** @var bool */
+ public $isDocReadonly;
++ /** @var string|null */
++ public $link;
+
+ public function __construct(
+ PropertyName $name,
+@@ -1406,7 +1400,8 @@ class PropertyInfo
+ ?Type $phpDocType,
+ ?Expr $defaultValue,
+ ?string $defaultValueString,
+- bool $isDocReadonly
++ bool $isDocReadonly,
++ ?string $link
+ ) {
+ $this->name = $name;
+ $this->flags = $flags;
+@@ -1415,6 +1410,7 @@ class PropertyInfo
+ $this->defaultValue = $defaultValue;
+ $this->defaultValueString = $defaultValueString;
+ $this->isDocReadonly = $isDocReadonly;
++ $this->link = $link;
+ }
+
+ public function discardInfoForOldPhpVersions(): void {
+@@ -1540,9 +1536,13 @@ class PropertyInfo
+ $fieldsynopsisElement->appendChild(new DOMText("\n "));
+ $fieldsynopsisElement->appendChild($this->getFieldSynopsisType()->getTypeForDoc($doc));
+
+- $className = str_replace("\\", "-", $this->name->class->toLowerString());
++ $className = str_replace(["\\", "_"], ["-", "-"], $this->name->class->toLowerString());
+ $varnameElement = $doc->createElement("varname", $this->name->property);
+- $varnameElement->setAttribute("linkend", "$className.props." . strtolower($this->name->property));
++ if ($this->link) {
++ $varnameElement->setAttribute("linkend", $this->link);
++ } else {
++ $varnameElement->setAttribute("linkend", "$className.props." . strtolower(str_replace("_", "-", $this->name->property)));
++ }
+ $fieldsynopsisElement->appendChild(new DOMText("\n "));
+ $fieldsynopsisElement->appendChild($varnameElement);
+
+@@ -1558,14 +1558,14 @@ class PropertyInfo
+ }
+
+ private function getFieldSynopsisType(): Type {
+- if ($this->type) {
+- return $this->type;
+- }
+-
+ if ($this->phpDocType) {
+ return $this->phpDocType;
+ }
+
++ if ($this->type) {
++ return $this->type;
++ }
++
+ throw new Exception("A property must have a type");
+ }
+
+@@ -1608,7 +1608,7 @@ class EnumCaseInfo {
+ public function getDeclaration(): string {
+ $escapedName = addslashes($this->name);
+ if ($this->value === null) {
+- $code = "\n\tzend_enum_add_case_cstr(class_entry, \"$escapedName\", NULL);\n";
++ $code = "\tzend_enum_add_case_cstr(class_entry, \"$escapedName\", NULL);\n";
+ } else {
+ $evaluator = new ConstExprEvaluator(function (Expr $expr) {
+ throw new Exception("Enum case $this->name has an unsupported value");
+@@ -2005,11 +2005,11 @@ class ClassInfo {
+ }
+
+ public static function getClassSynopsisFilename(Name $name): string {
+- return strtolower(implode('-', $name->parts));
++ return strtolower(str_replace("_", "-", implode('-', $name->parts)));
+ }
+
+ public static function getClassSynopsisReference(Name $name): string {
+- return "class." . strtolower(implode('-', $name->parts));
++ return "class." . self::getClassSynopsisFilename($name);
+ }
+
+ /**
+@@ -2019,10 +2019,6 @@ class ClassInfo {
+ */
+ private function collectInheritedMembers(array &$parentsWithInheritedProperties, array &$parentsWithInheritedMethods, array $classMap): void
+ {
+- if ($this->type !== "class") {
+- return;
+- }
+-
+ foreach ($this->extends as $parent) {
+ $parentInfo = $classMap[$parent->toString()] ?? null;
+ if (!$parentInfo) {
+@@ -2033,7 +2029,7 @@ class ClassInfo {
+ $parentsWithInheritedProperties[$parent->toString()] = $parent;
+ }
+
+- if (!empty($parentInfo->funcInfos) && !isset($parentsWithInheritedMethods[$parent->toString()])) {
++ if (!isset($parentsWithInheritedMethods[$parent->toString()]) && $parentInfo->hasMethods()) {
+ $parentsWithInheritedMethods[$parent->toString()] = $parent;
+ }
+
+@@ -2369,13 +2365,14 @@ function parseFunctionLike(
+ function parseProperty(
+ Name $class,
+ int $flags,
+- Stmt\PropertyProperty $property,
++ Stmt\PropertyProperty|Node\Param $property,
+ ?Node $type,
+ ?DocComment $comment,
+ PrettyPrinterAbstract $prettyPrinter
+ ): PropertyInfo {
+ $phpDocType = null;
+ $isDocReadonly = false;
++ $link = null;
+
+ if ($comment) {
+ $tags = parseDocComment($comment);
+@@ -2384,6 +2381,8 @@ function parseProperty(
+ $phpDocType = $tag->getType();
+ } elseif ($tag->name === 'readonly') {
+ $isDocReadonly = true;
++ } elseif ($tag->name === 'link') {
++ $link = $tag->value;
+ }
+ }
+ }
+@@ -2404,14 +2403,25 @@ function parseProperty(
+ }
+ }
+
++ $default = $property->default;
++ if ($property instanceof Node\Param) {
++ $name = $property->var->name;
++ if ($property->flags & Stmt\Class_::MODIFIER_READONLY) {
++ $default = null;
++ }
++ } else {
++ $name = $property->name;
++ }
++
+ return new PropertyInfo(
+- new PropertyName($class, $property->name->__toString()),
++ new PropertyName($class, (string) $name),
+ $flags,
+ $propertyType,
+ $phpDocType ? Type::fromString($phpDocType) : null,
+- $property->default,
+- $property->default ? $prettyPrinter->prettyPrintExpr($property->default) : null,
+- $isDocReadonly
++ $default,
++ $default ? $prettyPrinter->prettyPrintExpr($default) : null,
++ $isDocReadonly,
++ $link
+ );
+ }
+
+@@ -2594,6 +2604,20 @@ function handleStatements(FileInfo $fileInfo, array $stmts, PrettyPrinterAbstrac
+ $classStmt,
+ $cond
+ );
++ if ($classStmt->name->toString() === "__construct") {
++ foreach ($classStmt->params as $param) {
++ if ($param->flags) {
++ $propertyInfos[] = parseProperty(
++ $className,
++ $param->flags,
++ $param,
++ $param->type,
++ $param->getDocComment(),
++ $prettyPrinter
++ );
++ }
++ }
++ }
+ } else if ($classStmt instanceof Stmt\EnumCase) {
+ $enumCaseInfos[] = new EnumCaseInfo(
+ $classStmt->name->toString(), $classStmt->expr);
+@@ -2821,7 +2845,9 @@ function generateArgInfoCode(FileInfo $fileInfo, string $stubHash): string {
+ }
+
+ $generatedFunctionDeclarations[$key] = true;
+- return $fileInfo->declarationPrefix . $funcInfo->getDeclaration();
++ if ($decl = $funcInfo->getDeclaration()) {
++ return $fileInfo->declarationPrefix . $decl;
++ }
+ }
+ );
+
+@@ -2986,12 +3012,14 @@ function replaceClassSynopses(string $targetDirectory, array $classMap): array
+ $replacedXml = preg_replace(
+ [
+ "/REPLACED-ENTITY-([A-Za-z0-9._{}%-]+?;)/",
++ "/<phpdoc:(classref|exceptionref)\s+xmlns:phpdoc=\"([a-z0-9.:\/]+)\"\s+xmlns=\"([a-z0-9.:\/]+)\"\s+xml:id=\"([a-z0-9._-]+)\"\s*>/i",
+ "/<phpdoc:(classref|exceptionref)\s+xmlns:phpdoc=\"([a-z0-9.:\/]+)\"\s+xmlns=\"([a-z0-9.:\/]+)\"\s+xmlns:xi=\"([a-z0-9.:\/]+)\"\s+xml:id=\"([a-z0-9._-]+)\"\s*>/i",
+ "/<phpdoc:(classref|exceptionref)\s+xmlns:phpdoc=\"([a-z0-9.:\/]+)\"\s+xmlns=\"([a-z0-9.:\/]+)\"\s+xmlns:xlink=\"([a-z0-9.:\/]+)\"\s+xmlns:xi=\"([a-z0-9.:\/]+)\"\s+xml:id=\"([a-z0-9._-]+)\"\s*>/i",
+ "/<phpdoc:(classref|exceptionref)\s+xmlns=\"([a-z0-9.:\/]+)\"\s+xmlns:xlink=\"([a-z0-9.:\/]+)\"\s+xmlns:xi=\"([a-z0-9.:\/]+)\"\s+xmlns:phpdoc=\"([a-z0-9.:\/]+)\"\s+xml:id=\"([a-z0-9._-]+)\"\s*>/i",
+ ],
+ [
+ "&$1",
++ "<phpdoc:$1 xml:id=\"$4\" xmlns:phpdoc=\"$2\" xmlns=\"$3\">",
+ "<phpdoc:$1 xml:id=\"$5\" xmlns:phpdoc=\"$2\" xmlns=\"$3\" xmlns:xi=\"$4\">",
+ "<phpdoc:$1 xml:id=\"$6\" xmlns:phpdoc=\"$2\" xmlns=\"$3\" xmlns:xlink=\"$4\" xmlns:xi=\"$5\">",
+ "<phpdoc:$1 xml:id=\"$6\" xmlns:phpdoc=\"$5\" xmlns=\"$2\" xmlns:xlink=\"$3\" xmlns:xi=\"$4\">",
+@@ -3265,7 +3293,7 @@ function initPhpParser() {
+ }
+
+ $isInitialized = true;
+- $version = "4.13.0";
++ $version = "4.13.2";
+ $phpParserDir = __DIR__ . "/PHP-Parser-$version";
+ if (!is_dir($phpParserDir)) {
+ installPhpParser($version, $phpParserDir);