<?php

/*
 * 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\StringNotation;

use PhpCsFixer\AbstractFixer;
use PhpCsFixer\Fixer\WhitespacesAwareFixerInterface;
use PhpCsFixer\FixerDefinition\CodeSample;
use PhpCsFixer\FixerDefinition\FixerDefinition;
use PhpCsFixer\Preg;
use PhpCsFixer\Tokenizer\Token;
use PhpCsFixer\Tokenizer\Tokens;

/**
 * Fixes the line endings in multi-line strings.
 *
 * @author Ilija Tovilo <ilija.tovilo@me.com>
 */
final class StringLineEndingFixer extends AbstractFixer implements WhitespacesAwareFixerInterface
{
    /**
     * {@inheritdoc}
     */
    public function isCandidate(Tokens $tokens)
    {
        return $tokens->isAnyTokenKindsFound([T_CONSTANT_ENCAPSED_STRING, T_ENCAPSED_AND_WHITESPACE]);
    }

    /**
     * {@inheritdoc}
     */
    public function isRisky()
    {
        return true;
    }

    /**
     * {@inheritdoc}
     */
    public function getDefinition()
    {
        return new FixerDefinition(
            'All multi-line strings must use correct line ending.',
            [
                new CodeSample(
                    "<?php \$a = 'my\r\nmulti\nline\r\nstring';\r\n"
                ),
            ],
            null,
            'Changing the line endings of multi-line strings might affect string comparisons and outputs.'
        );
    }

    /**
     * {@inheritdoc}
     */
    protected function applyFix(\SplFileInfo $file, Tokens $tokens)
    {
        $ending = $this->whitespacesConfig->getLineEnding();

        foreach ($tokens as $tokenIndex => $token) {
            if (!$token->isGivenKind([T_CONSTANT_ENCAPSED_STRING, T_ENCAPSED_AND_WHITESPACE])) {
                continue;
            }

            $tokens[$tokenIndex] = new Token([
                $token->getId(),
                Preg::replace(
                    '#\R#u',
                    $ending,
                    $token->getContent()
                ),
            ]);
        }
    }
}


<?php

/*
 * 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\FunctionNotation;

use PhpCsFixer\AbstractFixer;
use PhpCsFixer\FixerDefinition\CodeSample;
use PhpCsFixer\FixerDefinition\FixerDefinition;
use PhpCsFixer\Tokenizer\CT;
use PhpCsFixer\Tokenizer\Tokens;

/**
 * Fixer for rules defined in PSR2 ¶4.6.
 *
 * @author Varga Bence <vbence@czentral.org>
 * @author Dariusz Rumiński <dariusz.ruminski@gmail.com>
 */
final class NoSpacesAfterFunctionNameFixer extends AbstractFixer
{
    /**
     * {@inheritdoc}
     */
    public function getDefinition()
    {
        return new FixerDefinition(
            'When making a method or function call, there MUST NOT be a space between the method or function name and the opening parenthesis.',
            [new CodeSample("<?php\nrequire ('sample.php');\necho (test (3));\nexit  (1);\n\$func ();\n")]
        );
    }

    /**
     * {@inheritdoc}
     */
    public function getPriority()
    {
        // must run before FunctionToConstantFixer
        return 2;
    }

    /**
     * {@inheritdoc}
     */
    public function isCandidate(Tokens $tokens)
    {
        return $tokens->isAnyTokenKindsFound(array_merge($this->getFunctionyTokenKinds(), [T_STRING]));
    }

    /**
     * {@inheritdoc}
     */
    protected function applyFix(\SplFileInfo $file, Tokens $tokens)
    {
        $functionyTokens = $this->getFunctionyTokenKinds();
        $languageConstructionTokens = $this->getLanguageConstructionTokenKinds();
        $braceTypes = $this->getBraceAfterVariableKinds();

        foreach ($tokens as $index => $token) {
            // looking for start brace
            if (!$token->equals('(')) {
                continue;
            }

            // last non-whitespace token, can never be `null` always at least PHP open tag before it
            $lastTokenIndex = $tokens->getPrevNonWhitespace($index);

            // check for ternary operator
            $endParenthesisIndex = $tokens->findBlockEnd(Tokens::BLOCK_TYPE_PARENTHESIS_BRACE, $index);
            $nextNonWhiteSpace = $tokens->getNextMeaningfulToken($endParenthesisIndex);
            if (
                null !== $nextNonWhiteSpace
                && $tokens[$nextNonWhiteSpace]->equals('?')
                && $tokens[$lastTokenIndex]->isGivenKind($languageConstructionTokens)
            ) {
                continue;
            }

            // check if it is a function call
            if ($tokens[$lastTokenIndex]->isGivenKind($functionyTokens)) {
                $this->fixFunctionCall($tokens, $index);
            } elseif ($tokens[$lastTokenIndex]->isGivenKind(T_STRING)) { // for real function calls or definitions
                $possibleDefinitionIndex = $tokens->getPrevMeaningfulToken($lastTokenIndex);
                if (!$tokens[$possibleDefinitionIndex]->isGivenKind(T_FUNCTION)) {
                    $this->fixFunctionCall($tokens, $index);
                }
            } elseif ($tokens[$lastTokenIndex]->equalsAny($braceTypes)) {
                $block = Tokens::detectBlockType($tokens[$lastTokenIndex]);
                if (
                    Tokens::BLOCK_TYPE_ARRAY_INDEX_CURLY_BRACE === $block['type']
                    || Tokens::BLOCK_TYPE_DYNAMIC_VAR_BRACE === $block['type']
                    || Tokens::BLOCK_TYPE_INDEX_SQUARE_BRACE === $block['type']
                    || Tokens::BLOCK_TYPE_PARENTHESIS_BRACE === $block['type']
                ) {
                    $this->fixFunctionCall($tokens, $index);
                }
            }
        }
    }

    /**
     * Fixes whitespaces around braces of a function(y) call.
     *
     * @param Tokens $tokens tokens to handle
     * @param int    $index  index of token
     */
    private function fixFunctionCall(Tokens $tokens, $index)
    {
        // remove space before opening brace
        if ($tokens[$index - 1]->isWhitespace()) {
            $tokens->clearAt($index - 1);
        }
    }

    /**
     * @return array<array|string>
     */
    private function getBraceAfterVariableKinds()
    {
        static $tokens = [
            ')',
            ']',
            [CT::T_DYNAMIC_VAR_BRACE_CLOSE],
            [CT::T_ARRAY_INDEX_CURLY_BRACE_CLOSE],
        ];

        return $tokens;
    }

    /**
     * Gets the token kinds which can work as function calls.
     *
     * @return int[] Token names
     */
    private function getFunctionyTokenKinds()
    {
        static $tokens = [
            T_ARRAY,
            T_ECHO,
            T_EMPTY,
            T_EVAL,
            T_EXIT,
            T_INCLUDE,
            T_INCLUDE_ONCE,
            T_ISSET,
            T_LIST,
            T_PRINT,
            T_REQUIRE,
            T_REQUIRE_ONCE,
            T_UNSET,
            T_VARIABLE,
        ];

        return $tokens;
    }

    /**
     * Gets the token kinds of actually language construction.
     *
     * @return int[]
     */
    private function getLanguageConstructionTokenKinds()
    {
        static $languageConstructionTokens = [
            T_ECHO,
            T_PRINT,
            T_INCLUDE,
            T_INCLUDE_ONCE,
            T_REQUIRE,
            T_REQUIRE_ONCE,
        ];

        return $languageConstructionTokens;
    }
}


<?php

/*
 * 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\Linter;

use PhpCsFixer\FileReader;
use PhpCsFixer\Tokenizer\CodeHasher;
use PhpCsFixer\Tokenizer\Tokens;

/**
 * Handle PHP code linting.
 *
 * @author Dariusz Rumiński <dariusz.ruminski@gmail.com>
 *
 * @internal
 */
final class TokenizerLinter implements LinterInterface
{
    public function __construct()
    {
        if (false === \defined('TOKEN_PARSE')) {
            throw new UnavailableLinterException('Cannot use tokenizer as linter.');
        }
    }

    /**
     * {@inheritdoc}
     */
    public function isAsync()
    {
        return false;
    }

    /**
     * {@inheritdoc}
     */
    public function lintFile($path)
    {
        return $this->lintSource(FileReader::createSingleton()->read($path));
    }

    /**
     * {@inheritdoc}
     */
    public function lintSource($source)
    {
        try {
            // To lint, we will parse the source into Tokens.
            // During that process, it might throw ParseError.
            // If it won't, cache of tokenized version of source will be kept, which is great for Runner.
            // Yet, first we need to clear already existing cache to not hit it and lint the code indeed.
            $codeHash = CodeHasher::calculateCodeHash($source);
            Tokens::clearCache($codeHash);
            Tokens::fromCode($source);

            return new TokenizerLintingResult();
        } catch (\ParseError $e) {
            return new TokenizerLintingResult($e);
        }
    }
}


<?php

/*
 * 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\ConfigurationDefinitionFixerInterface;
use PhpCsFixer\FixerConfiguration\AllowedValueSubset;
use PhpCsFixer\FixerConfiguration\FixerConfigurationResolver;
use PhpCsFixer\FixerConfiguration\FixerOptionBuilder;
use PhpCsFixer\FixerDefinition\CodeSample;
use PhpCsFixer\FixerDefinition\FixerDefinition;
use PhpCsFixer\Tokenizer\CT;
use PhpCsFixer\Tokenizer\Token;
use PhpCsFixer\Tokenizer\Tokens;

/**
 * @author SpacePossum
 */
