1
<?php
2
/**
3
 * This file is part of the Shieldon package.
4
 *
5
 * (c) Terry L. <contact@terryl.in>
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 <contact@terryl.in>
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\Log;
24

25
use DateInterval;
26
use DatePeriod;
27
use DateTime;
28
use RecursiveDirectoryIterator;
29
use RecursiveIteratorIterator;
30
use RuntimeException;
31
use function date;
32
use function explode;
33
use function file_exists;
34
use function file_put_contents;
35
use function is_dir;
36
use function is_writable;
37
use function mkdir;
38
use function rmdir;
39
use function strtotime;
40
use function umask;
41
use function unlink;
42

43
/**
44
 * Action Logger only support storing log into files, 
45
 * I don't want to make it complex, that's it.
46
 */
47
final class ActionLogger
48
{
49
    /**
50
     * The directory that data files stored to.
51
     *
52
     * @var string
53
     */
54
    protected $directory = '/tmp/';
55

56
    /**
57
     * The file's extension name'.
58
     *
59
     * @var string
60
     */
61
    protected $extension = 'log';
62

63
    /**
64
     * The file name.
65
     *
66
     * @var string
67
     */
68
    protected $file = '';
69

70
    /**
71
     * The file path.
72
     *
73
     * @var string
74
     */
75
    protected $filePath = '';
76

77
    /**
78
     * Constructor.
79
     *
80
     * @param string $directory The dirctory in where the logs were placed.
81
     * @param string $Ymd       The date string.
82
     */
83 3
    public function __construct(string $directory = '', string $Ymd = '')
84
    {
85 3
        if ('' !== $directory) {
86 3
            $this->directory = $directory;
87
        }
88

89 3
        $this->checkDirectory();
90

91 3
        if ('' === $Ymd) {
92 3
            $Ymd = date('Ymd', time());
93
        }
94

95 3
        $this->file = $Ymd . '.' . $this->extension;
96 3
        $this->filePath = rtrim($this->directory, '/') . '/' . $this->file;
97
    }
98

99
    /**
100
     * Append data to the file.
101
     *
102
     * @param array $record The log data.
103
     *
104
     * @return void
105
     */
106 3
    public function add(array $record): void
107
    {
108 3
        if (!empty($record['session_id'])) {
109 3
            $record['session_id'] = substr($record['session_id'], 0, 4);
110
        }
111

112 3
        $data = [];
113

114 3
        $data[0] = $record['ip']          ?? 'null';
115 3
        $data[1] = $record['session_id']  ?? 'null';
116 3
        $data[2] = $record['action_code'] ?? 'null';
117 3
        $data[3] = $record['timestamp']    ?? 'null';
118

119 3
        file_put_contents($this->filePath, implode(',', $data) . "\n", FILE_APPEND | LOCK_EX);
120
    }
121

122
    /**
123
     * Get data from log file.
124
     *
125
     * @param string $fromYmd The string in Ymd Date format.
126
     * @param string $toYmd   The end date.
127
     *
128
     * @return array
129
     */
130 3
    public function get(string $fromYmd = '', string $toYmd = ''): array
131
    {
132 3
        $results = [];
133

134 3
        if ('' === $toYmd) {
135 3
            $results = $this->getDataFromSingleDate($fromYmd);
136
        } else {
137 3
            $results = $this->getDataFromRange($fromYmd, $toYmd);
138
        }
139

140 3
        return $results;
141
    }
142

143
    /**
144
     * Create a directory for storing data files.
145
     *
146
     * @return bool
147
     */
148 3
    protected function checkDirectory(): bool
149
    {
150 3
        $result = true;
151

152 3
        if (!is_dir($this->directory)) {
153 3
            $originalUmask = umask(0);
154 3
            $result = mkdir($this->directory, 0777, true);
155 3
            umask($originalUmask);
156
        }
157

158
        // @codeCoverageIgnoreStart
159
        if (!is_writable($this->directory)) {
160
            throw new RuntimeException('The directory usded by ActionLogger must be writable. (' . $this->directory . ')');
161
        }
162
        // @codeCoverageIgnoreEnd
163
    
164 3
        return $result;
165
    }
166

167
    /**
168
     * Return current log's directory.
169
     *
170
     * @return string
171
     */
172 3
    public function getDirectory(): string
173
    {
174 3
        return $this->directory;
175
    }
176

177
    /**
178
     * Purge all logs and remove log directory.
179
     *
180
     * @return void
181
     */
182 3
    public function purgeLogs(): void
183
    {
184
        // Remove them recursively.
185
        
186 3
        if (file_exists($this->directory)) {
187 3
            $it = new RecursiveDirectoryIterator($this->directory, RecursiveDirectoryIterator::SKIP_DOTS);
188 3
            $files = new RecursiveIteratorIterator($it, RecursiveIteratorIterator::CHILD_FIRST);
189

190 3
            foreach ($files as $file) {
191 3
                if ($file->isDir()) {
192
                    // @codeCoverageIgnoreStart
193
                    rmdir($file->getRealPath());
194
                    // @codeCoverageIgnoreEnd
195
                } else {
196 3
                    unlink($file->getRealPath());
197
                }
198
            }
199 3
            unset($it, $files);
200

201 3
            if (is_dir($this->directory)) {
202 3
                rmdir($this->directory);
203
            }
204
        }
205
    }
206

207
    /**
208
     * Get current logger info.
209
     *
210
     * @return array
211
     */
212 3
    public function getCurrentLoggerInfo(): array
213
    {
214 3
        $data = [];
215

216 3
        if (file_exists($this->directory)) {
217 3
            $it = new RecursiveDirectoryIterator($this->directory, RecursiveDirectoryIterator::SKIP_DOTS);
218 3
            $files = new RecursiveIteratorIterator($it, RecursiveIteratorIterator::CHILD_FIRST);
219

220 3
            foreach ($files as $file) {
221 3
                if ($file->isFile()) {
222 3
                    $key = $file->getBasename('.log');
223 3
                    $size = $file->getSize();
224

225
                    // Data: datetime => file size.
226 3
                    $data[$key] = $size;
227
                } 
228
            }
229
        }
230

231 3
        return $data;
232
    }
233

234
    /**
235
     * Get data from log file.
236
     *
237
     * @param string $fromYmd The string in Ymd Date format.
238
     *
239
     * @return array
240
     */
241 3
    private function getDataFromSingleDate(string $fromYmd = ''): array
242
    {
243 3
        $results = [];
244

245 3
        $fromYmd = date('Ymd', strtotime($fromYmd));
246

247 3
        $this->file = $fromYmd . '.' . $this->extension;
248 3
        $this->filePath = rtrim($this->directory, '/') . '/' . $this->file;
249

250 3
        if (file_exists($this->filePath)) {
251

252 3
            $logFile = fopen($this->filePath, 'r');
253

254 3
            if (is_resource($logFile)) {
255 3
                while (!feof($logFile)) {
256 3
                    $line = fgets($logFile);
257
    
258 3
                    if (!empty($line)) {
259 3
                        $data = explode(',', trim($line));
260 3
                        $results[] = [
261 3
                            'ip'          => $data[0],
262 3
                            'session_id'  => $data[1],
263 3
                            'action_code' => $data[2],
264 3
                            'timestamp'    => $data[3],
265
                        ];
266
                    }
267

268 3
                    unset($line, $data);
269
                }
270 3
                fclose($logFile);
271
            }
272
        }
273

274 3
        return $results;
275
    }
276

277
    /**
278
     * Get data from log files.
279
     *
280
     * @param string $fromYmd The string in Ymd Date format.
281
     * @param string $toYmd   The end date.
282
     *
283
     * @return array
284
     */
285 3
    private function getDataFromRange(string $fromYmd = '', string $toYmd = ''): array
286
    {
287 3
        $results = [];
288

289
        // for quering date range.
290 3
        $fromYmd = date('Ymd', strtotime($fromYmd));
291 3
        $toYmd = date('Ymd', strtotime($toYmd));
292

293 3
        $begin = new DateTime($fromYmd);
294 3
        $end = new DateTime($toYmd);
295 3
        $end = $end->modify('+1 day'); 
296

297 3
        $interval = new DateInterval('P1D');
298 3
        $daterange = new DatePeriod($begin, $interval, $end);
299

300 3
        foreach ($daterange as $date) {
301

302 3
            $thisDayLogFile = $this->directory . '/' . $date->format('Ymd') . '.' . $this->extension;
303

304 3
            if (file_exists($thisDayLogFile)) {
305

306 3
                $logFile = fopen($thisDayLogFile, 'r');
307

308 3
                if (is_resource($logFile)) {
309 3
                    while (!feof($logFile)) {
310 3
                        $line = fgets($logFile);
311
    
312 3
                        if (!empty($line)) {
313 3
                            $data = explode(',', trim($line));
314
                        }
315
    
316 3
                        if (!empty($data[0])) {
317 3
                            $results[] = [
318 3
                                'ip'          => $data[0],
319 3
                                'session_id'  => $data[1],
320 3
                                'action_code' => $data[2],
321 3
                                'timestamp'    => $data[3],
322
                            ];
323
                        }
324 3
                        unset($line, $data);
325
                    }
326 3
                    fclose($logFile);
327
                }
328
            }
329
        }
330

331 3
        return $results;
332
    }
333
}

Read our documentation on viewing source code .

Loading