Completed
Push — master ( a4831f...a4416a )
by ARCANEDEV
07:50
created

SessionTracker::findByUuid()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 4
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 2

Importance

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