Passed
Push — dev5 ( 1140b1...da16e0 )
by Ron
09:38
created

backupRestore::handle()   B

Complexity

Conditions 5
Paths 5

Size

Total Lines 39

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 30

Importance

Changes 0
Metric Value
dl 0
loc 39
ccs 0
cts 27
cp 0
rs 8.9848
c 0
b 0
f 0
cc 5
nc 5
nop 0
crap 30
1
<?php
2
3
namespace App\Console\Commands;
4
5
use Zip;
6
use Illuminate\Support\Arr;
7
use Illuminate\Console\Command;
8
use Illuminate\Support\Facades\Storage;
9
use Illuminate\Support\Facades\File;
10
use Symfony\Component\Process\Process;
11
use Symfony\Component\Process\Exception\ProcessFailedException;
12
13
use Illuminate\Support\Facades\DB;
14
15
class backupRestore extends Command
16
{
17
    /**
18
     * The name and signature of the console command.
19
     *
20
     * @var string
21
     */
22
    protected $signature = 'tb-backup:restore {filename} {--confirmed}';
23
24
    /**
25
     * The console command description.
26
     *
27
     * @var string
28
     */
29
    protected $description = 'Restore application from a previous backup';
30
    protected $archive, $bar, $baseName;
31
32
    /**
33
     * Create a new command instance.
34
     *
35
     * @return void
0 ignored issues
show
Comprehensibility Best Practice introduced by
Adding a @return annotation to constructors is generally not recommended as a constructor does not have a meaningful return value.

Adding a @return annotation to a constructor is not recommended, since a constructor does not have a meaningful return value.

Please refer to the PHP core documentation on constructors.

Loading history...
36
     */
37 2
    public function __construct()
38
    {
39 2
        parent::__construct();
40 2
    }
41
42
    /**
43
     * Execute the console command.
44
     *
45
     * @return mixed
46
     */
47
    public function handle()
48
    {
49
        if(!Storage::disk('backup')->exists(
50
        /** @scrutinizer ignore-type */
51
        $this->argument('filename')))
0 ignored issues
show
Bug introduced by
It seems like $this->argument('filename') targeting Illuminate\Console\Conce...ractsWithIO::argument() can also be of type array or null; however, Illuminate\Contracts\Fil...em\Filesystem::exists() does only seem to accept string, maybe add an additional type check?

This check looks at variables that are passed out again to other methods.

If the outgoing method call has stricter type requirements than the method itself, an issue is raised.

An additional type check may prevent trouble.

Loading history...
52
        {
53
            $this->error('The backup filename you entered does not exist.');
54
            $this->error('Exiting                                        ');
55
            return 1;
56
        }
57
58
        if(!$this->option('confirmed'))
59
        {
60
            $this->question('____________________________________________________________');
61
            $this->question('|                  IMPORTANT NOTE:                         |');
62
            $this->question('|   ALL DATA WILL BE ERASED AND REPLACED WITH THE BACKUP   |');
63
            $this->question('|__________________________________________________________|');
64
        }
65
66
        if($this->option('confirmed') || $this->confirm('Are You Sure?'))
67
        {
68
            $this->line('Restoring Backup.  Please Wait');
69
            $this->line('This could take some time');
70
            $this->bar = $this->output->createProgressBar(10);
71
            $this->call('down');
72
            $this->prepareBackup();
73
            $this->checkVersion();
74
            $this->replaceFiles();
75
            $this->wipeDatabase();
76
            $this->loadDatabase();
77
            $this->cleanup();
78
            $this->bar->finish();
79
            $this->line('');
80
            $this->call('up');
81
            $this->line('Restore Completed');
82
        }
83
84
        return 1;
85
    }
86
87
    //  Open the zip and prepare it for restore
88
    protected function prepareBackup()
89
    {
90
        $this->bar->advance();
91
        $fileParts = pathinfo(
92
        /** @scrutinizer ignore-type */
93
        $this->argument('filename'));
94
        $this->baseName = $fileParts['filename'];
95
        $this->archive = Zip::open(config('filesystems.disks.backup.root').DIRECTORY_SEPARATOR.
96
        /** @scrutinizer ignore-type */
97
        $this->argument('filename'));
98
        // if (!$this->archive->has('version.txt')) {
99
        //     $this->error('THIS IS NOT A VALID TECH BENCH BACKUP');
100
        //     $this->error('Exiting');
101
        //     return 1;
102
        // }
103
104
        $this->archive->extract(config('filesystems.disks.backup.root').DIRECTORY_SEPARATOR.$this->baseName);
105
        $this->bar->advance();
106
    }
107
108
    protected function cleanup()
109
    {
110
        Storage::disk('backup')->deleteDirectory($this->baseName);
111
    }
112
113
    //  Check to verify that the version matches
114
    protected function checkVersion()
115
    {
116
        $backupVer = Storage::disk('backup')->get($this->baseName.DIRECTORY_SEPARATOR.'version.txt');
117
        $appVer = new \PragmaRX\Version\Package\Version();
118
        $this->bar->advance();
119
120
        if($backupVer !== $appVer->version_only())
121
        {
122
            $this->error('Unable to Restore, you are running '.$appVer->version());
123
            $this->error('This backup is for version '.$backupVer);
124
            $this->error('Please load the proper version before loading this backup');
125
126
            $this->cleanup();
127
            return 1;
128
        }
129
    }
130
131
    protected function clearAllFiles($disk)
132
    {
133
        $files = Storage::disk($disk)->files();
134
        foreach($files as $file)
135
        {
136
            if($file != '.gitignore')
137
            {
138
                Storage::disk($disk)->delete($file);
139
            }
140
        }
141
        $folders = Storage::disk($disk)->directories();
142
        foreach($folders as $folder)
143
        {
144
            Storage::disk($disk)->deleteDirectory($folder);
145
        }
146
    }
147
148
    protected function copyAllFiles($backupFolder, $disk)
149
    {
150
        $files = Storage::disk('backup')->allFiles($this->baseName.DIRECTORY_SEPARATOR.$backupFolder);
151
152
        foreach($files as $file)
153
        {
154
            $newFile = str_replace($this->baseName.'/'.$backupFolder.'/', '', $file);
155
            Storage::disk($disk)->put($newFile, Storage::disk('backup')->get($file));
156
        }
157
    }
158
159
    //  Replace the system files
160
    protected function replaceFiles()
161
    {
162
        $this->bar->advance();
163
        //  Start with the .env file
164
        $env = Storage::disk('backup')->get($this->baseName.DIRECTORY_SEPARATOR.'.env');
165
        File::put(base_path().DIRECTORY_SEPARATOR.'.env', $env);
166
        $this->bar->advance();
167
        //  Replace the log files
168
        $this->clearAllFiles('logs');
169
        $this->copyAllFiles('logs', 'logs');
170
        $this->bar->advance();
171
        //  Replace the public files
172
        $this->clearAllFiles('public');
173
        $this->copyAllFiles('public', 'public');
174
        $this->bar->advance();
175
        //  Replace all upladed files
176
        $this->clearAllFiles('local');
177
        $this->copyAllFiles('files', 'local');
178
        $this->bar->advance();
179
    }
180
181
    //  Clear out current database
182
    protected function wipeDatabase()
183
    {
184
        DB::connection(DB::getDefaultConnection())
185
            ->getSchemaBuilder()
186
            ->dropAllTables();
187
        DB::reconnect();
188
        $this->bar->advance();
189
    }
190
191
    //  Load the backed up database
192
    protected function loadDatabase()
193
    {
194
        $dbContents = Storage::disk('backup')->get($this->baseName.DIRECTORY_SEPARATOR.'database.sql');
195
        DB::unprepared($dbContents);
196
        $this->bar->advance();
197
    }
198
}
199