Completed
Push — master ( c4d057...a4831f )
by ARCANEDEV
07:52
created

Tracker::track()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 8
Code Lines 4

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 6
CRAP Score 2

Importance

Changes 0
Metric Value
dl 0
loc 8
rs 9.4285
c 0
b 0
f 0
ccs 6
cts 6
cp 1
cc 2
eloc 4
nc 2
nop 1
crap 2
1
<?php namespace Arcanedev\LaravelTracker;
2
3
use Arcanedev\LaravelTracker\Contracts\Detectors\CrawlerDetector;
4
use Arcanedev\LaravelTracker\Contracts\Detectors\DeviceDetector;
5
use Arcanedev\LaravelTracker\Contracts\Detectors\GeoIpDetector;
6
use Arcanedev\LaravelTracker\Contracts\Detectors\LanguageDetector;
7
use Arcanedev\LaravelTracker\Contracts\Detectors\UserDetector;
8
use Arcanedev\LaravelTracker\Contracts\Parsers\UserAgentParser;
9
use Arcanedev\LaravelTracker\Contracts\Tracker as TrackerContract;
10
use Illuminate\Contracts\Foundation\Application;
11
use Illuminate\Http\Request;
12
use Illuminate\Support\Arr;
13
14
/**
15
 * Class     Tracker
16
 *
17
 * @package  Arcanedev\LaravelTracker
18
 * @author   ARCANEDEV <[email protected]>
19
 */
