Repository: PHP-CS-Fixer/PHP-CS-Fixer (PHP)

Issue:
Regex in general_phpdoc_annotation_remove
## Support question

We want to forbid an annotation `@group`.
We want to use the PHP-CS-Fixer `general_phpdoc_annotation_remove` rule for this.

```php
'general_phpdoc_annotation_remove' => [
    'annotations' => [
        'group',
    ],
],
```

```php
/**
 * @group example
 * @GROup example
/*
function example() { ... }
```

The first annotation get removed, the second not. The rule is not case insensitive. 

Can I, how can I use regex for this rule to catch all annotations?


Discussion:
I think it would require a new config for the fixer that is saying if the casing should be ignored.
would you like to take a look at https://github.com/FriendsOfPHP/PHP-CS-Fixer/pull/6660 ?
@keradus oh man, awesome :heart: 
Thank you very much.
glad to help! observe the new release in upcoming days and enjoy using this new feature!



Patch:
diff --git a/doc/list.rst b/doc/list.rst
index 16b29e8103d..f82ee28e4a2 100644
--- a/doc/list.rst
+++ b/doc/list.rst
@@ -848,6 +848,10 @@ List of Available Rules
      | List of annotations to remove, e.g. `["author"]`.
      | Allowed types: ``array``
      | Default value: ``[]``
+   - | ``case_sensitive``
+     | Should annotations be case sensitive.
+     | Allowed types: ``bool``
+     | Default value: ``true``
 
 
    `Source PhpCsFixer\\Fixer\\Phpdoc\\GeneralPhpdocAnnotationRemoveFixer <./../src/Fixer/Phpdoc/GeneralPhpdocAnnotationRemoveFixer.php>`_
diff --git a/doc/rules/phpdoc/general_phpdoc_annotation_remove.rst b/doc/rules/phpdoc/general_phpdoc_annotation_remove.rst
index 38c26974cf7..d99c7b3788a 100644
--- a/doc/rules/phpdoc/general_phpdoc_annotation_remove.rst
+++ b/doc/rules/phpdoc/general_phpdoc_annotation_remove.rst
@@ -16,6 +16,15 @@ Allowed types: ``array``
 
 Default value: ``[]``
 
+``case_sensitive``
+~~~~~~~~~~~~~~~~~~
+
+Should annotations be case sensitive.
+
+Allowed types: ``bool``
+
+Default value: ``true``
+
 Examples
 --------
 
@@ -32,12 +41,30 @@ With configuration: ``['annotations' => ['author']]``.
     /**
      * @internal
    - * @author John Doe
+     * @AuThOr Jane Doe
      */
     function foo() {}
 
 Example #2
 ~~~~~~~~~~
 
+With configuration: ``['annotations' => ['author'], 'case_sensitive' => false]``.
+
+.. code-block:: diff
+
+   --- Original
+   +++ New
+    <?php
+    /**
+     * @internal
+   - * @author John Doe
+   - * @AuThOr Jane Doe
+     */
+    function foo() {}
+
+Example #3
+~~~~~~~~~~
+
 With configuration: ``['annotations' => ['package', 'subpackage']]``.
 
 .. code-block:: diff
