Completed
Push — master ( a41bbe...18bc86 )
by ARCANEDEV
08:47
created

SessionTracker::generateSession()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 9
Code Lines 5

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 6
CRAP Score 2

Importance

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