Skip to content

Commit

Permalink
[FEATURE] Link to php-methods (#30)
Browse files Browse the repository at this point in the history
The ids also have to contain the fqn name, as different components can have a method of the same name.
  • Loading branch information
linawolf authored Dec 17, 2023
1 parent bed620c commit 2b39f5e
Show file tree
Hide file tree
Showing 44 changed files with 490 additions and 215 deletions.
7 changes: 7 additions & 0 deletions resources/config/php-domain.php
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
use phpDocumentor\Guides\RestructuredText\Parser\Productions\DirectiveContentRule;
use Symfony\Component\DependencyInjection\Loader\Configurator\ContainerConfigurator;

use T3Docs\GuidesPhpDomain\Compiler\NodeTransformers\MemberNodeTransformer;
use T3Docs\GuidesPhpDomain\Directives\Php\CaseDirective;
use T3Docs\GuidesPhpDomain\Directives\Php\ClassDirective;
use T3Docs\GuidesPhpDomain\Directives\Php\ConstDirective;
Expand All @@ -19,6 +20,7 @@
use T3Docs\GuidesPhpDomain\TextRoles\ClassTextRole;
use T3Docs\GuidesPhpDomain\TextRoles\EnumTextRole;
use T3Docs\GuidesPhpDomain\TextRoles\ExceptionTextRole;
use T3Docs\GuidesPhpDomain\TextRoles\MethodTextRole;
use T3Docs\GuidesPhpDomain\TextRoles\TraitTextRole;
use function Symfony\Component\DependencyInjection\Loader\Configurator\service;

Expand Down Expand Up @@ -57,6 +59,9 @@
->set(ModifierService::class)
->set(NamespaceRepository::class)

->set(MemberNodeTransformer::class)
->tag('phpdoc.guides.compiler.nodeTransformers')

->set(ClassTextRole::class)
->tag('phpdoc.guides.parser.rst.text_role', ['domain' => 'php'])
->set(EnumTextRole::class)
Expand All @@ -65,6 +70,8 @@
->tag('phpdoc.guides.parser.rst.text_role', ['domain' => 'php'])
->set(InterfaceTextRole::class)
->tag('phpdoc.guides.parser.rst.text_role', ['domain' => 'php'])
->set(MethodTextRole::class)
->tag('phpdoc.guides.parser.rst.text_role', ['domain' => 'php'])
->set(TraitTextRole::class)
->tag('phpdoc.guides.parser.rst.text_role', ['domain' => 'php'])
;
Expand Down
74 changes: 74 additions & 0 deletions src/Compiler/NodeTransformers/MemberNodeTransformer.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
<?php

declare(strict_types=1);

namespace T3Docs\GuidesPhpDomain\Compiler\NodeTransformers;

use phpDocumentor\Guides\Compiler\CompilerContext;
use phpDocumentor\Guides\Compiler\NodeTransformer;
use phpDocumentor\Guides\Nodes\ClassNode;
use phpDocumentor\Guides\Nodes\DocumentNode;
use phpDocumentor\Guides\Nodes\Node;

use phpDocumentor\Guides\ReferenceResolvers\AnchorReducer;
use Psr\Log\LoggerInterface;
use T3Docs\GuidesPhpDomain\Nodes\PhpComponentNode;
use T3Docs\GuidesPhpDomain\Nodes\PhpMemberNode;

use function array_merge;

/**
* @implements NodeTransformer<Node>
*
* The "class" directive sets the "classes" attribute value on its content or on the first immediately following
* non-comment element. https://docutils.sourceforge.io/docs/ref/rst/directives.html#class
*/
class MemberNodeTransformer implements NodeTransformer
{
private ?PhpComponentNode $currentComponent = null;

public function __construct(
private readonly LoggerInterface $logger,
private readonly AnchorReducer $anchorReducer,
) {}

public function enterNode(Node $node, CompilerContext $compilerContext): Node
{
if ($node instanceof PhpComponentNode) {
if ($this->currentComponent instanceof \T3Docs\GuidesPhpDomain\Nodes\PhpComponentNode) {
$this->logger->warning(
sprintf('Nested PHP domain components (php:class, php:interface, php:enum etc) are not supported.
Found php:%s inside %s', $node->toString(), $this->currentComponent->toString()),
$compilerContext->getLoggerInformation()
);
}
$this->currentComponent = $node;
return $node;
}

if ($node instanceof PhpMemberNode && $this->currentComponent instanceof PhpComponentNode) {
$newId = $this->anchorReducer->reduceAnchor($this->currentComponent->getId() . '::' . $node->getName());
return $node->withId($newId);
}

return $node;
}

public function leaveNode(Node $node, CompilerContext $compilerContext): Node|null
{
if ($node instanceof PhpComponentNode) {
$this->currentComponent = null;
}
return $node;
}

public function supports(Node $node): bool
{
return $node instanceof PhpMemberNode || $node instanceof PhpComponentNode;
}

public function getPriority(): int
{
return 40_000;
}
}
21 changes: 19 additions & 2 deletions src/Nodes/PhpMemberNode.php
Original file line number Diff line number Diff line change
Expand Up @@ -5,29 +5,46 @@
namespace T3Docs\GuidesPhpDomain\Nodes;

use phpDocumentor\Guides\Nodes\CompoundNode;
use phpDocumentor\Guides\Nodes\LinkTargetNode;
use phpDocumentor\Guides\Nodes\Node;

/**
* Stores data on constants, methods and properties
*
* @extends CompoundNode<Node>
*/
abstract class PhpMemberNode extends CompoundNode
abstract class PhpMemberNode extends CompoundNode implements LinkTargetNode
{
public function __construct(
private readonly string $id,
private string $id,
private readonly string $type,
private readonly string $name,
array $value = [],
) {
parent::__construct($value);
}

public function withId(string $id): PhpMemberNode
{
$clone = clone($this);
$clone->id = $id;
return $clone;
}

public function getName(): string
{
return $this->name;
}

public function getLinkType(): string
{
return 'php:' . $this->type;
}
public function getLinkText(): string
{
return $this->name;
}

public function getType(): string
{
return $this->type;
Expand Down
59 changes: 59 additions & 0 deletions src/TextRoles/MethodTextRole.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
<?php

declare(strict_types=1);

namespace T3Docs\GuidesPhpDomain\TextRoles;

use Doctrine\Common\Lexer\Token;
use phpDocumentor\Guides\Nodes\Inline\AbstractLinkInlineNode;
use phpDocumentor\Guides\Nodes\Inline\ReferenceNode;
use phpDocumentor\Guides\ReferenceResolvers\AnchorReducer;
use phpDocumentor\Guides\RestructuredText\Parser\DocumentParserContext;
use phpDocumentor\Guides\RestructuredText\Parser\InlineLexer;
use phpDocumentor\Guides\RestructuredText\TextRoles\TextRole;
use Psr\Log\LoggerInterface;

final class MethodTextRole extends PhpComponentTextRole
{
private const TYPE = 'method';

/**
* @see https://regex101.com/r/EKNh6v/1
*/
private const METHOD_NAME_REGEX = '/^([a-zA-Z0-9_\\\]+)\:\:(\w+)(\(.*\)){0,1}$/';
public function __construct(
LoggerInterface $logger,
private readonly AnchorReducer $anchorReducer,
) {
parent::__construct($logger, $anchorReducer);
}

protected function createNode(DocumentParserContext $documentParserContext, string $referenceTarget, string|null $referenceName, string $role): ReferenceNode
{
if (preg_match(self::INTERLINK_NAME_REGEX, $referenceTarget, $matches)) {
return $this->createNodeWithInterlink($documentParserContext, $matches[2], $matches[1], $referenceName);
}
return $this->createNodeWithInterlink($documentParserContext, $referenceTarget, '', $referenceName);
}

private function createNodeWithInterlink(DocumentParserContext $documentParserContext, string $referenceTarget, string $interlinkDomain, string|null $referenceName): ReferenceNode
{
if (!preg_match(self::METHOD_NAME_REGEX, $referenceTarget, $matches)) {
$this->logger->warning($referenceTarget . ' is not a valid method name. Use the form "\Vendor\Path\Class::method" or "\Vendor\Path\Class::method(int $param)"', $documentParserContext->getLoggerInformation());
$id = $this->anchorReducer->reduceAnchor($referenceTarget);
return new ReferenceNode($id, $referenceName ?? $referenceTarget, $interlinkDomain, 'php:' . $this->getName());
}

$class = $matches[1];
$method = $matches[2];

$id = $this->anchorReducer->reduceAnchor($class . '::' . $method);

return new ReferenceNode($id, $referenceName ?? $referenceTarget, $interlinkDomain, 'php:' . $this->getName());
}

public function getName(): string
{
return self::TYPE;
}
}
6 changes: 3 additions & 3 deletions src/TextRoles/PhpComponentTextRole.php
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ abstract class PhpComponentTextRole implements TextRole
/**
* @see https://regex101.com/r/OyN05v/1
*/
private const INTERLINK_NAME_REGEX = '/^([a-zA-Z0-9]+):(.*$)/';
protected const INTERLINK_NAME_REGEX = '/^([a-zA-Z0-9]+):(.*$)/';

private readonly InlineLexer $lexer;

Expand Down Expand Up @@ -94,11 +94,11 @@ public function processNode(
$value = null;
}

return $this->createNode($referenceTarget, $value, $role);
return $this->createNode($documentParserContext, $referenceTarget, $value, $role);
}

/** @return ReferenceNode */
protected function createNode(string $referenceTarget, string|null $referenceName, string $role): AbstractLinkInlineNode
protected function createNode(DocumentParserContext $documentParserContext, string $referenceTarget, string|null $referenceName, string $role): AbstractLinkInlineNode
{
if (preg_match(self::INTERLINK_NAME_REGEX, $referenceTarget, $matches)) {
$interlinkDomain = $matches[1];
Expand Down
3 changes: 1 addition & 2 deletions tests/integration/class-abstract/expected/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,7 @@ <h1>abstract class</h1>
<em class="property"><span class="pre">class</span> </em>
<span class="sig-prename descclassname"><span class="pre">\TYPO3\CMS\Core\</span></span>
<span class="sig-name descname"><span class="pre">AbstractTest</span></span>

</dt>
</dt>
<dd>
<p>Lorem Ipsum Dolor!</p>
</dd>
Expand Down
3 changes: 1 addition & 2 deletions tests/integration/class-final-abstract/expected/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,7 @@ <h1>abstract final class causes warning</h1>
<em class="property"><span class="pre">class</span> </em>
<span class="sig-prename descclassname"><span class="pre">\TYPO3\CMS\Core\</span></span>
<span class="sig-name descname"><span class="pre">Test</span></span>

</dt>
</dt>
<dd>
<p>Lorem Ipsum Dolor!</p>
</dd>
Expand Down
3 changes: 1 addition & 2 deletions tests/integration/class-final/expected/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,7 @@ <h1>final class</h1>
<em class="property"><span class="pre">class</span> </em>
<span class="sig-prename descclassname"><span class="pre">\TYPO3\CMS\Core\</span></span>
<span class="sig-name descname"><span class="pre">Test</span></span>

</dt>
</dt>
<dd>
<p>Lorem Ipsum Dolor!</p>
</dd>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,7 @@ <h1>PHP Class with current namespace from directive</h1>
<em class="property"><span class="pre">class</span> </em>
<span class="sig-prename descclassname"><span class="pre">\TYPO3\CMS\Core\</span></span>
<span class="sig-name descname"><span class="pre">TestClass</span></span>

</dt>
</dt>
<dd>
<p>Lorem Ipsum Dolor!</p>
</dd>
Expand All @@ -21,8 +20,7 @@ <h1>PHP Class with current namespace from directive</h1>
<em class="property"><span class="pre">class</span> </em>
<span class="sig-prename descclassname"><span class="pre">\TYPO3\CMS\Core\</span></span>
<span class="sig-name descname"><span class="pre">AnotherClass</span></span>

</dt>
</dt>
<dd>
<p>Lorem Ipsum Dolor Another!</p>
</dd>
Expand All @@ -34,8 +32,7 @@ <h1>PHP Class with current namespace from directive</h1>
<em class="property"><span class="pre">class</span> </em>
<span class="sig-prename descclassname"><span class="pre">\MyVendor\Some\Namespace\</span></span>
<span class="sig-name descname"><span class="pre">AnotherClass</span></span>

</dt>
</dt>
<dd>
<p>Lorem Ipsum Dolor Yet Another!</p>
</dd>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,11 +8,10 @@ <h1>PHP class with constants</h1>
<em class="property"><span class="pre">class</span> </em>
<span class="sig-prename descclassname"><span class="pre">\ChuckleFactory\</span></span>
<span class="sig-name descname"><span class="pre">FunClass</span></span>

</dt>
</dt>
<dd>
<p>Where Fun Knows No Bounds!</p><dl class="php const">
<dt class="sig sig-object php" id="joy-constant">
<dt class="sig sig-object php" id="chucklefactory-funclass-joy-constant">
<span class="sig-name modifier"><span class="pre">public</span></span>
<span class="sig-name modifier"><span class="pre">private</span></span>
<em class="property"><span class="pre">const</span></em>
Expand All @@ -23,7 +22,7 @@ <h1>PHP class with constants</h1>
a pick-me-up, just access JOY_CONSTANT and let the chuckles begin.</p></dd>
</dl>
<dl class="php const">
<dt class="sig sig-object php" id="giggle-factor">
<dt class="sig sig-object php" id="chucklefactory-funclass-giggle-factor">
<span class="sig-name modifier"><span class="pre">public</span></span>
<span class="sig-name modifier"><span class="pre">protected</span></span>
<em class="property"><span class="pre">const</span></em>
Expand All @@ -34,7 +33,7 @@ <h1>PHP class with constants</h1>
It&#039;s known to spontaneously increase during code reviews and coffee breaks.</p></dd>
</dl>
<dl class="php const">
<dt class="sig sig-object php" id="whimsical-wonder">
<dt class="sig sig-object php" id="chucklefactory-funclass-whimsical-wonder">
<span class="sig-name modifier"><span class="pre">protected</span></span>
<span class="sig-name modifier"><span class="pre">private</span></span>
<em class="property"><span class="pre">const</span></em>
Expand Down
9 changes: 4 additions & 5 deletions tests/integration/class-with-const/expected/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -8,11 +8,10 @@ <h1>PHP class with constants</h1>
<em class="property"><span class="pre">class</span> </em>
<span class="sig-prename descclassname"><span class="pre">\SecretSociety\</span></span>
<span class="sig-name descname"><span class="pre">EnigmaticClass</span></span>

</dt>
</dt>
<dd>
<p>Unveiling the mysteries of constants!</p><dl class="php const">
<dt class="sig sig-object php" id="pi">
<dt class="sig sig-object php" id="secretsociety-enigmaticclass-pi">
<span class="sig-name modifier"><span class="pre">public</span></span>
<em class="property"><span class="pre">const</span></em>
<span class="sig-name descname"><span class="pre">PI</span></span>
Expand All @@ -22,7 +21,7 @@ <h1>PHP class with constants</h1>
to its diameter. Also used as the secret handshake among mathematicians.</p></dd>
</dl>
<dl class="php const">
<dt class="sig sig-object php" id="secret-number">
<dt class="sig sig-object php" id="secretsociety-enigmaticclass-secret-number">
<span class="sig-name modifier"><span class="pre">protected</span></span>
<em class="property"><span class="pre">const</span></em>
<span class="sig-name descname"><span class="pre">SECRET_NUMBER</span></span>
Expand All @@ -33,7 +32,7 @@ <h1>PHP class with constants</h1>
treasure chest.</p></dd>
</dl>
<dl class="php const">
<dt class="sig sig-object php" id="eternal-flame">
<dt class="sig sig-object php" id="secretsociety-enigmaticclass-eternal-flame">
<span class="sig-name modifier"><span class="pre">private</span></span>
<em class="property"><span class="pre">const</span></em>
<span class="sig-name descname"><span class="pre">ETERNAL_FLAME</span></span>
Expand Down
Loading

0 comments on commit 2b39f5e

Please sign in to comment.