From 95f4d124971ad16f1c81906d616f54db8a0e1068 Mon Sep 17 00:00:00 2001 From: Abdul Malik Ikhsan Date: Sat, 24 Oct 2026 14:20:32 +0700 Subject: [PATCH] fix: Stop treating .class.php files as PSR-4 aliases in Psr4NamespaceRule --- src/Rule/Rules/Composer/Psr4NamespaceRule.php | 2 -- tests/Rule/Composer/Psr4NamespaceRuleTest.php | 31 ++++++++++++++++--- 2 files changed, 27 insertions(+), 6 deletions(-) diff --git a/src/Rule/Rules/Composer/Psr4NamespaceRule.php b/src/Rule/Rules/Composer/Psr4NamespaceRule.php index aa8fa274..71a95451 100644 --- a/src/Rule/Rules/Composer/Psr4NamespaceRule.php +++ b/src/Rule/Rules/Composer/Psr4NamespaceRule.php @@ -20,7 +20,6 @@ use function file_exists; use function ltrim; use function max; -use function preg_replace; use function sprintf; use function str_ends_with; use function str_replace; @@ -113,7 +112,6 @@ private function expectedClassNames(string $file): array } $relativeClass = substr($relativeClass, 0, -4); - $relativeClass = (string) preg_replace('/\.class$/i', '', $relativeClass); $relativeClass = str_replace('/', '\\', $relativeClass); $className = $namespace . ltrim($relativeClass, '\\'); diff --git a/tests/Rule/Composer/Psr4NamespaceRuleTest.php b/tests/Rule/Composer/Psr4NamespaceRuleTest.php index 73893a52..d7179f7c 100644 --- a/tests/Rule/Composer/Psr4NamespaceRuleTest.php +++ b/tests/Rule/Composer/Psr4NamespaceRuleTest.php @@ -16,6 +16,7 @@ use function file_put_contents; use function json_encode; use function mkdir; +use function sprintf; #[CoversClass(Psr4NamespaceRule::class)] final class Psr4NamespaceRuleTest extends TestCase @@ -205,19 +206,41 @@ public function testPassesWhenRelativeFileIsNotPhpFile(): void $this->assertNotInstanceOf(RuleViolation::class, $psr4NamespaceRule->evaluate($this->makeNode('Foo', $file))); } - public function testStripsClassSuffixFromExpectedClassName(): void + public function testDoesNotTreatClassPhpSuffixAsPhpFile(): void { $basePath = $this->makeTempProject(); - $file = $basePath . '/tests/Foo.class.php'; + mkdir($basePath . '/tests/Service'); - file_put_contents($file, 'assertNotInstanceOf( RuleViolation::class, - $psr4NamespaceRule->evaluate($this->makeNode('App\\Tests\\Foo', $file)) + $psr4NamespaceRule->evaluate($this->makeNode('App\\Tests\\Foo', $basePath . '/tests/Foo.php')) ); + + foreach ($this->classPhpSuffixFiles() as $relativeFile => [$className, $expectedClassName]) { + $file = $basePath . '/tests/' . $relativeFile; + file_put_contents($file, 'evaluate($this->makeNode($className, $file)); + + $this->assertInstanceOf(RuleViolation::class, $violation, $relativeFile); + $this->assertSame( + sprintf('Class [%s] must match PSR-4 class [%s]', $className, $expectedClassName), + $violation->message, + $relativeFile + ); + } + } + + /** @return iterable */ + private function classPhpSuffixFiles(): iterable + { + yield 'Foo.class.php' => ['App\\Tests\\Foo', 'App\\Tests\\Foo.class']; + yield 'Bar.class.php' => ['App\\Tests\\BarClass', 'App\\Tests\\Bar.class']; + yield 'Service/Foo.class.php' => ['App\\Tests\\Service\\Foo', 'App\\Tests\\Service\\Foo.class']; } public function testFailsWhenTraitNameDoesNotMatchFilename(): void