Passed
Push — 17667-sqlite-create-index-with... ( 21cdd8...b9d5ca )
by Alexander
84:30 queued 44:33
created

FileTarget::export()   C

Complexity

Conditions 12
Paths 34

Size

Total Lines 45
Code Lines 32

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 12
eloc 32
nc 34
nop 0
dl 0
loc 45
rs 6.9666
c 1
b 0
f 0

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
/**
3
 * @link http://www.yiiframework.com/
4
 * @copyright Copyright (c) 2008 Yii Software LLC
5
 * @license http://www.yiiframework.com/license/
6
 */
7
8
namespace yii\log;
9
10
use Yii;
11
use yii\base\InvalidConfigException;
12
use yii\helpers\FileHelper;
13
14
/**
15
 * FileTarget records log messages in a file.
16
 *
17
 * The log file is specified via [[logFile]]. If the size of the log file exceeds
18
 * [[maxFileSize]] (in kilo-bytes), a rotation will be performed, which renames
19
 * the current log file by suffixing the file name with '.1'. All existing log
20
 * files are moved backwards by one place, i.e., '.2' to '.3', '.1' to '.2', and so on.
21
 * The property [[maxLogFiles]] specifies how many history files to keep.
22
 *
23
 * @author Qiang Xue <[email protected]>
24
 * @since 2.0
25
 */