final class FunctionToConstantFixer extends AbstractFixer implements ConfigurationDefinitionFixerInterface
{
    /**
     * @var array<string, Token[]>
     */
    private static $availableFunctions;

    /**
     * @var array<string, Token[]>
     */
    private $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_CLASS_C, '__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();
    }

    /**
     * {@inheritdoc}
     */
    public function configure(array $configuration = null)
    {
        parent::configure($configuration);

        $this->functionsFixMap = [];
        foreach ($this->configuration['functions'] as $key) {
            $this->functionsFixMap[$key] = self::$availableFunctions[$key];
        }
    }

    /**
     * {@inheritdoc}
     */
    public function getDefinition()
    {
        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']]),
            ],
            null,
            'Risky when any of the configured functions to replace are overridden.'
        );
    }

    /**
     * {@inheritdoc}
     */
    public function getPriority()
    {
        // should run before NativeFunctionCasingFixer
        // must run before NoExtraBlankLinesFixer, NoSinglelineWhitespaceBeforeSemicolonsFixer, NoTrailingWhitespaceFixer and NoWhitespaceInBlankLineFixer
        // must run after NoSpacesAfterFunctionNameFixer and NoSpacesInsideParenthesisFixer

        return 1;
    }

    /**
     * {@inheritdoc}
     */
    public function isCandidate(Tokens $tokens)
    {
        return $tokens->isTokenKindFound(T_STRING);
    }

    /**
     * {@inheritdoc}
     */
    public function isRisky()
    {
        return true;
    }

    /**
     * {@inheritdoc}
     */
    protected function applyFix(\SplFileInfo $file, Tokens $tokens)
    {
        for ($index = $tokens->count() - 4; $index > 0; --$index) {
            $candidate = $this->getReplaceCandidate($tokens, $index);
            if (null === $candidate) {
                continue;
            }

            $this->fixFunctionCallToConstant(
                $tokens,
                $index,
                $candidate[0], // brace open
                $candidate[1], // brace close
                $candidate[2]  // replacement
            );
        }
    }

    /**
     * {@inheritdoc}
     */
    protected function createConfigurationDefinition()
    {
        $functionNames = array_keys(self::$availableFunctions);

        return new FixerConfigurationResolver([
            (new FixerOptionBuilder('functions', 'List of function names to fix.'))
                ->setAllowedTypes(['array'])
                ->setAllowedValues([new AllowedValueSubset($functionNames)])
                ->setDefault([
                    'get_class',
                    'php_sapi_name',
                    'phpversion',
                    'pi',
                    // TODO on v3.0 add 'get_called_class' here
                ])
                ->getOption(),
        ]);
    }

    /**
     * @param Tokens  $tokens
     * @param int     $index
     * @param int     $braceOpenIndex
     * @param int     $braceCloseIndex
     * @param Token[] $replacements
     */
    private function fixFunctionCallToConstant(Tokens $tokens, $index, $braceOpenIndex, $braceCloseIndex, array $replacements)
    {
        $tokens->clearTokenAndMergeSurroundingWhitespace($braceCloseIndex);
        $tokens->clearTokenAndMergeSurroundingWhitespace($braceOpenIndex);

        if ($replacements[0]->isGivenKind([T_CLASS_C, T_STATIC])) {
            $prevIndex = $tokens->getPrevMeaningfulToken($index);
            $prevToken = $tokens[$prevIndex];
            if ($prevToken->isGivenKind(T_NS_SEPARATOR)) {
                $tokens->clearAt($prevIndex);
            }
        }

        $tokens->clearAt($index);
        $tokens->insertAt($index, $replacements);
    }

    /**
     * @param Tokens $tokens
     * @param int    $index
     *
     * @return null|array
     */
    private function getReplaceCandidate(Tokens $tokens, $index)
    {
        // test if we are at a function all
        if (!$tokens[$index]->isGivenKind(T_STRING)) {
            return null;
        }

        $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 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;
            }
        }

        // 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;
        }

        $clones = [];
        foreach ($this->functionsFixMap[$lowerContent] as $token) {
            $clones[] = clone $token;
        }

        return [
            $braceOpenIndex,
            $braceCloseIndex,
            $clones,
        ];
    }
}


<?php

/*
 * 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\Semicolon;

use PhpCsFixer\AbstractFixer;
use PhpCsFixer\Fixer\ConfigurationDefinitionFixerInterface;
use PhpCsFixer\FixerConfiguration\FixerConfigurationResolver;
use PhpCsFixer\FixerConfiguration\FixerOptionBuilder;
use PhpCsFixer\FixerDefinition\CodeSample;
use PhpCsFixer\FixerDefinition\FixerDefinition;
use PhpCsFixer\Preg;
use PhpCsFixer\Tokenizer\Token;
use PhpCsFixer\Tokenizer\Tokens;

/**
 * @author SpacePossum
 */
final class SpaceAfterSemicolonFixer extends AbstractFixer implements ConfigurationDefinitionFixerInterface
{
    /**
     * {@inheritdoc}
     */
    public function getDefinition()
    {
        return new FixerDefinition(
            'Fix whitespace after a semicolon.',
            [
                new CodeSample(
                    "<?php
                        sample();     \$test = 1;
                        sample();\$test = 2;
                        for ( ;;++\$sample) {
                        }\n"
                ),
                new CodeSample("<?php\nfor (\$i = 0; ; ++\$i) {\n}\n", [
                    'remove_in_empty_for_expressions' => true,
                ]),
            ]
        );
    }

    /**
     * {@inheritdoc}
     */
    public function getPriority()
    {
        return -1; // Must run after MultilineWhitespaceBeforeSemicolonsFixer
    }

    /**
     * {@inheritdoc}
     */
    public function isCandidate(Tokens $tokens)
    {
        return $tokens->isTokenKindFound(';');
    }

    /**
     * {@inheritdoc}
     */
    protected function createConfigurationDefinition()
    {
        return new FixerConfigurationResolver([
            (new FixerOptionBuilder('remove_in_empty_for_expressions', 'Whether spaces should be removed for empty `for` expressions.'))
                ->setAllowedTypes(['bool'])
                ->setDefault(false)
                ->getOption(),
        ]);
    }

    /**
     * {@inheritdoc}
     */
    protected function applyFix(\SplFileInfo $file, Tokens $tokens)
    {
        $insideForParenthesesUntil = null;

        for ($index = 0, $max = \count($tokens) - 1; $index < $max; ++$index) {
            if ($this->configuration['remove_in_empty_for_expressions']) {
                if ($tokens[$index]->isGivenKind(T_FOR)) {
                    $index = $tokens->getNextMeaningfulToken($index);
                    $insideForParenthesesUntil = $tokens->findBlockEnd(Tokens::BLOCK_TYPE_PARENTHESIS_BRACE, $index);

                    continue;
                }

                if ($index === $insideForParenthesesUntil) {
                    $insideForParenthesesUntil = null;

                    continue;
                }
            }

            if (!$tokens[$index]->equals(';')) {
                continue;
            }

            if (!$tokens[$index + 1]->isWhitespace()) {
                if (
                    !$tokens[$index + 1]->equalsAny([')', [T_INLINE_HTML]]) && (
                        !$this->configuration['remove_in_empty_for_expressions']
                        || !$tokens[$index + 1]->equals(';')
                    )
                ) {
                    $tokens->insertAt($index + 1, new Token([T_WHITESPACE, ' ']));
                    ++$max;
                }

                continue;
            }

            if (
                null !== $insideForParenthesesUntil
                && ($tokens[$index + 2]->equals(';') || $index + 2 === $insideForParenthesesUntil)
                && !Preg::match('/\R/', $tokens[$index + 1]->getContent())
            ) {
                $tokens->clearAt($index + 1);

                continue;
            }

            if (
                isset($tokens[$index + 2])
                && !$tokens[$index + 1]->equals([T_WHITESPACE, ' '])
                && $tokens[$index + 1]->isWhitespace(" \t")
                && !$tokens[$index + 2]->isComment()
                && !$tokens[$index + 2]->equals(')')
            ) {
                $tokens[$index + 1] = new Token([T_WHITESPACE, ' ']);
            }
        }
    }
}


<?php

/*
 * 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\Casing;

use PhpCsFixer\AbstractFixer;
use PhpCsFixer\FixerDefinition\CodeSample;
use PhpCsFixer\FixerDefinition\FixerDefinition;
use PhpCsFixer\Tokenizer\CT;
use PhpCsFixer\Tokenizer\Token;
use PhpCsFixer\Tokenizer\Tokens;

/**
 * @author SpacePossum
 */
final class NativeFunctionCasingFixer extends AbstractFixer
{
    /**
     * {@inheritdoc}
     */
    public function getDefinition()
    {
        return new FixerDefinition(
            'Function defined by PHP should be called using the correct casing.',
            [new CodeSample("<?php\nSTRLEN(\$str);\n")]
        );
    }

    /**
     * {@inheritdoc}
     */
    public function isCandidate(Tokens $tokens)
    {
        return $tokens->isTokenKindFound(T_STRING);
    }

