Skip to content

Commit 788b6c7

Browse files
committed
Add minParametersCount and excludedCallPatterns to RequireMultiLineCallSniff
1 parent 0bac10b commit 788b6c7

File tree

7 files changed

+193
-4
lines changed

7 files changed

+193
-4
lines changed

SlevomatCodingStandard/Sniffs/Functions/RequireMultiLineCallSniff.php

Lines changed: 64 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2,15 +2,18 @@
22

33
namespace SlevomatCodingStandard\Sniffs\Functions;
44

5+
use Exception;
56
use PHP_CodeSniffer\Files\File;
67
use SlevomatCodingStandard\Helpers\FixerHelper;
78
use SlevomatCodingStandard\Helpers\IndentationHelper;
89
use SlevomatCodingStandard\Helpers\SniffSettingsHelper;
910
use SlevomatCodingStandard\Helpers\TokenHelper;
11+
use UnexpectedValueException;
1012
use function array_unique;
1113
use function count;
1214
use function in_array;
1315
use function ltrim;
16+
use function preg_match;
1417
use function sprintf;
1518
use function strlen;
1619
use function trim;
@@ -27,12 +30,31 @@ class RequireMultiLineCallSniff extends AbstractLineCall
2730
{
2831

2932
public const CODE_REQUIRED_MULTI_LINE_CALL = 'RequiredMultiLineCall';
33+
private const DEFAULT_MIN_LINE_LENGTH = 121;
3034

31-
public int $minLineLength = 121;
35+
public ?int $minLineLength = null;
36+
37+
public ?int $minParametersCount = null;
38+
39+
/** @var list<string> */
40+
public array $excludedCallPatterns = [];
41+
42+
/** @var list<string>|null */
43+
public ?array $excludedCallNormalizedPatterns = null;
3244

3345
public function process(File $phpcsFile, int $stringPointer): void
3446
{
35-
$this->minLineLength = SniffSettingsHelper::normalizeInteger($this->minLineLength);
47+
$this->minLineLength = SniffSettingsHelper::normalizeNullableInteger($this->minLineLength);
48+
$this->minParametersCount = SniffSettingsHelper::normalizeNullableInteger($this->minParametersCount);
49+
50+
if ($this->minLineLength !== null && $this->minParametersCount !== null) {
51+
throw new UnexpectedValueException('Either minLineLength or minParametersCount can be set.');
52+
}
53+
54+
// Maintain backward compatibility if no configuration provided
55+
if ($this->minLineLength === null && $this->minParametersCount === null) {
56+
$this->minLineLength = self::DEFAULT_MIN_LINE_LENGTH;
57+
}
3658

3759
if (!$this->isCall($phpcsFile, $stringPointer)) {
3860
return;
@@ -125,6 +147,13 @@ public function process(File $phpcsFile, int $stringPointer): void
125147

126148
$name = ltrim($tokens[$stringPointer]['content'], '\\');
127149

150+
if (
151+
count($this->excludedCallPatterns) !== 0
152+
&& $this->isCallNameInPatterns($name, $this->getExcludedCallNormalizedPatterns())
153+
) {
154+
return;
155+
}
156+
128157
if (in_array($tokens[$previousPointer]['code'], [T_OBJECT_OPERATOR, T_DOUBLE_COLON], true)) {
129158
$error = sprintf('Call of method %s() should be split to more lines.', $name);
130159
} elseif ($tokens[$previousPointer]['code'] === T_NEW) {
@@ -224,11 +253,15 @@ private function shouldReportError(
224253
int $indentationLength
225254
): bool
226255
{
227-
if ($this->minLineLength === 0) {
256+
if ($this->minLineLength !== null && $this->minLineLength === 0) {
228257
return true;
229258
}
230259

231-
if ($lineLength < $this->minLineLength) {
260+
if ($this->minLineLength !== null && $lineLength < $this->minLineLength) {
261+
return false;
262+
}
263+
264+
if ($this->minParametersCount !== null && $parametersCount < $this->minParametersCount) {
232265
return false;
233266
}
234267

@@ -239,4 +272,31 @@ private function shouldReportError(
239272
return strlen(trim($lineStart) . trim($lineEnd)) > $indentationLength;
240273
}
241274

275+
/**
276+
* @param list<string> $normalizedPatterns
277+
*/
278+
private function isCallNameInPatterns(string $callName, array $normalizedPatterns): bool
279+
{
280+
foreach ($normalizedPatterns as $pattern) {
281+
if (!SniffSettingsHelper::isValidRegularExpression($pattern)) {
282+
throw new Exception(sprintf('%s is not valid PCRE pattern.', $pattern));
283+
}
284+
285+
if (preg_match($pattern, $callName) !== 0) {
286+
return true;
287+
}
288+
}
289+
290+
return false;
291+
}
292+
293+
/**
294+
* @return list<string>
295+
*/
296+
private function getExcludedCallNormalizedPatterns(): array
297+
{
298+
$this->excludedCallNormalizedPatterns ??= SniffSettingsHelper::normalizeArray($this->excludedCallPatterns);
299+
return $this->excludedCallNormalizedPatterns;
300+
}
301+
242302
}

tests/Sniffs/Functions/.DS_Store

6 KB
Binary file not shown.

tests/Sniffs/Functions/RequireMultiLineCallSniffTest.php

Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
namespace SlevomatCodingStandard\Sniffs\Functions;
44

55
use SlevomatCodingStandard\Sniffs\TestCase;
6+
use Throwable;
67

78
class RequireMultiLineCallSniffTest extends TestCase
89
{
@@ -135,4 +136,60 @@ public function testAllCallsErrors(): void
135136
self::assertAllFixedInFile($report);
136137
}
137138

139+
public function testThrowExceptionOnInvalidSetup(): void
140+
{
141+
$this->expectException(Throwable::class);
142+
143+
self::checkFile(
144+
__DIR__ . '/data/requireMultiLineCallAllCallsNoErrors.php',
145+
['minLineLength' => 100, 'minParametersCount' => 2],
146+
);
147+
}
148+
149+
public function testErrorsBasedOnParamCount(): void
150+
{
151+
$report = self::checkFile(
152+
__DIR__ . '/data/requireMultiLineCallParamCountErrors.php',
153+
[
154+
'minParametersCount' => 2,
155+
],
156+
);
157+
self::assertSame(2, $report->getErrorCount());
158+
159+
self::assertSniffError($report, 8, RequireMultiLineCallSniff::CODE_REQUIRED_MULTI_LINE_CALL);
160+
self::assertSniffError($report, 14, RequireMultiLineCallSniff::CODE_REQUIRED_MULTI_LINE_CALL);
161+
162+
self::assertAllFixedInFile($report);
163+
}
164+
165+
public function testThrowExceptionForInvalidPattern(): void
166+
{
167+
$this->expectException(Throwable::class);
168+
169+
self::checkFile(
170+
__DIR__ . '/data/requireMultiLineCallErrors.php',
171+
['includedCallPatterns' => ['invalidPattern']],
172+
);
173+
174+
self::checkFile(
175+
__DIR__ . '/data/requireMultiLineCallErrors.php',
176+
['excludedCallPatterns' => ['invalidPattern']],
177+
);
178+
}
179+
180+
public function testExcludedCallPatterns(): void
181+
{
182+
$report = self::checkFile(__DIR__ . '/data/requireMultiLineCallExcludedCallsErrors.php', [
183+
'minLineLength' => 0,
184+
'excludedCallPatterns' => ['/dontReportError/'],
185+
], [RequireMultiLineCallSniff::CODE_REQUIRED_MULTI_LINE_CALL]);
186+
187+
self::assertSame(2, $report->getErrorCount());
188+
189+
self::assertSniffError($report, 7, RequireMultiLineCallSniff::CODE_REQUIRED_MULTI_LINE_CALL);
190+
self::assertSniffError($report, 13, RequireMultiLineCallSniff::CODE_REQUIRED_MULTI_LINE_CALL);
191+
192+
self::assertAllFixedInFile($report);
193+
}
194+
138195
}
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
<?php
2+
3+
class Whatever
4+
{
5+
public function __construct()
6+
{
7+
$this->reportError(
8+
'false',
9+
true
10+
);
11+
$this->dontReportError('true', false);
12+
}
13+
}
14+
15+
function () {
16+
reportError(
17+
'false',
18+
true
19+
);
20+
dontReportError('true', false);
21+
};
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
<?php
2+
3+
class Whatever
4+
{
5+
public function __construct()
6+
{
7+
$this->reportError('false', true);
8+
$this->dontReportError('true', false);
9+
}
10+
}
11+
12+
function () {
13+
reportError('false', true);
14+
dontReportError('true', false);
15+
};
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
<?php
2+
3+
class Whatever
4+
{
5+
public function __construct()
6+
{
7+
$this->doAnything('false');
8+
$this->doAnything(
9+
'true',
10+
false
11+
);
12+
}
13+
}
14+
15+
function ($text) {
16+
sprintf(_('one parameter'));
17+
return sprintf(
18+
_('one parameter'),
19+
$text
20+
);
21+
};
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
<?php
2+
3+
class Whatever
4+
{
5+
public function __construct()
6+
{
7+
$this->doAnything('false');
8+
$this->doAnything('true', false);
9+
}
10+
}
11+
12+
function ($text) {
13+
sprintf(_('one parameter'));
14+
return sprintf(_('one parameter'), $text);
15+
};

0 commit comments

Comments
 (0)