diff --git a/README.rst b/README.rst index 6accd80ef8d..f9894b29e12 100644 --- a/README.rst +++ b/README.rst @@ -431,6 +431,10 @@ Choose from the list of available rules: *Risky rule: forcing strict types will stop non strict code from working.* +* **declared_class_casing** [@Symfony, @PhpCsFixer] + + Classes should be referred to using the correct casing. + * **dir_constant** [@Symfony:risky, @PhpCsFixer:risky] Replaces ``dirname(__FILE__)`` expression with equivalent ``__DIR__`` diff --git a/src/Fixer/Casing/DeclaredClassCasingFixer.php b/src/Fixer/Casing/DeclaredClassCasingFixer.php new file mode 100644 index 00000000000..0714b6d118b --- /dev/null +++ b/src/Fixer/Casing/DeclaredClassCasingFixer.php @@ -0,0 +1,116 @@ + + * Dariusz Rumiński + * + * 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 siad007 + */ +final class DeclaredClassCasingFixer extends AbstractFixer +{ + /** + * @var string[] + */ + private static $declaredClassNames; + + /** + * {@inheritdoc} + */ + public function getDefinition() + { + return new FixerDefinition( + 'Classes should be referred to using the correct casing.', + [new CodeSample("isTokenKindFound(T_STRING); + } + + /** + * @param \SplFileInfo $file + * @param Tokens $tokens + */ + protected function applyFix(\SplFileInfo $file, Tokens $tokens) + { + if (null === self::$declaredClassNames) { + foreach (get_declared_classes() as $class) { + self::$declaredClassNames[strtolower($class)] = $class; + } + } + + foreach ($tokens as $index => $token) { + $name = $this->declaredClass($index, $token, $tokens); + + if (null !== $name) { + $tokens[$index] = new Token([T_STRING, self::$declaredClassNames[$name]]); + } + } + } + + /** + * Get the lower case name of the declared class or null. + * + * @param int $index + * @param Token $token + * @param Tokens $tokens + * + * @return null|string + */ + private function declaredClass($index, Token $token, Tokens $tokens) + { + $lower = strtolower($token->getContent()); + + if (!\array_key_exists($lower, self::$declaredClassNames)) { + return null; + } + + $notBeforeTypes = [ + T_CLASS, + T_AS, + T_DOUBLE_COLON, + T_OBJECT_OPERATOR, + T_FUNCTION, + T_CONST, + T_TRAIT, + T_USE, + CT::T_USE_TRAIT, + ]; + + $beforeClassName = $tokens->getPrevMeaningfulToken($index); + + if ($tokens[$beforeClassName]->isGivenKind($notBeforeTypes)) { + return null; + } + + if ( + $tokens[$beforeClassName]->isGivenKind(T_NS_SEPARATOR) + && $tokens[$tokens->getPrevMeaningfulToken($beforeClassName)]->isGivenKind([T_STRING]) + ) { + return null; + } + + return $lower; + } +} diff --git a/src/RuleSet.php b/src/RuleSet.php index 26b2100e2ea..ac44a80df00 100644 --- a/src/RuleSet.php +++ b/src/RuleSet.php @@ -70,6 +70,7 @@ final class RuleSet implements RuleSetInterface 'class_definition' => ['single_line' => true], 'concat_space' => ['spacing' => 'none'], 'declare_equal_normalize' => true, + 'declared_class_casing' => true, 'function_typehint_space' => true, 'include' => true, 'increment_style' => true, diff --git a/tests/Fixer/Casing/DeclaredClassCasingFixerTest.php b/tests/Fixer/Casing/DeclaredClassCasingFixerTest.php new file mode 100644 index 00000000000..8a59785c422 --- /dev/null +++ b/tests/Fixer/Casing/DeclaredClassCasingFixerTest.php @@ -0,0 +1,139 @@ + + * Dariusz Rumiński + * + * This source file is subject to the MIT license that is bundled + * with this source code in the file LICENSE. + */ + +namespace PhpCsFixer\Tests\Fixer\Casing; + +use PhpCsFixer\Console\Application; +use PhpCsFixer\Tests\Test\AbstractFixerTestCase; + +/** + * @author siad007 + * + * @internal + * + * @covers \PhpCsFixer\Fixer\Casing\DeclaredClassCasingFixer + */ +final class DeclaredClassCasingFixerTest extends AbstractFixerTestCase +{ + /** + * @param string $expected + * @param null|string $input + * + * @dataProvider provideFixCases + */ + public function testFix($expected, $input = null) + { + $this->doTest($expected, $input); + } + + public function provideFixCases() + { + return [ + [ + 'exception(); + ', + ], + [ + ' [ + sprintf('