Skip to content

Commit 6f07223

Browse files
authored
Merge pull request #918 from phpDocumentor/backport/1.x/pr-914
[1.x] Merge pull request #914 from phpDocumentor/feature/image-target
2 parents 4b10c80 + c34759b commit 6f07223

File tree

11 files changed

+611
-1
lines changed

11 files changed

+611
-1
lines changed

packages/guides-restructured-text/src/RestructuredText/Directives/ImageDirective.php

Lines changed: 43 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,12 +14,21 @@
1414
namespace phpDocumentor\Guides\RestructuredText\Directives;
1515

1616
use phpDocumentor\Guides\Nodes\ImageNode;
17+
use phpDocumentor\Guides\Nodes\Inline\DocReferenceNode;
18+
use phpDocumentor\Guides\Nodes\Inline\HyperLinkNode;
19+
use phpDocumentor\Guides\Nodes\Inline\LinkInlineNode;
20+
use phpDocumentor\Guides\Nodes\Inline\ReferenceNode;
1721
use phpDocumentor\Guides\Nodes\Node;
1822
use phpDocumentor\Guides\ReferenceResolvers\DocumentNameResolverInterface;
1923
use phpDocumentor\Guides\RestructuredText\Parser\BlockContext;
2024
use phpDocumentor\Guides\RestructuredText\Parser\Directive;
2125

2226
use function dirname;
27+
use function filter_var;
28+
use function preg_match;
29+
30+
use const FILTER_VALIDATE_EMAIL;
31+
use const FILTER_VALIDATE_URL;
2332

