Completed
Push — master ( 536f4f...d0dfb4 )
by Felix
05:13
created

FileStorage   A

Complexity

Total Complexity 26

Size/Duplication

Total Lines 143
Duplicated Lines 0 %

Coupling/Cohesion

Components 1
Dependencies 2

Test Coverage

Coverage 96.43%

Importance

Changes 6
Bugs 1 Features 1
Metric Value
wmc 26
c 6
b 1
f 1
lcom 1
cbo 2
dl 0
loc 143
ccs 54
cts 56
cp 0.9643
rs 10

8 Methods

Rating   Name   Duplication   Size   Complexity  
A __construct() 0 14 4
A store() 0 8 3
C get() 0 23 8
A delete() 0 4 1
B garbageCollection() 0 15 5
A writeFile() 0 17 3
A getFileName() 0 4 1
A getFileNameFromKey() 0 4 1
1
<?php
2
/**
3
 * PhPsst.
4
 *
5
 * @copyright Copyright (c) 2016 Felix Sandström
6
 * @license   MIT
7
 */
8
9
namespace PhPsst\Storage;
10
11
use PhPsst\Password;
12
use PhPsst\PhPsstException;
13
14
/**
15
 */
16
class FileStorage implements StorageInterface
17
{
18
    /**
19
     * @var string
20
     */
21
    protected $dir;
22
23
    /**
24
     * @var int
25
     */
26
    protected $gcProbability;
27
28
    /**
29
     * @const string
30
     */
31
    const FILE_SUFFIX = '.phpsst';
32
33
    /**
34
     * FileStorage constructor.
35
     * @param string $dir
36
     * @param int $gcProbability
37
     */
38 8
    public function __construct($dir, $gcProbability)
39
    {
40 8
        $dir = rtrim($dir, '/') . '/';
41 8
        if (empty($dir) || !is_dir($dir)) {
42 1
            throw new \RuntimeException('Invalid directory path');
43
        }
44
45 7
        if ($gcProbability < 0) {
46
            throw new \LogicException('Invalid value for gcProbability');
47
        }
48
49 7
        $this->dir = $dir;
50 7
        $this->gcProbability = $gcProbability;
51 7
    }
52
53
    /**
54
     * @param Password $password
55
     * @param bool $allowOverwrite
56
     */
57 7
    public function store(Password $password, $allowOverwrite = false)
58
    {
59 7
        if (!$allowOverwrite && file_exists($this->getFileName($password))) {
60 1
            throw new PhPsstException('The ID already exists', PhPsstException::ID_IS_ALREADY_TAKEN);
61
        }
62
63 7
        $this->writeFile($password);
64 6
    }
65
66
    /**
67
     * @param $key
68
     * @return Password|null
69
     */
70 3
    public function get($key)
71
    {
72 3
        $password = null;
73 3
        if (!file_exists($this->getFileNameFromKey($key))) {
74 1
            throw new PhPsstException('No such ID exists', PhPsstException::NO_PASSWORD_WITH_ID_FOUND);
75
        }
76
77 3
        if (($jsonData = json_decode(file_get_contents($this->getFileNameFromKey($key))))) {
78 3
            if (!empty($jsonData->id)
79 3
                && !empty($jsonData->password)
80 3
                && !empty($jsonData->ttl)
81 3
                && !empty($jsonData->views)
82
            ) {
83 3
                $password = new Password($jsonData->id, $jsonData->password, $jsonData->ttl, $jsonData->views);
84 3
                if ($jsonData->ttlTime < time()) {
85 1
                    $this->delete($password);
86 1
                    throw new PhPsstException('No such ID exists', PhPsstException::NO_PASSWORD_WITH_ID_FOUND);
87
                }
88
            }
89
        }
90
91 3
        return $password;
92
    }
93
94
    /**
95
     * @param Password $password
96
     */
97 2
    public function delete(Password $password)
98
    {
99 2
        unlink($this->getFileName($password));
100 2
    }
101
102
    /**
103
     */
104 5
    protected function garbageCollection()
105
    {
106 5
        if (rand(1, $this->gcProbability) !== 1) {
107
            return;
108
        }
109
110 5
        $files = array_diff(scandir($this->dir), array('.', '..'));
111 5
        foreach ($files as $file) {
112 5
            if (($jsonData = json_decode(file_get_contents($this->dir . $file)))) {
113 5
                if ($jsonData->ttlTime < time()) {
114 5
                    unlink($this->dir . $file);
115
                }
116
            }
117
        }
118 5
    }
119
120
    /**
121
     * @param Password $password
122
     */
123 6
    protected function writeFile(Password $password)
124
    {
125 6
        $jsonData = json_encode([
126 6
            'id' => $password->getId(),
127 6
            'password' => $password->getPassword(),
128 6
            'ttl' => $password->getTtl(),
129 6
            'ttlTime' => time() + $password->getTtl(),
130 6
            'views' => $password->getViews(),
131
        ]);
132
133 6
        $fileName = $this->getFileName($password);
134 6
        if (!is_writable(dirname($fileName)) || !file_put_contents($fileName, $jsonData)) {
135 1
            throw new \RuntimeException('Can not write file');
136
        }
137
138 5
        $this->garbageCollection();
139 5
    }
140
141
    /**
142
     * @param Password $password
143
     * @return string
144
     */
145 3
    protected function getFileName(Password $password)
146
    {
147 3
        return $this->getFileNameFromKey($password->getId());
148
    }
149
150
    /**
151
     * @param string $key
152
     * @return string
153
     */
154 3
    protected function getFileNameFromKey($key)
155
    {
156 3
        return $this->dir . $key . self::FILE_SUFFIX;
157
    }
158
}
159