Completed
Pull Request — master (#440)
by
unknown
02:21 queued 19s
created

BackupJob   A

Complexity

Total Complexity 24

Size/Duplication

Total Lines 230
Duplicated Lines 0 %

Coupling/Cohesion

Components 1
Dependencies 14

Importance

Changes 0
Metric Value
dl 0
loc 230
rs 10
c 0
b 0
f 0
wmc 24
lcom 1
cbo 14

16 Methods

Rating   Name   Duplication   Size   Complexity  
A __construct() 0 8 1
A dontBackupFilesystem() 0 6 1
A dontBackupDatabases() 0 6 1
A setDefaultFilename() 0 6 1
A setFileSelection() 0 6 1
A setDbDumpers() 0 6 1
A setFilename() 0 6 1
A onlyBackupTo() 0 12 2
A setBackupDestinations() 0 6 1
B run() 0 29 4
A createBackupManifest() 0 14 1
A filesToBeBackedUp() 0 6 1
A directoriesUsedByBackupJob() 0 15 1
A createZipContainingEveryFileInManifest() 0 14 1
B dumpDatabases() 0 33 4
A copyToBackupDestinations() 0 18 2
1
<?php
2
3
namespace Spatie\Backup\Tasks\Backup;
4
5
use Exception;
6
use Carbon\Carbon;
7
use Spatie\DbDumper\DbDumper;
8
use Illuminate\Support\Collection;
9
use Spatie\Backup\Events\BackupHasFailed;
10
use Spatie\Backup\Events\BackupWasSuccessful;
11
use Spatie\Backup\Events\BackupZipWasCreated;
12
use Spatie\Backup\Exceptions\InvalidBackupJob;
13
use Spatie\TemporaryDirectory\TemporaryDirectory;
14
use Spatie\Backup\Events\BackupManifestWasCreated;
15
use Spatie\Backup\BackupDestination\BackupDestination;
16
17
class BackupJob
18
{
19
    /** @var \Spatie\Backup\Tasks\Backup\FileSelection */
20
    protected $fileSelection;
21
22
    /** @var \Illuminate\Support\Collection */
23
    protected $dbDumpers;
24
25
    /** @var \Illuminate\Support\Collection */
26
    protected $backupDestinations;
27
28
    /** @var string */
29
    protected $filename;
30
31
    /** @var \Spatie\TemporaryDirectory\TemporaryDirectory */
32
    protected $temporaryDirectory;
33
34
    public function __construct()
35
    {
36
        $this->dontBackupFilesystem();
37
        $this->dontBackupDatabases();
38
        $this->setDefaultFilename();
39
40
        $this->backupDestinations = new Collection();
41
    }
42
43
    public function dontBackupFilesystem(): BackupJob
44
    {
45
        $this->fileSelection = FileSelection::create();
46
47
        return $this;
48
    }
49
50
    public function dontBackupDatabases(): BackupJob
51
    {
52
        $this->dbDumpers = new Collection();
53
54
        return $this;
55
    }
56
57
    public function setDefaultFilename(): BackupJob
58
    {
59
        $this->filename = Carbon::now()->format('Y-m-d-H-i-s').'.zip';
60
61
        return $this;
62
    }
63
64
    public function setFileSelection(FileSelection $fileSelection): BackupJob
65
    {
66
        $this->fileSelection = $fileSelection;
67
68
        return $this;
69
    }
70
71
    public function setDbDumpers(Collection $dbDumpers): BackupJob
72
    {
73
        $this->dbDumpers = $dbDumpers;
74
75
        return $this;
76
    }
77
78
    public function setFilename(string $filename): BackupJob
79
    {
80
        $this->filename = $filename;
81
82
        return $this;
83
    }
84
85
    public function onlyBackupTo(string $diskName): BackupJob
86
    {
87
        $this->backupDestinations = $this->backupDestinations->filter(function (BackupDestination $backupDestination) use ($diskName) {
88
            return $backupDestination->diskName() === $diskName;
89
        });
90
91
        if (! count($this->backupDestinations)) {
92
            throw InvalidBackupJob::destinationDoesNotExist($diskName);
93
        }
94
95
        return $this;
96
    }
97
98
    public function setBackupDestinations(Collection $backupDestinations): BackupJob
99
    {
100
        $this->backupDestinations = $backupDestinations;
101
102
        return $this;
103
    }
104
105
    public function run()
106
    {
107
        $this->temporaryDirectory = (new TemporaryDirectory(storage_path('app/laravel-backup')))
108
            ->name('temp')
109
            ->force()
110
            ->create();
111
112
        try {
113
            if (! count($this->backupDestinations)) {
114
                throw InvalidBackupJob::noDestinationsSpecified();
115
            }
116
117
            $manifest = $this->createBackupManifest();
118
119
            if (! $manifest->count()) {
120
                throw InvalidBackupJob::noFilesToBeBackedUp();
121
            }
122
123
            $zipFile = $this->createZipContainingEveryFileInManifest($manifest);
124
125
            $this->copyToBackupDestinations($zipFile);
126
        } catch (Exception $exception) {
127
            consoleOutput()->error("Backup failed because {$exception->getMessage()}.".PHP_EOL.$exception->getTraceAsString());
0 ignored issues
show
Documentation Bug introduced by
The method error does not exist on object<Spatie\Backup\Helpers\ConsoleOutput>? Since you implemented __call, maybe consider adding a @method annotation.

If you implement __call and you know which methods are available, you can improve IDE auto-completion and static analysis by adding a @method annotation to the class.

This is often the case, when __call is implemented by a parent class and only the child class knows which methods exist:

class ParentClass {
    private $data = array();

    public function __call($method, array $args) {
        if (0 === strpos($method, 'get')) {
            return $this->data[strtolower(substr($method, 3))];
        }

        throw new \LogicException(sprintf('Unsupported method: %s', $method));
    }
}

/**
 * If this class knows which fields exist, you can specify the methods here:
 *
 * @method string getName()
 */
class SomeClass extends ParentClass { }
Loading history...
128
129
            event(new BackupHasFailed($exception));
130
        }
131
132
        $this->temporaryDirectory->delete();
133
    }
134
135
    protected function createBackupManifest(): Manifest
136
    {
137
        $databaseDumps = $this->dumpDatabases();
138
139
        consoleOutput()->info('Determining files to backup...');
0 ignored issues
show
Documentation Bug introduced by
The method info does not exist on object<Spatie\Backup\Helpers\ConsoleOutput>? Since you implemented __call, maybe consider adding a @method annotation.

If you implement __call and you know which methods are available, you can improve IDE auto-completion and static analysis by adding a @method annotation to the class.

This is often the case, when __call is implemented by a parent class and only the child class knows which methods exist:

class ParentClass {
    private $data = array();

    public function __call($method, array $args) {
        if (0 === strpos($method, 'get')) {
            return $this->data[strtolower(substr($method, 3))];
        }

        throw new \LogicException(sprintf('Unsupported method: %s', $method));
    }
}

/**
 * If this class knows which fields exist, you can specify the methods here:
 *
 * @method string getName()
 */
class SomeClass extends ParentClass { }
Loading history...
140
141
        $manifest = Manifest::create($this->temporaryDirectory->path('manifest.txt'))
142
            ->addFiles($databaseDumps)
143
            ->addFiles($this->filesToBeBackedUp());
0 ignored issues
show
Documentation introduced by
$this->filesToBeBackedUp() is of type object<Generator>, but the function expects a array.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
144
145
        event(new BackupManifestWasCreated($manifest));
146
147
        return $manifest;
148
    }
149
150
    public function filesToBeBackedUp()
151
    {
152
        $this->fileSelection->excludeFilesFrom($this->directoriesUsedByBackupJob());
153
154
        return $this->fileSelection->selectedFiles();
155
    }
156
157
    protected function directoriesUsedByBackupJob(): array
158
    {
159
        return $this->backupDestinations
160
            ->filter(function (BackupDestination $backupDestination) {
161
                return $backupDestination->filesystemType() === 'local';
162
            })
163
            ->map(function (BackupDestination $backupDestination) {
164
                return $backupDestination->disk()->getDriver()->getAdapter()->applyPathPrefix('').$backupDestination->backupName();
165
            })
166
            ->each(function (string $backupDestinationDirectory) {
167
                $this->fileSelection->excludeFilesFrom($backupDestinationDirectory);
168
            })
169
            ->push($this->temporaryDirectory->path())
170
            ->toArray();
171
    }
172
173
    protected function createZipContainingEveryFileInManifest(Manifest $manifest)
174
    {
175
        consoleOutput()->info("Zipping {$manifest->count()} files...");
0 ignored issues
show
Documentation Bug introduced by
The method info does not exist on object<Spatie\Backup\Helpers\ConsoleOutput>? Since you implemented __call, maybe consider adding a @method annotation.

If you implement __call and you know which methods are available, you can improve IDE auto-completion and static analysis by adding a @method annotation to the class.

This is often the case, when __call is implemented by a parent class and only the child class knows which methods exist:

class ParentClass {
    private $data = array();

    public function __call($method, array $args) {
        if (0 === strpos($method, 'get')) {
            return $this->data[strtolower(substr($method, 3))];
        }

        throw new \LogicException(sprintf('Unsupported method: %s', $method));
    }
}

/**
 * If this class knows which fields exist, you can specify the methods here:
 *
 * @method string getName()
 */
class SomeClass extends ParentClass { }
Loading history...
176
177
        $pathToZip = $this->temporaryDirectory->path(config('laravel-backup.backup.destination.filename_prefix').$this->filename);
178
179
        $zip = Zip::createForManifest($manifest, $pathToZip);
180
181
        consoleOutput()->info("Created zip containing {$zip->count()} files. Size is {$zip->humanReadableSize()}");
0 ignored issues
show
Documentation Bug introduced by
The method info does not exist on object<Spatie\Backup\Helpers\ConsoleOutput>? Since you implemented __call, maybe consider adding a @method annotation.

If you implement __call and you know which methods are available, you can improve IDE auto-completion and static analysis by adding a @method annotation to the class.

This is often the case, when __call is implemented by a parent class and only the child class knows which methods exist:

class ParentClass {
    private $data = array();

    public function __call($method, array $args) {
        if (0 === strpos($method, 'get')) {
            return $this->data[strtolower(substr($method, 3))];
        }

        throw new \LogicException(sprintf('Unsupported method: %s', $method));
    }
}

/**
 * If this class knows which fields exist, you can specify the methods here:
 *
 * @method string getName()
 */
class SomeClass extends ParentClass { }
Loading history...
182
183
        event(new BackupZipWasCreated($pathToZip));
184
185
        return $pathToZip;
186
    }
187
188
    /**
189
     * Dumps the databases to the given directory.
190
     * Returns an array with paths to the dump files.
191
     *
192
     * @return array
193
     */
194
    protected function dumpDatabases(): array
195
    {
196
        return $this->dbDumpers->map(function (DbDumper $dbDumper) {
197
            consoleOutput()->info("Dumping database {$dbDumper->getDbName()}...");
0 ignored issues
show
Documentation Bug introduced by
The method info does not exist on object<Spatie\Backup\Helpers\ConsoleOutput>? Since you implemented __call, maybe consider adding a @method annotation.

If you implement __call and you know which methods are available, you can improve IDE auto-completion and static analysis by adding a @method annotation to the class.

This is often the case, when __call is implemented by a parent class and only the child class knows which methods exist:

class ParentClass {
    private $data = array();

    public function __call($method, array $args) {
        if (0 === strpos($method, 'get')) {
            return $this->data[strtolower(substr($method, 3))];
        }

        throw new \LogicException(sprintf('Unsupported method: %s', $method));
    }
}

/**
 * If this class knows which fields exist, you can specify the methods here:
 *
 * @method string getName()
 */
class SomeClass extends ParentClass { }
Loading history...
198
199
            switch (class_basename($dbDumper)) {
200
                case 'MySql':
201
                    $extension = 'sql';
202
                    break;
203
                case 'MongoDb':
204
                    $extension = 'mongo';
205
                    break;
206
                default:
207
                    $extension = 'sql';
208
            }
209
210
            $fileName = $dbDumper->getDbName().'.'.$extension;
211
212
            $temporaryFilePath = $this->temporaryDirectory->path('db-dumps'.DIRECTORY_SEPARATOR.$fileName);
213
214
            $dbDumper->dumpToFile($temporaryFilePath);
215
216
            if (config('laravel-backup.backup.gzip_database_dump')) {
217
                consoleOutput()->info("Gzipping {$dbDumper->getDbName()}...");
0 ignored issues
show
Documentation Bug introduced by
The method info does not exist on object<Spatie\Backup\Helpers\ConsoleOutput>? Since you implemented __call, maybe consider adding a @method annotation.

If you implement __call and you know which methods are available, you can improve IDE auto-completion and static analysis by adding a @method annotation to the class.

This is often the case, when __call is implemented by a parent class and only the child class knows which methods exist:

class ParentClass {
    private $data = array();

    public function __call($method, array $args) {
        if (0 === strpos($method, 'get')) {
            return $this->data[strtolower(substr($method, 3))];
        }

        throw new \LogicException(sprintf('Unsupported method: %s', $method));
    }
}

/**
 * If this class knows which fields exist, you can specify the methods here:
 *
 * @method string getName()
 */
class SomeClass extends ParentClass { }
Loading history...
218
219
                $compressedDumpPath = Gzip::compress($temporaryFilePath);
220
221
                return $compressedDumpPath;
222
            }
223
224
            return $temporaryFilePath;
225
        })->toArray();
226
    }
227
228
    protected function copyToBackupDestinations(string $path)
229
    {
230
        $this->backupDestinations->each(function (BackupDestination $backupDestination) use ($path) {
231
            try {
232
                consoleOutput()->info("Copying zip to disk named {$backupDestination->diskName()}...");
0 ignored issues
show
Documentation Bug introduced by
The method info does not exist on object<Spatie\Backup\Helpers\ConsoleOutput>? Since you implemented __call, maybe consider adding a @method annotation.

If you implement __call and you know which methods are available, you can improve IDE auto-completion and static analysis by adding a @method annotation to the class.

This is often the case, when __call is implemented by a parent class and only the child class knows which methods exist:

class ParentClass {
    private $data = array();

    public function __call($method, array $args) {
        if (0 === strpos($method, 'get')) {
            return $this->data[strtolower(substr($method, 3))];
        }

        throw new \LogicException(sprintf('Unsupported method: %s', $method));
    }
}

/**
 * If this class knows which fields exist, you can specify the methods here:
 *
 * @method string getName()
 */
class SomeClass extends ParentClass { }
Loading history...
233
234
                $backupDestination->write($path);
235
236
                consoleOutput()->info("Successfully copied zip to disk named {$backupDestination->diskName()}.");
0 ignored issues
show
Documentation Bug introduced by
The method info does not exist on object<Spatie\Backup\Helpers\ConsoleOutput>? Since you implemented __call, maybe consider adding a @method annotation.

If you implement __call and you know which methods are available, you can improve IDE auto-completion and static analysis by adding a @method annotation to the class.

This is often the case, when __call is implemented by a parent class and only the child class knows which methods exist:

class ParentClass {
    private $data = array();

    public function __call($method, array $args) {
        if (0 === strpos($method, 'get')) {
            return $this->data[strtolower(substr($method, 3))];
        }

        throw new \LogicException(sprintf('Unsupported method: %s', $method));
    }
}

/**
 * If this class knows which fields exist, you can specify the methods here:
 *
 * @method string getName()
 */
class SomeClass extends ParentClass { }
Loading history...
237
238
                event(new BackupWasSuccessful($backupDestination));
239
            } catch (Exception $exception) {
240
                consoleOutput()->error("Copying zip failed because: {$exception->getMessage()}.");
0 ignored issues
show
Documentation Bug introduced by
The method error does not exist on object<Spatie\Backup\Helpers\ConsoleOutput>? Since you implemented __call, maybe consider adding a @method annotation.

If you implement __call and you know which methods are available, you can improve IDE auto-completion and static analysis by adding a @method annotation to the class.

This is often the case, when __call is implemented by a parent class and only the child class knows which methods exist:

class ParentClass {
    private $data = array();

    public function __call($method, array $args) {
        if (0 === strpos($method, 'get')) {
            return $this->data[strtolower(substr($method, 3))];
        }

        throw new \LogicException(sprintf('Unsupported method: %s', $method));
    }
}

/**
 * If this class knows which fields exist, you can specify the methods here:
 *
 * @method string getName()
 */
class SomeClass extends ParentClass { }
Loading history...
241
242
                event(new BackupHasFailed($exception, $backupDestination ?? null));
243
            }
244
        });
245
    }
246
}
247