Completed
Push — master ( 326b3e...061c53 )
by Freek
09:13
created

BackupJob::run()   B

Complexity

Conditions 4
Paths 8

Size

Total Lines 26
Code Lines 14

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 4
eloc 14
nc 8
nop 0
dl 0
loc 26
rs 8.5806
c 0
b 0
f 0
1
<?php
2
3
namespace Spatie\Backup\Tasks\Backup;
4
5
use Carbon\Carbon;
6
use Illuminate\Support\Collection;
7
use Spatie\Backup\BackupDestination\BackupDestination;
8
use Spatie\Backup\Events\BackupHasFailed;
9
use Spatie\Backup\Events\BackupManifestWasCreated;
10
use Spatie\Backup\Events\BackupWasSuccessful;
11
use Spatie\Backup\Exceptions\InvalidBackupJob;
12
use Exception;
13
14
class BackupJob
15
{
16
    /** @var \Spatie\Backup\Tasks\Backup\FileSelection */
17
    protected $fileSelection;
18
19
    /** @var \Illuminate\Support\Collection */
20
    protected $dbDumpers;
21
22
    /** @var \Illuminate\Support\Collection */
23
    protected $backupDestinations;
24
25
    /** @var string */
26
    protected $filename;
27
28
    /** @var \Spatie\Backup\Tasks\Backup\TemporaryDirectory */
29
    protected $temporaryDirectory;
30
31
    public function __construct()
32
    {
33
        $this->dontBackupFilesystem();
34
        $this->dontBackupDatabases();
35
        $this->setDefaultFilename();
36
37
        $this->backupDestinations = new Collection();
38
    }
39
40
    public function dontBackupFilesystem(): BackupJob
41
    {
42
        $this->fileSelection = FileSelection::create();
43
44
        return $this;
45
    }
46
47
    public function dontBackupDatabases(): BackupJob
48
    {
49
        $this->dbDumpers = new Collection();
50
51
        return $this;
52
    }
53
54
    public function setDefaultFilename(): BackupJob
55
    {
56
        $this->filename = date('Y-m-d-His').'.zip';
57
58
        return $this;
59
    }
60
61
    public function setFileSelection(FileSelection $fileSelection): BackupJob
62
    {
63
        $this->fileSelection = $fileSelection;
64
65
        return $this;
66
    }
67
68
    public function setDbDumpers(Collection $dbDumpers): BackupJob
69
    {
70
        $this->dbDumpers = $dbDumpers;
71
72
        return $this;
73
    }
74
75
    public function setFilename(string $filename): BackupJob
76
    {
77
        $this->filename = $filename;
78
79
        return $this;
80
    }
81
82
    public function onlyBackupTo(string $diskName): BackupJob
83
    {
84
        $this->backupDestinations = $this->backupDestinations->filter(function (BackupDestination $backupDestination) use ($diskName) {
85
            return $backupDestination->diskName() === $diskName;
86
        });
87
88
        if (! count($this->backupDestinations)) {
89
            throw InvalidBackupJob::destinationDoesNotExist($diskName);
90
        }
91
92
        return $this;
93
    }
94
95
    public function setBackupDestinations(Collection $backupDestinations): BackupJob
96
    {
97
        $this->backupDestinations = $backupDestinations;
98
99
        return $this;
100
    }
101
102
    public function run()
103
    {
104
        $this->temporaryDirectory = TemporaryDirectory::create();
105
106
        try {
107
            if (! count($this->backupDestinations)) {
108
                throw InvalidBackupJob::noDestinationsSpecified();
109
            }
110
111
            $manifest = $this->createBackupManifest();
112
113
            if (! $manifest->count()) {
114
                throw InvalidBackupJob::noFilesToBeBackedUp();
115
            }
116
117
            $zipFile = $this->createZipContainingEveryFileInManifest($manifest);
118
119
            $this->copyToBackupDestinations($zipFile);
120
        } catch (Exception $exception) {
121
            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...
122
123
            event(new BackupHasFailed($exception));
124
        }
125
126
        $this->temporaryDirectory->delete();
127
    }
128
129
    protected function createBackupManifest(): Manifest
130
    {
131
        $databaseDumps = $this->dumpDatabases($this->temporaryDirectory->path('db-dumps'));
132
133
        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...
134
135
        $manifest = Manifest::create($this->temporaryDirectory->path('manifest.txt'))
136
            ->addFiles($databaseDumps)
137
            ->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...
138
139
        event(new BackupManifestWasCreated($manifest));
140
141
        return $manifest;
142
    }
143
144
    public function filesToBeBackedUp()
145
    {
146
        $this->fileSelection->excludeFilesFrom($this->directoriesUsedByBackupJob());
147
148
        return $this->fileSelection->selectedFiles();
149
    }
150
151
    protected function directoriesUsedByBackupJob(): array
152
    {
153
        return $this->backupDestinations
154
            ->filter(function (BackupDestination $backupDestination) {
155
                return $backupDestination->filesystemType() === 'local';
156
            })
157
            ->map(function (BackupDestination $backupDestination) {
158
                return $backupDestination->disk()->getDriver()->getAdapter()->applyPathPrefix('');
159
            })
160
            ->each(function (string $localDiskRootDirectory) {
161
                $this->fileSelection->excludeFilesFrom($localDiskRootDirectory);
162
            })
163
            ->push($this->temporaryDirectory->path())
164
            ->toArray();
165
    }
166
167
    protected function createZipContainingEveryFileInManifest(Manifest $manifest)
168
    {
169
        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...
170
171
        $pathToZip = $this->temporaryDirectory->path(Carbon::now()->format('Y-m-d-h-i-s').'.zip');
172
173
        $zip = Zip::createForManifest($manifest, $pathToZip);
174
175
        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...
176
177
        return $pathToZip;
178
    }
179
180
    /**
181
     * Dumps the databases to the given directory.
182
     * Returns an array with paths to the dump files.
183
     *
184
     * @param string $directory
185
     *
186
     * @return array
187
     */
188
    protected function dumpDatabases(string $directory): array
189
    {
190
        return $this->dbDumpers->map(function ($dbDumper) use ($directory) {
191
            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...
192
193
            $fileName = $dbDumper->getDbName().'.sql';
194
            $temporaryFile = $directory.'/'.$fileName;
195
196
            $dbDumper->dumpToFile($temporaryFile);
197
198
            return $temporaryFile;
199
        })->toArray();
200
    }
201
202
    protected function copyToBackupDestinations(string $path)
203
    {
204
        $this->backupDestinations->each(function (BackupDestination $backupDestination) use ($path) {
205
            try {
206
                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...
207
208
                $backupDestination->write($path);
209
210
                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...
211
212
                event(new BackupWasSuccessful($backupDestination));
213
            } catch (Exception $exception) {
214
                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...
215
216
                event(new BackupHasFailed($exception, $backupDestination ?? null));
217
            }
218
        });
219
    }
220
}
221