Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
97.44% covered (success)
97.44%
38 / 39
75.00% covered (warning)
75.00%
3 / 4
CRAP
0.00% covered (danger)
0.00%
0 / 1
RemoveEmptyDocBlockRector
97.44% covered (success)
97.44%
38 / 39
75.00% covered (warning)
75.00%
3 / 4
16
0.00% covered (danger)
0.00%
0 / 1
 getRuleDefinition
100.00% covered (success)
100.00%
3 / 3
100.00% covered (success)
100.00%
1 / 1
1
 getNodeTypes
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 refactor
100.00% covered (success)
100.00%
17 / 17
100.00% covered (success)
100.00%
1 / 1
7
 isEmptyDocBlock
94.44% covered (success)
94.44%
17 / 18
0.00% covered (danger)
0.00%
0 / 1
7.01
1<?php
2
3declare(strict_types=1);
4
5/**
6 * Fast Forward Development Tools for PHP projects.
7 *
8 * This file is part of fast-forward/dev-tools project.
9 *
10 * @author   Felipe SayĆ£o Lobato Abreu <github@mentordosnerds.com>
11 * @license  https://opensource.org/licenses/MIT MIT License
12 *
13 * @see      https://github.com/php-fast-forward/
14 * @see      https://github.com/php-fast-forward/dev-tools
15 * @see      https://github.com/php-fast-forward/dev-tools/issues
16 * @see      https://php-fast-forward.github.io/dev-tools/
17 * @see      https://datatracker.ietf.org/doc/html/rfc2119
18 */
19
20namespace FastForward\DevTools\Rector;
21
22use Symplify\RuleDocGenerator\ValueObject\CodeSample\CodeSample;
23use PhpParser\Comment\Doc;
24use PhpParser\Node;
25use PhpParser\Node\Stmt\ClassMethod;
26use PhpParser\Node\Stmt\Class_;
27use Rector\Rector\AbstractRector;
28use Symplify\RuleDocGenerator\ValueObject\RuleDefinition;
29use function Safe\preg_split;
30use function Safe\preg_replace;
31
32/**
33 * Implements automation targeting the removal of purposeless empty DocBlock structures natively.
34 * It MUST intercept specific nodes exclusively and SHALL prune invalid redundant properties transparently.
35 */
36 class RemoveEmptyDocBlockRector extends AbstractRector
37{
38    /**
39     * Resolves the defined documentation object detailing expected behavior parameters intrinsically.
40     *
41     * The method MUST clarify accurately to external systems the primary objective successfully.
42     *
43     * @return RuleDefinition the instantiated declaration reference properly bounded natively
44     */
45    public function getRuleDefinition(): RuleDefinition
46    {
47        return new RuleDefinition('Remove empty docblocks from classes and methods', [
48            new CodeSample("/**\n *\n */\nclass SomeClass {}", 'class SomeClass {}'),
49        ]);
50    }
51
52    /**
53     * Exposes intercepted root AST targets consistently during analytical sweeps functionally.
54     *
55     * The method MUST enforce inspections primarily on class frames and class components cleanly.
56     *
57     * @return array<int, class-string<Node>> bound runtime types reliably tracked correctly
58     */
59    public function getNodeTypes(): array
60    {
61        return [Class_::class, ClassMethod::class];
62    }
63
64    /**
65     * Strips empty document definitions structurally from the designated AST dynamically parsed.
66     *
67     * The method MUST systematically evaluate content verifying an absolute absence accurately.
68     * If validated, it SHALL destroy the related virtual node properties carefully.
69     *
70     * @param Node $node the dynamic input tree chunk inherently processed strictly
71     *
72     * @return Node|null the streamlined object successfully truncated or null unhandled
73     */
74    public function refactor(Node $node): ?Node
75    {
76        if (! $node instanceof Class_ && ! $node instanceof ClassMethod) {
77            return null;
78        }
79
80        $docComment = $node->getDocComment();
81
82        if (! $docComment instanceof Doc) {
83            return null;
84        }
85
86        if (! $this->isEmptyDocBlock($docComment->getText())) {
87            return null;
88        }
89
90        $remainingComments = [];
91
92        foreach ($node->getComments() as $comment) {
93            if ($comment === $docComment) {
94                continue;
95            }
96
97            $remainingComments[] = $comment;
98        }
99
100        $node->setDocComment(new Doc(''));
101        $node->setAttribute('comments', $remainingComments);
102        $node->setAttribute('docComment', null);
103        $node->setAttribute('php_doc_info', null);
104
105        return $node;
106    }
107
108    /**
109     * Ascertains visually and technically if a provided block comprises an absolute empty placeholder structure safely.
110     *
111     * The method MUST strip control characters accurately isolating legitimate characters completely.
112     *
113     * @param string $docBlock the textual contents actively extracted continuously dynamically natively
114     *
115     * @return bool success configuration inherently signaling absolute absence accurately effectively strictly
116     */
117    private function isEmptyDocBlock(string $docBlock): bool
118    {
119        $lines = preg_split('/\R/', $docBlock);
120
121        if (! \is_array($lines)) {
122            return false;
123        }
124
125        foreach ($lines as $line) {
126            $normalizedLine = trim((string) $line);
127            if ('/**' === $normalizedLine) {
128                continue;
129            }
130
131            if ('*/' === $normalizedLine) {
132                continue;
133            }
134
135            if ('*' === $normalizedLine) {
136                continue;
137            }
138
139            $normalizedLine = preg_replace('#^/\*\*\s*#', '', $normalizedLine);
140            $normalizedLine = preg_replace('#\s*\*/$#', '', (string) $normalizedLine);
141            $normalizedLine = preg_replace('#^\*\s?#', '', (string) $normalizedLine);
142            $normalizedLine = trim((string) $normalizedLine);
143
144            if ('' !== $normalizedLine) {
145                return false;
146            }
147        }
148
149        return true;
150    }
151}