Weave Code
Weaver
Helps Laravel developers discover, compare, and choose open-source packages. See popularity, security, maintainers, and scores at a glance to make better decisions.
Feedback
Share your thoughts, report bugs, or suggest improvements.
Subject
Message

Mysqldump Php Laravel Package

ifsnop/mysqldump-php

MySQLDump-PHP is a dependency-free, pure-PHP library that recreates mysqldump: back up table structures, data, views, triggers, stored routines and events, with compression, hooks, hex blobs, virtual columns and direct restore. No MySQL binary is required, and output matches the original mysqldump.

View on GitHub
Deep Wiki
Context7

Getting Started

Before you install: license check. The package is GPL-3.0, not MIT. In a Laravel app this matters most if you redistribute the codebase (SaaS is generally fine; shipping a closed-source product that bundles it is a legal conversation, not a technical one). Decide this up front — it is the single biggest reason teams pick spatie/db-dumper + the mysqldump binary instead.

composer require ifsnop/mysqldump-php

Where to look first

  1. The "Constructor and default parameters" block in the README — that array is the API surface. Everything you can configure lives in $dumpSettings.
  2. The Full usage example wiki page for a side-by-side diff against real mysqldump output.
  3. The Dump Settings list — cross-reference each key against the MySQL mysqldump option it mirrors.

Minimal first use case — an Artisan command that dumps the default connection:

// app/Console/Commands/DumpDatabase.php
namespace App\Console\Commands;

use Illuminate\Console\Command;
use Ifsnop\Mysqldump\Mysqldump;

class DumpDatabase extends Command
{
    protected $signature = 'db:dump {--path=storage/app/backups}';

    public function handle(): int
    {
        $cfg = config('database.connections.mysql');

        $dsn = sprintf(
            'mysql:host=%s;port=%s;dbname=%s;charset=%s',
            $cfg['host'], $cfg['port'] ?? 3306, $cfg['database'], $cfg['charset'] ?? 'utf8mb4'
        );

        $file = base_path($this->option('path') . '/dump-' . now()->format('Ymd-His') . '.sql.gz');

        $dump = new Mysqldump($dsn, $cfg['username'], $cfg['password'], [
            'compress'          => Mysqldump::GZIP,
            'single-transaction'=> true,
            'lock-tables'       => false,
            'routines'          => true,
            'events'            => true,
            'add-drop-table'    => true,
            'skip-comments'     => true,
        ]);

        $dump->start($file);
        $this->info("Wrote {$file}");

        return self::SUCCESS;
    }
}

Note you must build the DSN yourself — this library does not read Laravel's database.connections array. Wrap that in a helper or a small factory class; you'll reuse it everywhere.


Implementation Patterns

1. Resolve the connection through a factory, not inline

Laravel's config supports unix_socket, host arrays, read/write splits, and url shorthand — none of which this package understands. Centralize:

// app/Support/MysqldumpFactory.php
class MysqldumpFactory
{
    public static function forConnection(string $name, array $overrides = []): Mysqldump
    {
        $cfg = config("database.connections.$name");

        $dsn = isset($cfg['unix_socket'])
            ? "mysql:unix_socket={$cfg['unix_socket']};dbname={$cfg['database']};charset=" . ($cfg['charset'] ?? 'utf8mb4')
            : sprintf('mysql:host=%s;port=%s;dbname=%s;charset=%s',
                $cfg['host'], $cfg['port'] ?? 3306, $cfg['database'], $cfg['charset'] ?? 'utf8mb4');

        return new Mysqldump($dsn, $cfg['username'], $cfg['password'], array_merge([
            'single-transaction' => true,
            'lock-tables'        => false,
            'add-drop-table'     => true,
            'routines'           => true,
            'events'             => true,
            'hex-blob'           => true,
            'default-character-set' => Mysqldump::UTF8MB4,
        ], $overrides), [
            \PDO::ATTR_PERSISTENT => false, // see Gotchas
        ]);
    }
}

2. Config file for dump profiles

