Passed
Push — master ( 4675fe...d9a72a )
by Cesar
52s queued 12s
created

Rotation::copyAndTruncate()   A

Complexity

Conditions 5
Paths 5

Size

Total Lines 58
Code Lines 29

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 5
eloc 29
nc 5
nop 1
dl 0
loc 58
rs 9.1448
c 0
b 0
f 0

How to fix   Long Method   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

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 RotativeProcessor $processor;
15
16
    private bool $_compress = false;
17
18
    private int $_minSize = 0;
19
20
    private bool $_truncate = false;
21
22
    private $thenCallback = null;
23
24
    public function __construct(array $options = [])
25
    {
26
        $this->processor = new RotativeProcessor();
27
28
        $this->methodsOptionables([
29
            'compress',
30
            'truncate',
31
            'minSize',
32
            'files',
33
            'then',
34
            'catch',
35
        ]);
36
37
        $this->options($options);
38
    }
39
40
    /**
41
     * Log files are rotated count times before being removed.
42
     */
43
    public function files(int $count): self
44
    {
45
        $this->processor->files($count);
46
47
        return $this;
48
    }
49
50
    /**
51
     * Old versions of log files are compressed.
52
     */
53
    public function compress(bool $compress = true): self
54
    {
55
        $this->_compress = $compress;
56
57
        if ($compress) {
58
            $this->processor->addExtension('gz');
59
        } else {
60
            $this->processor->removeExtention('gz');
61
        }
62
63
        return $this;
64
    }
65
66
    /**
67
     * Truncate the original log file in place after creating a copy, instead of
68
     * moving the old log file.
69
     *
70
     * It can be used when some program cannot be told to close its logfile and
71
     * thus might continue writing (appending) to the previous log file forever.
72
     */
73
    public function truncate(bool $truncate = true): self
74
    {
75
        $this->_truncate = $truncate;
76
77
        return $this;
78
    }
79
80
    /**
81
     * Log files are rotated when they grow bigger than size bytes.
82
     */
83
    public function minSize(int $bytes): self
84
    {
85
        $this->_minSize = $bytes;
86
87
        return $this;
88
    }
89
90
    /**
91
     * Function that will be executed when the rotation is successful.
92
     * The first argument will be the name of the destination file and
93
     * the second the name of the rotated file.
94
     */
95
    public function then(callable $callable): self
96
    {
97
        $this->thenCallback = $callable;
98
99
        return $this;
100
    }
101
102
    /**
103
     * Rotate file.
104
     *
105
     * @return bool true if rotated was successful
106
     */
107
    public function rotate(string $filename): bool
108
    {
109
        $this->setFilename($filename);
110
111
        if (!$this->canRotate($filename)) {
112
            return false;
113
        }
114
115
        $fileTemporary = $this->_truncate
116
            ? $this->copyAndTruncate($filename)
117
            : $this->move($filename);
118
119
        if (is_null($fileTemporary)) {
120
            return false;
121
        }
122
123
        $fileTarget = $this->runProcessor(
124
            $filename,
125
            $fileTemporary
126
        );
127
128
        if (is_null($fileTarget)) {
129
            return false;
130
        }
131
132
        $fileTarget = $this->runCompress($fileTarget);
133
134
        $this->sucessfull($filename, $fileTarget);
135
136
        return true;
137
    }
138
139
    /**
140
     * Run processor.
141
     */
142
    private function runProcessor(string $filenameSource, ?string $filenameTarget): ?string
143
    {
144
        $this->initProcessorFile($filenameSource);
145
146
        if (!$filenameTarget) {
147
            return null;
148
        }
149
150
        return $this->processor->handler($filenameTarget);
151
    }
152
153
    private function runCompress(string $filename): ?string
154
    {
155
        if (!$this->_compress) {
156
            return $filename;
157
        }
158
159
        $gz = new Gz();
160
161
        try {
162
            return $gz->handler($filename);
163
        } catch (Exception $error) {
164
            $this->exception($error);
165
166
            return null;
167
        }
168
    }
169
170
    private function sucessfull(string $filenameSource, ?string $filenameRotated): void
171
    {
172
        if (is_null($this->thenCallback) || is_null($filenameRotated)) {
173
            return;
174
        }
175
176
        call_user_func($this->thenCallback, $filenameRotated, $filenameSource);
177
    }
178
179
    /**
180
     * check if file need rotate.
181
     */
182
    private function canRotate(string $filename): bool
183
    {
184
        if (!is_file($filename)) {
185
            return false;
186
        }
187
188
        if (!$this->fileIsValid($filename)) {
189
            $this->exception(
190
                new Exception(sprintf('the file %s not is valid.', $filename), 10)
191
            );
192
193
            return false;
194
        }
195
196
        return filesize($filename) > ($this->_minSize > 0 ? $this->_minSize : 0);
197
    }
198
199
    /**
200
     * Set original File to processor.
201
     */
202
    private function initProcessorFile(string $filename): void
203
    {
204
        $this->processor->setFilenameSource($filename);
205
    }
206
207
    /**
208
     * check if file is valid to rotate.
209
     */
210
    private function fileIsValid(string $filename): bool
211
    {
212
        return is_writable($filename);
213
    }
214
215
    /**
216
     * copy data to temp file and truncate.
217
     */
218
    private function copyAndTruncate(string $filename): ?string
219
    {
220
        clearstatcache();
221
222
        $filenameTarget = tempnam(dirname($filename), 'LOG');
223
224
        $fd = fopen($filename, 'r+');
225
226
        if ($fd === false) {
227
            $this->exception(
228
                new Exception(sprintf('the file %s not can open.', $filename), 20)
229
            );
230
231
            return null;
232
        }
233
234
        if (!flock($fd, LOCK_EX)) {
235
            fclose($fd);
236
237
            $this->exception(
238
                new Exception(sprintf('the file %s not can lock.', $filename), 21)
239
            );
240
241
            return null;
242
        }
243
244
        if (!copy($filename, $filenameTarget)) {
245
            fclose($fd);
246
247
            $this->exception(
248
                new Exception(
249
                    sprintf('the file %s not can copy to temp file %s.', $filename, $filenameTarget),
250
                    22
251
                )
252
            );
253
254
            return null;
255
        }
256
257
        if (!ftruncate($fd, 0)) {
258
            fclose($fd);
259
260
            unlink($filenameTarget);
261
262
            $this->exception(
263
                new Exception(sprintf('the file %s not can truncate.', $filename), 23)
264
            );
265
266
            return null;
267
        }
268
269
        flock($fd, LOCK_UN);
270
271
        fflush($fd);
272
273
        fclose($fd);
274
275
        return $filenameTarget;
276
    }
277
278
    private function move(string $filename): ?string
279
    {
280
        clearstatcache();
281
282
        $filenameTarget = tempnam(dirname($filename), 'LOG');
283
284
        if (!rename($filename, $filenameTarget)) {
285
            $this->exception(
286
                new Exception(
287
                    sprintf('the file %s not can move to temp file %s.', $filename, $filenameTarget),
288
                    22
289
                )
290
            );
291
292
            return null;
293
        }
294
295
        return $filenameTarget;
296
    }
297
}
298