    /**
     * {@inheritdoc}
     */
    protected function applyFix(\SplFileInfo $file, Tokens $tokens)
    {
        static $nativeFunctionNames = null;

        if (null === $nativeFunctionNames) {
            $nativeFunctionNames = $this->getNativeFunctionNames();
        }

        for ($index = 0, $count = $tokens->count(); $index < $count; ++$index) {
            // test if we are at a function all
            if (!$tokens[$index]->isGivenKind(T_STRING)) {
                continue;
            }

            $next = $tokens->getNextMeaningfulToken($index);
            if (!$tokens[$next]->equals('(')) {
                $index = $next;

                continue;
            }

            $functionNamePrefix = $tokens->getPrevMeaningfulToken($index);
            if ($tokens[$functionNamePrefix]->isGivenKind([T_DOUBLE_COLON, T_NEW, T_OBJECT_OPERATOR, T_FUNCTION, CT::T_RETURN_REF])) {
                continue;
            }

            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
                $prev = $tokens->getPrevMeaningfulToken($functionNamePrefix);
                if ($tokens[$prev]->isGivenKind([T_STRING, T_NEW])) {
                    continue;
                }
            }

            // test if the function call is to a native PHP function
            $lower = strtolower($tokens[$index]->getContent());
            if (!\array_key_exists($lower, $nativeFunctionNames)) {
                continue;
            }

            $tokens[$index] = new Token([T_STRING, $nativeFunctionNames[$lower]]);
            $index = $next;
        }
    }

    /**
     * @return array<string, string>
     */
    private function getNativeFunctionNames()
    {
        $allFunctions = get_defined_functions();
        $functions = [];
        foreach ($allFunctions['internal'] as $function) {
            $functions[strtolower($function)] = $function;
        }

        return $functions;
    }
}


<?php

/*
 * 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\ConfigurationDefinitionFixerInterface;
use PhpCsFixer\FixerConfiguration\FixerConfigurationResolver;
use PhpCsFixer\FixerConfiguration\FixerOptionBuilder;
use PhpCsFixer\FixerDefinition\CodeSample;
use PhpCsFixer\FixerDefinition\FixerDefinition;
use PhpCsFixer\Tokenizer\Token;
use PhpCsFixer\Tokenizer\Tokens;

/**
 * @author Dariusz Rumiński <dariusz.ruminski@gmail.com>
 * @author SpacePossum
 */
final class DeclareEqualNormalizeFixer extends AbstractFixer implements ConfigurationDefinitionFixerInterface
{
    /**
     * @var string
     */
    private $callback;

    /**
     * {@inheritdoc}
     */
    public function configure(array $configuration = null)
    {
        parent::configure($configuration);

        $this->callback = 'none' === $this->configuration['space'] ? 'removeWhitespaceAroundToken' : 'ensureWhitespaceAroundToken';
    }

    /**
     * {@inheritdoc}
     */
    public function getDefinition()
    {
        return new FixerDefinition(
            'Equal sign in declare statement should be surrounded by spaces or not following configuration.',
            [
                new CodeSample("<?php\ndeclare(ticks =  1);\n"),
                new CodeSample("<?php\ndeclare(ticks=1);\n", ['space' => 'single']),
            ]
        );
    }

    /**
     * {@inheritdoc}
     */
    public function isCandidate(Tokens $tokens)
    {
        return $tokens->isTokenKindFound(T_DECLARE);
    }

    /**
     * {@inheritdoc}
     */
    protected function applyFix(\SplFileInfo $file, Tokens $tokens)
    {
        $callback = $this->callback;
        for ($index = 0, $count = $tokens->count(); $index < $count - 6; ++$index) {
            if (!$tokens[$index]->isGivenKind(T_DECLARE)) {
                continue;
            }

            while (!$tokens[++$index]->equals('='));

            $this->{$callback}($tokens, $index);
        }
    }

    /**
     * {@inheritdoc}
     */
    protected function createConfigurationDefinition()
    {
        return new FixerConfigurationResolver([
            (new FixerOptionBuilder('space', 'Spacing to apply around the equal sign.'))
                ->setAllowedValues(['single', 'none'])
                ->setDefault('none')
                ->getOption(),
        ]);
    }

    /**
     * @param Tokens $tokens
     * @param int    $index  of `=` token
     */
    private function ensureWhitespaceAroundToken(Tokens $tokens, $index)
    {
        if ($tokens[$index + 1]->isWhitespace()) {
            if (' ' !== $tokens[$index + 1]->getContent()) {
                $tokens[$index + 1] = new Token([T_WHITESPACE, ' ']);
            }
        } else {
            $tokens->insertAt($index + 1, new Token([T_WHITESPACE, ' ']));
        }

        if ($tokens[$index - 1]->isWhitespace()) {
            if (' ' !== $tokens[$index - 1]->getContent() && !$tokens[$tokens->getPrevNonWhitespace($index - 1)]->isComment()) {
                $tokens[$index - 1] = new Token([T_WHITESPACE, ' ']);
            }
        } else {
            $tokens->insertAt($index, new Token([T_WHITESPACE, ' ']));
        }
    }

    /**
     * @param Tokens $tokens
     * @param int    $index  of `=` token
     */
    private function removeWhitespaceAroundToken(Tokens $tokens, $index)
    {
        if (!$tokens[$tokens->getPrevNonWhitespace($index)]->isComment()) {
            $tokens->removeLeadingWhitespace($index);
        }

        $tokens->removeTrailingWhitespace($index);
    }
}


<?php

/*
 * 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\Alias;

use PhpCsFixer\AbstractFunctionReferenceFixer;
use PhpCsFixer\FixerDefinition\CodeSample;
use PhpCsFixer\FixerDefinition\FixerDefinition;
use PhpCsFixer\Tokenizer\Analyzer\ArgumentsAnalyzer;
use PhpCsFixer\Tokenizer\CT;
use PhpCsFixer\Tokenizer\Token;
use PhpCsFixer\Tokenizer\Tokens;

/**
 * @author SpacePossum
 */
final class PowToExponentiationFixer extends AbstractFunctionReferenceFixer
{
    /**
     * {@inheritdoc}
     */
    public function isCandidate(Tokens $tokens)
    {
        // minimal candidate to fix is seven tokens: pow(x,x);
        return $tokens->count() > 7 && $tokens->isTokenKindFound(T_STRING);
    }

    /**
     * {@inheritdoc}
     */
    public function getDefinition()
    {
        return new FixerDefinition(
            'Converts `pow` to the `**` operator.',
            [
                new CodeSample(
                    "<?php\n pow(\$a, 1);\n"
                ),
            ],
            null,
            'Risky when the function `pow` is overridden.'
        );
    }

    /**
     * {@inheritdoc}
     */
    public function getPriority()
    {
        // must be run before BinaryOperatorSpacesFixer
        return 3;
    }

    /**
     * {@inheritdoc}
     */
    protected function applyFix(\SplFileInfo $file, Tokens $tokens)
    {
        $candidates = $this->findPowCalls($tokens);
        $argumentsAnalyzer = new ArgumentsAnalyzer();

        $numberOfTokensAdded = 0;
        $previousCloseParenthesisIndex = \count($tokens);
        foreach (array_reverse($candidates) as $candidate) {
            // if in the previous iteration(s) tokens were added to the collection and this is done within the tokens
            // indexes of the current candidate than the index of the close ')' of the candidate has moved and so
            // the index needs to be updated
            if ($previousCloseParenthesisIndex < $candidate[2]) {
                $previousCloseParenthesisIndex = $candidate[2];
                $candidate[2] += $numberOfTokensAdded;
            } else {
                $previousCloseParenthesisIndex = $candidate[2];
                $numberOfTokensAdded = 0;
            }

            $arguments = $argumentsAnalyzer->getArguments($tokens, $candidate[1], $candidate[2]);
            if (2 !== \count($arguments)) {
                continue;
            }

            $numberOfTokensAdded += $this->fixPowToExponentiation(
                $tokens,
                $candidate[0], // functionNameIndex,
                $candidate[1], // openParenthesisIndex,
                $candidate[2], // closeParenthesisIndex,
                $arguments
            );
        }
    }

    /**
     * @param Tokens $tokens
     *
     * @return array[]
     */
    private function findPowCalls(Tokens $tokens)
    {
        $candidates = [];

        // Minimal candidate to fix is seven tokens: pow(x,x);
        $end = \count($tokens) - 6;

        // First possible location is after the open token: 1
        for ($i = 1; $i < $end; ++$i) {
            $candidate = $this->find('pow', $tokens, $i, $end);
            if (null === $candidate) {
                break;
            }

            $i = $candidate[1]; // proceed to openParenthesisIndex
            $candidates[] = $candidate;
        }

        return $candidates;
    }

