Skip to content
Merged
Show file tree
Hide file tree
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
41 changes: 40 additions & 1 deletion dev/src/Command/DocFxCommand.php
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,9 @@
use Google\Cloud\Dev\DocFx\Page\PageTree;
use Google\Cloud\Dev\DocFx\Page\OverviewPage;
use Google\Cloud\Dev\DocFx\XrefValidationTrait;
use RecursiveDirectoryIterator;
use RecursiveIteratorIterator;
use Symfony\Component\Process\Exception\ProcessFailedException;

/**
* @internal
Expand Down Expand Up @@ -147,7 +150,18 @@ protected function execute(InputInterface $input, OutputInterface $output)
$output->write('Running phpdoc to generate structure.xml... ');
// Run "phpdoc"
$process = self::getPhpDocCommand($component->getPath(), $outDir);
$process->mustRun();
try {
$process->mustRun();
} catch (ProcessFailedException $ex) {
if (false === strpos($process->getErrorOutput(), 'The arguments array must contain 3 items, 0 given')) {
throw $ex;
}
$output->writeln('<error>Process errored out, applying PHPDoc Tag Escape fix and trying again...</>');
$this->applyPhpDocTagEscapeFix($component->getPath());
$process->mustRun();
$output->write('<info>IT WORKED!</> Reverting Fix... ');
$this->applyPhpDocTagEscapeFix($component->getPath(), revert: true);
}
$output->writeln('Done.');
$xml = $outDir . '/structure.xml';
}
Expand Down Expand Up @@ -345,4 +359,29 @@ private function uploadToStagingBucket(string $outDir, string $stagingBucket): v
]);
$process->mustRun();
}

/**
* Applies a fix to solve an issue where {@*} is being parsed as a tag by
* phpDocumentor, which later causes an error when vprintf sees unescaped
* percent signs. Replaces {@*} with "&#42;&#47;" in the files were the
* errors occur.
*
* @see https://github.com/phpDocumentor/ReflectionDocBlock/pull/450
* @TODO: Remove this method once the fix is merged and released.
*/
private function applyPhpDocTagEscapeFix(string $componentPath, $revert = false)
{
$from = $revert ? '&#42;&#47;' : '{@*}';
$to = $revert ? '{@*}' : '&#42;&#47;';
$dirIter = new RecursiveDirectoryIterator($componentPath . '/src', RecursiveDirectoryIterator::SKIP_DOTS);
foreach (new RecursiveIteratorIterator($dirIter) as $file) {
if ($file->isFile()) {
$content = file_get_contents($file->getPathname());
// The error only occurs when both "{@*}" and "%" are present
if (str_contains($content, $from) && str_contains($content, '%')) {
file_put_contents($file->getPathname(), str_replace($from, $to, $content));
}
}
}
}
}
6 changes: 6 additions & 0 deletions dev/src/DocFx/Node/DocblockTrait.php
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@ public function getContent(): string
$content = $this->replaceProtoRef($content);
$content = $this->stripSnippetTag($content);
$content = $this->addPhpLanguageHintToFencedCodeBlock($content);
$content = $this->unescapeDocblockClosingTags($content);

return $content;
}
Expand Down Expand Up @@ -85,4 +86,9 @@ private function stripProtobufGeneratedField(string $content): string
$regex = '/Generated from protobuf field <code>.*<\/code>\Z/m';
return rtrim(preg_replace($regex, '', $content));
}

private function unescapeDocBlockClosingTags(string $content): string
{
return str_replace('{@*}', '*/', $content);
}
}
15 changes: 15 additions & 0 deletions dev/tests/Unit/DocFx/NodeTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
namespace Google\Cloud\Dev\Tests\Unit\DocFx;

use Google\Cloud\Dev\DocFx\Node\ClassNode;
use Google\Cloud\Dev\DocFx\Node\DocblockTrait;
use Google\Cloud\Dev\DocFx\Node\MethodNode;
use Google\Cloud\Dev\DocFx\Node\XrefTrait;
use Google\Cloud\Dev\DocFx\Node\FencedCodeBlockTrait;
Expand Down Expand Up @@ -561,4 +562,18 @@ public function provideBrokenXrefs()
[sprintf('{@see \%s::OUTPUT_NORMAL}', OutputInterface::class)], // valid constant
];
}

public function testEscapeDocblockClosingTags()
{
$classXml = '<class><full_name>TestClass</full_name><docblock><description>%s</description></docblock></class>';

$docblock = new class (new SimpleXMLElement(sprintf($classXml, 'the path must match `foo/{@*}bar/{@*}baz`'))) {
use DocblockTrait;

public function __construct(private SimpleXMLElement $xmlNode)
{}
};

$this->assertEquals('the path must match `foo/*/bar/*/baz`', $docblock->getContent());
}
}
Loading