Completed
Push — master ( f76772...1672bb )
by ARCANEDEV
07:47
created

SessionTracker::sessionIsKnown()   A

Complexity

Conditions 4
Paths 4

Size

Total Lines 13
Code Lines 8

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 3
CRAP Score 7.9062

Importance

Changes 0
Metric Value
dl 0
loc 13
ccs 3
cts 8
cp 0.375
rs 9.2
c 0
b 0
f 0
cc 4
eloc 8
nc 4
nop 0
crap 7.9062
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 2
    }
69
70
    /**
71
     * Get the session id.
72
     *
73
     * @return int
74
     */
75 2
    private function getSessionId()
76
    {
77 2
        return $this->sessionInfo['id'];
78
    }
79
80
    /**
81
     * Set the session id.
82
     *
83
     * @param  mixed  $id
84
     */
85 2
    private function setSessionId($id)
86
    {
87 2
        $this->sessionInfo['id'] = $id;
88 2
        $this->storeSession();
89 2
    }
90
91
    /* ------------------------------------------------------------------------------------------------
92
     |  Main Functions
93
     | ------------------------------------------------------------------------------------------------
94
     */
95
    /**
96
     * Track the session.
97
     *
98
     * @param  array  $data
99
     *
100
     * @return int
101
     */
102 6
    public function track(array $data)
103
    {
104 6
        $this->setSessionData($data);
105
106 2
        return $this->getSessionId();
107
    }
108
109
    /**
110
     * Check the session data.
111
     *
112
     * @param  array  $newData
113
     * @param  array  $currentData
114
     *
115
     * @return array
116
     */
117 6
    public function checkData(array $newData, array $currentData)
118
    {
119 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...
120 3
            ? $this->updateData($newData)
121 6
            : $newData;
122
    }
123
124
    /* ------------------------------------------------------------------------------------------------
125
     |  Other Functions
126
     | ------------------------------------------------------------------------------------------------
127
     */
128
    /**
129
     * Update the session data.
130
     *
131
     * @param  array  $data
132
     *
133
     * @return array
134
     */
135
    private function updateData(array $data)
136
    {
137
        /**  @var \Arcanedev\LaravelTracker\Models\Session $session  */
138
        $session = $this->checkIfUserChanged($data, Session::find($this->getSessionData('id')));
139
140
        $session->update(Arr::except($data, ['id', 'uuid']));
141
142
        return $data;
143
    }
144
145 6
    private function getSessionData($column = null)
146
    {
147 6
        $data = $this->session->get($this->getSessionKey());
148
149 6
        return $column ? Arr::get($data, $column, null) : $data;
150
    }
151
152
    /**
153
     * Check if user changed.
154
     *
155
     * @param  array                                     $data
156
     * @param  \Arcanedev\LaravelTracker\Models\Session  $model
157
     *
158
     * @return \Arcanedev\LaravelTracker\Models\Session  Session
159
     */
160
    private function checkIfUserChanged(array $data, $model)
161
    {
162
        if (
163
            ! is_null($model->user_id) &&
164
            ! is_null($data['user_id']) &&
165
            $data['user_id'] !== $model->user_id
166
        ) {
167
            $newSession = $this->regenerateSystemSession($data);
168
            $model      = $this->findByUuid($newSession['uuid']);
169
        }
170
171
        return $model;
172
    }
173
174
    private function regenerateSystemSession($data = null)
175
    {
176
        $data = $data ?: $this->getSessionData();
177
178
        if ($data) {
179
            $this->resetSessionUuid($data);
180
            $this->createSessionIfIsUnknown();
181
        }
182
183
        return $this->sessionInfo;
184
    }
185
186
    /**
187
     * Reset the session uuid.
188
     *
189
     * @param  array|null  $data
190
     *
191
     * @return array|null
192
     */
193
    private function resetSessionUuid($data = null)
194
    {
195
        $this->sessionInfo['uuid'] = null;
196
197
        $data = $data ?: $this->sessionInfo;
198
199
        unset($data['uuid']);
200
201
        $this->putSessionData($data);
202
        $this->checkSessionUuid();
203
204
        return $data;
205
    }
206
207
    /**
208
     * Put the session data.
209
     *
210
     * @param  mixed  $data
211
     */
212 2
    private function putSessionData($data)
213
    {
214 2
        $this->session->put([
215 2
            $this->getSessionKey() => $data
216 1
        ]);
217 2
    }
218
219
    /**
220
     * Check the session uuid.
221
     */
222 6
    private function checkSessionUuid()
