Просмотр исходного кода

ClassReferenceNameCasingFixer - introduction

SpacePossum 3 лет назад
Родитель
Сommit
776850a6fa

+ 7 - 0
doc/list.rst

@@ -208,6 +208,13 @@ List of Available Rules
    *warning deprecated*
 
    `Source PhpCsFixer\\Fixer\\LanguageConstruct\\ClassKeywordRemoveFixer <./../src/Fixer/LanguageConstruct/ClassKeywordRemoveFixer.php>`_
+-  `class_reference_name_casing <./rules/casing/class_reference_name_casing.rst>`_
+
+   When referencing a class it must be written using the correct casing.
+
+   Part of rule sets `@PhpCsFixer <./ruleSets/PhpCsFixer.rst>`_ `@Symfony <./ruleSets/Symfony.rst>`_
+
+   `Source PhpCsFixer\\Fixer\\Casing\\ClassReferenceNameCasingFixer <./../src/Fixer/Casing/ClassReferenceNameCasingFixer.php>`_
 -  `clean_namespace <./rules/namespace_notation/clean_namespace.rst>`_
 
    Namespace must not contain spacing, comments or PHPDoc.

+ 1 - 0
doc/ruleSets/Symfony.rst

@@ -24,6 +24,7 @@ Rules
 - `class_definition <./../rules/class_notation/class_definition.rst>`_
   config:
   ``['single_line' => true]``
+- `class_reference_name_casing <./../rules/casing/class_reference_name_casing.rst>`_
 - `clean_namespace <./../rules/namespace_notation/clean_namespace.rst>`_
 - `concat_space <./../rules/operator/concat_space.rst>`_
 - `echo_tag_syntax <./../rules/php_tag/echo_tag_syntax.rst>`_

+ 30 - 0
doc/rules/casing/class_reference_name_casing.rst

@@ -0,0 +1,30 @@
+====================================
+Rule ``class_reference_name_casing``
+====================================
+
+When referencing a class it must be written using the correct casing.
+
+Examples
+--------
+
+Example #1
+~~~~~~~~~~
+
+.. code-block:: diff
+
+   --- Original
+   +++ New
+    <?php
+   -throw new \exception();
+   +throw new \Exception();
+
+Rule sets
+---------
+
+The rule is part of the following rule sets:
+
+@PhpCsFixer
+  Using the `@PhpCsFixer <./../../ruleSets/PhpCsFixer.rst>`_ rule set will enable the ``class_reference_name_casing`` rule.
+
+@Symfony
+  Using the `@Symfony <./../../ruleSets/Symfony.rst>`_ rule set will enable the ``class_reference_name_casing`` rule.

+ 3 - 0
doc/rules/index.rst

@@ -86,6 +86,9 @@ Basic
 Casing
 ------
 
+- `class_reference_name_casing <./casing/class_reference_name_casing.rst>`_
+
+  When referencing a class it must be written using the correct casing.
 - `constant_case <./casing/constant_case.rst>`_
 
   The PHP constants ``true``, ``false``, and ``null`` MUST be written using the correct casing.

+ 1 - 1
src/DocBlock/TypeExpression.php

@@ -449,7 +449,7 @@ final class TypeExpression
             }
         }
 
