File "FunctionToConstantFixer.php"
Full Path: /var/www/html/back/vendor/friendsofphp/php-cs-fixer/src/Fixer/LanguageConstruct/FunctionToConstantFixer.php
File size: 10.6 KB
MIME-type: text/x-php
Charset: utf-8
<?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\LanguageConstruct;
use PhpCsFixer\AbstractFixer;
use PhpCsFixer\Fixer\ConfigurableFixerInterface;
use PhpCsFixer\Fixer\ConfigurableFixerTrait;
use PhpCsFixer\FixerConfiguration\AllowedValueSubset;
use PhpCsFixer\FixerConfiguration\FixerConfigurationResolver;
use PhpCsFixer\FixerConfiguration\FixerConfigurationResolverInterface;
use PhpCsFixer\FixerConfiguration\FixerOptionBuilder;
use PhpCsFixer\FixerDefinition\CodeSample;
use PhpCsFixer\FixerDefinition\FixerDefinition;
use PhpCsFixer\FixerDefinition\FixerDefinitionInterface;
use PhpCsFixer\Tokenizer\Analyzer\FunctionsAnalyzer;
use PhpCsFixer\Tokenizer\CT;
use PhpCsFixer\Tokenizer\Token;
use PhpCsFixer\Tokenizer\Tokens;
/**
* @phpstan-type _AutogeneratedInputConfiguration array{
* functions?: list<'get_called_class'|'get_class'|'get_class_this'|'php_sapi_name'|'phpversion'|'pi'>,
* }
* @phpstan-type _AutogeneratedComputedConfiguration array{
* functions: list<'get_called_class'|'get_class'|'get_class_this'|'php_sapi_name'|'phpversion'|'pi'>,
* }
*
* @implements ConfigurableFixerInterface<_AutogeneratedInputConfiguration, _AutogeneratedComputedConfiguration>
*
* @no-named-arguments Parameter names are not covered by the backward compatibility promise.
*/
final class FunctionToConstantFixer extends AbstractFixer implements ConfigurableFixerInterface
{
/** @use ConfigurableFixerTrait<_AutogeneratedInputConfiguration, _AutogeneratedComputedConfiguration> */
use ConfigurableFixerTrait;
/**
* @var null|array<string, non-empty-list<Token>>
*/
private static ?array $availableFunctions = null;
/**
* @var array<string, non-empty-list<Token>>
*/
private array $functionsFixMap;
public function __construct()
{
if (null === self::$availableFunctions) {
self::$availableFunctions = [
'get_called_class' => [
new Token([\T_STATIC, 'static']),
new Token([\T_DOUBLE_COLON, '::']),
new Token([CT::T_CLASS_CONSTANT, 'class']),
],
'get_class' => [
new Token([\T_STRING, 'self']),
new Token([\T_DOUBLE_COLON, '::']),
new Token([CT::T_CLASS_CONSTANT, 'class']),
],
'get_class_this' => [
new Token([\T_STATIC, 'static']),
new Token([\T_DOUBLE_COLON, '::']),
new Token([CT::T_CLASS_CONSTANT, 'class']),
],
'php_sapi_name' => [new Token([\T_STRING, 'PHP_SAPI'])],
'phpversion' => [new Token([\T_STRING, 'PHP_VERSION'])],
'pi' => [new Token([\T_STRING, 'M_PI'])],
];
}
parent::__construct();
}
public function getDefinition(): FixerDefinitionInterface
{
return new FixerDefinition(
'Replace core functions calls returning constants with the constants.',
[
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' => ['get_called_class', 'get_class_this', 'phpversion']],
),
],
null,
'Risky when any of the configured functions to replace are overridden.',
);
}
/**
* {@inheritdoc}
*
* Must run before NativeConstantInvocationFixer, NativeFunctionCasingFixer, NoExtraBlankLinesFixer, NoSinglelineWhitespaceBeforeSemicolonsFixer, NoTrailingWhitespaceFixer, NoWhitespaceInBlankLineFixer, SelfStaticAccessorFixer.
* Must run after NoSpacesAfterFunctionNameFixer, NoSpacesInsideParenthesisFixer, SpacesInsideParenthesesFixer.
*/
public function getPriority(): int
{
return 2;
}
public function isCandidate(Tokens $tokens): bool
{
return $tokens->isTokenKindFound(\T_STRING);
}
public function isRisky(): bool
{
return true;
}
protected function configurePostNormalisation(): void
{
$this->functionsFixMap = [];
foreach ($this->configuration['functions'] as $key) {
$this->functionsFixMap[$key] = self::$availableFunctions[$key];
}
}
protected function applyFix(\SplFileInfo $file, Tokens $tokens): void
{
$functionAnalyzer = new FunctionsAnalyzer();
for ($index = $tokens->count() - 4; $index > 0; --$index) {
$candidate = $this->getReplaceCandidate($tokens, $functionAnalyzer, $index);
if (null === $candidate) {
continue;
}
$this->fixFunctionCallToConstant(
$tokens,
$index,
$candidate[0], // brace open
$candidate[1], // brace close
$candidate[2], // replacement
);
}
}
protected function createConfigurationDefinition(): FixerConfigurationResolverInterface
{
$functionNames = array_keys(self::$availableFunctions);
return new FixerConfigurationResolver([
(new FixerOptionBuilder('functions', 'List of function names to fix.'))
->setAllowedTypes(['string[]'])
->setAllowedValues([new AllowedValueSubset($functionNames)])
->setDefault([
'get_called_class',
'get_class',
'get_class_this',
'php_sapi_name',
'phpversion',
'pi',
])
->getOption(),
]);
}
/**
* @param non-empty-list<Token> $replacements
*/
private function fixFunctionCallToConstant(Tokens $tokens, int $index, int $braceOpenIndex, int $braceCloseIndex, array $replacements): void
{
for ($i = $braceCloseIndex; $i >= $braceOpenIndex; --$i) {
if ($tokens[$i]->isGivenKind([\T_WHITESPACE, \T_COMMENT, \T_DOC_COMMENT])) {
continue;
}
$tokens->clearTokenAndMergeSurroundingWhitespace($i);
}
if (
$replacements[0]->isGivenKind([\T_CLASS_C, \T_STATIC])
|| ($replacements[0]->isGivenKind(\T_STRING) && 'self' === $replacements[0]->getContent())
) {
$prevIndex = $tokens->getPrevMeaningfulToken($index);
$prevToken = $tokens[$prevIndex];
if ($prevToken->isGivenKind(\T_NS_SEPARATOR)) {
$tokens->clearAt($prevIndex);
}
}
$tokens->clearAt($index);
$tokens->insertAt($index, $replacements);
}
/**
* @return ?array{int, int, non-empty-list<Token>}
*/
private function getReplaceCandidate(
Tokens $tokens,
FunctionsAnalyzer $functionAnalyzer,
int $index
): ?array {
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;
}
$braceCloseIndex = $tokens->getNextMeaningfulToken($braceOpenIndex);
if (!$tokens[$braceCloseIndex]->equals(')')) {
return null;
}
return $this->getReplacementTokenClones($lowerContent, $braceOpenIndex, $braceCloseIndex);
}
/**
* @return ?array{int, int, non-empty-list<Token>}
*/
private function fixGetClassCall(
Tokens $tokens,
FunctionsAnalyzer $functionAnalyzer,
int $index
): ?array {
if (!isset($this->functionsFixMap['get_class']) && !isset($this->functionsFixMap['get_class_this'])) {
return null;
}
if (!$functionAnalyzer->isGlobalFunctionCall($tokens, $index)) {
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);
}
} elseif (isset($this->functionsFixMap['get_class_this'])) {
$isThis = false;
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;
}
/**
* @return array{int, int, non-empty-list<Token>}
*/
private function getReplacementTokenClones(string $lowerContent, int $braceOpenIndex, int $braceCloseIndex): array
{
$clones = array_map(
static fn (Token $token): Token => clone $token,
$this->functionsFixMap[$lowerContent],
);
return [
$braceOpenIndex,
$braceCloseIndex,
$clones,
];
}
}