Skip to content

Commit 082cae8

Browse files
authored
Merge pull request #73 from kubawerlos/update/operator-linebreak-handle-ternary-operator
OperatorLinebreakFixer - handle ternary operator
2 parents c49fbc0 + 3f2961e commit 082cae8

File tree

5 files changed

+194
-69
lines changed

5 files changed

+194
-69
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
## [Unreleased]
44
- Add NoCommentedOutCodeFixer
55
- Add NullableParamStyleFixer
6+
- Feature: OperatorLinebreakFixer - handle ternary operator
67
- Fix: NoImportFromGlobalNamespaceFixer - class without namespace
78
- Fix: NoUselessClassCommentFixer - comment detection
89
- Fix: TokenRemover - remove last element of file

phpstan.neon.dist

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,10 @@
11
parameters:
22
ignoreErrors:
3-
- '#^Method PhpCsFixerCustomFixers\\Fixer\\NoReferenceInFunctionDefinitionFixer::getArgumentStartIndices\(\) should return array<int> but returns array<int, int\|null>\.$#'
4-
- '#^Method PhpCsFixerCustomFixers\\Fixer\\NoCommentedOutCodeFixer::getMessage\(\) should return string but returns array<string>\|string\.$#'
53
- '#^Cannot call method [a-zA-Z]+\(\) on [a-zA-Z\\]+\|null\.$#'
6-
- '#^Parameter .+ expects .+, .+ given\.$#'
4+
- '#^Method PhpCsFixerCustomFixers\\Fixer\\NoCommentedOutCodeFixer::getMessage\(\) should return string but returns array<string>\|string\.$#'
5+
- '#^Method PhpCsFixerCustomFixers\\Fixer\\NoReferenceInFunctionDefinitionFixer::getArgumentStartIndices\(\) should return array<int> but returns array<int, int\|null>\.$#'
6+
- '#^Method PhpCsFixerCustomFixers\\Fixer\\OperatorLinebreakFixer::getOperatorIndices\(\) should return array<int>\|null but returns array<int, int\|null>\.$#'
77
- '#^Only numeric types are allowed in (\-|\+|post\-decrement|post\-increment), int\|null given( on the left side)?\.$#'
8+
- '#^Parameter .+ expects .+, .+ given\.$#'
89
includes:
910
- vendor/phpstan/phpstan-strict-rules/rules.neon

src/Fixer/OperatorLinebreakFixer.php