20
class Tracker implements TrackerContract
21
{
22
    /* ------------------------------------------------------------------------------------------------
23
     |  Properties
24
     | ------------------------------------------------------------------------------------------------
25
     */
26
    /**
27
     * The application container.
28
     *
29
     * @var \Illuminate\Contracts\Foundation\Application
30
     */
31
    protected $app;
32
33
    /**
34
     * The request instance.
35
     *
36
     * @var \Illuminate\Http\Request
37
     */
38
    private $request;
39
40
    /**
41
     * @var bool
42
     */
43
    protected $enabled = false;
44
45
    /* ------------------------------------------------------------------------------------------------
46
     |  Constructor
47
     | ------------------------------------------------------------------------------------------------
48
     */
49
    /**
50
     * Tracker constructor.
51
     *
52
     * @param  \Illuminate\Contracts\Foundation\Application  $app
53
     */
54 24
    public function __construct(Application $app)
55
    {
56 24
        $this->app     = $app;
57 24
        $this->enabled = $this->getConfig('enabled', $this->enabled);
58 24
    }
59
60
    /* ------------------------------------------------------------------------------------------------
61
     |  Getters & Setters
62
     | ------------------------------------------------------------------------------------------------
63
     */
64
    /**
65
     * Get the config repository.
66
     *
67
     * @return \Illuminate\Contracts\Config\Repository
68
     */
69 24
    private function config()
70
    {
71 24
        return $this->app['config'];
72
    }
73
74
    /**
75
     * Get the tracker config.
76
     *
77
     * @param  string      $key
78
     * @param  mixed|null  $default
79
     *
80
     * @return mixed
81
     */
82 24
    private function getConfig($key, $default = null)
83
    {
84 24
        return $this->config()->get("laravel-tracker.$key", $default);
85
    }
86
87
    /**
88
     * Set the request.
89
     *
90
     * @param  \Illuminate\Http\Request  $request
91
     *
92
     * @return self
93
     */
94 6
    private function setRequest(Request $request)
95
    {
96 6
        $this->request = $request;
97
98 6
        return $this;
99
    }
100
101
    /* ------------------------------------------------------------------------------------------------
102
     |  Main Functions
103
     | ------------------------------------------------------------------------------------------------
104
     */
105
    /**
106
     * Start the tracking.
107
     *
108
     * @param  \Illuminate\Http\Request $request
109
     */
110 6
    public function track(Request $request)
111
    {
112 6
        if ($this->isEnabled()) {
113 6
            $this->setRequest($request);
114
115 6
            $activity = $this->getSessionActivityData();
0 ignored issues
show
Unused Code introduced by
$activity is not used, you could remove the assignment.

This check looks for variable assignements that are either overwritten by other assignments or where the variable is not used subsequently.

$myVar = 'Value';
$higher = false;

if (rand(1, 6) > 3) {
    $higher = true;
} else {
    $higher = false;
}

Both the $myVar assignment in line 1 and the $higher assignment in line 2 are dead. The first because $myVar is never used and the second because $higher is always overwritten for every possible time line.

Loading history...
116 3
        }
117 6
    }
118
119
    /**
120
     * Enable the tracker.
121
     */
122 6
    public function enable()
123
    {
124 6
        if ( ! $this->isEnabled())
125 6
            $this->enabled = true;
126 6
    }
127
128
    /**
129
     * Disable the tracker.
130
     */
131 6
    public function disable()
132
    {
133 6
        if ($this->isEnabled())
134 6
            $this->enabled = false;
135 6
    }
136
137
    /* ------------------------------------------------------------------------------------------------
138
     |  Check Functions
139
     | ------------------------------------------------------------------------------------------------
140
     */
141
    /**
142
     * Check if the tracker is enabled.
143
     *
144
     * @return bool
145
     */
146 24
    public function isEnabled()
147
    {
148 24
        return $this->enabled;
149
    }
150
151
    /* ------------------------------------------------------------------------------------------------
152
     |  Other Functions
153
     | ------------------------------------------------------------------------------------------------
154
     */
155
    /**
156
     * Get the session log data.
157
     *
158
     * @return array
159
     */
160 6
    protected function getSessionActivityData()
161
    {
162
        return [
163 6
            'session_id'  => $this->getSessionId(true),
164 6
            'method'      => $this->request->method(),
165 6
            'path_id'     => $this->getPathId(),
166 6
            'query_id'    => $this->getQueryId(),
167 6
            'referrer_id' => $this->getRefererId(),
168 6
            'is_ajax'     => $this->request->ajax(),
169 6
            'is_secure'   => $this->request->isSecure(),
170 6
            'is_json'     => $this->request->isJson(),
171 6
            'wants_json'  => $this->request->wantsJson(),
172 3
        ];
173
    }
174
175 6
    protected function getSessionId($updateLastActivity = false)
0 ignored issues
show
Unused Code introduced by
The parameter $updateLastActivity is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
176
    {
177 6
        return 0;
178
    }
179
180
    /**
181
     * @return array
182
     */
183
    protected function makeSessionData()
184
    {
185
        return [
186
            'user_id'      => $this->getUserId(),
187
            'device_id'    => $this->getDeviceId(),
188
            'client_ip'    => $this->request->getClientIp(),
189
            'geoip_id'     => $this->getGeoIpId(),
190
            'agent_id'     => $this->getAgentId(),
191
            'referrer_id'  => $this->getRefererId(),
192
            'cookie_id'    => $this->getCookieId(),
193
            'language_id'  => $this->getLanguageId(),
194
            'is_robot'     => $this->isRobot(),
195
196
            // The key user_agent is not present in the sessions table, but it's internally used to check
197
            // if the user agent changed during a session.
198
            'user_agent'   => $this->getCurrentUserAgent(),
199
        ];
200
    }
201
202 6
    private function getPathId()
203
    {
204 6
        return 0;
205
    }
206
207 6
    private function getQueryId()
208
    {
209 6
        return 0;
210
    }
211
212
    /**
213
     * Get the user id.
214
     *
215
     * @return int|null
216
     */
217
    private function getUserId()
218
    {
219
        return $this->getConfig('tracking.users', false)
220
            ? $this->app[UserDetector::class]->getCurrentUserId()
221
            : null;
222
    }
223
224
    /**
225
     * Get the device id.
226
     *
227
     * @return int|null
228
     */
229
    private function getDeviceId()
230
    {
231
        if ($this->getConfig('tracking.devices', false)) {
232
233
            $data  = $this->getCurrentDeviceProperties();
234
            $model = Models\Device::firstOrCreate($data, $data);
235
236
            return $model->id;
237
        }
238
239
        return null;
240
    }
241
242
    /**
243
     * Get the current device properties.
244
     *
245
     * @return array
246
     */
247
    public function getCurrentDeviceProperties()
248
    {
249
        if ($properties = $this->app[DeviceDetector::class]->detect()) {
250
            $ua = $this->getUserAgentParser();
251
252
            $properties['platform']         = $ua->getOperatingSystemFamily();
253
            $properties['platform_version'] = $ua->getOperatingSystemVersion();
254
        }
255
256
        return $properties;
257
    }
258
259
    private function getGeoIpId()
260
    {
261
        if ($this->getConfig('tracking.geoip', false)) {
262
            $data = $this->app[GeoIpDetector::class]->search(
263
                $this->request->getClientIp()
264
            );
265
266
            if ($data) {
267
                $model = Models\GeoIp::firstOrCreate(Arr::only($data, ['latitude', 'longitude']), $data);
268
269
                return $model->id;
270
            }
271
        }
272
273
        return null;
274
    }
275
276
    private function getAgentId()
277
    {
278
        if ($this->getConfig('tracking.user-agents', false)) {
279
            $data  = $this->getCurrentUserAgentData();
280
            $model = Models\Agent::firstOrCreate($data, $data);
281
282
            return $model->id;
283
        }
284
285
        return null;
286
    }
287
288 6
    private function getRefererId()
289
    {
290 6
        if ($this->getConfig('tracking.referers', false)) {
291
            /** @var  \Arcanedev\LaravelTracker\Contracts\Parsers\RefererParser  $parser */
292 6
            $parser = $this->app[\Arcanedev\LaravelTracker\Contracts\Parsers\RefererParser::class];
293 6
            $parsed = $parser->parseUrl($this->request->headers->get('referer'));
294
295 6
            if ($parsed) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $parsed 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...
296
                $domainId   = $this->getDomainId($parsed['domain']);
297
                $attributes = [
298
                    'url'               => $parsed['url'],
299
                    'host'              => $parsed['host'],
300
                    'domain_id'         => $domainId,
301
                    'medium'            => null,
302
                    'source'            => null,
303
                    'search_terms_hash' => null,
304
                ];
305
306
                $parsed = $parser->parse($parsed['url'], $this->request->url());
307
308
                if ($parsed->isKnown()) {
309
                    $attributes['medium']            = $parsed->getMedium();
310
                    $attributes['source']            = $parsed->getSource();
311
                    $attributes['search_terms_hash'] = sha1($parsed->getSearchTerm());
312
                }
313
314
                $referer = Models\Referer::firstOrCreate(
315
                    Arr::only($attributes, ['url', 'search_terms_hash']),
316
                    $attributes
317
                );
318
319
                if ($parsed->isKnown()) {
320
                    $this->storeSearchTerms($referer->id, $parsed->getSearchTerm());
321
                }
322
323
                return $referer->id;
324
            }
325 3
        }
326
327 6
        return null;
328
    }
