check_class_redundancy($relative_path, $metadata['class'], $extension, $filename, $is_rspade); } // Check blade.php files with @rsx_id if ($extension === 'blade.php' && isset($metadata['id'])) { $this->check_blade_redundancy($relative_path, $metadata['id'], $filename, $is_rspade); } // Check jqhtml files with Define: if ($extension === 'jqhtml' && isset($metadata['id'])) { $this->check_jqhtml_redundancy($relative_path, $metadata['id'], $filename, $is_rspade); } } private function check_class_redundancy(string $file, string $class_name, string $extension, string $filename, bool $is_rspade): void { $filename_without_ext = pathinfo($filename, PATHINFO_FILENAME); $dir_path = dirname($file); $short_name = $this->extract_short_name($class_name, $dir_path); if ($short_name === null) { return; // No short name available } // Check if current filename is the full name (redundant) $is_full_name = $is_rspade ? $filename_without_ext === $class_name : strtolower($filename_without_ext) === strtolower($class_name); if (!$is_full_name) { return; // Not using full name } // Check if short filename would be available $short_filename = $is_rspade ? $short_name . '.' . $extension : strtolower($short_name) . '.' . $extension; $short_path = dirname(base_path($file)) . '/' . $short_filename; if (file_exists($short_path)) { return; // Short name already taken } $this->add_violation( $file, 1, "Filename contains redundant prefix already represented in directory structure", "class $class_name", "Directory structure already contains the class name prefix.\n" . "Consider using the shorter filename: '$short_filename'\n" . " mv '$filename' '$short_filename'\n\n" . "IMPORTANT: Only the filename should be changed. The class name must remain unchanged.\n" . "The directory structure provides the necessary context for the shorter filename.\n" . "Example: rsx/app/demo/demo_controller.php → rsx/app/demo/controller.php\n" . " (but class Demo_Controller remains Demo_Controller)\n\n" . "This improves readability while maintaining uniqueness.", 'convention' ); } private function check_blade_redundancy(string $file, string $rsx_id, string $filename, bool $is_rspade): void { $filename_without_blade = str_replace('.blade.php', '', $filename); $dir_path = dirname($file); $short_name = $this->extract_short_name($rsx_id, $dir_path); if ($short_name === null) { return; } $is_full_name = $is_rspade ? $filename_without_blade === $rsx_id : strtolower($filename_without_blade) === strtolower($rsx_id); if (!$is_full_name) { return; } $short_filename = $is_rspade ? $short_name . '.blade.php' : strtolower($short_name) . '.blade.php'; $short_path = dirname(base_path($file)) . '/' . $short_filename; if (file_exists($short_path)) { return; } $this->add_violation( $file, 1, "Blade filename contains redundant prefix already represented in directory structure", "@rsx_id('$rsx_id')", "Directory structure already contains the @rsx_id prefix.\n" . "Consider using the shorter filename: '$short_filename'\n" . " mv '$filename' '$short_filename'\n\n" . "IMPORTANT: Only the filename should be changed. The @rsx_id must remain unchanged.\n" . "The directory structure provides the necessary context for the shorter filename.\n" . "Example: rsx/app/demo/sections/demo_sections_cards.blade.php → cards.blade.php\n" . " (but @rsx_id('demo.sections.cards') remains demo.sections.cards)\n\n" . "This improves readability while maintaining uniqueness.", 'convention' ); } private function check_jqhtml_redundancy(string $file, string $component_name, string $filename, bool $is_rspade): void { $filename_without_ext = pathinfo($filename, PATHINFO_FILENAME); $dir_path = dirname($file); $short_name = $this->extract_short_name($component_name, $dir_path); if ($short_name === null) { return; } $is_full_name = $is_rspade ? $filename_without_ext === $component_name : strtolower($filename_without_ext) === strtolower($component_name); if (!$is_full_name) { return; } $short_filename = $is_rspade ? $short_name . '.jqhtml' : strtolower($short_name) . '.jqhtml'; $short_path = dirname(base_path($file)) . '/' . $short_filename; if (file_exists($short_path)) { return; } $this->add_violation( $file, 1, "Jqhtml filename contains redundant prefix already represented in directory structure", "", "Directory structure already contains the component name prefix.\n" . "Consider using the shorter filename: '$short_filename'\n" . " mv '$filename' '$short_filename'\n\n" . "IMPORTANT: Only the filename should be changed. The component name must remain unchanged.\n" . "The directory structure provides the necessary context for the shorter filename.\n" . "Example: rsx/app/demo/components/demo_card.jqhtml → card.jqhtml\n" . " (but remains Demo_Card)\n\n" . "This improves readability while maintaining uniqueness.", 'convention' ); } /** * Extract short name from class/id if directory structure matches prefix * Returns null if directory structure doesn't match or if short name rules aren't met * * Rules: * - Original name must have 3+ segments for short name to be allowed (2-segment names must use full name) * - Short name must have 2+ segments (exception: if original was 1 segment, short can be 1 segment) */ private function extract_short_name(string $full_name, string $dir_path): ?string { $name_parts = explode('_', $full_name); $original_segment_count = count($name_parts); // If original name has exactly 2 segments, short name is NOT allowed if ($original_segment_count === 2) { return null; } // If only 1 segment, no prefix to match if ($original_segment_count === 1) { return null; } // Split directory path into parts and re-index $dir_parts = array_values(array_filter(explode('/', $dir_path))); // Find the maximum number of consecutive matches between end of dir_parts and start of name_parts $matched_parts = 0; $max_possible = min(count($dir_parts), count($name_parts) - 1); // Try to match last N dir parts with first N name parts for ($num_to_check = $max_possible; $num_to_check > 0; $num_to_check--) { $all_match = true; for ($i = 0; $i < $num_to_check; $i++) { $dir_idx = count($dir_parts) - $num_to_check + $i; if (strtolower($dir_parts[$dir_idx]) !== strtolower($name_parts[$i])) { $all_match = false; break; } } if ($all_match) { $matched_parts = $num_to_check; break; } } if ($matched_parts === 0) { return null; } // Calculate the short name $short_parts = array_slice($name_parts, $matched_parts); $short_segment_count = count($short_parts); // Validate short name segment count // Short name must have 2+ segments (unless original was 1 segment, which we already excluded above) if ($short_segment_count < 2) { return null; // Short name would be too short } return implode('_', $short_parts); } }