Completed
Push — master ( 243903...9a7d92 )
by ARCANEDEV
9s
created

src/Trackers/SessionTracker.php (3 issues)

Upgrade to new PHP Analysis Engine

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
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
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
This method is not used, and could be removed.
Loading history...
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