1 <?php
2
3 4 5 6 7 8 9
10 class rex_log_file implements Iterator
11 {
12
13 private $path;
14
15
16 private $file;
17
18
19 private $file2;
20
21
22 private $second = false;
23
24
25 private $pos;
26
27
28 private $key;
29
30
31 private $currentLine;
32
33
34 private $buffer;
35
36
37 private $bufferPos;
38
39 40 41 42 43 44
45 public function __construct($path, $maxFileSize = null)
46 {
47 $this->path = $path;
48 if (!file_exists($path)) {
49 rex_file::put($path, '');
50 }
51 if ($maxFileSize && filesize($path) > $maxFileSize) {
52 rename($path, $path . '.2');
53 }
54 $this->file = fopen($path, 'a+');
55 }
56
57 58 59 60 61
62 public function add(array $data)
63 {
64 fseek($this->file, 0, SEEK_END);
65 fwrite($this->file, new rex_log_entry(time(), $data) . "\n");
66 }
67
68 69 70
71 public function current()
72 {
73 return rex_log_entry::createFromString($this->currentLine);
74 }
75
76 77 78
79 public function next()
80 {
81 static $bufferSize = 500;
82
83 if ($this->pos < 0) {
84
85 $path2 = $this->path . '.2';
86 if ($this->second || !$this->file2 && !file_exists($path2)) {
87
88 $this->currentLine = null;
89 $this->key = null;
90 return;
91 }
92
93 if (!$this->file2) {
94 $this->file2 = fopen($path2, 'r');
95 }
96 $this->second = true;
97 $this->pos = null;
98 }
99
100
101 $file = $this->second ? $this->file2 : $this->file;
102
103 if (null === $this->pos) {
104
105 fseek($file, 0, SEEK_END);
106 $this->pos = (int) (ftell($file) / $bufferSize) * $bufferSize;
107 }
108
109 $line = '';
110
111 while ($this->pos >= 0) {
112 if ($this->bufferPos < 0) {
113
114 fseek($file, $this->pos);
115 $this->buffer = fread($file, $bufferSize);
116 $this->bufferPos = strlen($this->buffer) - 1;
117 }
118
119 for (; $this->bufferPos >= 0; --$this->bufferPos) {
120 $char = $this->buffer[$this->bufferPos];
121 if ("\n" === $char) {
122
123 --$this->bufferPos;
124 if ($this->bufferPos < 0) {
125 $this->pos -= $bufferSize;
126 }
127 break 2;
128 }
129 if ("\r" !== $char) {
130
131 $line = $char . $line;
132 }
133 }
134 $this->pos -= $bufferSize;
135 }
136 if (!$line = trim($line)) {
137
138 $this->next();
139 return;
140 }
141
142 ++$this->key;
143 $this->currentLine = $line;
144 }
145
146 147 148
149 public function key()
150 {
151 return $this->key;
152 }
153
154 155 156
157 public function valid()
158 {
159 return !empty($this->currentLine);
160 }
161
162 163 164
165 public function rewind()
166 {
167 $this->second = false;
168 $this->pos = null;
169 $this->key = -1;
170 $this->bufferPos = -1;
171 $this->next();
172 }
173
174 175 176 177 178 179 180
181 public static function delete($path)
182 {
183 return rex_file::delete($path) && rex_file::delete($path . '.2');
184 }
185 }
186
187 188 189 190 191 192 193
194 class rex_log_entry
195 {
196
197 private $timestamp;
198
199
200 private $data;
201
202 203 204 205 206 207
208 public function __construct($timestamp, array $data)
209 {
210 $this->timestamp = $timestamp;
211 $this->data = $data;
212 }
213
214 215 216 217 218 219 220
221 public static function createFromString($string)
222 {
223 $data = [];
224 foreach (explode('|', $string) as $part) {
225 $data[] = str_replace('\n', "\n", trim($part));
226 }
227
228 $timestamp = strtotime(array_shift($data));
229
230 return new self($timestamp, $data);
231 }
232
233 234 235 236 237 238 239
240 public function getTimestamp($format = null)
241 {
242 if (null === $format) {
243 return $this->timestamp;
244 }
245 return rex_formatter::strftime($this->timestamp, $format);
246 }
247
248 249 250 251 252
253 public function getData()
254 {
255 return $this->data;
256 }
257
258 259 260
261 public function __toString()
262 {
263 $data = implode(' | ', array_map('trim', $this->data));
264 $data = str_replace(["\r", "\n"], ['', '\n'], $data);
265 return date('Y-m-d H:i:s', $this->timestamp) . ' | ' . $data;
266 }
267 }
268