FileDriver::doInitialize()   A
last analyzed

Complexity

Conditions 4
Paths 5

Size

Total Lines 14
Code Lines 6

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 7
CRAP Score 4

Importance

Changes 0
Metric Value
eloc 6
c 0
b 0
f 0
dl 0
loc 14
ccs 7
cts 7
cp 1
rs 10
cc 4
nc 5
nop 1
crap 4
1
<?php
2
/**
3
 * This file is part of the Shieldon package.
4
 *
5
 * (c) Terry L. <[email protected]>
6
 *
7
 * For the full copyright and license information, please view the LICENSE
8
 * file that was distributed with this source code.
9
 *
10
 * php version 7.1.0
11
 *
12
 * @category  Web-security
13
 * @package   Shieldon
14
 * @author    Terry Lin <[email protected]>
15
 * @copyright 2019 terrylinooo
16
 * @license   https://github.com/terrylinooo/shieldon/blob/2.x/LICENSE MIT
17
 * @link      https://github.com/terrylinooo/shieldon
18
 * @see       https://shieldon.io
19
 */
20
21
declare(strict_types=1);
22
23
namespace Shieldon\Firewall\Driver;
24
25
use Shieldon\Firewall\Driver\DriverProvider;
26
use Shieldon\Firewall\Driver\FileDriverTrait;
27
use RecursiveDirectoryIterator;
28
use RecursiveIteratorIterator;
29
use function file_exists;
30
use function file_get_contents;
31
use function file_put_contents;
32
use function in_array;
33
use function is_dir;
34
use function json_decode;
35
use function ksort;
36
use function rmdir;
37
use function touch;
38
use function unlink;
39
40
/**
41
 * File Driver.
42
 */