223
    {
224 6
        if ( ! isset($this->sessionInfo['uuid']) || ! $this->sessionInfo['uuid'])
225 6
            $this->sessionInfo['uuid'] = $this->getSystemSessionId();
226 6
    }
227
228
    /**
229
     * Get the system session id.
230
     *
231
     * @return string
232
     */
233 6
    private function getSystemSessionId()
234
    {
235 6
        $sessionData = $this->getSessionData();
236
237 6
        return isset($sessionData['uuid']) ? $sessionData['uuid'] : (string) UUID::uuid4();
238
    }
239
240
    /**
241
     * @return bool
242
     */
243 6
    private function createSessionIfIsUnknown()
244
    {
245
        /** @var \Arcanedev\LaravelTracker\Models\Session $session */
246 6
        if ($known = $this->sessionIsKnown()) {
247
            $session = Session::find($id = $this->getSessionData('id'));
248
            $session->updated_at = Carbon::now();
249
            $session->save();
250
251
            $this->sessionInfo['id'] = $id;
252
        }
253
        else {
254 6
            var_dump(
0 ignored issues
show
Security Debugging Code introduced by
var_dump(\Illuminate\Sup...), $this->sessionInfo); looks like debug code. Are you sure you do not want to remove it? This might expose sensitive data.
Loading history...
255 6
                Arr::only($this->sessionInfo, ['uuid']),
256 6
                $this->sessionInfo
257 3
            );
258 6
            $session = Session::firstOrCreate(Arr::only($this->sessionInfo, ['uuid']), $this->sessionInfo);
259 2
            $this->setSessionId($session->id);
260
        }
261
262 2
        return $known;
263
    }
264
265
    /**
266
     * Check if the session is known.
267
     *
268
     * @return bool
269
     */
270 6
    private function sessionIsKnown()
271
    {
272 6
        if ( ! $this->session->has($this->getSessionKey()))
273 6
            return false;
274
275
        if ( ! $this->getSessionData('uuid') == $this->getSystemSessionId())
276
            return false;
277
278
        if ( ! $this->findByUuid($this->getSessionData('uuid')))
279
            return false;
280
281
        return true;
282
    }
283
284
    /**
285
     * Find a session by its uuid.
286
     *
287
     * @param  string  $uuid
288
     *
289
     * @return \Arcanedev\LaravelTracker\Models\Session
290
     */
291
    private function findByUuid($uuid)
292
    {
293
        return Session::where('uuid', $uuid)->first();
294
    }
295
296
    /**
297
     * Store the session information.
298
     */
299 2
    private function storeSession()
300
    {
301 2
        $this->putSessionData($this->sessionInfo);
302 2
    }
303
304
    /**
305
     * Generate session data.
306
     *
307
     * @param  array  $sessionInfo
308
     */
309 6
    private function generateSession($sessionInfo)
310
    {
311 6
        $this->sessionInfo = $sessionInfo;
312
313 6
        if ( ! $this->checkSessionDataIsReliable()) {
314
            $this->regenerateSystemSession();
315
        }
316
317 6
        $this->checkSessionUuid();
318 6
    }
319
320
    /**
321
     * Check if the session data is reliable.
322
     *
323
     * @return bool
324
     */
325 6
    private function checkSessionDataIsReliable()
326
    {
327 6
        $data = $this->getSessionData();
328
329 6
        if (isset($data['user_id']) && ($data['user_id'] !== $this->sessionInfo['user_id']))
330 3
            return false;
331
332 6
        if (isset($data['client_ip']) && ($data['client_ip'] !== $this->sessionInfo['client_ip']))
333 3
            return false;
334
335 6
        if (isset($data['user_agent']) && ($data['user_agent'] !== $this->sessionInfo['user_agent']))
336 3
            return false;
337
338 6
        return true;
339
    }
340
341
    /**
342
     * Ensure that the session data is complete.
343
     */
344
    private function ensureSessionDataIsComplete()
345
    {
346
        $sessionData = $this->getSessionData();
347
        $completed   = true;
348
349
        foreach ($this->sessionInfo as $key => $value) {
350
            if ($sessionData[$key] !== $value) {
351
                /** @var \Arcanedev\LaravelTracker\Models\Session $model */
352
                if ( ! isset($model))
353
                    $model = Session::find($this->getSessionId());
354
355
                $model->setAttribute($key, $value);
356
                $model->save();
357
358
                $completed = false;
359
            }
360
        }
361
362
        if ( ! $completed) $this->storeSession();
363
    }
364
}
365