Completed
Push — master ( 2ff1b0...b38f5c )
by ARCANEDEV
18:09 queued 07:56
created

SessionTracker::ensureSessionDataIsComplete()   B

Complexity

Conditions 5
Paths 8

Size

Total Lines 21
Code Lines 12

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 30

Importance

Changes 0
Metric Value
dl 0
loc 21
ccs 0
cts 15
cp 0
rs 8.7624
c 0
b 0
f 0
cc 5
eloc 12
nc 8
nop 0
crap 30
1
<?php namespace Arcanedev\LaravelTracker\Trackers;
2
3
use Arcanedev\LaravelTracker\Contracts\Trackers\SessionTracker as SessionTrackerContract;
4
use Arcanedev\LaravelTracker\Support\BindingManager;
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(BindingManager::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
     * Regenerate system session.
170
     *
171
     * @param  array|null  $data
172
     *
173
     * @return array
174
     */
175
    private function regenerateSystemSession($data = null)
176
    {
177
        $data = $data ?: $this->getSessionData();
178
179
        if ($data) {
180
            $this->resetSessionUuid($data);
181
            $this->createSessionIfIsUnknown();
182
        }
183
184
        return $this->sessionInfo;
185
    }
186
187
    /**
188
     * Reset the session uuid.
189
     *
190
     * @param  array|null  $data
191
     *
192
     * @return array|null
193
     */
194
    private function resetSessionUuid($data = null)
195
    {
196
        $this->sessionInfo['uuid'] = null;
197
198
        $data = $data ?: $this->sessionInfo;
199
200
        unset($data['uuid']);
201
202
        $this->putSessionData($data);
203
        $this->checkSessionUuid();
204
205
        return $data;
206
    }
207
208
    /**
209
     * Put the session data.
210
     *
211
     * @param  array  $data
212
     */
213 18
    private function putSessionData(array $data)
214
    {
215 18
        $this->session()->put([
216 18
            $this->getSessionKey() => $data
217 9
        ]);
218 18
    }
219
220
    /**
221
     * Check the session uuid.
222
     */
223 18
    private function checkSessionUuid()
224
    {
225 18
        if ( ! isset($this->sessionInfo['uuid']) || ! $this->sessionInfo['uuid'])
226 18
            $this->sessionInfo['uuid'] = $this->getSystemSessionId();
227 18
    }
228
229
    /**
230
     * Get the system session id.
231
     *
232
     * @return string
233
     */
234 18
    private function getSystemSessionId()
235
    {
236 18
        return Arr::get($this->getSessionData(), 'uuid', (string) Uuid::uuid4());
237
    }
238
239
    /**
240
     * @return bool
241
     */
242 18
    private function createSessionIfIsUnknown()
243
    {
244 18
        $model = $this->getModel();
245
        /** @var \Arcanedev\LaravelTracker\Models\Session $session */
246 18
        if ($known = $this->sessionIsKnown()) {
247
            $session = $model->find($id = $this->getSessionData('id'));
248
            $session->updated_at = Carbon::now();
249
            $session->save();
250
251
            $this->setSessionId($id);
252
        }
253
        else {
254 18
            $session = $model->firstOrCreate(Arr::only($this->sessionInfo, ['uuid']), $this->sessionInfo);
255 18
            $this->setSessionId($session->id);
256 18
            $this->putSessionData($this->sessionInfo);
257
        }
258
259 18
        return $known;
260
    }
261
262
    /**
263
     * Check if the session is known.
264
     *
265
     * @return bool
266
     */
267 18
    private function sessionIsKnown()
268
    {
269 18
        if ( ! $this->session()->has($this->getSessionKey()))
270 18
            return false;
271
272
        if ($this->getSessionData('uuid') != $this->getSystemSessionId())
273
            return false;
274
275
        if ( ! $this->findByUuid($this->getSessionData('uuid')))
276
            return false;
277
278
        return true;
279
    }
280
281
    /**
282
     * Find a session by its uuid.
283
     *
284
     * @param  string  $uuid
285
     *
286
     * @return \Arcanedev\LaravelTracker\Models\Session
287
     */
288
    private function findByUuid($uuid)
289
    {
290
        return $this->getModel()->where('uuid', $uuid)->first();
291
    }
292
293
    /**
294
     * Generate session data.
295
     *
296
     * @param  array  $sessionInfo
297
     */
298 18
    private function generateSession(array $sessionInfo)
299
    {
300 18
        $this->sessionInfo = $sessionInfo;
301
302 18
        if ( ! $this->checkSessionDataIsReliable())
303 9
            $this->regenerateSystemSession();
304
305 18
        $this->checkSessionUuid();
306 18
    }
307
308
    /**
309
     * Check if the session data is reliable.
310
     *
311
     * @return bool
312
     */
313 18
    private function checkSessionDataIsReliable()
314
    {
315 18
        $data = $this->getSessionData();
316
317 18
        if (isset($data['user_id']) && ($data['user_id'] !== $this->sessionInfo['user_id']))
318 9
            return false;
319
320 18
        if (isset($data['client_ip']) && ($data['client_ip'] !== $this->sessionInfo['client_ip']))
321 9
            return false;
322
323 18
        if (isset($data['user_agent']) && ($data['user_agent'] !== $this->sessionInfo['user_agent']))
324 9
            return false;
325
326 18
        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 = $this->getModel()->find($this->getSessionId());
342
343
                $model->setAttribute($key, $value);
344
                $model->save();
345
346
                $completed = false;
347
            }
348
        }
349
350
        if ( ! $completed)
351
            $this->putSessionData($this->sessionInfo);
352
    }
353
}
354