    /**
     * @param Tokens         $tokens
     * @param int            $functionNameIndex
     * @param int            $openParenthesisIndex
     * @param int            $closeParenthesisIndex
     * @param array<int,int> $arguments
     *
     * @return int number of tokens added to the collection
     */
    private function fixPowToExponentiation(Tokens $tokens, $functionNameIndex, $openParenthesisIndex, $closeParenthesisIndex, array $arguments)
    {
        // find the argument separator ',' directly after the last token of the first argument;
        // replace it with T_POW '**'
        $tokens[$tokens->getNextTokenOfKind(reset($arguments), [','])] = new Token([T_POW, '**']);

        // clean up the function call tokens prt. I
        $tokens->clearAt($closeParenthesisIndex);
        $previousIndex = $tokens->getPrevMeaningfulToken($closeParenthesisIndex);
        if ($tokens[$previousIndex]->equals(',')) {
            $tokens->clearAt($previousIndex); // trailing ',' in function call (PHP 7.3)
        }

        $added = 0;
        // check if the arguments need to be wrapped in parenthesis
        foreach (array_reverse($arguments, true) as $argumentStartIndex => $argumentEndIndex) {
            if ($this->isParenthesisNeeded($tokens, $argumentStartIndex, $argumentEndIndex)) {
                $tokens->insertAt($argumentEndIndex + 1, new Token(')'));
                $tokens->insertAt($argumentStartIndex, new Token('('));
                $added += 2;
            }
        }

        // clean up the function call tokens prt. II
        $tokens->clearAt($openParenthesisIndex);
        $tokens->clearAt($functionNameIndex);

        $prev = $tokens->getPrevMeaningfulToken($functionNameIndex);
        if ($tokens[$prev]->isGivenKind(T_NS_SEPARATOR)) {
            $tokens->clearAt($prev);
        }

        return $added;
    }

    /**
     * @param Tokens $tokens
     * @param int    $argumentStartIndex
     * @param int    $argumentEndIndex
     *
     * @return bool
     */
    private function isParenthesisNeeded(Tokens $tokens, $argumentStartIndex, $argumentEndIndex)
    {
        static $allowedKinds = [
            T_DNUMBER, T_LNUMBER, T_VARIABLE, T_STRING, T_OBJECT_OPERATOR, T_CONSTANT_ENCAPSED_STRING, T_DOUBLE_CAST,
            T_INT_CAST, T_INC, T_DEC, T_NS_SEPARATOR, T_WHITESPACE, T_DOUBLE_COLON, T_LINE, T_COMMENT, T_DOC_COMMENT,
            CT::T_NAMESPACE_OPERATOR,
        ];

        for ($i = $argumentStartIndex; $i <= $argumentEndIndex; ++$i) {
            if ($tokens[$i]->isGivenKind($allowedKinds) || $tokens->isEmptyAt($i)) {
                continue;
            }

            if (null !== $blockType = Tokens::detectBlockType($tokens[$i])) {
                $i = $tokens->findBlockEnd($blockType['type'], $i);

                continue;
            }

            if ($tokens[$i]->equals('$')) {
                $i = $tokens->getNextMeaningfulToken($i);
                if ($tokens[$i]->isGivenKind(CT::T_DYNAMIC_VAR_BRACE_OPEN)) {
                    $i = $tokens->findBlockEnd(Tokens::BLOCK_TYPE_DYNAMIC_VAR_BRACE, $i);

                    continue;
                }
            }

            if ($tokens[$i]->equals('+') && $tokens->getPrevMeaningfulToken($i) < $argumentStartIndex) {
                continue;
            }

            return true;
        }

        return false;
    }
}


<?php

/*
 * 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\Report;

use PhpCsFixer\Preg;
use Symfony\Component\Console\Formatter\OutputFormatter;

/**
 * @author Boris Gorbylev <ekho@ekho.name>
 *
 * @internal
 */
final class JunitReporter implements ReporterInterface
{
    /**
     * {@inheritdoc}
     */
    public function getFormat()
    {
        return 'junit';
    }

    /**
     * {@inheritdoc}
     */
    public function generate(ReportSummary $reportSummary)
    {
        if (!\extension_loaded('dom')) {
            throw new \RuntimeException('Cannot generate report! `ext-dom` is not available!');
        }

        $dom = new \DOMDocument('1.0', 'UTF-8');
        $testsuites = $dom->appendChild($dom->createElement('testsuites'));
        /** @var \DomElement $testsuite */
        $testsuite = $testsuites->appendChild($dom->createElement('testsuite'));
        $testsuite->setAttribute('name', 'PHP CS Fixer');

        if (\count($reportSummary->getChanged())) {
            $this->createFailedTestCases($dom, $testsuite, $reportSummary);
        } else {
            $this->createSuccessTestCase($dom, $testsuite);
        }

        if ($reportSummary->getTime()) {
            $testsuite->setAttribute(
                'time',
                sprintf(
                    '%.3f',
                    $reportSummary->getTime() / 1000
                )
            );
        }

        $dom->formatOutput = true;

        return $reportSummary->isDecoratedOutput() ? OutputFormatter::escape($dom->saveXML()) : $dom->saveXML();
    }

    private function createSuccessTestCase(\DOMDocument $dom, \DOMElement $testsuite)
    {
        $testcase = $dom->createElement('testcase');
        $testcase->setAttribute('name', 'All OK');
        $testcase->setAttribute('assertions', '1');

        $testsuite->appendChild($testcase);
        $testsuite->setAttribute('tests', '1');
        $testsuite->setAttribute('assertions', '1');
        $testsuite->setAttribute('failures', '0');
        $testsuite->setAttribute('errors', '0');
    }

    /**
     * @param \DOMDocument  $dom
     * @param \DOMElement   $testsuite
     * @param ReportSummary $reportSummary
     */
    private function createFailedTestCases(\DOMDocument $dom, \DOMElement $testsuite, ReportSummary $reportSummary)
    {
        $assertionsCount = 0;
        foreach ($reportSummary->getChanged() as $file => $fixResult) {
            $testcase = $this->createFailedTestCase(
                $dom,
                $file,
                $fixResult,
                $reportSummary->shouldAddAppliedFixers()
            );
            $testsuite->appendChild($testcase);
            $assertionsCount += (int) $testcase->getAttribute('assertions');
        }

        $testsuite->setAttribute('tests', (string) \count($reportSummary->getChanged()));
        $testsuite->setAttribute('assertions', (string) $assertionsCount);
        $testsuite->setAttribute('failures', (string) $assertionsCount);
        $testsuite->setAttribute('errors', '0');
    }

    /**
     * @param \DOMDocument $dom
     * @param string       $file
     * @param array        $fixResult
     * @param bool         $shouldAddAppliedFixers
     *
     * @return \DOMElement
     */
    private function createFailedTestCase(\DOMDocument $dom, $file, array $fixResult, $shouldAddAppliedFixers)
    {
        $appliedFixersCount = \count($fixResult['appliedFixers']);

        $testName = str_replace('.', '_DOT_', Preg::replace('@\.'.pathinfo($file, PATHINFO_EXTENSION).'$@', '', $file));

        $testcase = $dom->createElement('testcase');
        $testcase->setAttribute('name', $testName);
        $testcase->setAttribute('file', $file);
        $testcase->setAttribute('assertions', (string) $appliedFixersCount);

        $failure = $dom->createElement('failure');
        $failure->setAttribute('type', 'code_style');
        $testcase->appendChild($failure);

        if ($shouldAddAppliedFixers) {
            $failureContent = "applied fixers:\n---------------\n";

            foreach ($fixResult['appliedFixers'] as $appliedFixer) {
                $failureContent .= "* {$appliedFixer}\n";
            }
        } else {
            $failureContent = "Wrong code style\n";
        }

        if (!empty($fixResult['diff'])) {
            $failureContent .= "\nDiff:\n---------------\n\n".$fixResult['diff'];
        }

        $failure->appendChild($dom->createCDATASection(trim($failureContent)));

        return $testcase;
    }
}


<?php

/*
 * 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\DoctrineAnnotation;

use Doctrine\Common\Annotations\DocLexer;
use PhpCsFixer\AbstractDoctrineAnnotationFixer;
use PhpCsFixer\Doctrine\Annotation\Tokens;
use PhpCsFixer\FixerConfiguration\FixerConfigurationResolver;
use PhpCsFixer\FixerConfiguration\FixerOptionBuilder;
use PhpCsFixer\FixerDefinition\CodeSample;
use PhpCsFixer\FixerDefinition\FixerDefinition;

/**
 * Forces the configured operator for assignment in arrays in Doctrine Annotations.
 */
final class DoctrineAnnotationArrayAssignmentFixer extends AbstractDoctrineAnnotationFixer
{
    /**
     * {@inheritdoc}
     */
    public function getDefinition()
    {
        return new FixerDefinition(
            'Doctrine annotations must use configured operator for assignment in arrays.',
            [
                new CodeSample(
                    "<?php\n/**\n * @Foo({bar : \"baz\"})\n */\nclass Bar {}\n"
                ),
                new CodeSample(
                    "<?php\n/**\n * @Foo({bar = \"baz\"})\n */\nclass Bar {}\n",
                    ['operator' => ':']
                ),
            ]
        );
    }

    /**
     * {@inheritdoc}
     */
    public function getPriority()
    {
        // must run before DoctrineAnnotationSpacesFixer
        return 1;
    }

    /**
     * {@inheritdoc}
     */
    protected function createConfigurationDefinition()
    {
        $options = parent::createConfigurationDefinition()->getOptions();

        $operator = new FixerOptionBuilder('operator', 'The operator to use.');
        $options[] = $operator
            ->setAllowedValues(['=', ':'])
            ->setDefault('=')
            ->getOption()
        ;

        return new FixerConfigurationResolver($options);
    }

