Phred/src/commands/install.php

131 lines
4.8 KiB
PHP
Raw Normal View History

2025-12-14 23:10:01 +00:00
<?php
declare(strict_types=1);
use Phred\Console\Command;
use Symfony\Component\Console\Input\InputInterface as Input;
use Symfony\Component\Console\Output\OutputInterface as Output;
return new class extends Command {
protected string $command = 'install';
protected string $description = 'Scaffold the Phred project structure (idempotent).';
protected array $options = [
'--force' => ['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("<info>created</info> $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
$stubDir = dirname(__DIR__) . '/stubs/install';
2025-12-14 23:10:01 +00:00
$files = [
'public/index.php' => file_get_contents($stubDir . '/public_index.stub'),
'bootstrap/app.php' => file_get_contents($stubDir . '/bootstrap_app.stub'),
'config/app.php' => file_get_contents($stubDir . '/config_app.stub'),
'routes/web.php' => file_get_contents($stubDir . '/routes_web.stub'),
'routes/api.php' => file_get_contents($stubDir . '/routes_api.stub'),
'.env.example' => file_get_contents($stubDir . '/env_example.stub'),
2025-12-14 23:10:01 +00:00
];
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("<info>wrote</info> $relative");
}
}
// Copy .env if missing
if (!file_exists($root . '/.env') && file_exists($root . '/.env.example')) {
@copy($root . '/.env.example', $root . '/.env');
$output->writeln('<info>created</info> .env');
}
// Root phred launcher (Unix)
$launcher = $root . '/phred';
if (!file_exists($launcher) || $force) {
$shim = "#!/usr/bin/env php\n<?php\nrequire __DIR__ . '/bin/phred';\n";
@file_put_contents($launcher, $shim);
@chmod($launcher, 0755);
$output->writeln('<info>wrote</info> 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('<info>wrote</info> 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('<info>updated</info> .gitignore');
}
$output->writeln("\n<comment>Phred scaffold complete.</comment>");
$output->writeln("Try: <info>./phred</info> or <info>php bin/phred</info>");
return 0;
}
};