VisitorTracker   B
last analyzed

Complexity

Total Complexity 48

Size/Duplication

Total Lines 357
Duplicated Lines 0 %

Coupling/Cohesion

Components 1
Dependencies 8

Test Coverage

Coverage 60.55%

Importance

Changes 0
Metric Value
wmc 48
lcom 1
cbo 8
dl 0
loc 357
rs 8.4864
c 0
b 0
f 0
ccs 66
cts 109
cp 0.6055

22 Methods

Rating   Name   Duplication   Size   Complexity  
A getModel() 0 4 1
A getSessionKey() 0 4 1
A getVisitorId() 0 4 1
A setVisitorId() 0 4 1
A track() 0 6 1
A checkData() 0 6 4
A updateData() 0 6 1
A getVisitorData() 0 6 2
A resetVisitorUuid() 0 13 2
A checkVisitorUuid() 0 5 3
A getVisitorIdFromSystem() 0 4 1
A createVisitorIfIsUnknown() 0 23 2
A isVisitorKnown() 0 13 4
A findByUuid() 0 4 1
A checkDataIsUnreliable() 0 4 2
A setVisitorData() 0 8 2
B checkIfUserChanged() 0 15 5
A regenerateSystemVisitor() 0 11 3
A putSessionData() 0 6 1
A generateVisitor() 0 9 2
A checkVisitorDataIsReliable() 0 10 3
B ensureVisitorDataIsComplete() 0 21 5

How to fix   Complexity   

Complex Class

Complex classes like VisitorTracker 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 VisitorTracker, and based on these observations, apply Extract Interface, too.

1
<?php namespace Arcanedev\LaravelTracker\Trackers;
2
3
use Arcanedev\LaravelTracker\Contracts\Trackers\VisitorTracker as VisitorTrackerContract;
4
use Arcanedev\LaravelTracker\Support\BindingManager;
5
use Carbon\Carbon;
6
use Illuminate\Support\Arr;
7
use Ramsey\Uuid\Uuid;
8
9
/**
10
 * Class     VisitorTracker
11
 *
12
 * @package  Arcanedev\LaravelTracker\Trackers
13
 * @author   ARCANEDEV <[email protected]>
14
 */