Publish a config/backup.php with named profiles (nightly, anonymized, schema-only, ci-fixture) so the settings array isn't scattered across commands. schema-only is just ['no-data' => true]; ci-fixture is ['no-data' => false, 'include-tables' => ['users','roles','permissions']].

3. Queue it, don't run it in a request

Dumps of a multi-GB database will blow past PHP-FPM's max_execution_time. Push to a job with an explicit $timeout and $tries = 1, and call set_time_limit(0) inside the job. Prefer the database or a dedicated backups queue so a long dump doesn't starve user-facing jobs.

4. Staging/Dev refresh with GDPR-safe anonymization

This is the killer feature and the main reason to choose this package over shelling out — you can't easily transform rows with the CLI.

$dump->setTransformTableRowHook(function (string $table, array $row) {
    return match ($table) {
        'users' => array_merge($row, [
            'email'      => "user{$row['id']}@example.test",
            'name'       => fake()->name(),
            'phone'      => null,
            'remember_token' => null,
        ]),
        'orders' => array_merge($row, ['notes' => '[redacted]']),
        default  => $row,
    };
});

Pair it with exclude-tables for jobs, failed_jobs, cache, cache_locks, sessions, telescope_entries, pulse_* — these are noise and can be huge.

5. Sampling large tables instead of dumping everything

For dev environments, per-table where and limit beats a full dump:

$dump->setTableWheres([
    'orders'      => 'created_at > NOW() - INTERVAL 90 DAY',
    'audit_logs'  => 'created_at > NOW() - INTERVAL 7 DAY',
]);
$dump->setTableLimits([
    'orders'    => 5000,
    'customers' => 1000,
]);

Note the interaction: where and limit combine, and both are applied in addition to the global 'where' setting (the per-table ones override it for that table).

6. Dumping straight to a compressed artifact

compress => Mysqldump::GZIP (needs ext-zlib) or BZIP2 (needs ext-bz2) writes .sql.gz directly — no temp file, no gzip process. For non-local disks, dump to a temp local path and Storage::disk('s3')->putFileAs(...) afterwards; the S3 stream wrapper isn't reliably fopen-able from the library's writer.

7. Restore for CI and local resets

Mysqldump::restore($file, $dsn, $user, $pass) exists precisely for environments with no mysql binary (CI containers, restricted hosting). Good for a php artisan db:refresh-from-dump command in a Docker test stack. Don't use it as a production DR tool — it's a statement-splitter, not a real client.

8. Progress reporting

$dump->setInfoHook(function ($object, $info) {
    if ($object === 'table') {
        Log::info("dumping {$info['name']}", ['rows' => $info['rowCount']]);
    }
});

Per-table granularity only — no row-level progress, so don't build a progress bar on it.

9. Integration with your own backup orchestration

If you already use spatie/laravel-backup, this package is a substitute for its dumper, not a complement. The sensible pattern is: use spatie/laravel-backup for file backups + retention + notification, and register this package as the DB step when the host has no mysqldump/exec() available. Otherwise, let spatie/db-dumper shell out — it's faster and MIT.


Gotchas and Tips

Defaults are not mysqldump defaults. The one that bites hardest: add-drop-table => false. Restoring the resulting dump onto an existing database fails with "table already exists." Set it to true (and consider add-drop-database for full-reset workflows) unless you're deliberately appending.

lock-tables defaults to true and single-transaction defaults to true. On a busy production InnoDB database, LOCK TABLES will stall writes. Explicitly set 'lock-tables' => false and rely on single-transaction. Conversely, if you have MyISAM or non-transactional tables, single-transaction gives you no consistency guarantee — you have to choose, and the library won't warn you.

DEFINER clauses will break restores. Triggers, views, and routines are dumped with DEFINER=olduser@oldhost. Restoring into a different environment fails or creates objects owned by a non-existent user. Set 'skip-definer' => true for anything crossing environments. Also set 'routines' => true and 'events' => true — both default to false, so you will silently lose stored procedures and schedulers.