    /**
     * {@inheritdoc}
     */
    protected function fixAnnotations(Tokens $tokens)
    {
        $scopes = [];
        foreach ($tokens as $token) {
            if ($token->isType(DocLexer::T_OPEN_PARENTHESIS)) {
                $scopes[] = 'annotation';

                continue;
            }

            if ($token->isType(DocLexer::T_OPEN_CURLY_BRACES)) {
                $scopes[] = 'array';

                continue;
            }

            if ($token->isType([DocLexer::T_CLOSE_PARENTHESIS, DocLexer::T_CLOSE_CURLY_BRACES])) {
                array_pop($scopes);

                continue;
            }

            if ('array' === end($scopes) && $token->isType([DocLexer::T_EQUALS, DocLexer::T_COLON])) {
                $token->setContent($this->configuration['operator']);
            }
        }
    }
}


<?php

/*
 * 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\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
{
    /**
     * {@inheritdoc}
     */
    public function getDefinition()
    {
        return new FixerDefinition(
            'PHP code must use the long `<?php` tags or short-echo `<?=` tags and not other tag variations.',
            [
                new CodeSample(
                    '<?

echo "Hello!";
'
                ),
            ]
        );
    }

    /**
     * {@inheritdoc}
     */
    public function getPriority()
    {
        // must run before all Token-based fixers
        return 98;
    }

    /**
     * {@inheritdoc}
     */
    public function isCandidate(Tokens $tokens)
    {
        return true;
    }

    /**
     * {@inheritdoc}
     */
    protected function applyFix(\SplFileInfo $file, Tokens $tokensOrg)
    {
        $content = $tokensOrg->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 (!$count) {
            return;
        }

        /* the following code is magic to revert previous replacements which should NOT be replaced, for example incorrectly replacing
         * > echo '<? ';
         * with
         * > echo '<?php ';
         */
        $tokens = Tokens::fromCode($newContent);

        $tokensOldContent = '';
        $tokensOldContentLength = 0;

        foreach ($tokens as $index => $token) {
            if ($token->isGivenKind(T_OPEN_TAG)) {
                $tokenContent = $token->getContent();

                if ('<?php' !== strtolower(substr($content, $tokensOldContentLength, 5))) {
                    $tokenContent = '<? ';
                }

                $tokensOldContent .= $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;
                        }
                    }
                }

                $tokens[$index] = new Token([$token->getId(), $tokenContent]);
                $token = $tokens[$index];
            }

            $tokensOldContent .= $token->getContent();
            $tokensOldContentLength += \strlen($token->getContent());
        }

        $tokensOrg->overrideRange(0, $tokensOrg->count() - 1, $tokens);
    }
}


<?php

/*
 * 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\PhpUnit;

use PhpCsFixer\AbstractFixer;
use PhpCsFixer\Fixer\ConfigurationDefinitionFixerInterface;
use PhpCsFixer\FixerConfiguration\AllowedValueSubset;
use PhpCsFixer\FixerConfiguration\FixerConfigurationResolverRootless;
use PhpCsFixer\FixerConfiguration\FixerOptionBuilder;
use PhpCsFixer\FixerDefinition\CodeSample;
use PhpCsFixer\FixerDefinition\FixerDefinition;
use PhpCsFixer\Tokenizer\Token;
use PhpCsFixer\Tokenizer\Tokens;

/**
 * @author SpacePossum
 * @author Dariusz Rumiński <dariusz.ruminski@gmail.com>
 */
final class PhpUnitDedicateAssertFixer extends AbstractFixer implements ConfigurationDefinitionFixerInterface
{
    private static $fixMap = [
        'array_key_exists' => ['assertArrayNotHasKey', 'assertArrayHasKey'],
        'empty' => ['assertNotEmpty', 'assertEmpty'],
        'file_exists' => ['assertFileNotExists', 'assertFileExists'],
        'is_array' => true,
        'is_bool' => true,
        'is_callable' => true,
        'is_dir' => ['assertDirectoryNotExists', 'assertDirectoryExists'],
        'is_double' => true,
        'is_float' => true,
        'is_infinite' => ['assertFinite', 'assertInfinite'],
        'is_int' => true,
        'is_integer' => true,
        'is_long' => true,
        'is_nan' => [false, 'assertNan'],
        'is_null' => ['assertNotNull', 'assertNull'],
        'is_numeric' => true,
        'is_object' => true,
        'is_readable' => ['assertNotIsReadable', 'assertIsReadable'],
        'is_real' => true,
        'is_resource' => true,
        'is_scalar' => true,
        'is_string' => true,
        'is_writable' => ['assertNotIsWritable', 'assertIsWritable'],
    ];

    /**
     * @var string[]
     */
    private $functions = [];

    /**
     * {@inheritdoc}
     */
    public function configure(array $configuration = null)
    {
        parent::configure($configuration);

        if (isset($this->configuration['functions'])) {
            $this->functions = $this->configuration['functions'];

            return;
        }

        // assertions added in 3.0: assertArrayNotHasKey assertArrayHasKey assertFileNotExists assertFileExists assertNotNull, assertNull
        $this->functions = [
            'array_key_exists',
            'file_exists',
            'is_null',
        ];

        if (PhpUnitTargetVersion::fulfills($this->configuration['target'], PhpUnitTargetVersion::VERSION_3_5)) {
            // assertions added in 3.5: assertInternalType assertNotEmpty assertEmpty
            $this->functions = array_merge($this->functions, [
                'empty',
                'is_array',
                'is_bool',
                'is_boolean',
                'is_callable',
                'is_double',
                'is_float',
                'is_int',
                'is_integer',
                'is_long',
                'is_numeric',
                'is_object',
                'is_real',
                'is_resource',
                'is_scalar',
                'is_string',
            ]);
        }

        if (PhpUnitTargetVersion::fulfills($this->configuration['target'], PhpUnitTargetVersion::VERSION_5_0)) {
            // assertions added in 5.0: assertFinite assertInfinite assertNan
            $this->functions = array_merge($this->functions, [
                'is_infinite',
                'is_nan',
            ]);
        }

        if (PhpUnitTargetVersion::fulfills($this->configuration['target'], PhpUnitTargetVersion::VERSION_5_6)) {
            // assertions added in 5.6: assertDirectoryExists assertDirectoryNotExists assertIsReadable assertNotIsReadable assertIsWritable assertNotIsWritable
            $this->functions = array_merge($this->functions, [
                'is_dir',
                'is_readable',
                'is_writable',
            ]);
        }
    }

    /**
     * {@inheritdoc}
     */
    public function isCandidate(Tokens $tokens)
    {
        return $tokens->isTokenKindFound(T_STRING);
    }

    /**
     * {@inheritdoc}
     */
    public function isRisky()
    {
        return true;
    }

    /**
     * {@inheritdoc}
     */
    public function getDefinition()
    {
        return new FixerDefinition(
            'PHPUnit assertions like `assertInternalType`, `assertFileExists`, should be used over `assertTrue`.',
            [
                new CodeSample(
                    '<?php
$this->assertTrue(is_float( $a), "my message");
$this->assertTrue(is_nan($a));
'
                ),
                new CodeSample(
                    '<?php
$this->assertTrue(is_dir($a));
$this->assertTrue(is_writable($a));
$this->assertTrue(is_readable($a));
',
                    ['target' => PhpUnitTargetVersion::VERSION_5_6]
                ),
            ],
            null,
            'Fixer could be risky if one is overriding PHPUnit\'s native methods.'
        );
    }

    /**
     * {@inheritdoc}
     */
    public function getPriority()
    {
        // should be run after the PhpUnitConstructFixer.
        return -15;
    }

    /**
     * {@inheritdoc}
     */
    protected function applyFix(\SplFileInfo $file, Tokens $tokens)
    {
        foreach ($this->getPreviousAssertCall($tokens) as $assertCall) {
            // test and fix for assertTrue/False to dedicated asserts
            if ('asserttrue' === $assertCall['loweredName'] || 'assertfalse' === $assertCall['loweredName']) {
                $this->fixAssertTrueFalse($tokens, $assertCall);

                continue;
            }

            if (
                'assertsame' === $assertCall['loweredName']
                || 'assertnotsame' === $assertCall['loweredName']
                || 'assertequals' === $assertCall['loweredName']
                || 'assertnotequals' === $assertCall['loweredName']
            ) {
                $this->fixAssertSameEquals($tokens, $assertCall);

                continue;
            }
        }
    }

    /**
     * {@inheritdoc}
     */
    protected function createConfigurationDefinition()
    {
        $values = [
            'array_key_exists',
            'empty',
            'file_exists',
            'is_array',
            'is_bool',
            'is_callable',
            'is_double',
            'is_float',
            'is_infinite',
            'is_int',
            'is_integer',
            'is_long',
            'is_nan',
            'is_null',
            'is_numeric',
            'is_object',
            'is_real',
            'is_resource',
            'is_scalar',
            'is_string',
        ];

        sort($values);

        return new FixerConfigurationResolverRootless('functions', [
            (new FixerOptionBuilder('functions', 'List of assertions to fix (overrides `target`).'))
                ->setAllowedTypes(['null', 'array'])
                ->setAllowedValues([
                    null,
                    new AllowedValueSubset($values),
                ])
                ->setDefault(null)
                ->setDeprecationMessage('Use option `target` instead.')
                ->getOption(),
            (new FixerOptionBuilder('target', 'Target version of PHPUnit.'))
                ->setAllowedTypes(['string'])
                ->setAllowedValues([
                    PhpUnitTargetVersion::VERSION_3_0,
                    PhpUnitTargetVersion::VERSION_3_5,
                    PhpUnitTargetVersion::VERSION_5_0,
                    PhpUnitTargetVersion::VERSION_5_6,
                    PhpUnitTargetVersion::VERSION_NEWEST,
                ])
                ->setDefault(PhpUnitTargetVersion::VERSION_5_0) // @TODO 3.x: change to `VERSION_NEWEST`
                ->getOption(),
        ], $this->getName());
    }

