$file_metadata) { // Skip non-PHP files if (($file_metadata['extension'] ?? '') !== 'php') { continue; } // Skip if no public static methods if (empty($file_metadata['public_static_methods'])) { continue; } // Check each method foreach ($file_metadata['public_static_methods'] as $method_name => $method_info) { $attributes = $method_info['attributes'] ?? []; // Check if method has both #[SPA] and #[Route] $has_spa = isset($attributes['SPA']); $has_route = isset($attributes['Route']); if ($has_spa && $has_route) { $line = $method_info['line'] ?? 1; $class_name = $file_metadata['class'] ?? 'Unknown'; $this->add_violation( $file, $line, "Method '{$method_name}' has both #[SPA] and #[Route] attributes. These should not be combined.", "#[SPA]\n#[Route('...')]\npublic static function {$method_name}(...)", $this->build_suggestion($class_name, $method_name), 'critical' ); } } } } /** * Build detailed suggestion explaining SPA architecture */ private function build_suggestion(string $class_name, string $method_name): string { $lines = []; $lines[] = "The #[SPA] and #[Route] attributes serve different purposes and should not be combined."; $lines[] = ""; $lines[] = "WHAT #[SPA] DOES:"; $lines[] = " - Marks a method as an SPA bootstrap entry point"; $lines[] = " - Returns rsx_view(SPA) which loads the JavaScript SPA shell"; $lines[] = " - Acts as a catch-all for client-side routing"; $lines[] = " - Typically ONE per feature/bundle (e.g., Frontend_Spa_Controller::index)"; $lines[] = ""; $lines[] = "HOW SPA ROUTING WORKS:"; $lines[] = " - Routes are defined in JavaScript action classes with @route() decorator"; $lines[] = " - Example: @route('/contacts') on Contacts_Index_Action.js"; $lines[] = " - The SPA router matches URLs to actions CLIENT-SIDE"; $lines[] = " - Server only provides the bootstrap shell, not individual pages"; $lines[] = ""; $lines[] = "TO FIX:"; $lines[] = " 1. Remove the #[Route] attribute from the #[SPA] method"; $lines[] = " 2. Create JavaScript action classes for each route you need:"; $lines[] = ""; $lines[] = " // Example: rsx/app/frontend/contacts/Contacts_Index_Action.js"; $lines[] = " @route('/contacts')"; $lines[] = " @layout('Frontend_Layout')"; $lines[] = " @spa('{$class_name}::{$method_name}')"; $lines[] = " class Contacts_Index_Action extends Spa_Action { }"; $lines[] = ""; $lines[] = "See: php artisan rsx:man spa"; return implode("\n", $lines); } }