Skip to content

Commit 787a1fd

Browse files
authored
Deprecate OperatorLinebreakFixer (#420)
1 parent 925dde5 commit 787a1fd

File tree

5 files changed

+29
-285
lines changed

5 files changed

+29
-285
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
## v2.4.0 - [Unreleased]
44
- Update PHP CS Fixer to v2.17
5+
- Deprecate OperatorLinebreakFixer - use "operator_linebreak"
56
- Remove PhpCsFixerCustomFixers\Analyzer\ReferenceAnalyzer - use PhpCsFixer\Tokenizer\Analyzer\ReferenceAnalyzer
67
- Remove PhpCsFixerCustomFixers\Analyzer\SwitchAnalyzer - use PhpCsFixer\Tokenizer\Analyzer\SwitchAnalyzer
78

README.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -322,8 +322,8 @@ Configuration options:
322322
```
323323

324324
#### OperatorLinebreakFixer
325-
Operators must always be at the beginning or at the end of the line.
326-
*To be deprecated after [this](https://github.com/FriendsOfPHP/PHP-CS-Fixer/pull/4021) is merged and released.*
325+
Operators - when multiline - must always be at the beginning or at the end of the line.
326+
DEPRECATED: use `operator_linebreak` instead.
327327
Configuration options:
328328
- `only_booleans` (`bool`): whether to limit operators to only boolean ones; defaults to `false`
329329
- `position` (`'beginning'`, `'end'`): whether to place operators at the beginning or at the end of the line; defaults to `'beginning'`

dev-tools/src/Readme/ReadmeCommand.php

Lines changed: 4 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -181,12 +181,10 @@ private function fixers(): string
181181
$fixer->getDefinition()->getSummary()
182182
);
183183

184-
if ($fixer instanceof DeprecatingFixerInterface) {
185-
$output .= \sprintf(
186-
"\n *To be deprecated after [this](https://github.com/FriendsOfPHP/PHP-CS-Fixer/pull/%d) is merged and released.*",
187-
$fixer->getPullRequestId()
188-
);
189-
}
184+
$output .= !$fixer instanceof DeprecatingFixerInterface ? '' : \sprintf(
185+
"\n *To be deprecated after [this](https://github.com/FriendsOfPHP/PHP-CS-Fixer/pull/%d) is merged and released.*",
186+
$fixer->getPullRequestId()
187+
);
190188

191189
$output .= $fixer instanceof DeprecatedFixerInterface ? \sprintf("\n DEPRECATED: use `%s` instead.", \implode('`, `', $fixer->getSuccessorsNames())) : '';
192190

src/Fixer/OperatorLinebreakFixer.php

Lines changed: 20 additions & 275 deletions
Original file line numberDiff line numberDiff line change
@@ -14,322 +14,67 @@
1414
namespace PhpCsFixerCustomFixers\Fixer;
1515

1616
use PhpCsFixer\Fixer\ConfigurationDefinitionFixerInterface;
17-
use PhpCsFixer\FixerConfiguration\FixerConfigurationResolver;
18-
use PhpCsFixer\FixerConfiguration\FixerOptionBuilder;
19-
use PhpCsFixer\FixerDefinition\CodeSample;
20-
use PhpCsFixer\FixerDefinition\FixerDefinition;
17+
use PhpCsFixer\Fixer\DeprecatedFixerInterface;
18+
use PhpCsFixer\FixerConfiguration\FixerConfigurationResolverInterface;
2119
use PhpCsFixer\FixerDefinition\FixerDefinitionInterface;
22-
use PhpCsFixer\Preg;
23-
use PhpCsFixer\Tokenizer\Analyzer\Analysis\CaseAnalysis;
24-
use PhpCsFixer\Tokenizer\Analyzer\ReferenceAnalyzer;
25-
use PhpCsFixer\Tokenizer\Analyzer\SwitchAnalyzer;
26-
use PhpCsFixer\Tokenizer\Token;
2720
use PhpCsFixer\Tokenizer\Tokens;
2821

29-
final class OperatorLinebreakFixer extends AbstractFixer implements ConfigurationDefinitionFixerInterface, DeprecatingFixerInterface
22+
/**
23+
* @deprecated
24+
*/
25+
final class OperatorLinebreakFixer extends AbstractFixer implements ConfigurationDefinitionFixerInterface, DeprecatedFixerInterface
3026
{
31-
private const BOOLEAN_OPERATORS = [[T_BOOLEAN_AND], [T_BOOLEAN_OR], [T_LOGICAL_AND], [T_LOGICAL_OR], [T_LOGICAL_XOR]];
32-
private const NON_BOOLEAN_OPERATORS = ['%', '&', '*', '+', '-', '.', '/', ':', '<', '=', '>', '?', '^', '|', [T_AND_EQUAL], [T_COALESCE], [T_CONCAT_EQUAL], [T_DIV_EQUAL], [T_DOUBLE_ARROW], [T_IS_EQUAL], [T_IS_GREATER_OR_EQUAL], [T_IS_IDENTICAL], [T_IS_NOT_EQUAL], [T_IS_NOT_IDENTICAL], [T_IS_SMALLER_OR_EQUAL], [T_MINUS_EQUAL], [T_MOD_EQUAL], [T_MUL_EQUAL], [T_OBJECT_OPERATOR], [T_OR_EQUAL], [T_PAAMAYIM_NEKUDOTAYIM], [T_PLUS_EQUAL], [T_POW], [T_POW_EQUAL], [T_SL], [T_SL_EQUAL], [T_SPACESHIP], [T_SR], [T_SR_EQUAL], [T_XOR_EQUAL]];
33-
34-
/** @var string */
35-
private $position = 'beginning';
36-
37-
/** @var array<array<int|string>|string> */
38-
private $operators = [];
27+
/** @var \PhpCsFixer\Fixer\Operator\OperatorLinebreakFixer */
28+
private $fixer;
3929

4030
public function __construct()
4131
{
42-
$this->operators = \array_merge(self::BOOLEAN_OPERATORS, self::NON_BOOLEAN_OPERATORS);
32+
$this->fixer = new \PhpCsFixer\Fixer\Operator\OperatorLinebreakFixer();
4333
}
4434

4535
public function getDefinition(): FixerDefinitionInterface
4636
{
47-
return new FixerDefinition(
48-
'Operators must always be at the beginning or at the end of the line.',
49-
[new CodeSample('<?php
50-
function foo() {
51-
return $bar ||
52-
$baz;
53-
}
54-
')]
55-
);
37+
return $this->fixer->getDefinition();
5638
}
5739

58-
public function getConfigurationDefinition(): FixerConfigurationResolver
40+
public function getConfigurationDefinition(): FixerConfigurationResolverInterface
5941
{
60-
return new FixerConfigurationResolver([
61-
(new FixerOptionBuilder('only_booleans', 'whether to limit operators to only boolean ones'))
62-
->setAllowedTypes(['bool'])
63-
->setDefault(false)
64-
->getOption(),
65-
(new FixerOptionBuilder('position', 'whether to place operators at the beginning or at the end of the line'))
66-
->setAllowedValues(['beginning', 'end'])
67-
->setDefault($this->position)
68-
->getOption(),
69-
]);
42+
return $this->fixer->getConfigurationDefinition();
7043
}
7144

7245
/**
7346
* @param null|array<string, bool|string> $configuration
7447
*/
7548
public function configure(?array $configuration = null): void
7649
{
77-
if (isset($configuration['only_booleans']) && $configuration['only_booleans'] === true) {
78-
$this->operators = self::BOOLEAN_OPERATORS;
79-
}
80-
81-
if (isset($configuration['position'])) {
82-
/** @var string $position */
83-
$position = $configuration['position'];
84-
$this->position = $position;
85-
}
50+
$this->fixer->configure($configuration);
8651
}
8752

8853
public function getPriority(): int
8954
{
90-
return 0;
91-
}
92-
93-
public function getPullRequestId(): int
94-
{
95-
return 4021;
55+
return $this->fixer->getPriority();
9656
}
9757

9858
public function isCandidate(Tokens $tokens): bool
9959
{
100-
return true;
60+
return $this->fixer->isCandidate($tokens);
10161
}
10262

10363
public function isRisky(): bool
10464
{
105-
return false;
65+
return $this->fixer->isRisky();
10666
}
10767

10868
public function fix(\SplFileInfo $file, Tokens $tokens): void
10969
{
110-
$referenceAnalyzer = new ReferenceAnalyzer();
111-
112-
$excludedIndices = $this->getExcludedIndices($tokens);
113-
114-
$index = $tokens->count();
115-
while ($index > 1) {
116-
$index--;
117-
118-
/** @var Token $token */
119-
$token = $tokens[$index];
120-
121-
if (!$token->equalsAny($this->operators, false)) {
122-
continue;
123-
}
124-
125-
if ($referenceAnalyzer->isReference($tokens, $index)) {
126-
continue;
127-
}
128-
129-
if (\in_array($index, $excludedIndices, true)) {
130-
continue;
131-
}
132-
133-
$operatorIndices = [$index];
134-
if ($token->equals(':')) {
135-
/** @var int $prevIndex */
136-
$prevIndex = $tokens->getPrevMeaningfulToken($index);
137-
138-
/** @var Token $prevToken */
139-
$prevToken = $tokens[$prevIndex];
140-
141-
if ($prevToken->equals('?')) {
142-
$operatorIndices = [$prevIndex, $index];
143-
$index = $prevIndex;
144-
}
145-
}
146-
147-
$this->fixOperatorLinebreak($tokens, $operatorIndices);
148-
}
149-
}
150-
151-
/**
152-
* Currently only colons from "switch".
153-
*
154-
* @return int[]
155-
*/
156-
private function getExcludedIndices(Tokens $tokens): array
157-
{
158-
$indices = [];
159-
for ($index = $tokens->count() - 1; $index > 0; $index--) {
160-
/** @var Token $token */
161-
$token = $tokens[$index];
162-
163-
if ($token->isGivenKind(T_SWITCH)) {
164-
$indices = \array_merge($indices, $this->getCasesColonsForSwitch($tokens, $index));
165-
}
166-
}
167-
168-
return $indices;
169-
}
170-
171-
/**
172-
* @return int[]
173-
*/
174-
private function getCasesColonsForSwitch(Tokens $tokens, int $switchIndex): array
175-
{
176-
return \array_map(
177-
static function (CaseAnalysis $caseAnalysis): int {
178-
return $caseAnalysis->getColonIndex();
179-
},
180-
(new SwitchAnalyzer())->getSwitchAnalysis($tokens, $switchIndex)->getCases()
181-
);
182-
}
183-
184-
/**
185-
* @param non-empty-array<int> $operatorIndices
186-
*/
187-
private function fixOperatorLinebreak(Tokens $tokens, array $operatorIndices): void
188-
{
189-
/** @var int $minOperatorIndices */
190-
$minOperatorIndices = \min($operatorIndices);
191-
192-
/** @var int $maxOperatorIndices */
193-
$maxOperatorIndices = \max($operatorIndices);
194-
195-
/** @var int $prevIndex */
196-
$prevIndex = $tokens->getPrevMeaningfulToken($minOperatorIndices);
197-
$indexStart = $prevIndex + 1;
198-
199-
/** @var int $nextIndex */
200-
$nextIndex = $tokens->getNextMeaningfulToken($maxOperatorIndices);
201-
$indexEnd = $nextIndex - 1;
202-
203-
if (!$this->isMultiline($tokens, $indexStart, $indexEnd)) {
204-
return; // operator is not surrounded by multiline whitespaces, do not touch it
205-
}
206-
207-
if ($this->position === 'beginning') {
208-
if (!$this->isMultiline($tokens, $maxOperatorIndices, $indexEnd)) {
209-
return; // operator already is placed correctly
210-
}
211-
$this->fixMoveToTheBeginning($tokens, $operatorIndices);
212-
213-
return;
214-
}
215-
216-
if (!$this->isMultiline($tokens, $indexStart, $minOperatorIndices)) {
217-
return; // operator already is placed correctly
218-
}
219-
$this->fixMoveToTheEnd($tokens, $operatorIndices);
220-
}
221-
222-
/**
223-
* @param non-empty-array<int> $operatorIndices
224-
*/
225-
private function fixMoveToTheBeginning(Tokens $tokens, array $operatorIndices): void
226-
{
227-
/** @var int $minOperatorIndices */
228-
$minOperatorIndices = \min($operatorIndices);
229-
230-
/** @var int $maxOperatorIndices */
231-
$maxOperatorIndices = \max($operatorIndices);
232-
233-
/** @var int $prevIndex */
234-
$prevIndex = $tokens->getNonEmptySibling($minOperatorIndices, -1);
235-
236-
/** @var Token $prevToken */
237-
$prevToken = $tokens[$prevIndex];
238-
239-
/** @var int $nextIndex */
240-
$nextIndex = $tokens->getNextMeaningfulToken($maxOperatorIndices);
241-
242-
for ($i = $nextIndex - 1; $i > $maxOperatorIndices; $i--) {
243-
/** @var Token $token */
244-
$token = $tokens[$i];
245-
246-
if ($token->isWhitespace() && Preg::match('/\R/u', $token->getContent()) === 1) {
247-
$isWhitespaceBefore = $prevToken->isWhitespace();
248-
$inserts = $this->getReplacementsAndClear($tokens, $operatorIndices, -1);
249-
if ($isWhitespaceBefore) {
250-
$inserts[] = new Token([T_WHITESPACE, ' ']);
251-
}
252-
$tokens->insertAt($nextIndex, $inserts);
253-
254-
break;
255-
}
256-
}
70+
$this->fixer->fix($file, $tokens);
25771
}
25872

25973
/**
260-
* @param non-empty-array<int> $operatorIndices
74+
* @return string[]
26175
*/
262-
private function fixMoveToTheEnd(Tokens $tokens, array $operatorIndices): void
76+
public function getSuccessorsNames(): array
26377
{
264-
/** @var int $minOperatorIndices */
265-
$minOperatorIndices = \min($operatorIndices);
266-
267-
/** @var int $maxOperatorIndices */
268-
$maxOperatorIndices = \max($operatorIndices);
269-
270-
/** @var int $prevIndex */
271-
$prevIndex = $tokens->getPrevMeaningfulToken($minOperatorIndices);
272-
273-
/** @var int $nextIndex */
274-
$nextIndex = $tokens->getNonEmptySibling($maxOperatorIndices, 1);
275-
276-
/** @var Token $nextToken */
277-
$nextToken = $tokens[$nextIndex];
278-
279-
for ($i = $prevIndex + 1; $i < $maxOperatorIndices; $i++) {
280-
/** @var Token $token */
281-
$token = $tokens[$i];
282-
283-
if ($token->isWhitespace() && Preg::match('/\R/u', $token->getContent()) === 1) {
284-
$isWhitespaceAfter = $nextToken->isWhitespace();
285-
$inserts = $this->getReplacementsAndClear($tokens, $operatorIndices, 1);
286-
if ($isWhitespaceAfter) {
287-
\array_unshift($inserts, new Token([T_WHITESPACE, ' ']));
288-
}
289-
$tokens->insertAt($prevIndex + 1, $inserts);
290-
291-
break;
292-
}
293-
}
294-
}
295-
296-
/**
297-
* @param int[] $indices
298-
*
299-
* @return Token[]
300-
*/
301-
private function getReplacementsAndClear(Tokens $tokens, array $indices, int $direction): array
302-
{
303-
return \array_map(
304-
static function (int $index) use ($tokens, $direction): Token {
305-
/** @var Token $token */
306-
$token = $tokens[$index];
307-
308-
/** @var Token $siblingToken */
309-
$siblingToken = $tokens[$index + $direction];
310-
311-
if ($siblingToken->isWhitespace()) {
312-
$tokens->clearAt($index + $direction);
313-
}
314-
$tokens->clearAt($index);
315-
316-
return $token;
317-
},
318-
$indices
319-
);
320-
}
321-
322-
private function isMultiline(Tokens $tokens, int $indexStart, int $indexEnd): bool
323-
{
324-
for ($index = $indexStart; $index <= $indexEnd; $index++) {
325-
/** @var Token $token */
326-
$token = $tokens[$index];
327-
328-
if (\strpos($token->getContent(), "\n") !== false) {
329-
return true;
330-
}
331-
}
332-
333-
return false;
78+
return [$this->fixer->getName()];
33479
}
33580
}

tests/Fixer/OperatorLinebreakFixerTest.php

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -34,9 +34,9 @@ public function testIsRisky(): void
3434
self::assertFalse($this->fixer->isRisky());
3535
}
3636

37-
public function testDeprecatingPullRequest(): void
37+
public function testSuccessorName(): void
3838
{
39-
self::assertSame(4021, $this->fixer->getPullRequestId());
39+
self::assertContains('operator_linebreak', $this->fixer->getSuccessorsNames());
4040
}
4141

4242
/**

0 commit comments

Comments
 (0)