2433
/**
2534
* Renders an image, example :
@@ -30,6 +39,12 @@
3039
*/
3140
final class ImageDirective extends BaseDirective
3241
{
42+
/** @see https://regex101.com/r/9dUrzu/3 */
43+
public const REFERENCE_REGEX = '/^([a-zA-Z0-9-_]+)_$/';
44+
45+
/** @see https://regex101.com/r/6vPoiA/2 */
46+
public const REFERENCE_ESCAPED_REGEX = '/^`([^`]+)`_$/';
47+
3348
public function __construct(
3449
private readonly DocumentNameResolverInterface $documentNameResolver,
3550
) {
@@ -45,11 +60,38 @@ public function processNode(
4560
BlockContext $blockContext,
4661
Directive $directive,
4762
): Node {
48-
return new ImageNode(
63+
$node = new ImageNode(
4964
$this->documentNameResolver->absoluteUrl(
5065
dirname($blockContext->getDocumentParserContext()->getContext()->getCurrentAbsolutePath()),
5166
$directive->getData(),
5267
),
5368
);
69+
if ($directive->hasOption('target')) {
70+
$targetReference = (string) $directive->getOption('target')->getValue();
71+
$node->setTarget($this->resolveLinkTarget($targetReference));
72+
}
73+
74+
return $node;
75+
}
76+
77+
private function resolveLinkTarget(string $targetReference): LinkInlineNode
78+
{
79+
if (filter_var($targetReference, FILTER_VALIDATE_EMAIL)) {
80+
return new HyperLinkNode('', $targetReference);
81+
}
82+
83+
if (filter_var($targetReference, FILTER_VALIDATE_URL)) {
84+
return new HyperLinkNode('', $targetReference);
85+
}
86+
87+
if (preg_match(self::REFERENCE_REGEX, $targetReference, $matches)) {
88+
return new ReferenceNode($matches[1], '');
89+
}
90+
91+
if (preg_match(self::REFERENCE_ESCAPED_REGEX, $targetReference, $matches)) {
92+
return new ReferenceNode($matches[1], '');
93+
}
94+
95+
return new DocReferenceNode($targetReference, '');
5496
}
5597
}

packages/guides/resources/config/guides.php

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@
2828
use phpDocumentor\Guides\ReferenceResolvers\DocumentNameResolverInterface;
2929
use phpDocumentor\Guides\ReferenceResolvers\EmailReferenceResolver;
3030
use phpDocumentor\Guides\ReferenceResolvers\ExternalReferenceResolver;
31+
use phpDocumentor\Guides\ReferenceResolvers\ImageReferenceResolverPreRender;
3132
use phpDocumentor\Guides\ReferenceResolvers\Interlink\DefaultInventoryLoader;
3233
use phpDocumentor\Guides\ReferenceResolvers\Interlink\DefaultInventoryRepository;
3334
use phpDocumentor\Guides\ReferenceResolvers\Interlink\InventoryLoader;
@@ -192,6 +193,8 @@
192193

193194
->set(ReferenceResolverPreRender::class)
194195
->tag('phpdoc.guides.prerenderer')
196+
->set(ImageReferenceResolverPreRender::class)
197+
->tag('phpdoc.guides.prerenderer')
195198

196199
->set(InMemoryRendererFactory::class)
197200
->arg('$renderSets', tagged_iterator('phpdoc.renderer.typerenderer', 'format'))

packages/guides/resources/template/html/body/image.html.twig

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
{% if node.target %}<a href="{{ node.target.url }}">{% endif %}
12
<img
23
src="{%- if node.value is external_target -%} {{ node.value }} {%- else -%} {{ asset(node.value) }} {%- endif -%}"
34
{% if node.hasOption('width') %}width="{{ node.option('width') }}"{% endif%}
@@ -7,3 +8,4 @@
78
{% if node.hasOption('title') %}title="{{ node.option('title') }}"{% endif%}
89
{% if node.classesString %}class="{{ node.classesString }}"{% endif%}
910
/>
11+
{% if node.target %}</a>{% endif %}

packages/guides/src/Nodes/ImageNode.php

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,19 @@
1313

1414
namespace phpDocumentor\Guides\Nodes;
1515

16+
use phpDocumentor\Guides\Nodes\Inline\LinkInlineNode;
17+
1618
final class ImageNode extends TextNode
1719
{
20+
public LinkInlineNode|null $target = null;
21+
22+
public function getTarget(): LinkInlineNode|null
23+
{
24+
return $this->target;
25+
}
26+
27+
public function setTarget(LinkInlineNode|null $target): void
28+
{
29+
$this->target = $target;
30+
}
1831
}
Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
/**
6+
* This file is part of phpDocumentor.
7+
*
8+
* For the full copyright and license information, please view the LICENSE
9+
* file that was distributed with this source code.
10+
*
11+
* @link https://phpdoc.org
12+
*/
13+
14+
namespace phpDocumentor\Guides\ReferenceResolvers;
15+
16+
use phpDocumentor\Guides\NodeRenderers\PreRenderers\PreNodeRenderer;
17+
use phpDocumentor\Guides\Nodes\ImageNode;
18+
use phpDocumentor\Guides\Nodes\Node;
19+
use phpDocumentor\Guides\RenderContext;
20+
use Psr\Log\LoggerInterface;
21+
use Webmozart\Assert\Assert;
22+
23+
use function array_merge;
24+
use function sprintf;
25+
26+
final class ImageReferenceResolverPreRender implements PreNodeRenderer
27+
{
28+
public function __construct(
29+
private readonly DelegatingReferenceResolver $referenceResolver,
30+
private readonly LoggerInterface $logger,
31+
) {
32+
}
33+
34+
public function supports(Node $node): bool
35+
{
36+
return $node instanceof ImageNode;
37+
}
38+
39+
public function execute(Node $node, RenderContext $renderContext): Node
40+
{
41+
Assert::isInstanceOf($node, ImageNode::class);
42+
if ($node->getTarget() === null) {
43+
return $node;
44+
}
45+
46+
$referenceLinkNode = $node->getTarget();
47+
$messages = new Messages();
48+
$resolved = $this->referenceResolver->resolve($referenceLinkNode, $renderContext, $messages);
49+
if (!$resolved) {
50+
$this->logger->warning(
51+
$messages->getLastWarning()?->getMessage() ?? sprintf(
52+
'Target %s of image could not be resolved in %s',
53+
$referenceLinkNode->getTargetReference(),
54+
$renderContext->getCurrentFileName(),
55+
),
56+
array_merge($renderContext->getLoggerInformation(), $messages->getLastWarning()?->getDebugInfo() ?? []),
57+
);
58+
}
59+
60+
return $node;
61+
}
62+
}
Lines changed: 180 additions & 0 deletions
Loading

0 commit comments

Comments
 (0)