Completed
Pull Request — master (#224)
by Gerardo
01:27
created

LaravelLogViewer::convertExceptionToArray()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 16

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 16
rs 9.7333
c 0
b 0
f 0
cc 1
nc 1
nop 1
1
<?php
2
3
namespace Rap2hpoutre\LaravelLogViewer;
4
5
use Rap2hpoutre\LaravelLogViewer\Level;
6
use Rap2hpoutre\LaravelLogViewer\Pattern;
7
use Illuminate\Support\Arr;
8
use Illuminate\Support\Str;
9
10
/**
11
 * Class LaravelLogViewer
12
 * @package Rap2hpoutre\LaravelLogViewer
13
 */
14
class LaravelLogViewer
15
{
16
    /**
17
     * @var string file
18
     */
19
    private $file;
20
21
    /**
22
     * @var string folder
23
     */
24
    private $folder;
25
26
    /**
27
     * @var string storage_path
28
     */
29
    private $storage_path;
30
31
    /**
32
     * Why? Uh... Sorry
33
     */
34
    const MAX_FILE_SIZE = 52428800;
35
36
    /**
37
     * @var Level level
38
     */
39
    private $level;
40
41
    /**
42
     * @var Pattern pattern
43
     */
44
    private $pattern;
45
46
    /**
47
     * LaravelLogViewer constructor.
48
     */
49
    public function __construct()
50
    {
51
        $this->level = new Level();
52
        $this->pattern = new Pattern();
53
        $this->storage_path = function_exists('config') ? config('logviewer.storage_path', storage_path('logs')) : storage_path('logs');
54
55
    }
56
57
    /**
58
     * @param string $folder
59
     */
60
    public function setFolder($folder)
61
    {
62
        if (app('files')->exists($folder)) {
63
            $this->folder = $folder;
64
        }
65
        if(is_array($this->storage_path)) {
66 View Code Duplication
            foreach ($this->storage_path as $value) {
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...
67
                $logsPath = $value . '/' . $folder;
68
                if (app('files')->exists($logsPath)) {
69
                    $this->folder = $folder;
70
                    break;
71
                }
72
            }
73
        } else {
74
            if ($this->storage_path) {
75
                $logsPath = $this->storage_path . '/' . $folder;
76
                if (app('files')->exists($logsPath)) {
77
                    $this->folder = $folder;
78
                }
79
            }
80
        }
81
    }
82
83
    /**
84
     * @param string $file
85
     * @throws \Exception
86
     */
87
    public function setFile($file)
88
    {
89
        $file = $this->pathToLogFile($file);
90
91
        if (app('files')->exists($file)) {
92
            $this->file = $file;
93
        }
94
    }
95
96
    /**
97
     * @param string $file
98
     * @return string
99
     * @throws \Exception
100
     */
101
    public function pathToLogFile($file)
102
    {
103
104
        if (app('files')->exists($file)) { // try the absolute path
105
            return $file;
106
        }
107
        if (is_array($this->storage_path)) {
108 View Code Duplication
            foreach ($this->storage_path as $folder) {
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...
109
                if (app('files')->exists($folder . '/' . $file)) { // try the absolute path
110
                    $file = $folder . '/' . $file;
111
                    break;
112
                }
113
            }
114
            return $file;
115
        }
116
117
        $logsPath = $this->storage_path;
118
        $logsPath .= ($this->folder) ? '/' . $this->folder : '';
119
        $file = $logsPath . '/' . $file;
120
        // check if requested file is really in the logs directory
121
        if (dirname($file) !== $logsPath) {
122
            throw new \Exception('No such log file');
123
        }
124
        return $file;
125
    }
126
127
    /**
128
     * @return string
129
     */
130
    public function getFolderName()
131
    {
132
        return $this->folder;
133
    }
134
135
    /**
136
     * @return string
137
     */
138
    public function getFileName()
139
    {
140
        return basename($this->file);
141
    }
142
143
    /**
144
     * @return array
145
     */
146
    public function all($parseStack = false)
147
    {
148
        $log = array();
0 ignored issues
show
Unused Code introduced by
$log is not used, you could remove the assignment.

This check looks for variable assignements that are either overwritten by other assignments or where the variable is not used subsequently.

$myVar = 'Value';
$higher = false;

if (rand(1, 6) > 3) {
    $higher = true;
} else {
    $higher = false;
}

Both the $myVar assignment in line 1 and the $higher assignment in line 2 are dead. The first because $myVar is never used and the second because $higher is always overwritten for every possible time line.

Loading history...
149
150
        if (!$this->file) {
151
            $log_file = (!$this->folder) ? $this->getFiles() : $this->getFolderFiles();
152
            if (!count($log_file)) {
153
                return [];
154
            }
155
            $this->file = $log_file[0];
156
        }
157
158
        $max_file_size = function_exists('config') ? config('logviewer.max_file_size', LaravelLogViewer::MAX_FILE_SIZE) : LaravelLogViewer::MAX_FILE_SIZE;
159
        if (app('files')->size($this->file) > $max_file_size) {
160
            return null;
161
        }
162
163
        $file = app('files')->get($this->file);
164
165
166
        return $this->convertStringExceptionLogToArray($file, $parseStack);
167
168
    }
169
170
    /**
171
     * Convert the given exception to an array.
172
     *
173
     * @param  \Exception  $e
174
     * @return array
175
     */
176
    //https://laravel.com/api/[x.x]/Illuminate/Foundation/Exceptions/Handler.html#method_convertExceptionToArray
177
    public function convertExceptionToArray(\Exception $e)
178
    {
179
        return
180
            // config('app.debug') ?
181
        [
182
            'message' => $e->getMessage(),
183
            'exception' => get_class($e),
184
            'file' => $e->getFile(),
185
            'line' => $e->getLine(),
186
            'trace' => collect($e->getTrace())->map(function ($trace) {
187
                return Arr::except($trace, ['args']);
188
            })->all(),
189
        // ] : [
190
        //     'message' => $this->isHttpException($e) ? $e->getMessage() : 'Server Error',
191
        ];
192
    }
193
194
    /**
195
     *
196
     * @param  String  $e
197
     * @return array
198
     */
199
    public function convertStringExceptionLogToArray(String $e, $parseStack = false)
200
    {
201
        $log = array();
202
203
        preg_match_all($this->pattern->getPattern('logs'), $e, $headings);
204
205
        if (!is_array($headings)) {
206
            return $log;
207
        }
208
209
        $log_data = preg_split($this->pattern->getPattern('logs'), $e);
210
211
        if ($log_data[0] < 1) {
212
            array_shift($log_data);
213
        }
214
        foreach ($headings as $h) {
215
            for ($i = 0, $j = count($h); $i < $j; $i++) {
216
                foreach ($this->level->all() as $level) {
217
                    if (strpos(strtolower($h[$i]), '.' . $level) || strpos(strtolower($h[$i]), $level . ':')) {
218
                        // $h[$i] = str_replace('\\\\', '\\', $h[$i]);
219
                        $current = [];
220
                        preg_match($this->pattern->getPattern('current_log', 0) . $level . $this->pattern->getPattern('current_log', 1), $h[$i], $current);
221
                        if (!isset($current[4])) {
222
                            preg_match($this->pattern->getPattern('current_log', 0) . $level . $this->pattern->getPattern('current_log', 2), $h[$i], $current);
223
                            if (!$current) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $current of type string[] is implicitly converted to a boolean; are you sure this is intended? If so, consider using empty($expr) instead to make it clear that you intend to check for an array without elements.

This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.

Consider making the comparison explicit by using empty(..) or ! empty(...) instead.

Loading history...
224
                                continue;
225
                            }
226
                            $log[] = array(
227
                                'context' => $current[3],
228
                                'level' => $level,
229
                                'folder' => $this->folder,
230
                                'level_class' => $this->level->cssClass($level),
231
                                'level_img' => $this->level->img($level),
232
                                'date' => $current[1],
233
                                'message' => $current[4]." ".(isset($current[5]) ? $current[5] : ""). " ",
234
                                // 'exception' => null,
235
                                // 'code' => null,
236
                                // 'file' => null,
237
                                // 'line' => null,
238
                                // 'trace' => null,
239
                            );
240
                        } else {
241
                            // $log_data[$i] = str_replace('\\\\', '\\', $log_data[$i]);
242
                            $trace = $log_data[$i];
243
                            if ($parseStack) {
244
                                $trace = $this->convertStringExceptionStackToArray($log_data[$i]);
245
                            }
246
                            $log[] = array(
247
                                'context' => $current[3],
248
                                'level' => $level,
249
                                'folder' => $this->folder,
250
                                'level_class' => $this->level->cssClass($level),
251
                                'level_img' => $this->level->img($level),
252
                                'date' => $current[1],
253
                                'message' => $current[6],
254
                                'exception' => $current[4],
255
                                // 'code' => $current[5],
256
                                'file' => isset($current[7]) ? $current[7] : null,
257
                                'line' => isset($current[8]) ? intval($current[8]) : null,
258
                                'trace' => $trace,
259
                            );
260
                        }
261
                    }
262
                }
263
            }
264
        }
265
266
        return array_reverse($log);
267
    }
268
269
    /**
270
     *
271
     * @param  String  $e
272
     * @return array
273
     */
274
    public function convertStringExceptionToArray(String $e)
275
    {
276
        $log = array();
277
278
        $eArray = preg_split("/\n/", $e);
279
        preg_match($this->pattern->getPattern('current_log_string'), $eArray[0], $current);
280
281
        $log['message'] = $current[2];
282
        $log['exception'] = $current[1];
283
        $log['file'] = isset($current[3]) ? $current[3] : null;
284
        $log['line'] = isset($current[4]) ? intval($current[4]) : null;
285
        $log['trace'] = $this->convertArrayExceptionStackStringToArray(array_slice($eArray, 2));
286
287
        return $log;
288
    }
289
290
    /**
291
     *
292
     * @param  String  $eStack
293
     * @return array
294
     */
295
    public function convertStringExceptionStackToArray(String $eStack)
296
    {
297
        $eStack = preg_replace($this->pattern->getPattern('stack_init_section'), '', $eStack);
298
        $eStackArray = preg_split("/\\n *\#/", $eStack);
299
        return $this->convertArrayExceptionStackStringToArray($eStackArray);
300
    }
301
302
    /**
303
     *
304
     * @param  array  $eStackArray
305
     * @return array
306
     */
307
    public function convertArrayExceptionStackStringToArray(array $eStackArray)
308
    {
309
        $eStackArrayReturn = [];
310
311
        foreach ($eStackArray as $s) {
312
            $s = preg_replace($this->pattern->getPattern('stack_startWith'), '', $s);
313
            if (Str::startsWith($s, "{main}")) {
314
                break;
315
            }
316
            if (Str::startsWith($s, "[internal function]: ")) {
317
                $s = str_replace("[internal function]: ", "", $s);
318
                $call = new \StdClass();
319
                preg_match($this->pattern->getPattern('stack', 0), $s, $matchs);
320
                if ($matchs) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $matchs of type string[] is implicitly converted to a boolean; are you sure this is intended? If so, consider using ! empty($expr) instead to make it clear that you intend to check for an array without elements.

This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.

Consider making the comparison explicit by using empty(..) or ! empty(...) instead.

Loading history...
321
                    $call->function = $matchs[3];
322
                    $call->class = $matchs[1];
323
                    $call->type = $matchs[2];
324
                    // $call->args = $matchs[4];
325
                    $eStackArrayReturn[] = $call;
326 View Code Duplication
                } else {
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...
327
                    preg_match($this->pattern->getPattern('stack', 1), $s, $matchs);
328
                    $call->function = $matchs[1];
329
                    // $call->args = $matchs[2];
330
                    $eStackArrayReturn[] = $call;
331
                }
332
            } else {
333
                preg_match($this->pattern->getPattern('stack', 2), $s, $matchs);
334
                if ($matchs) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $matchs of type string[] is implicitly converted to a boolean; are you sure this is intended? If so, consider using ! empty($expr) instead to make it clear that you intend to check for an array without elements.

This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.

Consider making the comparison explicit by using empty(..) or ! empty(...) instead.

Loading history...
335
                    $call = new \StdClass();
336
                    $call->file = $matchs[1];
337
                    $call->line = intval($matchs[2]);
338
                    $tmp = $matchs[3];
339
                    preg_match($this->pattern->getPattern('stack', 0), $tmp, $matchs);
340
                    if ($matchs) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $matchs of type string[] is implicitly converted to a boolean; are you sure this is intended? If so, consider using ! empty($expr) instead to make it clear that you intend to check for an array without elements.

This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.

Consider making the comparison explicit by using empty(..) or ! empty(...) instead.

Loading history...
341
                        $call->function = $matchs[3];
342
                        $call->class = $matchs[1];
343
                        $call->type = $matchs[2];
344
                        if (isset($matchs[4])) {
345
                            // $call->args = $matchs[4];
346
                        }
347 View Code Duplication
                    } else {
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...
348
                        preg_match($this->pattern->getPattern('stack', 1), $tmp, $matchs);
349
                        if ($matchs) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $matchs of type string[] is implicitly converted to a boolean; are you sure this is intended? If so, consider using ! empty($expr) instead to make it clear that you intend to check for an array without elements.

This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.

Consider making the comparison explicit by using empty(..) or ! empty(...) instead.

Loading history...
350
                            $call->function = $matchs[1];
351
                        }
352
                        if (isset($matchs[2])) {
353
                            // $call->args = $matchs[2];
354
                        }
355
                    }
356
                    $eStackArrayReturn[] = $call;
357
                } else {
358
                    $eStackArrayReturn[] = $s;
359
                }