329
330
    /**
331
     * Get the domain id.
332
     *
333
     * @param  string  $name
334
     *
335
     * @return int
336
     */
337
    private function getDomainId($name)
338
    {
339
        $data = compact('name');
340
341
        return Models\Domain::firstOrCreate($data, $data)->id;
342
    }
343
344
    private function storeSearchTerms($refererId, $searchTerms)
345
    {
346
        foreach (explode(' ', $searchTerms) as $term) {
347
            $attributes = [
348
                'referer_id'  => $refererId,
349
                'search_term' => $term,
350
            ];
351
            Models\RefererSearchTerm::firstOrCreate($attributes, $attributes);
352
        }
353
    }
354
355
    private function getCookieId()
356
    {
357
        if ($this->getConfig('tracking.cookies', false)) {
358
            if ( ! $cookie = $this->request->cookie($this->getConfig('cookie.name'))) {
359
                $cookie = (string) \Ramsey\Uuid\Uuid::uuid4();
360
361
                $this->app['cookie']->queue($this->getConfig('cookie.name'), $cookie, 0);
362
            }
363
364
            return Models\Cookie::firstOrCreate(['uuid' => $cookie])->id;
365
        }
366
367
        return null;
368
    }
369
370
    private function getLanguageId()
371
    {
372
        if ($this->getConfig('tracking.languages', false)) {
373
            $languages = $this->app[LanguageDetector::class]->detect();
374
375
            return Models\Language::firstOrCreate($languages)->id;
376
        }
377
378
        return null;
379
    }
380
381
    /**
382
     * @return bool
383
     */
384
    protected function isRobot()
385
    {
386
        /** @var  \Arcanedev\LaravelTracker\Contracts\Detectors\CrawlerDetector  $crawler */
387
        $crawler = $this->app[CrawlerDetector::class];
388
389
        return $crawler->isRobot();
390
    }
391
392
    /**
393
     * Get the user agent parser.
394
     *
395
     * @return \Arcanedev\LaravelTracker\Contracts\Parsers\UserAgentParser
396
     */
397
    public function getUserAgentParser()
398
    {
399
        return $this->app[UserAgentParser::class];
400
    }
401
402
    private function getCurrentUserAgent()
403
    {
404
        return $this->getUserAgentParser()->getOriginalUserAgent();
405
    }
406
407
    private function getCurrentUserAgentData()
408
    {
409
        return [
410
            'name'            => $this->getUserAgentParser()->getOriginalUserAgent() ?: 'Other',
411
            'browser'         => $this->getUserAgentParser()->getBrowser(),
412
            'browser_version' => $this->getUserAgentParser()->getUserAgentVersion(),
413
        ];
414
    }
415
}
416