Completed
Push — feature/decoupled ( b00aba...a10681 )
by Webysther
02:10
created

Filesystem::hasFile()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 3
Code Lines 1

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 2

Importance

Changes 0
Metric Value
cc 1
eloc 1
nc 1
nop 1
dl 0
loc 3
ccs 0
cts 3
cp 0
crap 2
rs 10
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;
13
14
use League\Flysystem\Filesystem as FlyFilesystem;
15
use League\Flysystem\Adapter\Local;
16
use League\Flysystem\Cached\CachedAdapter;
17
use League\Flysystem\Cached\Storage\Memory;
18
use Exception;
19
use FilesystemIterator;
20
21
/**
22
 * Middleware to access filesystem with transparent gz encode/decode.
23
 *
24
 * @author Webysther Nunes <[email protected]>
25
 */
26
class Filesystem
27
{
28
    use GZip;
29
30
    /**
31
     * @var FlyFilesystem
32
     */
33
    protected $filesystem;
34
35
    /**
36
     * @var string
37
     */
38
    protected $directory;
39
40
    /**
41
     * Ephemeral cache for folder files count.
42
     *
43
     * @var array
44
     */
45
    protected $countedFolder = [];
46
47
    /**
48
     * @param string $dir        Base directory
49
     * @param bool   $initialize If true initialize the filesystem access
50
     */
51
    public function __construct($baseDirectory)
52
    {
53
        $this->directory = realpath($baseDirectory).DIRECTORY_SEPARATOR;
54
55
        // Create the adapter
56
        $localAdapter = new Local($this->directory);
57
58
        // Create the cache store
59
        $cacheStore = new Memory();
60
61
        // Decorate the adapter
62
        $adapter = new CachedAdapter($localAdapter, $cacheStore);
63
64
        // And use that to create the file system
65
        $this->filesystem = new FlyFilesystem($adapter);
66
    }
67
68
    /**
69
     * Add suffix gz to json file
70
     *
71
     * @param string $path
72
     *
73
     * @return string
74
     */
75 View Code Duplication
    protected function getGzName(string $path):string
1 ignored issue
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
76
    {
77
        $fullPath = $this->getFullPath($path);
78
        $extension = pathinfo($fullPath, PATHINFO_EXTENSION);
79
80
        if ($extension == 'json') {
81
            return $path.'.gz';
82
        }
83
84
        return $path;
85
    }
86
87
    /**
88
     * Get link name from gz
89
     *
90
     * @param  string $path
91
     * @return string
92
     */
93 View Code Duplication
    protected function getLink(string $path):string
1 ignored issue
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
94
    {
95
        $fullPath = $this->getFullPath($path);
96
        $extension = pathinfo($fullPath, PATHINFO_EXTENSION);
97
98
        if ($extension == 'gz') {
99
            return substr($path, 0, -3);
100
        }
101
102
        return $path;
103
    }
104
105
    /**
106
     * Decode from gz after read from disk.
107
     *
108
     * @see FlyFilesystem::read
109
     */
110
    public function read(string $path):string
111
    {
112
        $path = $this->getGzName($path);
113
        $file = $this->filesystem->read($path);
114
115
        if ($file === false) {
116
            return '';
117
        }
118
119
        return $this->decode($file);
120
    }
121
122
    /**
123
     * Encode to gz before write to disk with hash checking.
124
     *
125
     * @see FlyFilesystem::write
126
     */
127
    public function write(string $path, string $contents):Filesystem
128
    {
129
        $file = $this->getGzName($path);
130
        $this->filesystem->put($file, $this->encode($contents));
131
        $decoded = $this->decode($contents);
132
133
        if ($this->getHash($decoded) != $this->getHashFile($file)) {
134
            $this->filesystem->delete($file);
135
            throw new Exception("Write file $path hash failed");
136
        }
137
138
        $this->symlink($file);
139
140
        return $this;
141
    }
142
143
    /**
144
     * Simple touch.
145
     *
146
     * @param string $path
147
     *
148
     * @return Filesystem
149
     */
150
    public function touch(string $path):Filesystem
151
    {
152
        if ($this->has($path)) {
153
            return $this;
154
        }
155
156
        touch($this->getFullPath($path));
157
158
        return $this;
159
    }
160
161
    /**
162
     * @param  string  $file
163
     * @return boolean
164
     */
165
    protected function isGzFile(string $file):bool
166
    {
167
        if(substr($this->getGzName($file), -3) == '.gz'){
168
            return true;
169
        }
170
171
        return false;
172
    }
173
174
    /**
175
     * Create a symlink.
176
     *
177
     * @param string $file
178
     *
179
     * @return Filesystem
180
     */
181
    protected function symlink(string $file):Filesystem
182
    {
183
        if (!$this->hasFile($file) || !$this->isGzFile($file)) {
184
            return $this;
185
        }
186
187
        $path = $this->getGzName($file);
188
        $link = $this->getLink($path);
189
190
        if($this->hasLink($link)){
191
            return $this;
192
        }
193
194
        symlink(basename($path), $this->getFullPath($link));
195
196
        return $this;
197
    }
198
199
    /**
200
     * @see FlyFilesystem::has
201
     */
202
    public function has(string $path):bool
203
    {
204
        return $this->hasFile($path) && $this->hasLink($path);
205
    }
206
207
    /**
208
     * @see FlyFilesystem::has
209
     */
210
    protected function hasFile(string $path):bool
211
    {
212
        return file_exists($this->getFullPath($this->getGzName($path)));
213
    }
214
215
    /**
216
     * @see FlyFilesystem::has
217
     */
218
    protected function hasLink(string $path):bool
219
    {
220
        return is_link($this->getFullPath($this->getLink($path)));
221
    }
222
223
    /**
224
     * Move to not dot name of file
225
     *
226
     * @param string $from
227
     *
228
     * @return Filesystem
229
     */
230
    public function move(string $from):Filesystem
231
    {
232
        $file = $this->getGzName($from);
233
        $target = substr($file, 1);
234
        $this->filesystem->rename($from, $target);
235
        $this->symlink($target);
236
        return $this;
237
    }
238
239
    /**
240
     * @see FlyFilesystem::delete
241
     * @see FlyFilesystem::deleteDir
242
     */
243
    public function delete(string $fileOrDirectory):Filesystem
244
    {
245
        $path = $this->getFullPath($fileOrDirectory);
246
247
        if (is_dir($path)) {
248
            $this->filesystem->deleteDir($fileOrDirectory);
249
250
            return $this;
251
        }
252
253
        $file = $this->getGzName($path);
254
        if (file_exists($file)) {
255
            unlink($file);
256
        }
257
258
        $link = $this->getLink($path);
259
        if (is_link($link)) {
260
            unlink($file);
261
        }
262
263
        return $this;
264
    }
265
266
    /**
267
     * Glob without file sort.
268
     *
269
     * @param string $pattern
270
     *
271
     * @return array
272
     */
273
    public function glob(string $pattern):array
274
    {
275
        $return = glob($pattern, GLOB_NOSORT);
276
277
        if ($return === false) {
278
            return [];
279
        }
280
281
        return $return;
282
    }
283
284
    /**
285
     * Count files inside folder, if is a file, return 0.
286
     *
287
     * @param string $folder
288
     *
289
     * @return int
290
     */
291
    public function getCount(string $folder):int
292
    {
293
        $path = $this->getFullPath($folder);
294
        $hash = $this->getHash($path);
295
296
        if (!is_dir($path)) {
297
            return 0;
298
        }
299
300
        if (array_key_exists($hash, $this->countedFolder)) {
301
            return $this->countedFolder[$hash];
302
        }
303
304
        $iterator = new FilesystemIterator(
305
            $path,
306
            FilesystemIterator::SKIP_DOTS
307
        );
308
309
        $totalFiles = iterator_count($iterator);
310
        $this->countedFolder[$hash] = $totalFiles;
311
312
        return $totalFiles;
313
    }
314
315
    /**
316
     * Get full path.
317
     *
318
     * @param string $path
319
     *
320
     * @return string
321
     */
322
    protected function getFullPath(string $path):string
323
    {
324
        if(strpos($path, $this->directory) !== false){
325
            return $path;
326
        }
327
328
        return $this->directory.$path;
329
    }
330
331
    /**
332
     * Calculates SHA256.
333
     *
334
     * @param string $string
335
     *
336
     * @return string
337
     */
338
    public function getHash(string $string):string
339
    {
340
        return hash('sha256', $string);
341
    }
342
343
    /**
344
     * Calculates SHA256 for file.
345
     *
346
     * @param string $path
347
     *
348
     * @return string
349
     */
350
    public function getHashFile(string $path):string
351
    {
352
        // dont use hash_file because content is saved with gz
353
        return $this->getHash($this->read($path));
354
    }
355
}
356