Completed
Pull Request — develop (#283)
by John
04:43
created

LogProviderFile::writeLine()   D

Complexity

Conditions 9
Paths 75

Size

Total Lines 37
Code Lines 22

Duplication

Lines 0
Ratio 0 %

Importance

Changes 4
Bugs 0 Features 3
Metric Value
c 4
b 0
f 3
dl 0
loc 37
rs 4.909
cc 9
eloc 22
nc 75
nop 1
1
<?php
2
3
namespace Alpha\Util\Logging;
4
5
use Alpha\Util\Config\ConfigProvider;
6
use Alpha\Exception\PHPException;
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) 2015, 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
                try {
112
                    $logsDir = $config->get('app.file.store.dir').'logs';
113
114
                    if (!file_exists($logsDir)) {
115
                        if (!mkdir($logsDir, 0766)) {
116
                            throw new PHPException('Could not create the directory ['.$logsDir.']');
117
                        }
118
                    }
119
120
                    $fp = fopen($this->path, 'a+');
121
                    if (!fputcsv($fp, $line, ',', '"', '\\')) {
122
                        throw new PHPException('Could not write to the CSV file ['.$this->path.']');
123
                    }
124
125
                    if ($this->checkFileSize() >= $this->MaxSize) {
126
                        $this->backupFile();
127
                    }
128
                } catch (\Exception $e) {
129
                    echo 'Unable to write to the log file ['.$this->path.'], error ['.$e->getMessage().']';
130
                    exit;
0 ignored issues
show
Coding Style Compatibility introduced by
The method writeLine() contains an exit expression.

An exit expression should only be used in rare cases. For example, if you write a short command line script.

In most cases however, using an exit expression makes the code untestable and often causes incompatibilities with other libraries. Thus, unless you are absolutely sure it is required here, we recommend to refactor your code to avoid its usage.

Loading history...
131
                }
132
            }
133
        }
134
    }
135
136
    /**
137
     * Returns the size in megabytes of the log file on disc.
138
     *
139
     * @return float
140
     *
141
     * @since 1.0
142
     */
143
    private function checkFileSize()
144
    {
145
        $size = filesize($this->path);
146
147
        return ($size / 1024) / 1024;
148
    }
149
150
    /**
151
     * Creates a backup of the log file, which has the same file name and location as the
152
     * current file plus a timestamp appended.
153
     *
154
     * @since 1.0
155
     */
156
    private function backupFile()
157
    {
158
        // generate the name of the backup file name to contain a timestampe
159
        $backName = str_replace('.log', '-backup-'.date('y-m-d H.i.s').'.log', $this->path);
160
161
        // renames the logfile as the value of $backName
162
        rename($this->path, $backName);
163
        //creates a new log file, and sets it's permission for writting!
164
        $fp = fopen($this->path, 'a+'); // remember set directory permissons to allow creation!
165
        fclose($fp);
166
        //sets 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 View Code Duplication
                        case 'INFO':
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
197
                            $body .= '<td class="info">'.htmlentities($line[$col], ENT_COMPAT, 'utf-8').'</td>';
198
                        break;
199 View Code Duplication
                        case 'WARN':
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
200
                            $body .= '<td class="warn">'.htmlentities($line[$col], ENT_COMPAT, 'utf-8').'</td>';
201
                        break;
202 View Code Duplication
                        case 'ERROR':
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
203
                            $body .= '<td class="error">'.htmlentities($line[$col], ENT_COMPAT, 'utf-8').'</td>';
204
                        break;
205 View Code Duplication
                        case 'FATAL':
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
206
                            $body .= '<td class="fatal">'.htmlentities($line[$col], ENT_COMPAT, 'utf-8').'</td>';
207
                        break;
208 View Code Duplication
                        case 'SQL':
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
209
                            $body .= '<td class="sql">'.htmlentities($line[$col], ENT_COMPAT, 'utf-8').'</td>';
210
                        break;
211 View Code Duplication
                        default:
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
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