-
Notifications
You must be signed in to change notification settings - Fork 7.9k
Add support for replacing class synopses based on stubs #7340
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
abc313c
02da8a9
4354a32
6a21290
7f169d9
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -1494,7 +1494,7 @@ public function getClassSynopsisElement(DOMDocument $doc, array $classMap): ?DOM | |
$parentClassName = self::getClassSynopsisFilename($parent); | ||
$includeElement = $this->createIncludeElement( | ||
$doc, | ||
"xmlns(db=http://docbook.org/ns/docbook) xpointer(id('class.$parentClassName')/db:partintro/db:section/db:classsynopsis/db:fieldsynopsis[preceding-sibling::db:classsynopsisinfo[1][@role='comment' and text()='&Properties;']]))" | ||
"xmlns(db=http://docbook.org/ns/docbook) xpointer(id('class.$parentClassName')/db:partintro/db:section/db:classsynopsis/db:fieldsynopsis[preceding-sibling::db:classsynopsisinfo[1][@role='comment' and text()='Properties']]))" | ||
); | ||
$classSynopsis->appendChild($includeElement); | ||
} | ||
|
@@ -1512,7 +1512,7 @@ public function getClassSynopsisElement(DOMDocument $doc, array $classMap): ?DOM | |
$classSynopsis->appendChild(new DOMText("\n ")); | ||
$includeElement = $this->createIncludeElement( | ||
$doc, | ||
"xmlns(db=http://docbook.org/ns/docbook) xpointer(id('class.$className')/db:refentry/db:refsect1[@role='description']/descendant::db:constructorsynopsis[not(@role='procedural')]" | ||
"xmlns(db=http://docbook.org/ns/docbook) xpointer(id('class.$className')/db:refentry/db:refsect1[@role='description']/descendant::db:constructorsynopsis[not(@role='procedural')])" | ||
); | ||
$classSynopsis->appendChild($includeElement); | ||
} | ||
|
@@ -1530,7 +1530,7 @@ public function getClassSynopsisElement(DOMDocument $doc, array $classMap): ?DOM | |
$classSynopsis->appendChild(new DOMText("\n ")); | ||
$includeElement = $this->createIncludeElement( | ||
$doc, | ||
"xmlns(db=http://docbook.org/ns/docbook) xpointer(id('class.$className')/db:refentry/db:refsect1[@role='description']/descendant::db:destructorsynopsis[not(@role='procedural')]" | ||
"xmlns(db=http://docbook.org/ns/docbook) xpointer(id('class.$className')/db:refentry/db:refsect1[@role='description']/descendant::db:destructorsynopsis[not(@role='procedural')])" | ||
); | ||
$classSynopsis->appendChild($includeElement); | ||
} | ||
|
@@ -2440,7 +2440,113 @@ function generateClassSynopses(array $classMap): array { | |
*/ | ||
function replaceClassSynopses(string $targetDirectory, array $classMap): array | ||
{ | ||
throw new Exception("Not yet implemented!"); | ||
$classSynopses = []; | ||
|
||
$it = new RecursiveIteratorIterator( | ||
new RecursiveDirectoryIterator($targetDirectory), | ||
RecursiveIteratorIterator::LEAVES_ONLY | ||
); | ||
|
||
foreach ($it as $file) { | ||
$pathName = $file->getPathName(); | ||
if (!preg_match('/\.xml$/i', $pathName)) { | ||
continue; | ||
} | ||
|
||
$xml = file_get_contents($pathName); | ||
if ($xml === false) { | ||
continue; | ||
} | ||
|
||
if (stripos($xml, "<classsynopsis") === false) { | ||
continue; | ||
} | ||
|
||
$replacedXml = getReplacedSynopsisXml($xml); | ||
|
||
$doc = new DOMDocument(); | ||
$doc->formatOutput = false; | ||
$doc->preserveWhiteSpace = true; | ||
$doc->validateOnParse = true; | ||
$success = $doc->loadXML($replacedXml); | ||
if (!$success) { | ||
echo "Failed opening $pathName\n"; | ||
continue; | ||
} | ||
|
||
$classSynopsisElements = []; | ||
foreach ($doc->getElementsByTagName("classsynopsis") as $element) { | ||
$classSynopsisElements[] = $element; | ||
} | ||
|
||
foreach ($classSynopsisElements as $classSynopsis) { | ||
if (!$classSynopsis instanceof DOMElement) { | ||
continue; | ||
} | ||
|
||
$firstChild = $classSynopsis->firstElementChild; | ||
if ($firstChild === null) { | ||
continue; | ||
} | ||
$firstChild = $firstChild->firstElementChild; | ||
if ($firstChild === null) { | ||
continue; | ||
} | ||
$className = $firstChild->textContent; | ||
if (!isset($classMap[$className])) { | ||
continue; | ||
} | ||
$classInfo = $classMap[$className]; | ||
|
||
$newClassSynopsis = $classInfo->getClassSynopsisElement($doc, $classMap); | ||
if ($newClassSynopsis === null) { | ||
continue; | ||
} | ||
|
||
// Check if there is any change - short circuit if there is not any. | ||
|
||
if (replaceAndCompareXmls($doc, $classSynopsis, $newClassSynopsis)) { | ||
continue; | ||
} | ||
|
||
// Return the updated XML | ||
|
||
$replacedXml = $doc->saveXML(); | ||
|
||
$replacedXml = preg_replace( | ||
[ | ||
"/REPLACED-ENTITY-([A-Za-z0-9._{}%-]+?;)/", | ||
"/<phpdoc:classref\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\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", | ||
], | ||
[ | ||
"&$1", | ||
"<phpdoc:classref xml:id=\"$4\" xmlns:phpdoc=\"$1\" xmlns=\"$2\" xmlns:xi=\"$4\">", | ||
"<phpdoc:classref xml:id=\"$5\" xmlns:phpdoc=\"$1\" xmlns=\"$2\" xmlns:xlink=\"$3\" xmlns:xi=\"$4\">", | ||
], | ||
$replacedXml | ||
); | ||
|
||
$classSynopses[$pathName] = $replacedXml; | ||
} | ||
} | ||
|
||
return $classSynopses; | ||
} | ||
|
||
function getReplacedSynopsisXml(string $xml): string | ||
{ | ||
return preg_replace( | ||
[ | ||
"/&([A-Za-z0-9._{}%-]+?;)/", | ||
"/<(\/)*xi:([A-Za-z]+?)/" | ||
], | ||
[ | ||
"REPLACED-ENTITY-$1", | ||
"<$1XI$2", | ||
], | ||
$xml | ||
); | ||
} | ||
|
||
/** | ||
|
@@ -2489,7 +2595,7 @@ function replaceMethodSynopses(string $targetDirectory, array $funcMap, array $a | |
continue; | ||
} | ||
|
||
$replacedXml = preg_replace("/&([A-Za-z0-9._{}%-]+?;)/", "REPLACED-ENTITY-$1", $xml); | ||
$replacedXml = getReplacedSynopsisXml($xml); | ||
|
||
$doc = new DOMDocument(); | ||
$doc->formatOutput = false; | ||
|
@@ -2501,10 +2607,6 @@ function replaceMethodSynopses(string $targetDirectory, array $funcMap, array $a | |
continue; | ||
} | ||
|
||
$docComparator = new DOMDocument(); | ||
$docComparator->preserveWhiteSpace = false; | ||
$docComparator->formatOutput = true; | ||
|
||
$methodSynopsisElements = []; | ||
foreach ($doc->getElementsByTagName("constructorsynopsis") as $element) { | ||
$methodSynopsisElements[] = $element; | ||
|
@@ -2568,19 +2670,7 @@ function replaceMethodSynopses(string $targetDirectory, array $funcMap, array $a | |
|
||
// Check if there is any change - short circuit if there is not any. | ||
|
||
$xml1 = $doc->saveXML($methodSynopsis); | ||
$xml1 = preg_replace("/&([A-Za-z0-9._{}%-]+?;)/", "REPLACED-ENTITY-$1", $xml1); | ||
$docComparator->loadXML($xml1); | ||
$xml1 = $docComparator->saveXML(); | ||
|
||
$methodSynopsis->parentNode->replaceChild($newMethodSynopsis, $methodSynopsis); | ||
|
||
$xml2 = $doc->saveXML($newMethodSynopsis); | ||
$xml2 = preg_replace("/&([A-Za-z0-9._{}%-]+?;)/", "REPLACED-ENTITY-$1", $xml2); | ||
$docComparator->loadXML($xml2); | ||
$xml2 = $docComparator->saveXML(); | ||
|
||
if ($xml1 === $xml2) { | ||
if (replaceAndCompareXmls($doc, $methodSynopsis, $newMethodSynopsis)) { | ||
continue; | ||
} | ||
|
||
|
@@ -2631,6 +2721,28 @@ function replaceMethodSynopses(string $targetDirectory, array $funcMap, array $a | |
return $methodSynopses; | ||
} | ||
|
||
function replaceAndCompareXmls(DOMDocument $doc, DOMElement $originalSynopsis, DOMElement $newSynopsis): bool | ||
{ | ||
$docComparator = new DOMDocument(); | ||
$docComparator->preserveWhiteSpace = false; | ||
$docComparator->formatOutput = true; | ||
|
||
$xml1 = $doc->saveXML($originalSynopsis); | ||
$xml1 = getReplacedSynopsisXml($xml1); | ||
$docComparator->loadXML($xml1); | ||
$xml1 = $docComparator->saveXML(); | ||
|
||
$originalSynopsis->parentNode->replaceChild($newSynopsis, $originalSynopsis); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Should this be in here? This looks unrelated to the comparison. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yes, even though it makes the function have a side-effect, it's still absolutely required (I tried to clone The reason why we need this code is that it attaches the There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Hm, would using DOMDocument::importNode work? Maybe it's possible to directly import it to $docComparator even? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I've just tried this:
But it still doesn't disregard whitespace changes - I guess because There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Okay, let's stick with what you have then. Maybe rename the function to make it clear that it also modifies, something like There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
|
||
|
||
$xml2 = $doc->saveXML($newSynopsis); | ||
$xml2 = getReplacedSynopsisXml($xml2); | ||
|
||
$docComparator->loadXML($xml2); | ||
$xml2 = $docComparator->saveXML(); | ||
|
||
return $xml1 === $xml2; | ||
} | ||
|
||
function installPhpParser(string $version, string $phpParserDir) { | ||
$lockFile = __DIR__ . "/PHP-Parser-install-lock"; | ||
$lockFd = fopen($lockFile, 'w+'); | ||
|
@@ -2715,13 +2827,12 @@ function initPhpParser() { | |
$context->forceRegeneration = isset($options["f"]) || isset($options["force-regeneration"]); | ||
$context->forceParse = $context->forceRegeneration || $printParameterStats || $verify || $generateClassSynopses || $replaceClassSynopses || $generateMethodSynopses || $replaceMethodSynopses; | ||
|
||
$targetClassSynopses = $argv[$optind + 1] ?? null; | ||
if ($replaceClassSynopses && $targetClassSynopses === null) { | ||
$targetSynopses = $argv[$argc - 1] ?? null; | ||
kocsismate marked this conversation as resolved.
Show resolved
Hide resolved
|
||
if ($replaceClassSynopses && $targetSynopses === null) { | ||
die("A target class synopsis directory must be provided for.\n"); | ||
} | ||
|
||
$targetMethodSynopses = $argv[$optind + 1 + ($targetClassSynopses !== null)] ?? null; | ||
if ($replaceMethodSynopses && $targetMethodSynopses === null) { | ||
if ($replaceMethodSynopses && $targetSynopses === null) { | ||
die("A target method synopsis directory must be provided.\n"); | ||
} | ||
|
||
|
@@ -2884,7 +2995,7 @@ function(?ArgInfo $aliasArg, ?ArgInfo $aliasedArg) use ($aliasFunc, $aliasedFunc | |
} | ||
|
||
if ($replaceClassSynopses) { | ||
$classSynopses = replaceClassSynopses($targetClassSynopses, $classMap); | ||
$classSynopses = replaceClassSynopses($targetSynopses, $classMap); | ||
|
||
foreach ($classSynopses as $filename => $content) { | ||
if (file_put_contents($filename, $content)) { | ||
|
@@ -2912,7 +3023,7 @@ function(?ArgInfo $aliasArg, ?ArgInfo $aliasedArg) use ($aliasFunc, $aliasedFunc | |
} | ||
|
||
if ($replaceMethodSynopses) { | ||
$methodSynopses = replaceMethodSynopses($targetMethodSynopses, $funcMap, $aliasMap); | ||
$methodSynopses = replaceMethodSynopses($targetSynopses, $funcMap, $aliasMap); | ||
|
||
foreach ($methodSynopses as $filename => $content) { | ||
if (file_put_contents($filename, $content)) { | ||
|
Uh oh!
There was an error while loading. Please reload this page.