Passed
Push — master ( 610dd1...d8a0ed )
by Yaroslav
03:07
created

FilesystemStorage::createFromUrl()   B

Complexity

Conditions 7
Paths 14

Size

Total Lines 35
Code Lines 21

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 21
CRAP Score 7

Importance

Changes 0
Metric Value
cc 7
eloc 21
nc 14
nop 4
dl 0
loc 35
ccs 21
cts 21
cp 1
crap 7
rs 8.6506
c 0
b 0
f 0
1
<?php
2
3
namespace LaraGeoData\Storage;
4
5
use Exception;
6
use Illuminate\Filesystem\Filesystem;
7
use Illuminate\Support\Str;
8
use Symfony\Component\Process\Process;
9
use ZipArchive;
10
11
/**
12
 * Stores collected data into files
13
 */
14
class FilesystemStorage
15
{
16
17
    /**
18
     * Provider.
19
     *
20
     * @var Filesystem
21
     */
22
    protected Filesystem $files;
23
24
    /**
25
     * Directory name.
26
     *
27
     * @var string
28
     */
29
    protected string $dirname;
30
31
    /**
32
     * @param Filesystem $files
33
     * @param string $dirname
34
     */
35 11
    public function __construct(Filesystem $files, string $dirname)
36
    {
37 11
        $this->files   = $files;
38 11
        $this->dirname = rtrim($dirname, DIRECTORY_SEPARATOR) . DIRECTORY_SEPARATOR;
39 11
    }
40
41
    /**
42
     * Create directory if not exists.
43
     *
44
     * @throws Exception
45
     */
46 7
    public function prepareDirectory(): void
47
    {
48 7
        if (!$this->files->isDirectory($this->dirname)) {
49 7
            if ($this->files->makeDirectory($this->dirname, 0777, true)) {
50 6
                $this->files->put($this->dirname . '.gitignore', "*\n!.gitignore\n");
51
            } else {
52
                throw new Exception("Cannot create directory '$this->dirname'.");
53
            }
54
        }
55 6
    }
56
57
    /**
58
     * Clear all stored files.
59
     *
60
     * @return bool
61
     * @throws Exception
62
     */
63 4
    public function truncate(): bool
64
    {
65 4
        $result = $this->files->deleteDirectory($this->dirname);
66 4
        $this->prepareDirectory();
67
68 4
        return $result;
69
    }
70
71
    /**
72
     * CheckIf File exists.
73
     *
74
     * @param string $filePath
75
     *
76
     * @return bool
77
     */
78 6
    public function exists(string $filePath): bool
79
    {
80 6
        return $this->files->exists($this->dirname . ltrim($filePath, DIRECTORY_SEPARATOR));
81
    }
82
83
    /**
84
     * Delete file.
85
     *
86
     * @param string $filePath
87
     *
88
     * @return bool
89
     */
90 1
    public function delete(string $filePath): bool
91
    {
92 1
        return  $this->files->delete($this->dirname . ltrim($filePath, DIRECTORY_SEPARATOR));
93
    }
94
95
    /**
96
     * @param string $url
97
     * @param string|null $filePath
98
     * @param \Closure|null $progressCallback
99
     *
100
     * @return bool
101
     * @throws Exception
102
     */
103 6
    public function createFromUrl(string $url, ?string $filePath = null, ?\Closure $progressCallback = null, bool $force = false): bool
104
    {
105 6
        $this->prepareDirectory();
106 6
        if (!$filePath) {
107 2
            $filePath = Str::afterLast($url, DIRECTORY_SEPARATOR);
108
        }
109
110 6
        $exists = $this->exists($filePath);
111 6
        if ($exists && !$force) {
112 2
            return false;
113
        }
114
115 5
        if ($exists) {
116 1
            $this->delete($filePath);
117
        }
118
119 5
        $path = $this->dirname . ltrim($filePath, DIRECTORY_SEPARATOR);
120
121 5
        $dir = Str::beforeLast($path, DIRECTORY_SEPARATOR);
122 5
        $this->files->ensureDirectoryExists($dir, 0777, true);
123
124 5
        switch (config('geonames.storage.download_provider')) {
125 5
            case 'curl_php':
126 1
                $result = $this->downloadViaCurlPhp($url, $path, $progressCallback);
127
128 1
                break;
129 4
            case 'wget':
130 3
                $result = $this->downloadViaWget($url, $path);
131
132 3
                break;
133
            default:
134 1
                throw new Exception('Current download provider not supported');
135
        }
136
137 4
        return $result;
138
    }
139
140
    /**
141
     * Extract zip file.
142
     *
143
     * @param string $file
144
     *
145
     * @return bool
146
     * @throws Exception
147
     */
148 2
    public function extractZipFile(string $file): bool
149
    {
150 2
        $zipArchive = new ZipArchive();
151 2
        $filePath   = $this->dirname . trim($file, DIRECTORY_SEPARATOR);
152 2
        if (true === ($openResult = $zipArchive->open($filePath, ZipArchive::RDONLY))) {
153 1
            $extractTo     = Str::beforeLast($filePath, DIRECTORY_SEPARATOR) . DIRECTORY_SEPARATOR;
154 1
            $extractResult = $zipArchive->extractTo($extractTo);
155 1
            $zipArchive->close();
156 1
            if ($extractResult === true) {
157 1
                return true;
158
            }
159
        }
160
161 1
        if (is_numeric($openResult)) {
162 1
            throw new Exception("Zip open [{$filePath}] error: {$openResult}. See: https://php.net/manual/en/zip.constants.php");
163
        }
164
165
        return false;
166
    }
167
168
    /**
169
     * Download File using wget util.
170
     *
171
     * @param string $url
172
     * @param string $path
173
     *
174
     * @return bool
175
     */
176 3
    protected function downloadViaWget(string $url, string $path): bool
177
    {
178 3
        $process = new Process([
179 3
            'wget',
180
            //'-c',
181 3
            '-N',
182 3
            '-O',
183 3
            $path,
184 3
            $url,
185
        ]);
186 3
        $process->run();
187
188 3
        return $process->isSuccessful();
189
    }
190
191
    /**
192
     * Download File using wget util.
193
     *
194
     * @param string $url
195
     * @param string $path
196
     *
197
     * @return bool
198
     * @throws Exception
199
     */
200 1
    protected function downloadViaCurlPhp(string $url, string $path, ?\Closure $progressCallback = null): bool
201
    {
202 1
        $fp = fopen($path, 'w');
203 1
        $ch = curl_init($url);
204 1
        if ($progressCallback) {
205 1
            curl_setopt($ch, CURLOPT_PROGRESSFUNCTION, $progressCallback);
206 1
            curl_setopt($ch, CURLOPT_NOPROGRESS, false);
207
        }
208 1
        curl_setopt($ch, CURLOPT_FILE, $fp);
209
210 1
        $result = curl_exec($ch);
211
212 1
        if ($errorNumber = curl_errno($ch)) {
213 1
            throw new Exception("Error [{$errorNumber}]. See https://php.net/manual/en/function.curl-errno.php");
214
        }
215
216 1
        curl_close($ch);
217 1
        fclose($fp);
218
219 1
        return (bool) $result;
220
    }
221
}
222