Completed
Push — master ( ec2990...7a5f39 )
by ARCANEDEV
07:59
created

SessionTracker::createSessionIfIsUnknown()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 18
Code Lines 11

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 6
CRAP Score 2.3755

Importance

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