Skip to content

Commit

Permalink
Merge pull request #95 from mirko-pagliai/develop
Browse files Browse the repository at this point in the history
Develop
  • Loading branch information
mirko-pagliai authored Jul 18, 2023
2 parents 7f52c20 + febae72 commit 527328a
Show file tree
Hide file tree
Showing 13 changed files with 107 additions and 60 deletions.
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
# 2.x branch
## 2.12 branch
### 2.12.1
* fixed a little bug in the `bootstrap.php` file;
* the `Exceptionist` class provided by me-tools is no longer used (in anticipation of an upcoming deprecation).

### 2.12.0
* added `AbstractBackupUtility::timeout()` method, so now `BackupExport`/`BackupImport` utilities have a method to set the
timeout for shell commands at runtime. Added `--timeout` option (short: `-t`) for `ExportCommand`/`ImportCommand`;
Expand Down
2 changes: 1 addition & 1 deletion config/bootstrap.php
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@
'DatabaseBackup.chmod' => 0664,
'DatabaseBackup.connection' => 'default',
'DatabaseBackup.processTimeout' => 60,
'DatabaseBackup.target' => ROOT . 'backups',
'DatabaseBackup.target' => rtrim(ROOT, DS) . DS . 'backups',
'DatabaseBackup.mysql.export' => '{{BINARY}} --defaults-file={{AUTH_FILE}} {{DB_NAME}}',
'DatabaseBackup.mysql.import' => '{{BINARY}} --defaults-extra-file={{AUTH_FILE}} {{DB_NAME}}',
'DatabaseBackup.postgres.export' => '{{BINARY}} --format=c -b --dbname=\'postgresql://{{DB_USER}}{{DB_PASSWORD}}@{{DB_HOST}}/{{DB_NAME}}\'',
Expand Down
6 changes: 4 additions & 2 deletions src/Command/ExportCommand.php
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,6 @@
use DatabaseBackup\Console\Command;
use DatabaseBackup\Utility\BackupExport;
use Exception;
use Tools\Exceptionist;

/**
* Exports a database backup
Expand Down Expand Up @@ -98,7 +97,10 @@ public function execute(Arguments $args, ConsoleIo $io): void

/** @var string $file */
$file = $BackupExport->export();
Exceptionist::isTrue($file, __d('database_backup', 'The `{0}` event stopped the operation', 'Backup.beforeExport'));
if (!$file) {
$io->error(__d('database_backup', 'The `{0}` event stopped the operation', 'Backup.beforeExport'));
$this->abort();
}
$io->success(__d('database_backup', 'Backup `{0}` has been exported', rtr($file)));

//Sends via email and/or rotates
Expand Down
8 changes: 5 additions & 3 deletions src/Command/ImportCommand.php
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,6 @@
use DatabaseBackup\Console\Command;
use DatabaseBackup\Utility\BackupImport;
use Exception;
use Tools\Exceptionist;

