Completed
Push — master ( 9431fe...0a33e9 )
by Yaro
06:56
created

LogEnvelope::censorSensitiveFields()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 6
Code Lines 3

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 6
rs 9.4285
c 0
b 0
f 0
cc 2
eloc 3
nc 2
nop 2
1
<?php 
2
3
namespace Yaro\LogEnvelope;
4
5
use Exception;
6
use SplFileObject;
7
use Illuminate\Support\Facades\Log;
8
use Illuminate\Support\Facades\Request;
9
use Illuminate\Support\Facades\Session;
10
use Yaro\LogEnvelope\Drivers\DriverFactory;
11
12
class LogEnvelope
13
{
14
    private $config = [];
15
    private $cachedConfig = [];
16
17
    public function __construct()
18
    {
19
        $this->config['censored_fields'] = config('yaro.log-envelope.censored_fields', ['password']);
20
        $this->config['except'] = config('yaro.log-envelope.except', []);
21
        $this->config['count'] = config('yaro.log-envelope.lines_count', 6);
22
        $this->config['drivers'] = config('yaro.log-envelope.drivers', []);
23
    } // end __construct
24
25
    public function send($exception)
26
    {
27
        $this->onBefore();
28
29
        try {
30
            $data = $this->getExceptionData($exception);
31
32
            if ($this->isSkipException($data['class'])) {
33
                return;
34
            }
35
36
            foreach ($this->config['drivers'] as $driver => $driverConfig) {
37
                DriverFactory::create($driver, $data)->setConfig($driverConfig)->send();
38
            }
39
        } catch (Exception $e) {
40
            Log::error($e);
41
        }
42
        
43
        $this->onAfter();
44
    } // end send
45
    
46
    private function onBefore()
47
    {
48
        $this->cachedConfig = [];
49
        $forcedConfig = config('yaro.log-envelope.force_config', []);
50
        foreach ($forcedConfig as $configKey => $configValue) {
51
            $this->cachedConfig[$configKey] = config($configKey);
52
        }
53
        if ($forcedConfig) {
54
            config($forcedConfig);
55
        }
56
    } // end onBefore
57
    
58
    private function onAfter()
59
    {
60
        if ($this->cachedConfig) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $this->cachedConfig of type array 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...
61
            config($this->cachedConfig);
62
        }
63
    } // end onAfter
64
    
65
    public function isSkipException($exceptionClass)
66
    {
67
        return in_array($exceptionClass, $this->config['except']);
68
    } // end isSkipException
69
70
    private function getExceptionData($exception)
0 ignored issues
show
Coding Style introduced by
getExceptionData uses the super-global variable $_POST which is generally not recommended.

Instead of super-globals, we recommend to explicitly inject the dependencies of your class. This makes your code less dependent on global state and it becomes generally more testable:

// Bad
class Router
{
    public function generate($path)
    {
        return $_SERVER['HOST'].$path;
    }
}

// Better
class Router
{
    private $host;

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

    public function generate($path)
    {
        return $this->host.$path;
    }
}

class Controller
{
    public function myAction(Request $request)
    {
        // Instead of
        $page = isset($_GET['page']) ? intval($_GET['page']) : 1;

        // Better (assuming you use the Symfony2 request)
        $page = $request->query->get('page', 1);
    }
}
Loading history...
71
    {
72
        $data = [];
73
74
        $data['host'] = Request::server('HTTP_HOST');
75
        $data['method'] = Request::method();
76
        $data['fullUrl'] = Request::fullUrl();
77
        if (php_sapi_name() === 'cli') {
78
            $data['host'] = parse_url(config('app.url'), PHP_URL_HOST);
79
            $data['method'] = 'CLI';
80
        }
81
        $data['exception'] = $exception->getMessage();
82
        $data['error'] = $exception->getTraceAsString();
83
        $data['line'] = $exception->getLine();
84
        $data['file'] = $exception->getFile();
85
        $data['class'] = get_class($exception);
86
        $data['storage'] = array(
87
            'SERVER'  => Request::server(),
88
            'GET'     => Request::query(),
89
            'POST'    => $_POST,
90
            'FILE'    => Request::file(),
91
            'OLD'     => Request::hasSession() ? Request::old() : [],
0 ignored issues
show
Bug introduced by
The method hasSession() does not exist on Illuminate\Support\Facades\Request. Did you maybe mean session()?

This check marks calls to methods that do not seem to exist on an object.

This is most likely the result of a method being renamed without all references to it being renamed likewise.

Loading history...
92
            'COOKIE'  => Request::cookie(),
93
            'SESSION' => Request::hasSession() ? Session::all() : [],
0 ignored issues
show
Bug introduced by
The method hasSession() does not exist on Illuminate\Support\Facades\Request. Did you maybe mean session()?

This check marks calls to methods that do not seem to exist on an object.

This is most likely the result of a method being renamed without all references to it being renamed likewise.

Loading history...
94
            'HEADERS' => Request::header(),
95
        );
96
97
        // Remove empty, false and null values
0 ignored issues
show
Unused Code Comprehensibility introduced by
39% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
98
        $data['storage'] = array_filter($data['storage']);
99
100
        // Censor sensitive field values
101
        array_walk_recursive($data['storage'], 'self::censorSensitiveFields');
102
103
        $count = $this->config['count'];
104
        
105
        $data['exegutor']   = [];
106
        $data['file_lines'] = [];
107
        
108
        $file = new SplFileObject($data['file']);
109
        for ($i = -1 * abs($count); $i <= abs($count); $i++) {
110
            list($line, $exegutorLine) = $this->getLineInfo($file, $data['line'], $i);
111
            if (!$line && !$exegutorLine) {
112
                continue;
113
            }
114
            $data['exegutor'][] = $exegutorLine;
115
            $data['file_lines'][$data['line'] + $i] = $line;
116
        }
117
118
        // to make Symfony exception more readable
119
        if ($data['class'] == 'Symfony\Component\Debug\Exception\FatalErrorException') {
120
            preg_match("~^(.+)' in ~", $data['exception'], $matches);
121
            if (isset($matches[1])) {
122
                $data['exception'] = $matches[1];
123
            }
124
        }
125
126
        return $data;
127
    } // end getExceptionData
128
129
    /**
130
     * Set the value of specified fields to *****
131
     *
132
     * @param string $value
133
     * @param string $key
134
     * @return void
135
     */
136
    public function censorSensitiveFields(&$value, $key)
137
    {
138
        if (in_array($key, $this->config['censored_fields'], true)) {
139
            $value = '*****';
140
        }
141
    }
142
143
    /**
144
     * @param SplFileObject $file
145
     */
146
    private function getLineInfo($file, $line, $i)
147
    {
148
        $currentLine = $line + $i;
149
        // cuz array starts with 0, when file lines start count from 1
150
        $index = $currentLine - 1;
151
        if ($index < 0) {
152
            return [false, false];
153
        }
154
        $file->seek($index);
155
156
        return [
157
            $file->__toString(),
158
            [
159
                'line' => '<span style="color:#aaaaaa;">' . $currentLine . '.</span> ' . SyntaxHighlight::process($file->__toString()),
160
                'wrap_left' => $i ? '' : '<span style="color: #F5F5F5; background-color: #5A3E3E; width: 100%; display: block;">',
161
                'wrap_right' => $i ? '' : '</span>',
162
            ]
163
        ];
164
    } // end getLineInfo
165
}
166