hmillet/backup-commands-bundle
hmillet/backup-commands-bundle provides console commands for creating and managing backups in PHP/Symfony applications, with configurable targets and retention so routine backups can be scheduled and automated from the CLI.
First, calibrate expectations. hmillet/backup-commands-bundle is a Symfony bundle, not a Laravel package. There is no service provider, no config/backup.php, no facade, no auto-discovery. It has one star, zero dependents, and until v3.0 its last release was March 2018. Treat it as a reference implementation you may vendor or re-implement, not as a dependency you composer require and forget about.
What v3.0 changes — and what it doesn't. The release notes list exactly two items: replace deprecated Dialog Helper and increase dump timeout. Read those carefully:
DialogHelper was deprecated in Symfony 2.x and removed in Symfony 3.0, replaced by QuestionHelper. Touching it means the bundle's command layer has finally been dragged forward to a Symfony version where the old helper no longer exists. That is real maintenance — but it is also a major version bump for a two-line changelog, and the breaking part of a v3.0 is almost never documented in the bullet list. Assume the Command classes, their constructor signatures, or the Symfony version constraint in composer.json moved. Check composer.json before anything else.Neither change makes this a Laravel package, and neither makes it safe to drop into a new project. The verdict below is unchanged; the maintenance signal is slightly less grim.
Minimal path to a first working backup:
composer.json — the Symfony and PHP constraints are the single most useful thing in the repo, and v3.0 has almost certainly changed them.Command/ — the console commands are the public surface. Their names, options, and arguments tell you what the bundle actually does. Compare against the pre-3.0 tag if you can; that diff is the breaking-change list.Resources/config/services.yml (or config/) — shows the service IDs and which classes actually do the dumping/archiving.DependencyInjection/Configuration.php — the config tree is the real "API". Every knob the bundle supports is declared here. Look for a timeout key after v3.0; if there isn't one, the increase is hardcoded.mysqldump of the app database plus a tar of storage/app/public, written to a path outside the webroot. That is the 80% case and the one worth validating first. Deliberately test it against a database large enough to have triggered the old timeout.Application, or (b) instantiates the bundle's dumper/archiver services directly with your config. Option (b) is cleaner and is what most teams end up doing — and it is also the option that v3.0's command-layer churn cannot break.If you are already on Laravel, ask yourself mid-step 1 whether spatie/laravel-backup solves the same problem. It almost certainly does, with a maintained codebase and Laravel-native scheduling. The rest of this assessment assumes you have a reason to use this bundle anyway (legacy compatibility, a specific dumper behavior, curiosity).
Pattern 1 — Embedded Symfony console. The lowest-friction bridge. Register the bundle's commands on a standalone Symfony\Component\Console\Application inside an Artisan command:
// app/Console/Commands/RunBackup.php
$app = new \Symfony\Component\Console\Application();
$app->add(new \Hmillet\BackupCommandsBundle\Command\BackupCommand(/* deps */));
$app->setAutoExit(false);
$input = new \Symfony\Component\Console\Input\ArrayInput([
'command' => 'backup:...',
'--option' => $value,
]);
$app->run($input, new \Symfony\Component\Console\Output\ConsoleOutput());
This works but is clunky: you lose Artisan's exit-code handling, output formatting, and signal handling unless you wire them. v3.0 makes this the riskiest pattern in the document — the release specifically rewrote the interaction layer (DialogHelper → QuestionHelper), so command construction and any interact() behavior you depended on may have shifted. If you must use this pattern, pin the exact version and re-run your integration tests on every bump.
Pattern 2 — Direct service construction (preferred, and now more clearly so). Read services.yml, find the class that performs the dump, and instantiate it in a Laravel service provider with arguments from config/backup.php. You keep the bundle for its dumper logic and throw away its Symfony plumbing. This is the pattern that survives upgrades — and v3.0 is a good demonstration of why: a change to the console layer that would force retesting under Pattern 1 is invisible here. If the dumper constructor takes a timeout argument, pass your own value rather than inheriting whatever v3.0 chose.
Pattern 3 — Config translation layer. The bundle's Configuration.php defines a tree; mirror it in a Laravel config file and write a small adapter that maps your flat array to the constructor arguments the services expect. Do this once, test it, and never touch it again. Without it, every environment (staging, prod, local) becomes a hand-edited YAML file, which is exactly what you left Symfony to avoid. When v3.0 adds or renames a key, this adapter is the one file you fix instead of grepping your Ansible repo.
Day-to-day workflows that make sense:
app/Console/Kernel.php → $schedule->command('backup:run')->dailyAt('02:00'). Keep the schedule in Laravel; the bundle should only ever be the "do the work" layer.migrate wrapper before php artisan migrate on production. This is where a working, verified backup command earns its keep.Integration tips:
$this->app->singleton(...) in a dedicated BackupServiceProvider, not in AppServiceProvider. Binding them yourself means a Symfony-version bump inside the bundle cannot rewire your app.Process or Symfony\Component\Process to invoke mysqldump/pg_dump yourself if the bundle's dumper is too coupled. You are allowed to use the bundle for its archive/file logic only.env() and pass in at construction.composer.json and in a comment in your adapter. The jump from "dormant since 2018" to "shipping majors" means you can no longer assume the API is frozen.3.0 tag is where undocumented breaking changes live. Read the actual diff (git diff 2.x..3.0), not the release notes. Pay attention to composer.json constraints, Command::configure() signatures, and constructor arguments.ContainerInterface and ParameterBag. You cannot pass app() where a Symfony container is expected without an adapter. If a class is typed against Symfony\Component\DependencyInjection\ContainerInterface, instantiate it manually rather than trying to make Laravel pretend to be Symfony. This is unchanged by v3.0 and is the main reason Pattern 2 wins.Config component merges bundle config with defaults and throws on unknown keys. If you feed it a Laravel array with extra keys, you get exceptions that look like service errors. Validate against the tree first. After v3.0, re-check the tree — a version bump that touches the command layer often renames an option alongside it.Symfony\...\Output\OutputInterface; Artisan's output is a different class. Capture output with a BufferedOutput and re-emit it through $this->info(), or pipe to Log. Do not assume $this->line() inside a Symfony command works. The DialogHelper → QuestionHelper swap in v3.0 also means any interactive prompt behavior you scripted around needs re-testing; better still, run non-interactively (--no-interaction) and never depend on prompt semantics at all.dump returning non-zero exit codes, partial files on disk-full, truncated dumps after a timeout kill, and empty archives are all classic. After every backup, assert file size > 0, assert size is within a plausible range of the previous run, and (for SQL dumps) that the file ends with a plausible terminator. Wire an alert if the nightly file is missing or suspiciously small.ps; prefer a ~/.my.cnf or env-var approach.private or final, fork the class into app/Support/ rather than fighting the container. Forking is also a legitimate response to an unwanted v3.0 change: vendor the dumper, own it, drop the bundle.spatie/laravel-backup, or write ~100 lines wrapping mysqldump + tar + an S3 upload. A 3.0 that replaces a helper removed in Symfony 3.0 and bumps a timeout is maintenance catching up on a decade of drift, not a signal that the project is actively developed. Reserve this bundle for the case where you are maintaining a Symfony app that already depends on it, or porting a specific behavior you cannot reproduce cheaply.How can I help you explore Laravel packages today?