43
class FileDriver extends DriverProvider
44
{
45
    use FileDriverTrait;
46
47
    /**
48
     * The directory that data files stored to.
49
     *
50
     * @var string
51
     */
52
    protected $directory = '/tmp/';
53
54
55
    /**
56
     * The file's extension name'.
57
     *
58
     * @var string
59
     */
60
    protected $extension = 'json';
61
62
    /**
63
     * Constructor.
64
     *
65
     * @param string $directory The directory for storing data files.
66
     */
67 132
    public function __construct(string $directory = '')
68
    {
69 132
        if ('' !== $directory) {
70 132
            $this->directory = $directory;
71
        }
72
    }
73
74
    /**
75
     * Initialize data tables.
76
     *
77
     * @param bool $dbCheck This is for creating data tables automatically
78
     *                      Turn it off, if you don't want to check data tables every pageview.
79
     *
80
     * @return void
81
     */
82 101
    protected function doInitialize(bool $dbCheck = true): void
83
    {
84 101
        if (!$this->isInitialized) {
85 101
            if (!empty($this->channel)) {
86 18
                $this->setChannel($this->channel);
87
            }
88
89
            // Check the directory where data files write into.
90 101
            if ($dbCheck) {
91 101
                $this->createDirectory();
92
            }
93
        }
94
95 101
        $this->isInitialized = true;
96
    }
97
98
    /**
99
     * {@inheritDoc}
100
     *
101
     * @param string $type The type of the data table.
102
     *
103
     * @return array
104
     */
105 29
    protected function doFetchAll(string $type = 'filter'): array
106
    {
107 29
        $results = [];
108
109 29
        $this->assertInvalidDataTable($type);
110
111 29
        $dir = $this->getDirectory($type);
112
113 29
        if (is_dir($dir)) {
114 29
            $it = new RecursiveDirectoryIterator($dir, RecursiveDirectoryIterator::SKIP_DOTS);
115 29
            $files = new RecursiveIteratorIterator($it, RecursiveIteratorIterator::CHILD_FIRST);
116
117 29
            foreach ($files as $file) {
118 22
                if ($file->isFile()) {
119
                    $filename = $file->getPath() . '/' . $file->getFilename();
120 22
                    $fileContent = file_get_contents($filename);
121 22
122
                    if (!empty($fileContent)) {
123 22
                        $content = json_decode($fileContent, true);
124 22
125
                        if ($type === 'session') {
126 22
                            $sort = $content['microtimestamp'] . '.' . $file->getFilename();
127 20
                        } else {
128
                            $sort = $file->getMTime() . '.' . $file->getFilename();
129 2
                        }
130
                        $results[$sort] = $content;
131 22
                    }
132
                }
133
            }
134
            unset($it, $files);
135 29
136
            // Sort by ascending timestamp (microtimestamp).
137
            ksort($results);
138 29
        }
139
140
        return $results;
141 29
    }
142
143
    /**
144
     * {@inheritDoc}
145
     *
146
     * @param string $ip   The data id of the entry to fetch.
147
     * @param string $type The type of the data table.
148
     *
149
     * @return array
150
     */
151
    protected function doFetch(string $ip, string $type = 'filter'): array
152 44
    {
153
        $results = [];
154 44
155
        if (!file_exists($this->getFilename($ip, $type)) ||
156
            !in_array($type, $this->tableTypes)
157 44
        ) {
158 44
            return $results;
159
        }
160 24
161
        $fileContent = file_get_contents($this->getFilename($ip, $type));
162
        $resultData = json_decode($fileContent, true);
163 37
164 37
        // rule | session
165
        if (is_array($resultData)) {
166
            $results = $resultData;
167 37
        }
168 37
169
        // filter
170
        if (!empty($resultData['log_data'])) {
171
            return $resultData['log_data'];
172 37
        }
173 13
174
        return $results;
175
    }
176 30
177
    /**
178
     * {@inheritDoc}
179
     *
180
     * @param string $ip   The data id of the entry to check for.
181
     * @param string $type The type of the data table.
182
     *
183
     * @return bool
184
     */
185
    protected function checkExist(string $ip, string $type = 'filter'): bool
186
    {
187 2
        if (file_exists($this->getFilename($ip, $type))) {
188
            return true;
189 2
        }
190 1
191
        return false;
192
    }
193 1
194
    /**
195
     * {@inheritDoc}
196
     *
197
     * @param string $ip     The data id.
198
     * @param array  $data   The data.
199
     * @param string $type   The type of the data table.
200
     * @param int    $expire The data will be deleted after expiring.
201
     *
202
     * @return bool
203
     */
204
    protected function doSave(string $ip, array $data, string $type = 'filter', $expire = 0): bool
205
    {
206 104
        $logData = [];
207
208 104
        switch ($type) {
209
            case 'rule':
210
                $logData = $data;
211
                $logData['log_ip'] = $ip;
212 104
                break;
213 14
214 14
            case 'filter':
215 14
                $logData['log_ip'] = $ip;
216
                $logData['log_data'] = $data;
217 104
                break;
218 21
219 21
            case 'session':
220 21
                unset($data['parsed_data']);
221
                $logData = $data;
222 103
                break;
223 103
        }
224 103
225 103
        $result = file_put_contents($this->getFilename($ip, $type), json_encode($logData));
226
227
        // Update file time.
228 104
        touch($this->getFilename($ip, $type), time());
229
230
        return ($result > 0) ? true : false;
231 104
    }
232
233 104
    /**
234
     * {@inheritDoc}
235
     *
236
     * @param string $ip   The key name of a redis entry.
237
     * @param string $type The type of the data table.
238
     *
239
     * @return bool
240
     */
241
    protected function doDelete(string $ip, string $type = 'filter'): bool
242
    {
243
        if (in_array($type, ['rule', 'filter', 'session'])) {
244 17
            return $this->remove($this->getFilename($ip, $type));
245
        }
246 17
 
247 16
        return false;
248
    }
249
250 1
    /**
251
     * {@inheritDoc}
252
     *
253
     * Rebuild data tables.
254
     *
255
     * @return bool
256
     */
257
    protected function doRebuild(): bool
258
    {
259
        // Those are Shieldon logs directories.
260 35
        $tables = [
261
            'filter'  => $this->getDirectory('filter'),
262
            'rule'    => $this->getDirectory('rule'),
263 35
            'session' => $this->getDirectory('session'),
264 35
        ];
265 35
266 35
        // Remove them recursively.
267 35
        foreach ($tables as $dir) {
268
            if (file_exists($dir)) {
269
                $it = new RecursiveDirectoryIterator($dir, RecursiveDirectoryIterator::SKIP_DOTS);
270 35
                $files = new RecursiveIteratorIterator($it, RecursiveIteratorIterator::CHILD_FIRST);
271 35
    
272 32
                foreach ($files as $file) {
273 32
                    if ($file->isDir()) {
274
                        // @codeCoverageIgnoreStart
275 32
                        rmdir($file->getRealPath());
276 29
                        // @codeCoverageIgnoreEnd
277
                    } else {
278
                        unlink($file->getRealPath());
279
                    }
280
                }
281 29
                unset($it, $files);
282
283
                if (is_dir($dir)) {
284 32
                    rmdir($dir);
285
                }
286 32
            }
287 35
        }
288
289
        return $this->createDirectory();
290
    }
291
292 35
    /**
293
     * Remove a Shieldon log file.
294
     * Removing a log file works as the same as removing a SQL table's row.
295
     *
296
     * @param string $logFilePath The absolute path of the log file.
297
     *
298
     * @return bool
299
     */
300
    private function remove(string $logFilePath): bool
301
    {
302
        if (file_exists($logFilePath)) {
303 16
            return unlink($logFilePath);
304
        }
305 16
        return false;
306 15
    }
307
}
308