Skip to content

Commit 1528f7a

Browse files
authored
Merge pull request #607 from phpDocumentor/task/move-url-calculation
!![Task] move url calculation from render context
2 parents d9d1a73 + 49e5c8d commit 1528f7a

File tree

9 files changed

+137
-27
lines changed

9 files changed

+137
-27
lines changed

packages/guides/src/NodeRenderers/Html/MenuEntryRenderer.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ public function render(Node $node, RenderContext $renderContext): string
2828
$renderContext,
2929
'body/menu/menu-item.html.twig',
3030
[
31-
'url' => $renderContext->relativeDocUrl($node->getUrl(), $node->getValue()->getId()),
31+
'url' => $renderContext->generateCanonicalOutputUrl($node->getUrl(), $node->getValue()->getId()),
3232
'node' => $node,
3333
],
3434
);

packages/guides/src/ReferenceResolvers/DocReferenceResolver.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ public function resolve(LinkInlineNode $node, RenderContext $renderContext): boo
2525
return false;
2626
}
2727

28-
$node->setUrl($renderContext->relativeDocUrl($document->getFile()));
28+
$node->setUrl($renderContext->generateCanonicalOutputUrl($document->getFile()));
2929
if ($node->getValue() === '') {
3030
$node->setValue($document->getTitle()->toString());
3131
}

packages/guides/src/ReferenceResolvers/RefReferenceResolver.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ public function resolve(LinkInlineNode $node, RenderContext $renderContext): boo
2323
return false;
2424
}
2525

26-
$node->setUrl($renderContext->relativeDocUrl($target->getDocumentPath(), $target->getAnchor()));
26+
$node->setUrl($renderContext->generateCanonicalOutputUrl($target->getDocumentPath(), $target->getAnchor()));
2727
if ($node->getValue() === '') {
2828
$node->setValue($target->getTitle() ?? '');
2929
}

packages/guides/src/RenderContext.php

Lines changed: 14 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -15,15 +15,12 @@
1515

1616
use Exception;
1717
use League\Flysystem\FilesystemInterface;
18-
use League\Uri\Uri;
19-
use League\Uri\UriInfo;
2018
use phpDocumentor\Guides\Nodes\DocumentNode;
2119
use phpDocumentor\Guides\Nodes\DocumentTree\DocumentEntryNode;
2220
use phpDocumentor\Guides\Nodes\Node;
2321
use phpDocumentor\Guides\Nodes\ProjectNode;
2422

2523
use function dirname;
26-
use function ltrim;
2724
use function trim;
2825

2926
class RenderContext
@@ -35,7 +32,7 @@ class RenderContext
3532
private array $allDocuments;
3633

3734
private function __construct(
38-
string $outputFolder,
35+
private readonly string $outputFolder,
3936
private readonly string $currentFileName,
4037
private readonly FilesystemInterface $origin,
4138
private readonly FilesystemInterface $destination,
@@ -97,22 +94,22 @@ public function canonicalUrl(string $url): string
9794
return $this->urlGenerator->canonicalUrl($this->getDirName(), $url);
9895
}
9996