15
class VisitorTracker extends AbstractTracker implements VisitorTrackerContract
16
{
17
    /* -----------------------------------------------------------------
18
     |  Properties
19
     | -----------------------------------------------------------------
20
     */
21
22
    /** @var  array */
23
    private $visitorInfo = [];
24
25
    /* -----------------------------------------------------------------
26
     |  Getters and Setters
27
     | -----------------------------------------------------------------
28
     */
29
30
    /**
31
     * Get the model.
32
     *
33
     * @return \Arcanedev\LaravelTracker\Models\Visitor
34
     */
35 9
    protected function getModel()
36
    {
37 9
        return $this->makeModel(BindingManager::MODEL_VISITOR);
38
    }
39
40
    /**
41
     * Get the session key.
42
     *
43
     * @return string
44
     */
45 9
    private function getSessionKey()
46
    {
47 9
        return $this->getConfig('session.name', 'tracker_session');
48
    }
49
50
    /**
51
     * Set the visitor data.
52
     *
53
     * @param  array  $data
54
     */
55 9
    private function setVisitorData(array $data)
56
    {
57 9
        $this->generateVisitor($data);
58
59 9
        if ($this->createVisitorIfIsUnknown()) {
60
            $this->ensureVisitorDataIsComplete();
61
        }
62 9
    }
63
64
    /**
65
     * Get the visitor id.
66
     *
67
     * @return int
68
     */
69 9
    private function getVisitorId()
70
    {
71 9
        return $this->visitorInfo['id'];
72
    }
73
74
    /**
75
     * Set the visitor id.
76
     *
77
     * @param  mixed  $id
78
     */
79 9
    private function setVisitorId($id)
80
    {
81 9
        $this->visitorInfo['id'] = $id;
82 9
    }
83
84
    /* -----------------------------------------------------------------
85
     |  Main Methods
86
     | -----------------------------------------------------------------
87
     */
88
89
    /**
90
     * Track the visitor.
91
     *
92
     * @param  array  $data
93
     *
94
     * @return int
95
     */
96 9
    public function track(array $data)
97
    {
98 9
        $this->setVisitorData($data);
99
100 9
        return $this->getVisitorId();
101
    }
102
103
    /**
104
     * Check the visitor data.
105
     *
106
     * @param  array  $currentData
107
     * @param  array  $newData
108
     *
109
     * @return array
110
     */
111 6
    public function checkData(array $currentData, array $newData)
112
    {
113 6
        return ($currentData && $newData && $currentData !== $newData)
0 ignored issues
show
Bug Best Practice introduced by
The expression $currentData 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...
Bug Best Practice introduced by
The expression $newData 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...
114 6
            ? $this->updateData($newData)
115 6
            : $newData;
116
    }
117
118
    /* -----------------------------------------------------------------
119
     |  Other Methods
120
     | -----------------------------------------------------------------
121
     */
122
123
    /**
124
     * Update the visitor data.
125
     *
126
     * @param  array  $data
127
     *
128
     * @return array
129
     */
130 6
    private function updateData(array $data)
131
    {
132 6
        $this->checkIfUserChanged($data);
133
134 6
        return $data;
135
    }
136
137
    /**
138
     * Get the visitor data.
139
     *
140
     * @param  string|null  $column
141
     *
142
     * @return mixed
143
     */
144 9
    private function getVisitorData($column = null)
145
    {
146 9
        $data = $this->session()->get($this->getSessionKey());
147
148 9
        return is_null($column) ? $data : Arr::get($data, $column, null);
149
    }
150
151
    /**
152
     * Check if user changed.
153
     *
154
     * @param  array  $data
155
     */
156 6
    private function checkIfUserChanged(array $data)
157
    {
158 6
        $model = $this->getModel()->newQuery()->find($this->getVisitorData('id'));
159
160
        if (
161 6
            ! is_null($model) &&
162 4
            ! is_null($model->user_id) &&
163 4
            ! is_null($data['user_id']) &&
164 4
            $data['user_id'] !== $model->user_id
165
        ) {
166
            $newVisitor = $this->regenerateSystemVisitor($data);
167
            $model      = $this->findByUuid($newVisitor['uuid']);
168
            $model->update(Arr::except($data, ['id', 'uuid']));
169
        }
170 6
    }
171
172
    /**
173
     * Regenerate visitor data for the system.
174
     *
175
     * @param  array|null  $data
176
     *
177
     * @return array
178
     */
179
    private function regenerateSystemVisitor($data = null)
180
    {
181
        $data = $data ?: $this->getVisitorData();
182
183
        if ($data) {
184
            $this->resetVisitorUuid($data);
185
            $this->createVisitorIfIsUnknown();
186
        }
187
188
        return $this->visitorInfo;
189
    }
190
191
    /**
192
     * Reset the visitor uuid.
193
     *
194
     * @param  array|null  $data
195
     *
196
     * @return array|null
197
     */
198
    private function resetVisitorUuid($data = null)
199
    {
200
        $this->visitorInfo['uuid'] = null;
201
202
        $data = $data ?: $this->visitorInfo;
203
204
        unset($data['uuid']);
205
206
        $this->putSessionData($data);
207
        $this->checkVisitorUuid();
208
209
        return $data;
210
    }
211
212
    /**
213
     * Put the session data.
214
     *
215
     * @param  array  $data
216
     */
217 9
    private function putSessionData(array $data)
218
    {
219 9
        $this->session()->put([
220 9
            $this->getSessionKey() => $data
221
        ]);
222 9
    }
223
224
    /**
225
     * Check the visitor uuid.
226
     */
227 9
    private function checkVisitorUuid()
228
    {
229 9
        if ( ! isset($this->visitorInfo['uuid']) || ! $this->visitorInfo['uuid'])
230 9
            $this->visitorInfo['uuid'] = $this->getVisitorIdFromSystem();
231 9
    }
232
233
    /**
234
     * Get the visitor id from the system.
235
     *
236
     * @return string
237
     */
238 9
    private function getVisitorIdFromSystem()
239
    {
240 9
        return Arr::get($this->getVisitorData(), 'uuid', (string) Uuid::uuid4());
241
    }
242
243
    /**
244
     * Create a new visitor if is unknown.
245
     *
246
     * @return bool
247
     */
248 9
    private function createVisitorIfIsUnknown()
249
    {
250 9
        $model = $this->getModel();
251
252
        /** @var  \Arcanedev\LaravelTracker\Models\Visitor  $visitor */
253 9
        if ($this->isVisitorKnown()) {
254
            $visitor = $model->newQuery()->find($id = $this->getVisitorData($model->getKeyName()));
255
            $visitor->updated_at = Carbon::now();
256
            $visitor->save();
257
258
            $this->setVisitorId($id);
259
260
            return true;
261
        }
262
263 9
        $visitor = $model->newQuery()
264 9
            ->firstOrCreate(Arr::only($this->visitorInfo, ['uuid']), $this->visitorInfo);
265
266 9
        $this->setVisitorId($visitor->getKey());
267 9
        $this->putSessionData($this->visitorInfo);
268
269 9
        return false;
270
    }
271
272
    /**
273
     * Check if the visitor is known.
274
     *
275
     * @return bool
276
     */
277 9
    private function isVisitorKnown()
278
    {
279 9
        if ( ! $this->session()->has($this->getSessionKey()))
280 9
            return false;
281
282
        if ($this->getVisitorData('uuid') != $this->getVisitorIdFromSystem())
283
            return false;
284
285
        if ( ! $this->findByUuid($this->getVisitorData('uuid')))
286
            return false;
287
288
        return true;
289
    }
290
291
    /**
292
     * Find a visitor by its uuid.
293
     *
294
     * @param  string  $uuid
295
     *
296
     * @return \Arcanedev\LaravelTracker\Models\Visitor
297
     */
298
    private function findByUuid($uuid)
299
    {
300
        return $this->getModel()->where('uuid', $uuid)->first();
301
    }
302
303
    /**
304
     * Generate visitor data.
305
     *
306
     * @param  array  $visitorInfo
307
     */
308 9
    private function generateVisitor(array $visitorInfo)
309
    {
310 9
        $this->visitorInfo = $visitorInfo;
311
312 9
        if ( ! $this->checkVisitorDataIsReliable())
313
            $this->regenerateSystemVisitor();
314
315 9
        $this->checkVisitorUuid();
316 9
    }
317
318
    /**
319
     * Check if the visitor data is reliable.
320
     *
321
     * @return bool
322
     */
323 9
    private function checkVisitorDataIsReliable()
324
    {
325 9
        $data = $this->getVisitorData();
326
327 9
        foreach (['user_id', 'client_ip', 'user_agent'] as $key) {
328 9
            if ($this->checkDataIsUnreliable($data, $key)) return false;
329
        }
330
331 9
        return true;
332
    }
333
334
    /**
335
     * Check the data is unreliable.
336
     *
337
     * @param  array|null  $data
338
     * @param  string      $key
339
     *
340
     * @return bool
341
     */
342 9
    private function checkDataIsUnreliable($data, $key)
343
    {
344 9
        return isset($data[$key]) && ($data[$key] !== $this->visitorInfo[$key]);
345
    }
346
347
    /**
348
     * Ensure that the visitor data is complete.
349
     */
350
    private function ensureVisitorDataIsComplete()
351
    {
352
        $visitorData = $this->getVisitorData();
353
        $completed   = true;
354
355
        foreach ($this->visitorInfo as $key => $value) {
356
            if ($visitorData[$key] !== $value) {
357
                /** @var  \Arcanedev\LaravelTracker\Models\Visitor  $model */
358
                if ( ! isset($model))
359
                    $model = $this->getModel()->find($this->getVisitorId());
360
361
                $model->setAttribute($key, $value);
362
                $model->save();
363
364
                $completed = false;
365
            }
366
        }
367
368
        if ( ! $completed)
369
            $this->putSessionData($this->visitorInfo);
370
    }
371
}
372