.php to config.php, the active theme to theme.php. Never overwrites a file; the old * files stay in place (platform:check warns while both exist). */ class PlatformInit extends Command { protected $signature = 'platform:init {--from-current : Copy the active platform config and theme into the folder}'; protected $description = 'Create the platform folder (optionally from the current config and theme)'; public function handle(): int { $root = platform_path(); $skeleton = base_path('platform.example'); foreach (File::allFiles($skeleton, true) as $file) { $this->copyIfMissing($file->getPathname(), "{$root}/{$file->getRelativePathname()}"); } if ($this->option('from-current')) { $name = (string) config('app.timebank_config'); if (is_file(config_path("{$name}.php"))) { $this->copyIfMissing(config_path("{$name}.php"), "{$root}/config.php"); } else { $this->warn("No config/{$name}.php to copy."); } $id = theme_id(); $theme = ['id' => $id, ...(config("themes.themes.{$id}") ?? [])]; $this->writeIfMissing("{$root}/theme.php", "export($theme) . ";\n"); $this->newLine(); $this->line('Brand files to copy by hand, if this platform has them:'); // The files in use now, after the fallbacks (a theme's own logo, the generated text logo, the default) foreach ([false => 'mail-logo.png', true => 'mail-logo-inverted.png'] as $inverted => $target) { $logo = theme_email_logo_file((bool) $inverted); if (is_file($logo)) { $this->line(' ' . (str_starts_with($logo, base_path() . '/') ? substr($logo, strlen(base_path()) + 1) : $logo) . " -> platform/public/{$target}"); } } if ($dir = theme_logo('favicon_dir')) { $this->line(" public/{$dir}/* -> platform/public/"); } $this->line(' a changed logo: php artisan platform:override ' . (theme_logo('svg_inline') ?: "logos.{$id}")); } $this->newLine(); $this->info("Platform folder: {$root}. Then: php artisan platform:link, php artisan platform:check."); return self::SUCCESS; } private function copyIfMissing(string $from, string $to): void { if (file_exists($to)) { $this->line(" kept {$to}"); return; } File::ensureDirectoryExists(dirname($to)); File::copy($from, $to); $this->line(" created {$to}"); } private function writeIfMissing(string $to, string $content): void { if (file_exists($to)) { $this->line(" kept {$to}"); return; } File::ensureDirectoryExists(dirname($to)); File::put($to, $content); $this->line(" created {$to}"); } /** An array as PHP source with short array syntax, indented like the config files. */ private function export(mixed $value, int $depth = 0): string { if (!is_array($value)) { return var_export($value, true); } $pad = str_repeat(' ', $depth + 1); $isList = array_is_list($value); $lines = array_map( fn ($key, $item) => $pad . ($isList ? '' : var_export($key, true) . ' => ') . $this->export($item, $depth + 1) . ',', array_keys($value), $value ); return $lines ? "[\n" . implode("\n", $lines) . "\n" . str_repeat(' ', $depth) . ']' : '[]'; } }