['mode' => 'flag', 'description' => 'Overwrite existing files when scaffolding.'], ]; public function handle(Input $input, Output $output): int { $force = (bool) $input->getOption('force'); // Define placeholders to keep static analyzers from flagging variables used inside template strings /** @var mixed $app */ $app = null; /** @var mixed $router */ $router = null; $root = getcwd(); $dirs = [ 'public', 'bootstrap', 'config', 'routes', 'modules', 'resources', 'storage', 'storage/logs', 'storage/cache', 'storage/sessions', 'storage/views', 'storage/uploads', 'tests', 'console', 'console/commands', ]; foreach ($dirs as $d) { $path = $root . DIRECTORY_SEPARATOR . $d; if (!is_dir($path)) { @mkdir($path, 0777, true); $output->writeln("created $d/"); } } // .gitkeep for empty directories commonly empty foreach (['modules', 'resources', 'storage/logs', 'storage/cache', 'storage/sessions', 'storage/views', 'storage/uploads', 'console/commands'] as $maybeEmpty) { $file = $root . DIRECTORY_SEPARATOR . $maybeEmpty . DIRECTORY_SEPARATOR . '.gitkeep'; if (!file_exists($file)) { @file_put_contents($file, ""); } } // Files to scaffold $files = [ 'public/index.php' => <<<'PHP' <<<'PHP' safeLoad(); } // TODO: Build and return an application kernel/closure return static function () { return null; // placeholder }; PHP, 'config/app.php' => <<<'PHP' getenv('APP_NAME') ?: 'Phred App', 'env' => getenv('APP_ENV') ?: 'local', 'debug' => (bool) (getenv('APP_DEBUG') ?: true), 'timezone' => getenv('APP_TIMEZONE') ?: 'UTC', ]; PHP, 'routes/web.php' => <<<'PHP' get('/', [HomeController::class, '__invoke']); PHP, 'routes/api.php' => <<<'PHP' get('/health', [HealthController::class, '__invoke']); PHP, '.env.example' => <<<'ENV' APP_NAME=Phred App APP_ENV=local APP_DEBUG=true APP_TIMEZONE=UTC API_FORMAT=rest ENV, ]; foreach ($files as $relative => $contents) { $path = $root . DIRECTORY_SEPARATOR . $relative; if (!file_exists($path) || $force) { if (!is_dir(dirname($path))) { @mkdir(dirname($path), 0777, true); } @file_put_contents($path, rtrim($contents) . "\n"); $output->writeln("wrote $relative"); } } // Copy .env if missing if (!file_exists($root . '/.env') && file_exists($root . '/.env.example')) { @copy($root . '/.env.example', $root . '/.env'); $output->writeln('created .env'); } // Root phred launcher (Unix) $launcher = $root . '/phred'; if (!file_exists($launcher) || $force) { $shim = "#!/usr/bin/env php\nwriteln('wrote phred'); } // Windows launcher $launcherWin = $root . '/phred.bat'; if (!file_exists($launcherWin) || $force) { $shimWin = "@ECHO OFF\r\nphp \"%~dp0bin\\phred\" %*\r\n"; @file_put_contents($launcherWin, $shimWin); $output->writeln('wrote phred.bat'); } // Ensure .gitignore has sensible defaults $gitignore = $root . '/.gitignore'; $giLines = [ "/vendor/", "/.env", "/storage/*", "!/storage/.gitkeep", "/.phpunit.cache", "/.php-cs-fixer.cache", ]; $existing = file_exists($gitignore) ? file($gitignore, FILE_IGNORE_NEW_LINES) : []; $set = $existing ? array_flip($existing) : []; $changed = false; foreach ($giLines as $line) { if (!isset($set[$line])) { $existing[] = $line; $changed = true; } } if ($changed) { @file_put_contents($gitignore, implode("\n", $existing) . "\n"); $output->writeln('updated .gitignore'); } $output->writeln("\nPhred scaffold complete."); $output->writeln("Try: ./phred or php bin/phred"); return 0; } };