Passed
Push — master ( 345089...22c30c )
by Alexander
02:10
created

FileTarget::export()   B

Complexity

Conditions 11
Paths 34

Size

Total Lines 48
Code Lines 32

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 24
CRAP Score 12.8905

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 11
eloc 32
c 1
b 0
f 0
nc 34
nop 0
dl 0
loc 48
ccs 24
cts 32
cp 0.75
crap 12.8905
rs 7.3166

How to fix   Complexity   

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
namespace Yiisoft\Log\Target\File;
3
4
use Yiisoft\Files\FileHelper;
5
use Yiisoft\Log\LogRuntimeException;
6
use Yiisoft\Log\Target;
7
8
/**
9
 * FileTarget records log messages in a file.
10
 *
11
 * The log file is specified via [[logFile]]. If the size of the log file exceeds
12
 * [[maxFileSize]] (in kilo-bytes), a rotation will be performed, which renames
13
 * the current log file by suffixing the file name with '.1'. All existing log
14
 * files are moved backwards by one place, i.e., '.2' to '.3', '.1' to '.2', and so on.
15
 * The property [[maxLogFiles]] specifies how many history files to keep.
16
 */
17
class FileTarget extends Target
18
{
19
    /**
20
     * @var string log file path. If not set, it will use the "/tmp/app.log" file.
21
     * The directory containing the log files will be automatically created if not existing.
22
     */
23
    private $logFile;
24
    /**
25
     * @var int the permission to be set for newly created log files.
26
     * This value will be used by PHP chmod() function. No umask will be applied.
27
     * If not set, the permission will be determined by the current environment.
28
     */
29
    private $fileMode;
30
    /**
31
     * @var int the permission to be set for newly created directories.
32
     * This value will be used by PHP chmod() function. No umask will be applied.
33
     * Defaults to 0775, meaning the directory is read-writable by owner and group,
34
     * but read-only for other users.
35
     */
36
    private $dirMode;
37
38
    /**
39
     * @var FileRotatorInterface
40
     */
41
    private $rotator;
42
43 3
    public function __construct(
44
        string $logFile = '/tmp/app.log',
45
        FileRotatorInterface $rotator = null,
46
        int $dirMode = 0775,
47
        int $fileMode = null
48
    ) {
49 3
        $this->logFile = $logFile;
50 3
        $this->rotator = $rotator;
51 3
        $this->dirMode = $dirMode;
52 3
        $this->fileMode = $fileMode;
53
    }
54
55
    /**
56
     * Writes log messages to a file.
57
     * Starting from version 2.0.14, this method throws LogRuntimeException in case the log can not be exported.
58
     * @throws LogRuntimeException if unable to open or write complete log to file
59
     */
60 2
    public function export(): void
61
    {
62 2
        $logPath = dirname($this->logFile);
63
64 2
        if (!file_exists($logPath)) {
65
            FileHelper::createDirectory($logPath, $this->dirMode);
66
        }
67
68 2
        $text = implode("\n", array_map([$this, 'formatMessage'], $this->getMessages())) . "\n";
69
70 2
        if (($fp = fopen($this->logFile, 'ab')) === false) {
71
            throw new LogRuntimeException("Unable to append to log file: {$this->logFile}");
72
        }
73
74 2
        @flock($fp, LOCK_EX);
0 ignored issues
show
Security Best Practice introduced by
It seems like you do not handle an error condition for flock(). This can introduce security issues, and is generally not recommended. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-unhandled  annotation

74
        /** @scrutinizer ignore-unhandled */ @flock($fp, LOCK_EX);

If you suppress an error, we recommend checking for the error condition explicitly:

// For example instead of
@mkdir($dir);

// Better use
if (@mkdir($dir) === false) {
    throw new \RuntimeException('The directory '.$dir.' could not be created.');
}
Loading history...
75 2
        if ($this->rotator !== null) {
76
            // clear stat cache to ensure getting the real current file size and not a cached one
77
            // this may result in rotating twice when cached file size is used on subsequent calls
78 2
            clearstatcache();
79
        }
80 2
        if ($this->rotator !== null && @filesize($this->logFile) > $this->rotator->getMaxFileSize() * 1024) {
0 ignored issues
show
Bug introduced by
The method getMaxFileSize() does not exist on Yiisoft\Log\Target\File\FileRotatorInterface. Since it exists in all sub-types, consider adding an abstract or default implementation to Yiisoft\Log\Target\File\FileRotatorInterface. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

80
        if ($this->rotator !== null && @filesize($this->logFile) > $this->rotator->/** @scrutinizer ignore-call */ getMaxFileSize() * 1024) {
Loading history...
81 2
            @flock($fp, LOCK_UN);
82 2
            @fclose($fp);
0 ignored issues
show
Security Best Practice introduced by
It seems like you do not handle an error condition for fclose(). This can introduce security issues, and is generally not recommended. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-unhandled  annotation

82
            /** @scrutinizer ignore-unhandled */ @fclose($fp);

If you suppress an error, we recommend checking for the error condition explicitly:

// For example instead of
@mkdir($dir);

// Better use
if (@mkdir($dir) === false) {
    throw new \RuntimeException('The directory '.$dir.' could not be created.');
}
Loading history...
83 2
            $this->rotator->rotateFile($this->logFile);
84 2
            $writeResult = @file_put_contents($this->logFile, $text, FILE_APPEND | LOCK_EX);
85 2
            if ($writeResult === false) {
86
                $error = error_get_last();
87
                throw new LogRuntimeException("Unable to export log through file!: {$error['message']}");
88
            }
89 2
            $textSize = strlen($text);
90 2
            if ($writeResult < $textSize) {
91 2
                throw new LogRuntimeException("Unable to export whole log through file! Wrote $writeResult out of $textSize bytes.");
92
            }
93
        } else {
94 2
            $writeResult = @fwrite($fp, $text);
95 2
            if ($writeResult === false) {
96
                $error = error_get_last();
97
                throw new LogRuntimeException("Unable to export log through file!: {$error['message']}");
98
            }
99 2
            $textSize = strlen($text);
100 2
            if ($writeResult < $textSize) {
101
                throw new LogRuntimeException("Unable to export whole log through file! Wrote $writeResult out of $textSize bytes.");
102
            }
103 2
            @flock($fp, LOCK_UN);
104 2
            @fclose($fp);
105
        }
106 2
        if ($this->fileMode !== null) {
107
            @chmod($this->logFile, $this->fileMode);
0 ignored issues
show
Security Best Practice introduced by
It seems like you do not handle an error condition for chmod(). This can introduce security issues, and is generally not recommended. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-unhandled  annotation

107
            /** @scrutinizer ignore-unhandled */ @chmod($this->logFile, $this->fileMode);

If you suppress an error, we recommend checking for the error condition explicitly:

// For example instead of
@mkdir($dir);

// Better use
if (@mkdir($dir) === false) {
    throw new \RuntimeException('The directory '.$dir.' could not be created.');
}
Loading history...
108
        }
109
    }
110
}
111