-        if (null === $this->namespace || '' === $this->namespace->getShortName()) {
+        if (null === $this->namespace || $this->namespace->isGlobalNamespace()) {
             return $type;
         }
 

+ 147 - 0
src/Fixer/Casing/ClassReferenceNameCasingFixer.php

@@ -0,0 +1,147 @@
+<?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\Casing;
+
+use PhpCsFixer\AbstractFixer;
+use PhpCsFixer\FixerDefinition\CodeSample;
+use PhpCsFixer\FixerDefinition\FixerDefinition;
+use PhpCsFixer\FixerDefinition\FixerDefinitionInterface;
+use PhpCsFixer\Tokenizer\Analyzer\Analysis\NamespaceAnalysis;
+use PhpCsFixer\Tokenizer\Analyzer\NamespacesAnalyzer;
+use PhpCsFixer\Tokenizer\CT;
+use PhpCsFixer\Tokenizer\Token;
+use PhpCsFixer\Tokenizer\Tokens;
+
+final class ClassReferenceNameCasingFixer extends AbstractFixer
+{
+    /**
+     * {@inheritdoc}
+     */
+    public function getDefinition(): FixerDefinitionInterface
+    {
+        return new FixerDefinition(
+            'When referencing a class it must be written using the correct casing.',
+            [
+                new CodeSample("<?php\nthrow new \\exception();\n"),
+            ]
+        );
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function isCandidate(Tokens $tokens): bool
+    {
+        return $tokens->isTokenKindFound(T_STRING);
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    protected function applyFix(\SplFileInfo $file, Tokens $tokens): void
+    {
+        $namespacesAnalyzer = new NamespacesAnalyzer();
+        $classNames = $this->getClassNames();
+
+        foreach ($namespacesAnalyzer->getDeclarations($tokens) as $namespace) {
+            foreach ($this->getClassReference($tokens, $namespace) as $reference) {
+                $currentContent = $tokens[$reference]->getContent();
+                $lowerCurrentContent = strtolower($currentContent);
+
+                if (isset($classNames[$lowerCurrentContent]) && $currentContent !== $classNames[$lowerCurrentContent]) {
+                    $tokens[$reference] = new Token([T_STRING, $classNames[$lowerCurrentContent]]);
+                }
+            }
+        }
+    }
+
+    private function getClassReference(Tokens $tokens, NamespaceAnalysis $namespace): \Generator
+    {
+        static $notBeforeKinds;
+
+        if (null === $notBeforeKinds) {
+            $notBeforeKinds = [
+                CT::T_USE_TRAIT,
+                T_AS,
+                T_CASE, // PHP 8.1 trait enum-case
+                T_CLASS,
+                T_CONST,
+                T_DOUBLE_COLON,
+                T_FUNCTION,
+                T_INTERFACE,
+                T_OBJECT_OPERATOR,
+                T_TRAIT,
+            ];
+
+            if (\defined('T_ENUM')) { // @TODO: drop condition when PHP 8.1+ is required
+                $notBeforeKinds[] = T_ENUM;
+            }
+        }
+
+        $namespaceIsGlobal = $namespace->isGlobalNamespace();
+
+        for ($index = $namespace->getScopeStartIndex(); $index < $namespace->getScopeEndIndex(); ++$index) {
+            if (!$tokens[$index]->isGivenKind(T_STRING)) {
+                continue;
+            }
+
+            $nextIndex = $tokens->getNextMeaningfulToken($index);
+
+            if ($tokens[$nextIndex]->isGivenKind(T_NS_SEPARATOR)) {
+                continue;
+            }
+
+            $prevIndex = $tokens->getPrevMeaningfulToken($index);
+            $isNamespaceSeparator = $tokens[$prevIndex]->isGivenKind(T_NS_SEPARATOR);
+
+            if (!$isNamespaceSeparator && !$namespaceIsGlobal) {
+                continue;
+            }
+
+            if ($isNamespaceSeparator) {
+                $prevIndex = $tokens->getPrevMeaningfulToken($prevIndex);
+
+                if ($tokens[$prevIndex]->isGivenKind(T_STRING)) {
+                    continue;
+                }
+            } elseif ($tokens[$prevIndex]->isGivenKind($notBeforeKinds)) {
+                continue;
+            }
+
+            if (!$tokens[$prevIndex]->isGivenKind([T_NEW]) && $tokens[$nextIndex]->equals('(')) {
+                continue;
+            }
+
+            yield $index;
+        }
+    }
+
+    private function getClassNames(): array
+    {
+        static $classes = null;
+
+        if (null === $classes) {
+            $classes = [];
+
+            foreach (get_declared_classes() as $class) {
+                if ((new \ReflectionClass($class))->isInternal()) {
+                    $classes[strtolower($class)] = $class;
+                }
+            }
+        }
+
+        return $classes;
+    }
+}

+ 1 - 1
src/Fixer/ConstantNotation/NativeConstantInvocationFixer.php

@@ -188,7 +188,7 @@ namespace {
         // 'scope' is 'namespaced' here
         /** @var NamespaceAnalysis $namespace */
         foreach (array_reverse($namespaces) as $namespace) {
-            if ('' === $namespace->getFullName()) {
+            if ($namespace->isGlobalNamespace()) {
                 continue;
             }
 

+ 1 - 1
src/Fixer/FunctionNotation/NativeFunctionInvocationFixer.php

@@ -206,7 +206,7 @@ $c = get_class($d);
         // 'scope' is 'namespaced' here
         /** @var NamespaceAnalysis $namespace */
         foreach (array_reverse($namespaces) as $namespace) {
-            $this->fixFunctionCalls($tokens, $this->functionFilter, $namespace->getScopeStartIndex(), $namespace->getScopeEndIndex(), '' === $namespace->getFullName());
+            $this->fixFunctionCalls($tokens, $this->functionFilter, $namespace->getScopeStartIndex(), $namespace->getScopeEndIndex(), $namespace->isGlobalNamespace());
         }
     }
 

+ 1 - 1
src/Fixer/Import/GlobalNamespaceImportFixer.php

@@ -120,7 +120,7 @@ if (count($x)) {
     {
         $namespaceAnalyses = (new NamespacesAnalyzer())->getDeclarations($tokens);
 
-        if (1 !== \count($namespaceAnalyses) || '' === $namespaceAnalyses[0]->getFullName()) {
+        if (1 !== \count($namespaceAnalyses) || $namespaceAnalyses[0]->isGlobalNamespace()) {
             return;
         }
 

+ 1 - 0
src/RuleSet/Sets/SymfonySet.php

@@ -46,6 +46,7 @@ final class SymfonySet extends AbstractRuleSetDescription
             'class_definition' => [
                 'single_line' => true,
             ],
+            'class_reference_name_casing' => true,
             'clean_namespace' => true,
             'concat_space' => true,
             'echo_tag_syntax' => true,

Некоторые файлы не были показаны из-за большого количества измененных файлов