100-
public function relativeDocUrl(string $filename, string|null $anchor = null): string
97+
/**
98+
* Generate a canonical output URL with the configured file extension and anchor
99+
*/
100+
public function generateCanonicalOutputUrl(string $linkedDocument, string|null $anchor = null): string
101101
{
102-
if (UriInfo::isAbsolutePath(Uri::createFromString($filename))) {
103-
return $this->destinationPath . $this->urlGenerator->createFileUrl($filename, $this->outputFormat, $anchor);
104-
}
105-
106-
$baseUrl = ltrim($this->urlGenerator->absoluteUrl($this->destinationPath, $this->getDirName()), '/');
107-
108-
if ($this->projectNode->findDocumentEntry($filename) !== null) {
109-
return $this->destinationPath . '/'
110-
. $this->urlGenerator->createFileUrl($filename, $this->outputFormat, $anchor);
102+
if ($this->projectNode->findDocumentEntry($linkedDocument) !== null) {
103+
// todo: this is a hack, existing documents are expected to be handled like absolute links in some places
104+
$linkedDocument = '/' . $linkedDocument;
111105
}
112106

113-
return $this->urlGenerator->canonicalUrl(
114-
$baseUrl,
115-
$this->urlGenerator->createFileUrl($filename, $this->outputFormat, $anchor),
107+
return $this->urlGenerator->generateOutputUrlFromDocumentPath(
108+
$this->getDirName(),
109+
$this->outputFolder,
110+
$linkedDocument,
111+
$this->outputFormat,
112+
$anchor,
116113
);
117114
}
118115

packages/guides/src/Twig/AssetsExtension.php

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -114,7 +114,7 @@ public function renderNode(array $context, Node|array|null $node): string
114114
public function renderTarget(array $context, Target $target): string
115115
{
116116
if ($target instanceof InternalTarget) {
117-
return $this->getRenderContext($context)->relativeDocUrl($target->getDocumentPath(), $target->getAnchor());
117+
return $this->getRenderContext($context)->generateCanonicalOutputUrl($target->getDocumentPath(), $target->getAnchor());
118118
}
119119

120120
return $target->getUrl();
@@ -146,7 +146,7 @@ public function renderMenu(array $context, string $menuType, int $maxMenuCount =
146146
/** @param array{env: RenderContext} $context */
147147
public function renderLink(array $context, string $url, string|null $anchor = null): string
148148
{
149-
return $this->getRenderContext($context)->relativeDocUrl($url, $anchor);
149+
return $this->getRenderContext($context)->generateCanonicalOutputUrl($url, $anchor);
150150
}
151151

152152
private function copyAsset(

packages/guides/src/UrlGenerator.php

Lines changed: 25 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
use function explode;
2020
use function implode;
2121
use function ltrim;
22+
use function rtrim;
2223
use function trim;
2324

2425
final class UrlGenerator implements UrlGeneratorInterface
@@ -50,8 +51,6 @@ public function absoluteUrl(string $basePath, string $url): string
5051
* identifier to find the metadata for that file. Technically speaking, the canonical URL is the absolute URL
5152
* without the preceeding slash. But due to the many locations that this method is used; it will do its own
5253
* resolving.
53-
*
54-
* @todo simplify this method into the other methods or vice versa
5554
*/
5655
public function canonicalUrl(string $basePath, string $url): string
5756
{
@@ -84,4 +83,28 @@ public function createFileUrl(string $filename, string $outputFormat = 'html', s
8483
return $filename . '.' . $outputFormat .
8584
($anchor !== null ? '#' . $anchor : '');
8685
}
86+
87+
/**
88+
* Generate a canonical output URL with file extension, anchor and prefixed by
89+
* an absolute or relative path
90+
*/
91+
public function generateOutputUrlFromDocumentPath(
92+
string $currentDirectory,
93+
string $destinationPath,
94+
string $linkedDocument,
95+
string $outputFormat,
96+
string|null $anchor = null,
97+
): string {
98+
$canonicalUrl = $this->canonicalUrl(
99+
$currentDirectory,
100+
$linkedDocument,
101+
);
102+
103+
$fileUrl = $this->createFileUrl($canonicalUrl, $outputFormat, $anchor);
104+
if ($destinationPath === '') {
105+
return $fileUrl;
106+
}
107+
108+
return rtrim($destinationPath, '/') . '/' . $fileUrl;
109+
}
87110
}

packages/guides/src/UrlGeneratorInterface.php

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -21,13 +21,23 @@ public function absoluteUrl(string $basePath, string $url): string;
2121
* identifier to find the metadata for that file. Technically speaking, the canonical URL is the absolute URL
2222
* without the preceeding slash. But due to the many locations that this method is used; it will do its own
2323
* resolving.
24-
*
25-
* @todo simplify this method into the other methods or vice versa
2624
*/
2725
public function canonicalUrl(string $basePath, string $url): string;
2826

2927
/**
3028
* Create a url with a file ending derived from the output format
3129
*/
3230
public function createFileUrl(string $filename, string $outputFormat = 'html', string|null $anchor = null): string;
31+
32+
/**
33+
* Generate a canonical output URL with file extension, anchor and prefixed by
34+
* an absolute or relative path
35+
*/
36+
public function generateOutputUrlFromDocumentPath(
37+
string $currentDirectory,
38+
string $destinationPath,
39+
string $linkedDocument,
40+
string $outputFormat,
41+
string|null $anchor = null,
42+
): string;
3343
}

packages/guides/tests/unit/RenderContextTest.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,7 @@ public function testRelativeDocUrl(
4343
$projectNode,
4444
);
4545

46-
self::assertSame($result, $context->relativeDocUrl($linkedDocument, $anchor));
46+
self::assertSame($result, $context->generateCanonicalOutputUrl($linkedDocument, $anchor));
4747
}
4848

4949
/** @return string[][] */

packages/guides/tests/unit/UrlGeneratorTest.php

Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -137,4 +137,84 @@ public static function abstractUrlProvider(): array
137137
],
138138
];
139139
}
140+
141+
#[DataProvider('documentPathProvider')]
142+
public function testRelativeDocUrl(
143+
string $currentDirectory,
144+
string $destinationPath,
145+
string $linkedDocument,
146+
string $result,
147+
string|null $anchor = null,
148+
): void {
149+
$urlGenerator = new UrlGenerator();
150+
self::assertSame($result, $urlGenerator->generateOutputUrlFromDocumentPath(
151+
$currentDirectory,
152+
$destinationPath,
153+
$linkedDocument,
154+
'txt',
155+
$anchor,
156+
));
157+
}
158+
159+
/** @return array<string, array<string, bool|string>> */
160+
public static function documentPathProvider(): array
161+
{
162+
return [
163+
'relative document' => [
164+
'currentDirectory' => 'getting-started',
165+
'destinationPath' => 'guide',
166+
'linkedDocument' => 'installing',
167+
'result' => 'guide/getting-started/installing.txt',
168+
],
169+
'absolute document path' => [
170+
'currentDirectory' => 'getting-started',
171+
'destinationPath' => 'guide',
172+
'linkedDocument' => '/installing',
173+
'result' => 'guide/installing.txt',
174+
],
175+
'absolute document path with anchor' => [
176+
'currentDirectory' => 'getting-started',
177+
'destinationPath' => 'guide',
178+
'linkedDocument' => '/getting-started/configuration',
179+
'result' => 'guide/getting-started/configuration.txt#composer',
180+
'anchor' => 'composer',
181+
],
182+
'relative document path up in directory' => [
183+
'currentDirectory' => 'getting-started',
184+
'destinationPath' => 'guide',
185+
'linkedDocument' => '../references/installing',
186+
'result' => 'guide/references/installing.txt',
187+
],
188+
'relative document path up in subdirectory' => [
189+
'currentDirectory' => 'getting-started/something',
190+
'destinationPath' => 'guide',
191+
'linkedDocument' => '../references/installing',
192+
'result' => 'guide/getting-started/references/installing.txt',
193+
],
194+
'relative document path two up in directory' => [
195+
'currentDirectory' => 'getting-started/something',
196+
'destinationPath' => 'guide',
197+
'linkedDocument' => '../../references/installing',
198+
'result' => 'guide/references/installing.txt',
199+
],
200+
'Empty destination' => [
201+
'currentDirectory' => 'getting-started/something',
202+
'destinationPath' => '',
203+
'linkedDocument' => '../../references/installing',
204+
'result' => 'references/installing.txt',
205+
],
206+
'Destination is empty absolute path' => [
207+
'currentDirectory' => 'getting-started/something',
208+
'destinationPath' => '/',
209+
'linkedDocument' => '../../references/installing',
210+
'result' => '/references/installing.txt',
211+
],
212+
'Destination is absolute' => [
213+
'currentDirectory' => 'getting-started/something',
214+
'destinationPath' => '/guide/',
215+
'linkedDocument' => '../../references/installing',
216+
'result' => '/guide/references/installing.txt',
217+
],
218+
];
219+
}
140220
}

0 commit comments

Comments
 (0)