Lines changed: 105 additions & 64 deletions
Original file line numberDiff line numberDiff line change
@@ -16,53 +16,55 @@
1616
final class OperatorLinebreakFixer extends AbstractFixer implements ConfigurationDefinitionFixerInterface, DeprecatingFixerInterface
1717
{
1818
private const BOOLEAN_OPERATORS = [
19-
'&&',
20-
'||',
21-
'and',
22-
'or',
23-
'xor',
19+
'&&' => T_BOOLEAN_AND,
20+
'||' => T_BOOLEAN_OR,
21+
'and' => T_LOGICAL_AND,
22+
'or' => T_LOGICAL_OR,
23+
'xor' => T_LOGICAL_XOR,
2424
];
2525

2626
private const NON_BOOLEAN_OPERATORS = [
27-
'=',
28-
'*',
29-
'/',
30-
'%',
31-
'<',
32-
'>',
33-
'|',
34-
'^',
35-
'+',
36-
'-',
37-
'&',
38-
'&=',
39-
'.=',
40-
'/=',
41-
'=>',
42-
'==',
43-
'>=',
44-
'===',
45-
'!=',
46-
'<>',
47-
'!==',
48-
'<=',
49-
'-=',
50-
'%=',
51-
'*=',
52-
'|=',
53-
'+=',
54-
'<<',
55-
'<<=',
56-
'>>',
57-
'>>=',
58-
'^=',
59-
'**',
60-
'**=',
61-
'<=>',
62-
'??',
27+
'=' => true,
28+
'*' => true,
29+
'/' => true,
30+
'%' => true,
31+
'<' => true,
32+
'>' => true,
33+
'|' => true,
34+
'^' => true,
35+
'+' => true,
36+
'-' => true,
37+
'&' => true,
38+
'&=' => true,
39+
'.=' => true,
40+
'/=' => true,
41+
'=>' => true,
42+
'==' => true,
43+
'>=' => true,
44+
'===' => true,
45+
'!=' => true,
46+
'<>' => true,
47+
'!==' => true,
48+
'<=' => true,
49+
'-=' => true,
50+
'%=' => true,
51+
'*=' => true,
52+
'|=' => true,
53+
'+=' => true,
54+
'<<' => true,
55+
'<<=' => true,
56+
'>>' => true,
57+
'>>=' => true,
58+
'^=' => true,
59+
'**' => true,
60+
'**=' => true,
61+
'<=>' => true,
62+
'??' => true,
63+
'?' => true,
64+
':' => true,
6365
];
6466

65-
/** @var string[] */
67+
/** @var array<string, int|true> */
6668
private $operators;
6769

6870
/** @var string */
@@ -140,21 +142,17 @@ public function getPullRequestId(): int
140142
private function fixMoveToTheBeginning(Tokens $tokens): void
141143
{
142144
for ($index = 0; $index < $tokens->count(); $index++) {
143-
$tokenContent = \strtolower($tokens[$index]->getContent());
144-
145-
if (!\in_array($tokenContent, $this->operators, true)) {
145+
$indices = $this->getOperatorIndices($tokens, $index);
146+
if ($indices === null) {
146147
continue;
147148
}
148149

149-
$nextIndex = $tokens->getNextMeaningfulToken($index);
150+
$nextIndex = $tokens->getNextMeaningfulToken(\max($indices));
150151
for ($i = $nextIndex - 1; $i > $index; $i--) {
151-
if ($tokens[$i]->isWhitespace() && Preg::match('/\R/', $tokens[$i]->getContent()) === 1) {
152-
$operator = clone $tokens[$index];
153-
$tokens->clearAt($index);
154-
if ($tokens[$index - 1]->isWhitespace()) {
155-
$tokens->clearTokenAndMergeSurroundingWhitespace($index - 1);
156-
}
157-
$tokens->insertAt($nextIndex, [$operator, new Token([T_WHITESPACE, ' '])]);
152+
if ($tokens[$i]->isWhitespace() && Preg::match('/\R/u', $tokens[$i]->getContent()) === 1) {
153+
$operators = $this->getReplacementsAndClear($tokens, $indices, -1);
154+
$tokens->insertAt($nextIndex, \array_merge($operators, [new Token([T_WHITESPACE, ' '])]));
155+
158156
break;
159157
}
160158
}
@@ -165,25 +163,68 @@ private function fixMoveToTheBeginning(Tokens $tokens): void
165163
private function fixMoveToTheEnd(Tokens $tokens): void
166164
{
167165
for ($index = $tokens->count() - 1; $index > 0; $index--) {
168-
$tokenContent = \strtolower($tokens[$index]->getContent());
169-
170-
if (!\in_array($tokenContent, $this->operators, true)) {
166+
$indices = $this->getOperatorIndices($tokens, $index);
167+
if ($indices === null) {
171168
continue;
172169
}
173170

174-
$prevIndex = $tokens->getPrevMeaningfulToken($index);
171+
$prevIndex = $tokens->getPrevMeaningfulToken(\min($indices));
175172
for ($i = $prevIndex + 1; $i < $index; $i++) {
176-
if ($tokens[$i]->isWhitespace() && Preg::match('/\R/', $tokens[$i]->getContent()) === 1) {
177-
$operator = clone $tokens[$index];
178-
$tokens->clearAt($index);
179-
if ($tokens[$index + 1]->isWhitespace()) {
180-
$tokens->clearTokenAndMergeSurroundingWhitespace($index + 1);
181-
}
182-
$tokens->insertAt($prevIndex + 1, [new Token([T_WHITESPACE, ' ']), $operator]);
173+
if ($tokens[$i]->isWhitespace() && Preg::match('/\R/u', $tokens[$i]->getContent()) === 1) {
174+
$operators = $this->getReplacementsAndClear($tokens, $indices, 1);
175+
$tokens->insertAt($prevIndex + 1, \array_merge([new Token([T_WHITESPACE, ' '])], $operators));
176+
183177
break;
184178
}
185179
}
186180
$index = $prevIndex;
187181
}
188182
}
183+
184+
/**
185+
* @return null|int[]
186+
*/
187+
private function getOperatorIndices(Tokens $tokens, int $index): ?array
188+
{
189+
if (!isset($this->operators[\strtolower($tokens[$index]->getContent())])) {
190+
return null;
191+
}
192+
193+
if (isset($this->operators['?']) && $tokens[$index]->getContent() === '?') {
194+
$nextIndex = $tokens->getNextMeaningfulToken($index);
195+
if ($tokens[$nextIndex]->getContent() === ':') {
196+
return [$index, $nextIndex];
197+
}
198+
}
199+
200+
if (isset($this->operators[':']) && $tokens[$index]->getContent() === ':') {
201+
$prevIndex = $tokens->getPrevMeaningfulToken($index);
202+
if ($tokens[$prevIndex]->getContent() === '?') {
203+
return [$prevIndex, $index];
204+
}
205+
}
206+
207+
return [$index];
208+
}
209+
210+
/**
211+
* @param int[] $indices
212+
*
213+
* @return Token[]
214+
*/
215+
private function getReplacementsAndClear(Tokens $tokens, array $indices, int $direction): array
216+
{
217+
return \array_map(
218+
static function ($index) use ($tokens, $direction) {
219+
$clone = $tokens[$index];
220+
if ($tokens[$index + $direction]->isWhitespace()) {
221+
$tokens->clearAt($index + $direction);
222+
}
223+
$tokens->clearAt($index);
224+
225+
return $clone;
226+
},
227+
$indices
228+
);
229+
}
189230
}

tests/Fixer/NullableParamStyleFixerTest.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,7 @@ public function testFix(string $expected, ?string $input = null, ?array $configu
4646
$this->doTest($expected, $input);
4747
}
4848

49-
public function provideFixCases(): \Iterator
49+
public function provideFixCases(): \Generator
5050
{
5151
yield ['<?php function foo($x = null) {}'];
5252
yield ['<?php function foo(int $x, ?int $y) {}'];

tests/Fixer/OperatorLinebreakFixerTest.php

Lines changed: 83 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,7 @@ public function testFix(string $expected, ?string $input = null, ?array $configu
5151
$this->doTest($expected, $input);
5252
}
5353

54-
public function provideFixCases(): \Iterator
54+
public function provideFixCases(): \Generator
5555
{
5656
yield [
5757
'<?php
@@ -63,6 +63,7 @@ public function provideFixCases(): \Iterator
6363
$bar;
6464
',
6565
];
66+
6667
yield [
6768
'<?php
6869
return $foo
@@ -86,6 +87,19 @@ public function provideFixCases(): \Iterator
8687
yield [
8788
'<?php
8889
return $foo
90+
? $bar
91+
: $baz;
92+
',
93+
'<?php
94+
return $foo ?
95+
$bar :
96+
$baz;
97+
',
98+
];
99+
100+
yield [
101+
'<?php
102+
return $foo
89103
|| $bar
90104
|| $baz;
91105
',
@@ -216,6 +230,74 @@ function foo() {
216230
// Third comment
217231
isThisJustFantasy();
218232
}
233+
',
234+
];
235+
236+
yield [
237+
'<?php
238+
function foo() {
239+
return $a
240+
&& (
241+
$b
242+
|| $c
243+
)
244+
&& $d;
245+
}
246+
',
247+
'<?php
248+
function foo() {
249+
return $a &&
250+
(
251+
$b ||
252+
$c
253+
) &&
254+
$d;
255+
}
256+
',
257+
];
258+
259+
yield [
260+
'<?php
261+
return $foo
262+
?: $bar;
263+
',
264+
'<?php
265+
return $foo ?:
266+
$bar;
267+
',
268+
];
269+
270+
yield [
271+
'<?php
272+
return $foo ?:
273+
$bar;
274+
',
275+
'<?php
276+
return $foo
277+
?: $bar;
278+
',
279+
['position' => 'end'],
280+
];
281+
282+
yield [
283+
'<?php
284+
return $foo
285+
?: $bar;
286+
',
287+
'<?php
288+
return $foo ? :
289+
$bar;
290+
',
291+
];
292+
293+
yield [
294+
'<?php
295+
return $foo/* Lorem ipsum */
296+
?: $bar;
297+
',
298+
'<?php
299+
return $foo ?/* Lorem ipsum */:
300+
$bar;
219301
',
220302
];
221303
}

0 commit comments

Comments
 (0)