Skip to content

Commit b2fba89

Browse files
committed
Generators/HTML: slugify anchor links
... to prevent issues with URL encoding. Note: as this _may_ result in duplicate anchor links, this commit includes a protection against this by adding a numeric suffix to the anchor if a duplicate is detected. Includes a test with a variety of non-ascii chars and duplicate titles. Includes updated test expectations for various other tests.
1 parent 4bc3d2e commit b2fba89

File tree

37 files changed

+270
-42
lines changed

37 files changed

+270
-42
lines changed

src/Generators/HTML.php

Lines changed: 27 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -113,6 +113,13 @@ class HTML extends Generator
113113
}
114114
</style>';
115115

116+
/**
117+
* List of seen slugified anchors to ensure uniqueness.
118+
*
119+
* @var array<string, true>
120+
*/
121+
private $seenAnchors = [];
122+
116123

117124
/**
118125
* Generates the documentation for a standard.
@@ -132,6 +139,10 @@ public function generate()
132139
$content = ob_get_contents();
133140
ob_end_clean();
134141

142+
// Clear anchor cache after Documentation generation.
143+
// The anchor generation for the TOC anchor links will use the same logic, so should end up with the same unique slugs.
144+
$this->seenAnchors = [];
145+
135146
if (trim($content) !== '') {
136147
echo $this->getFormattedHeader();
137148
echo $this->getFormattedToc();
@@ -325,7 +336,22 @@ public function processSniff(DOMNode $doc)
325336
*/
326337
private function titleToAnchor($title)
327338
{
328-
return str_replace(' ', '-', $title);
339+
// Slugify the text.
340+
$title = strtolower($title);
341+
$title = str_replace(' ', '-', $title);
342+
$title = preg_replace('`[^a-z0-9._-]`', '-', $title);
343+
344+
if (isset($this->seenAnchors[$title]) === false) {
345+
// Add to "seen" list.
346+
$this->seenAnchors[$title] = true;
347+
} else {
348+
// Try to find a unique anchor for this title.
349+
for ($i = 2; (isset($this->seenAnchors[$title.'-'.$i]) === true); $i++);
350+
$title .= '-'.$i;
351+
$this->seenAnchors[$title] = true;
352+
}
353+
354+
return $title;
329355

330356
}//end titleToAnchor()
331357

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
<?xml version="1.0"?>
2+
<ruleset xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" name="GeneratorTest" xsi:noNamespaceSchemaLocation="https://raw.githubusercontent.com/PHPCSStandards/PHP_CodeSniffer/master/phpcs.xsd">
3+
4+
<config name="installed_paths" value="./tests/Core/Generators/Fixtures/"/>
5+
6+
<rule ref="StandardWithDocs.Content.DocumentationTitleToAnchorSlug1"/>
7+
<rule ref="StandardWithDocs.Content.DocumentationTitleToAnchorSlug2"/>
8+
<rule ref="StandardWithDocs.Content.DocumentationTitleToAnchorSlug3"/>
9+
10+
</ruleset>

tests/Core/Generators/Expectations/ExpectedOutputCodeComparisonBlankLines.html

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -88,7 +88,7 @@
8888
</head>
8989
<body>
9090
<h1>GeneratorTest Coding Standards</h1>
91-
<h2 id="Code-Comparison,-blank-lines">Code Comparison, blank lines<a class="sniffanchor" href="#Code-Comparison,-blank-lines"> &sect; </a></h2>
91+
<h2 id="code-comparison--blank-lines">Code Comparison, blank lines<a class="sniffanchor" href="#code-comparison--blank-lines"> &sect; </a></h2>
9292
<p class="text">This is a standard block.</p>
9393
<table class="code-comparison">
9494
<tr>

tests/Core/Generators/Expectations/ExpectedOutputCodeComparisonBlockLength.html

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -88,7 +88,7 @@
8888
</head>
8989
<body>
9090
<h1>GeneratorTest Coding Standards</h1>
91-
<h2 id="Code-Comparison,-block-length">Code Comparison, block length<a class="sniffanchor" href="#Code-Comparison,-block-length"> &sect; </a></h2>
91+
<h2 id="code-comparison--block-length">Code Comparison, block length<a class="sniffanchor" href="#code-comparison--block-length"> &sect; </a></h2>
9292
<p class="text">This is a standard block.</p>
9393
<table class="code-comparison">
9494
<tr>

