Skip to content

Commit c3a815a

Browse files
authored
Add FunctionParameterSeparationFixer (#1068)
1 parent 322d0e0 commit c3a815a

File tree

5 files changed

+467
-0
lines changed

5 files changed

+467
-0
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
# CHANGELOG for PHP CS Fixer: custom fixers
22

33
## v3.32.0
4+
- Add FunctionParameterSeparationFixer
45
- Update minimum PHP CS Fixer version to 3.85.0
56

67
## v3.31.0

README.md

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -172,6 +172,24 @@ Value from `foreach` must not be used if possible.
172172
}
173173
```
174174

175+
#### FunctionParameterSeparationFixer
176+
Function parameters, if any is having attribute or hook, must be separated by a blank line.
177+
```diff
178+
<?php
179+
class Foo {
180+
public function __construct(
181+
#[Attibute1]
182+
private string $x,
183+
+
184+
#[Attibute2]
185+
private string $y,
186+
+
187+
#[Attibute3]
188+
private string $z,
189+
) {}
190+
}
191+
```
192+
175193
#### InternalClassCasingFixer
176194
When referencing an internal class it must be written using the correct casing.
177195
DEPRECATED: use `class_reference_name_casing` instead.
Lines changed: 135 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,135 @@
1+
<?php declare(strict_types=1);
2+
3+
/*
4+
* This file is part of PHP CS Fixer: custom fixers.
5+
*
6+
* (c) 2018 Kuba Werłos
7+
*
8+
* For the full copyright and license information, please view
9+
* the LICENSE file that was distributed with this source code.
10+
*/
11+
12+
namespace PhpCsFixerCustomFixers\Fixer;
13+
14+
use PhpCsFixer\FixerDefinition\FixerDefinition;
15+
use PhpCsFixer\FixerDefinition\FixerDefinitionInterface;
16+
use PhpCsFixer\FixerDefinition\VersionSpecification;
17+
use PhpCsFixer\FixerDefinition\VersionSpecificCodeSample;
18+
use PhpCsFixer\Preg;
19+
use PhpCsFixer\Tokenizer\CT;
20+
use PhpCsFixer\Tokenizer\FCT;
21+
use PhpCsFixer\Tokenizer\Token;
22+
use PhpCsFixer\Tokenizer\Tokens;
23+
24+
/**
25+
* @no-named-arguments
26+
*/
27+
final class FunctionParameterSeparationFixer extends AbstractFixer
28+
{
29+
public function getDefinition(): FixerDefinitionInterface
30+
{
31+
return new FixerDefinition(
32+
'Function parameters, if any is having attribute or hook, must be separated by a blank line.',
33+
[
34+
new VersionSpecificCodeSample(
35+
<<<'PHP'
36+
<?php
37+
class Foo {
38+
public function __construct(
39+
#[Attibute1]
40+
private string $x,
41+
#[Attibute2]
42+
private string $y,
43+
#[Attibute3]
44+
private string $z,
45+
) {}
46+
}
47+
48+
PHP,
49+
new VersionSpecification(80000),
50+
),
51+
],
52+
'',
53+
);
54+
}
55+
56+
/**
57+
* Must run after MethodArgumentSpaceFixer.
58+
*/
59+
public function getPriority(): int
60+
{
61+
return 0;
62+
}
63+
64+
public function isCandidate(Tokens $tokens): bool
65+
{
66+
return $tokens->isTokenKindFound(\T_FUNCTION);
67+
}
68+
69+
public function isRisky(): bool
70+
{
71+
return false;
72+
}
73+
74+
public function fix(\SplFileInfo $file, Tokens $tokens): void
75+
{
76+
for ($index = $tokens->count() - 1; $index > 0; $index--) {
77+
if (!$tokens[$index]->isGivenKind([\T_FUNCTION])) {
78+
continue;
79+
}
80+
81+
$openParenthesisIndex = $tokens->getNextTokenOfKind($index, ['(']);
82+
\assert(\is_int($openParenthesisIndex));
83+
84+
$closeParenthesisIndex = $tokens->findBlockEnd(Tokens::BLOCK_TYPE_PARENTHESIS_BRACE, $openParenthesisIndex);
85+
86+
if (!self::shouldBeFixed($tokens, $openParenthesisIndex, $closeParenthesisIndex)) {
87+
continue;
88+
}
89+
90+
self::fixFunction($tokens, $openParenthesisIndex, $closeParenthesisIndex);
91+
}
92+
}
93+
94+
private static function shouldBeFixed(Tokens $tokens, int $openParenthesisIndex, int $closeParenthesisIndex): bool
95+
{
96+
for ($index = $openParenthesisIndex; $index < $closeParenthesisIndex; $index++) {
97+
if ($tokens[$index]->isGivenKind([FCT::T_ATTRIBUTE, CT::T_PROPERTY_HOOK_BRACE_OPEN])) {
98+
return true;
99+
}
100+
}
101+
102+
return false;
103+
}
104+
105+
private static function fixFunction(Tokens $tokens, int $openParenthesisIndex, int $closeParenthesisIndex): void
106+
{
107+
$prevCloseParenthesisIndex = $tokens->getPrevMeaningfulToken($closeParenthesisIndex);
108+
\assert(\is_int($prevCloseParenthesisIndex));
109+
110+
for ($index = $openParenthesisIndex; $index < $prevCloseParenthesisIndex; $index++) {
111+
if ($tokens[$index]->isGivenKind(FCT::T_ATTRIBUTE)) {
112+
$index = $tokens->findBlockEnd(Tokens::BLOCK_TYPE_ATTRIBUTE, $index);
113+
continue;
114+
}
115+
116+
if ($tokens[$index]->isGivenKind(CT::T_PROPERTY_HOOK_BRACE_OPEN)) {
117+
$index = $tokens->findBlockEnd(Tokens::BLOCK_TYPE_PROPERTY_HOOK, $index);
118+
continue;
119+
}
120+
121+
if (!$tokens[$index]->equals(',')) {
122+
continue;
123+
}
124+
125+
if (!$tokens[$index + 1]->isGivenKind(\T_WHITESPACE) || \substr_count($tokens[$index + 1]->getContent(), "\n") !== 1) {
126+
continue;
127+
}
128+
129+
$tokens[$index + 1] = new Token([
130+
\T_WHITESPACE,
131+
Preg::replace('/(\\r\\n|\\n)(\\h+)$/', '$1$1$2', $tokens[$index + 1]->getContent(), 1),
132+
]);
133+
}
134+
}
135+
}

0 commit comments

Comments
 (0)