Completed
Push — master ( c30100...d2035e )
by ARCANEDEV
16:12 queued 05:42
created

VisitorTracker::putSessionData()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 6
Code Lines 3

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 5
CRAP Score 1

Importance

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