FileStorage::writeFile()   A
last analyzed

Complexity

Conditions 3
Paths 2

Size

Total Lines 10
Code Lines 5

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 6
CRAP Score 3

Importance

Changes 0
Metric Value
cc 3
eloc 5
nc 2
nop 1
dl 0
loc 10
ccs 6
cts 6
cp 1
crap 3
rs 10
c 0
b 0
f 0
1
<?php
2
/**
3
 * PhPsst.
4
 *
5
 * @copyright Copyright (c) 2018 Felix Sandström
6
 * @license   MIT
7
 */
8
9
namespace PhPsst\Storage;
10
11
use PhPsst\Password;
12
use PhPsst\PhPsstException;
13
14
/**
15
 * @author Felix Sandström <http://github.com/felixsand>
16
 */
17
class FileStorage extends Storage
18
{
19
    /**
20
     * @var string
21
     */
22
    protected $dir;
23
24
    /**
25
     * @var int
26
     */
27
    protected $gcProbability;
28
29
    /**
30
     * @const string
31
     */
32
    private const FILE_SUFFIX = '.phpsst';
33
34 10
    public function __construct(string $dir, int $gcProbability)
35
    {
36 10
        $dir = rtrim($dir, '/') . '/';
37 10
        if (empty($dir) || !is_dir($dir)) {
38 1
            throw new \RuntimeException('Invalid directory path');
39
        }
40
41 9
        if ($gcProbability < 0) {
42 1
            throw new \LogicException('Invalid value for gcProbability');
43
        }
44
45 8
        $this->dir = $dir;
46 8
        $this->gcProbability = $gcProbability;
47
    }
48
49 8
    public function store(Password $password, bool $allowOverwrite = false): void
50
    {
51 8
        if (!$allowOverwrite && file_exists($this->getFileName($password))) {
52 2
            throw new PhPsstException('The ID already exists', PhPsstException::ID_IS_ALREADY_TAKEN);
53
        }
54
55 8
        $this->writeFile($password);
56
    }
57
58 3
    public function get(string $key): ?Password
59
    {
60 3
        $password = null;
61 3
        if (file_exists($this->getFileNameFromKey($key))
62 3
            && ($passwordData = file_get_contents($this->getFileNameFromKey($key)))) {
63 3
            $password = $this->getPasswordFromJson($passwordData);
64
        }
65
66 3
        return $password;
67
    }
68
69 2
    public function delete(Password $password): void
70
    {
71 2
        unlink($this->getFileName($password));
72
    }
73
74 6
    protected function garbageCollection(): void
75
    {
76 6
        if (!$this->gcProbability || rand(1, $this->gcProbability) !== 1) {
77 1
            return;
78
        }
79
80 5
        $files = array_diff(scandir($this->dir, SCANDIR_SORT_NONE), array('.', '..'));
81 5
        foreach ($files as $file) {
82 5
            $fileContent = file_get_contents($this->dir . $file);
83 5
            $jsonData = json_decode($fileContent);
84 5
            if ($jsonData && $jsonData->ttl < time()) {
85 1
                unlink($this->dir . $file);
86
            }
87
        }
88
    }
89
90 7
    protected function writeFile(Password $password): void
91
    {
92 7
        $jsonData = $password->getJson();
93
94 7
        $fileName = $this->getFileName($password);
95 7
        if (!is_writable(\dirname($fileName)) || !file_put_contents($fileName, $jsonData)) {
96 1
            throw new \RuntimeException('Can not write file');
97
        }
98
99 6
        $this->garbageCollection();
100
    }
101
102 4
    protected function getFileName(Password $password): string
103
    {
104 4
        return $this->getFileNameFromKey($password->getId());
105
    }
106
107 4
    protected function getFileNameFromKey(string $key): string
108
    {
109 4
        return $this->dir . $key . self::FILE_SUFFIX;
110
    }
111
}
112