26
class FileTarget extends Target
27
{
28
    /**
29
     * @var string log file path or [path alias](guide:concept-aliases). If not set, it will use the "@runtime/logs/app.log" file.
30
     * The directory containing the log files will be automatically created if not existing.
31
     */
32
    public $logFile;
33
    /**
34
     * @var bool whether log files should be rotated when they reach a certain [[maxFileSize|maximum size]].
35
     * Log rotation is enabled by default. This property allows you to disable it, when you have configured
36
     * an external tools for log rotation on your server.
37
     * @since 2.0.3
38
     */
39
    public $enableRotation = true;
40
    /**
41
     * @var int maximum log file size, in kilo-bytes. Defaults to 10240, meaning 10MB.
42
     */
43
    public $maxFileSize = 10240; // in KB
44
    /**
45
     * @var int number of log files used for rotation. Defaults to 5.
46
     */
47
    public $maxLogFiles = 5;
48
    /**
49
     * @var int the permission to be set for newly created log files.
50
     * This value will be used by PHP chmod() function. No umask will be applied.
51
     * If not set, the permission will be determined by the current environment.
52
     */
53
    public $fileMode;
54
    /**
55
     * @var int the permission to be set for newly created directories.
56
     * This value will be used by PHP chmod() function. No umask will be applied.
57
     * Defaults to 0775, meaning the directory is read-writable by owner and group,
58
     * but read-only for other users.
59
     */
60
    public $dirMode = 0775;
61
    /**
62
     * @var bool Whether to rotate log files by copy and truncate in contrast to rotation by
63
     * renaming files. Defaults to `true` to be more compatible with log tailers and is windows
64
     * systems which do not play well with rename on open files. Rotation by renaming however is
65
     * a bit faster.
66
     *
67
     * The problem with windows systems where the [rename()](https://secure.php.net/manual/en/function.rename.php)
68
     * function does not work with files that are opened by some process is described in a
69
     * [comment by Martin Pelletier](https://secure.php.net/manual/en/function.rename.php#102274) in
70
     * the PHP documentation. By setting rotateByCopy to `true` you can work
71
     * around this problem.
72
     */
73
    public $rotateByCopy = true;
74
75
76
    /**
77
     * Initializes the route.
78
     * This method is invoked after the route is created by the route manager.
79
     */
80
    public function init()
81
    {
82
        parent::init();
83
        if ($this->logFile === null) {
84
            $this->logFile = Yii::$app->getRuntimePath() . '/logs/app.log';
85
        } else {
86
            $this->logFile = Yii::getAlias($this->logFile);
0 ignored issues
show
Documentation Bug introduced by
It seems like Yii::getAlias($this->logFile) can also be of type boolean. However, the property $logFile is declared as type string. Maybe add an additional type check?

Our type inference engine has found a suspicous assignment of a value to a property. This check raises an issue when a value that can be of a mixed type is assigned to a property that is type hinted more strictly.

For example, imagine you have a variable $accountId that can either hold an Id object or false (if there is no account id yet). Your code now assigns that value to the id property of an instance of the Account class. This class holds a proper account, so the id value must no longer be false.

Either this assignment is in error or a type check should be added for that assignment.

class Id
{
    public $id;

    public function __construct($id)
    {
        $this->id = $id;
    }

}

class Account
{
    /** @var  Id $id */
    public $id;
}

$account_id = false;

if (starsAreRight()) {
    $account_id = new Id(42);
}

$account = new Account();
if ($account instanceof Id)
{
    $account->id = $account_id;
}
Loading history...
87
        }
88
        if ($this->maxLogFiles < 1) {
89
            $this->maxLogFiles = 1;
90
        }
91
        if ($this->maxFileSize < 1) {
92
            $this->maxFileSize = 1;
93
        }
94
    }
95
96
    /**
97
     * Writes log messages to a file.
98
     * Starting from version 2.0.14, this method throws LogRuntimeException in case the log can not be exported.
99
     * @throws InvalidConfigException if unable to open the log file for writing
100
     * @throws LogRuntimeException if unable to write complete log to file
101
     */
102
    public function export()
103
    {
104
        if (strpos($this->logFile, '://') === false || strncmp($this->logFile, 'file://', 7) === 0) {
105
            $logPath = dirname($this->logFile);
106
            FileHelper::createDirectory($logPath, $this->dirMode, true);
107
        }
108
109
        $text = implode("\n", array_map([$this, 'formatMessage'], $this->messages)) . "\n";
110
        if (($fp = @fopen($this->logFile, 'a')) === false) {
111
            throw new InvalidConfigException("Unable to append to log file: {$this->logFile}");
112
        }
113
        @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

113
        /** @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...
114
        if ($this->enableRotation) {
115
            // clear stat cache to ensure getting the real current file size and not a cached one
116
            // this may result in rotating twice when cached file size is used on subsequent calls
117
            clearstatcache();
118
        }
119
        if ($this->enableRotation && @filesize($this->logFile) > $this->maxFileSize * 1024) {
120
            @flock($fp, LOCK_UN);
121
            @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

121
            /** @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...
122
            $this->rotateFiles();
123
            $writeResult = @file_put_contents($this->logFile, $text, FILE_APPEND | LOCK_EX);
124
            if ($writeResult === false) {
125
                $error = error_get_last();
126
                throw new LogRuntimeException("Unable to export log through file!: {$error['message']}");
127
            }
128
            $textSize = strlen($text);
129
            if ($writeResult < $textSize) {
130
                throw new LogRuntimeException("Unable to export whole log through file! Wrote $writeResult out of $textSize bytes.");
131
            }
132
        } else {
133
            $writeResult = @fwrite($fp, $text);
134
            if ($writeResult === false) {
135
                $error = error_get_last();
136
                throw new LogRuntimeException("Unable to export log through file!: {$error['message']}");
137
            }
138
            $textSize = strlen($text);
139
            if ($writeResult < $textSize) {
140
                throw new LogRuntimeException("Unable to export whole log through file! Wrote $writeResult out of $textSize bytes.");
141
            }
142
            @flock($fp, LOCK_UN);
143
            @fclose($fp);
144
        }
145
        if ($this->fileMode !== null) {
146
            @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

146
            /** @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...
147
        }
148
    }
149
150
    /**
151
     * Rotates log files.
152
     */
153
    protected function rotateFiles()
154
    {
155
        $file = $this->logFile;
156
        for ($i = $this->maxLogFiles; $i >= 0; --$i) {
157
            // $i == 0 is the original log file
158
            $rotateFile = $file . ($i === 0 ? '' : '.' . $i);
159
            if (is_file($rotateFile)) {
160
                // suppress errors because it's possible multiple processes enter into this section
161
                if ($i === $this->maxLogFiles) {
162
                    @unlink($rotateFile);
0 ignored issues
show
Security Best Practice introduced by
It seems like you do not handle an error condition for unlink(). 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

162
                    /** @scrutinizer ignore-unhandled */ @unlink($rotateFile);

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...
163
                    continue;
164
                }
165
                $newFile = $this->logFile . '.' . ($i + 1);
166
                $this->rotateByCopy ? $this->rotateByCopy($rotateFile, $newFile) : $this->rotateByRename($rotateFile, $newFile);
167
                if ($i === 0) {
168
                    $this->clearLogFile($rotateFile);
169
                }
170
            }
171
        }
172
    }
173
174
    /***
175
     * Clear log file without closing any other process open handles
176
     * @param string $rotateFile
177
     */
178
    private function clearLogFile($rotateFile)
179
    {
180
        if ($filePointer = @fopen($rotateFile, 'a')) {
181
            @ftruncate($filePointer, 0);
0 ignored issues
show
Security Best Practice introduced by
It seems like you do not handle an error condition for ftruncate(). 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

181
            /** @scrutinizer ignore-unhandled */ @ftruncate($filePointer, 0);

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...
182
            @fclose($filePointer);
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

182
            /** @scrutinizer ignore-unhandled */ @fclose($filePointer);

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...
183
        }
184
    }
185
186
    /***
187
     * Copy rotated file into new file
188
     * @param string $rotateFile
189
     * @param string $newFile
190
     */
191
    private function rotateByCopy($rotateFile, $newFile)
192
    {
193
        @copy($rotateFile, $newFile);
0 ignored issues
show
Security Best Practice introduced by
It seems like you do not handle an error condition for copy(). 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

193
        /** @scrutinizer ignore-unhandled */ @copy($rotateFile, $newFile);

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...
194
        if ($this->fileMode !== null) {
195
            @chmod($newFile, $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

195
            /** @scrutinizer ignore-unhandled */ @chmod($newFile, $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...
196
        }
197
    }
198
199
    /**
200
     * Renames rotated file into new file
201
     * @param string $rotateFile
202
     * @param string $newFile
203
     */
204
    private function rotateByRename($rotateFile, $newFile)
205
    {
206
        @rename($rotateFile, $newFile);
0 ignored issues
show
Security Best Practice introduced by
It seems like you do not handle an error condition for rename(). 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

206
        /** @scrutinizer ignore-unhandled */ @rename($rotateFile, $newFile);

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...
207
    }
208
}
209