diff --git a/src/Rule/Rules/Composer/Psr4DirectoryExistsRule.php b/src/Rule/Rules/Composer/Psr4DirectoryExistsRule.php index 900c0944..633cdca0 100644 --- a/src/Rule/Rules/Composer/Psr4DirectoryExistsRule.php +++ b/src/Rule/Rules/Composer/Psr4DirectoryExistsRule.php @@ -28,7 +28,7 @@ public function __construct( public function evaluateProject(string $basePath, Architecture $architecture, array $skipPaths = []): ?RuleViolation { - $composerFile = rtrim($basePath, '/') . '/composer.json'; + $composerFile = Path::normalise(rtrim($basePath, '/') . '/composer.json', canonicalise: true); if (! file_exists($composerFile)) { return $this->violation( diff --git a/src/Rule/Rules/Composer/Psr4EmptyNamespacePrefixRule.php b/src/Rule/Rules/Composer/Psr4EmptyNamespacePrefixRule.php index 58424005..e8194d04 100644 --- a/src/Rule/Rules/Composer/Psr4EmptyNamespacePrefixRule.php +++ b/src/Rule/Rules/Composer/Psr4EmptyNamespacePrefixRule.php @@ -9,9 +9,9 @@ use Boundwize\StructArmed\Rule\ComposerJsonRuleInterface; use Boundwize\StructArmed\Rule\MultipleProjectRuleViolationInterface; use Boundwize\StructArmed\Rule\RuleViolation; +use Boundwize\StructArmed\Util\Path; use function array_keys; -use function file_exists; use function is_array; use function is_string; use function rtrim; @@ -38,18 +38,14 @@ public function evaluateProject(string $basePath, Architecture $architecture, ar */ public function evaluateProjectAll(string $basePath, Architecture $architecture, array $skipPaths = []): array { - $composerFile = rtrim($basePath, '/') . '/composer.json'; - - if (! file_exists($composerFile)) { - return []; - } - $composer = $this->psr4PathResolver->composerConfig($basePath); if ($composer === null) { return []; } + $composerFile = Path::normalise(rtrim($basePath, '/') . '/composer.json', canonicalise: true); + $violations = []; foreach (['autoload', 'autoload-dev'] as $section) { diff --git a/src/Rule/Rules/Composer/Psr4RootPathRule.php b/src/Rule/Rules/Composer/Psr4RootPathRule.php index 5fb93676..4b131441 100644 --- a/src/Rule/Rules/Composer/Psr4RootPathRule.php +++ b/src/Rule/Rules/Composer/Psr4RootPathRule.php @@ -11,7 +11,6 @@ use Boundwize\StructArmed\Rule\RuleViolation; use Boundwize\StructArmed\Util\Path; -use function file_exists; use function is_array; use function is_string; use function rtrim; @@ -36,18 +35,14 @@ public function evaluateProject(string $basePath, Architecture $architecture, ar */ public function evaluateProjectAll(string $basePath, Architecture $architecture, array $skipPaths = []): array { - $composerFile = rtrim($basePath, '/') . '/composer.json'; - - if (! file_exists($composerFile)) { - return []; - } - $composer = $this->psr4PathResolver->composerConfig($basePath); if ($composer === null) { return []; } + $composerFile = Path::normalise(rtrim($basePath, '/') . '/composer.json', canonicalise: true); + $violations = []; $normalisedBasePath = Path::normalise($basePath, canonicalise: true); diff --git a/src/Rule/Rules/Composer/Psr4SourcePathsRule.php b/src/Rule/Rules/Composer/Psr4SourcePathsRule.php index 8f8b2fe2..8f84fce5 100644 --- a/src/Rule/Rules/Composer/Psr4SourcePathsRule.php +++ b/src/Rule/Rules/Composer/Psr4SourcePathsRule.php @@ -34,7 +34,7 @@ public function __construct( public function evaluateProject(string $basePath, Architecture $architecture, array $skipPaths = []): ?RuleViolation { - $composerFile = rtrim($basePath, '/') . '/composer.json'; + $composerFile = Path::normalise(rtrim($basePath, '/') . '/composer.json', canonicalise: true); if (! file_exists($composerFile)) { return $this->violation( diff --git a/tests/Rule/Composer/Psr4ComposerFilePathCanonicalisationTest.php b/tests/Rule/Composer/Psr4ComposerFilePathCanonicalisationTest.php new file mode 100644 index 00000000..9d36125b --- /dev/null +++ b/tests/Rule/Composer/Psr4ComposerFilePathCanonicalisationTest.php @@ -0,0 +1,78 @@ +makeTempProject(<<<'JSON' +{ + "autoload": { + "psr-4": { + "": "src/" + } + } +} +JSON); + + $violations = (new Psr4EmptyNamespacePrefixRule())->evaluateProjectAll( + $basePath . '/.', + Architecture::define() + ); + + $this->assertCount(1, $violations); + $this->assertSame($this->expectedComposerFilePath($basePath), $violations[0]->file); + } + + public function testRootPathRuleReportsCanonicalisedComposerFilePath(): void + { + $basePath = $this->makeTempProject(<<<'JSON' +{ + "autoload": { + "psr-4": { + "App\\": "." + } + } +} +JSON); + + $violations = (new Psr4RootPathRule())->evaluateProjectAll( + $basePath . '/.', + Architecture::define() + ); + + $this->assertCount(1, $violations); + $this->assertSame($this->expectedComposerFilePath($basePath), $violations[0]->file); + } + + private function expectedComposerFilePath(string $basePath): string + { + return str_replace('\\', '/', (string) realpath($basePath)) . '/composer.json'; + } + + private function makeTempProject(string $composerJson): string + { + $basePath = $this->makeTemporaryDirectory('structarmed-psr4-composer-file-path'); + file_put_contents($basePath . '/composer.json', $composerJson); + + return $basePath; + } +} diff --git a/tests/Rule/Composer/Psr4ComposerFilePathNormalisationTest.php b/tests/Rule/Composer/Psr4ComposerFilePathNormalisationTest.php new file mode 100644 index 00000000..10929a3f --- /dev/null +++ b/tests/Rule/Composer/Psr4ComposerFilePathNormalisationTest.php @@ -0,0 +1,41 @@ +evaluateProject( + self::WINDOWS_STYLE_MISSING_BASE_PATH, + Architecture::define() + ); + + $this->assertInstanceOf(RuleViolation::class, $violation); + $this->assertSame('C:/structarmed-missing-fixture/app/composer.json', $violation->file); + } + + public function testSourcePathsRuleReportsForwardSlashesForWindowsStyleBasePath(): void + { + $violation = (new Psr4SourcePathsRule(null))->evaluateProject( + self::WINDOWS_STYLE_MISSING_BASE_PATH, + Architecture::define() + ); + + $this->assertInstanceOf(RuleViolation::class, $violation); + $this->assertSame('C:/structarmed-missing-fixture/app/composer.json', $violation->file); + } +}