360
            }
361
        }
362
        return $eStackArrayReturn;
363
    }
364
365
    /**
366
     *
367
     * @param  array  $stackArray
368
     * @return String
369
     */
370
    public static function convertArrayExceptionStacArrayToString(array $stackArray)
371
    {
372
        $stack = "";
373
        $stack .= "[stacktrace]";
374
        $stack .= "\n";
375
        foreach ($stackArray as $index=>$data) {
376
            $dataArray = (array)$data;
377
            $stack .= "#$index ";
378
            if (isset($dataArray['file'])) {
379
                $stack .= "{$dataArray['file']} ({$dataArray['line']}): ";
380
            }
381
            $strClass = "";
382
            if (isset($dataArray['class'])) {
383
                $strClass = $dataArray['class'];
384
            }
385
            $strType = "";
386
            if (isset($dataArray['type'])) {
387
                $strType = $dataArray['type'];
388
            }
389
            $strFunction = "";
390
            if (isset($dataArray['function'])) {
391
                 $strFunction = $dataArray['function'];
392
            }
393
            $strArgs = "";
394
            if (isset($dataArray['args'])) {
395
                $strArgs = $dataArray['args'];
396
            }
397
            $stack .= "{$strClass}{$strType}{$strFunction}({$strArgs})";
398
            $stack .= "\n";
399
        }
400
        return $stack;
401
    }
