Passed
Push — master ( 953993...8c8503 )
by Yaroslav
02:55
created

FilesystemStorage::storeFromUrl()   A

Complexity

Conditions 4
Paths 6

Size

Total Lines 26
Code Lines 16

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 16
CRAP Score 4

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 4
eloc 16
c 1
b 0
f 0
nc 6
nop 3
dl 0
loc 26
ccs 16
cts 16
cp 1
crap 4
rs 9.7333
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 8
    public function __construct(Filesystem $files, string $dirname)
36
    {
37 8
        $this->files   = $files;
38 8
        $this->dirname = rtrim($dirname, DIRECTORY_SEPARATOR) . DIRECTORY_SEPARATOR;
39 8
    }
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 5
    public function exists(string $filePath): bool
79
    {
80 5
        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 5
    public function storeFromUrl(string $url, ?string $filePath = null, ?\Closure $progressCallback = null): bool
104
    {
105 5
        $this->prepareDirectory();
106 5
        if (!$filePath) {
107 2
            $filePath = Str::afterLast($url, DIRECTORY_SEPARATOR);
108
        }
109
110 5
        $path = $this->dirname . ltrim($filePath, DIRECTORY_SEPARATOR);
111
112 5
        $dir = Str::beforeLast($path, DIRECTORY_SEPARATOR);
113 5
        $this->files->ensureDirectoryExists($dir, 0777, true);
114
115 5
        switch (config('geonames.storage.download_provider')) {
116 5
            case 'curl_php':
117 1
                $result = $this->downloadViaCurlPhp($url, $path, $progressCallback);
118
119 1
                break;
120 4
            case 'wget':
121 3
                $result = $this->downloadViaWget($url, $path);
122
123 3
                break;
124
            default:
125 1
                throw new Exception('Current download provider not supported');
126
        }
127
128 4
        return $result;
129
    }
130
131
    /**
132
     * Extract zip file.
133
     *
134
     * @param string $file
135
     *
136
     * @return bool
137
     * @throws Exception
138
     */
139 2
    public function extractZipFile(string $file): bool
140
    {
141 2
        $zipArchive = new ZipArchive();
142 2
        $filePath   = $this->dirname . trim($file, DIRECTORY_SEPARATOR);
143 2
        if (true === ($openResult = $zipArchive->open($filePath, ZipArchive::RDONLY))) {
144 1
            $extractTo     = Str::beforeLast($filePath, DIRECTORY_SEPARATOR) . DIRECTORY_SEPARATOR;
145 1
            $extractResult = $zipArchive->extractTo($extractTo);
146 1
            $zipArchive->close();
147 1
            if ($extractResult === true) {
148 1
                return true;
149
            }
150
        }
151
152 1
        if (is_numeric($openResult)) {
153 1
            throw new Exception("Zip open [{$filePath}] error: {$openResult}. See: https://php.net/manual/en/zip.constants.php");
154
        }
155
156
        return false;
157
    }
158
159
    /**
160
     * Download File using wget util.
161
     *
162
     * @param string $url
163
     * @param string $path
164
     *
165
     * @return bool
166
     */
167 3
    protected function downloadViaWget(string $url, string $path): bool
168
    {
169 3
        $process = new Process([
170 3
            'wget',
171
            //'-c',
172 3
            '-N',
173 3
            '-O',
174 3
            $path,
175 3
            $url,
176
        ]);
177 3
        $process->run();
178
179 3
        return $process->isSuccessful();
180
    }
181
182
    /**
183
     * Download File using wget util.
184
     *
185
     * @param string $url
186
     * @param string $path
187
     *
188
     * @return bool
189
     * @throws Exception
190
     */
191 1
    protected function downloadViaCurlPhp(string $url, string $path, ?\Closure $progressCallback = null): bool
192
    {
193 1
        $fp = fopen($path, 'w');
194 1
        $ch = curl_init($url);
195 1
        if ($progressCallback) {
196 1
            curl_setopt($ch, CURLOPT_PROGRESSFUNCTION, $progressCallback);
197 1
            curl_setopt($ch, CURLOPT_NOPROGRESS, false);
198
        }
199 1
        curl_setopt($ch, CURLOPT_FILE, $fp);
200
201 1
        $result = curl_exec($ch);
202
203 1
        if ($errorNumber = curl_errno($ch)) {
204 1
            throw new Exception("Error [{$errorNumber}]. See https://php.net/manual/en/function.curl-errno.php");
205
        }
206
207 1
        curl_close($ch);
208 1
        fclose($fp);
209
210 1
        return (bool) $result;
211
    }
212
}
213