MIF_E31230892/scan_kelas_usage.php

429 lines
13 KiB
PHP

<?php
/**
* Scan Kelas Usage Script
*
* Script untuk scan semua penggunaan $santri->kelas di codebase
* dan generate laporan markdown untuk refactoring guidance
*
* Usage:
* php scan_kelas_usage.php
*
* Output:
* KELAS_USAGE_MAP.md
*/
// Configuration
$baseDir = __DIR__ . '/sim-pkpps';
$outputFile = __DIR__ . '/KELAS_USAGE_MAP.md';
// Check if base directory exists
if (!is_dir($baseDir)) {
echo "❌ Error: Base directory not found: {$baseDir}\n";
echo "Current directory: " . __DIR__ . "\n";
exit(1);
}
// Directories to scan
$scanDirs = [
'app/Http/Controllers',
'app/Models',
'resources/views',
'database/migrations',
'database/seeders',
'routes',
];
// Patterns to search (regex)
$patterns = [
'property_access' => '/\$santri\s*->\s*kelas(?!\w)/',
'array_access' => '/\$santri\[[\'"]kelas[\'"]\]/',
'blade_kelas' => '/\{\{\s*\$santri\s*->\s*kelas\s*\}\}/',
'where_kelas' => '/->where\([\'"]kelas[\'"]\s*,/',
'wherein_kelas' => '/->whereIn\([\'"]kelas[\'"]\s*,/',
'select_kelas' => '/SELECT.*santris\.kelas/i',
'enum_values' => '/(\'PB\'|\'Lambatan\'|\'Cepatan\')\s*(,|\]|\))/i',
'kelas_column' => '/[\'"]kelas[\'"]\s*=>/i',
];
echo "╔══════════════════════════════════════════════════════╗\n";
echo "║ Scanning Santri.kelas Usage in Codebase ║\n";
echo "╚══════════════════════════════════════════════════════╝\n\n";
// Initialize results
$results = [];
$totalFiles = 0;
$totalMatches = 0;
// Scan each directory
foreach ($scanDirs as $dir) {
$fullPath = $baseDir . '/' . $dir;
if (!is_dir($fullPath)) {
echo "⚠️ Directory not found: {$dir}\n";
continue;
}
echo "📁 Scanning: {$dir}\n";
$files = scanDirectory($fullPath, $dir);
foreach ($files as $file) {
$matches = scanFile($file['full_path'], $patterns);
if (!empty($matches)) {
$totalFiles++;
$totalMatches += count($matches);
$results[$dir][] = [
'file' => $file['relative_path'],
'full_path' => $file['full_path'],
'matches' => $matches,
];
echo " ✓ Found " . count($matches) . " match(es) in: " . basename($file['relative_path']) . "\n";
}
}
}
echo "\n";
echo "Summary:\n";
echo " 📊 Files scanned: " . countAllFiles($scanDirs, $baseDir) . "\n";
echo " ✓ Files with matches: {$totalFiles}\n";
echo " 🔍 Total matches: {$totalMatches}\n";
echo "\n";
// Generate markdown report
echo "📝 Generating report: KELAS_USAGE_MAP.md\n";
generateMarkdownReport($results, $outputFile);
echo "✓ Report generated successfully!\n";
echo "\nNext steps:\n";
echo " 1. Review KELAS_USAGE_MAP.md\n";
echo " 2. Prioritize refactoring (HIGH -> MEDIUM -> LOW)\n";
echo " 3. Test each change thoroughly\n";
echo " 4. Use \$santri->kelas_name for backward compatibility\n\n";
// ============================================
// HELPER FUNCTIONS
// ============================================
/**
* Recursively scan directory for PHP and Blade files
*/
function scanDirectory($dir, $relativePath)
{
$files = [];
$items = scandir($dir);
foreach ($items as $item) {
if ($item === '.' || $item === '..') {
continue;
}
$fullPath = $dir . '/' . $item;
$relPath = $relativePath . '/' . $item;
if (is_dir($fullPath)) {
$files = array_merge($files, scanDirectory($fullPath, $relPath));
} elseif (preg_match('/\.(php|blade\.php)$/', $item)) {
$files[] = [
'full_path' => $fullPath,
'relative_path' => $relPath,
];
}
}
return $files;
}
/**
* Scan file for patterns
*/
function scanFile($filePath, $patterns)
{
$content = file_get_contents($filePath);
$lines = explode("\n", $content);
$matches = [];
foreach ($lines as $lineNum => $line) {
foreach ($patterns as $type => $pattern) {
if (preg_match($pattern, $line)) {
$matches[] = [
'line' => $lineNum + 1,
'type' => $type,
'content' => trim($line),
];
}
}
}
return $matches;
}
/**
* Count all files in directories
*/
function countAllFiles($dirs, $baseDir)
{
$count = 0;
foreach ($dirs as $dir) {
$fullPath = $baseDir . '/' . $dir;
if (is_dir($fullPath)) {
$count += count(scanDirectory($fullPath, $dir));
}
}
return $count;
}
/**
* Generate markdown report
*/
function generateMarkdownReport($results, $outputFile)
{
$md = "# Santri.kelas Usage Mapping\n\n";
$md .= "_Generated: " . date('Y-m-d H:i:s') . "_\n\n";
$md .= "This document maps all usage of `\$santri->kelas` and related patterns in the codebase ";
$md .= "to guide refactoring to the new kelas system.\n\n";
$md .= "---\n\n";
$md .= "## 📊 Summary\n\n";
$totalFiles = 0;
$totalMatches = 0;
foreach ($results as $dir => $files) {
$totalFiles += count($files);
foreach ($files as $file) {
$totalMatches += count($file['matches']);
}
}
$md .= "- **Total files with kelas usage:** {$totalFiles}\n";
$md .= "- **Total matches found:** {$totalMatches}\n\n";
$md .= "---\n\n";
// Priority mapping
$priorities = categorizePriority($results);
$md .= "## 🎯 Priority Levels\n\n";
$md .= "### 🔴 HIGH Priority (Break functionality)\n\n";
if (!empty($priorities['high'])) {
foreach ($priorities['high'] as $item) {
$md .= "- **{$item['file']}**\n";
$md .= " - Issue: {$item['reason']}\n";
$md .= " - Action Required: {$item['action']}\n\n";
}
} else {
$md .= "_No high priority items found_\n\n";
}
$md .= "### 🟡 MEDIUM Priority (UI/Display)\n\n";
if (!empty($priorities['medium'])) {
foreach ($priorities['medium'] as $item) {
$md .= "- **{$item['file']}**\n";
$md .= " - Issue: {$item['reason']}\n";
$md .= " - Action Required: {$item['action']}\n\n";
}
} else {
$md .= "_No medium priority items found_\n\n";
}
$md .= "### 🟢 LOW Priority (Backward compatible)\n\n";
if (!empty($priorities['low'])) {
foreach ($priorities['low'] as $item) {
$md .= "- **{$item['file']}**\n";
$md .= " - Note: {$item['reason']}\n\n";
}
} else {
$md .= "_No low priority items found_\n\n";
}
$md .= "---\n\n";
// Detailed listing by directory
$md .= "## 📂 Detailed Listing by Directory\n\n";
foreach ($results as $dir => $files) {
$md .= "### " . ucfirst(str_replace('/', ' / ', $dir)) . "\n\n";
foreach ($files as $file) {
$md .= "#### 📄 `{$file['file']}`\n\n";
// Group matches by type
$byType = [];
foreach ($file['matches'] as $match) {
$byType[$match['type']][] = $match;
}
foreach ($byType as $type => $matches) {
$md .= "**Pattern: `{$type}`**\n\n";
foreach ($matches as $match) {
$md .= "- **Line {$match['line']}:** `{$match['content']}`\n";
}
$md .= "\n";
}
// Suggested action
$action = getRefactoringAction($file['file'], $byType);
$md .= "**💡 Suggested Action:**\n";
$md .= $action . "\n\n";
$md .= "---\n\n";
}
}
// Migration guide
$md .= "## 📖 Refactoring Guide\n\n";
$md .= "### General Patterns\n\n";
$md .= "#### 1. Display in Views (Blade)\n";
$md .= "```php\n";
$md .= "// OLD:\n";
$md .= "{{ \$santri->kelas }}\n\n";
$md .= "// NEW (backward compatible):\n";
$md .= "{{ \$santri->kelas_name }}\n";
$md .= "```\n\n";
$md .= "#### 2. Filter in Controllers\n";
$md .= "```php\n";
$md .= "// OLD:\n";
$md .= "\$santris = Santri::where('kelas', 'PB')->get();\n\n";
$md .= "// NEW:\n";
$md .= "\$santris = Santri::whereHas('kelasSantri', function(\$q) {\n";
$md .= " \$q->where('id_kelas', 1); // PB = 1\n";
$md .= "})->get();\n";
$md .= "```\n\n";
$md .= "#### 3. Kegiatan-Kelas Relation\n";
$md .= "```php\n";
$md .= "// OLD: Filter santri by kelas for kegiatan\n";
$md .= "\$santris = Santri::whereIn('kelas', ['PB', 'Lambatan'])->get();\n\n";
$md .= "// NEW: Use kegiatan relation\n";
$md .= "\$santris = \$kegiatan->getEligibleSantris();\n";
$md .= "```\n\n";
$md .= "### Testing Checklist\n\n";
$md .= "- [ ] Santri detail page displays correct kelas\n";
$md .= "- [ ] Santri list filter by kelas works\n";
$md .= "- [ ] Dashboard statistics by kelas accurate\n";
$md .= "- [ ] Kegiatan filtering by kelas works\n";
$md .= "- [ ] Absensi shows correct santri per kegiatan\n";
$md .= "- [ ] Reports include correct kelas information\n";
$md .= "- [ ] Mobile API returns kelas data correctly\n\n";
// Write to file
file_put_contents($outputFile, $md);
}
/**
* Categorize by priority
*/
function categorizePriority($results)
{
$priorities = [
'high' => [],
'medium' => [],
'low' => [],
];
foreach ($results as $dir => $files) {
foreach ($files as $file) {
$fileName = basename($file['file']);
$priority = determinePriority($file['file'], $file['matches']);
$priorities[$priority['level']][] = [
'file' => $file['file'],
'reason' => $priority['reason'],
'action' => $priority['action'] ?? 'Review and update',
];
}
}
return $priorities;
}
/**
* Determine priority level
*/
function determinePriority($filePath, $matches)
{
$fileName = basename($filePath);
// HIGH: Controllers with where/whereIn
if (strpos($filePath, 'Controller') !== false) {
foreach ($matches as $match) {
if (in_array($match['type'], ['where_kelas', 'wherein_kelas'])) {
return [
'level' => 'high',
'reason' => 'Query filtering by kelas column',
'action' => 'Update to use kelasSantri relationship',
];
}
}
}
// HIGH: Migration files
if (strpos($filePath, 'migration') !== false) {
return [
'level' => 'high',
'reason' => 'Database schema definition',
'action' => 'Review but DO NOT modify old migrations',
];
}
// MEDIUM: Views
if (strpos($filePath, 'views') !== false || strpos($filePath, '.blade.php') !== false) {
return [
'level' => 'medium',
'reason' => 'Display kelas in UI',
'action' => 'Change to use $santri->kelas_name accessor',
];
}
// MEDIUM: Models
if (strpos($filePath, 'Models') !== false) {
return [
'level' => 'medium',
'reason' => 'Model attribute or accessor',
'action' => 'Review accessor implementation',
];
}
// LOW: Everything else
return [
'level' => 'low',
'reason' => 'Other usage',
'action' => 'Review as needed',
];
}
/**
* Get refactoring action suggestion
*/
function getRefactoringAction($filePath, $matchesByType)
{
$action = "";
if (strpos($filePath, 'Controller') !== false) {
if (isset($matchesByType['where_kelas']) || isset($matchesByType['wherein_kelas'])) {
$action .= "1. Replace `where('kelas')` with `whereHas('kelasSantri')`\n";
$action .= "2. Update query to use kelas ID instead of name\n";
$action .= "3. Test filter functionality thoroughly\n";
}
}
if (strpos($filePath, '.blade.php') !== false) {
if (isset($matchesByType['blade_kelas']) || isset($matchesByType['property_access'])) {
$action .= "1. Replace `{{ \$santri->kelas }}` with `{{ \$santri->kelas_name }}`\n";
$action .= "2. Test display in browser\n";
}
}
if (strpos($filePath, 'Model') !== false) {
$action .= "1. Review model methods and accessors\n";
$action .= "2. Ensure backward compatibility\n";
$action .= "3. Add tests for new relations\n";
}
if (empty($action)) {
$action = "Review usage and update as needed based on context.";
}
return $action;
}