#!/usr/bin/env php Laravel model basename */ $manualCiToLaravel = [ 'FlagModel.php' => 'CurrentFlag.php', ]; /** Laravel files skipped (aliases / inheritance) */ $skipLaravelBasenames = [ 'Teacher.php', 'Communication.php', 'EventCharge.php', 'ClassModel.php', 'Calendar.php', ]; function ciFileToLaravelFile(string $ciBasename, array $manual): ?string { if (isset($manual[$ciBasename])) { return $manual[$ciBasename]; } if (! preg_match('/^(.+)Model\.php$/', $ciBasename, $m)) { return null; } $base = $m[1]; if ($base === 'Parent' || $base === 'Class') { return $base.'Model.php'; } return $base.'.php'; } function extractFirstBracketArray(string $src, string $needle): ?string { $pos = strpos($src, $needle); if ($pos === false) { return null; } $pos += strlen($needle); $len = strlen($src); while ($pos < $len && ctype_space($src[$pos])) { $pos++; } if ($pos >= $len || $src[$pos] !== '[') { return null; } $start = $pos; $depth = 0; for ($i = $pos; $i < $len; $i++) { $c = $src[$i]; if ($c === '[') { $depth++; } elseif ($c === ']') { $depth--; if ($depth === 0) { return substr($src, $start, $i - $start + 1); } } } return null; } /** @return list */ function parseQuotedStringsFromArrayLiteral(string $arrayLiteral): array { $out = []; if (! preg_match_all("/'((?:\\\\'|[^'])*)'/", $arrayLiteral, $m)) { return $out; } foreach ($m[1] as $raw) { $out[] = str_replace("\\'", "'", $raw); } return $out; } /** Render inner array only: [\n 'a',\n ] */ function renderFillableArray(array $items): string { $lines = []; foreach ($items as $item) { $escaped = addcslashes($item, "\\'"); $lines[] = " '".$escaped."',"; } return "[\n".implode("\n", $lines)."\n ]"; } function replaceFillableBlock(string $laravel, string $newArrayLiteral): string { $re = '/protected\s+\$fillable\s*=\s*\[[\s\S]*?\];/m'; if (! preg_match($re, $laravel)) { return $laravel; } return preg_replace($re, 'protected $fillable = '.$newArrayLiteral.';', $laravel, 1); } function replaceTimestampsLine(string $laravel, bool $useTimestamps): string { $line = $useTimestamps ? ' public $timestamps = true;' : ' public $timestamps = false;'; if (preg_match('/public\s+\$timestamps\s*=\s*(true|false)\s*;/', $laravel)) { return preg_replace( '/public\s+\$timestamps\s*=\s*(true|false)\s*;/', $line, $laravel, 1 ); } // Insert after class opening brace line (first line after "class X extends Y") return preg_replace( '/(class\s+\w+[^\n]*\n\{[\t ]*\n)/', '$1'.$line.NL.NL, $laravel, 1 ) ?: preg_replace('/(class\s+\w+[^\n]*\n\{)/', '$1'.NL.$line, $laravel, 1); } $updated = 0; $skipped = 0; $missing = []; foreach (glob($ciModelsDir.DIRECTORY_SEPARATOR.'*Model.php') ?: [] as $ciPath) { $ciBasename = basename($ciPath); $laravelBasename = ciFileToLaravelFile($ciBasename, $manualCiToLaravel); if ($laravelBasename === null) { continue; } if (in_array($laravelBasename, $skipLaravelBasenames, true)) { fwrite(STDERR, "Skip (alias/base): {$laravelBasename}".NL); $skipped++; continue; } $lvPath = $laravelModelsDir.DIRECTORY_SEPARATOR.$laravelBasename; if (! is_file($lvPath)) { $missing[] = $laravelBasename.' (from '.$ciBasename.')'; continue; } $ciSrc = (string) file_get_contents($ciPath); $lvSrc = (string) file_get_contents($lvPath); $useTs = true; if (preg_match('/protected\s+\$useTimestamps\s*=\s*(true|false)\s*;/', $ciSrc, $tm)) { $useTs = $tm[1] === 'true'; } $allowedBody = extractFirstBracketArray($ciSrc, 'protected $allowedFields = '); if ($allowedBody === null) { fwrite(STDERR, "No allowedFields in {$ciBasename}".NL); $skipped++; continue; } $fields = parseQuotedStringsFromArrayLiteral($allowedBody); if ($fields === []) { fwrite(STDERR, "Empty allowedFields in {$ciBasename}".NL); $skipped++; continue; } if (! preg_match('/protected\s+\$fillable\s*=\s*\[/', $lvSrc)) { fwrite(STDERR, "No \$fillable array in {$laravelBasename} — add manually".NL); $skipped++; continue; } $rendered = renderFillableArray($fields); $newLv = replaceFillableBlock($lvSrc, $rendered); $newLv = replaceTimestampsLine($newLv, $useTs); if ($newLv === $lvSrc) { fwrite(STDERR, "Unchanged: {$laravelBasename}".NL); $skipped++; continue; } file_put_contents($lvPath, $newLv); fwrite(STDOUT, "Updated {$laravelBasename}".NL); $updated++; } fwrite(STDOUT, NL.'Done. Updated: '.$updated.', skipped: '.$skipped.NL); if ($missing !== []) { fwrite(STDERR, 'Missing Laravel files ('.count($missing).'): '.implode(', ', array_slice($missing, 0, 20)).(count($missing) > 20 ? '…' : '').NL); exit(2); }