1 diff --git a/build/gen_stub.php b/build/gen_stub.php
2 index 5f74d26dbc..71265c12fa 100755
3 --- a/build/gen_stub.php
4 +++ b/build/gen_stub.php
5 @@ -733,10 +733,6 @@ class ArgInfo {
7 private function setTypes(?Type $type, ?Type $phpDocType): void
9 - if ($phpDocType !== null && Type::equals($type, $phpDocType)) {
10 - throw new Exception('PHPDoc param type "' . $phpDocType->__toString() . '" is unnecessary');
14 $this->phpDocType = $phpDocType;
16 @@ -793,7 +789,7 @@ class FunctionName implements FunctionOrMethodName {
19 public function getDeclarationName(): string {
20 - return $this->name->getLast();
21 + return strtr($this->name->toString(), "\\", "_");
24 public function getDeclaration(): string {
25 @@ -910,10 +906,6 @@ class ReturnInfo {
27 private function setTypes(?Type $type, ?Type $phpDocType, bool $tentativeReturnType): void
29 - if ($phpDocType !== null && Type::equals($type, $phpDocType)) {
30 - throw new Exception('PHPDoc return type "' . $phpDocType->__toString() . '" is unnecessary');
34 $this->phpDocType = $phpDocType;
35 $this->tentativeReturnType = $tentativeReturnType;
36 @@ -1152,8 +1144,8 @@ class FuncInfo {
38 // Render A\B as "A\\B" in C strings for namespaces
40 - "\tZEND_NS_FE(\"%s\", %s, %s)\n",
41 - addslashes($namespace), $declarationName, $this->getArgInfoName());
42 + "\tZEND_NS_RAW_FENTRY(\"%s\", \"%s\", ZEND_FN(%s), %s, 0)\n",
43 + addslashes($namespace), substr((string)$this->name, strlen($namespace)+1), $declarationName, $this->getArgInfoName());
45 return sprintf("\tZEND_FE(%s, %s)\n", $declarationName, $this->getArgInfoName());
47 @@ -1398,6 +1390,8 @@ class PropertyInfo
48 public $defaultValueString;
50 public $isDocReadonly;
51 + /** @var string|null */
54 public function __construct(
56 @@ -1406,7 +1400,8 @@ class PropertyInfo
59 ?string $defaultValueString,
61 + bool $isDocReadonly,
65 $this->flags = $flags;
66 @@ -1415,6 +1410,7 @@ class PropertyInfo
67 $this->defaultValue = $defaultValue;
68 $this->defaultValueString = $defaultValueString;
69 $this->isDocReadonly = $isDocReadonly;
70 + $this->link = $link;
73 public function discardInfoForOldPhpVersions(): void {
74 @@ -1540,9 +1536,13 @@ class PropertyInfo
75 $fieldsynopsisElement->appendChild(new DOMText("\n "));
76 $fieldsynopsisElement->appendChild($this->getFieldSynopsisType()->getTypeForDoc($doc));
78 - $className = str_replace("\\", "-", $this->name->class->toLowerString());
79 + $className = str_replace(["\\", "_"], ["-", "-"], $this->name->class->toLowerString());
80 $varnameElement = $doc->createElement("varname", $this->name->property);
81 - $varnameElement->setAttribute("linkend", "$className.props." . strtolower($this->name->property));
83 + $varnameElement->setAttribute("linkend", $this->link);
85 + $varnameElement->setAttribute("linkend", "$className.props." . strtolower(str_replace("_", "-", $this->name->property)));
87 $fieldsynopsisElement->appendChild(new DOMText("\n "));
88 $fieldsynopsisElement->appendChild($varnameElement);
90 @@ -1558,14 +1558,14 @@ class PropertyInfo
93 private function getFieldSynopsisType(): Type {
98 if ($this->phpDocType) {
99 return $this->phpDocType;
103 + return $this->type;
106 throw new Exception("A property must have a type");
109 @@ -1608,7 +1608,7 @@ class EnumCaseInfo {
110 public function getDeclaration(): string {
111 $escapedName = addslashes($this->name);
112 if ($this->value === null) {
113 - $code = "\n\tzend_enum_add_case_cstr(class_entry, \"$escapedName\", NULL);\n";
114 + $code = "\tzend_enum_add_case_cstr(class_entry, \"$escapedName\", NULL);\n";
116 $evaluator = new ConstExprEvaluator(function (Expr $expr) {
117 throw new Exception("Enum case $this->name has an unsupported value");
118 @@ -2005,11 +2005,11 @@ class ClassInfo {
121 public static function getClassSynopsisFilename(Name $name): string {
122 - return strtolower(implode('-', $name->parts));
123 + return strtolower(str_replace("_", "-", implode('-', $name->parts)));
126 public static function getClassSynopsisReference(Name $name): string {
127 - return "class." . strtolower(implode('-', $name->parts));
128 + return "class." . self::getClassSynopsisFilename($name);
132 @@ -2019,10 +2019,6 @@ class ClassInfo {
134 private function collectInheritedMembers(array &$parentsWithInheritedProperties, array &$parentsWithInheritedMethods, array $classMap): void
136 - if ($this->type !== "class") {
140 foreach ($this->extends as $parent) {
141 $parentInfo = $classMap[$parent->toString()] ?? null;
143 @@ -2033,7 +2029,7 @@ class ClassInfo {
144 $parentsWithInheritedProperties[$parent->toString()] = $parent;
147 - if (!empty($parentInfo->funcInfos) && !isset($parentsWithInheritedMethods[$parent->toString()])) {
148 + if (!isset($parentsWithInheritedMethods[$parent->toString()]) && $parentInfo->hasMethods()) {
149 $parentsWithInheritedMethods[$parent->toString()] = $parent;
152 @@ -2369,13 +2365,14 @@ function parseFunctionLike(
153 function parseProperty(
156 - Stmt\PropertyProperty $property,
157 + Stmt\PropertyProperty|Node\Param $property,
159 ?DocComment $comment,
160 PrettyPrinterAbstract $prettyPrinter
163 $isDocReadonly = false;
167 $tags = parseDocComment($comment);
168 @@ -2384,6 +2381,8 @@ function parseProperty(
169 $phpDocType = $tag->getType();
170 } elseif ($tag->name === 'readonly') {
171 $isDocReadonly = true;
172 + } elseif ($tag->name === 'link') {
173 + $link = $tag->value;
177 @@ -2404,14 +2403,25 @@ function parseProperty(
181 + $default = $property->default;
182 + if ($property instanceof Node\Param) {
183 + $name = $property->var->name;
184 + if ($property->flags & Stmt\Class_::MODIFIER_READONLY) {
188 + $name = $property->name;
191 return new PropertyInfo(
192 - new PropertyName($class, $property->name->__toString()),
193 + new PropertyName($class, (string) $name),
196 $phpDocType ? Type::fromString($phpDocType) : null,
197 - $property->default,
198 - $property->default ? $prettyPrinter->prettyPrintExpr($property->default) : null,
201 + $default ? $prettyPrinter->prettyPrintExpr($default) : null,
207 @@ -2594,6 +2604,20 @@ function handleStatements(FileInfo $fileInfo, array $stmts, PrettyPrinterAbstrac
211 + if ($classStmt->name->toString() === "__construct") {
212 + foreach ($classStmt->params as $param) {
213 + if ($param->flags) {
214 + $propertyInfos[] = parseProperty(
219 + $param->getDocComment(),
225 } else if ($classStmt instanceof Stmt\EnumCase) {
226 $enumCaseInfos[] = new EnumCaseInfo(
227 $classStmt->name->toString(), $classStmt->expr);
228 @@ -2821,7 +2845,9 @@ function generateArgInfoCode(FileInfo $fileInfo, string $stubHash): string {
231 $generatedFunctionDeclarations[$key] = true;
232 - return $fileInfo->declarationPrefix . $funcInfo->getDeclaration();
233 + if ($decl = $funcInfo->getDeclaration()) {
234 + return $fileInfo->declarationPrefix . $decl;
239 @@ -2986,12 +3012,14 @@ function replaceClassSynopses(string $targetDirectory, array $classMap): array
240 $replacedXml = preg_replace(
242 "/REPLACED-ENTITY-([A-Za-z0-9._{}%-]+?;)/",
243 + "/<phpdoc:(classref|exceptionref)\s+xmlns:phpdoc=\"([a-z0-9.:\/]+)\"\s+xmlns=\"([a-z0-9.:\/]+)\"\s+xml:id=\"([a-z0-9._-]+)\"\s*>/i",
244 "/<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",
245 "/<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",
246 "/<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",
250 + "<phpdoc:$1 xml:id=\"$4\" xmlns:phpdoc=\"$2\" xmlns=\"$3\">",
251 "<phpdoc:$1 xml:id=\"$5\" xmlns:phpdoc=\"$2\" xmlns=\"$3\" xmlns:xi=\"$4\">",
252 "<phpdoc:$1 xml:id=\"$6\" xmlns:phpdoc=\"$2\" xmlns=\"$3\" xmlns:xlink=\"$4\" xmlns:xi=\"$5\">",
253 "<phpdoc:$1 xml:id=\"$6\" xmlns:phpdoc=\"$5\" xmlns=\"$2\" xmlns:xlink=\"$3\" xmlns:xi=\"$4\">",
254 @@ -3265,7 +3293,7 @@ function initPhpParser() {
257 $isInitialized = true;
258 - $version = "4.13.0";
259 + $version = "4.13.2";
260 $phpParserDir = __DIR__ . "/PHP-Parser-$version";
261 if (!is_dir($phpParserDir)) {
262 installPhpParser($version, $phpParserDir);