Completed
Push — master ( 7a3330...a6bb7a )
by Dorian
01:45
created

FilesystemManager::shouldRemoveFolders()   C

Complexity

Conditions 8
Paths 6

Size

Total Lines 67
Code Lines 40

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 67
rs 6.6523
c 0
b 0
f 0
cc 8
eloc 40
nc 6
nop 2

How to fix   Long Method   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

1
<?php declare(strict_types=1);
2
3
namespace App\Filesystem;
4
5
use App\Domain\Collection\Downloads;
6
use App\Domain\Collection\Path;
7
use App\Domain\Download;
8
use App\UI\UserInterface;
9
use Symfony\Component\Filesystem\Filesystem;
10
use Symfony\Component\Finder\Finder;
11
12
abstract class FilesystemManager
13
{
14
    /** @var \App\UI\UserInterface */
15
    protected $ui;
16
17
    /**
18
     * @param \App\UI\UserInterface $ui
19
     */
20
    public function __construct(UserInterface $ui)
21
    {
22
        $this->ui = $ui;
23
    }
24
25
    /**
26
     * @param \App\Domain\Collection\Path $downloadPath
27
     *
28
     * @return \Symfony\Component\Finder\Finder
29
     */
30
    abstract protected function getAllDownloadsFolderFinder(Path $downloadPath): Finder;
31
32
    /**
33
     * @param \App\Domain\Download $download
34
     *
35
     * @return \Symfony\Component\Finder\Finder|\SplFileInfo[]
36
     * @throws \InvalidArgumentException
37
     */
38
    abstract protected function getDownloadFolderFinder(Download $download): Finder;
39
40
    /**
41
     * @return bool
42
     */
43
    protected function skip(): bool
44
    {
45
        if ($this->ui->isDryRun()) {
46
            $this->ui->writeln('<info>[DRY-RUN]</info> Not doing anything...'.PHP_EOL);
47
48
            return true;
49
        }
50
51
        return false;
52
    }
53
54
    /**
55
     * {@inheritdoc}
56
     * @param \App\Domain\Collection\Downloads $downloads
57
     *
58
     * @throws \RuntimeException
59
     */
60
    protected function cleanFilesystem(Downloads $downloads, Path $downloadPath): void
61
    {
62
        $foldersToRemove = $this->getFoldersToRemove($downloads, $downloadPath);
63
64
        if ($this->shouldRemoveFolders($foldersToRemove, $downloadPath)) {
65
            $this->removeFolders($foldersToRemove);
66
        }
67
    }
68
69
    /**
70
     * @param \App\Domain\Collection\Downloads $downloads
71
     * @param \App\Domain\Collection\Path $downloadPath
72
     *
73
     * @return \App\Filesystem\FilesystemObjects
74
     * @throws \RuntimeException
75
     */
76
    protected function getFoldersToRemove(Downloads $downloads, Path $downloadPath): FilesystemObjects
77
    {
78
        $foldersToRemove = new FilesystemObjects();
0 ignored issues
show
Bug introduced by
The call to FilesystemObjects::__construct() misses a required argument $elements.

This check looks for function calls that miss required arguments.

Loading history...
79
        try {
80
            $completedDownloadsFolders = $this->getCompletedDownloadsFolders($downloads);
81
82
            foreach ($this->getAllDownloadsFolderFinder($downloadPath)->getIterator() as $folder) {
83
                if (!$this->isFolderInCollection($folder, $foldersToRemove, true, $downloadPath) &&
84
                    !$this->isFolderInCollection($folder, $completedDownloadsFolders)
85
                ) {
86
                    $foldersToRemove->add($folder);
87
                }
88
            }
89
        } catch (\LogicException $e) {
90
            // Here we know that the download folder will exist.
91
        }
92
93
        return $foldersToRemove;
94
    }
95
96
    /**
97
     * Checks if a folder (or one of its parent, up to the $limit parameter) is found in the collection of folders.
98
     *
99
     * @param \SplFileInfo $folderToSearchFor
100
     * @param \App\Filesystem\FilesystemObjects $folders
101
     * @param bool $loopOverParentsFolders
102
     * @param \App\Domain\Collection\Path $untilPath
103
     *
104
     * @return bool
105
     * @throws \RuntimeException
106
     */
107
    protected function isFolderInCollection(
108
        \SplFileInfo $folderToSearchFor,
109
        FilesystemObjects $folders,
110
        bool $loopOverParentsFolders = false,
111
        ?Path $untilPath = null
112
    ): bool {
113
        foreach ($folders as $folder) {
114
            do {
115
                // This allows to match "/root/path" in "/root/path" or "/root/path/sub_path"
116
                if (0 === strpos($folder->getRealPath(), $folderToSearchFor->getRealPath())) {
117
                    return true;
118
                }
119
120
                if (!$loopOverParentsFolders) {
121
                    break;
122
                }
123
                if (null === $untilPath) {
124
                    throw new \RuntimeException(
125
                        'If $loopOverParentsFolders is set to true, then $untilPath must be provided.'.
126
                        'Otherwise you will experience infinite loops.'
127
                    );
128
                }
129
130
                $folderToSearchFor = $folderToSearchFor->getPathInfo();
131
132
            } while ($folderToSearchFor->getRealPath() !== (string) $untilPath);
133
        }
134
135
        return false;
136
    }
137
138
    /**
139
     * @param \App\Filesystem\FilesystemObjects $foldersToRemove
140
     */
141
    protected function removeFolders(FilesystemObjects $foldersToRemove): void
142
    {
143
        $errors = [];
144
        foreach ($foldersToRemove as $folderToRemove) {
145
            $relativeFolderPath = $folderToRemove->getRelativePathname();
146
147
            try {
148
                (new Filesystem())->remove($folderToRemove->getRealPath());
149
150
                $this->ui->writeln(
151
                    sprintf(
152
                        '%s* The folder <info>%s</info> has been removed.',
153
                        $this->ui->indent(2),
154
                        $relativeFolderPath
155
                    )
156
                );
157
            } catch (\Exception $e) {
158
                $this->ui->logError(
159
                    sprintf(
160
                        '%s* <error>The folder %s could not be removed.</error>',
161
                        $this->ui->indent(2),
162
                        $relativeFolderPath
163
                    ),
164
                    $errors
165
                );
166
            }
167
        }
168
        $this->ui->displayErrors($errors, 'the removal of folders', 'info', 1);
169
170
        $this->ui->writeln(PHP_EOL.'<info>Done.</info>'.PHP_EOL);
171
    }
172
173
    /**
174
     * @param \App\Domain\Collection\Downloads $downloads
175
     *
176
     * @return \App\Filesystem\FilesystemObjects
177
     */
178
    protected function getCompletedDownloadsFolders(Downloads $downloads): FilesystemObjects
179
    {
180
        $completedDownloadsFolders = new FilesystemObjects();
0 ignored issues
show
Bug introduced by
The call to FilesystemObjects::__construct() misses a required argument $elements.

This check looks for function calls that miss required arguments.

Loading history...
181
        foreach ($downloads as $download) {
182
            try {
183
                foreach ($this->getDownloadFolderFinder($download) as $downloadFolder) {
184
                    $completedDownloadsFolders->add($downloadFolder->getPathInfo());
185
                }
186
            } catch (\InvalidArgumentException $e) {
0 ignored issues
show
Coding Style Comprehensibility introduced by
Consider adding a comment why this CATCH block is empty.
Loading history...
187
            }
188
        }
189
190
        return $completedDownloadsFolders;
191
    }
192
193
    /**
194
     * @param \App\Filesystem\FilesystemObjects $foldersToRemove
195
     * @param \App\Domain\Collection\Path $downloadPath
196
     *
197
     * @return bool
198
     */
199
    protected function shouldRemoveFolders(FilesystemObjects $foldersToRemove, Path $downloadPath): bool
200
    {
201
        $this->ui->write(
202
            sprintf(
203
                'Synchronize the <info>%s</info> folder with the downloaded contents... ',
204
                (string) $downloadPath
205
            )
206
        );
207
208
        if ($foldersToRemove->isEmpty()) {
209
            $this->ui->writeln('<info>Done.</info>');
210
211
            return false;
212
        }
213
214
        $this->ui->writeln(PHP_EOL);
215
216
        if (!$this->ui->isDryRun() && !$this->ui->isInteractive()) {
217
            return true;
218
        }
219
220
        $confirmationDefault = true;
221
222
        // If there's less than 10 folders, we can display them
223
        $nbFoldersToRemove = $foldersToRemove->count();
224
        if ($nbFoldersToRemove <= 10) {
225
            $this->ui->writeln(
226
                sprintf(
227
                    '%sThe script is about to remove the following folders from <info>%s</info>:',
228
                    $this->ui->indent(),
229
                    (string) $downloadPath
230
                )
231
            );
232
            $this->ui->listing(
233
                $foldersToRemove
234
                    ->map(function (\SplFileInfo $folder) use ($downloadPath) {
235
                        return sprintf(
236
                            '<info>%s</info>',
237
                            str_replace((string) $downloadPath.DIRECTORY_SEPARATOR, '', $folder->getRealPath())
238
                        );
239
                    })
240
                    ->toArray(),
241
                3
242
            );
243
        } else {
244
            $confirmationDefault = false;
245
246
            $this->ui->write(
247
                sprintf(
248
                    '%sThe script is about to remove <question> %s </question> folders from <info>%s</info>. ',
249
                    $this->ui->indent(),
250
                    $nbFoldersToRemove,
251
                    (string) $downloadPath
252
                )
253
            );
254
        }
255
256
        $this->ui->write($this->ui->indent());
257
258
        if ($this->skip() || !$this->ui->confirm($confirmationDefault)) {
259
            $this->ui->writeln(($this->ui->isDryRun() ? '' : PHP_EOL).'<info>Done.</info>'.PHP_EOL);
260
261
            return false;
262
        }
263
264
        return true;
265
    }
266
}
267