-
-
Notifications
You must be signed in to change notification settings - Fork 1.6k
/
FullOpeningTagFixer.php
123 lines (100 loc) · 3.92 KB
/
FullOpeningTagFixer.php
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
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
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
<?php
declare(strict_types=1);
/*
* This file is part of PHP CS Fixer.
*
* (c) Fabien Potencier <fabien@symfony.com>
* Dariusz Rumiński <dariusz.ruminski@gmail.com>
*
* This source file is subject to the MIT license that is bundled
* with this source code in the file LICENSE.
*/
namespace PhpCsFixer\Fixer\PhpTag;
use PhpCsFixer\AbstractFixer;
use PhpCsFixer\FixerDefinition\CodeSample;
use PhpCsFixer\FixerDefinition\FixerDefinition;
use PhpCsFixer\FixerDefinition\FixerDefinitionInterface;
use PhpCsFixer\Preg;
use PhpCsFixer\Tokenizer\Token;
use PhpCsFixer\Tokenizer\Tokens;
/**
* Fixer for rules defined in PSR1 ¶2.1.
*
* @author Dariusz Rumiński <dariusz.ruminski@gmail.com>
*/
final class FullOpeningTagFixer extends AbstractFixer
{
public function getDefinition(): FixerDefinitionInterface
{
return new FixerDefinition(
'PHP code must use the long `<?php` tags or short-echo `<?=` tags and not other tag variations.',
[
new CodeSample(
'<?
echo "Hello!";
'
),
]
);
}
public function getPriority(): int
{
// must run before all Token-based fixers
return 98;
}
public function isCandidate(Tokens $tokens): bool
{
return true;
}
protected function applyFix(\SplFileInfo $file, Tokens $tokens): void
{
$content = $tokens->generateCode();
// replace all <? with <?php to replace all short open tags even without short_open_tag option enabled
$newContent = Preg::replace('/<\?(?:phP|pHp|pHP|Php|PhP|PHp|PHP)?(\s|$)/', '<?php$1', $content, -1, $count);
if (0 === $count) {
return;
}
/* the following code is magic to revert previous replacements which should NOT be replaced, for example incorrectly replacing
* > echo '<? ';
* with
* > echo '<?php ';
*/
$newTokens = Tokens::fromCode($newContent);
$tokensOldContentLength = 0;
foreach ($newTokens as $index => $token) {
if ($token->isGivenKind(T_OPEN_TAG)) {
$tokenContent = $token->getContent();
$possibleOpenContent = substr($content, $tokensOldContentLength, 5);
if (false === $possibleOpenContent || '<?php' !== strtolower($possibleOpenContent)) { /** @phpstan-ignore-line as pre PHP 8.0 `false` might be returned by substr @TODO clean up when PHP8+ is required */
$tokenContent = '<? ';
}
$tokensOldContentLength += \strlen($tokenContent);
continue;
}
if ($token->isGivenKind([T_COMMENT, T_DOC_COMMENT, T_CONSTANT_ENCAPSED_STRING, T_ENCAPSED_AND_WHITESPACE, T_STRING])) {
$tokenContent = '';
$tokenContentLength = 0;
$parts = explode('<?php', $token->getContent());
$iLast = \count($parts) - 1;
foreach ($parts as $i => $part) {
$tokenContent .= $part;
$tokenContentLength += \strlen($part);
if ($i !== $iLast) {
$originalTokenContent = substr($content, $tokensOldContentLength + $tokenContentLength, 5);
if ('<?php' === strtolower($originalTokenContent)) {
$tokenContent .= $originalTokenContent;
$tokenContentLength += 5;
} else {
$tokenContent .= '<?';
$tokenContentLength += 2;
}
}
}
$newTokens[$index] = new Token([$token->getId(), $tokenContent]);
$token = $newTokens[$index];
}
$tokensOldContentLength += \strlen($token->getContent());
}
$tokens->overrideRange(0, $tokens->count() - 1, $newTokens);
}
}