Passed
Pull Request — master (#18)
by Cesar
01:52
created

Rotation::copyAndTruncate()   B

Complexity

Conditions 6
Paths 6

Size

Total Lines 66
Code Lines 33

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 6
eloc 33
c 0
b 0
f 0
nc 6
nop 1
dl 0
loc 66
rs 8.7697

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