Completed
Pull Request — master (#2)
by ARCANEDEV
09:30
created

SessionTracker::findByUuid()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 4
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 2

Importance

Changes 0
Metric Value
dl 0
loc 4
c 0
b 0
f 0
ccs 0
cts 2
cp 0
rs 10
cc 1
eloc 2
nc 1
nop 1
crap 2
1
<?php namespace Arcanedev\LaravelTracker\Trackers;
2
3
use Arcanedev\LaravelTracker\Contracts\Trackers\SessionTracker as SessionTrackerContract;
4
use Arcanedev\LaravelTracker\Models\AbstractModel;
5
use Carbon\Carbon;
6
use Illuminate\Support\Arr;
7
use Ramsey\Uuid\Uuid;
8
9
/**
10
 * Class     SessionTracker
11
 *
12
 * @package  Arcanedev\LaravelTracker\Trackers
13
 * @author   ARCANEDEV <[email protected]>
14
 */
15
class SessionTracker extends AbstractTracker implements SessionTrackerContract
16
{
17
    /* ------------------------------------------------------------------------------------------------
18
     |  Properties
19
     | ------------------------------------------------------------------------------------------------
20
     */
21
    /** @var array */
22
    private $sessionInfo = [];
23
24
    /* ------------------------------------------------------------------------------------------------
25
     |  Getters and Setters
26
     | ------------------------------------------------------------------------------------------------
27
     */
28
    /**
29
     * Get the model.
30
     *
31
     * @return \Arcanedev\LaravelTracker\Models\Session
32
     */
33 18
    protected function getModel()
34
    {
35 18
        return $this->makeModel(AbstractModel::MODEL_SESSION);
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 session data.
50
     *
51
     * @param  array  $data
52
     */
53 18
    private function setSessionData(array $data)
54
    {
55 18
        $this->generateSession($data);
56
57 18
        if ($this->createSessionIfIsUnknown()) {
58
            $this->ensureSessionDataIsComplete();
59
        }
60 18
    }
61
62
    /**
63
     * Get the session id.
64
     *
65
     * @return int
66
     */
67 18
    private function getSessionId()
68
    {
69 18
        return $this->sessionInfo['id'];
70
    }
71
72
    /**
73
     * Set the session id.
74
     *
75
     * @param  mixed  $id
76
     */
77 18
    private function setSessionId($id)
78
    {
79 18
        $this->sessionInfo['id'] = $id;
80 18
    }
81
82
    /* ------------------------------------------------------------------------------------------------
83
     |  Main Functions
84
     | ------------------------------------------------------------------------------------------------
85
     */
86
    /**
87
     * Track the session.
88
     *
89
     * @param  array  $data
90
     *
91
     * @return int
92
     */
93 18
    public function track(array $data)
94
    {
95 18
        $this->setSessionData($data);
96
97 18
        return $this->getSessionId();
98
    }
99
100
    /**
101
     * Check the session 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 session 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 12
             ->update(Arr::except($data, ['id', 'uuid']));
130
131 12
        return $data;
132
    }
133
134
    /**
135
     * Get the session data.
136
     *
137
     * @param  string|null  $column
138
     *
139
     * @return mixed
140
     */
141 18
    private function getSessionData($column = null)
142
    {
143 18
        $data = $this->session()->get($this->getSessionKey());
144
145 18
        return is_null($column) ? $data : Arr::get($data, $column, null);
146
    }
147
148
    /**
149
     * Check if user changed.
150
     *
151
     * @param  array  $data
152
     *
153
     * @return \Arcanedev\LaravelTracker\Models\Session
154
     */
155 12
    private function checkIfUserChanged(array $data)
156
    {
157 12
        $model = $this->getModel()->find($this->getSessionData('id'));
158
159 12
        if (is_null($model) && ! $this->sessionIsKnown())
160 12
            return $this->createSessionForGuest($data);
161
162
        if (
163
            ! is_null($model->user_id) &&
164
            ! is_null($data['user_id']) &&
165
            $data['user_id'] !== $model->user_id
166
        ) {
167
            $newSession = $this->regenerateSystemSession($data);
168
            $model      = $this->findByUuid($newSession['uuid']);
169
        }
170
171
        return $model;
172
    }
173
174
    /**
175
     * Create session for guest.
176
     *
177
     * @param  array  $data
178
     *
179
     * @return \Arcanedev\LaravelTracker\Models\Session
180
     */
181 12
    private function createSessionForGuest(array $data)
182
    {
183 12
        $this->generateSession($data);
184
185 12
        $session = $this->getModel()->fill($this->sessionInfo);
186 12
        $session->save();
187
188 12
        $this->putSessionData($data);
189 12
        $this->setSessionId($session->id);
190
191 12
        return $session;
192
    }
193
194
    /**
195
     * Regenerate system session.
196
     *
197
     * @param  array|null  $data
198
     *
199
     * @return array
200
     */
201
    private function regenerateSystemSession($data = null)
202
    {
203
        $data = $data ?: $this->getSessionData();
204
205
        if ($data) {
206
            $this->resetSessionUuid($data);
207
            $this->createSessionIfIsUnknown();
208
        }
209
210
        return $this->sessionInfo;
211
    }
212
213
    /**
214
     * Reset the session uuid.
215
     *
216
     * @param  array|null  $data
217
     *
218
     * @return array|null
219
     */
220
    private function resetSessionUuid($data = null)
221
    {
222
        $this->sessionInfo['uuid'] = null;
223
224
        $data = $data ?: $this->sessionInfo;
225
226
        unset($data['uuid']);
227
228
        $this->putSessionData($data);
229
        $this->checkSessionUuid();
230
231
        return $data;
232
    }
233
234
    /**
235
     * Put the session data.
236
     *
237
     * @param  array  $data
238
     */
239 18
    private function putSessionData(array $data)
240
    {
241 18
        $this->session()->put([
242 18
            $this->getSessionKey() => $data
243 9
        ]);
244 18
    }
245
246
    /**
247
     * Check the session uuid.
248
     */
249 18
    private function checkSessionUuid()
250
    {
251 18
        if ( ! isset($this->sessionInfo['uuid']) || ! $this->sessionInfo['uuid'])
252 18
            $this->sessionInfo['uuid'] = $this->getSystemSessionId();
253 18
    }
254
255
    /**
256
     * Get the system session id.
257
     *
258
     * @return string
259
     */
260 18
    private function getSystemSessionId()
261
    {
262 18
        return Arr::get($this->getSessionData(), 'uuid', (string) Uuid::uuid4());
263
    }
264
265
    /**
266
     * @return bool
267
     */
268 18
    private function createSessionIfIsUnknown()
269
    {
270 18
        $model = $this->getModel();
271
        /** @var \Arcanedev\LaravelTracker\Models\Session $session */
272 18
        if ($known = $this->sessionIsKnown()) {
273
            $session = $model->find($id = $this->getSessionData('id'));
274
            $session->updated_at = Carbon::now();
275
            $session->save();
276
277
            $this->setSessionId($id);
278
        }
279
        else {
280 18
            $session = $model->firstOrCreate(Arr::only($this->sessionInfo, ['uuid']), $this->sessionInfo);
281 18
            $this->setSessionId($session->id);
282 18
            $this->putSessionData($this->sessionInfo);
283
        }
284
285 18
        return $known;
286
    }
287
288
    /**
289
     * Check if the session is known.
290
     *
291
     * @return bool
292
     */
293 18
    private function sessionIsKnown()
294
    {
295 18
        if ( ! $this->session()->has($this->getSessionKey()))
296 18
            return false;
297
298 12
        if ($this->getSessionData('uuid') != $this->getSystemSessionId())
299 12
            return false;
300
301
        if ( ! $this->findByUuid($this->getSessionData('uuid')))
302
            return false;
303
304
        return true;
305
    }
306
307
    /**
308
     * Find a session by its uuid.
309
     *
310
     * @param  string  $uuid
311
     *
312
     * @return \Arcanedev\LaravelTracker\Models\Session
313
     */
314
    private function findByUuid($uuid)
315
    {
316
        return $this->getModel()->where('uuid', $uuid)->first();
317
    }
318
319
    /**
320
     * Generate session data.
321
     *
322
     * @param  array  $sessionInfo
323
     */
324 18
    private function generateSession(array $sessionInfo)
325
    {
326 18
        $this->sessionInfo = $sessionInfo;
327
328 18
        if ( ! $this->checkSessionDataIsReliable())
329 9
            $this->regenerateSystemSession();
330
331 18
        $this->checkSessionUuid();
332 18
    }
333
334
    /**
335
     * Check if the session data is reliable.
336
     *
337
     * @return bool
338
     */
339 18
    private function checkSessionDataIsReliable()
340
    {
341 18
        $data = $this->getSessionData();
342
343 18
        if (isset($data['user_id']) && ($data['user_id'] !== $this->sessionInfo['user_id']))
344 9
            return false;
345
346 18
        if (isset($data['client_ip']) && ($data['client_ip'] !== $this->sessionInfo['client_ip']))
347 9
            return false;
348
349 18
        if (isset($data['user_agent']) && ($data['user_agent'] !== $this->sessionInfo['user_agent']))
350 9
            return false;
351
352 18
        return true;
353
    }
354
355
    /**
356
     * Ensure that the session data is complete.
357
     */
358
    private function ensureSessionDataIsComplete()
359
    {
360
        $sessionData = $this->getSessionData();
361
        $completed   = true;
362
363
        foreach ($this->sessionInfo as $key => $value) {
364
            if ($sessionData[$key] !== $value) {
365
                /** @var \Arcanedev\LaravelTracker\Models\Session $model */
366
                if ( ! isset($model))
367
                    $model = $this->getModel()->find($this->getSessionId());
368
369
                $model->setAttribute($key, $value);
370
                $model->save();
371
372
                $completed = false;
373
            }
374
        }
375
376
        if ( ! $completed)
377
            $this->putSessionData($this->sessionInfo);
378
    }
379
}
380