SwitchAnalyzer.php 3.43 KB
Newer Older
冯超鹏's avatar
冯超鹏 committed
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123
<?php

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

use PhpCsFixer\Tokenizer\Analyzer\Analysis\CaseAnalysis;
use PhpCsFixer\Tokenizer\Analyzer\Analysis\SwitchAnalysis;
use PhpCsFixer\Tokenizer\Tokens;

/**
 * @author Kuba Werłos <werlos@gmail.com>
 *
 * @internal
 */
final class SwitchAnalyzer
{
    /**
     * @param int $switchIndex
     *
     * @return SwitchAnalysis
     */
    public function getSwitchAnalysis(Tokens $tokens, $switchIndex)
    {
        if (!$tokens[$switchIndex]->isGivenKind(T_SWITCH)) {
            throw new \InvalidArgumentException(sprintf('Index %d is not "switch".', $switchIndex));
        }

        $casesStartIndex = $this->getCasesStart($tokens, $switchIndex);
        $casesEndIndex = $this->getCasesEnd($tokens, $casesStartIndex);

        $cases = [];
        $ternaryOperatorDepth = 0;
        $index = $casesStartIndex;
        while ($index < $casesEndIndex) {
            ++$index;
            if ($tokens[$index]->isGivenKind(T_SWITCH)) {
                $index = (new self())->getSwitchAnalysis($tokens, $index)->getCasesEnd();

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

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

                continue;
            }
            $cases[] = new CaseAnalysis($index);
        }

        return new SwitchAnalysis($casesStartIndex, $casesEndIndex, $cases);
    }

    /**
     * @param int $switchIndex
     *
     * @return int
     */
    private function getCasesStart(Tokens $tokens, $switchIndex)
    {
        /** @var int $parenthesisStartIndex */
        $parenthesisStartIndex = $tokens->getNextMeaningfulToken($switchIndex);
        $parenthesisEndIndex = $tokens->findBlockEnd(Tokens::BLOCK_TYPE_PARENTHESIS_BRACE, $parenthesisStartIndex);

        $casesStartIndex = $tokens->getNextMeaningfulToken($parenthesisEndIndex);
        \assert(\is_int($casesStartIndex));

        return $casesStartIndex;
    }

    /**
     * @param int $casesStartIndex
     *
     * @return int
     */
    private function getCasesEnd(Tokens $tokens, $casesStartIndex)
    {
        if ($tokens[$casesStartIndex]->equals('{')) {
            return $tokens->findBlockEnd(Tokens::BLOCK_TYPE_CURLY_BRACE, $casesStartIndex);
        }

        $depth = 1;
        $index = $casesStartIndex;
        while ($depth > 0) {
            /** @var int $index */
            $index = $tokens->getNextMeaningfulToken($index);

            if ($tokens[$index]->isGivenKind(T_ENDSWITCH)) {
                --$depth;

                continue;
            }

            if (!$tokens[$index]->isGivenKind(T_SWITCH)) {
                continue;
            }
            $index = $this->getCasesStart($tokens, $index);
            if ($tokens[$index]->equals(':')) {
                ++$depth;
            }
        }

        /** @var int $afterEndswitchIndex */
        $afterEndswitchIndex = $tokens->getNextMeaningfulToken($index);

        return $tokens[$afterEndswitchIndex]->equals(';') ? $afterEndswitchIndex : $index;
    }
}