1
|
|
|
<?php namespace Arcanedev\LaravelTracker\Trackers; |
2
|
|
|
|
3
|
|
|
use Arcanedev\LaravelTracker\Contracts\Trackers\VisitorTracker as VisitorTrackerContract; |
4
|
|
|
use Arcanedev\LaravelTracker\Support\BindingManager; |
5
|
|
|
use Carbon\Carbon; |
6
|
|
|
use Illuminate\Support\Arr; |
7
|
|
|
use Ramsey\Uuid\Uuid; |
8
|
|
|
|
9
|
|
|
/** |
10
|
|
|
* Class VisitorTracker |
11
|
|
|
* |
12
|
|
|
* @package Arcanedev\LaravelTracker\Trackers |
13
|
|
|
* @author ARCANEDEV <[email protected]> |
14
|
|
|
*/ |
15
|
|
|
class VisitorTracker extends AbstractTracker implements VisitorTrackerContract |
16
|
|
|
{ |
17
|
|
|
/* ------------------------------------------------------------------------------------------------ |
18
|
|
|
| Properties |
19
|
|
|
| ------------------------------------------------------------------------------------------------ |
20
|
|
|
*/ |
21
|
|
|
/** @var array */ |
22
|
|
|
private $visitorInfo = []; |
23
|
|
|
|
24
|
|
|
/* ------------------------------------------------------------------------------------------------ |
25
|
|
|
| Getters and Setters |
26
|
|
|
| ------------------------------------------------------------------------------------------------ |
27
|
|
|
*/ |
28
|
|
|
/** |
29
|
|
|
* Get the model. |
30
|
|
|
* |
31
|
|
|
* @return \Arcanedev\LaravelTracker\Models\Visitor |
32
|
|
|
*/ |
33
|
18 |
|
protected function getModel() |
34
|
|
|
{ |
35
|
18 |
|
return $this->makeModel(BindingManager::MODEL_VISITOR); |
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 visitor data. |
50
|
|
|
* |
51
|
|
|
* @param array $data |
52
|
|
|
*/ |
53
|
18 |
|
private function setVisitorData(array $data) |
54
|
|
|
{ |
55
|
18 |
|
$this->generateVisitor($data); |
56
|
|
|
|
57
|
18 |
|
if ($this->createVisitorIfIsUnknown()) { |
58
|
|
|
$this->ensureVisitorDataIsComplete(); |
59
|
|
|
} |
60
|
18 |
|
} |
61
|
|
|
|
62
|
|
|
/** |
63
|
|
|
* Get the visitor id. |
64
|
|
|
* |
65
|
|
|
* @return int |
66
|
|
|
*/ |
67
|
18 |
|
private function getVisitorId() |
68
|
|
|
{ |
69
|
18 |
|
return $this->visitorInfo['id']; |
70
|
|
|
} |
71
|
|
|
|
72
|
|
|
/** |
73
|
|
|
* Set the visitor id. |
74
|
|
|
* |
75
|
|
|
* @param mixed $id |
76
|
|
|
*/ |
77
|
18 |
|
private function setVisitorId($id) |
78
|
|
|
{ |
79
|
18 |
|
$this->visitorInfo['id'] = $id; |
80
|
18 |
|
} |
81
|
|
|
|
82
|
|
|
/* ------------------------------------------------------------------------------------------------ |
83
|
|
|
| Main Functions |
84
|
|
|
| ------------------------------------------------------------------------------------------------ |
85
|
|
|
*/ |
86
|
|
|
/** |
87
|
|
|
* Track the visitor. |
88
|
|
|
* |
89
|
|
|
* @param array $data |
90
|
|
|
* |
91
|
|
|
* @return int |
92
|
|
|
*/ |
93
|
18 |
|
public function track(array $data) |
94
|
|
|
{ |
95
|
18 |
|
$this->setVisitorData($data); |
96
|
|
|
|
97
|
18 |
|
return $this->getVisitorId(); |
98
|
|
|
} |
99
|
|
|
|
100
|
|
|
/** |
101
|
|
|
* Check the visitor 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) |
|
|
|
|
111
|
12 |
|
? $this->updateData($newData) |
112
|
12 |
|
: $newData; |
113
|
|
|
} |
114
|
|
|
|
115
|
|
|
/* ------------------------------------------------------------------------------------------------ |
116
|
|
|
| Other Functions |
117
|
|
|
| ------------------------------------------------------------------------------------------------ |
118
|
|
|
*/ |
119
|
|
|
/** |
120
|
|
|
* Update the visitor 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 visitor data. |
135
|
|
|
* |
136
|
|
|
* @param string|null $column |
137
|
|
|
* |
138
|
|
|
* @return mixed |
139
|
|
|
*/ |
140
|
18 |
|
private function getVisitorData($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->getVisitorData('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
|
|
|
$newVisitor = $this->regenerateSystemVisitor($data); |
163
|
|
|
$model = $this->findByUuid($newVisitor['uuid']); |
164
|
|
|
$model->update(Arr::except($data, ['id', 'uuid'])); |
165
|
|
|
} |
166
|
12 |
|
} |
167
|
|
|
|
168
|
|
|
/** |
169
|
|
|
* Regenerate visitor data for the system. |
170
|
|
|
* |
171
|
|
|
* @param array|null $data |
172
|
|
|
* |
173
|
|
|
* @return array |
174
|
|
|
*/ |
175
|
|
|
private function regenerateSystemVisitor($data = null) |
176
|
|
|
{ |
177
|
|
|
$data = $data ?: $this->getVisitorData(); |
178
|
|
|
|
179
|
|
|
if ($data) { |
180
|
|
|
$this->resetVisitorUuid($data); |
181
|
|
|
$this->createVisitorIfIsUnknown(); |
182
|
|
|
} |
183
|
|
|
|
184
|
|
|
return $this->visitorInfo; |
185
|
|
|
} |
186
|
|
|
|
187
|
|
|
/** |
188
|
|
|
* Reset the visitor uuid. |
189
|
|
|
* |
190
|
|
|
* @param array|null $data |
191
|
|
|
* |
192
|
|
|
* @return array|null |
193
|
|
|
*/ |
194
|
|
|
private function resetVisitorUuid($data = null) |
195
|
|
|
{ |
196
|
|
|
$this->visitorInfo['uuid'] = null; |
197
|
|
|
|
198
|
|
|
$data = $data ?: $this->visitorInfo; |
199
|
|
|
|
200
|
|
|
unset($data['uuid']); |
201
|
|
|
|
202
|
|
|
$this->putSessionData($data); |
203
|
|
|
$this->checkVisitorUuid(); |
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 visitor uuid. |
222
|
|
|
*/ |
223
|
18 |
|
private function checkVisitorUuid() |
224
|
|
|
{ |
225
|
18 |
|
if ( ! isset($this->visitorInfo['uuid']) || ! $this->visitorInfo['uuid']) |
226
|
18 |
|
$this->visitorInfo['uuid'] = $this->getVisitorIdFromSystem(); |
227
|
18 |
|
} |
228
|
|
|
|
229
|
|
|
/** |
230
|
|
|
* Get the visitor id from the system. |
231
|
|
|
* |
232
|
|
|
* @return string |
233
|
|
|
*/ |
234
|
18 |
|
private function getVisitorIdFromSystem() |
235
|
|
|
{ |
236
|
18 |
|
return Arr::get($this->getVisitorData(), 'uuid', (string) Uuid::uuid4()); |
237
|
|
|
} |
238
|
|
|
|
239
|
|
|
/** |
240
|
|
|
* Create a new visitor if is unknown. |
241
|
|
|
* |
242
|
|
|
* @return bool |
243
|
|
|
*/ |
244
|
18 |
|
private function createVisitorIfIsUnknown() |
245
|
|
|
{ |
246
|
18 |
|
$model = $this->getModel(); |
247
|
|
|
|
248
|
|
|
/** @var \Arcanedev\LaravelTracker\Models\Visitor $visitor */ |
249
|
18 |
|
if ($this->isVisitorKnown()) { |
250
|
|
|
$visitor = $model->find($id = $this->getVisitorData('id')); |
251
|
|
|
$visitor->updated_at = Carbon::now(); |
252
|
|
|
$visitor->save(); |
253
|
|
|
|
254
|
|
|
$this->setVisitorId($id); |
255
|
|
|
|
256
|
|
|
return true; |
257
|
|
|
} |
258
|
|
|
|
259
|
18 |
|
$visitor = $model->firstOrCreate(Arr::only($this->visitorInfo, ['uuid']), $this->visitorInfo); |
260
|
18 |
|
$this->setVisitorId($visitor->id); |
261
|
18 |
|
$this->putSessionData($this->visitorInfo); |
262
|
|
|
|
263
|
18 |
|
return false; |
264
|
|
|
} |
265
|
|
|
|
266
|
|
|
/** |
267
|
|
|
* Check if the visitor is known. |
268
|
|
|
* |
269
|
|
|
* @return bool |
270
|
|
|
*/ |
271
|
18 |
|
private function isVisitorKnown() |
272
|
|
|
{ |
273
|
18 |
|
if ( ! $this->session()->has($this->getSessionKey())) |
274
|
18 |
|
return false; |
275
|
|
|
|
276
|
|
|
if ($this->getVisitorData('uuid') != $this->getVisitorIdFromSystem()) |
277
|
|
|
return false; |
278
|
|
|
|
279
|
|
|
if ( ! $this->findByUuid($this->getVisitorData('uuid'))) |
280
|
|
|
return false; |
281
|
|
|
|
282
|
|
|
return true; |
283
|
|
|
} |
284
|
|
|
|
285
|
|
|
/** |
286
|
|
|
* Find a visitor by its uuid. |
287
|
|
|
* |
288
|
|
|
* @param string $uuid |
289
|
|
|
* |
290
|
|
|
* @return \Arcanedev\LaravelTracker\Models\Visitor |
291
|
|
|
*/ |
292
|
|
|
private function findByUuid($uuid) |
293
|
|
|
{ |
294
|
|
|
return $this->getModel()->where('uuid', $uuid)->first(); |
295
|
|
|
} |
296
|
|
|
|
297
|
|
|
/** |
298
|
|
|
* Generate visitor data. |
299
|
|
|
* |
300
|
|
|
* @param array $visitorInfo |
301
|
|
|
*/ |
302
|
18 |
|
private function generateVisitor(array $visitorInfo) |
303
|
|
|
{ |
304
|
18 |
|
$this->visitorInfo = $visitorInfo; |
305
|
|
|
|
306
|
18 |
|
if ( ! $this->checkVisitorDataIsReliable()) |
307
|
9 |
|
$this->regenerateSystemVisitor(); |
308
|
|
|
|
309
|
18 |
|
$this->checkVisitorUuid(); |
310
|
18 |
|
} |
311
|
|
|
|
312
|
|
|
/** |
313
|
|
|
* Check if the visitor data is reliable. |
314
|
|
|
* |
315
|
|
|
* @return bool |
316
|
|
|
*/ |
317
|
18 |
|
private function checkVisitorDataIsReliable() |
318
|
|
|
{ |
319
|
18 |
|
$data = $this->getVisitorData(); |
320
|
|
|
|
321
|
18 |
|
foreach (['user_id', 'client_ip', 'user_agent'] as $key) { |
322
|
18 |
|
if ($this->checkDataIsUnreliable($data, $key)) return false; |
323
|
9 |
|
} |
324
|
|
|
|
325
|
18 |
|
return true; |
326
|
|
|
} |
327
|
|
|
|
328
|
|
|
/** |
329
|
|
|
* Check the data is unreliable. |
330
|
|
|
* |
331
|
|
|
* @param array|null $data |
332
|
|
|
* @param string $key |
333
|
|
|
* |
334
|
|
|
* @return bool |
335
|
|
|
*/ |
336
|
18 |
|
private function checkDataIsUnreliable($data, $key) |
337
|
|
|
{ |
338
|
18 |
|
return isset($data[$key]) && ($data[$key] !== $this->visitorInfo[$key]); |
339
|
|
|
} |
340
|
|
|
|
341
|
|
|
/** |
342
|
|
|
* Ensure that the visitor data is complete. |
343
|
|
|
*/ |
344
|
|
|
private function ensureVisitorDataIsComplete() |
345
|
|
|
{ |
346
|
|
|
$visitorData = $this->getVisitorData(); |
347
|
|
|
$completed = true; |
348
|
|
|
|
349
|
|
|
foreach ($this->visitorInfo as $key => $value) { |
350
|
|
|
if ($visitorData[$key] !== $value) { |
351
|
|
|
/** @var \Arcanedev\LaravelTracker\Models\Visitor $model */ |
352
|
|
|
if ( ! isset($model)) |
353
|
|
|
$model = $this->getModel()->find($this->getVisitorId()); |
354
|
|
|
|
355
|
|
|
$model->setAttribute($key, $value); |
356
|
|
|
$model->save(); |
357
|
|
|
|
358
|
|
|
$completed = false; |
359
|
|
|
} |
360
|
|
|
} |
361
|
|
|
|
362
|
|
|
if ( ! $completed) |
363
|
|
|
$this->putSessionData($this->visitorInfo); |
364
|
|
|
} |
365
|
|
|
} |
366
|
|
|
|
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.