These results are based on our legacy PHP analysis, consider migrating to our new PHP analysis engine instead. Learn more
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
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
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 | |||
130 | 12 | return $data; |
|
131 | } |
||
132 | |||
133 | /** |
||
134 | * Get the session data. |
||
135 | * |
||
136 | * @param string|null $column |
||
137 | * |
||
138 | * @return mixed |
||
139 | */ |
||
140 | 18 | private function getSessionData($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->getSessionData('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 | $newSession = $this->regenerateSystemSession($data); |
||
163 | $model = $this->findByUuid($newSession['uuid']); |
||
164 | $model->update(Arr::except($data, ['id', 'uuid'])); |
||
165 | } |
||
166 | 12 | } |
|
167 | |||
168 | /** |
||
169 | * Create session for guest. |
||
170 | * |
||
171 | * @param array $data |
||
172 | * |
||
173 | * @return \Arcanedev\LaravelTracker\Models\Session |
||
174 | */ |
||
175 | private function createSessionForGuest(array $data) |
||
0 ignored issues
–
show
|
|||
176 | { |
||
177 | $this->generateSession($data); |
||
178 | |||
179 | $session = $this->getModel()->fill($this->sessionInfo); |
||
180 | $session->save(); |
||
181 | |||
182 | $this->putSessionData($data); |
||
183 | $this->setSessionId($session->id); |
||
184 | |||
185 | return $session; |
||
186 | } |
||
187 | |||
188 | /** |
||
189 | * Regenerate system session. |
||
190 | * |
||
191 | * @param array|null $data |
||
192 | * |
||
193 | * @return array |
||
194 | */ |
||
195 | private function regenerateSystemSession($data = null) |
||
196 | { |
||
197 | $data = $data ?: $this->getSessionData(); |
||
198 | |||
199 | if ($data) { |
||
200 | $this->resetSessionUuid($data); |
||
201 | $this->createSessionIfIsUnknown(); |
||
202 | } |
||
203 | |||
204 | return $this->sessionInfo; |
||
205 | } |
||
206 | |||
207 | /** |
||
208 | * Reset the session uuid. |
||
209 | * |
||
210 | * @param array|null $data |
||
211 | * |
||
212 | * @return array|null |
||
213 | */ |
||
214 | private function resetSessionUuid($data = null) |
||
215 | { |
||
216 | $this->sessionInfo['uuid'] = null; |
||
217 | |||
218 | $data = $data ?: $this->sessionInfo; |
||
219 | |||
220 | unset($data['uuid']); |
||
221 | |||
222 | $this->putSessionData($data); |
||
223 | $this->checkSessionUuid(); |
||
224 | |||
225 | return $data; |
||
226 | } |
||
227 | |||
228 | /** |
||
229 | * Put the session data. |
||
230 | * |
||
231 | * @param array $data |
||
232 | */ |
||
233 | 18 | private function putSessionData(array $data) |
|
234 | { |
||
235 | 18 | $this->session()->put([ |
|
236 | 18 | $this->getSessionKey() => $data |
|
237 | 9 | ]); |
|
238 | 18 | } |
|
239 | |||
240 | /** |
||
241 | * Check the session uuid. |
||
242 | */ |
||
243 | 18 | private function checkSessionUuid() |
|
244 | { |
||
245 | 18 | if ( ! isset($this->sessionInfo['uuid']) || ! $this->sessionInfo['uuid']) |
|
246 | 18 | $this->sessionInfo['uuid'] = $this->getSystemSessionId(); |
|
247 | 18 | } |
|
248 | |||
249 | /** |
||
250 | * Get the system session id. |
||
251 | * |
||
252 | * @return string |
||
253 | */ |
||
254 | 18 | private function getSystemSessionId() |
|
255 | { |
||
256 | 18 | return Arr::get($this->getSessionData(), 'uuid', (string) Uuid::uuid4()); |
|
257 | } |
||
258 | |||
259 | /** |
||
260 | * @return bool |
||
261 | */ |
||
262 | 18 | private function createSessionIfIsUnknown() |
|
263 | { |
||
264 | 18 | $model = $this->getModel(); |
|
265 | /** @var \Arcanedev\LaravelTracker\Models\Session $session */ |
||
266 | 18 | if ($known = $this->sessionIsKnown()) { |
|
267 | $session = $model->find($id = $this->getSessionData('id')); |
||
268 | $session->updated_at = Carbon::now(); |
||
269 | $session->save(); |
||
270 | |||
271 | $this->setSessionId($id); |
||
272 | } |
||
273 | else { |
||
274 | 18 | $session = $model->firstOrCreate(Arr::only($this->sessionInfo, ['uuid']), $this->sessionInfo); |
|
275 | 18 | $this->setSessionId($session->id); |
|
276 | 18 | $this->putSessionData($this->sessionInfo); |
|
277 | } |
||
278 | |||
279 | 18 | return $known; |
|
280 | } |
||
281 | |||
282 | /** |
||
283 | * Check if the session is known. |
||
284 | * |
||
285 | * @return bool |
||
286 | */ |
||
287 | 18 | private function sessionIsKnown() |
|
288 | { |
||
289 | 18 | if ( ! $this->session()->has($this->getSessionKey())) |
|
290 | 18 | return false; |
|
291 | |||
292 | if ($this->getSessionData('uuid') != $this->getSystemSessionId()) |
||
293 | return false; |
||
294 | |||
295 | if ( ! $this->findByUuid($this->getSessionData('uuid'))) |
||
296 | return false; |
||
297 | |||
298 | return true; |
||
299 | } |
||
300 | |||
301 | /** |
||
302 | * Find a session by its uuid. |
||
303 | * |
||
304 | * @param string $uuid |
||
305 | * |
||
306 | * @return \Arcanedev\LaravelTracker\Models\Session |
||
307 | */ |
||
308 | private function findByUuid($uuid) |
||
309 | { |
||
310 | return $this->getModel()->where('uuid', $uuid)->first(); |
||
311 | } |
||
312 | |||
313 | /** |
||
314 | * Generate session data. |
||
315 | * |
||
316 | * @param array $sessionInfo |
||
317 | */ |
||
318 | 18 | private function generateSession(array $sessionInfo) |
|
319 | { |
||
320 | 18 | $this->sessionInfo = $sessionInfo; |
|
321 | |||
322 | 18 | if ( ! $this->checkSessionDataIsReliable()) |
|
323 | 9 | $this->regenerateSystemSession(); |
|
324 | |||
325 | 18 | $this->checkSessionUuid(); |
|
326 | 18 | } |
|
327 | |||
328 | /** |
||
329 | * Check if the session data is reliable. |
||
330 | * |
||
331 | * @return bool |
||
332 | */ |
||
333 | 18 | private function checkSessionDataIsReliable() |
|
334 | { |
||
335 | 18 | $data = $this->getSessionData(); |
|
336 | |||
337 | 18 | if (isset($data['user_id']) && ($data['user_id'] !== $this->sessionInfo['user_id'])) |
|
338 | 9 | return false; |
|
339 | |||
340 | 18 | if (isset($data['client_ip']) && ($data['client_ip'] !== $this->sessionInfo['client_ip'])) |
|
341 | 9 | return false; |
|
342 | |||
343 | 18 | if (isset($data['user_agent']) && ($data['user_agent'] !== $this->sessionInfo['user_agent'])) |
|
344 | 9 | return false; |
|
345 | |||
346 | 18 | return true; |
|
347 | } |
||
348 | |||
349 | /** |
||
350 | * Ensure that the session data is complete. |
||
351 | */ |
||
352 | private function ensureSessionDataIsComplete() |
||
353 | { |
||
354 | $sessionData = $this->getSessionData(); |
||
355 | $completed = true; |
||
356 | |||
357 | foreach ($this->sessionInfo as $key => $value) { |
||
358 | if ($sessionData[$key] !== $value) { |
||
359 | /** @var \Arcanedev\LaravelTracker\Models\Session $model */ |
||
360 | if ( ! isset($model)) |
||
361 | $model = $this->getModel()->find($this->getSessionId()); |
||
362 | |||
363 | $model->setAttribute($key, $value); |
||
364 | $model->save(); |
||
365 | |||
366 | $completed = false; |
||
367 | } |
||
368 | } |
||
369 | |||
370 | if ( ! $completed) |
||
371 | $this->putSessionData($this->sessionInfo); |
||
372 | } |
||
373 | } |
||
374 |
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.