402
403
    /**
404
     * @return array
405
     */
406
    public function getFolders()
407
    {
408
        $folders = glob($this->storage_path . '/*', GLOB_ONLYDIR);
409
        if (is_array($this->storage_path)) {
410
            foreach ($this->storage_path as $value) {
411
                $folders = array_merge(
412
                    $folders,
413
                    glob($value . '/*', GLOB_ONLYDIR)
414
                );
415
            }
416
        }
417
418
        if (is_array($folders)) {
419
            foreach ($folders as $k => $folder) {
420
                $folders[$k] = basename($folder);
421
            }
422
        }
423
        return array_values($folders);
424
    }
425
426
    /**
427
     * @param bool $basename
428
     * @return array
429
     */
430
    public function getFolderFiles($basename = false)
431
    {
432
        return $this->getFiles($basename, $this->folder);
433
    }
434
435
    /**
436
     * @param bool $basename
437
     * @param string $folder
438
     * @return array
439
     */
440
    public function getFiles($basename = false, $folder = '')
441
    {
442
        $pattern = function_exists('config') ? config('logviewer.pattern', '*.log') : '*.log';
443
        $files = glob(
444
            $this->storage_path . '/' . $folder . '/' . $pattern,
445
            preg_match($this->pattern->getPattern('files'), $pattern) ? GLOB_BRACE : 0
446
        );
447
        if (is_array($this->storage_path)) {
448
            foreach ($this->storage_path as $value) {
449
                $files = array_merge(
450
                  $files,
451
                  glob(
452
                      $value . '/' . $folder . '/' . $pattern,
453
                      preg_match($this->pattern->getPattern('files'), $pattern) ? GLOB_BRACE : 0
454
                  )
455
                );
456
            }
457
        }
458
459
        $files = array_reverse($files);
460
        $files = array_filter($files, 'is_file');
461
        if ($basename && is_array($files)) {
462
            foreach ($files as $k => $file) {
463
                $files[$k] = basename($file);
464
            }
465
        }
466
        return array_values($files);
467
    }
468
}
469