Completed
Pull Request — master (#56)
by Jeroen van
03:07
created

BackupCommand   A

Complexity

Total Complexity 33

Size/Duplication

Total Lines 300
Duplicated Lines 0 %

Coupling/Cohesion

Components 1
Dependencies 2

Importance

Changes 19
Bugs 6 Features 6
Metric Value
wmc 33
c 19
b 6
f 6
lcom 1
cbo 2
dl 0
loc 300
rs 9.4

14 Methods

Rating   Name   Duplication   Size   Complexity  
B fire() 0 32 4
B getAllFilesToBeBackedUp() 0 20 5
A createZip() 0 21 3
A copyFile() 0 16 2
A getTargetFileSystems() 0 10 2
A writeIgnoreFile() 0 5 1
A getBackupDestinationFileName() 0 15 2
A getPrefix() 0 8 2
A getSuffix() 0 8 2
A copyFileToFileSystem() 0 12 1
A getOptions() 0 9 1
A getDatabaseDump() 0 18 2
A guardAgainstInvalidOptions() 0 6 3
A removeTemporaryFiles() 0 8 3
1
<?php
2
3
namespace Spatie\Backup\Commands;
4
5
use Illuminate\Console\Command;
6
use Illuminate\Support\Facades\Storage;
7
use Symfony\Component\Console\Input\InputOption;
8
use ZipArchive;
9
10
class BackupCommand extends Command
11
{
12
    /**
13
     * The console command name.
14
     *
15
     * @var string
16
     */
17
    protected $name = 'backup:run';
18
19
    /**
20
     * The console command description.
21
     *
22
     * @var string
23
     */
24
    protected $description = 'Run the backup';
25
26
    /**
27
     * Files that will be remove at the end of the command.
28
     *
29
     * @var array
30
     */
31
    protected $temporaryFiles = [];
32
33
    /**
34
     * Execute the console command.
35
     *
36
     * @return bool
37
     */
38
    public function fire()
39
    {
40
        $this->guardAgainstInvalidOptions();
41
42
        $this->info('Start backing up');
43
44
        $files = $this->getAllFilesToBeBackedUp();
45
46
        if (count($files) == 0) {
47
            $this->info('Nothing to backup');
48
49
            return true;
50
        }
51
52
        $backupZipFile = $this->createZip($files);
53
54
        $this->temporaryFiles[] = $backupZipFile;
55
56
        if (filesize($backupZipFile) == 0) {
57
            $this->warn('The zipfile that will be backupped has a filesize of zero.');
58
        }
59
60
        foreach ($this->getTargetFileSystems() as $fileSystem) {
61
            $this->copyFileToFileSystem($backupZipFile, $fileSystem);
62
        }
63
64
        $this->removeTemporaryFiles();
65
66
        $this->info('Backup successfully completed');
67
68
        return true;
69
    }
70
71
    /**
72
     * Return an array with path to files that should be backed up.
73
     *
74
     * @return array
75
     */
76
    protected function getAllFilesToBeBackedUp()
77
    {
78
        $files = [];
79
80
        if ((!$this->option('only-files')) && config('laravel-backup.source.backup-db')) {
81
            $files[] = ['realFile' => $this->getDatabaseDump($files), 'fileInZip' => 'dump.sql'];
0 ignored issues
show
Unused Code introduced by
The call to BackupCommand::getDatabaseDump() has too many arguments starting with $files.

This check compares calls to functions or methods with their respective definitions. If the call has more arguments than are defined, it raises an issue.

If a function is defined several times with a different number of parameters, the check may pick up the wrong definition and report false positives. One codebase where this has been known to happen is Wordpress.

In this case you can add the @ignore PhpDoc annotation to the duplicate definition and it will be ignored.

Loading history...
82
        }
83
84
        if (!$this->option('only-db')) {
85
            $this->comment('Determining which files should be backed up...');
86
            $fileBackupHandler = app()->make('Spatie\Backup\BackupHandlers\Files\FilesBackupHandler')
87
                ->setIncludedFiles(config('laravel-backup.source.files.include'))
88
                ->setExcludedFiles(config('laravel-backup.source.files.exclude'));
89
            foreach ($fileBackupHandler->getFilesToBeBackedUp() as $file) {
90
                $files[] = ['realFile' => $file, 'fileInZip' => 'files/'.$file];
91
            }
92
        }
93
94
        return $files;
95
    }
96
97
    /**
98
     * Create a zip for the given files.
99
     *
100
     * @param $files
101
     *
102
     * @return string
103
     */
104
    protected function createZip($files)
105
    {
106
        $this->comment('Start zipping '.count($files).' files...');
107
108
        $tempZipFile = tempnam(sys_get_temp_dir(), 'laravel-backup-zip');
109
110
        $zip = new ZipArchive();
111
        $zip->open($tempZipFile, ZipArchive::CREATE);
112
113
        foreach ($files as $file) {
114
            if (file_exists($file['realFile'])) {
115
                $zip->addFile($file['realFile'], $file['fileInZip']);
116
            }
117
        }
118
119
        $zip->close();
120
121
        $this->comment('Zip created!');
122
123
        return $tempZipFile;
124
    }
125
126
    /**
127
     * Copy the given file on the given disk to the given destination.
128
     *
129
     * @param string                                      $file
130
     * @param \Illuminate\Contracts\Filesystem\Filesystem $disk
131
     * @param string                                      $destination
132
     * @param bool                                        $addIgnoreFile
133
     */
134
    protected function copyFile($file, $disk, $destination, $addIgnoreFile = false)
135
    {
136
        $destinationDirectory = dirname($destination);
137
138
        $disk->makeDirectory($destinationDirectory);
139
140
        if ($addIgnoreFile) {
141
            $this->writeIgnoreFile($disk, $destinationDirectory);
142
        }
143
144
        /*
145
         * The file could be quite large. Use a stream to copy it
146
         * to the target disk to avoid memory problems
147
         */
148
        $disk->getDriver()->writeStream($destination, fopen($file, 'r+'));
149
    }
150
151
    /**
152
     * Get the filesystems to where the database should be dumped.
153
     *
154
     * @return array
155
     */
156
    protected function getTargetFileSystems()
157
    {
158
        $fileSystems = config('laravel-backup.destination.filesystem');
159
160
        if (is_array($fileSystems)) {
161
            return $fileSystems;
162
        }
163
164
        return [$fileSystems];
165
    }
166
167
    /**
168
     * Write an ignore-file on the given disk in the given directory.
169
     *
170
     * @param \Illuminate\Contracts\Filesystem\Filesystem $disk
171
     * @param string                                      $dumpDirectory
172
     */
173
    protected function writeIgnoreFile($disk, $dumpDirectory)
174
    {
175
        $gitIgnoreContents = '*'.PHP_EOL.'!.gitignore';
176
        $disk->put($dumpDirectory.'/.gitignore', $gitIgnoreContents);
177
    }
178
179
    /**
180
     * Determine the name of the zip that contains the backup.
181
     *
182
     * @return string
183
     */
184
    protected function getBackupDestinationFileName()
185
    {
186
        $backupDirectory = config('laravel-backup.destination.path');
187
        $backupFilename = $this->getPrefix().date('YmdHis').$this->getSuffix().'.zip';
188
189
        $destination = $backupDirectory;
190
        
191
        if ($destination !='') {
192
            $destination .= '/';
193
        }
194
        
195
        $destination .= $backupFilename;
196
197
        return $destination;
198
    }
199
200
    /**
201
     * Get the prefix to be used in the filename of the backup file.
202
     *
203
     * @return string
204
     */
205
    public function getPrefix()
206
    {
207
        if ($this->option('prefix') != '') {
208
            return $this->option('prefix');
0 ignored issues
show
Bug Compatibility introduced by
The expression $this->option('prefix'); of type string|array adds the type array to the return on line 208 which is incompatible with the return type documented by Spatie\Backup\Commands\BackupCommand::getPrefix of type string.
Loading history...
209
        }
210
211
        return config('laravel-backup.destination.prefix');
212
    }
213
214
    /**
215
     * Get the suffix to be used in the filename of the backup file.
216
     *
217
     * @return string
218
     */
219
    public function getSuffix()
220
    {
221
        if ($this->option('suffix') != '') {
222
            return $this->option('suffix');
0 ignored issues
show
Bug Compatibility introduced by
The expression $this->option('suffix'); of type string|array adds the type array to the return on line 222 which is incompatible with the return type documented by Spatie\Backup\Commands\BackupCommand::getSuffix of type string.
Loading history...
223
        }
224
225
        return config('laravel-backup.destination.suffix');
226
    }
227
228
    /**
229
     * Copy the given file to given filesystem.
230
     *
231
     * @param string $file
232
     * @param $fileSystem
233
     */
234
    public function copyFileToFileSystem($file, $fileSystem)
235
    {
236
        $this->comment('Start uploading backup to '.$fileSystem.'-filesystem...');
237
238
        $disk = Storage::disk($fileSystem);
239
240
        $backupFilename = $this->getBackupDestinationFileName();
241
242
        $this->copyFile($file, $disk, $backupFilename, $fileSystem == 'local');
243
244
        $this->comment('Backup stored on '.$fileSystem.'-filesystem in file "'.$backupFilename.'"');
245
    }
246
247
    /**
248
     * Get the console command options.
249
     *
250
     * @return array
251
     */
252
    protected function getOptions()
253
    {
254
        return [
255
            ['only-db', null, InputOption::VALUE_NONE, 'Only backup the database.'],
256
            ['only-files', null, InputOption::VALUE_NONE, 'Only backup the files.'],
257
            ['prefix', null, InputOption::VALUE_REQUIRED, 'The name of the zip file will get prefixed with this string.'],
258
            ['suffix', null, InputOption::VALUE_REQUIRED, 'The name of the zip file will get suffixed with this string.'],
259
        ];
260
    }
261
262
    /**
263
     * Get a dump of the db.
264
     *
265
     * @return string
266
     *
267
     * @throws \Exception
268
     */
269
    protected function getDatabaseDump()
270
    {
271
        $databaseBackupHandler = app()->make('Spatie\Backup\BackupHandlers\Database\DatabaseBackupHandler');
272
273
        $filesToBeBackedUp = $databaseBackupHandler->getFilesToBeBackedUp();
274
275
        if (count($filesToBeBackedUp) != 1) {
276
            throw new \Exception('could not backup db');
277
        }
278
279
        $this->comment('Database dumped');
280
281
        $dbDumpFile = $filesToBeBackedUp[0];
282
283
        $this->temporaryFiles[] = $dbDumpFile;
284
285
        return $dbDumpFile;
286
    }
287
288
    /**
289
     * @throws \Exception
290
     */
291
    protected function guardAgainstInvalidOptions()
292
    {
293
        if ($this->option('only-db') && $this->option('only-files')) {
294
            throw new \Exception('cannot use only-db and only-files together');
295
        }
296
    }
297
298
    /**
299
     * Remove temporary files
300
     */
301
    protected function removeTemporaryFiles()
302
    {
303
        foreach ($this->temporaryFiles as $temporaryFile) {
304
            if (file_exists($temporaryFile)) {
305
                unlink($temporaryFile);
306
            }
307
        }
308
    }
309
}
310