Skip to content

Commit 07f7496

Browse files
authored
Merge pull request #33 from PcComponentes/feature/RequireSingleLineMethod
feat: RequireSingleLineMethod rule
2 parents e2199ab + b034d5c commit 07f7496

File tree

2 files changed

+146
-0
lines changed

2 files changed

+146
-0
lines changed
Lines changed: 143 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,143 @@
1+
<?php
2+
declare(strict_types=1);
3+
4+
namespace PcComponentesCodingStandard\Sniffs\Classes;
5+
6+
use Exception;
7+
use PHP_CodeSniffer\Files\File;
8+
use SlevomatCodingStandard\Helpers\FixerHelper;
9+
use SlevomatCodingStandard\Helpers\FunctionHelper;
10+
use SlevomatCodingStandard\Helpers\SniffSettingsHelper;
11+
use SlevomatCodingStandard\Sniffs\Classes\RequireSingleLineMethodSignatureSniff;
12+
use function count;
13+
use function preg_match;
14+
use function sprintf;
15+
use function strlen;
16+
use const T_ATTRIBUTE;
17+
18+
/**
19+
* Custom sniff that requires single-line method signatures,
20+
* except when attributes are present in parameters.
21+
*
22+
* Based on SlevomatCodingStandard\Sniffs\Classes\RequireSingleLineMethodSignatureSniff (v8.22.1)
23+
*/
24+
class RequireSingleLineMethodSignatureExceptWithAttributesSniff extends RequireSingleLineMethodSignatureSniff
25+
{
26+
public function process(File $phpcsFile, $methodPointer): void
27+
{
28+
$this->maxLineLength = SniffSettingsHelper::normalizeInteger($this->maxLineLength);
29+
30+
if (!FunctionHelper::isMethod($phpcsFile, $methodPointer)) {
31+
return;
32+
}
33+
34+
$tokens = $phpcsFile->getTokens();
35+
36+
[$signatureStartPointer, $signatureEndPointer] = $this->getSignatureStartAndEndPointers($phpcsFile, $methodPointer);
37+
38+
if ($tokens[$signatureStartPointer]['line'] === $tokens[$signatureEndPointer]['line']) {
39+
return;
40+
}
41+
42+
#region CUSTOM: Check if there are attributes in the method parameters
43+
if ($this->hasAttributesInParameters($phpcsFile, $methodPointer)) {
44+
// Allow multi-line when attributes are present
45+
return;
46+
}
47+
#endregion
48+
49+
$signature = $this->getSignature($phpcsFile, $signatureStartPointer, $signatureEndPointer);
50+
$methodName = FunctionHelper::getName($phpcsFile, $methodPointer);
51+
52+
if (
53+
count($this->includedMethodPatterns) !== 0
54+
&& !$this->isMethodNameInPatterns($methodName, $this->getIncludedMethodNormalizedPatterns())
55+
) {
56+
return;
57+
}
58+
59+
if (
60+
count($this->excludedMethodPatterns) !== 0
61+
&& $this->isMethodNameInPatterns($methodName, $this->getExcludedMethodNormalizedPatterns())
62+
) {
63+
return;
64+
}
65+
66+
if ($this->maxLineLength !== 0 && strlen($signature) > $this->maxLineLength) {
67+
return;
68+
}
69+
70+
$error = sprintf('Signature of method "%s" should be placed on a single line.', $methodName);
71+
$fix = $phpcsFile->addFixableError($error, $methodPointer, self::CODE_REQUIRED_SINGLE_LINE_SIGNATURE);
72+
if (!$fix) {
73+
return;
74+
}
75+
76+
$phpcsFile->fixer->beginChangeset();
77+
78+
FixerHelper::change($phpcsFile, $signatureStartPointer, $signatureEndPointer, $signature);
79+
80+
$phpcsFile->fixer->endChangeset();
81+
}
82+
83+
/**
84+
* CUSTOM: Check if method has attributes in parameters
85+
*/
86+
private function hasAttributesInParameters(File $phpcsFile, int $methodPointer): bool
87+
{
88+
$tokens = $phpcsFile->getTokens();
89+
90+
if (!isset($tokens[$methodPointer]['parenthesis_opener'])
91+
|| !isset($tokens[$methodPointer]['parenthesis_closer'])
92+
) {
93+
return false;
94+
}
95+
96+
$openParenthesis = $tokens[$methodPointer]['parenthesis_opener'];
97+
$closeParenthesis = $tokens[$methodPointer]['parenthesis_closer'];
98+
99+
for ($i = $openParenthesis + 1; $i < $closeParenthesis; $i++) {
100+
if ($tokens[$i]['code'] === T_ATTRIBUTE) {
101+
return true;
102+
}
103+
}
104+
105+
return false;
106+
}
107+
108+
/**
109+
* @param list<string> $normalizedPatterns
110+
*/
111+
private function isMethodNameInPatterns(string $methodName, array $normalizedPatterns): bool
112+
{
113+
foreach ($normalizedPatterns as $pattern) {
114+
if (!SniffSettingsHelper::isValidRegularExpression($pattern)) {
115+
throw new Exception(sprintf('%s is not valid PCRE pattern.', $pattern));
116+
}
117+
118+
if (preg_match($pattern, $methodName) !== 0) {
119+
return true;
120+
}
121+
}
122+
123+
return false;
124+
}
125+
126+
/**
127+
* @return list<string>
128+
*/
129+
private function getIncludedMethodNormalizedPatterns(): array
130+
{
131+
$this->includedMethodNormalizedPatterns ??= SniffSettingsHelper::normalizeArray($this->includedMethodPatterns);
132+
return $this->includedMethodNormalizedPatterns;
133+
}
134+
135+
/**
136+
* @return list<string>
137+
*/
138+
private function getExcludedMethodNormalizedPatterns(): array
139+
{
140+
$this->excludedMethodNormalizedPatterns ??= SniffSettingsHelper::normalizeArray($this->excludedMethodPatterns);
141+
return $this->excludedMethodNormalizedPatterns;
142+
}
143+
}

src/ruleset.xml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
<exclude name="SlevomatCodingStandard.Classes.ClassConstantVisibility" />
88
<exclude name="SlevomatCodingStandard.Classes.ClassLength.ClassTooLong" />
99
<exclude name="SlevomatCodingStandard.Classes.DisallowConstructorPropertyPromotion" />
10+
<exclude name="SlevomatCodingStandard.Classes.RequireSingleLineMethodSignature" />
1011
<exclude name="SlevomatCodingStandard.Classes.SuperfluousExceptionNaming" />
1112
<exclude name="SlevomatCodingStandard.Commenting.DisallowCommentAfterCode" />
1213
<exclude name="SlevomatCodingStandard.Complexity.Cognitive.ComplexityTooHigh" />
@@ -102,4 +103,6 @@
102103
</property>
103104
</properties>
104105
</rule>
106+
<!-- Custom rules -->
107+
<rule ref="./Sniffs/Classes/RequireSingleLineMethodSignatureExceptWithAttributesSniff.php"/>
105108
</ruleset>

0 commit comments

Comments
 (0)