LogProviderFile::setPath()   A
last analyzed

Complexity

Conditions 1
Paths 1

Size

Total Lines 4

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
c 0
b 0
f 0
dl 0
loc 4
rs 10
cc 1
nc 1
nop 1
1
<?php
2
3
namespace Alpha\Util\Logging;
4
5
use Alpha\Util\Config\ConfigProvider;
6
use Alpha\Util\File\FileUtils;
7
8
/**
9
 * Generic log file class to encapsulate common file I/O and rendering calls.
10
 *
11
 * @since 1.0
12
 *
13
 * @author John Collins <[email protected]>
14
 * @license http://www.opensource.org/licenses/bsd-license.php The BSD License
15
 * @copyright Copyright (c) 2019, John Collins (founder of Alpha Framework).
16
 * All rights reserved.
17
 *
18
 * <pre>
19
 * Redistribution and use in source and binary forms, with or
20
 * without modification, are permitted provided that the
21
 * following conditions are met:
22
 *
23
 * * Redistributions of source code must retain the above
24
 *   copyright notice, this list of conditions and the
25
 *   following disclaimer.
26
 * * Redistributions in binary form must reproduce the above
27
 *   copyright notice, this list of conditions and the
28
 *   following disclaimer in the documentation and/or other
29
 *   materials provided with the distribution.
30
 * * Neither the name of the Alpha Framework nor the names
31
 *   of its contributors may be used to endorse or promote
32
 *   products derived from this software without specific
33
 *   prior written permission.
34
 *
35
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
36
 * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
37
 * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
38
 * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
39
 * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
40
 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
41
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
42
 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
43
 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
44
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
45
 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
46
 * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
47
 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
48
 * </pre>
49
 */
