Completed
Pull Request — master (#424)
by
unknown
19:25
created

BackupJob::gzipSqlFile()   B

Complexity

Conditions 4
Paths 4

Size

Total Lines 27
Code Lines 17

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 4
eloc 17
nc 4
nop 2
dl 0
loc 27
rs 8.5806
c 0
b 0
f 0
1
<?php
2
3
namespace Spatie\Backup\Tasks\Backup;
4
5
use Exception;
6
use Carbon\Carbon;
7
use Spatie\Backup\Helpers\Format;
8
use Spatie\DbDumper\DbDumper;
9
use Illuminate\Support\Collection;
10
use Spatie\Backup\Events\BackupHasFailed;
11
use Spatie\Backup\Events\BackupWasSuccessful;
12
use Spatie\Backup\Events\BackupZipWasCreated;
13
use Spatie\Backup\Exceptions\InvalidBackupJob;
14
use Spatie\TemporaryDirectory\TemporaryDirectory;
15
use Spatie\Backup\Events\BackupManifestWasCreated;
16
use Spatie\Backup\BackupDestination\BackupDestination;
17
18
class BackupJob
19
{
20
    /** @var \Spatie\Backup\Tasks\Backup\FileSelection */
21
    protected $fileSelection;
22
23
    /** @var \Illuminate\Support\Collection */
24
    protected $dbDumpers;
25
26
    /** @var \Illuminate\Support\Collection */
27
    protected $backupDestinations;
28
29
    /** @var string */
30
    protected $filename;
31
32
    /** @var \Spatie\TemporaryDirectory\TemporaryDirectory */
33
    protected $temporaryDirectory;
34
35
    public function __construct()
36
    {
37
        $this->dontBackupFilesystem();
38
        $this->dontBackupDatabases();
39
        $this->setDefaultFilename();
40
41
        $this->backupDestinations = new Collection();
42
    }
43
44
    public function dontBackupFilesystem(): BackupJob
45
    {
46
        $this->fileSelection = FileSelection::create();
47
48
        return $this;
49
    }
50
51
    public function dontBackupDatabases(): BackupJob
52
    {
53
        $this->dbDumpers = new Collection();
54
55
        return $this;
56
    }
57
58
    public function setDefaultFilename(): BackupJob
59
    {
60
        $this->filename = Carbon::now()->format('Y-m-d-H-i-s').'.zip';
61
62
        return $this;
63
    }
64
65
    public function setFileSelection(FileSelection $fileSelection): BackupJob
66
    {
67
        $this->fileSelection = $fileSelection;
68
69
        return $this;
70
    }
71
72
    public function setDbDumpers(Collection $dbDumpers): BackupJob
73
    {
74
        $this->dbDumpers = $dbDumpers;
75
76
        return $this;
77
    }
78
79
    public function setFilename(string $filename): BackupJob
80
    {
81
        $this->filename = $filename;
82
83
        return $this;
84
    }
85
86
    public function onlyBackupTo(string $diskName): BackupJob
87
    {
88
        $this->backupDestinations = $this->backupDestinations->filter(function (BackupDestination $backupDestination) use ($diskName) {
89
            return $backupDestination->diskName() === $diskName;
90
        });
91
92
        if (! count($this->backupDestinations)) {
93
            throw InvalidBackupJob::destinationDoesNotExist($diskName);
94
        }
95
96
        return $this;
97
    }
98
99
    public function setBackupDestinations(Collection $backupDestinations): BackupJob
100
    {
101
        $this->backupDestinations = $backupDestinations;
102
103
        return $this;
104
    }
105
106
    public function run()
107
    {
108
        $this->temporaryDirectory = (new TemporaryDirectory(storage_path('app/laravel-backup')))
109
            ->name('temp')
110
            ->force()
111
            ->create();
112
113
        try {
114
            if (! count($this->backupDestinations)) {
115
                throw InvalidBackupJob::noDestinationsSpecified();
116
            }
117
118
            $manifest = $this->createBackupManifest();
119
120
            if (! $manifest->count()) {
121
                throw InvalidBackupJob::noFilesToBeBackedUp();
122
            }
123
124
            $zipFile = $this->createZipContainingEveryFileInManifest($manifest);
125
126
            $this->copyToBackupDestinations($zipFile);
127
        } catch (Exception $exception) {
128
            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...
129
130
            event(new BackupHasFailed($exception));
131
        }
132
133
        $this->temporaryDirectory->delete();
134
    }
135
136
    protected function createBackupManifest(): Manifest
137
    {
138
        $databaseDumps = $this->dumpDatabases();
139
140
        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...
141
142
        $manifest = Manifest::create($this->temporaryDirectory->path('manifest.txt'))
143
            ->addFiles($databaseDumps)
144
            ->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...
145
146
        event(new BackupManifestWasCreated($manifest));
147
148
        return $manifest;
149
    }
150
151
    public function filesToBeBackedUp()
152
    {
153
        $this->fileSelection->excludeFilesFrom($this->directoriesUsedByBackupJob());
154
155
        return $this->fileSelection->selectedFiles();
156
    }
157
158
    protected function directoriesUsedByBackupJob(): array
159
    {
160
        return $this->backupDestinations
161
            ->filter(function (BackupDestination $backupDestination) {
162
                return $backupDestination->filesystemType() === 'local';
163
            })
164
            ->map(function (BackupDestination $backupDestination) {
165
                return $backupDestination->disk()->getDriver()->getAdapter()->applyPathPrefix('').$backupDestination->backupName();
166
            })
167
            ->each(function (string $backupDestinationDirectory) {
168
                $this->fileSelection->excludeFilesFrom($backupDestinationDirectory);
169
            })
170
            ->push($this->temporaryDirectory->path())
171
            ->toArray();
172
    }
173
174
    protected function createZipContainingEveryFileInManifest(Manifest $manifest)
175
    {
176
        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...
177
178
        $pathToZip = $this->temporaryDirectory->path(config('laravel-backup.backup.destination.filename_prefix').$this->filename);
179
180
        $zip = Zip::createForManifest($manifest, $pathToZip);
181
182
        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...
183
184
        event(new BackupZipWasCreated($pathToZip));
185
186
        return $pathToZip;
187
    }
188
189
    /**
190
     * Dumps the databases to the given directory.
191
     * Returns an array with paths to the dump files.
192
     *
193
     * @return array
194
     */
195
    protected function dumpDatabases(): array
196
    {
197
        return $this->dbDumpers->map(function (DbDumper $dbDumper) {
198
            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...
199
200
            $fileName = $dbDumper->getDbName().'.sql';
201
202
            $temporaryFilePath = $this->temporaryDirectory->path('db-dumps'.DIRECTORY_SEPARATOR.$fileName);
203
204
            $dbDumper->dumpToFile($temporaryFilePath);
205
206
            if (config('laravel-backup.backup.gzipSql') === true) {
207
                return $this->gzipSqlFile($dbDumper, $temporaryFilePath);
208
            }
209
210
            return $temporaryFilePath;
211
        })->toArray();
212
    }
213
214
    /**
215
     * @param $temporaryFilePath
216
     * @return string
217
     */
218
    protected function gzipSqlFile($dbDumper, $temporaryFilePath)
219
    {
220
        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...
221
222
        $gzipPath = $temporaryFilePath . '.gz';
223
224
        if ($gzipOut = gzopen($gzipPath, 'w9')) {
225
            if ($gzipIn = fopen($temporaryFilePath, 'rb')) {
226
                while (!feof($gzipIn)) {
227
                    gzwrite($gzipOut, fread($gzipIn, 1024 * 512));
228
                }
229
                fclose($gzipIn);
230
            } else {
231
                consoleOutput()->error("Gzip failed for {$dbDumper->getDbName()}");
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...
232
                return $temporaryFilePath;
233
            }
234
235
            gzclose($gzipOut);
236
        } else {
237
            consoleOutput()->error("Gzip failed for {$dbDumper->getDbName()}");
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...
238
            return $temporaryFilePath;
239
        }
240
241
        consoleOutput()->info("Gzipped {$dbDumper->getDbName()} from ".Format::humanReadableSize(filesize($temporaryFilePath))." to ".Format::humanReadableSize(filesize($gzipPath)));
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...
242
243
        return $gzipPath;
244
    }
245
246
247
248
    protected function copyToBackupDestinations(string $path)
249
    {
250
        $this->backupDestinations->each(function (BackupDestination $backupDestination) use ($path) {
251
            try {
252
                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...
253
254
                $backupDestination->write($path);
255
256
                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...
257
258
                event(new BackupWasSuccessful($backupDestination));
259
            } catch (Exception $exception) {
260
                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...
261
262
                event(new BackupHasFailed($exception, $backupDestination ?? null));
263
            }
264
        });
265
    }
266
}
267