Completed
Push — master ( 952513...9f4dd4 )
by Tyler
01:50
created

Recorder   B

Complexity

Total Complexity 40

Size/Duplication

Total Lines 195
Duplicated Lines 0 %

Coupling/Cohesion

Components 1
Dependencies 3

Test Coverage

Coverage 84.15%

Importance

Changes 1
Bugs 0 Features 0
Metric Value
wmc 40
c 1
b 0
f 0
lcom 1
cbo 3
dl 0
loc 195
ccs 69
cts 82
cp 0.8415
rs 8.2608

11 Methods

Rating   Name   Duplication   Size   Complexity  
A __construct() 0 3 1
D record() 0 42 10
A canCollect() 0 6 4
B collect() 0 21 8
A getUserId() 0 8 3
A getMethod() 0 8 2
A getData() 0 8 2
A getUrl() 0 8 2
A getIp() 0 3 1
A getStatusCode() 0 7 2
B excludeKeys() 0 12 5

How to fix   Complexity   

Complex Class

Complex classes like Recorder often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes. You can also have a look at the cohesion graph to spot any un-connected, or weakly-connected components.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

While breaking up the class, it is a good idea to analyze how other classes use Recorder, and based on these observations, apply Extract Interface, too.

1
<?php
2
3
namespace Tylercd100\LERN\Components;
4
5
use Exception;
6
use Illuminate\Support\Facades\Auth;
7
use Illuminate\Support\Facades\Input;
8
use Illuminate\Support\Facades\Request;
9
use Symfony\Component\HttpKernel\Exception\HttpExceptionInterface;
10
use Tylercd100\LERN\Exceptions\RecorderFailedException;
11
use Tylercd100\LERN\Models\ExceptionModel;
12
13
class Recorder extends Component {
14
15
    /**
16
     * @var mixed
17
     */
18
    protected $config = [];
19
20
    /**
21
     * The constructor
22
     */
23 48
    public function __construct() {
24 48
        $this->config = config('lern.record');
25 48
    }
26
27
    /**
28
     * Records an Exception to the database
29
     * @param  Exception $e The exception you want to record
30
     * @return false|ExceptionModel
31
     * @throws RecorderFailedException
32
     */
33 9
    public function record(Exception $e)
34
    {
35 9
        if ($this->shouldntHandle($e)) {
36 3
            return false;
37
        }
38
39
        $opts = [
40 6
            'class'       => get_class($e),
41 6
            'file'        => $e->getFile(),
42 6
            'line'        => $e->getLine(),
43 6
            'code'        => (is_int($e->getCode()) ? $e->getCode() : 0),
44 6
            'message'     => $e->getMessage(),
45 6
            'trace'       => $e->getTraceAsString(),
46
        ];
47
48 6
        $configDependant = array_keys($this->config['collect']);
49
50
        try {
51 6
            foreach ($configDependant as $key) {
52 6
                if ($this->canCollect($key)) {
53 3
                    $value = $this->collect($key, $e);
54 3
                    if ($value !== null) {
55 6
                        $opts[$key] = $value;
56
                    }
57
                }
58
            }
59
60 6
            $class = config('lern.recorder.model');
61 6
            $class = !empty($class) ? $class : ExceptionModel::class;
62
63 6
            $model = new $class();
64 6
            foreach($opts as $key => $value) {
65 6
                $model->{$key} = $value;
66
            }
67
68 6
            $model->save();
69 6
            return $model;
70
        } catch (Exception $e) {
71
            $code = (is_int($e->getCode()) ? $e->getCode() : 0);
72
            throw new RecorderFailedException($e->getMessage(), $code, $e);
73
        }
74
    }
75
76
    /**
77
     * Checks the config to see if you can collect certain information
78
     * @param  string $type the config value you want to check
79
     * @return boolean      
80
     */
81 6
    private function canCollect($type) {
82 6
        if (!empty($this->config) && !empty($this->config['collect']) && !empty($this->config['collect'][$type])) {
83 3
            return $this->config['collect'][$type] === true;
84
        }
85 3
        return false;
86
    }
87
88
    /**
89
     * @param string $key
90
     * @param Exception $e
91
     * @return array|int|null|string
92
     * @throws Exception
93
     */
94 3
    protected function collect($key, Exception $e = null) {
95
        switch ($key) {
96 3
            case 'user_id':
97 3
                return $this->getUserId();
98 3
            case 'method':
99 3
                return $this->getMethod();
100 3
            case 'url':
101 3
                return $this->getUrl();
102 3
            case 'data':
103 3
                return $this->getData();
104 3
            case 'ip':
105
                return $this->getIp();
106 3
            case 'status_code':
107 3
                if ($e === null) {
108
                    return 0;
109
                }
110 3
                return $this->getStatusCode($e);
111
            default:
112
                throw new Exception("{$key} is not supported! Therefore it cannot be collected!");
113
        }
114
    }
115
116
    /**
117
     * Gets the ID of the User that is logged in
118
     * @return integer|null The ID of the User or Null if not logged in
119
     */
120 3
    protected function getUserId() {
121 3
        $user = Auth::user();
122 3
        if (is_object($user) && !empty($user->id)) {
0 ignored issues
show
Bug introduced by
Accessing id on the interface Illuminate\Contracts\Auth\Authenticatable suggest that you code against a concrete implementation. How about adding an instanceof check?

If you access a property on an interface, you most likely code against a concrete implementation of the interface.

Available Fixes

  1. Adding an additional type check:

    interface SomeInterface { }
    class SomeClass implements SomeInterface {
        public $a;
    }
    
    function someFunction(SomeInterface $object) {
        if ($object instanceof SomeClass) {
            $a = $object->a;
        }
    }
    
  2. Changing the type hint:

    interface SomeInterface { }
    class SomeClass implements SomeInterface {
        public $a;
    }
    
    function someFunction(SomeClass $object) {
        $a = $object->a;
    }
    
Loading history...
123
            return $user->id;
0 ignored issues
show
Bug introduced by
Accessing id on the interface Illuminate\Contracts\Auth\Authenticatable suggest that you code against a concrete implementation. How about adding an instanceof check?

If you access a property on an interface, you most likely code against a concrete implementation of the interface.

Available Fixes

  1. Adding an additional type check:

    interface SomeInterface { }
    class SomeClass implements SomeInterface {
        public $a;
    }
    
    function someFunction(SomeInterface $object) {
        if ($object instanceof SomeClass) {
            $a = $object->a;
        }
    }
    
  2. Changing the type hint:

    interface SomeInterface { }
    class SomeClass implements SomeInterface {
        public $a;
    }
    
    function someFunction(SomeClass $object) {
        $a = $object->a;
    }
    
Loading history...
124
        } else {
125 3
            return null;
126
        }
127
    }
128
129
    /**
130
     * Gets the Method of the Request
131
     * @return string|null Possible values are null or GET, POST, DELETE, PUT, etc...
132
     */
133 3
    protected function getMethod() {
134 3
        $method = Request::method();
135 3
        if (!empty($method)) {
136 3
            return $method;
137
        } else {
138
            return null;
139
        }
140
    }
141
142
    /**
143
     * Gets the input data of the Request
144
     * @return array|null The Input data or null
145
     */
146 6
    protected function getData() {
147 6
        $data = Input::all();
148 6
        if (is_array($data)) {
149 6
            return $this->excludeKeys($data);
150
        } else {
151
            return null;
152
        }
153
    }
154
155
    /**
156
     * Gets the URL of the Request
157
     * @return string|null Returns a URL string or null
158
     */
159 3
    protected function getUrl() {
160 3
        $url = Request::url();
161 3
        if (is_string($url)) {
162 3
            return $url;
163
        } else {
164
            return null;
165
        }
166
    }
167
168
    /**
169
     * Returns the IP from the request
170
     *
171
     * @return string
172
     */
173
    protected function getIp() {
174
        return Request::ip();
175
    }
176
177
    /**
178
     * Gets the status code of the Exception
179
     * @param  Exception $e The Exception to check
180
     * @return string|integer The status code value
181
     */
182 3
    protected function getStatusCode(Exception $e) {
183 3
        if ($e instanceof HttpExceptionInterface) {
184
            return $e->getStatusCode();
185
        } else {
186 3
            return 0;
187
        }
188
    }
189
190
    /**
191
     * This function will remove all keys from an array recursively as defined in the config file
192
     * @param  array $data The array to remove keys from
193
     * @return void
194
     */
195 12
    protected function excludeKeys(array $data) {
196 12
        $keys = isset($this->config['excludeKeys']) ? $this->config['excludeKeys'] : [];
197 12
        foreach ($data as $key => &$value) {
198 9
            if (in_array($key, $keys)) {
199 9
                unset($data[$key]);
200 9
            } else if (is_array($value)) {
201 9
                $value = $this->excludeKeys($value);
202
            }
203
        }
204
205 12
        return $data;
206
    }
207
}
208