50
class LogProviderFile implements LogProviderInterface
51
{
52
    /**
53
     * The log file path.
54
     *
55
     * @var string
56
     *
57
     * @since 1.0
58
     */
59
    private $path;
60
61
    /**
62
     * The maximum size of the log file in megabytes before a backup is created and a
63
     * new file is created, default is 5.
64
     *
65
     * @var int
66
     *
67
     * @since 1.0
68
     */
69
    private $maxSize = 5;
70
71
    /**
72
     * Set the file path.
73
     *
74
     * @param string $path
75
     *
76
     * @since 2.0
77
     */
78
    public function setPath($path)
79
    {
80
        $this->path = $path;
81
    }
82
83
    /**
84
     * Set the max log size in megabytes.
85
     *
86
     * @param int $maxSize
87
     *
88
     * @since 1.0
89
     */
90
    public function setMaxSize($maxSize)
91
    {
92
        $this->maxSize = $maxSize;
93
    }
94
95
    /**
96
     * {@inheritdoc}
97
     */
98
    public function writeLine($line)
99
    {
100
        $config = ConfigProvider::getInstance();
101
102
        if ($this->path != '') {
103
            try {
104
                $fp = fopen($this->path, 'a+');
105
                fputcsv($fp, $line, ',', '"', '\\');
106
107
                if ($this->checkFileSize() >= $this->maxSize) {
108
                    $this->backupFile();
109
                }
110
            } catch (\Exception $e) {
111
                $logsDir = $config->get('app.file.store.dir').'logs';
112
113
                if (!file_exists($logsDir)) {
114
                    if (!mkdir($logsDir, 0766, true)) {
115
                        error_log('Could not create the directory ['.$logsDir.']');
116
                    }
117
                }
118
119
                $fp = fopen($this->path, 'a+');
120
                if (!fputcsv($fp, $line, ',', '"', '\\')) {
121
                    error_log('Could not write to the CSV file ['.$this->path.']');
122
                }
123
124
                if ($this->checkFileSize() >= $this->maxSize) {
125
                    $this->backupFile();
126
                }
127
            }
128
        }
129
    }
130
131
    /**
132
     * Returns the size in megabytes of the log file on disc.
133
     *
134
     * @return integer
135
     *
136
     * @since 1.0
137
     */
138
    private function checkFileSize()
139
    {
140
        clearstatcache();
141
        $size = filesize($this->path);
142
143
        return ($size/1024)/1024;
144
    }
145
146
    /**
147
     * Creates a backup of the log file, which has the same file name and location as the
148
     * current file plus a timestamp appended.
149
     *
150
     * @since 1.0
151
     */
152
    private function backupFile()
153
    {
154
        // generate the name of the backup file name to contain a timestampe
155
        $backName = str_replace('.log', '-backup-'.date('y-m-d').'.log', $this->path);
156
157
        // renames the logfile as the value of $backName
158
        rename($this->path, $backName);
159
160
        FileUtils::zip($backName, $backName.'.zip');
161
        unlink($backName);
162
163
        // creates a new log file, and sets it's permission for writing!
164
        $fp = fopen($this->path, 'a+'); // remember set directory permissons to allow creation!
165
        fclose($fp);
166
        //s ets the new permission to rw+:rw+:rw+
167
        chmod($this->path, 0666);
168
    }
169
170
    /**
171
     * {@inheritdoc}
172
     */
173
    public function renderLog($cols)
174
    {
175
        // render the start of the table
176
        $body = '<table class="table">';
177
        $body .= '<tr>';
178
        foreach ($cols as $heading) {
179
            $body .= '<th>'.$heading.'</th>';
180
        }
181
        $body .= '</tr>';
182
183
        $fp = fopen($this->path, 'r');
184
185
        while (($line = fgetcsv($fp)) !== false) {
186
            $body .= '<tr>';
187
188
            for ($col = 0; $col < count($line); ++$col) {
0 ignored issues
show
Performance Best Practice introduced by
It seems like you are calling the size function count() as part of the test condition. You might want to compute the size beforehand, and not on each iteration.

If the size of the collection does not change during the iteration, it is generally a good practice to compute it beforehand, and not on each iteration:

for ($i=0; $i<count($array); $i++) { // calls count() on each iteration
}

// Better
for ($i=0, $c=count($array); $i<$c; $i++) { // calls count() just once
}
Loading history...
189
190
                // if it is an error log, render the error types field in different colours
191
                if ($col == 1 && $cols[1] == 'Level') {
192
                    switch ($line[$col]) {
193
                        case 'DEBUG':
194
                            $body .= '<td class="debug">'.htmlentities($line[$col], ENT_COMPAT, 'utf-8').'</td>';
195
                        break;
196
                        case 'INFO':
197
                            $body .= '<td class="info">'.htmlentities($line[$col], ENT_COMPAT, 'utf-8').'</td>';
198
                        break;
199
                        case 'WARN':
200
                            $body .= '<td class="warn">'.htmlentities($line[$col], ENT_COMPAT, 'utf-8').'</td>';
201
                        break;
202
                        case 'ERROR':
203
                            $body .= '<td class="error">'.htmlentities($line[$col], ENT_COMPAT, 'utf-8').'</td>';
204
                        break;
205
                        case 'FATAL':
206
                            $body .= '<td class="fatal">'.htmlentities($line[$col], ENT_COMPAT, 'utf-8').'</td>';
207
                        break;
208
                        case 'SQL':
209
                            $body .= '<td class="sql">'.htmlentities($line[$col], ENT_COMPAT, 'utf-8').'</td>';
210
                        break;
211
                        default:
212
                            $body .= '<td>'.htmlentities($line[$col], ENT_COMPAT, 'utf-8').'</td>';
213
                        break;
214
                    }
215
                } else {
216
                    if ($cols[$col] == 'Message') {
217
                        $body .= '<td><pre>'.htmlentities($line[$col], ENT_COMPAT, 'utf-8').'</pre></td>';
218
                    } else {
219
                        $body .= '<td>'.htmlentities($line[$col], ENT_COMPAT, 'utf-8').'</td>';
220
                    }
221
                }
222
            }
223
224
            $body .= '</tr>';
225
        }
226
227
        $body .= '</table>';
228
229
        return $body;
230
    }
231
}
232