Skip to content

Commit 4aa32c3

Browse files
committed
Merge branch '2.15' into 2.16
2 parents 0396141 + 608d274 commit 4aa32c3

11 files changed

+212
-49
lines changed

.composer-require-checker.json

+1
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
"Symfony\\Contracts\\EventDispatcher\\Event",
44
"Symfony\\Contracts\\EventDispatcher\\EventDispatcherInterface",
55
"Symfony\\Component\\EventDispatcher\\Event",
6+
"PhpCsFixer\\PhpunitConstraintIsIdenticalString\\Constraint\\IsIdenticalString",
67
"PhpCsFixer\\Tests\\Test\\Constraint\\SameStringsConstraint",
78
"PhpCsFixer\\Tests\\Test\\IsIdenticalConstraint",
89
"PHPUnit\\Framework\\Constraint\\IsIdentical",

composer.json

+1
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,7 @@
6767
"tests/Test/IntegrationCaseFactory.php",
6868
"tests/Test/IntegrationCaseFactoryInterface.php",
6969
"tests/Test/InternalIntegrationCaseFactory.php",
70+
"tests/Test/IsIdenticalConstraint.php",
7071
"tests/TestCase.php"
7172
]
7273
},

src/DocBlock/Tag.php

+1-1
Original file line numberDiff line numberDiff line change
@@ -80,7 +80,7 @@ public function getName()
8080
/**
8181
* Set the tag name.
8282
*
83-
* This will also be persisted to the upsteam line and annotation.
83+
* This will also be persisted to the upstream line and annotation.
8484
*
8585
* @param string $name
8686
*/

src/Event/Event.php

+5-2
Original file line numberDiff line numberDiff line change
@@ -12,10 +12,13 @@
1212

1313
namespace PhpCsFixer\Event;
1414

