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
|
24 |
|
public function __construct(SessionStore $session) |
38
|
|
|
{ |
39
|
24 |
|
$this->session = $session; |
40
|
24 |
|
} |
41
|
|
|
|
42
|
|
|
/* ------------------------------------------------------------------------------------------------ |
43
|
|
|
| Getters & Setters |
44
|
|
|
| ------------------------------------------------------------------------------------------------ |
45
|
|
|
*/ |
46
|
|
|
/** |
47
|
|
|
* Get the session key. |
48
|
|
|
* |
49
|
|
|
* @return string |
50
|
|
|
*/ |
51
|
6 |
|
private function getSessionKey() |
52
|
|
|
{ |
53
|
6 |
|
return config('laravel-tracker.session.name', 'session_name_here'); |
54
|
|
|
} |
55
|
|
|
|
56
|
|
|
/** |
57
|
|
|
* Set the session data. |
58
|
|
|
* |
59
|
|
|
* @param array $data |
60
|
|
|
*/ |
61
|
6 |
|
private function setSessionData(array $data) |
62
|
|
|
{ |
63
|
6 |
|
$this->generateSession($data); |
64
|
|
|
|
65
|
6 |
|
if ($this->createSessionIfIsUnknown()) { |
66
|
|
|
$this->ensureSessionDataIsComplete(); |
67
|
|
|
} |
68
|
4 |
|
} |
69
|
|
|
|
70
|
|
|
/** |
71
|
|
|
* Get the session id. |
72
|
|
|
* |
73
|
|
|
* @return int |
74
|
|
|
*/ |
75
|
4 |
|
private function getSessionId() |
76
|
|
|
{ |
77
|
4 |
|
return $this->sessionInfo['id']; |
78
|
|
|
} |
79
|
|
|
|
80
|
|
|
/** |
81
|
|
|
* Set the session id. |
82
|
|
|
* |
83
|
|
|
* @param mixed $id |
84
|
|
|
*/ |
85
|
4 |
|
private function setSessionId($id) |
86
|
|
|
{ |
87
|
4 |
|
$this->sessionInfo['id'] = $id; |
88
|
4 |
|
} |
89
|
|
|
|
90
|
|
|
/* ------------------------------------------------------------------------------------------------ |
91
|
|
|
| Main Functions |
92
|
|
|
| ------------------------------------------------------------------------------------------------ |
93
|
|
|
*/ |
94
|
|
|
/** |
95
|
|
|
* Track the session. |
96
|
|
|
* |
97
|
|
|
* @param array $data |
98
|
|
|
* |
99
|
|
|
* @return int |
100
|
|
|
*/ |
101
|
6 |
|
public function track(array $data) |
102
|
|
|
{ |
103
|
6 |
|
$this->setSessionData($data); |
104
|
|
|
|
105
|
4 |
|
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) |
|
|
|
|
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
|
6 |
|
private function getSessionData($column = null) |
145
|
|
|
{ |
146
|
6 |
|
$data = $this->session->get($this->getSessionKey()); |
147
|
|
|
|
148
|
6 |
|
return $column ? Arr::get($data, $column, null) : $data; |
149
|
|
|
} |
150
|
|
|
|
151
|
|
|
/** |
152
|
|
|
* Check if user changed. |
153
|
|
|
* |
154
|
|
|
* @param array $data |
155
|
|
|
* @param \Arcanedev\LaravelTracker\Models\Session $model |
156
|
|
|
* |
157
|
|
|
* @return \Arcanedev\LaravelTracker\Models\Session Session |
158
|
|
|
*/ |
159
|
|
|
private function checkIfUserChanged(array $data, $model) |
160
|
|
|
{ |
161
|
|
|
if ( |
162
|
|
|
! is_null($model->user_id) && |
163
|
|
|
! is_null($data['user_id']) && |
164
|
|
|
$data['user_id'] !== $model->user_id |
165
|
|
|
) { |
166
|
|
|
$newSession = $this->regenerateSystemSession($data); |
167
|
|
|
$model = $this->findByUuid($newSession['uuid']); |
168
|
|
|
} |
169
|
|
|
|
170
|
|
|
return $model; |
171
|
|
|
} |
172
|
|
|
|
173
|
|
|
private function regenerateSystemSession($data = null) |
174
|
|
|
{ |
175
|
|
|
$data = $data ?: $this->getSessionData(); |
176
|
|
|
|
177
|
|
|
if ($data) { |
178
|
|
|
$this->resetSessionUuid($data); |
179
|
|
|
$this->createSessionIfIsUnknown(); |
180
|
|
|
} |
181
|
|
|
|
182
|
|
|
return $this->sessionInfo; |
183
|
|
|
} |
184
|
|
|
|
185
|
|
|
/** |
186
|
|
|
* Reset the session uuid. |
187
|
|
|
* |
188
|
|
|
* @param array|null $data |
189
|
|
|
* |
190
|
|
|
* @return array|null |
191
|
|
|
*/ |
192
|
|
|
private function resetSessionUuid($data = null) |
193
|
|
|
{ |
194
|
|
|
$this->sessionInfo['uuid'] = null; |
195
|
|
|
|
196
|
|
|
$data = $data ?: $this->sessionInfo; |
197
|
|
|
|
198
|
|
|
unset($data['uuid']); |
199
|
|
|
|
200
|
|
|
$this->putSessionData($data); |
201
|
|
|
$this->checkSessionUuid(); |
202
|
|
|
|
203
|
|
|
return $data; |
204
|
|
|
} |
205
|
|
|
|
206
|
|
|
/** |
207
|
|
|
* Put the session data. |
208
|
|
|
* |
209
|
|
|
* @param mixed $data |
210
|
|
|
*/ |
211
|
4 |
|
private function putSessionData($data) |
212
|
|
|
{ |
213
|
4 |
|
$this->session->put([ |
214
|
4 |
|
$this->getSessionKey() => $data |
215
|
2 |
|
]); |
216
|
4 |
|
} |
217
|
|
|
|
218
|
|
|
/** |
219
|
|
|
* Check the session uuid. |
220
|
|
|
*/ |
221
|
6 |
|
private function checkSessionUuid() |
222
|
|
|
{ |
223
|
6 |
|
if ( ! isset($this->sessionInfo['uuid']) || ! $this->sessionInfo['uuid']) |
224
|
6 |
|
$this->sessionInfo['uuid'] = $this->getSystemSessionId(); |
225
|
6 |
|
} |
226
|
|
|
|
227
|
|
|
/** |
228
|
|
|
* Get the system session id. |
229
|
|
|
* |
230
|
|
|
* @return string |
231
|
|
|
*/ |
232
|
6 |
|
private function getSystemSessionId() |
233
|
|
|
{ |
234
|
6 |
|
$sessionData = $this->getSessionData(); |
235
|
|
|
|
236
|
6 |
|
return isset($sessionData['uuid']) ? $sessionData['uuid'] : (string) UUID::uuid4(); |
237
|
|
|
} |
238
|
|
|
|
239
|
|
|
/** |
240
|
|
|
* @return bool |
241
|
|
|
*/ |
242
|
6 |
|
private function createSessionIfIsUnknown() |
243
|
|
|
{ |
244
|
|
|
/** @var \Arcanedev\LaravelTracker\Models\Session $session */ |
245
|
6 |
|
if ($known = $this->sessionIsKnown()) { |
246
|
|
|
$session = Session::find($id = $this->getSessionData('id')); |
247
|
|
|
$session->updated_at = Carbon::now(); |
248
|
|
|
$session->save(); |
249
|
|
|
|
250
|
|
|
$this->setSessionId($id); |
251
|
|
|
} |
252
|
|
|
else { |
253
|
6 |
|
$session = $this->firstOrCreate(Arr::only($this->sessionInfo, ['uuid']), $this->sessionInfo); |
254
|
4 |
|
$this->setSessionId($session->id); |
255
|
4 |
|
$this->storeSession(); |
256
|
|
|
} |
257
|
|
|
|
258
|
4 |
|
return $known; |
259
|
|
|
} |
260
|
|
|
|
261
|
6 |
|
protected function firstOrCreate(array $attributes, array $values = []) |
262
|
|
|
{ |
263
|
6 |
|
if (version_compare(app()->version(), '5.2.0', '>=')) |
264
|
5 |
|
return Session::firstOrCreate($attributes, $values); |
265
|
|
|
|
266
|
2 |
|
$instance = new Session; |
267
|
|
|
|
268
|
2 |
|
foreach ($attributes as $key => $value) { |
269
|
2 |
|
$instance = $instance->where($key, $value); |
270
|
1 |
|
} |
271
|
|
|
|
272
|
2 |
|
if ( ! is_null($first = $instance->first())) |
273
|
1 |
|
return $first; |
274
|
|
|
|
275
|
2 |
|
$instance = new Session($attributes + $values); |
276
|
2 |
|
$instance->save(); |
277
|
|
|
|
278
|
2 |
|
return $instance; |
279
|
|
|
} |
280
|
|
|
|
281
|
|
|
/** |
282
|
|
|
* Check if the session is known. |
283
|
|
|
* |
284
|
|
|
* @return bool |
285
|
|
|
*/ |
286
|
6 |
|
private function sessionIsKnown() |
287
|
|
|
{ |
288
|
6 |
|
if ( ! $this->session->has($this->getSessionKey())) |
289
|
6 |
|
return false; |
290
|
|
|
|
291
|
|
|
if ( ! $this->getSessionData('uuid') == $this->getSystemSessionId()) |
292
|
|
|
return false; |
293
|
|
|
|
294
|
|
|
if ( ! $this->findByUuid($this->getSessionData('uuid'))) |
295
|
|
|
return false; |
296
|
|
|
|
297
|
|
|
return true; |
298
|
|
|
} |
299
|
|
|
|
300
|
|
|
/** |
301
|
|
|
* Find a session by its uuid. |
302
|
|
|
* |
303
|
|
|
* @param string $uuid |
304
|
|
|
* |
305
|
|
|
* @return \Arcanedev\LaravelTracker\Models\Session |
306
|
|
|
*/ |
307
|
|
|
private function findByUuid($uuid) |
308
|
|
|
{ |
309
|
|
|
return Session::where('uuid', $uuid)->first(); |
310
|
|
|
} |
311
|
|
|
|
312
|
|
|
/** |
313
|
|
|
* Store the session information. |
314
|
|
|
*/ |
315
|
4 |
|
private function storeSession() |
316
|
|
|
{ |
317
|
4 |
|
$this->putSessionData($this->sessionInfo); |
318
|
4 |
|
} |
319
|
|
|
|
320
|
|
|
/** |
321
|
|
|
* Generate session data. |
322
|
|
|
* |
323
|
|
|
* @param array $sessionInfo |
324
|
|
|
*/ |
325
|
6 |
|
private function generateSession($sessionInfo) |
326
|
|
|
{ |
327
|
6 |
|
$this->sessionInfo = $sessionInfo; |
328
|
|
|
|
329
|
6 |
|
if ( ! $this->checkSessionDataIsReliable()) { |
330
|
|
|
$this->regenerateSystemSession(); |
331
|
|
|
} |
332
|
|
|
|
333
|
6 |
|
$this->checkSessionUuid(); |
334
|
6 |
|
} |
335
|
|
|
|
336
|
|
|
/** |
337
|
|
|
* Check if the session data is reliable. |
338
|
|
|
* |
339
|
|
|
* @return bool |
340
|
|
|
*/ |
341
|
6 |
|
private function checkSessionDataIsReliable() |
342
|
|
|
{ |
343
|
6 |
|
$data = $this->getSessionData(); |
344
|
|
|
|
345
|
6 |
|
if (isset($data['user_id']) && ($data['user_id'] !== $this->sessionInfo['user_id'])) |
346
|
3 |
|
return false; |
347
|
|
|
|
348
|
6 |
|
if (isset($data['client_ip']) && ($data['client_ip'] !== $this->sessionInfo['client_ip'])) |
349
|
3 |
|
return false; |
350
|
|
|
|
351
|
6 |
|
if (isset($data['user_agent']) && ($data['user_agent'] !== $this->sessionInfo['user_agent'])) |
352
|
3 |
|
return false; |
353
|
|
|
|
354
|
6 |
|
return true; |
355
|
|
|
} |
356
|
|
|
|
357
|
|
|
/** |
358
|
|
|
* Ensure that the session data is complete. |
359
|
|
|
*/ |
360
|
|
|
private function ensureSessionDataIsComplete() |
361
|
|
|
{ |
362
|
|
|
$sessionData = $this->getSessionData(); |
363
|
|
|
$completed = true; |
364
|
|
|
|
365
|
|
|
foreach ($this->sessionInfo as $key => $value) { |
366
|
|
|
if ($sessionData[$key] !== $value) { |
367
|
|
|
/** @var \Arcanedev\LaravelTracker\Models\Session $model */ |
368
|
|
|
if ( ! isset($model)) |
369
|
|
|
$model = Session::find($this->getSessionId()); |
370
|
|
|
|
371
|
|
|
$model->setAttribute($key, $value); |
372
|
|
|
$model->save(); |
373
|
|
|
|
374
|
|
|
$completed = false; |
375
|
|
|
} |
376
|
|
|
} |
377
|
|
|
|
378
|
|
|
if ( ! $completed) $this->storeSession(); |
379
|
|
|
} |
380
|
|
|
} |
381
|
|
|
|
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.