diff --git a/src/DocBlock/DocBlock.php b/src/DocBlock/DocBlock.php
index e1a25ff09d6..5a0b2f40a19 100644
--- a/src/DocBlock/DocBlock.php
+++ b/src/DocBlock/DocBlock.php
@@ -185,15 +185,14 @@ public function getAnnotation(int $pos): ?Annotation
      */
     public function getAnnotationsOfType($types): array
     {
+        $typesToSearchFor = (array) $types;
+
         $annotations = [];
-        $types = (array) $types;
 
         foreach ($this->getAnnotations() as $annotation) {
-            $tag = $annotation->getTag()->getName();
-            foreach ($types as $type) {
-                if ($type === $tag) {
-                    $annotations[] = $annotation;
-                }
+            $tagName = $annotation->getTag()->getName();
+            if (\in_array($tagName, $typesToSearchFor, true)) {
+                $annotations[] = $annotation;
             }
         }
 
diff --git a/src/Fixer/Phpdoc/GeneralPhpdocAnnotationRemoveFixer.php b/src/Fixer/Phpdoc/GeneralPhpdocAnnotationRemoveFixer.php
index 8920d5626d6..f8b7211aea3 100644
--- a/src/Fixer/Phpdoc/GeneralPhpdocAnnotationRemoveFixer.php
+++ b/src/Fixer/Phpdoc/GeneralPhpdocAnnotationRemoveFixer.php
@@ -15,6 +15,7 @@
 namespace PhpCsFixer\Fixer\Phpdoc;
 
 use PhpCsFixer\AbstractFixer;
+use PhpCsFixer\DocBlock\Annotation;
 use PhpCsFixer\DocBlock\DocBlock;
 use PhpCsFixer\Fixer\ConfigurableFixerInterface;
 use PhpCsFixer\FixerConfiguration\FixerConfigurationResolver;
@@ -45,6 +46,7 @@ public function getDefinition(): FixerDefinitionInterface
 /**
  * @internal
  * @author John Doe
+ * @AuThOr Jane Doe
  */
 function foo() {}
 ',
@@ -52,6 +54,17 @@ function foo() {}
                 ),
                 new CodeSample(
                     '<?php
+/**
+ * @internal
+ * @author John Doe
+ * @AuThOr Jane Doe
+ */
+function foo() {}
+',
+                    ['annotations' => ['author'], 'case_sensitive' => false]
+                ),
+                new CodeSample(
+                    '<?php
 /**
  * @author John Doe
  * @package ACME API
@@ -100,7 +113,7 @@ protected function applyFix(\SplFileInfo $file, Tokens $tokens): void
             }
 
             $doc = new DocBlock($token->getContent());
-            $annotations = $doc->getAnnotationsOfType($this->configuration['annotations']);
+            $annotations = $this->getAnnotationsToRemove($doc);
 
             // nothing to do if there are no annotations
             if (0 === \count($annotations)) {
@@ -129,6 +142,35 @@ protected function createConfigurationDefinition(): FixerConfigurationResolverIn
                 ->setAllowedTypes(['array'])
                 ->setDefault([])
                 ->getOption(),
+            (new FixerOptionBuilder('case_sensitive', 'Should annotations be case sensitive.'))
+                ->setAllowedTypes(['bool'])
+                ->setDefault(true)
+                ->getOption(),
         ]);
     }
+
+    /**
+     * @return list<Annotation>
+     */
+    private function getAnnotationsToRemove(DocBlock $doc): array
+    {
+        if (true === $this->configuration['case_sensitive']) {
+            return $doc->getAnnotationsOfType($this->configuration['annotations']);
+        }
+
+        $typesToSearchFor = array_map(function (string $type): string {
+            return strtolower($type);
+        }, $this->configuration['annotations']);
+
+        $annotations = [];
+
+        foreach ($doc->getAnnotations() as $annotation) {
+            $tagName = strtolower($annotation->getTag()->getName());
+            if (\in_array($tagName, $typesToSearchFor, true)) {
+                $annotations[] = $annotation;
+            }
+        }
+
+        return $annotations;
+    }
 }


Test patch:
diff --git a/tests/Fixer/Phpdoc/GeneralPhpdocAnnotationRemoveFixerTest.php b/tests/Fixer/Phpdoc/GeneralPhpdocAnnotationRemoveFixerTest.php
index ca2b647dcd2..390307d477b 100644
--- a/tests/Fixer/Phpdoc/GeneralPhpdocAnnotationRemoveFixerTest.php
+++ b/tests/Fixer/Phpdoc/GeneralPhpdocAnnotationRemoveFixerTest.php
@@ -181,6 +181,38 @@ function hello($name)
 ',
                 ['annotations' => ['noinspection']],
             ],
+
+            [
+                '<?php
+/**
+* @internal
+* @AuThOr Jane Doe
+*/
+function foo() {}',
+                '<?php
+/**
+* @internal
+* @author John Doe
+* @AuThOr Jane Doe
+*/
+function foo() {}',
+                ['annotations' => ['author'], 'case_sensitive' => true],
+            ],
+            [
+                '<?php
+/**
+* @internal
+*/
+function foo() {}',
+                '<?php
+/**
+* @internal
+* @author John Doe
+* @AuThOr Jane Doe
+*/
+function foo() {}',
+                ['annotations' => ['author'], 'case_sensitive' => false],
+            ],
         ];
     }
 }