Completed
Push — master ( 2a36b8...a41bbe )
by ARCANEDEV
08:50
created

SessionTracker::updateData()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 9
Code Lines 4

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 2

Importance

Changes 0
Metric Value
dl 0
loc 9
c 0
b 0
f 0
ccs 0
cts 4
cp 0
rs 9.6666
cc 1
eloc 4
nc 1
nop 1
crap 2
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 6
    }
69
70
    /**
71
     * Get the session id.
72
     *
73
     * @return int
74
     */
75 6
    private function getSessionId()
76
    {
77 6
        return $this->sessionInfo['id'];
78
    }
79
80
    /**
81
     * Set the session id.
82
     *
83
     * @param  mixed  $id
84
     */
85 6
    private function setSessionId($id)
86
    {
87 6
        $this->sessionInfo['id'] = $id;
88 6
    }
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 6
        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)
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...
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 6
    private function putSessionData($data)
212
    {
213 6
        $this->session->put([
214 6
            $this->getSessionKey() => $data
215 3
        ]);
216 6
    }
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 = Session::firstOrCreate(Arr::only($this->sessionInfo, ['uuid']), $this->sessionInfo);
254 6
            $this->setSessionId($session->id);
255 6
            $this->storeSession();
256
        }
257
258 6
        return $known;
259
    }
260
261
    /**
262
     * Check if the session is known.
263
     *
264
     * @return bool
265
     */
266 6
    private function sessionIsKnown()
267
    {
268 6
        if ( ! $this->session->has($this->getSessionKey()))
269 6
            return false;
270
271
        if ( ! $this->getSessionData('uuid') == $this->getSystemSessionId())
272
            return false;
273
274
        if ( ! $this->findByUuid($this->getSessionData('uuid')))
275
            return false;
276
277
        return true;
278
    }
279
280
    /**
281
     * Find a session by its uuid.
282
     *
283
     * @param  string  $uuid
284
     *
285
     * @return \Arcanedev\LaravelTracker\Models\Session
286
     */
287
    private function findByUuid($uuid)
288
    {
289
        return Session::where('uuid', $uuid)->first();
290
    }
291
292
    /**
293
     * Store the session information.
294
     */
295 6
    private function storeSession()
296
    {
297 6
        $this->putSessionData($this->sessionInfo);
298 6
    }
299
300
    /**
301
     * Generate session data.
302
     *
303
     * @param  array  $sessionInfo
304
     */
305 6
    private function generateSession($sessionInfo)
306
    {
307 6
        $this->sessionInfo = $sessionInfo;
308
309 6
        if ( ! $this->checkSessionDataIsReliable()) {
310
            $this->regenerateSystemSession();
311
        }
312
313 6
        $this->checkSessionUuid();
314 6
    }
315
316
    /**
317
     * Check if the session data is reliable.
318
     *
319
     * @return bool
320
     */
321 6
    private function checkSessionDataIsReliable()
322
    {
323 6
        $data = $this->getSessionData();
324
325 6
        if (isset($data['user_id']) && ($data['user_id'] !== $this->sessionInfo['user_id']))
326 3
            return false;
327
328 6
        if (isset($data['client_ip']) && ($data['client_ip'] !== $this->sessionInfo['client_ip']))
329 3
            return false;
330
331 6
        if (isset($data['user_agent']) && ($data['user_agent'] !== $this->sessionInfo['user_agent']))
332 3
            return false;
333
334 6
        return true;
335
    }
336
337
    /**
338
     * Ensure that the session data is complete.
339
     */
340
    private function ensureSessionDataIsComplete()
341
    {
342
        $sessionData = $this->getSessionData();
343
        $completed   = true;
344
345
        foreach ($this->sessionInfo as $key => $value) {
346
            if ($sessionData[$key] !== $value) {
347
                /** @var \Arcanedev\LaravelTracker\Models\Session $model */
348
                if ( ! isset($model))
349
                    $model = Session::find($this->getSessionId());
350
351
                $model->setAttribute($key, $value);
352
                $model->save();
353
354
                $completed = false;
355
            }
356
        }
357
358
        if ( ! $completed) $this->storeSession();
359
    }
360
}
361