Views are filtered by include-tables. If you whitelist tables you may accidentally drop views, or vice versa — use the separate include-views key.

Don't use ATTR_PERSISTENT => true in queue workers. The documented PDO default is persistent, which in a long-lived queue:work process means a stale connection after wait_timeout → "MySQL server has gone away" mid-dump. Override PDO::ATTR_PERSISTENT => false and construct a fresh dumper per job.

PDO::MYSQL_ATTR_USE_BUFFERED_QUERY => false is load-bearing. It's what keeps memory flat on large tables, but it means you cannot run another query on that same PDO handle while a table is being dumped. Don't share the connection.

Wrap start() in try/finally and clean up partial files. Exceptions (lost connection, permission error, disk full) leave a truncated .sql.gz behind that looks like a valid backup by filename alone. Either write to *.tmp and rename() on success, or unlink() in catch.

Privileges. SELECT alone is not enough. Views need SHOW VIEW; triggers need TRIGGER; any locked table needs LOCK TABLES. On managed MySQL (RDS, PlanetScale) you often can't grant TRIGGER — then set 'skip-triggers' => true rather than crashing the whole backup.

hex-blob => true is the default — it's safer and faster than escaping, but roughly doubles the byte size of binary columns in the output. On a media-heavy table this can turn a 2 GB dump into 4 GB. exclude-tables those, or set the option off if you know your blobs are ASCII-safe.

Reproducible dumps. For checksums/diffing, set 'skip-dump-date' => true and 'skip-comments' => true. The dump timestamp line otherwise changes every run.

where strings are raw SQL. They are interpolated straight into the SELECT. Never accept them from user input. Also validate that a per-table where references real columns — a typo produces a confusing SQL error mid-dump, after some tables have already been written.

no-data is polymorphic: true (skip all data), an array of literal table names, or regex patterns. The regex behavior is underdocumented and anchors are not implied — /^log_/ works, log_ may match more than you expect. Same warning for exclude-tables.

UTF8MB4BZIP2 in the README is odd naming, not a bug you introduced — the constant for utf8mb4 with compression. Just use a literal 'utf8mb4' string if it confuses your team.

default-character-set / DSN charset mismatch silently mangles emoji. Set the charset in both the PDO DSN and default-character-set. Laravel's charset config key only reaches the first if you wire it up yourself.

The README's "Requirements: PHP 5.3+" is stale. Check composer.json in the installed version for the real constraint before pinning on older infrastructure. The 2.x line requires a DSN (no more discrete host/db arguments).

Extension points beyond the hooks: subclass Mysqldump and override the protected methods (column type mapping, the _table* helpers, getDumpSettings()). This is how you'd add, say, SET @@SESSION.time_zone handling or per-column masking. Treat those protected methods as unstable — they're not a documented contract and refactors across minor versions have broken them.

Missing versus the real mysqldump: no --set-gtid-purged, no --column-statistics, no parallel-table dumping, no built-in retention/verification. For very large databases (100 GB+) on hosts where the binary is available, the CLI will be meaningfully faster — this library's advantage is portability and hooks, not throughput.

Debugging tip: run once with compress => Mysqldump::NONE and skip-comments => false to a local file, then diff against mysqldump output for the same DB. The README's test suite does exactly this, and it's the fastest way to isolate whether a restore failure is the library's output or your restore environment.

Weaver

How can I help you explore Laravel packages today?

Conversation history is not saved when not logged in.
Prompt
Add packages to context
No packages found.
ably/pubsub-server
mstfkhazaal/defender
mfonte/base62x
karnoweb/metapilot
tobya/bcsapiwrapper
swalker2/cpanel
vish4395/laravel-translation-manager
morningtrain/economic
teksite/systeminfo-laravel
atomcms/voting
run6/weather
siokas/laravelembeddirectives
adamlien/pkuuids
gilsonsouza/lara-doc-generator
manelgavalda/enrollment_mobile
unabomber/timezone
delatbabel/contacts
astritzeqiri/g-recaptcha
kevinongko/laravel-genode
developeruz/laravel-analytics