/**
* Imports a database backup
Expand Down Expand Up @@ -53,7 +52,7 @@ protected function buildOptionParser(ConsoleOptionParser $parser): ConsoleOption
* @param \Cake\Console\Arguments $args The command arguments
* @param \Cake\Console\ConsoleIo $io The console io
* @return void
* @throws \Cake\Console\Exception\StopException|\ReflectionException
* @throws \ReflectionException
*/
public function execute(Arguments $args, ConsoleIo $io): void
{
Expand All @@ -71,7 +70,10 @@ public function execute(Arguments $args, ConsoleIo $io): void

/** @var string $file */
$file = $BackupImport->import();
Exceptionist::isTrue($file, __d('database_backup', 'The `{0}` event stopped the operation', 'Backup.beforeImport'));
if (!$file) {
$io->error(__d('database_backup', 'The `{0}` event stopped the operation', 'Backup.beforeImport'));
$this->abort();
}
$io->success(__d('database_backup', 'Backup `{0}` has been imported', rtr($file)));
} catch (Exception $e) {
$io->error($e->getMessage());
Expand Down
24 changes: 14 additions & 10 deletions src/Driver/AbstractDriver.php
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@
use Cake\Event\EventDispatcherTrait;
use Cake\Event\EventListenerInterface;
use DatabaseBackup\BackupTrait;
use Tools\Exceptionist;
use LogicException;

/**
* Represents a driver containing all methods to export/import database backups according to the connection
Expand Down Expand Up @@ -64,13 +64,14 @@ final public function implementedEvents(): array
* have the final executables, including compression.
* @param string $type Type or the request operation (`export` or `import`)
* @return string
* @throws \Tools\Exception\NotInArrayException
* @throws \LogicException
* @throws \ReflectionException
* @throws \ErrorException
*/
private function getExecutable(string $type): string
{
Exceptionist::inArray($type, ['export', 'import']);
if (!in_array($type, ['export', 'import'])) {
throw new LogicException(__d('database_backup', '`$type` parameter should be `export` or `import`'));
}
$driverName = strtolower(self::getDriverName());
$replacements = [
'{{BINARY}}' => escapeshellarg($this->getBinary(DATABASE_BACKUP_EXECUTABLES[$driverName][$type])),
Expand All @@ -89,9 +90,8 @@ private function getExecutable(string $type): string
* Gets the executable command to export the database, with compression if requested
* @param string $filename Filename where you want to export the database
* @return string
* @throws \Tools\Exception\NotInArrayException
* @throws \LogicException
* @throws \ReflectionException
* @throws \ErrorException
*/
public function getExportExecutable(string $filename): string
{
Expand All @@ -108,9 +108,8 @@ public function getExportExecutable(string $filename): string
* Gets the executable command to import the database, with compression if requested
* @param string $filename Filename from which you want to import the database
* @return string
* @throws \Tools\Exception\NotInArrayException
* @throws \LogicException
* @throws \ReflectionException
* @throws \ErrorException
*/
public function getImportExecutable(string $filename): string
{
Expand Down Expand Up @@ -165,11 +164,16 @@ public function beforeImport(): bool
* Gets a binary path
* @param string $name Binary name
* @return string
* @throws \ErrorException
* @throws \LogicException
*/
public function getBinary(string $name): string
{
return Exceptionist::isTrue(Configure::read('DatabaseBackup.binaries.' . $name), 'Binary for `' . $name . '` could not be found. You have to set its path manually');
$binary = Configure::read('DatabaseBackup.binaries.' . $name);
if (!$binary) {
throw new LogicException(__d('database_backup', 'Binary for `{0}` could not be found. You have to set its path manually', $name));
}

return $binary;
}

/**
Expand Down
8 changes: 5 additions & 3 deletions src/Utility/AbstractBackupUtility.php
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,6 @@
use DatabaseBackup\Driver\AbstractDriver;
use LogicException;
use Symfony\Component\Process\Process;
use Tools\Exceptionist;

/**
* AbstractBackupUtility.
Expand Down Expand Up @@ -93,7 +92,8 @@ public function timeout(int $timeout)
/**
* Gets the driver instance
* @return \DatabaseBackup\Driver\AbstractDriver A driver instance
* @throws \ErrorException|\ReflectionException
* @throws \LogicException
* @throws \ReflectionException
* @since 2.0.0
*/
public function getDriver(): AbstractDriver
Expand All @@ -102,7 +102,9 @@ public function getDriver(): AbstractDriver
$name = $this->getDriverName();
/** @var class-string<\DatabaseBackup\Driver\AbstractDriver> $className */
$className = App::classname('DatabaseBackup.' . $name, 'Driver');
Exceptionist::isTrue($className, __d('database_backup', 'The `{0}` driver does not exist', $name));
if (!$className) {
throw new LogicException(__d('database_backup', 'The `{0}` driver does not exist', $name));
}

$this->Driver = new $className($this->getConnection());
}
Expand Down
31 changes: 20 additions & 11 deletions src/Utility/BackupExport.php
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@
namespace DatabaseBackup\Utility;

use Cake\Core\Configure;
use Tools\Exceptionist;
use LogicException;
use Tools\Filesystem;

/**
Expand Down Expand Up @@ -68,7 +68,7 @@ class BackupExport extends AbstractBackupUtility
* @param string|null $compression Compression type name
* @return $this
* @see https://github.com/mirko-pagliai/cakephp-database-backup/wiki/How-to-use-the-BackupExport-utility#compression
* @throws \ErrorException
* @throws \LogicException
* @noinspection PhpMissingReturnTypeInspection
*/
public function compression(?string $compression)
Expand All @@ -77,7 +77,9 @@ public function compression(?string $compression)

if ($compression) {
$this->extension = (string)array_search($compression, $this->getValidCompressions());
Exceptionist::isTrue($this->extension, __d('database_backup', 'Invalid compression type'));
if (!$this->extension) {
throw new LogicException(__d('database_backup', 'Invalid compression type'));
}
}
$this->compression = $compression;

Expand All @@ -91,7 +93,7 @@ public function compression(?string $compression)
* @param string $filename Filename. It can be an absolute path and may contain patterns
* @return $this
* @see https://github.com/mirko-pagliai/cakephp-database-backup/wiki/How-to-use-the-BackupExport-utility#filename
* @throws \ErrorException|\Tools\Exception\NotWritableException
* @throws \LogicException
* @noinspection PhpMissingReturnTypeInspection
*/
public function filename(string $filename)
Expand All @@ -107,11 +109,15 @@ public function filename(string $filename)
], $filename);

$filename = $this->getAbsolutePath($filename);
Exceptionist::isWritable(dirname($filename));
Exceptionist::isTrue(!file_exists($filename), __d('database_backup', 'File `{0}` already exists', $filename));

//Checks for extension
Exceptionist::isTrue($this->getExtension($filename), __d('database_backup', 'Invalid `{0}` file extension', pathinfo($filename, PATHINFO_EXTENSION)));
if (!is_writable(dirname($filename))) {
throw new LogicException(__d('database_backup', 'File or directory `' . dirname($filename) . '` is not writable'));
}
if (file_exists($filename)) {
throw new LogicException(__d('database_backup', 'File `{0}` already exists', $filename));
}
if (!$this->getExtension($filename)) {
throw new LogicException(__d('database_backup', 'Invalid `{0}` file extension', pathinfo($filename, PATHINFO_EXTENSION)));
}

//Sets the compression
$this->compression($this->getCompression($filename));
Expand Down Expand Up @@ -156,7 +162,8 @@ public function send(?string $recipient = null)
* - `Backup.beforeExport`: will be triggered before export;
* - `Backup.afterExport`: will be triggered after export.
* @return string|false Filename path on success or `false` if the `Backup.beforeExport` event is stopped
* @throws \Exception
* @throws \LogicException
* @throws \ReflectionException
* @see \DatabaseBackup\Driver\AbstractDriver::afterExport()
* @see \DatabaseBackup\Driver\AbstractDriver::beforeExport()
* @see https://github.com/mirko-pagliai/cakephp-database-backup/wiki/How-to-use-the-BackupExport-utility#export
Expand All @@ -180,7 +187,9 @@ public function export()

//Exports
$Process = $this->getProcess($this->getDriver()->getExportExecutable($filename));
Exceptionist::isTrue($Process->isSuccessful(), __d('database_backup', 'Export failed with error message: `{0}`', rtrim($Process->getErrorOutput())));
if (!$Process->isSuccessful()) {
throw new LogicException(__d('database_backup', 'Export failed with error message: `{0}`', rtrim($Process->getErrorOutput())));
}
Filesystem::instance()->chmod($filename, Configure::read('DatabaseBackup.chmod'));

//Dispatches the `Backup.afterExport` event implemented by the driver
Expand Down
25 changes: 17 additions & 8 deletions src/Utility/BackupImport.php
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@
*/
namespace DatabaseBackup\Utility;

use Tools\Exceptionist;
use LogicException;

/**
* Utility to import databases
Expand All @@ -27,15 +27,19 @@ class BackupImport extends AbstractBackupUtility
* @param string $filename Filename. It can be an absolute path
* @return $this
* @see https://github.com/mirko-pagliai/cakephp-database-backup/wiki/How-to-use-the-BackupImport-utility#filename
* @throws \ErrorException|\Tools\Exception\NotReadableException
* @throws \LogicException
* @noinspection PhpMissingReturnTypeInspection
*/
public function filename(string $filename)
{
$filename = Exceptionist::isReadable($this->getAbsolutePath($filename));
$filename = $this->getAbsolutePath($filename);
if (!is_readable($filename)) {
throw new LogicException(__d('database_backup', 'File or directory `' . $filename . '` is not readable'));
}

//Checks for extension
Exceptionist::isTrue($this->getExtension($filename), __d('database_backup', 'Invalid file extension'));
if (!$this->getExtension($filename)) {
throw new LogicException(__d('database_backup', 'Invalid file extension'));
}

$this->filename = $filename;

Expand All @@ -49,14 +53,17 @@ public function filename(string $filename)
* - `Backup.beforeImport`: will be triggered before import;
* - `Backup.afterImport`: will be triggered after import.
* @return string|false Filename path on success or `false` if the `Backup.beforeImport` event is stopped
* @throws \ErrorException|\ReflectionException
* @throws \LogicException
* @throws \ReflectionException
* @see \DatabaseBackup\Driver\AbstractDriver::afterImport()
* @see \DatabaseBackup\Driver\AbstractDriver::beforeImport()
* @see https://github.com/mirko-pagliai/cakephp-database-backup/wiki/How-to-use-the-BackupImport-utility#import
*/
public function import()
{
Exceptionist::isTrue(!empty($this->filename), __d('database_backup', 'You must first set the filename'));
if (empty($this->filename)) {
throw new LogicException(__d('database_backup', 'You must first set the filename'));
}

//This allows the filename to be set again with a next call of this method
$filename = $this->filename;
Expand All @@ -70,7 +77,9 @@ public function import()

//Imports
$Process = $this->getProcess($this->getDriver()->getImportExecutable($filename));
Exceptionist::isTrue($Process->isSuccessful(), __d('database_backup', 'Import failed with error message: `{0}`', rtrim($Process->getErrorOutput())));
if (!$Process->isSuccessful()) {
throw new LogicException(__d('database_backup', 'Import failed with error message: `{0}`', rtrim($Process->getErrorOutput())));
}

//Dispatches the `Backup.afterImport` event implemented by the driver
$this->getDriver()->dispatchEvent('Backup.afterImport');
Expand Down
29 changes: 18 additions & 11 deletions src/Utility/BackupManager.php
Original file line number Diff line number Diff line change
Expand Up @@ -21,9 +21,9 @@
use Cake\Mailer\Mailer;
use Cake\ORM\Entity;
use DatabaseBackup\BackupTrait;
use LogicException;
use Symfony\Component\Finder\Finder;
use Symfony\Component\Finder\SplFileInfo;
use Tools\Exceptionist;
use Tools\Filesystem;

/**
Expand All @@ -38,11 +38,14 @@ class BackupManager
* @param string $filename Backup filename you want to delete. The path can be relative to the backup directory
* @return string Deleted backup file
* @see https://github.com/mirko-pagliai/cakephp-database-backup/wiki/How-to-use-the-BackupManager-utility#delete
* @throws \Tools\Exception\NotWritableException
* @throws \LogicException
*/
public static function delete(string $filename): string
{
$filename = Exceptionist::isWritable(self::getAbsolutePath($filename));
$filename = self::getAbsolutePath($filename);
if (!is_writable($filename)) {
throw new LogicException(__d('database_backup', 'File or directory `' . $filename . '` is not writable'));
}
Filesystem::instance()->remove($filename);

return $filename;
Expand Down Expand Up @@ -87,12 +90,13 @@ public static function index(): CollectionInterface
* @param int $rotate Number of backups that you want to keep
* @return array<\Cake\ORM\Entity> Array of deleted files
* @see https://github.com/mirko-pagliai/cakephp-database-backup/wiki/How-to-use-the-BackupManager-utility#rotate
* @throws \ErrorException
* @noinspection PhpDocRedundantThrowsInspection
* @throws \LogicException
*/
public static function rotate(int $rotate): array
{
Exceptionist::isPositive($rotate, __d('database_backup', 'Invalid rotate value'));
if (!is_positive($rotate)) {
throw new LogicException(__d('database_backup', 'Invalid rotate value'));
}
$backupsToBeDeleted = self::index()->skip($rotate);
array_map([__CLASS__, 'delete'], $backupsToBeDeleted->extract('filename')->toList());

Expand All @@ -105,26 +109,29 @@ public static function rotate(int $rotate): array
* @param string $recipient Recipient's email address
* @return \Cake\Mailer\Mailer
* @since 1.1.0
* @throws \Tools\Exception\NotReadableException
* @throws \LogicException
*/
protected static function getEmailInstance(string $backup, string $recipient): Mailer
{
$file = Exceptionist::isReadable(self::getAbsolutePath($backup));
$filename = self::getAbsolutePath($backup);
if (!is_readable($filename)) {
throw new LogicException(__d('database_backup', 'File or directory `' . $filename . '` is not readable'));
}
$server = env('SERVER_NAME', 'localhost');

return (new Mailer())
->setFrom(Configure::readOrFail('DatabaseBackup.mailSender'))
->setTo($recipient)
->setSubject(__d('database_backup', 'Database backup {0} from {1}', basename($file), $server))
->setAttachments([basename($file) => compact('file') + ['mimetype' => mime_content_type($file)]]);
->setSubject(__d('database_backup', 'Database backup {0} from {1}', basename($filename), $server))
->setAttachments([basename($filename) => ['file' => $filename, 'mimetype' => mime_content_type($filename)]]);
}

/**
* Sends a backup file via email
* @param string $filename Backup filename you want to send via email. The path can be relative to the backup directory
* @param string $recipient Recipient's email address
* @return array{headers: string, message: string}
* @throws \Tools\Exception\NotReadableException
* @throws \LogicException
* @since 1.1.0
*/
public static function send(string $filename, string $recipient): array
Expand Down
2 changes: 1 addition & 1 deletion tests/TestCase/Utility/BackupExportAndImportTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,7 @@ public function setUp(): void
/** @var \Cake\Database\Connection $connection */
$connection = $this->getConnection('test');
foreach (['Articles', 'Comments'] as $name) {
$this->{$name} ??= $this->getTable($name, compact('connection'));
$this->{$name} ??= $this->fetchTable($name, compact('connection'));
}
}

Expand Down
Loading

0 comments on commit 527328a

Please sign in to comment.