tests/Core/Generators/Expectations/ExpectedOutputCodeComparisonEncoding.html

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -88,7 +88,7 @@
8888
</head>
8989
<body>
9090
<h1>GeneratorTest Coding Standards</h1>
91-
<h2 id="Code-Comparison,-char-encoding">Code Comparison, char encoding<a class="sniffanchor" href="#Code-Comparison,-char-encoding"> &sect; </a></h2>
91+
<h2 id="code-comparison--char-encoding">Code Comparison, char encoding<a class="sniffanchor" href="#code-comparison--char-encoding"> &sect; </a></h2>
9292
<p class="text">This is a standard block.</p>
9393
<table class="code-comparison">
9494
<tr>

tests/Core/Generators/Expectations/ExpectedOutputCodeComparisonLineLength.html

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -88,7 +88,7 @@
8888
</head>
8989
<body>
9090
<h1>GeneratorTest Coding Standards</h1>
91-
<h2 id="Code-Comparison,-line-length">Code Comparison, line length<a class="sniffanchor" href="#Code-Comparison,-line-length"> &sect; </a></h2>
91+
<h2 id="code-comparison--line-length">Code Comparison, line length<a class="sniffanchor" href="#code-comparison--line-length"> &sect; </a></h2>
9292
<p class="text">Ensure there is no PHP &quot;Warning: str_repeat(): Second argument has to be greater than or equal to 0&quot;.<br/>
9393
Ref: squizlabs/PHP_CodeSniffer#2522</p>
9494
<table class="code-comparison">

tests/Core/Generators/Expectations/ExpectedOutputCodeTitleLineWrapping.html

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -88,7 +88,7 @@
8888
</head>
8989
<body>
9090
<h1>GeneratorTest Coding Standards</h1>
91-
<h2 id="Code-Title,-line-wrapping">Code Title, line wrapping<a class="sniffanchor" href="#Code-Title,-line-wrapping"> &sect; </a></h2>
91+
<h2 id="code-title--line-wrapping">Code Title, line wrapping<a class="sniffanchor" href="#code-title--line-wrapping"> &sect; </a></h2>
9292
<p class="text">This is a standard block.</p>
9393
<table class="code-comparison">
9494
<tr>

tests/Core/Generators/Expectations/ExpectedOutputCodeTitleWhitespace.html

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -88,7 +88,7 @@
8888
</head>
8989
<body>
9090
<h1>GeneratorTest Coding Standards</h1>
91-
<h2 id="Code-Title,-whitespace-handling">Code Title, whitespace handling<a class="sniffanchor" href="#Code-Title,-whitespace-handling"> &sect; </a></h2>
91+
<h2 id="code-title--whitespace-handling">Code Title, whitespace handling<a class="sniffanchor" href="#code-title--whitespace-handling"> &sect; </a></h2>
9292
<p class="text">This is a standard block.</p>
9393
<table class="code-comparison">
9494
<tr>

tests/Core/Generators/Expectations/ExpectedOutputDocumentationTitleLength.html

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -88,7 +88,7 @@
8888
</head>
8989
<body>
9090
<h1>GeneratorTest Coding Standards</h1>
91-
<h2 id="This-is-a-very-very-very-very-very-very-very-very-very-very-very-long-title">This is a very very very very very very very very very very very long title<a class="sniffanchor" href="#This-is-a-very-very-very-very-very-very-very-very-very-very-very-long-title"> &sect; </a></h2>
91+
<h2 id="this-is-a-very-very-very-very-very-very-very-very-very-very-very-long-title">This is a very very very very very very very very very very very long title<a class="sniffanchor" href="#this-is-a-very-very-very-very-very-very-very-very-very-very-very-long-title"> &sect; </a></h2>
9292
<p class="text">This is a standard block.</p>
9393
<div class="tag-line">Documentation generated on #REDACTED# by <a href="https://github.com/PHPCSStandards/PHP_CodeSniffer">PHP_CodeSniffer #VERSION#</a></div>
9494
</body>

tests/Core/Generators/Expectations/ExpectedOutputDocumentationTitlePCREFallback.html

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -88,7 +88,7 @@
8888
</head>
8989
<body>
9090
<h1>GeneratorTest Coding Standards</h1>
91-
<h2 id="Documentation-Title-PCRE-Fallback">Documentation Title PCRE Fallback<a class="sniffanchor" href="#Documentation-Title-PCRE-Fallback"> &sect; </a></h2>
91+
<h2 id="documentation-title-pcre-fallback">Documentation Title PCRE Fallback<a class="sniffanchor" href="#documentation-title-pcre-fallback"> &sect; </a></h2>
9292
<p class="text">Testing the document title can get determined from the sniff name if missing.</p>
9393
<p class="text">This file name contains an acronym on purpose to test the word splitting.</p>
9494
<div class="tag-line">Documentation generated on #REDACTED# by <a href="https://github.com/PHPCSStandards/PHP_CodeSniffer">PHP_CodeSniffer #VERSION#</a></div>

0 commit comments

Comments
 (0)