    /**
     * @param Tokens $tokens
     * @param array  $assertCall
     */
    private function fixAssertTrueFalse(Tokens $tokens, array $assertCall)
    {
        $testDefaultNamespaceTokenIndex = false;
        $testIndex = $tokens->getNextMeaningfulToken($assertCall['openBraceIndex']);

        if (!$tokens[$testIndex]->isGivenKind([T_EMPTY, T_STRING])) {
            if (!$tokens[$testIndex]->isGivenKind(T_NS_SEPARATOR)) {
                return;
            }

            $testDefaultNamespaceTokenIndex = $testIndex;
            $testIndex = $tokens->getNextMeaningfulToken($testIndex);
        }

        $testOpenIndex = $tokens->getNextMeaningfulToken($testIndex);
        if (!$tokens[$testOpenIndex]->equals('(')) {
            return;
        }

        $testCloseIndex = $tokens->findBlockEnd(Tokens::BLOCK_TYPE_PARENTHESIS_BRACE, $testOpenIndex);

        $assertCallCloseIndex = $tokens->getNextMeaningfulToken($testCloseIndex);
        if (!$tokens[$assertCallCloseIndex]->equalsAny([')', ','])) {
            return;
        }

        $isPositive = 'asserttrue' === $assertCall['loweredName'];

        $content = strtolower($tokens[$testIndex]->getContent());
        if (!\in_array($content, $this->functions, true)) {
            return;
        }

        if (\is_array(self::$fixMap[$content])) {
            if (false !== self::$fixMap[$content][$isPositive]) {
                $tokens[$assertCall['index']] = new Token([T_STRING, self::$fixMap[$content][$isPositive]]);
                $this->removeFunctionCall($tokens, $testDefaultNamespaceTokenIndex, $testIndex, $testOpenIndex, $testCloseIndex);
            }

            return;
        }

        $type = substr($content, 3);

        $tokens[$assertCall['index']] = new Token([T_STRING, $isPositive ? 'assertInternalType' : 'assertNotInternalType']);
        $tokens[$testIndex] = new Token([T_CONSTANT_ENCAPSED_STRING, "'".$type."'"]);
        $tokens[$testOpenIndex] = new Token(',');

        $tokens->clearTokenAndMergeSurroundingWhitespace($testCloseIndex);
        $commaIndex = $tokens->getPrevMeaningfulToken($testCloseIndex);
        if ($tokens[$commaIndex]->equals(',')) {
            $tokens->removeTrailingWhitespace($commaIndex);
            $tokens->clearAt($commaIndex);
        }

        if (!$tokens[$testOpenIndex + 1]->isWhitespace()) {
            $tokens->insertAt($testOpenIndex + 1, new Token([T_WHITESPACE, ' ']));
        }

        if (false !== $testDefaultNamespaceTokenIndex) {
            $tokens->clearTokenAndMergeSurroundingWhitespace($testDefaultNamespaceTokenIndex);
        }
    }

    /**
     * @param Tokens $tokens
     * @param array  $assertCall
     */
    private function fixAssertSameEquals(Tokens $tokens, array $assertCall)
    {
        // @ $this->/self::assertEquals/Same([$nextIndex])
        $expectedIndex = $tokens->getNextMeaningfulToken($assertCall['openBraceIndex']);

        // do not fix
        // let $a = [1,2]; $b = "2";
        // "$this->assertEquals("2", count($a)); $this->assertEquals($b, count($a)); $this->assertEquals(2.1, count($a));"

        if (!$tokens[$expectedIndex]->isGivenKind(T_LNUMBER)) {
            return;
        }

        // @ $this->/self::assertEquals/Same([$nextIndex,$commaIndex])
        $commaIndex = $tokens->getNextMeaningfulToken($expectedIndex);
        if (!$tokens[$commaIndex]->equals(',')) {
            return;
        }

        // @ $this->/self::assertEquals/Same([$nextIndex,$commaIndex,$countCallIndex])
        $countCallIndex = $tokens->getNextMeaningfulToken($commaIndex);
        if ($tokens[$countCallIndex]->isGivenKind(T_NS_SEPARATOR)) {
            $defaultNamespaceTokenIndex = $countCallIndex;
            $countCallIndex = $tokens->getNextMeaningfulToken($countCallIndex);
        } else {
            $defaultNamespaceTokenIndex = false;
        }

        if (!$tokens[$countCallIndex]->isGivenKind(T_STRING)) {
            return;
        }

        $lowerContent = strtolower($tokens[$countCallIndex]->getContent());
        if ('count' !== $lowerContent && 'sizeof' !== $lowerContent) {
            return; // not a call to "count" or "sizeOf"
        }

        // @ $this->/self::assertEquals/Same([$nextIndex,$commaIndex,[$defaultNamespaceTokenIndex,]$countCallIndex,$countCallOpenBraceIndex])
        $countCallOpenBraceIndex = $tokens->getNextMeaningfulToken($countCallIndex);
        if (!$tokens[$countCallOpenBraceIndex]->equals('(')) {
            return;
        }

        $countCallCloseBraceIndex = $tokens->findBlockEnd(Tokens::BLOCK_TYPE_PARENTHESIS_BRACE, $countCallOpenBraceIndex);

        $afterCountCallCloseBraceIndex = $tokens->getNextMeaningfulToken($countCallCloseBraceIndex);
        if (!$tokens[$afterCountCallCloseBraceIndex]->equalsAny([')', ','])) {
            return;
        }

        $this->removeFunctionCall(
            $tokens,
            $defaultNamespaceTokenIndex,
            $countCallIndex,
            $countCallOpenBraceIndex,
            $countCallCloseBraceIndex
        );

        $tokens[$assertCall['index']] = new Token([
            T_STRING,
            false === strpos($assertCall['loweredName'], 'not', 6) ? 'assertCount' : 'assertNotCount',
        ]);
    }

    private function getPreviousAssertCall(Tokens $tokens)
    {
        for ($index = $tokens->count(); $index > 0; --$index) {
            $index = $tokens->getPrevTokenOfKind($index, [[T_STRING]]);
            if (null === $index) {
                return;
            }

            // test if "assert" something call
            $loweredContent = strtolower($tokens[$index]->getContent());
            if ('assert' !== substr($loweredContent, 0, 6)) {
                continue;
            }

            // test candidate for simple calls like: ([\]+'some fixable call'(...))
            $openBraceIndex = $tokens->getNextMeaningfulToken($index);
            if (!$tokens[$openBraceIndex]->equals('(')) {
                continue;
            }

            $operatorIndex = $tokens->getPrevMeaningfulToken($index);
            $referenceIndex = $tokens->getPrevMeaningfulToken($operatorIndex);

            if (
                !($tokens[$operatorIndex]->equals([T_OBJECT_OPERATOR, '->']) && $tokens[$referenceIndex]->equals([T_VARIABLE, '$this']))
                && !($tokens[$operatorIndex]->equals([T_DOUBLE_COLON, '::']) && $tokens[$referenceIndex]->equals([T_STRING, 'self']))
                && !($tokens[$operatorIndex]->equals([T_DOUBLE_COLON, '::']) && $tokens[$referenceIndex]->equals([T_STATIC, 'static']))
            ) {
                continue;
            }

            yield [
                'index' => $index,
                'loweredName' => $loweredContent,
                'openBraceIndex' => $openBraceIndex,
                'closeBraceIndex' => $tokens->findBlockEnd(Tokens::BLOCK_TYPE_PARENTHESIS_BRACE, $openBraceIndex),
            ];
        }
    }

    /**
     * @param Tokens    $tokens
     * @param false|int $callNSIndex
     * @param int       $callIndex
     * @param int       $openIndex
     * @param int       $closeIndex
     */
    private function removeFunctionCall(Tokens $tokens, $callNSIndex, $callIndex, $openIndex, $closeIndex)
    {
        $tokens->clearTokenAndMergeSurroundingWhitespace($callIndex);
        if (false !== $callNSIndex) {
            $tokens->clearTokenAndMergeSurroundingWhitespace($callNSIndex);
        }

        $tokens->clearTokenAndMergeSurroundingWhitespace($openIndex);
        $commaIndex = $tokens->getPrevMeaningfulToken($closeIndex);
        if ($tokens[$commaIndex]->equals(',')) {
            $tokens->removeTrailingWhitespace($commaIndex);
            $tokens->clearAt($commaIndex);
        }

        $tokens->clearTokenAndMergeSurroundingWhitespace($closeIndex);
    }
}


<?php

/*
 * 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\Tokenizer\Transformer;

use PhpCsFixer\Tokenizer\AbstractTransformer;
use PhpCsFixer\Tokenizer\Token;
use PhpCsFixer\Tokenizer\Tokens;

/**
 * Move trailing whitespaces from comments and docs into following T_WHITESPACE token.
 *
 * @author Dariusz Rumiński <dariusz.ruminski@gmail.com>
 *
 * @internal
 */
