1 | <?php |
||
2 | |||
3 | namespace Cesargb\Log; |
||
4 | |||
5 | use Cesargb\Log\Compress\Gz; |
||
6 | use Cesargb\Log\Processors\RotativeProcessor; |
||
7 | use Exception; |
||
8 | |||
9 | class Rotation |
||
10 | { |
||
11 | use Optionable; |
||
12 | use ErrorHandler; |
||
13 | |||
14 | private const COMPRESS_DEFAULT_LEVEL = null; |
||
15 | |||
16 | private RotativeProcessor $processor; |
||
17 | |||
18 | private bool $_compress = false; |
||
19 | private ?int $_compressLevel = self::COMPRESS_DEFAULT_LEVEL; |
||
20 | |||
21 | private int $_minSize = 0; |
||
22 | |||
23 | private bool $_truncate = false; |
||
24 | |||
25 | /** |
||
26 | * @param mixed[] $options |
||
27 | */ |
||
28 | public function __construct(array $options = []) |
||
29 | { |
||
30 | $this->processor = new RotativeProcessor(); |
||
31 | |||
32 | $this->methodsOptionables([ |
||
33 | 'compress', |
||
34 | 'truncate', |
||
35 | 'minSize', |
||
36 | 'files', |
||
37 | 'then', |
||
38 | 'catch', |
||
39 | 'finally', |
||
40 | ]); |
||
41 | |||
42 | $this->options($options); |
||
43 | } |
||
44 | |||
45 | /** |
||
46 | * Log files are rotated count times before being removed. |
||
47 | */ |
||
48 | public function files(int $count): self |
||
49 | { |
||
50 | $this->processor->files($count); |
||
51 | |||
52 | return $this; |
||
53 | } |
||
54 | |||
55 | /** |
||
56 | * Old versions of log files are compressed. |
||
57 | */ |
||
58 | public function compress(bool|int $level = true): self |
||
59 | { |
||
60 | $this->_compress = (bool)($level); |
||
61 | $this->_compressLevel = is_numeric($level) |
||
62 | ? $level |
||
63 | : self::COMPRESS_DEFAULT_LEVEL; |
||
64 | |||
65 | if ($this->_compress) { |
||
66 | $this->processor->addExtension('gz'); |
||
67 | } else { |
||
68 | $this->processor->removeExtension('gz'); |
||
69 | } |
||
70 | |||
71 | return $this; |
||
72 | } |
||
73 | |||
74 | /** |
||
75 | * Truncate the original log file in place after creating a copy, instead of |
||
76 | * moving the old log file. |
||
77 | * |
||
78 | * It can be used when some program cannot be told to close its logfile and |
||
79 | * thus might continue writing (appending) to the previous log file forever. |
||
80 | */ |
||
81 | public function truncate(bool $truncate = true): self |
||
82 | { |
||
83 | $this->_truncate = $truncate; |
||
84 | |||
85 | return $this; |
||
86 | } |
||
87 | |||
88 | /** |
||
89 | * Log files are rotated when they grow bigger than size bytes. |
||
90 | */ |
||
91 | public function minSize(int $bytes): self |
||
92 | { |
||
93 | $this->_minSize = $bytes; |
||
94 | |||
95 | return $this; |
||
96 | } |
||
97 | |||
98 | /** |
||
99 | * Rotate file. |
||
100 | * |
||
101 | * @return bool true if rotated was successful |
||
102 | */ |
||
103 | public function rotate(string $filename): bool |
||
104 | { |
||
105 | $this->setFilename($filename); |
||
106 | |||
107 | if (!$this->canRotate($filename)) { |
||
108 | return false; |
||
109 | } |
||
110 | |||
111 | $fileTemporary = $this->_truncate |
||
112 | ? $this->copyAndTruncate($filename) |
||
0 ignored issues
–
show
|
|||
113 | : $this->move($filename); |
||
114 | |||
115 | if (is_null($fileTemporary)) { |
||
116 | return false; |
||
117 | } |
||
118 | |||
119 | $fileTarget = $this->runProcessor( |
||
120 | $filename, |
||
121 | $fileTemporary |
||
122 | ); |
||
123 | |||
124 | if (is_null($fileTarget)) { |
||
125 | return false; |
||
126 | } |
||
127 | |||
128 | $fileTarget = $this->runCompress($fileTarget); |
||
129 | |||
130 | $this->successful($filename, $fileTarget); |
||
131 | |||
132 | return true; |
||
133 | } |
||
134 | |||
135 | /** |
||
136 | * Run processor. |
||
137 | */ |
||
138 | private function runProcessor(string $filenameSource, ?string $filenameTarget): ?string |
||
139 | { |
||
140 | $this->initProcessorFile($filenameSource); |
||
141 | |||
142 | if (!$filenameTarget) { |
||
143 | return null; |
||
144 | } |
||
145 | |||
146 | return $this->processor->handler($filenameTarget); |
||
147 | } |
||
148 | |||
149 | private function runCompress(string $filename): ?string |
||
150 | { |
||
151 | if (!$this->_compress) { |
||
152 | return $filename; |
||
153 | } |
||
154 | |||
155 | $gz = new Gz(); |
||
156 | |||
157 | try { |
||
158 | return $gz->handler($filename, $this->_compressLevel); |
||
159 | } catch (Exception $error) { |
||
160 | $this->exception($error); |
||
161 | |||
162 | return null; |
||
163 | } |
||
164 | } |
||
165 | |||
166 | /** |
||
167 | * check if file need rotate. |
||
168 | */ |
||
169 | private function canRotate(string $filename): bool |
||
170 | { |
||
171 | if (!file_exists($filename)) { |
||
172 | $this->finished(sprintf('the file %s not exists.', $filename), $filename); |
||
173 | |||
174 | return false; |
||
175 | } |
||
176 | |||
177 | if (!$this->fileIsValid($filename)) { |
||
178 | $this->exception( |
||
179 | new Exception(sprintf('the file %s not is valid.', $filename), 10) |
||
180 | ); |
||
181 | |||
182 | return false; |
||
183 | } |
||
184 | |||
185 | return filesize($filename) > ($this->_minSize > 0 ? $this->_minSize : 0); |
||
186 | } |
||
187 | |||
188 | /** |
||
189 | * Set original File to processor. |
||
190 | */ |
||
191 | private function initProcessorFile(string $filename): void |
||
192 | { |
||
193 | $this->processor->setFilenameSource($filename); |
||
194 | } |
||
195 | |||
196 | /** |
||
197 | * check if file is valid to rotate. |
||
198 | */ |
||
199 | private function fileIsValid(string $filename): bool |
||
200 | { |
||
201 | return is_file($filename) && is_writable($filename); |
||
202 | } |
||
203 | |||
204 | /** |
||
205 | * copy data to temp file and truncate. |
||
206 | */ |
||
207 | private function copyAndTruncate(string $filename): ?string |
||
208 | { |
||
209 | clearstatcache(); |
||
210 | |||
211 | $filenameTarget = $this->getTempFilename(dirname($filename)); |
||
212 | |||
213 | if (!$filenameTarget) { |
||
214 | return null; |
||
215 | } |
||
216 | |||
217 | $fd = $this->openFileWithLock($filename); |
||
218 | |||
219 | if (!$fd) { |
||
220 | return null; |
||
221 | } |
||
222 | |||
223 | if (!copy($filename, $filenameTarget)) { |
||
224 | fclose($fd); |
||
225 | |||
226 | $this->exception( |
||
227 | new Exception( |
||
228 | sprintf('the file %s not can copy to temp file %s.', $filename, $filenameTarget), |
||
229 | 22 |
||
230 | ) |
||
231 | ); |
||
232 | |||
233 | return null; |
||
234 | } |
||
235 | |||
236 | if (!ftruncate($fd, 0)) { |
||
237 | fclose($fd); |
||
238 | |||
239 | unlink($filenameTarget); |
||
240 | |||
241 | $this->exception( |
||
242 | new Exception(sprintf('the file %s not can truncate.', $filename), 23) |
||
243 | ); |
||
244 | |||
245 | return null; |
||
246 | } |
||
247 | |||
248 | flock($fd, LOCK_UN); |
||
249 | |||
250 | fflush($fd); |
||
251 | |||
252 | fclose($fd); |
||
253 | |||
254 | return $filenameTarget; |
||
255 | } |
||
256 | |||
257 | private function move(string $filename): ?string |
||
258 | { |
||
259 | clearstatcache(); |
||
260 | |||
261 | $filenameTarget = $this->getTempFilename(dirname($filename)); |
||
262 | |||
263 | if (!$filenameTarget) { |
||
264 | return null; |
||
265 | } |
||
266 | |||
267 | if (!rename($filename, $filenameTarget)) { |
||
268 | $this->exception( |
||
269 | new Exception( |
||
270 | sprintf('the file %s not can move to temp file %s.', $filename, $filenameTarget), |
||
271 | 22 |
||
272 | ) |
||
273 | ); |
||
274 | |||
275 | return null; |
||
276 | } |
||
277 | |||
278 | return $filenameTarget; |
||
279 | } |
||
280 | |||
281 | private function getTempFilename(string $path): ?string |
||
282 | { |
||
283 | $filename = tempnam($path, 'LOG'); |
||
284 | |||
285 | if ($filename === false) { |
||
286 | $this->exception( |
||
287 | new Exception(sprintf('the file %s not can create temp file.', $path), 19) |
||
288 | ); |
||
289 | |||
290 | return null; |
||
291 | } |
||
292 | |||
293 | return $filename; |
||
294 | } |
||
295 | |||
296 | /** |
||
297 | * @return null|resource |
||
298 | */ |
||
299 | private function openFileWithLock(string $filename) |
||
300 | { |
||
301 | $fd = fopen($filename, 'r+'); |
||
302 | |||
303 | if ($fd === false) { |
||
304 | $this->exception( |
||
305 | new Exception(sprintf('the file %s not can open.', $filename), 20) |
||
306 | ); |
||
307 | |||
308 | return null; |
||
309 | } |
||
310 | |||
311 | if (!flock($fd, LOCK_EX)) { |
||
312 | fclose($fd); |
||
313 | |||
314 | $this->exception( |
||
315 | new Exception(sprintf('the file %s not can lock.', $filename), 21) |
||
316 | ); |
||
317 | |||
318 | return null; |
||
319 | } |
||
320 | |||
321 | return $fd; |
||
322 | } |
||
323 | } |
||
324 |
This check looks for function or method calls that always return null and whose return value is used.
The method
getObject()
can return nothing but null, so it makes no sense to use the return value.The reason is most likely that a function or method is imcomplete or has been reduced for debug purposes.