From 0363dbfef466cd06fcc2a5a9e4dbdaad281da464 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?M=C3=A1t=C3=A9=20Kocsis?= Date: Mon, 4 Sep 2023 10:35:55 +0200 Subject: [PATCH] Add support for verifying and syncronizing predefined constants with the manual --- build/gen_stub.php | 444 +++++++++++++++++++++++++++++++-------------- 1 file changed, 310 insertions(+), 134 deletions(-) diff --git a/build/gen_stub.php b/build/gen_stub.php index 9fc26052c86..6605d2d2478 100755 --- a/build/gen_stub.php +++ b/build/gen_stub.php @@ -164,8 +164,8 @@ function extractStubHash(string $arginfoFile): ?string { class Context { public bool $forceParse = false; public bool $forceRegeneration = false; - /** @var iterable */ - public iterable $allConstInfos = []; + /** @var array */ + public array $allConstInfos = []; /** @var FileInfo[] */ public array $parsedFiles = []; } @@ -1604,20 +1604,20 @@ class EvaluatedValue public array $originatingConsts; /** - * @param iterable $allConstInfos + * @param array $allConstInfos */ - public static function createFromExpression(Expr $expr, ?SimpleType $constType, ?string $cConstName, iterable $allConstInfos): EvaluatedValue + public static function createFromExpression(Expr $expr, ?SimpleType $constType, ?string $cConstName, array $allConstInfos): EvaluatedValue { // This visitor replaces the PHP constants by C constants. It allows direct expansion of the compiled constants, e.g. later in the pretty printer. $visitor = new class($allConstInfos) extends PhpParser\NodeVisitorAbstract { /** @var iterable */ public array $visitedConstants = []; - /** @var iterable */ - public iterable $allConstInfos; + /** @var array */ + public array $allConstInfos; - /** @param iterable $allConstInfos */ - public function __construct(iterable $allConstInfos) + /** @param array $allConstInfos */ + public function __construct(array $allConstInfos) { $this->allConstInfos = $allConstInfos; } @@ -1639,11 +1639,10 @@ class EvaluatedValue return null; } - foreach ($this->allConstInfos as $const) { - if ($originatingConstName->equals($const->name)) { - $this->visitedConstants[] = $const; - return $const->getValue($this->allConstInfos)->expr; - } + $const = $this->allConstInfos[$originatingConstName->__toString()] ?? null; + if ($const !== null) { + $this->visitedConstants[] = $const; + return $const->getValue($this->allConstInfos)->expr; } } }; @@ -1813,10 +1812,8 @@ abstract class VariableLike abstract protected function getFieldSynopsisName(): string; - /** - * @param iterable $allConstInfos - */ - abstract protected function getFieldSynopsisValueString(iterable $allConstInfos): ?string; + /** @param array $allConstInfos */ + abstract protected function getFieldSynopsisValueString(array $allConstInfos): ?string; abstract public function discardInfoForOldPhpVersions(): void; @@ -1900,10 +1897,8 @@ abstract class VariableLike return $typeCode; } - /** - * @param iterable $allConstInfos - */ - public function getFieldSynopsisElement(DOMDocument $doc, iterable $allConstInfos): DOMElement + /** @param array $allConstInfos */ + public function getFieldSynopsisElement(DOMDocument $doc, array $allConstInfos): DOMElement { $fieldsynopsisElement = $doc->createElement("fieldsynopsis"); @@ -1974,6 +1969,7 @@ class ConstInfo extends VariableLike public ?string $valueString; public ?string $cond; public ?string $cValue; + public bool $isUndocumentable; /** * @var AttributeInfo[] $attributes @@ -1988,6 +1984,7 @@ class ConstInfo extends VariableLike bool $isDeprecated, ?string $cond, ?string $cValue, + bool $isUndocumentable, ?string $link, ?int $phpVersionIdMinimumCompatibility, array $attributes @@ -1998,13 +1995,12 @@ class ConstInfo extends VariableLike $this->isDeprecated = $isDeprecated; $this->cond = $cond; $this->cValue = $cValue; + $this->isUndocumentable = $isUndocumentable; parent::__construct($flags, $type, $phpDocType, $link, $phpVersionIdMinimumCompatibility, $attributes); } - /** - * @param iterable $allConstInfos - */ - public function getValue(iterable $allConstInfos): EvaluatedValue + /** @param array $allConstInfos */ + public function getValue(array $allConstInfos): EvaluatedValue { return EvaluatedValue::createFromExpression( $this->value, @@ -2036,10 +2032,8 @@ class ConstInfo extends VariableLike return $this->name->__toString(); } - /** - * @param iterable $allConstInfos - */ - protected function getFieldSynopsisValueString(iterable $allConstInfos): ?string + /** @param array $allConstInfos */ + protected function getFieldSynopsisValueString(array $allConstInfos): ?string { $value = EvaluatedValue::createFromExpression($this->value, null, $this->cValue, $allConstInfos); if ($value->isUnknownConstValue) { @@ -2055,6 +2049,26 @@ class ConstInfo extends VariableLike return $this->valueString; } + public function getPredefinedConstantTerm(DOMDocument $doc, int $indentationLevel): DOMElement { + $indentation = str_repeat(" ", $indentationLevel); + + $termElement = $doc->createElement("term"); + + $constantElement = $doc->createElement("constant"); + $constantElement->textContent = $this->name->__toString(); + + $typeElement = ($this->phpDocType ?? $this->type)->getTypeForDoc($doc); + $stubConstantType = $constantElement->textContent; + + $termElement->appendChild(new DOMText("\n$indentation ")); + $termElement->appendChild($constantElement); + $termElement->appendChild(new DOMText("\n$indentation (")); + $termElement->appendChild($typeElement); + $termElement->appendChild(new DOMText(")\n$indentation")); + + return $termElement; + } + public function discardInfoForOldPhpVersions(): void { $this->type = null; $this->flags &= ~Class_::MODIFIER_FINAL; @@ -2062,10 +2076,8 @@ class ConstInfo extends VariableLike $this->attributes = []; } - /** - * @param iterable $allConstInfos - */ - public function getDeclaration(iterable $allConstInfos): string + /** @param array $allConstInfos */ + public function getDeclaration(array $allConstInfos): string { $simpleType = ($this->phpDocType ?? $this->type)->tryToSimpleType(); if ($simpleType && $simpleType->name === "mixed") { @@ -2102,10 +2114,8 @@ class ConstInfo extends VariableLike return $code; } - /** - * @param iterable $allConstInfos - */ - private function getGlobalConstDeclaration(EvaluatedValue $value, iterable $allConstInfos): string + /** @param array $allConstInfos */ + private function getGlobalConstDeclaration(EvaluatedValue $value, array $allConstInfos): string { $constName = str_replace('\\', '\\\\', $this->name->__toString()); $constValue = $value->value; @@ -2141,10 +2151,8 @@ class ConstInfo extends VariableLike throw new Exception("Unimplemented constant type");} - /** - * @param iterable $allConstInfos - */ - private function getClassConstDeclaration(EvaluatedValue $value, iterable $allConstInfos): string + /** @param array $allConstInfos */ + private function getClassConstDeclaration(EvaluatedValue $value, array $allConstInfos): string { $constName = $this->name->getDeclarationName(); @@ -2326,10 +2334,8 @@ class PropertyInfo extends VariableLike return $this->name->getDeclarationName(); } - /** - * @param iterable $allConstInfos - */ - protected function getFieldSynopsisValueString(iterable $allConstInfos): ?string + /** @param array $allConstInfos */ + protected function getFieldSynopsisValueString(array $allConstInfos): ?string { return $this->defaultValueString; } @@ -2340,10 +2346,8 @@ class PropertyInfo extends VariableLike $this->attributes = []; } - /** - * @param iterable $allConstInfos - */ - public function getDeclaration(iterable $allConstInfos): string { + /** @param array $allConstInfos */ + public function getDeclaration(array $allConstInfos): string { $code = "\n"; $propertyName = $this->name->getDeclarationName(); @@ -2437,10 +2441,8 @@ class EnumCaseInfo { $this->value = $value; } - /** - * @param iterable $allConstInfos - */ - public function getDeclaration(iterable $allConstInfos): string { + /** @param array $allConstInfos */ + public function getDeclaration(array $allConstInfos): string { $escapedName = addslashes($this->name); if ($this->value === null) { $code = "\n\tzend_enum_add_case_cstr(class_entry, \"$escapedName\", NULL);\n"; @@ -2467,8 +2469,8 @@ class AttributeInfo { $this->args = $args; } - /** @param iterable $allConstInfos */ - public function generateCode(string $invocation, string $nameSuffix, iterable $allConstInfos, ?int $phpVersionIdMinimumCompatibility): string { + /** @param array $allConstInfos */ + public function generateCode(string $invocation, string $nameSuffix, array $allConstInfos, ?int $phpVersionIdMinimumCompatibility): string { $php82MinimumCompatibility = $phpVersionIdMinimumCompatibility === null || $phpVersionIdMinimumCompatibility >= PHP_82_VERSION_ID; /* see ZEND_KNOWN_STRINGS in Zend/strings.h */ $knowns = []; @@ -2574,10 +2576,8 @@ class ClassInfo { $this->isUndocumentable = $isUndocumentable; } - /** - * @param ConstInfo[] $allConstInfos - */ - public function getRegistration(iterable $allConstInfos): string + /** @param array $allConstInfos */ + public function getRegistration(array $allConstInfos): string { $params = []; foreach ($this->extends as $extends) { @@ -2789,10 +2789,10 @@ class ClassInfo { /** * @param array $classMap - * @param iterable $allConstInfos + * @param array $allConstInfos * @param iterable $allConstInfo */ - public function getClassSynopsisDocument(array $classMap, iterable $allConstInfos): ?string { + public function getClassSynopsisDocument(array $classMap, array $allConstInfos): ?string { $doc = new DOMDocument(); $doc->formatOutput = true; @@ -2808,9 +2808,9 @@ class ClassInfo { /** * @param array $classMap - * @param iterable $allConstInfos + * @param array $allConstInfos */ - public function getClassSynopsisElement(DOMDocument $doc, array $classMap, iterable $allConstInfos): ?DOMElement { + public function getClassSynopsisElement(DOMDocument $doc, array $classMap, array $allConstInfos): ?DOMElement { $classSynopsis = $doc->createElement("classsynopsis"); $classSynopsis->setAttribute("class", $this->type === "interface" ? "interface" : "class"); @@ -3266,14 +3266,18 @@ class FileInfo { } } - /** - * @return iterable - */ - public function getAllConstInfos(): iterable { - $result = $this->constInfos; + /** @return array */ + public function getAllConstInfos(): array { + $result = []; + + foreach ($this->constInfos as $constInfo) { + $result[$constInfo->name->__toString()] = $constInfo; + } foreach ($this->classInfos as $classInfo) { - $result = array_merge($result, $classInfo->constInfos); + foreach ($classInfo->constInfos as $constInfo) { + $result[$constInfo->name->__toString()] = $constInfo; + } } return $result; @@ -3562,6 +3566,7 @@ function parseConstLike( ?Node $type, ?DocComment $docComment, ?string $cond, + bool $isUndocumentable, ?int $phpVersionIdMinimumCompatibility, array $attributes ): ConstInfo { @@ -3578,6 +3583,8 @@ function parseConstLike( $deprecated = true; } elseif ($tag->name === 'cvalue') { $cValue = $tag->value; + } elseif ($tag->name === 'undocumentable') { + $isUndocumentable = true; } elseif ($tag->name === 'link') { $link = $tag->value; } @@ -3598,6 +3605,7 @@ function parseConstLike( $deprecated, $cond, $cValue, + $isUndocumentable, $link, $phpVersionIdMinimumCompatibility, $attributes @@ -3855,6 +3863,7 @@ function handleStatements(FileInfo $fileInfo, array $stmts, PrettyPrinterAbstrac null, $stmt->getDocComment(), $cond, + $fileInfo->isUndocumentable, $fileInfo->generateLegacyArginfoForPhpVersionId, [] ); @@ -3900,6 +3909,7 @@ function handleStatements(FileInfo $fileInfo, array $stmts, PrettyPrinterAbstrac $classStmt->type, $classStmt->getDocComment(), $cond, + $fileInfo->isUndocumentable, $fileInfo->generateLegacyArginfoForPhpVersionId, createAttributes($classStmt->attrGroups) ); @@ -4164,12 +4174,12 @@ function generateCodeWithConditions( } /** - * @param iterable $allConstInfos + * @param array $allConstInfos */ function generateArgInfoCode( string $stubFilenameWithoutExtension, FileInfo $fileInfo, - iterable $allConstInfos, + array $allConstInfos, string $stubHash ): string { $code = "/* This is a generated file, edit the .stub.php file instead.\n" @@ -4251,10 +4261,8 @@ function generateArgInfoCode( return $code; } -/** - * @param iterable $allConstInfos - */ -function generateClassEntryCode(FileInfo $fileInfo, iterable $allConstInfos): string { +/** @param array $allConstInfos */ +function generateClassEntryCode(FileInfo $fileInfo, array $allConstInfos): string { $code = ""; foreach ($fileInfo->classInfos as $class) { @@ -4292,10 +4300,8 @@ function generateFunctionEntries(?Name $className, array $funcInfos, ?string $co return $code; } -/** - * @param iterable $funcInfos - */ -function generateFunctionAttributeInitialization(iterable $funcInfos, iterable $allConstInfos, ?int $phpVersionIdMinimumCompatibility, ?string $parentCond = null): string { +/** @param iterable $funcInfos */ +function generateFunctionAttributeInitialization(iterable $funcInfos, array $allConstInfos, ?int $phpVersionIdMinimumCompatibility, ?string $parentCond = null): string { return generateCodeWithConditions( $funcInfos, "", @@ -4336,10 +4342,11 @@ function generateFunctionAttributeInitialization(iterable $funcInfos, iterable $ /** * @param iterable $constInfos + * @param array $allConstInfos */ function generateConstantAttributeInitialization( iterable $constInfos, - iterable $allConstInfos, + array $allConstInfos, ?int $phpVersionIdMinimumCompatibility, ?string $parentCond = null ): string { @@ -4366,10 +4373,11 @@ function generateConstantAttributeInitialization( /** * @param iterable $propertyInfos + * @param array $allConstInfos */ function generatePropertyAttributeInitialization( iterable $propertyInfos, - iterable $allConstInfos, + array $allConstInfos, ?int $phpVersionIdMinimumCompatibility ): string { $code = ""; @@ -4484,12 +4492,123 @@ function generateVersionDependentFlagCode(string $codeTemplate, array $flagsByPh return $result; } +/** + * @param array $constMap + * @param array $undocumentedConstMap + * @return array + */ +function replacePredefinedConstants(string $targetDirectory, array $constMap, array &$undocumentedConstMap): array { + /** @var array $documentedConstMap */ + $documentedConstMap = []; + /** @var array $predefinedConstants */ + $predefinedConstants = []; + + $it = new RecursiveIteratorIterator( + new RecursiveDirectoryIterator($targetDirectory), + RecursiveIteratorIterator::LEAVES_ONLY + ); + + foreach ($it as $file) { + $pathName = $file->getPathName(); + if (!preg_match('/constants\.xml$/i', $pathName)) { + continue; + } + + $xml = file_get_contents($pathName); + if ($xml === false) { + continue; + } + + if (stripos($xml, "formatOutput = false; + $doc->preserveWhiteSpace = true; + $doc->validateOnParse = true; + $success = $doc->loadXML($replacedXml); + if (!$success) { + echo "Failed opening $pathName\n"; + continue; + } + + $updated = false; + + foreach ($doc->getElementsByTagName("varlistentry") as $entry) { + if (!$entry instanceof DOMElement) { + continue; + } + + $list = $entry->getElementsByTagName("term"); + $manualTermElement = $list->item(0); + if (!$manualTermElement instanceof DOMElement) { + continue; + } + + $list = $manualTermElement->getElementsByTagName("constant"); + $manualConstantElement = $list->item(0); + if (!$manualConstantElement instanceof DOMElement) { + continue; + } + $manualConstantName = $manualConstantElement->textContent; + + $stubConstant = $constMap[$manualConstantName] ?? null; + if ($stubConstant === null) { + continue; + } + + $documentedConstMap[$manualConstantName] = $manualConstantName; + + if ($entry->firstChild instanceof DOMText) { + $indentationLevel = strlen(str_replace("\n", "", $entry->firstChild->textContent)); + } else { + $indentationLevel = 3; + } + $newTermElement = $stubConstant->getPredefinedConstantTerm($doc, $indentationLevel); + + if ($manualTermElement->textContent === $newTermElement->textContent) { + continue; + } + + $manualTermElement->parentNode->replaceChild($newTermElement, $manualTermElement); + $updated = true; + } + + if ($updated) { + $replacedXml = $doc->saveXML(); + + $replacedXml = preg_replace( + [ + "/REPLACED-ENTITY-([A-Za-z0-9._{}%-]+?;)/", + '//i', + '//i', + ], + [ + "&$1", + "", + "", + ], + $replacedXml + ); + + $predefinedConstants[$pathName] = $replacedXml; + } + } + + $undocumentedConstMap = array_diff_key($constMap, $documentedConstMap); + + return $predefinedConstants; +} + /** * @param array $classMap - * @param iterable $allConstInfos + * @param array $allConstInfos * @return array */ -function generateClassSynopses(array $classMap, iterable $allConstInfos): array { +function generateClassSynopses(array $classMap, array $allConstInfos): array { $result = []; foreach ($classMap as $classInfo) { @@ -4504,13 +4623,19 @@ function generateClassSynopses(array $classMap, iterable $allConstInfos): array /** * @param array $classMap - * $param iterable $allConstInfos + * @param array $allConstInfos + * @param array $undocumentedClassMap * @return array */ -function replaceClassSynopses(string $targetDirectory, array $classMap, iterable $allConstInfos, bool $isVerify): array -{ - $existingClassSynopses = []; - +function replaceClassSynopses( + string $targetDirectory, + array $classMap, + array $allConstInfos, + array &$undocumentedClassMap +): array { + /** @var array $documentedClassMap */ + $documentedClassMap = []; + /** @var array $classSynopses */ $classSynopses = []; $it = new RecursiveIteratorIterator( @@ -4568,7 +4693,7 @@ function replaceClassSynopses(string $targetDirectory, array $classMap, iterable continue; } - $existingClassSynopses[$className] = $className; + $documentedClassMap[$className] = $className; $classInfo = $classMap[$className]; @@ -4611,15 +4736,7 @@ function replaceClassSynopses(string $targetDirectory, array $classMap, iterable } } - if ($isVerify) { - $missingClassSynopses = array_diff_key($classMap, $existingClassSynopses); - foreach ($missingClassSynopses as $className => $info) { - /** @var ClassInfo $info */ - if (!$info->isUndocumentable) { - echo "Warning: Missing class synopsis for $className\n"; - } - } - } + $undocumentedClassMap = array_diff_key($classMap, $documentedClassMap); return $classSynopses; } @@ -4660,10 +4777,21 @@ function generateMethodSynopses(array $funcMap, array $aliasMap): array { /** * @param array $funcMap * @param array $aliasMap + * @param array $methodSynopsisWarnings + * @param array $undocumentedFuncMap * @return array */ -function replaceMethodSynopses(string $targetDirectory, array $funcMap, array $aliasMap, bool $isVerify): array { - $existingMethodSynopses = []; +function replaceMethodSynopses( + string $targetDirectory, + array $funcMap, + array $aliasMap, + bool $isVerifyManual, + array &$methodSynopsisWarnings, + array &$undocumentedFuncMap +): array { + /** @var array $documentedFuncMap */ + $documentedFuncMap = []; + /** @var array $methodSynopses */ $methodSynopses = []; $it = new RecursiveIteratorIterator( @@ -4682,7 +4810,7 @@ function replaceMethodSynopses(string $targetDirectory, array $funcMap, array $a continue; } - if ($isVerify) { + if ($isVerifyManual) { $matches = []; preg_match("/\s*([\w:]+)\s*<\/refname>\s*\s*&Alias;\s*<(?:function|methodname)>\s*([\w:]+)\s*<\/(?:function|methodname)>\s*<\/refpurpose>/i", $xml, $matches); $aliasName = $matches[1] ?? null; @@ -4695,7 +4823,7 @@ function replaceMethodSynopses(string $targetDirectory, array $funcMap, array $a ($func === null || $func->alias === null || $func->alias->__toString() !== $aliasName) && ($alias->alias === null || $alias->alias->__toString() !== $funcName) ) { - echo "Warning: $aliasName()" . ($alias->alias ? " is an alias of " . $alias->alias->__toString() . "(), but it" : "") . " is incorrectly documented as an alias for $funcName()\n"; + $methodSynopsisWarnings[] = "$aliasName()" . ($alias->alias ? " is an alias of " . $alias->alias->__toString() . "(), but it" : "") . " is incorrectly documented as an alias for $funcName()"; } $matches = []; @@ -4703,11 +4831,11 @@ function replaceMethodSynopses(string $targetDirectory, array $funcMap, array $a $descriptionFuncName = $matches[1] ?? null; $descriptionFunc = $funcMap[$descriptionFuncName] ?? null; if ($descriptionFunc && $funcName !== $descriptionFuncName) { - echo "Warning: Alias in the method synopsis description of $pathName doesn't match the alias in the \n"; + $methodSynopsisWarnings[] = "Alias in the method synopsis description of $pathName doesn't match the alias in the "; } if ($aliasName) { - $existingMethodSynopses[$aliasName] = $aliasName; + $documentedFuncMap[$aliasName] = $aliasName; } } @@ -4754,7 +4882,7 @@ function replaceMethodSynopses(string $targetDirectory, array $funcMap, array $a } $funcInfo = $funcMap[$funcName]; - $existingMethodSynopses[$funcInfo->name->__toString()] = $funcInfo->name->__toString(); + $documentedFuncMap[$funcInfo->name->__toString()] = $funcInfo->name->__toString(); $newMethodSynopsis = $funcInfo->getMethodSynopsisElement($funcMap, $aliasMap, $doc); if ($newMethodSynopsis === null) { @@ -4840,15 +4968,7 @@ function replaceMethodSynopses(string $targetDirectory, array $funcMap, array $a } } - if ($isVerify) { - $missingMethodSynopses = array_diff_key($funcMap, $existingMethodSynopses); - foreach ($missingMethodSynopses as $functionName => $info) { - /** @var FuncInfo $info */ - if (!$info->isUndocumentable) { - echo "Warning: Missing method synopsis for $functionName()\n"; - } - } - } + $undocumentedFuncMap = array_diff_key($funcMap, $documentedFuncMap); return $methodSynopses; } @@ -4943,8 +5063,9 @@ $optind = null; $options = getopt( "fh", [ - "force-regeneration", "parameter-stats", "help", "verify", "generate-classsynopses", "replace-classsynopses", - "generate-methodsynopses", "replace-methodsynopses", "generate-optimizer-info" + "force-regeneration", "parameter-stats", "help", "verify", "verify-manual", "replace-predefined-constants", + "generate-classsynopses", "replace-classsynopses", "generate-methodsynopses", "replace-methodsynopses", + "generate-optimizer-info", ], $optind ); @@ -4952,25 +5073,29 @@ $options = getopt( $context = new Context; $printParameterStats = isset($options["parameter-stats"]); $verify = isset($options["verify"]); +$verifyManual = isset($options["verify-manual"]); +$replacePredefinedConstants = isset($options["replace-predefined-constants"]); $generateClassSynopses = isset($options["generate-classsynopses"]); $replaceClassSynopses = isset($options["replace-classsynopses"]); $generateMethodSynopses = isset($options["generate-methodsynopses"]); $replaceMethodSynopses = isset($options["replace-methodsynopses"]); $generateOptimizerInfo = isset($options["generate-optimizer-info"]); $context->forceRegeneration = isset($options["f"]) || isset($options["force-regeneration"]); -$context->forceParse = $context->forceRegeneration || $printParameterStats || $verify || $generateClassSynopses || $generateOptimizerInfo || $replaceClassSynopses || $generateMethodSynopses || $replaceMethodSynopses; +$context->forceParse = $context->forceRegeneration || $printParameterStats || $verify || $verifyManual || $replacePredefinedConstants || $generateClassSynopses || $generateOptimizerInfo || $replaceClassSynopses || $generateMethodSynopses || $replaceMethodSynopses; -$targetSynopses = $argv[$argc - 1] ?? null; -if ($replaceClassSynopses && $targetSynopses === null) { - die("A target class synopsis directory must be provided for.\n"); +$manualTarget = $argv[$argc - 1] ?? null; +if (($replacePredefinedConstants || $verifyManual) && $manualTarget === null) { + die("A target manual directory must be provided.\n"); } - -if ($replaceMethodSynopses && $targetSynopses === null) { - die("A target method synopsis directory must be provided.\n"); +if (($replaceClassSynopses || $verifyManual) && $manualTarget === null) { + die("A target manual directory must be provided.\n"); +} +if (($replaceMethodSynopses || $verifyManual) && $manualTarget === null) { + die("A target manual directory must be provided.\n"); } if (isset($options["h"]) || isset($options["help"])) { - die("\nusage: gen_stub.php [ -f | --force-regeneration ] [ --generate-classsynopses ] [ --replace-classsynopses ] [ --generate-methodsynopses ] [ --replace-methodsynopses ] [ --parameter-stats ] [ --verify ] [ --generate-optimizer-info ] [ -h | --help ] [ name.stub.php | directory ] [ directory ]\n\n"); + die("\nUsage: gen_stub.php [ -f | --force-regeneration ] [ --replace-predefined-constants ] [ --generate-classsynopses ] [ --replace-classsynopses ] [ --generate-methodsynopses ] [ --replace-methodsynopses ] [ --parameter-stats ] [ --verify ] [ --verify-manual ] [ --generate-optimizer-info ] [ -h | --help ] [ name.stub.php | directory ] [ directory ]\n\n"); } $fileInfos = []; @@ -5015,6 +5140,15 @@ $funcMap = []; /** @var array $aliasMap */ $aliasMap = []; +/** @var array $undocumentedConstMap */ +$undocumentedConstMap = []; +/** @var array $undocumentedClassMap */ +$undocumentedClassMap = []; +/** @var array $undocumentedFuncMap */ +$undocumentedFuncMap = []; +/** @var array $methodSynopsisWarnings */ +$methodSynopsisWarnings = []; + foreach ($fileInfos as $fileInfo) { foreach ($fileInfo->getAllFuncInfos() as $funcInfo) { $funcMap[$funcInfo->name->__toString()] = $funcInfo; @@ -5115,6 +5249,18 @@ if ($verify) { } } +if ($replacePredefinedConstants || $verifyManual) { + $predefinedConstants = replacePredefinedConstants($manualTarget, $context->allConstInfos, $undocumentedConstMap); + + if ($replacePredefinedConstants) { + foreach ($predefinedConstants as $filename => $content) { + if (file_put_contents($filename, $content)) { + echo "Saved $filename\n"; + } + } + } +} + if ($generateClassSynopses) { $classSynopsesDirectory = getcwd() . "/classsynopses"; @@ -5132,12 +5278,14 @@ if ($generateClassSynopses) { } } -if ($replaceClassSynopses) { - $classSynopses = replaceClassSynopses($targetSynopses, $classMap, $context->allConstInfos, $verify); +if ($replaceClassSynopses || $verifyManual) { + $classSynopses = replaceClassSynopses($manualTarget, $classMap, $context->allConstInfos, $undocumentedClassMap); - foreach ($classSynopses as $filename => $content) { - if (file_put_contents($filename, $content)) { - echo "Saved $filename\n"; + if ($replaceClassSynopses) { + foreach ($classSynopses as $filename => $content) { + if (file_put_contents($filename, $content)) { + echo "Saved $filename\n"; + } } } } @@ -5159,12 +5307,14 @@ if ($generateMethodSynopses) { } } -if ($replaceMethodSynopses) { - $methodSynopses = replaceMethodSynopses($targetSynopses, $funcMap, $aliasMap, $verify); +if ($replaceMethodSynopses || $verifyManual) { + $methodSynopses = replaceMethodSynopses($manualTarget, $funcMap, $aliasMap, $verifyManual, $methodSynopsisWarnings, $undocumentedFuncMap); - foreach ($methodSynopses as $filename => $content) { - if (file_put_contents($filename, $content)) { - echo "Saved $filename\n"; + if ($replaceMethodSynopses) { + foreach ($methodSynopses as $filename => $content) { + if (file_put_contents($filename, $content)) { + echo "Saved $filename\n"; + } } } } @@ -5177,3 +5327,29 @@ if ($generateOptimizerInfo) { echo "Saved $filename\n"; } } + +if ($verifyManual) { + foreach ($undocumentedConstMap as $constName => $info) { + if ($info->name->isClassConst() || $info->isUndocumentable) { + continue; + } + + echo "Warning: Missing predefined constant for $constName\n"; + } + + foreach ($methodSynopsisWarnings as $warning) { + echo "Warning: $warning\n"; + } + + foreach ($undocumentedClassMap as $className => $info) { + if (!$info->isUndocumentable) { + echo "Warning: Missing class synopsis for $className\n"; + } + } + + foreach ($undocumentedFuncMap as $functionName => $info) { + if (!$info->isUndocumentable) { + echo "Warning: Missing method synopsis for $functionName()\n"; + } + } +}