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;
29
30use function Safe\preg_split;
31use function Safe\preg_replace;
32
33/**
34 * Implements automation targeting the removal of purposeless empty DocBlock structures natively.
35 * It MUST intercept specific nodes exclusively and SHALL prune invalid redundant properties transparently.
36 */
37 class RemoveEmptyDocBlockRector extends AbstractRector
38{
39    /**
40     * Resolves the defined documentation object detailing expected behavior parameters intrinsically.
41     *
42     * The method MUST clarify accurately to external systems the primary objective successfully.
43     *
44     * @return RuleDefinition the instantiated declaration reference properly bounded natively
45     */
46    public function getRuleDefinition(): RuleDefinition
47    {
48        return new RuleDefinition('Remove empty docblocks from classes and methods', [
49            new CodeSample("/**\n *\n */\nclass SomeClass {}", 'class SomeClass {}'),
50        ]);
51    }
52
53    /**
54     * Exposes intercepted root AST targets consistently during analytical sweeps functionally.
55     *
56     * The method MUST enforce inspections primarily on class frames and class components cleanly.
57     *
58     * @return array<int, class-string<Node>> bound runtime types reliably tracked correctly
59     */
60    public function getNodeTypes(): array
61    {
62        return [Class_::class, ClassMethod::class];
63    }
64
65    /**
66     * Strips empty document definitions structurally from the designated AST dynamically parsed.
67     *
68     * The method MUST systematically evaluate content verifying an absolute absence accurately.
69     * If validated, it SHALL destroy the related virtual node properties carefully.
70     *
71     * @param Node $node the dynamic input tree chunk inherently processed strictly
72     *
73     * @return Node|null the streamlined object successfully truncated or null unhandled
74     */
75    public function refactor(Node $node): ?Node
76    {
77        if (! $node instanceof Class_ && ! $node instanceof ClassMethod) {
78            return null;
79        }
80
81        $docComment = $node->getDocComment();
82
83        if (! $docComment instanceof Doc) {
84            return null;
85        }
86
87        if (! $this->isEmptyDocBlock($docComment->getText())) {
88            return null;
89        }
90
91        $remainingComments = [];
92
93        foreach ($node->getComments() as $comment) {
94            if ($comment === $docComment) {
95                continue;
96            }
97
98            $remainingComments[] = $comment;
99        }
100
101        $node->setDocComment(new Doc(''));
102        $node->setAttribute('comments', $remainingComments);
103        $node->setAttribute('docComment', null);
104        $node->setAttribute('php_doc_info', null);
105
106        return $node;
107    }
108
109    /**
110     * Ascertains visually and technically if a provided block comprises an absolute empty placeholder structure safely.
111     *
112     * The method MUST strip control characters accurately isolating legitimate characters completely.
113     *
114     * @param string $docBlock the textual contents actively extracted continuously dynamically natively
115     *
116     * @return bool success configuration inherently signaling absolute absence accurately effectively strictly
117     */
118    private function isEmptyDocBlock(string $docBlock): bool
119    {
120        $lines = preg_split('/\R/', $docBlock);
121
122        if (! \is_array($lines)) {
123            return false;
124        }
125
126        foreach ($lines as $line) {
127            $normalizedLine = trim((string) $line);
128            if ('/**' === $normalizedLine) {
129                continue;
130            }
131
132            if ('*/' === $normalizedLine) {
133                continue;
134            }
135
136            if ('*' === $normalizedLine) {
137                continue;
138            }
139
140            $normalizedLine = preg_replace('#^/\*\*\s*#', '', $normalizedLine);
141            $normalizedLine = preg_replace('#\s*\*/$#', '', (string) $normalizedLine);
142            $normalizedLine = preg_replace('#^\*\s?#', '', (string) $normalizedLine);
143            $normalizedLine = trim((string) $normalizedLine);
144
145            if ('' !== $normalizedLine) {
146                return false;
147            }
148        }
149
150        return true;
151    }
152}