final class WhitespacyCommentTransformer extends AbstractTransformer
{
    /**
     * {@inheritdoc}
     */
    public function getCustomTokens()
    {
        return [];
    }

    /**
     * {@inheritdoc}
     */
    public function getRequiredPhpVersionId()
    {
        return 50000;
    }

    /**
     * {@inheritdoc}
     */
    public function process(Tokens $tokens, Token $token, $index)
    {
        if (!$token->isComment()) {
            return;
        }

        $content = $token->getContent();
        $trimmedContent = rtrim($content);

        // nothing trimmed, nothing to do
        if ($content === $trimmedContent) {
            return;
        }

        $whitespaces = substr($content, \strlen($trimmedContent));

        $tokens[$index] = new Token([$token->getId(), $trimmedContent]);

        if (isset($tokens[$index + 1]) && $tokens[$index + 1]->isWhitespace()) {
            $tokens[$index + 1] = new Token([T_WHITESPACE, $whitespaces.$tokens[$index + 1]->getContent()]);
        } else {
            $tokens->insertAt($index + 1, new Token([T_WHITESPACE, $whitespaces]));
        }
    }
}


<?php

/*
 * 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\Console;

use PhpCsFixer\Console\Command\DescribeCommand;
use PhpCsFixer\Console\Command\FixCommand;
use PhpCsFixer\Console\Command\HelpCommand;
use PhpCsFixer\Console\Command\ReadmeCommand;
use PhpCsFixer\Console\Command\SelfUpdateCommand;
use PhpCsFixer\Console\SelfUpdate\GithubClient;
use PhpCsFixer\Console\SelfUpdate\NewVersionChecker;
use PhpCsFixer\PharChecker;
use PhpCsFixer\ToolInfo;
use Symfony\Component\Console\Application as BaseApplication;
use Symfony\Component\Console\Command\ListCommand;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\ConsoleOutputInterface;
use Symfony\Component\Console\Output\OutputInterface;

/**
 * @author Fabien Potencier <fabien@symfony.com>
 * @author Dariusz Rumiński <dariusz.ruminski@gmail.com>
 *
 * @internal
 */
final class Application extends BaseApplication
{
    const VERSION = '2.14.4-DEV';
    const VERSION_CODENAME = 'Sunrise';

    /**
     * @var ToolInfo
     */
    private $toolInfo;

    public function __construct()
    {
        if (!getenv('PHP_CS_FIXER_FUTURE_MODE')) {
            error_reporting(-1);
        }

        parent::__construct('PHP CS Fixer', self::VERSION);

        $this->toolInfo = new ToolInfo();

        $this->add(new DescribeCommand());
        $this->add(new FixCommand($this->toolInfo));
        $this->add(new ReadmeCommand());
        $this->add(new SelfUpdateCommand(
            new NewVersionChecker(new GithubClient()),
            $this->toolInfo,
            new PharChecker()
        ));
    }

    /**
     * {@inheritdoc}
     */
    public function doRun(InputInterface $input, OutputInterface $output)
    {
        $stdErr = $output instanceof ConsoleOutputInterface
            ? $output->getErrorOutput()
            : ($input->hasParameterOption('--format', true) && 'txt' !== $input->getParameterOption('--format', null, true) ? null : $output)
        ;
        if (null !== $stdErr) {
            $warningsDetector = new WarningsDetector($this->toolInfo);
            $warningsDetector->detectOldVendor();
            $warningsDetector->detectOldMajor();
            foreach ($warningsDetector->getWarnings() as $warning) {
                $stdErr->writeln(sprintf($stdErr->isDecorated() ? '<bg=yellow;fg=black;>%s</>' : '%s', $warning));
            }
        }

        return parent::doRun($input, $output);
    }

    /**
     * {@inheritdoc}
     */
    public function getLongVersion()
    {
        $version = parent::getLongVersion();
        if (self::VERSION_CODENAME) {
            $version .= ' <info>'.self::VERSION_CODENAME.'</info>';
        }
        $version .= ' by <comment>Fabien Potencier</comment> and <comment>Dariusz Ruminski</comment>';

        $commit = '@git-commit@';

        if ('@'.'git-commit@' !== $commit) {
            $version .= ' ('.substr($commit, 0, 7).')';
        }

        return $version;
    }

    /**
     * {@inheritdoc}
     */
    protected function getDefaultCommands()
    {
        return [new HelpCommand(), new ListCommand()];
    }
}


<?php

/*
 * 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;

use PhpCsFixer\Tokenizer\Analyzer\FunctionsAnalyzer;
use PhpCsFixer\Tokenizer\Tokens;

/**
 * @internal
 *
 * @author Vladimir Reznichenko <kalessil@gmail.com>
 */
abstract class AbstractFunctionReferenceFixer extends AbstractFixer
{
    /**
     * {@inheritdoc}
     */
    public function isRisky()
    {
        return true;
    }

    /**
     * Looks up Tokens sequence for suitable candidates and delivers boundaries information,
     * which can be supplied by other methods in this abstract class.
     *
     * @param string   $functionNameToSearch
     * @param Tokens   $tokens
     * @param int      $start
     * @param null|int $end
     *
     * @return null|int[] returns $functionName, $openParenthesis, $closeParenthesis packed into array
     */
    protected function find($functionNameToSearch, Tokens $tokens, $start = 0, $end = null)
    {
        // make interface consistent with findSequence
        $end = null === $end ? $tokens->count() : $end;

        // find raw sequence which we can analyse for context
        $candidateSequence = [[T_STRING, $functionNameToSearch], '('];
        $matches = $tokens->findSequence($candidateSequence, $start, $end, false);
        if (null === $matches) {
            // not found, simply return without further attempts
            return null;
        }

        // translate results for humans
        list($functionName, $openParenthesis) = array_keys($matches);

        $functionsAnalyzer = new FunctionsAnalyzer();

        if (!$functionsAnalyzer->isGlobalFunctionCall($tokens, $functionName)) {
            return $this->find($functionNameToSearch, $tokens, $openParenthesis, $end);
        }

        return [$functionName, $openParenthesis, $tokens->findBlockEnd(Tokens::BLOCK_TYPE_PARENTHESIS_BRACE, $openParenthesis)];
    }
}


<?php

/*
 * 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\Phpdoc;

use PhpCsFixer\AbstractFixer;
use PhpCsFixer\DocBlock\DocBlock;
use PhpCsFixer\DocBlock\Line;
use PhpCsFixer\Fixer\ConfigurationDefinitionFixerInterface;
use PhpCsFixer\Fixer\WhitespacesAwareFixerInterface;
use PhpCsFixer\FixerConfiguration\FixerConfigurationResolver;
use PhpCsFixer\FixerConfiguration\FixerOptionBuilder;
use PhpCsFixer\FixerDefinition\CodeSample;
use PhpCsFixer\FixerDefinition\FixerDefinition;
use PhpCsFixer\Preg;
use PhpCsFixer\Tokenizer\Analyzer\ArgumentsAnalyzer;
use PhpCsFixer\Tokenizer\Token;
use PhpCsFixer\Tokenizer\Tokens;

/**
 * @author Dariusz Rumiński <dariusz.ruminski@gmail.com>
 */
final class PhpdocAddMissingParamAnnotationFixer extends AbstractFixer implements ConfigurationDefinitionFixerInterface, WhitespacesAwareFixerInterface
{
    /**
     * {@inheritdoc}
     */
    public function getDefinition()
    {
        return new FixerDefinition(
            'PHPDoc should contain `@param` for all params.',
            [
                new CodeSample(
                    '<?php
/**
 * @param int $bar
 *
 * @return void
 */
function f9(string $foo, $bar, $baz) {}
'
                ),
                new CodeSample(
                    '<?php
/**
 * @param int $bar
 *
 * @return void
 */
function f9(string $foo, $bar, $baz) {}
',
                    ['only_untyped' => true]
                ),
                new CodeSample(
                    '<?php
/**
 * @param int $bar
 *
 * @return void
 */
function f9(string $foo, $bar, $baz) {}
',
                    ['only_untyped' => false]
                ),
            ]
        );
    }

    /**
     * {@inheritdoc}
     */
    public function getPriority()
    {
        // must be run after PhpdocNoAliasTagFixer
        // must be run before PhpdocAlignFixer and PhpdocNoEmptyReturnFixer
        return 10;
    }

    /**
     * {@inheritdoc}
     */
    public function isCandidate(Tokens $tokens)
    {
        return $tokens->isTokenKindFound(T_DOC_COMMENT);
    }

    /**
     * {@inheritdoc}
     */
    public function isRisky()
    {
        return false;
    }

