|
@@ -19,6 +19,7 @@ use PhpCsFixer\FixerConfiguration\FixerConfigurationResolver;
|
|
|
use PhpCsFixer\FixerConfiguration\FixerOptionBuilder;
|
|
|
use PhpCsFixer\FixerDefinition\CodeSample;
|
|
|
use PhpCsFixer\FixerDefinition\FixerDefinition;
|
|
|
+use PhpCsFixer\Tokenizer\Analyzer\FunctionsAnalyzer;
|
|
|
use PhpCsFixer\Tokenizer\CT;
|
|
|
use PhpCsFixer\Tokenizer\Token;
|
|
|
use PhpCsFixer\Tokenizer\Tokens;
|
|
@@ -51,6 +52,11 @@ final class FunctionToConstantFixer extends AbstractFixer implements Configurati
|
|
|
'php_sapi_name' => [new Token([T_STRING, 'PHP_SAPI'])],
|
|
|
'phpversion' => [new Token([T_STRING, 'PHP_VERSION'])],
|
|
|
'pi' => [new Token([T_STRING, 'M_PI'])],
|
|
|
+ 'get_class_this' => [
|
|
|
+ new Token([T_STATIC, 'static']),
|
|
|
+ new Token([T_DOUBLE_COLON, '::']),
|
|
|
+ new Token([CT::T_CLASS_CONSTANT, 'class']),
|
|
|
+ ],
|
|
|
];
|
|
|
}
|
|
|
|
|
@@ -78,8 +84,13 @@ final class FunctionToConstantFixer extends AbstractFixer implements Configurati
|
|
|
return new FixerDefinition(
|
|
|
'Replace core functions calls returning constants with the constants.',
|
|
|
[
|
|
|
- new CodeSample("<?php\necho phpversion();\necho pi();\necho php_sapi_name();\n"),
|
|
|
- new CodeSample("<?php\necho phpversion();\necho pi();\n", ['functions' => ['phpversion']]),
|
|
|
+ new CodeSample(
|
|
|
+ "<?php\necho phpversion();\necho pi();\necho php_sapi_name();\nclass Foo\n{\n public function Bar()\n {\n echo get_class();\n echo get_called_class();\n }\n}\n"
|
|
|
+ ),
|
|
|
+ new CodeSample(
|
|
|
+ "<?php\necho phpversion();\necho pi();\nclass Foo\n{\n public function Bar()\n {\n echo get_class();\n get_class(\$this);\n echo get_called_class();\n }\n}\n",
|
|
|
+ ['functions' => ['phpversion', 'get_called_class', 'get_class_this']]
|
|
|
+ ),
|
|
|
],
|
|
|
null,
|
|
|
'Risky when any of the configured functions to replace are overridden.'
|
|
@@ -118,8 +129,10 @@ final class FunctionToConstantFixer extends AbstractFixer implements Configurati
|
|
|
*/
|
|
|
protected function applyFix(\SplFileInfo $file, Tokens $tokens)
|
|
|
{
|
|
|
+ $functionAnalyzer = new FunctionsAnalyzer();
|
|
|
+
|
|
|
for ($index = $tokens->count() - 4; $index > 0; --$index) {
|
|
|
- $candidate = $this->getReplaceCandidate($tokens, $index);
|
|
|
+ $candidate = $this->getReplaceCandidate($tokens, $functionAnalyzer, $index);
|
|
|
if (null === $candidate) {
|
|
|
continue;
|
|
|
}
|
|
@@ -164,8 +177,13 @@ final class FunctionToConstantFixer extends AbstractFixer implements Configurati
|
|
|
*/
|
|
|
private function fixFunctionCallToConstant(Tokens $tokens, $index, $braceOpenIndex, $braceCloseIndex, array $replacements)
|
|
|
{
|
|
|
- $tokens->clearTokenAndMergeSurroundingWhitespace($braceCloseIndex);
|
|
|
- $tokens->clearTokenAndMergeSurroundingWhitespace($braceOpenIndex);
|
|
|
+ for ($i = $braceCloseIndex; $i >= $braceOpenIndex; --$i) {
|
|
|
+ if ($tokens[$i]->equalsAny([[T_WHITESPACE], [T_COMMENT], [T_DOC_COMMENT]])) {
|
|
|
+ continue;
|
|
|
+ }
|
|
|
+
|
|
|
+ $tokens->clearTokenAndMergeSurroundingWhitespace($i);
|
|
|
+ }
|
|
|
|
|
|
if ($replacements[0]->isGivenKind([T_CLASS_C, T_STATIC])) {
|
|
|
$prevIndex = $tokens->getPrevMeaningfulToken($index);
|
|
@@ -184,43 +202,110 @@ final class FunctionToConstantFixer extends AbstractFixer implements Configurati
|
|
|
*
|
|
|
* @return null|array
|
|
|
*/
|
|
|
- private function getReplaceCandidate(Tokens $tokens, $index)
|
|
|
- {
|
|
|
- // test if we are at a function all
|
|
|
+ private function getReplaceCandidate(
|
|
|
+ Tokens $tokens,
|
|
|
+ FunctionsAnalyzer $functionAnalyzer,
|
|
|
+ $index
|
|
|
+ ) {
|
|
|
if (!$tokens[$index]->isGivenKind(T_STRING)) {
|
|
|
return null;
|
|
|
}
|
|
|
|
|
|
+ $lowerContent = strtolower($tokens[$index]->getContent());
|
|
|
+
|
|
|
+ if ('get_class' === $lowerContent) {
|
|
|
+ return $this->fixGetClassCall($tokens, $functionAnalyzer, $index);
|
|
|
+ }
|
|
|
+
|
|
|
+ if (!isset($this->functionsFixMap[$lowerContent])) {
|
|
|
+ return null;
|
|
|
+ }
|
|
|
+
|
|
|
+ if (!$functionAnalyzer->isGlobalFunctionCall($tokens, $index)) {
|
|
|
+ return null;
|
|
|
+ }
|
|
|
+
|
|
|
+ // test if function call without parameters
|
|
|
$braceOpenIndex = $tokens->getNextMeaningfulToken($index);
|
|
|
if (!$tokens[$braceOpenIndex]->equals('(')) {
|
|
|
return null;
|
|
|
}
|
|
|
|
|
|
- // test if function call without parameters
|
|
|
$braceCloseIndex = $tokens->getNextMeaningfulToken($braceOpenIndex);
|
|
|
if (!$tokens[$braceCloseIndex]->equals(')')) {
|
|
|
return null;
|
|
|
}
|
|
|
|
|
|
- $functionNamePrefix = $tokens->getPrevMeaningfulToken($index);
|
|
|
- if ($tokens[$functionNamePrefix]->isGivenKind([T_DOUBLE_COLON, T_NEW, T_OBJECT_OPERATOR, T_FUNCTION, CT::T_RETURN_REF])) {
|
|
|
+ return $this->getReplacementTokenClones($lowerContent, $braceOpenIndex, $braceCloseIndex);
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * @param int $index
|
|
|
+ *
|
|
|
+ * @return null|array
|
|
|
+ */
|
|
|
+ private function fixGetClassCall(
|
|
|
+ Tokens $tokens,
|
|
|
+ FunctionsAnalyzer $functionAnalyzer,
|
|
|
+ $index
|
|
|
+ ) {
|
|
|
+ if (!isset($this->functionsFixMap['get_class']) && !isset($this->functionsFixMap['get_class_this'])) {
|
|
|
+ return null;
|
|
|
+ }
|
|
|
+
|
|
|
+ if (!$functionAnalyzer->isGlobalFunctionCall($tokens, $index)) {
|
|
|
return null;
|
|
|
}
|
|
|
|
|
|
- if ($tokens[$functionNamePrefix]->isGivenKind(T_NS_SEPARATOR)) {
|
|
|
- // skip if the call is to a constructor or to a function in a namespace other than the default
|
|
|
- $prevIndex = $tokens->getPrevMeaningfulToken($functionNamePrefix);
|
|
|
- if ($tokens[$prevIndex]->isGivenKind([T_STRING, T_NEW])) {
|
|
|
- return null;
|
|
|
+ $braceOpenIndex = $tokens->getNextMeaningfulToken($index);
|
|
|
+ $braceCloseIndex = $tokens->findBlockEnd(Tokens::BLOCK_TYPE_PARENTHESIS_BRACE, $braceOpenIndex);
|
|
|
+
|
|
|
+ if ($braceCloseIndex === $tokens->getNextMeaningfulToken($braceOpenIndex)) { // no arguments passed
|
|
|
+ if (isset($this->functionsFixMap['get_class'])) {
|
|
|
+ return $this->getReplacementTokenClones('get_class', $braceOpenIndex, $braceCloseIndex);
|
|
|
}
|
|
|
- }
|
|
|
+ } else {
|
|
|
+ if (isset($this->functionsFixMap['get_class_this'])) {
|
|
|
+ $isThis = false;
|
|
|
|
|
|
- // test if the function call is to a native PHP function
|
|
|
- $lowerContent = strtolower($tokens[$index]->getContent());
|
|
|
- if (!\array_key_exists($lowerContent, $this->functionsFixMap)) {
|
|
|
- return null;
|
|
|
+ for ($i = $braceOpenIndex + 1; $i < $braceCloseIndex; ++$i) {
|
|
|
+ if ($tokens[$i]->equalsAny([[T_WHITESPACE], [T_COMMENT], [T_DOC_COMMENT], ')'])) {
|
|
|
+ continue;
|
|
|
+ }
|
|
|
+
|
|
|
+ if ($tokens[$i]->isGivenKind(T_VARIABLE) && '$this' === strtolower($tokens[$i]->getContent())) {
|
|
|
+ $isThis = true;
|
|
|
+
|
|
|
+ continue;
|
|
|
+ }
|
|
|
+
|
|
|
+ if (false === $isThis && $tokens[$i]->equals('(')) {
|
|
|
+ continue;
|
|
|
+ }
|
|
|
+
|
|
|
+ $isThis = false;
|
|
|
+
|
|
|
+ break;
|
|
|
+ }
|
|
|
+
|
|
|
+ if ($isThis) {
|
|
|
+ return $this->getReplacementTokenClones('get_class_this', $braceOpenIndex, $braceCloseIndex);
|
|
|
+ }
|
|
|
+ }
|
|
|
}
|
|
|
|
|
|
+ return null;
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * @param string $lowerContent
|
|
|
+ * @param int $braceOpenIndex
|
|
|
+ * @param int $braceCloseIndex
|
|
|
+ *
|
|
|
+ * @return array
|
|
|
+ */
|
|
|
+ private function getReplacementTokenClones($lowerContent, $braceOpenIndex, $braceCloseIndex)
|
|
|
+ {
|
|
|
$clones = [];
|
|
|
foreach ($this->functionsFixMap[$lowerContent] as $token) {
|
|
|
$clones[] = clone $token;
|