BackupService
in package
uses
LocatorAwareTrait
Database-agnostic backup and restore service.
Exports all application tables via the ORM as JSON, compresses with gzip, and encrypts with AES-256-GCM. Restore reverses the process.
Table of Contents
Constants
- CIPHER = 'aes-256-gcm'
- EXCLUDED_TABLES = ['queued_jobs', 'queue_processes', 'backups']
- Tables excluded from backup (transient or migration-tracking data).
- IV_LENGTH = 12
- PBKDF2_ALGO = 'sha256'
- PBKDF2_ITERATIONS = 100000
- SALT_LENGTH = 16
- TAG_LENGTH = 16
Methods
- export() : array{data: string, meta: array}
- Export all application tables to an encrypted backup file.
- import() : array{table_count: int, row_count: int}
- Import (restore) from an encrypted backup.
- clearApplicationCachesAfterRestore() : void
- Clear Cake cache pools after successful restore so runtime state matches DB.
- decrypt() : string
- Decrypt data encrypted by encrypt().
- deriveKey() : string
- Derive a 256-bit key from a passphrase using PBKDF2.
- dropPostgresForeignKeys() : array<int, array{table: string, name: string, definition: string}>
- Drop FK constraints for Postgres tables and return definitions for later re-add.
- encrypt() : string
- Encrypt data with AES-256-GCM using a PBKDF2-derived key.
- fetchPostgresForeignKeyDefinition() : string|null
- Fetch postgres foreign key definition.
- getInsertBatchSize() : int
- Get insert batch size.
- insertBatchRows() : void
- isIntegerLikeType() : bool
- Check if integer like type.
- isNumericColumnType() : bool
- Check if numeric column type.
- normalizeBooleanForPostgres() : mixed
- Normalize Postgres boolean column values from mixed JSON payload forms.
- normalizeColumnScalarForMysql() : mixed
- Coerce scalar values into DB-safe representations for MySQL inserts.
- normalizeComplexValueForMysql() : array{converted: bool, value: mixed}
- Coerce array/object values for MySQL inserts.
- normalizeEmptyStringForMysql() : array{converted: bool, value: mixed}
- Coerce empty-string values for MySQL numeric/boolean/temporal columns.
- normalizeNullForMysql() : array{converted: bool, value: mixed}
- Coerce nulls for non-nullable MySQL columns.
- normalizeRowForInsert() : array<string, mixed>
- Normalize row values for MySQL inserts, coercing temporal values from ISO-8601 JSON forms to DB-friendly SQL literal formats.
- normalizeTemporalValueForMysql() : string|null
- Convert ISO-8601 temporal strings to MySQL-compatible temporal formats.
- reportProgress() : void
- restorePostgresForeignKeys() : int
- Re-add previously dropped Postgres FK constraints.
Constants
CIPHER
private
mixed
CIPHER
= 'aes-256-gcm'
EXCLUDED_TABLES
Tables excluded from backup (transient or migration-tracking data).
private
mixed
EXCLUDED_TABLES
= ['queued_jobs', 'queue_processes', 'backups']
IV_LENGTH
private
mixed
IV_LENGTH
= 12
PBKDF2_ALGO
private
mixed
PBKDF2_ALGO
= 'sha256'
PBKDF2_ITERATIONS
private
mixed
PBKDF2_ITERATIONS
= 100000
SALT_LENGTH
private
mixed
SALT_LENGTH
= 16
TAG_LENGTH
private
mixed
TAG_LENGTH
= 16
Methods
export()
Export all application tables to an encrypted backup file.
public
export(string $encryptionKey) : array{data: string, meta: array}
Parameters
- $encryptionKey : string
-
User-provided encryption key
Return values
array{data: string, meta: array} —Encrypted bytes and metadata
import()
Import (restore) from an encrypted backup.
public
import(string $encryptedData, string $encryptionKey[, callable|null $progressReporter = null ]) : array{table_count: int, row_count: int}
Parameters
- $encryptedData : string
-
Raw encrypted backup bytes
- $encryptionKey : string
-
User-provided encryption key
- $progressReporter : callable|null = null
Return values
array{table_count: int, row_count: int} —Import statistics
clearApplicationCachesAfterRestore()
Clear Cake cache pools after successful restore so runtime state matches DB.
private
clearApplicationCachesAfterRestore() : void
decrypt()
Decrypt data encrypted by encrypt().
private
decrypt(string $data, string $passphrase) : string
Parameters
- $data : string
- $passphrase : string
Return values
stringderiveKey()
Derive a 256-bit key from a passphrase using PBKDF2.
private
deriveKey(string $passphrase, string $salt) : string
Parameters
- $passphrase : string
- $salt : string
Return values
stringdropPostgresForeignKeys()
Drop FK constraints for Postgres tables and return definitions for later re-add.
private
dropPostgresForeignKeys(mixed $connection, mixed $schemaCollection, mixed $driver, array<int, string> $tableNames) : array<int, array{table: string, name: string, definition: string}>
Parameters
- $connection : mixed
- $schemaCollection : mixed
- $driver : mixed
- $tableNames : array<int, string>
Return values
array<int, array{table: string, name: string, definition: string}>encrypt()
Encrypt data with AES-256-GCM using a PBKDF2-derived key.
private
encrypt(string $data, string $passphrase) : string
Output format: salt(16) + iv(12) + tag(16) + ciphertext
Parameters
- $data : string
- $passphrase : string
Return values
stringfetchPostgresForeignKeyDefinition()
Fetch postgres foreign key definition.
private
fetchPostgresForeignKeyDefinition(mixed $connection, string $tableName, string $constraintName) : string|null
Parameters
- $connection : mixed
- $tableName : string
- $constraintName : string
Return values
string|nullgetInsertBatchSize()
Get insert batch size.
private
getInsertBatchSize(int $columnCount, bool $isPostgres) : int
Parameters
- $columnCount : int
- $isPostgres : bool
Return values
intinsertBatchRows()
private
insertBatchRows(mixed $connection, mixed $driver, string $quotedTable, array<int, string> $columns, array<int, array<string, mixed>> $batch, TableSchemaInterface $tableSchema, bool $isPostgres) : void
Parameters
- $connection : mixed
- $driver : mixed
- $quotedTable : string
- $columns : array<int, string>
- $batch : array<int, array<string, mixed>>
- $tableSchema : TableSchemaInterface
- $isPostgres : bool
isIntegerLikeType()
Check if integer like type.
private
isIntegerLikeType(string $columnType) : bool
Parameters
- $columnType : string
Return values
boolisNumericColumnType()
Check if numeric column type.
private
isNumericColumnType(string $columnType) : bool
Parameters
- $columnType : string
Return values
boolnormalizeBooleanForPostgres()
Normalize Postgres boolean column values from mixed JSON payload forms.
private
normalizeBooleanForPostgres(TableSchemaInterface $tableSchema, string $column, mixed $value) : mixed
Parameters
- $tableSchema : TableSchemaInterface
- $column : string
- $value : mixed
normalizeColumnScalarForMysql()
Coerce scalar values into DB-safe representations for MySQL inserts.
private
normalizeColumnScalarForMysql(mixed $value, string $columnType) : mixed
Parameters
- $value : mixed
- $columnType : string
normalizeComplexValueForMysql()
Coerce array/object values for MySQL inserts.
private
normalizeComplexValueForMysql(mixed $value, string $columnType) : array{converted: bool, value: mixed}
Parameters
- $value : mixed
- $columnType : string
Return values
array{converted: bool, value: mixed}normalizeEmptyStringForMysql()
Coerce empty-string values for MySQL numeric/boolean/temporal columns.
private
normalizeEmptyStringForMysql(TableSchemaInterface $tableSchema, string $column, string $columnType) : array{converted: bool, value: mixed}
Parameters
- $tableSchema : TableSchemaInterface
- $column : string
- $columnType : string
Return values
array{converted: bool, value: mixed}normalizeNullForMysql()
Coerce nulls for non-nullable MySQL columns.
private
normalizeNullForMysql(TableSchemaInterface $tableSchema, string $column, string $columnType) : array{converted: bool, value: mixed}
Parameters
- $tableSchema : TableSchemaInterface
- $column : string
- $columnType : string
Return values
array{converted: bool, value: mixed}normalizeRowForInsert()
Normalize row values for MySQL inserts, coercing temporal values from ISO-8601 JSON forms to DB-friendly SQL literal formats.
private
normalizeRowForInsert(array<string, mixed> $row, array<int, string> $columns, TableSchemaInterface $tableSchema, bool $isPostgres) : array<string, mixed>
Parameters
- $row : array<string, mixed>
- $columns : array<int, string>
- $tableSchema : TableSchemaInterface
- $isPostgres : bool
Return values
array<string, mixed>normalizeTemporalValueForMysql()
Convert ISO-8601 temporal strings to MySQL-compatible temporal formats.
private
normalizeTemporalValueForMysql(string $value, string $columnType) : string|null
Parameters
- $value : string
- $columnType : string
Return values
string|nullreportProgress()
private
reportProgress(callable(array<string, mixed>): void|null $progressReporter, string $phase, string $message[, array<string, mixed> $context = [] ]) : void
Parameters
- $progressReporter : callable(array<string, mixed>): void|null
- $phase : string
- $message : string
- $context : array<string, mixed> = []
restorePostgresForeignKeys()
Re-add previously dropped Postgres FK constraints.
private
restorePostgresForeignKeys(mixed $connection, mixed $driver, array<int, array{table: string, name: string, definition: string}> $droppedForeignKeys) : int
Parameters
- $connection : mixed
- $driver : mixed
- $droppedForeignKeys : array<int, array{table: string, name: string, definition: string}>