    /**
     * {@inheritdoc}
     */
    protected function applyFix(\SplFileInfo $file, Tokens $tokens)
    {
        $argumentsAnalyzer = new ArgumentsAnalyzer();

        for ($index = 0, $limit = $tokens->count(); $index < $limit; ++$index) {
            $mainIndex = $index;
            $token = $tokens[$index];

            if (!$token->isGivenKind(T_DOC_COMMENT)) {
                continue;
            }

            $tokenContent = $token->getContent();

            if (false !== stripos($tokenContent, 'inheritdoc')) {
                continue;
            }

            // ignore one-line phpdocs like `/** foo */`, as there is no place to put new annotations
            if (false === strpos($tokenContent, "\n")) {
                continue;
            }

            $index = $tokens->getNextMeaningfulToken($index);

            if (null === $index) {
                return;
            }

            while ($tokens[$index]->isGivenKind([
                T_ABSTRACT,
                T_FINAL,
                T_PRIVATE,
                T_PROTECTED,
                T_PUBLIC,
                T_STATIC,
                T_VAR,
            ])) {
                $index = $tokens->getNextMeaningfulToken($index);
            }

            if (!$tokens[$index]->isGivenKind(T_FUNCTION)) {
                continue;
            }

            $openIndex = $tokens->getNextTokenOfKind($index, ['(']);
            $index = $tokens->findBlockEnd(Tokens::BLOCK_TYPE_PARENTHESIS_BRACE, $openIndex);

            $arguments = [];

            foreach ($argumentsAnalyzer->getArguments($tokens, $openIndex, $index) as $start => $end) {
                $argumentInfo = $this->prepareArgumentInformation($tokens, $start, $end);

                if (!$this->configuration['only_untyped'] || '' === $argumentInfo['type']) {
                    $arguments[$argumentInfo['name']] = $argumentInfo;
                }
            }

            if (!\count($arguments)) {
                continue;
            }

            $doc = new DocBlock($tokenContent);
            $lastParamLine = null;

            foreach ($doc->getAnnotationsOfType('param') as $annotation) {
                $pregMatched = Preg::match('/^[^$]+(\$\w+).*$/s', $annotation->getContent(), $matches);

                if (1 === $pregMatched) {
                    unset($arguments[$matches[1]]);
                }

                $lastParamLine = max($lastParamLine, $annotation->getEnd());
            }

            if (!\count($arguments)) {
                continue;
            }

            $lines = $doc->getLines();
            $linesCount = \count($lines);

            Preg::match('/^(\s*).*$/', $lines[$linesCount - 1]->getContent(), $matches);
            $indent = $matches[1];

            $newLines = [];

            foreach ($arguments as $argument) {
                $type = $argument['type'] ?: 'mixed';

                if ('?' !== $type[0] && 'null' === strtolower($argument['default'])) {
                    $type = 'null|'.$type;
                }

                $newLines[] = new Line(sprintf(
                    '%s* @param %s %s%s',
                    $indent,
                    $type,
                    $argument['name'],
                    $this->whitespacesConfig->getLineEnding()
                ));
            }

            array_splice(
                $lines,
                $lastParamLine ? $lastParamLine + 1 : $linesCount - 1,
                0,
                $newLines
            );

            $tokens[$mainIndex] = new Token([T_DOC_COMMENT, implode('', $lines)]);
        }
    }

    /**
     * {@inheritdoc}
     */
    protected function createConfigurationDefinition()
    {
        return new FixerConfigurationResolver([
            (new FixerOptionBuilder('only_untyped', 'Whether to add missing `@param` annotations for untyped parameters only.'))
                ->setDefault(true)
                ->setAllowedTypes(['bool'])
                ->getOption(),
        ]);
    }

    /**
     * @param Tokens $tokens
     * @param int    $start
     * @param int    $end
     *
     * @return array
     */
    private function prepareArgumentInformation(Tokens $tokens, $start, $end)
    {
        $info = [
            'default' => '',
            'name' => '',
            'type' => '',
        ];

        $sawName = false;

        for ($index = $start; $index <= $end; ++$index) {
            $token = $tokens[$index];

            if ($token->isComment() || $token->isWhitespace()) {
                continue;
            }

            if ($token->isGivenKind(T_VARIABLE)) {
                $sawName = true;
                $info['name'] = $token->getContent();

                continue;
            }

            if ($token->equals('=')) {
                continue;
            }

            if ($sawName) {
                $info['default'] .= $token->getContent();
            } elseif ('&' !== $token->getContent()) {
                if ($token->isGivenKind(T_ELLIPSIS)) {
                    if ('' === $info['type']) {
                        $info['type'] = 'array';
                    } else {
                        $info['type'] .= '[]';
                    }
                } else {
                    $info['type'] .= $token->getContent();
                }
            }
        }

        return $info;
    }
}


<?php

/*
 * 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\ClassUsage;

use PhpCsFixer\AbstractFixer;
use PhpCsFixer\FixerDefinition\CodeSample;
use PhpCsFixer\FixerDefinition\FixerDefinition;
use PhpCsFixer\Tokenizer\Token;
use PhpCsFixer\Tokenizer\Tokens;

/**
 * @author Kuba Werłos <werlos@gmail.com>
 */
final class DateTimeImmutableFixer extends AbstractFixer
{
    /**
     * {@inheritdoc}
     */
    public function getDefinition()
    {
        return new FixerDefinition(
            'Class `DateTimeImmutable` should be used instead of `DateTime`.',
            [new CodeSample("<?php\nnew DateTime();\n")],
            null,
            'Risky when the code relies on modifying `DateTime` objects or if any of the `date_create*` functions are overridden.'
        );
    }

    /**
     * {@inheritdoc}
     */
    public function isCandidate(Tokens $tokens)
    {
        return $tokens->isTokenKindFound(T_STRING);
    }

    /**
     * {@inheritdoc}
     */
    public function isRisky()
    {
        return true;
    }

    /**
     * {@inheritdoc}
     */
    protected function applyFix(\SplFileInfo $file, Tokens $tokens)
    {
        $isInNamespace = false;
        $isImported = false; // e.g. use DateTime;

        for ($index = 0, $limit = $tokens->count(); $index < $limit; ++$index) {
            $token = $tokens[$index];

            if ($token->isGivenKind(T_NAMESPACE)) {
                $isInNamespace = true;

                continue;
            }

            if ($token->isGivenKind(T_USE) && $isInNamespace) {
                $nextIndex = $tokens->getNextMeaningfulToken($index);
                if ('datetime' !== strtolower($tokens[$nextIndex]->getContent())) {
                    continue;
                }
                $nextNextIndex = $tokens->getNextMeaningfulToken($nextIndex);
                if ($tokens[$nextNextIndex]->equals(';')) {
                    $isImported = true;
                }

                $index = $nextNextIndex;

                continue;
            }

            if (!$token->isGivenKind(T_STRING)) {
                continue;
            }

            $lowercaseContent = strtolower($token->getContent());

            if ('datetime' === $lowercaseContent) {
                $this->fixClassUsage($tokens, $index, $isInNamespace, $isImported);
                $limit = $tokens->count(); // update limit, as fixing class usage may insert new token
            } elseif ('date_create' === $lowercaseContent) {
                $this->fixFunctionUsage($tokens, $index, 'date_create_immutable');
            } elseif ('date_create_from_format' === $lowercaseContent) {
                $this->fixFunctionUsage($tokens, $index, 'date_create_immutable_from_format');
            }
        }
    }

    /**
     * @param Tokens $tokens
     * @param int    $index
     * @param bool   $isInNamespace
     * @param bool   $isImported
     */
    private function fixClassUsage(Tokens $tokens, $index, $isInNamespace, $isImported)
    {
        $nextIndex = $tokens->getNextMeaningfulToken($index);
        if ($tokens[$nextIndex]->isGivenKind(T_DOUBLE_COLON)) {
            $nextNextIndex = $tokens->getNextMeaningfulToken($nextIndex);
            if ($tokens[$nextNextIndex]->isGivenKind(T_STRING)) {
                $nextNextNextIndex = $tokens->getNextMeaningfulToken($nextNextIndex);
                if (!$tokens[$nextNextNextIndex]->equals('(')) {
                    return;
                }
            }
        }

        $isUsedAlone = false; // e.g. new DateTime();
        $isUsedWithLeadingBackslash = false; // e.g. new \DateTime();

        $prevIndex = $tokens->getPrevMeaningfulToken($index);
        if ($tokens[$prevIndex]->isGivenKind(T_NS_SEPARATOR)) {
            $prevPrevIndex = $tokens->getPrevMeaningfulToken($prevIndex);
            if (!$tokens[$prevPrevIndex]->isGivenKind(T_STRING)) {
                $isUsedWithLeadingBackslash = true;
            }
        } elseif (!$tokens[$prevIndex]->isGivenKind([T_DOUBLE_COLON, T_OBJECT_OPERATOR])) {
            $isUsedAlone = true;
        }

        if ($isUsedWithLeadingBackslash || $isUsedAlone && ($isInNamespace && $isImported || !$isInNamespace)) {
            $tokens[$index] = new Token([T_STRING, \DateTimeImmutable::class]);
            if ($isInNamespace && $isUsedAlone) {
                $tokens->insertAt($index, new Token([T_NS_SEPARATOR, '\\']));
            }
        }
    }

    /**
     * @param Tokens $tokens
     * @param int    $index
     * @param string $replacement
     */
    private function fixFunctionUsage(Tokens $tokens, $index, $replacement)
    {
        $prevIndex = $tokens->getPrevMeaningfulToken($index);
        if ($tokens[$prevIndex]->isGivenKind([T_DOUBLE_COLON, T_NEW, T_OBJECT_OPERATOR])) {
            return;
        }
        if ($tokens[$prevIndex]->isGivenKind(T_NS_SEPARATOR)) {
            $prevPrevIndex = $tokens->getPrevMeaningfulToken($prevIndex);
            if ($tokens[$prevPrevIndex]->isGivenKind([T_NEW, T_STRING])) {
                return;
            }
        }

        $tokens[$index] = new Token([T_STRING, $replacement]);
    }
}