$line) { $line_number = $line_num + 1; // Check each event handler foreach (self::EVENT_HANDLERS as $event_attr) { // Pattern: onclick="..." or onclick='...' (but not @onclick which is allowed in jqhtml) // We need to make sure it's not preceded by @ symbol $pattern = '/(? $event_attr, 'line' => $line_number, 'code' => trim($line), 'is_jqhtml' => $is_jqhtml, ]; break; // Only need first violation per line } } } if (!empty($violations)) { return ['inline_event_violations' => $violations]; } return null; } /** * Check file for inline event handler violations stored in metadata */ public function check(string $file_path, string $contents, array $metadata = []): void { // Skip layouts if (str_contains($file_path, 'layout') || str_contains($file_path, 'Layout')) { return; } // Check for violations in code quality metadata if (isset($metadata['code_quality_metadata']['BLADE-EVENT-01']['inline_event_violations'])) { $violations = $metadata['code_quality_metadata']['BLADE-EVENT-01']['inline_event_violations']; // Throw on first violation foreach ($violations as $violation) { $is_jqhtml = $violation['is_jqhtml']; $file_type = $is_jqhtml ? 'jqhtml template' : 'Blade view'; $error_message = "Code Quality Violation (BLADE-EVENT-01) - Inline Event Handler in {$file_type}\n\n"; $error_message .= "CRITICAL: Inline event handlers (e.g., {$violation['event_attr']}=\"...\") are not allowed\n\n"; $error_message .= "File: {$file_path}\n"; $error_message .= "Line: {$violation['line']}\n"; $error_message .= "Code: {$violation['code']}\n\n"; $error_message .= $this->get_detailed_remediation($file_path, $is_jqhtml); throw new \App\RSpade\CodeQuality\RuntimeChecks\YoureDoingItWrongException( $error_message, 0, null, base_path($file_path), $violation['line'] ); } } } /** * Get detailed remediation instructions */ private function get_detailed_remediation(string $file_path, bool $is_jqhtml): string { $path_parts = pathinfo($file_path); $base_name = $path_parts['filename']; // Remove .blade from blade.php files $base_name = str_replace('.blade', '', $base_name); // Determine JavaScript filename $js_filename = $base_name . '.js'; $js_path = dirname($file_path) . '/' . $js_filename; // Convert to class name (handle snake_case to PascalCase if needed) $class_name = str_replace('_', ' ', $base_name); $class_name = str_replace(' ', '_', ucwords($class_name)); if ($is_jqhtml) { return $this->get_jqhtml_remediation($js_path, $class_name, $base_name); } else { return $this->get_blade_remediation($js_path, $class_name); } } /** * Get remediation for Blade views */ private function get_blade_remediation(string $js_path, string $class_name): string { return "FRAMEWORK CONVENTION: Event handlers must be in companion JavaScript files, not inline. REQUIRED STEPS: 1. Remove the inline event handler attribute (e.g., onclick=\"...\") 2. Add an id or class to the element for jQuery selection 3. Create/update companion JavaScript file: {$js_path} 4. Bind the event handler in the on_app_ready() method EXAMPLE IMPLEMENTATION: Blade View (remove inline handler): Companion JavaScript ({$js_path}): /** * JavaScript for {$class_name} view */ class {$class_name} { /** * Initialize when app is ready * This method is automatically called by RSX framework */ static on_app_ready() { // Only initialize if we're on this specific view if (!$('.{$class_name}').exists()) { return; } // Bind event handlers using jQuery $('#my-button').click(() => { // Event handler code here console.log('Button clicked'); }); } } KEY CONVENTIONS: - Event handlers MUST be in companion .js file - Use static on_app_ready() method (called automatically) - Check for view presence before initializing - Use jQuery .click(), .change(), etc. for event binding - NO inline event handlers in HTML attributes WHY THIS MATTERS: - Separation of concerns: HTML structure separate from behavior - Maintainability: All JavaScript in one place - Testing: Event handlers can be unit tested - Security: Reduces XSS attack surface - LLM-friendly: Predictable patterns for AI code generation"; } /** * Get remediation for jqhtml templates */ private function get_jqhtml_remediation(string $js_path, string $class_name, string $template_name): string { return "FRAMEWORK CONVENTION: jqhtml templates should use @event syntax, not inline event handlers. REQUIRED STEPS: 1. Replace inline event handler (onclick=\"...\") with @event syntax (@click) 2. Reference a method on the companion JavaScript class 3. Implement the method in {$js_path} EXAMPLE IMPLEMENTATION: jqhtml Template (use @event syntax): Companion JavaScript ({$js_path}): /** * {$class_name} jqhtml component */ class {$class_name} extends Jqhtml_Component { on_create() { // Initialize component state this.data.count = 0; } // Event handler method referenced in template handle_click() { this.data.count++; console.log('Clicked', this.data.count, 'times'); this.render(); // Re-render if needed } } KEY CONVENTIONS FOR JQHTML: - Use @event syntax in template (@click, @change, @submit, etc.) - Event handlers MUST reference methods on the component class - Method names should use underscore_case (e.g., handle_click) - NO inline JavaScript code in @event attributes - NO standard HTML event attributes (onclick, onchange, etc.) COMMON @EVENT ATTRIBUTES: - @click - Click events - @change - Input/select change events - @submit - Form submission - @keyup, @keydown - Keyboard events - @focus, @blur - Focus events For more details on jqhtml event handling, see: php artisan rsx:man jqhtml WHY THIS MATTERS: - Framework integration: @event syntax integrates with jqhtml lifecycle - Automatic preventDefault: Framework handles event.preventDefault() automatically - Component encapsulation: All behavior defined in component class - Maintainability: Clear separation of template and logic - Type safety: Methods can be validated and tested"; } }