15+
use Symfony\Component\EventDispatcher\EventDispatcher;
16+
use Symfony\Contracts\EventDispatcher\EventDispatcherInterface;
17+
1518
// Since PHP-CS-FIXER is PHP 5.6 compliant we can't always use Symfony Contracts (currently needs PHP ^7.1.3)
16-
// This conditionnal inheritance will be useless when PHP-CS-FIXER no longer supports PHP versions
19+
// This conditional inheritance will be useless when PHP-CS-FIXER no longer supports PHP versions
1720
// inferior to Symfony/Contracts PHP minimal version
18-
if (class_exists(\Symfony\Contracts\EventDispatcher\Event::class)) {
21+
if (is_subclass_of(EventDispatcher::class, EventDispatcherInterface::class)) {
1922
class Event extends \Symfony\Contracts\EventDispatcher\Event
2023
{
2124
}

src/Fixer/Basic/NonPrintableCharacterFixer.php

+11-2
Original file line numberDiff line numberDiff line change
@@ -141,6 +141,7 @@ protected function applyFix(\SplFileInfo $file, Tokens $tokens)
141141

142142
$previousToken = $tokens[$index - 1];
143143
$stringTypeChanged = false;
144+
$swapQuotes = false;
144145

145146
if ($previousToken->isGivenKind(T_START_HEREDOC)) {
146147
$previousTokenContent = $previousToken->getContent();
@@ -150,12 +151,20 @@ protected function applyFix(\SplFileInfo $file, Tokens $tokens)
150151
$stringTypeChanged = true;
151152
}
152153
} elseif ("'" === $content[0]) {
153-
$content = Preg::replace('/^\'(.*)\'$/', '"$1"', $content);
154154
$stringTypeChanged = true;
155+
$swapQuotes = true;
155156
}
156157

158+
if ($swapQuotes) {
159+
$content = str_replace("\\'", "'", $content);
160+
}
157161
if ($stringTypeChanged) {
158-
$content = Preg::replace('/([\\\\$])/', '\\\\$1', $content);
162+
$content = Preg::replace('/(\\\\{1,2})/', '\\\\\\\\', $content);
163+
$content = str_replace('$', '\$', $content);
164+
}
165+
if ($swapQuotes) {
166+
$content = str_replace('"', '\"', $content);
167+
$content = Preg::replace('/^\'(.*)\'$/', '"$1"', $content);
159168
}
160169

161170
$tokens[$index] = new Token([$token->getId(), strtr($content, $escapeSequences)]);

src/Fixer/LanguageConstruct/ClassKeywordRemoveFixer.php

+26-40
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
use PhpCsFixer\AbstractFixer;
1616
use PhpCsFixer\FixerDefinition\CodeSample;
1717
use PhpCsFixer\FixerDefinition\FixerDefinition;
18+
use PhpCsFixer\Tokenizer\Analyzer\NamespacesAnalyzer;
1819
use PhpCsFixer\Tokenizer\CT;
1920
use PhpCsFixer\Tokenizer\Token;
2021
use PhpCsFixer\Tokenizer\Tokens;
@@ -63,40 +64,16 @@ public function isCandidate(Tokens $tokens)
6364
*/
6465
protected function applyFix(\SplFileInfo $file, Tokens $tokens)
6566
{
66-
$this->replaceClassKeywords($tokens);
67-
}
67+
$namespacesAnalyzer = new NamespacesAnalyzer();
6868

69-
/**
70-
* Replaces ::class keyword, namespace by namespace.
71-
*
72-
* It uses recursive method to get rid of token index changes.
73-
*
74-
* @param int $namespaceNumber
75-
*/
76-
private function replaceClassKeywords(Tokens $tokens, $namespaceNumber = -1)
77-
{
78-
$namespaceIndexes = array_keys($tokens->findGivenKind(T_NAMESPACE));
79-
80-
// Namespace blocks
81-
if (\count($namespaceIndexes) && isset($namespaceIndexes[$namespaceNumber])) {
82-
$startIndex = $namespaceIndexes[$namespaceNumber];
83-
84-
$namespaceBlockStartIndex = $tokens->getNextTokenOfKind($startIndex, [';', '{']);
85-
$endIndex = $tokens[$namespaceBlockStartIndex]->equals('{')
86-
? $tokens->findBlockEnd(Tokens::BLOCK_TYPE_CURLY_BRACE, $namespaceBlockStartIndex)
87-
: $tokens->getNextTokenOfKind($namespaceBlockStartIndex, [T_NAMESPACE]);
88-
$endIndex = $endIndex ?: $tokens->count() - 1;
89-
} elseif (-1 === $namespaceNumber) { // Out of any namespace block
90-
$startIndex = 0;
91-
$endIndex = \count($namespaceIndexes) ? $namespaceIndexes[0] : $tokens->count() - 1;
92-
} else {
93-
return;
69+
$previousNamespaceScopeEndIndex = 0;
70+
foreach ($namespacesAnalyzer->getDeclarations($tokens) as $declaration) {
71+
$this->replaceClassKeywordsSection($tokens, '', $previousNamespaceScopeEndIndex, $declaration->getStartIndex());
72+
$this->replaceClassKeywordsSection($tokens, $declaration->getFullName(), $declaration->getStartIndex(), $declaration->getScopeEndIndex());
73+
$previousNamespaceScopeEndIndex = $declaration->getScopeEndIndex();
9474
}
9575

96-
$this->storeImports($tokens, $startIndex, $endIndex);
97-
$tokens->rewind();
98-
$this->replaceClassKeywordsSection($tokens, $startIndex, $endIndex);
99-
$this->replaceClassKeywords($tokens, $namespaceNumber + 1);
76+
$this->replaceClassKeywordsSection($tokens, '', $previousNamespaceScopeEndIndex, $tokens->count() - 1);
10077
}
10178

10279
/**
@@ -152,21 +129,29 @@ static function ($import) {
152129
}
153130

154131
/**
155-
* @param int $startIndex
156-
* @param int $endIndex
132+
* @param string $namespace
133+
* @param int $startIndex
134+
* @param int $endIndex
157135
*/
158-
private function replaceClassKeywordsSection(Tokens $tokens, $startIndex, $endIndex)
136+
private function replaceClassKeywordsSection(Tokens $tokens, $namespace, $startIndex, $endIndex)
159137
{
138+
if ($endIndex - $startIndex < 3) {
139+
return;
140+
}
141+
142+
$this->storeImports($tokens, $startIndex, $endIndex);
143+
160144
$ctClassTokens = $tokens->findGivenKind(CT::T_CLASS_CONSTANT, $startIndex, $endIndex);
161145
foreach (array_reverse(array_keys($ctClassTokens)) as $classIndex) {
162-
$this->replaceClassKeyword($tokens, $classIndex);
146+
$this->replaceClassKeyword($tokens, $namespace, $classIndex);
163147
}
164148
}
165149

166150
/**
167-
* @param int $classIndex
151+
* @param string $namespace
152+
* @param int $classIndex
168153
*/
169-
private function replaceClassKeyword(Tokens $tokens, $classIndex)
154+
private function replaceClassKeyword(Tokens $tokens, $namespace, $classIndex)
170155
{
171156
$classEndIndex = $tokens->getPrevMeaningfulToken($classIndex);
172157
$classEndIndex = $tokens->getPrevMeaningfulToken($classEndIndex);
@@ -218,20 +203,21 @@ private function replaceClassKeyword(Tokens $tokens, $classIndex)
218203

219204
$tokens->insertAt($classBeginIndex, new Token([
220205
T_CONSTANT_ENCAPSED_STRING,
221-
"'".$this->makeClassFQN($classImport, $classString)."'",
206+
"'".$this->makeClassFQN($namespace, $classImport, $classString)."'",
222207
]));
223208
}
224209

225210
/**
211+
* @param string $namespace
226212
* @param false|string $classImport
227213
* @param string $classString
228214
*
229215
* @return string
230216
*/
231-
private function makeClassFQN($classImport, $classString)
217+
private function makeClassFQN($namespace, $classImport, $classString)
232218
{
233219
if (false === $classImport) {
234-
return $classString;
220+
return ('' !== $namespace ? ($namespace.'\\') : '').$classString;
235221
}
236222

237223
$classStringArray = explode('\\', $classString);

src/Fixer/PhpUnit/PhpUnitMethodCasingFixer.php

+3-3
Original file line numberDiff line numberDiff line change
@@ -238,7 +238,7 @@ private function updateDocBlock(Tokens $tokens, $docBlockIndex)
238238
$doc = new DocBlock($tokens[$docBlockIndex]->getContent());
239239
$lines = $doc->getLines();
240240

241-
$docBlockNeesUpdate = false;
241+
$docBlockNeedsUpdate = false;
242242
for ($inc = 0; $inc < \count($lines); ++$inc) {
243243
$lineContent = $lines[$inc]->getContent();
244244
if (false === strpos($lineContent, '@depends')) {
@@ -256,11 +256,11 @@ private function updateDocBlock(Tokens $tokens, $docBlockIndex)
256256

257257
if ($newLineContent !== $lineContent) {
258258
$lines[$inc] = new Line($newLineContent);
259-
$docBlockNeesUpdate = true;
259+
$docBlockNeedsUpdate = true;
260260
}
261261
}
262262

263-
if ($docBlockNeesUpdate) {
263+
if ($docBlockNeedsUpdate) {
264264
$lines = implode('', $lines);
265265
$tokens[$docBlockIndex] = new Token([T_DOC_COMMENT, $lines]);
266266
}

src/Fixer/StringNotation/ExplicitStringVariableFixer.php

+1-1
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,7 @@ public function getDefinition()
4444
."\n".'- When there are two valid ways of doing the same thing, using both is confusing, there should be a coding standard to follow'
4545
."\n".'- PHP manual marks `"$var"` syntax as implicit and `"${var}"` syntax as explicit: explicit code should always be preferred'
4646
."\n".'- Explicit syntax allows word concatenation inside strings, e.g. `"${var}IsAVar"`, implicit doesn\'t'
47-
."\n".'- Explicit syntax is easier to detect for IDE/editors and therefore has colors/hightlight with higher contrast, which is easier to read'
47+
."\n".'- Explicit syntax is easier to detect for IDE/editors and therefore has colors/highlight with higher contrast, which is easier to read'
4848
."\n".'Backtick operator is skipped because it is harder to handle; you can use `backtick_to_shell_exec` fixer to normalize backticks to strings'
4949
);
5050
}

tests/Fixer/Basic/NonPrintableCharacterFixerTest.php

+96
Original file line numberDiff line numberDiff line change
@@ -198,6 +198,102 @@ function f(string $p)
198198
[
199199
'<?php echo \'\';',
200200
],
201+
[
202+
<<<'EXPECTED'
203+
<?php echo "Double \" quote \u{200b} inside";
204+
EXPECTED
205+
,
206+
sprintf(<<<'INPUT'
207+
<?php echo 'Double " quote %s inside';
208+
INPUT
209+
, pack('H*', 'e2808b')),
210+
],
211+
[
212+
<<<'EXPECTED'
213+
<?php echo "Single ' quote \u{200b} inside";
214+
EXPECTED
215+
,
216+
sprintf(<<<'INPUT'
217+
<?php echo 'Single \' quote %s inside';
218+
INPUT
219+
, pack('H*', 'e2808b')),
220+
],
221+
[
222+
<<<'EXPECTED'
223+
<?php echo <<<STRING
224+
Quotes ' and " to be handled \u{200b} properly \\' and \\"
225+
STRING
226+
;
227+
EXPECTED
228+
,
229+
sprintf(<<<'INPUT'
230+
<?php echo <<<'STRING'
231+
Quotes ' and " to be handled %s properly \' and \"
232+
STRING
233+
;
234+
INPUT
235+
, pack('H*', 'e2808b')),
236+
],
237+
[
238+
<<<'EXPECTED'
239+
<?php echo "\\\u{200b}\"";
240+
EXPECTED
241+
,
242+
sprintf(<<<'INPUT'
243+
<?php echo '\\%s"';
244+
INPUT
245+
, pack('H*', 'e2808b')),
246+
],
247+
[
248+
<<<'EXPECTED'
249+
<?php echo "\\\u{200b}'";
250+
EXPECTED
251+
,
252+
sprintf(<<<'INPUT'
253+
<?php echo '\\%s\'';
254+
INPUT
255+
, pack('H*', 'e2808b')),
256+
],
257+
[
258+
<<<'EXPECTED'
259+
<?php echo "Backslash 1 \\ \u{200b}";
260+
EXPECTED
261+
,
262+
sprintf(<<<'INPUT'
263+
<?php echo 'Backslash 1 \ %s';
264+
INPUT
265+
, pack('H*', 'e2808b')),
266+
],
267+
[
268+
<<<'EXPECTED'
269+
<?php echo "Backslash 2 \\ \u{200b}";
270+
EXPECTED
271+
,
272+
sprintf(<<<'INPUT'
273+
<?php echo 'Backslash 2 \\ %s';
274+
INPUT
275+
, pack('H*', 'e2808b')),
276+
],
277+
[
278+
<<<'EXPECTED'
279+
<?php echo "Backslash 3 \\\\ \u{200b}";
280+
EXPECTED
281+
,
282+
sprintf(<<<'INPUT'
283+
<?php echo 'Backslash 3 \\\ %s';
284+
INPUT
285+
, pack('H*', 'e2808b')),
286+
],
287+
[
288+
<<<'EXPECTED'
289+
<?php echo "Backslash 4 \\\\ \u{200b}";
290+
EXPECTED
291+
,
292+
sprintf(<<<'INPUT'
293+
<?php echo 'Backslash 4 \\\\ %s';
294+
INPUT
295+
, pack('H*', 'e2808b')),
296+
],
201297
];
202298
}
203299

tests/Fixer/LanguageConstruct/ClassKeywordRemoveFixerTest.php

+48
Original file line numberDiff line numberDiff line change
@@ -215,6 +215,26 @@ public function c() {
215215
}
216216
',
217217
],
218+
[
219+
"<?php
220+
namespace Foo;
221+
var_dump('Foo\\Bar\\Baz');
222+
",
223+
'<?php
224+
namespace Foo;
225+
var_dump(Bar\\Baz::class);
226+
',
227+
],
228+
[
229+
"<?php
230+
namespace Foo\\Bar;
231+
var_dump('Foo\\Bar\\Baz');
232+
",
233+
'<?php
234+
namespace Foo\\Bar;
235+
var_dump(Baz::class);
236+
',
237+
],
218238
];
219239
}
220240

@@ -251,6 +271,34 @@ public function provideFix70Cases()
251271
echo C::class;
252272
',
253273
],
274+
[
275+
"<?php
276+
var_dump('Foo');
277+
namespace A {
278+
use B\\C;
279+
var_dump('B\\C');
280+
}
281+
var_dump('Bar\\Baz');
282+
namespace B {
283+
use A\\C\\D;
284+
var_dump('A\\C\\D');
285+
}
286+
var_dump('Qux\\Quux');
287+
",
288+
'<?php
289+
var_dump(Foo::class);
290+
namespace A {
291+
use B\\C;
292+
var_dump(C::class);
293+
}
294+
var_dump(Bar\\Baz::class);
295+
namespace B {
296+
use A\\C\\D;
297+
var_dump(D::class);
298+
}
299+
var_dump(Qux\\Quux::class);
300+
',
301+
],
254302
];
255303
}
256304
}

0 commit comments

Comments
 (0)