Clean::execute()   A
last analyzed

Complexity

Conditions 4
Paths 4

Size

Total Lines 17
Code Lines 9

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 20

Importance

Changes 0
Metric Value
cc 4
eloc 9
nc 4
nop 2
dl 0
loc 17
ccs 0
cts 10
cp 0
crap 20
rs 9.9666
c 0
b 0
f 0
1
<?php
2
3
declare(strict_types=1);
4
5
/*
6
 * This file is part of the Packagist Mirror.
7
 *
8
 * For the full license information, please view the LICENSE.md
9
 * file that was distributed with this source code.
10
 */
11
12
namespace Webs\Mirror\Command;
13
14
use Symfony\Component\Console\Input\InputInterface;
15
use Symfony\Component\Console\Input\InputOption;
16
use Symfony\Component\Console\Output\OutputInterface;
17
use stdClass;
18
19
/**
20
 * Clean mirror outdated files.
21
 *
22
 * @author Webysther Nunes <[email protected]>
23
 */
24
class Clean extends Base
25
{
26
    /**
27
     * @var array
28
     */
29
    protected $changed = [];
30
31
    /**
32
     * @var array
33
     */
34
    protected $removed = [];
35
36
    /**
37
     * @var bool
38
     */
39
    protected $isScrub = false;
40
41
    /**
42
     * {@inheritdoc}
43
     */
44 1
    public function __construct($name = '')
45
    {
46 1
        parent::__construct('clean');
47 1
        $this->setDescription(
48 1
            'Clean outdated files of mirror'
49
        );
50 1
    }
51
52
    /**
53
     * Console params configuration.
54
     */
55 1
    protected function configure():void
56
    {
57 1
        parent::configure();
58 1
        $this->addOption(
59 1
            'scrub',
60 1
            null,
61 1
            InputOption::VALUE_NONE,
62 1
            'Check all directories for old files, use only to check all disk'
63
        );
64 1
    }
65
66
    /**
67
     * {@inheritdoc}
68
     */
69
    public function execute(InputInterface $input, OutputInterface $output):int
70
    {
71
        $this->initialize($input, $output);
72
        $this->bootstrap();
73
74
        if ($input->hasOption('scrub') && $input->getOption('scrub')) {
75
            $this->isScrub = true;
76
        }
77
78
        $this->flushProviders();
79
        $this->flushPackages();
80
81
        if (!count($this->changed)) {
82
            $output->writeln('<info>Nothing to clean</>');
83
        }
84
85
        return $this->getExitCode();
86
    }
87
88
    /**
89
     * @return void
90
     */
91
    public function bootstrap():void
92
    {
93
        $this->progressBar->setConsole($this->input, $this->output);
94
        $this->package->setConsole($this->input, $this->output);
95
        $this->package->setHttp($this->http);
96
        $this->package->setFilesystem($this->filesystem);
97
        $this->provider->setConsole($this->input, $this->output);
98
        $this->provider->setHttp($this->http);
99
        $this->provider->setFilesystem($this->filesystem);
100
    }
101
102
    /**
103
     * Flush old cached files of providers.
104
     *
105
     * @return Clean
106
     */
107
    protected function flushProviders():Clean
108
    {
109
        if (!$this->filesystem->hasFile(self::MAIN)) {
110
            return $this;
111
        }
112
113
        $providers = json_decode($this->filesystem->read(self::MAIN));
114
        $includes = array_keys($this->provider->normalize($providers));
115
116
        $this->initialized = $this->filesystem->hasFile(self::INIT);
117
118
        foreach ($includes as $uri) {
119
            $pattern = $this->filesystem->getGzName($this->shortname($uri));
120
            $glob = $this->filesystem->glob($pattern);
121
122
            $this->output->writeln(
123
                'Check old file of <info>'.
124
                $pattern.
125
                '</>'
126
            );
127
128
            // If not have one file or not scrumbbing
129
            if (!(count($glob) > 1 || $this->isScrub)) {
130
                continue;
131
            }
132
133
            $this->changed[] = $uri;
134
            $uri = $this->filesystem->getFullPath($this->filesystem->getGzName($uri));
135
            $diff = array_diff($glob, [$uri]);
136
            $this->removeAll($diff)->showRemoved();
137
        }
138
139
        return $this;
140
    }
141
142
    /**
143
     * Flush old cached files of packages.
144
     *
145
     * @return bool True if work, false otherside
146
     */
147
    protected function flushPackages():bool
148
    {
149
        $increment = 0;
150
151
        foreach ($this->changed as $uri) {
152
            $providers = json_decode($this->filesystem->read($uri));
153
            $list = $this->package->normalize($providers->providers);
154
155
            $this->output->writeln(
156
                '['.++$increment.'/'.count($this->changed).'] '.
157
                'Check old packages for provider '.
158
                '<info>'.$this->shortname($uri).'</>'
159
            );
160
            $this->progressBar->start(count($list));
161
            $this->flushPackage(array_keys($list));
162
            $this->progressBar->end();
163
            if (!$this->progressBar->isDisabled()) {
164
                $this->output->write(PHP_EOL);
165
            }
166
            $this->showRemoved();
167
        }
168
169
        return true;
170
    }
171
172
    /**
173
     * Flush from one provider.
174
     *
175
     * @param array $list List of packages
176
     */
177
    protected function flushPackage(array $list):void
178
    {
179
        $packages = $this->package->getDownloaded();
180
181
        foreach ($list as $uri) {
182
            $this->progressBar->progress();
183
184
            if ($this->canSkipPackage($uri, $packages)) {
185
                continue;
186
            }
187
188
            $gzName = $this->filesystem->getGzName($uri);
189
            $pattern = $this->shortname($gzName);
190
            $glob = $this->filesystem->glob($pattern);
191
192
            // If only have the file dont exist old files
193
            if (count($glob) < 2) {
194
                continue;
195
            }
196
197
            // Remove current value
198
            $fullPath = $this->filesystem->getFullPath($gzName);
199
            $diff = array_diff($glob, [$fullPath]);
200
            $this->removeAll($diff);
201
        }
202
    }
203
204
    /**
205
     * @param string $uri
206
     * @param array  $packages
207
     * @return bool
208
     */
209
    protected function canSkipPackage(string $uri, array $packages):bool
210
    {
211
        if ($this->initialized) {
212
            return true;
213
        }
214
215
        $folder = dirname($uri);
216
217
        // This uri was changed by last download?
218
        if (count($packages) && !in_array($uri, $packages)) {
219
            return true;
220
        }
221
222
        // If only have the file and link dont exist old files
223
        if ($this->filesystem->getCount($folder) < 3) {
224
            return true;
225
        }
226
227
        return false;
228
    }
229
230
    /**
231
     * Remove all files
232
     *
233
     * @param  array  $files
234
     * @return Clean
235
     */
236
    protected function removeAll(array $files):Clean
237
    {
238
        foreach ($files as $file) {
239
            $this->filesystem->delete($file);
240
        }
241
242
        $this->removed = [];
243
        if ($this->isVerbose()) {
244
            $this->removed = $files;
245
        }
246
247
        return $this;
248
    }
249
250
    /**
251
     * Show packages removed.
252
     *
253
     * @return Clean
254
     */
255
    protected function showRemoved():Clean
256
    {
257
        $base = getenv('PUBLIC_DIR').DIRECTORY_SEPARATOR;
258
259
        foreach ($this->removed as $file) {
260
            $file = str_replace($base, '', $file);
261
            $this->output->writeln(
262
                'File <fg=blue;>'.$file.'</> was removed!'
263
            );
264
        }
265
266
        $this->removed = [];
267
268
        return $this;
269
    }
270
}
271