Passed
Push — 2.x ( ac5abc...fbc7ae )
by Terry
02:06
created

FileDriver::getFilename()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 10
Code Lines 6

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 1
eloc 6
nc 1
nop 2
dl 0
loc 10
rs 10
c 0
b 0
f 0
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
11
declare(strict_types=1);
12
13
namespace Shieldon\Firewall\Driver;
14
15
use Shieldon\Firewall\Driver\DriverProvider;
16
use Shieldon\Firewall\Driver\FileDriverTrait;
17
use RecursiveDirectoryIterator;
18
use RecursiveIteratorIterator;
19
20
use function file_exists;
21
use function file_get_contents;
22
use function file_put_contents;
23
use function in_array;
24
use function is_dir;
25
use function json_decode;
26
use function ksort;
27
use function rmdir;
28
use function touch;
29
use function unlink;
30
31
/**
32
 * File Driver.
33
 */
34
class FileDriver extends DriverProvider
35
{
36
    use FileDriverTrait;
37
38
    /**
39
     * The directory that data files stored to.
40
     *
41
     * @var string
42
     */
43
    protected $directory = '/tmp/';
44
45
46
    /**
47
     * The file's extension name'.
48
     *
49
     * @var string
50
     */
51
    protected $extension = 'json';
52
53
54
    /**
55
     * A file that confirms the required dictories have been created.
56
     *
57
     * @var string
58
     */
59
    private $checkPoint = 'shieldon_check_exist.txt';
60
61
    /**
62
     * Constructor.
63
     *
64
     * @param string $directory
65
     */
66
    public function __construct(string $directory = '')
67
    {
68
        if ('' !== $directory) {
69
            $this->directory = $directory;
70
        }
71
    }
72
73
    /**
74
     * Initialize data tables.
75
     *
76
     * @param bool $dbCheck This is for creating data tables automatically
77
     *                      Turn it off, if you don't want to check data tables every pageview.
78
     *
79
     * @return void
80
     */
81
    protected function doInitialize(bool $dbCheck = true): void
82
    {
83
        if (!$this->isInitialized) {
84
            if (!empty($this->channel)) {
85
                $this->setChannel($this->channel);
86
            }
87
88
            // Check the directory where data files write into.
89
            $this->createDirectory();
90
        }
91
92
        $this->isInitialized = true;
93
    }
94
95
    /**
96
     * {@inheritDoc}
97
     */
98
    protected function doFetchAll(string $type = 'filter'): array
99
    {
100
        $results = [];
101
102
        if (!in_array($type, $this->tableTypes)) {
103
            return $results;
104
        }
105
106
        $dir = $this->getDirectory($type);
107
108
        if (is_dir($dir)) {
109
            $it = new RecursiveDirectoryIterator($dir, RecursiveDirectoryIterator::SKIP_DOTS);
110
            $files = new RecursiveIteratorIterator($it, RecursiveIteratorIterator::CHILD_FIRST);
111
112
            foreach ($files as $file) {
113
                if ($file->isFile()) {
114
115
                    $content = json_decode(file_get_contents($file->getPath() . '/' . $file->getFilename()), true);
116
117
                    if ($type === 'session') {
118
                        $sort = $content['microtimesamp'] . '.' . $file->getFilename(); 
119
                    } else {
120
                        $sort = $file->getMTime() . '.' . $file->getFilename();
121
                    }
122
                    $results[$sort] = $content;
123
                }
124
            }
125
            unset($it, $files);
126
127
            // Sort by ascending timesamp (microtimesamp).
128
            ksort($results);
129
        }
130
131
        return $results;
132
    }
133
134
    /**
135
     * {@inheritDoc}
136
     */
137
    protected function doFetch(string $ip, string $type = 'filter'): array
138
    {
139
        $results = [];
140
141
        if (
142
            !file_exists($this->getFilename($ip, $type)) || 
143
            !in_array($type, $this->tableTypes)
144
        ) {
145
            return $results;
146
        }
147
148
        $fileContent = file_get_contents($this->getFilename($ip, $type));
149
        $resultData = json_decode($fileContent, true);
150
151
        // rule | session
152
        if (is_array($resultData)) {
153
            $results = $resultData;
154
        }
155
156
        // filter 
157
        if (!empty($resultData['log_data'])) {
158
            return $resultData['log_data']; 
159
        }
160
161
        return $results;
162
    }
163
164
    /**
165
     * {@inheritDoc}
166
     */
167
    protected function checkExist(string $ip, string $type = 'filter'): bool
168
    {
169
        if (file_exists($this->getFilename($ip, $type))) {
170
            return true;
171
        }
172
173
        return false;
174
    }
175
176
    /**
177
     * {@inheritDoc}
178
     */
179
    protected function doSave(string $ip, array $data, string $type = 'filter', $expire = 0): bool
180
    {
181
        switch ($type) {
182
183
            case 'rule':
184
                $logData = $data;
185
                $logData['log_ip'] = $ip;
186
                break;
187
188
            case 'filter':
189
                $logData['log_ip'] = $ip;
0 ignored issues
show
Comprehensibility Best Practice introduced by
$logData was never initialized. Although not strictly required by PHP, it is generally a good practice to add $logData = array(); before regardless.
Loading history...
190
                $logData['log_data'] = $data;
191
                break;
192
193
            case 'session':
194
                $logData = $data;
195
                break;
196
        }
197
198
        $result = file_put_contents($this->getFilename($ip, $type), json_encode($logData));
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable $logData does not seem to be defined for all execution paths leading up to this point.
Loading history...
199
200
        // Update file time.
201
        touch($this->getFilename($ip, $type), time());
202
203
        return ($result > 0) ? true : false;
204
    }
205
206
    /**
207
     * {@inheritDoc}
208
     */
209
    protected function doDelete(string $ip, string $type = 'filter'): bool
210
    {
211
        switch ($type) {
212
            case 'rule':
213
                // no break
214
            case 'filter':
215
                // no break
216
            case 'session':
217
                return $this->remove($this->getFilename($ip, $type));
218
        }
219
220
        return false;
221
    }
222
223
    /**
224
     * {@inheritDoc}
225
     */
226
    protected function doRebuild(): bool
227
    {
228
        // Those are Shieldon logs directories.
229
        $removeDirs = [
230
            $this->getDirectory('filter'),
231
            $this->getDirectory('rule'),
232
            $this->getDirectory('session'),
233
        ];
234
        
235
        // Remove them recursively.
236
        foreach ($removeDirs as $dir) {
237
            if (file_exists($dir)) {
238
                $it = new RecursiveDirectoryIterator($dir, RecursiveDirectoryIterator::SKIP_DOTS);
239
                $files = new RecursiveIteratorIterator($it, RecursiveIteratorIterator::CHILD_FIRST);
240
    
241
                foreach ($files as $file) {
242
                    if ($file->isDir()) {
243
                        // @codeCoverageIgnoreStart
244
                        rmdir($file->getRealPath());
245
                        // @codeCoverageIgnoreEnd
246
                    } else {
247
                        unlink($file->getRealPath());
248
                    }
249
                }
250
                unset($it, $files);
251
252
                if (is_dir($dir)) {
253
                    rmdir($dir);
254
                }
255
            }
256
        }
257
258
        $checkingFile = $this->directory . '/' . $this->channel . '_' . $this->checkPoint;
259
260
        if (file_exists($checkingFile)) {
261
            unlink($checkingFile);
262
        }
263
264
        $conA = !is_dir($this->getDirectory('filter'));
265
        $conB = !is_dir($this->getDirectory('rule'));
266
        $conC = !is_dir($this->getDirectory('session'));
267
268
        // Check if are Shieldon directories removed or not.
269
        $result = ($conA && $conB && $conC);
270
271
        $this->createDirectory();
272
273
        return $result;
274
    }
275
276
    /**
277
     * Remove a Shieldon log file.
278
     * Removing a log file works as the same as removing a SQL table's row.
279
     * 
280
     * @param string $logFilePath The absolute path of the log file.
281
     *
282
     * @return bool
283
     */
284
    private function remove(string $logFilePath): bool
285
    {
286
        if (file_exists($logFilePath)) {
287
            return unlink($logFilePath);
288
        }
289
        return false;
290
    }
291
}
292
293