findPatternMatchesInPhpFiles('pages', '/\bnew\s+[\\\\A-Za-z0-9_]+(?:Service|Gateway|Repository)\s*\(/'); $this->assertSame([], $violations, "Direct service/repository/gateway instantiation found in pages/:\n" . implode("\n", $violations)); } public function testPagesDoNotUseDatabaseFacadeDirectly(): void { $violations = $this->findPatternMatchesInPhpFiles('pages', '/\bDB::/'); $this->assertSame([], $violations, "Direct DB facade usage found in pages/:\n" . implode("\n", $violations)); } public function testAdminPagesDoNotUseFactoryCreateChains(): void { $violations = $this->findPatternMatchesInPhpFiles('pages/admin', '/app\([^\n]*Factory::class\)->create/'); $this->assertSame([], $violations, "Factory create-chain usage found in pages/admin/:\n" . implode("\n", $violations)); } public function testAdminPagesDoNotReferenceFactoryClasses(): void { $violations = $this->findPatternMatchesInPhpFiles('pages/admin', '/Factory::class/'); $this->assertSame([], $violations, "Factory class usage found in pages/admin/:\n" . implode("\n", $violations)); } public function testPagesUseRequestInputInsteadOfSuperglobals(): void { $patterns = [ '/\$_POST/' => '$_POST', '/\$_GET/' => '$_GET', '/\$_FILES/' => '$_FILES', '/REQUEST_METHOD/' => 'REQUEST_METHOD', '/\$_SESSION/' => '$_SESSION', '/\$_SERVER/' => '$_SERVER', '/\$_COOKIE/' => '$_COOKIE', ]; foreach ($patterns as $pattern => $label) { $violations = $this->findPatternMatchesInPhpFiles('pages', $pattern); $this->assertSame([], $violations, "Superglobal {$label} usage found in pages/:\n" . implode("\n", $violations)); } } public function testServicesDoNotUseSuperglobals(): void { $patterns = [ '/\$_POST/' => '$_POST', '/\$_GET/' => '$_GET', '/\$_FILES/' => '$_FILES', '/\$_SERVER/' => '$_SERVER', '/\$_COOKIE/' => '$_COOKIE', '/\$_SESSION/' => '$_SESSION', '/\$_REQUEST/' => '$_REQUEST', ]; foreach ($patterns as $pattern => $label) { $violations = $this->findPatternMatchesInPhpFiles('lib/Service', $pattern); $this->assertSame([], $violations, "Superglobal {$label} usage found in lib/Service/:\n" . implode("\n", $violations)); } } public function testApiPagesDoNotReadJsonBodyDirectly(): void { $violations = $this->findPatternMatchesInPhpFiles('pages/api/v1', '/ApiResponse::readJsonBody\s*\(/'); $this->assertSame([], $violations, "ApiResponse::readJsonBody usage found in pages/api/v1/:\n" . implode("\n", $violations)); } public function testTemplatesDoNotUseLegacyFilterToggleMarkup(): void { $pattern = '/data-(?:toolbar-toggle|filter-overflow|filter-toggle)/'; $violations = array_merge( $this->findPatternMatchesInFiles('templates', $pattern, ['phtml']), $this->findPatternMatchesInFiles('pages', $pattern, ['phtml']) ); sort($violations); $this->assertSame([], $violations, "Legacy filter toggle markup found in templates/pages:\n" . implode("\n", $violations)); } public function testPagesAndTemplatesDoNotUseInlineModuleScripts(): void { $pattern = '/]*\btype=["\']module["\'])(?![^>]*\bsrc=)[^>]*>/i'; $violations = array_merge( $this->findPatternMatchesInFiles('templates', $pattern, ['phtml']), $this->findPatternMatchesInFiles('pages', $pattern, ['phtml']) ); sort($violations); $this->assertSame([], $violations, "Inline module scripts found in templates/pages:\n" . implode("\n", $violations)); } public function testPagesAndTemplatesUseAssetVersionForJsUrls(): void { $pattern = '/asset\(\s*[\'"][^\'"]+\.js/i'; $violations = array_merge( $this->findPatternMatchesInFiles('templates', $pattern, ['phtml']), $this->findPatternMatchesInFiles('pages', $pattern, ['phtml']) ); sort($violations); $this->assertSame([], $violations, "asset(...) JS URLs found in templates/pages:\n" . implode("\n", $violations)); } public function testTemplatesDoNotUseDatabaseFacadeDirectly(): void { $violations = $this->findPatternMatchesInFiles('templates', '/\bDB::/', ['phtml']); $this->assertSame([], $violations, "Direct DB facade usage found in templates/:\n" . implode("\n", $violations)); } public function testTemplatesDoNotResolveServiceGatewayOrRepositoryViaDI(): void { $violations = $this->findPatternMatchesInFiles( 'templates', '/app\(\s*[\\\\A-Za-z0-9_]*(?:Service|Gateway|Repository)::class\s*\)/', ['phtml'] ); $this->assertSame([], $violations, "DI resolution of Service/Gateway/Repository found in templates/:\n" . implode("\n", $violations)); } public function testNoLegacySettingsGatewayOrServiceReferencesExist(): void { $patterns = [ '/\bSettingService::class\b/' => 'SettingService::class', '/\bSettingGateway::class\b/' => 'SettingGateway::class', '/\bnew\s+SettingService\s*\(/' => 'new SettingService(...)', '/\bnew\s+SettingGateway\s*\(/' => 'new SettingGateway(...)', '/use\s+[^;]*\\\\SettingService;/' => 'use ...\\SettingService', '/use\s+[^;]*\\\\SettingGateway;/' => 'use ...\\SettingGateway', ]; foreach ($patterns as $pattern => $label) { $violations = array_merge( $this->findPatternMatchesInPhpFiles('lib', $pattern), $this->findPatternMatchesInPhpFiles('pages', $pattern), $this->findPatternMatchesInPhpFiles('tests', $pattern), ); $violations = array_values(array_filter( $violations, static fn (string $path): bool => $path !== 'tests/Architecture/CoreStarterkitContractTest.php' )); $violations = array_values(array_unique($violations)); sort($violations); $this->assertSame([], $violations, "Legacy settings reference {$label} found:\n" . implode("\n", $violations)); } } }