diff --git a/src/main/kotlin/com/funivan/idea/phpClean/inspections/classNameCollision/ClassNameCollisionInspection.html b/src/main/kotlin/com/funivan/idea/phpClean/inspections/classNameCollision/ClassNameCollisionInspection.html index bf14027..90b6259 100644 --- a/src/main/kotlin/com/funivan/idea/phpClean/inspections/classNameCollision/ClassNameCollisionInspection.html +++ b/src/main/kotlin/com/funivan/idea/phpClean/inspections/classNameCollision/ClassNameCollisionInspection.html @@ -1,5 +1,6 @@ Classes with same name in different namespaces can be confused. (Disabled by default) +

Option: Ignore vendor classes. When enabled, class name collisions with classes defined in the project's vendor directory are ignored.

 namespace App {
   class User {}; // <- Class name collision with \Cli\User
diff --git a/src/main/kotlin/com/funivan/idea/phpClean/inspections/classNameCollision/ClassNameCollisionInspection.kt b/src/main/kotlin/com/funivan/idea/phpClean/inspections/classNameCollision/ClassNameCollisionInspection.kt
index 6ab4793..cb0454a 100644
--- a/src/main/kotlin/com/funivan/idea/phpClean/inspections/classNameCollision/ClassNameCollisionInspection.kt
+++ b/src/main/kotlin/com/funivan/idea/phpClean/inspections/classNameCollision/ClassNameCollisionInspection.kt
@@ -2,19 +2,42 @@ package com.funivan.idea.phpClean.inspections.classNameCollision
 
 import com.funivan.idea.phpClean.spl.PhpCleanInspection
 import com.intellij.codeInspection.ProblemsHolder
+import com.intellij.codeInspection.ui.SingleCheckboxOptionsPanel
+import com.intellij.openapi.project.Project
+import com.intellij.openapi.vfs.LocalFileSystem
+import com.intellij.openapi.vfs.VfsUtilCore
+import com.intellij.openapi.vfs.VirtualFile
 import com.intellij.psi.PsiElement
 import com.intellij.psi.PsiElementVisitor
 import com.jetbrains.php.PhpIndex
 import com.jetbrains.php.lang.psi.elements.PhpClass
 import com.jetbrains.php.lang.psi.visitors.PhpElementVisitor
+import javax.swing.JComponent
 
 class ClassNameCollisionInspection : PhpCleanInspection() {
+    companion object {
+        private const val VENDOR_DIR_NAME = "vendor"
+    }
+
+    @JvmField
+    var ignoreVendorClasses = false
+
     override fun getShortName() = "ClassNameCollisionInspection"
+
+    override fun createOptionsPanel(): JComponent {
+        return SingleCheckboxOptionsPanel(
+            "Ignore vendor classes",
+            this,
+            "ignoreVendorClasses"
+        )
+    }
+
     override fun buildVisitor(holder: ProblemsHolder, isOnTheFly: Boolean): PsiElementVisitor {
+        val vendorDir = if (ignoreVendorClasses) findVendorDir(holder.project) else null
         return object : PhpElementVisitor() {
             override fun visitPhpClass(phpClass: PhpClass) {
                 phpClass.nameIdentifier?.let { name ->
-                    find(phpClass, name)?.let { clazz ->
+                    find(phpClass, name, vendorDir)?.let { clazz ->
                         holder.registerProblem(
                             name,
                             "Class name collision with ${clazz.fqn}"
@@ -27,8 +50,32 @@ class ClassNameCollisionInspection : PhpCleanInspection() {
 
     private fun find(
         origin: PhpClass,
-        name: PsiElement
-    ) = PhpIndex.getInstance(origin.project)
-        .getClassesByName(name.text)
-        .firstOrNull { it.fqn != origin.fqn }
+        name: PsiElement,
+        vendorDir: VirtualFile?
+    ): PhpClass? {
+        val collisions = PhpIndex.getInstance(origin.project)
+            .getClassesByName(name.text)
+            .filter { it.fqn != origin.fqn }
+        val filtered = if (vendorDir == null) {
+            collisions
+        } else {
+            collisions.filterNot { isVendorClass(it, vendorDir) }
+        }
+        return filtered.firstOrNull()
+    }
+
+    private fun findVendorDir(project: Project): VirtualFile? {
+        val basePath = project.basePath
+            ?: return null
+        val projectDir = LocalFileSystem.getInstance().findFileByPath(basePath)
+            ?: return null
+        return projectDir.findChild(VENDOR_DIR_NAME)
+    }
+
+    private fun isVendorClass(phpClass: PhpClass, vendorDir: VirtualFile): Boolean {
+        val file = phpClass.containingFile.virtualFile
+            ?: return false
+
+        return VfsUtilCore.isAncestor(vendorDir, file, true)
+    }
 }
diff --git a/src/test/kotlin/com/funivan/idea/phpClean/inspections/classNameCollision/ClassNameCollisionInspectionTest.kt b/src/test/kotlin/com/funivan/idea/phpClean/inspections/classNameCollision/ClassNameCollisionInspectionTest.kt
index 3612e69..b32060c 100644
--- a/src/test/kotlin/com/funivan/idea/phpClean/inspections/classNameCollision/ClassNameCollisionInspectionTest.kt
+++ b/src/test/kotlin/com/funivan/idea/phpClean/inspections/classNameCollision/ClassNameCollisionInspectionTest.kt
@@ -34,4 +34,46 @@ class ClassNameCollisionInspectionTest : BaseInspectionTest() {
                 """
         )
     }
+
+    @Test
+    fun testVendorCollisionIgnoredWhenOptionEnabled() {
+        myFixture.addFileToProject(
+            "vendor/lib/User.php",
+            """User {};
+                }
+            """
+        )
+    }
 }