Passed
Push — develop ( fa8a11...2e586e )
by Andrew
04:35
created

IA::getAnalyticsObj()   D

Complexity

Conditions 13
Paths 386

Size

Total Lines 73
Code Lines 44

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 73
rs 4.0766
c 0
b 0
f 0
cc 13
eloc 44
nc 386
nop 0

How to fix   Long Method    Complexity   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

1
<?php
2
/**
3
 * Instant Analytics plugin for Craft CMS 3.x
4
 *
5
 * Instant Analytics brings full Google Analytics support to your Twig templates
6
 *
7
 * @link      https://nystudio107.com
8
 * @copyright Copyright (c) 2017 nystudio107
9
 */
10
11
namespace nystudio107\instantanalytics\services;
12
13
use nystudio107\instantanalytics\InstantAnalytics;
14
use nystudio107\instantanalytics\helpers\IAnalytics;
15
use nystudio107\instantanalytics\models\Settings;
16
17
use Jaybizzle\CrawlerDetect\CrawlerDetect;
18
19
use Craft;
20
use craft\base\Component;
21
use craft\elements\User as UserElement;
22
use craft\helpers\UrlHelper;
23
24
use yii\base\Exception;
25
26
/**
27
 * IA Service
28
 *
29
 * All of your plugin’s business logic should go in services, including saving
30
 * data, retrieving data, etc. They provide APIs that your controllers,
31
 * template variables, and other plugins can interact with.
32
 *
33
 * https://craftcms.com/docs/plugins/services
34
 *
35
 * @author    nystudio107
36
 * @package   InstantAnalytics
37
 * @since     1.0.0
38
 */
39
class IA extends Component
40
{
41
    // Public Methods
42
    // =========================================================================
43
44
    protected $cachedAnalytics = null;
45
46
    /**
47
     * Get the global variables for our Twig context
48
     *
49
     * @param $title
50
     *
51
     * @return IAnalytics
52
     */
53
    public function getGlobals($title)
54
    {
55
        if ($this->cachedAnalytics) {
56
            $analytics = $this->cachedAnalytics;
57
        } else {
58
            $analytics = $this->pageViewAnalytics("", $title);
59
            $this->cachedAnalytics = $analytics;
60
        }
61
62
        return $analytics;
63
    }
64
65
    /**
66
     * Get a PageView analytics object
67
     *
68
     * @param string $url
69
     * @param string $title
70
     *
71
     * @return null|IAnalytics
72
     */
73
    public function pageViewAnalytics($url = "", $title = "")
74
    {
75
        $result = null;
76
        $analytics = $this->analytics();
77
        if ($analytics) {
78
            $url = $this->documentPathFromUrl($url);
79
            // Prepare the Analytics object, and send the pageview
80
            $analytics->setDocumentPath($url)
81
                ->setDocumentTitle($title);
82
            $result = $analytics;
83
            Craft::info(
84
                "Created sendPageView for `" . $url . "` - `" . $title . "`",
85
                __METHOD__
86
            );
87
        }
88
89
        return $result;
90
    }
91
92
    /**
93
     * Get an Event analytics object
94
     *
95
     * @param string $eventCategory
96
     * @param string $eventAction
97
     * @param string $eventLabel
98
     * @param int    $eventValue
99
     *
100
     * @return null|IAnalytics
101
     */
102
    public function eventAnalytics($eventCategory = "", $eventAction = "", $eventLabel = "", $eventValue = 0)
103
    {
104
        $result = null;
105
        $analytics = $this->analytics();
106
        if ($analytics) {
107
            $url = $this->documentPathFromUrl();
108
            $analytics->setDocumentPath($url)
109
                ->setEventCategory($eventCategory)
110
                ->setEventAction($eventAction)
111
                ->setEventLabel($eventLabel)
112
                ->setEventValue(intval($eventValue));
113
            $result = $analytics;
114
            Craft::info(
115
                "Created sendEvent for `" . $eventCategory . "` - `" . $eventAction . "` - `" . $eventLabel . "` - `" . $eventValue . "`",
116
                __METHOD__
117
            );
118
        }
119
120
        return $result;
121
    }
122
123
    /**
124
     * getAnalyticsObject() return an analytics object
125
     *
126
     * @return IAnalytics object
127
     */
128
    public function analytics()
129
    {
130
        $analytics = $this->getAnalyticsObj();
131
        Craft::info(
132
            "Created generic analytics object",
133
            __METHOD__
134
        );
135
136
        return $analytics;
137
    }
138
139
    /**
140
     * Get a PageView tracking URL
141
     *
142
     * @param $url
143
     * @param $title
144
     *
145
     * @return string
146
     * @throws \yii\base\Exception
147
     */
148
    public function pageViewTrackingUrl($url, $title)
149
    {
150
        $urlParams = [
151
            'url'   => $url,
152
            'title' => $title,
153
        ];
154
        $path = parse_url($url, PHP_URL_PATH);
155
        $pathFragments = explode('/', rtrim($path, '/'));
156
        $fileName = end($pathFragments);
157
        $trackingUrl = UrlHelper::siteUrl('instantanalytics/pageViewTrack/' . $fileName, $urlParams);
158
        Craft::info(
159
            "Created pageViewTrackingUrl for " . $trackingUrl,
160
            __METHOD__
161
        );
162
163
        return $trackingUrl;
164
    }
165
166
    /**
167
     * Get an Event tracking URL
168
     *
169
     * @param        $url
170
     * @param string $eventCategory
171
     * @param string $eventAction
172
     * @param string $eventLabel
173
     * @param int    $eventValue
174
     *
175
     * @return string
176
     * @throws \yii\base\Exception
177
     */
178
    public function eventTrackingUrl($url, $eventCategory = "", $eventAction = "", $eventLabel = "", $eventValue = 0)
179
    {
180
        $urlParams = [
181
            'url'           => $url,
182
            'eventCategory' => $eventCategory,
183
            'eventAction'   => $eventAction,
184
            'eventLabel'    => $eventLabel,
185
            'eventValue'    => $eventValue,
186
        ];
187
        $fileName = pathinfo(parse_url($url, PHP_URL_PATH), PATHINFO_BASENAME);
188
        $trackingUrl = UrlHelper::siteUrl('instantanalytics/eventTrack/' . $fileName, $urlParams);
189
        Craft::info(
190
            "Created eventTrackingUrl for " . $trackingUrl,
191
            __METHOD__
192
        );
193
194
        return $trackingUrl;
195
    }
196
197
    /**
198
     * _shouldSendAnalytics determines whether we should be sending Google
199
     * Analytics data
200
     *
201
     * @return bool
202
     */
203
    public function shouldSendAnalytics()
204
    {
205
        $result = true;
206
207
        /** @var Settings $settings */
208
        $settings = InstantAnalytics::$plugin->getSettings();
209
        $request = Craft::$app->getRequest();
210
        $requestIp = $request->getUserIP();
211
212
        if (!$settings->sendAnalyticsData) {
213
            if ($settings->logExcludedAnalytics) {
214
                Craft::info(
215
                    "Analytics excluded for: " . $requestIp . " due to: `sendAnalyticsData`",
216
                    __METHOD__
217
                );
218
            }
219
220
            return false;
221
        }
222
223
        if (!$settings->sendAnalyticsInDevMode && Craft::$app->getConfig()->getGeneral()->devMode) {
224
            if ($settings->logExcludedAnalytics) {
225
                Craft::info(
226
                    "Analytics excluded for: " . $requestIp . " due to: `sendAnalyticsInDevMode`",
227
                    __METHOD__
228
                );
229
            }
230
231
            return false;
232
        }
233
234
        if ($request->getIsConsoleRequest()) {
235
            if ($settings->logExcludedAnalytics) {
236
                Craft::info(
237
                    "Analytics excluded for: " . $requestIp . " due to: `craft()->isConsole()`",
238
                    __METHOD__
239
                );
240
            }
241
242
            return false;
243
        }
244
245
        if ($request->getIsCpRequest()) {
246
            if ($settings->logExcludedAnalytics) {
247
                Craft::info(
248
                    "Analytics excluded for: " . $requestIp . " due to: `craft()->request->isCpRequest()`",
249
                    __METHOD__
250
                );
251
            }
252
253
            return false;
254
        }
255
256
        if ($request->getIsLivePreview()) {
257
            if ($settings->logExcludedAnalytics) {
258
                Craft::info(
259
                    "Analytics excluded for: " . $requestIp . " due to: `craft()->request->isLivePreview()`",
260
                    __METHOD__
261
                );
262
            }
263
264
            return false;
265
        }
266
267
        // Check the $_SERVER[] super-global exclusions
268
        if (isset($settings->serverExcludes) && is_array($settings->serverExcludes)) {
269
            foreach ($settings->serverExcludes as $match => $matchArray) {
270
                if (isset($_SERVER[$match])) {
271
                    foreach ($matchArray as $matchItem) {
272
                        if (preg_match($matchItem, $_SERVER[$match])) {
273
                            if ($settings->logExcludedAnalytics) {
274
                                Craft::info(
275
                                    "Analytics excluded for: " . $requestIp . " due to: `serverExcludes`",
276
                                    __METHOD__
277
                                );
278
                            }
279
280
                            return false;
281
                        }
282
                    }
283
                }
284
            }
285
        }
286
287
        // Filter out bot/spam requests via UserAgent
288
        if ($settings->filterBotUserAgents) {
289
            $crawlerDetect = new CrawlerDetect;
290
            // Check the user agent of the current 'visitor'
291
            if ($crawlerDetect->isCrawler()) {
292
                if ($settings->logExcludedAnalytics) {
293
                    Craft::info(
294
                        "Analytics excluded for: " . $requestIp . " due to: `filterBotUserAgents`",
295
                        __METHOD__
296
                    );
297
                }
298
299
                return false;
300
            }
301
        }
302
303
        // Filter by user group
304
        $userService = Craft::$app->getUser();
305
        /** @var UserElement $user */
306
        $user = $userService->getIdentity();
307
        if ($user) {
308
            if ($settings->adminExclude && $user->admin) {
309
                if ($settings->logExcludedAnalytics) {
310
                    Craft::info(
311
                        "Analytics excluded for: " . $requestIp . " due to: `adminExclude`",
312
                        __METHOD__
313
                    );
314
                }
315
316
                return false;
317
            }
318
319
            if (isset($settings->groupExcludes) && is_array($settings->groupExcludes)) {
320
                foreach ($settings->groupExcludes as $matchItem) {
321
                    if ($user->isInGroup($matchItem)) {
322
                        if ($settings->logExcludedAnalytics) {
323
                            Craft::info(
324
                                "Analytics excluded for: " . $requestIp . " due to: `groupExcludes`",
325
                                __METHOD__
326
                            );
327
                        }
328
329
                        return false;
330
                    }
331
                }
332
            }
333
        }
334
335
        return $result;
336
    }
337
338
    /**
339
     * Return a sanitized documentPath from a URL
340
     *
341
     * @param $url
342
     *
343
     * @return bool|string
344
     */
345
    protected function documentPathFromUrl($url = '')
346
    {
347
        if ($url == "") {
348
            $url = Craft::$app->getRequest()->getFullPath();
349
        }
350
351
        // We want to send just a path to GA for page views
352
        if (UrlHelper::isAbsoluteUrl($url)) {
353
            $urlParts = parse_url($url);
354
            if (isset($urlParts['path'])) {
355
                $url = $urlParts['path'];
356
            } else {
357
                $url = "/";
358
            }
359
            if (isset($urlParts['query'])) {
360
                $url = $url."?".$urlParts['query'];
361
            }
362
        }
363
364
        // We don't want to send protocol-relative URLs either
365
        if (UrlHelper::isProtocolRelativeUrl($url)) {
366
            $url = substr($url, 1);
367
        }
368
369
        // Strip the query string if that's the global config setting
370
        $settings = InstantAnalytics::$plugin->getSettings();
371
        if (isset($settings) && isset($settings->stripQueryString) && $settings->stripQueryString) {
372
            $url = UrlHelper::stripQueryString($url);
373
        }
374
375
        // We always want the path to be / rather than empty
376
        if ($url == "") {
377
            $url = "/";
378
        }
379
380
        return $url;
381
    }
382
383
    /**
384
     * Get the Google Analytics object, primed with the default values
385
     *
386
     * @return IAnalytics object
387
     */
388
    private function getAnalyticsObj()
389
    {
390
        $analytics = null;
391
        $settings = InstantAnalytics::$plugin->getSettings();
392
        $request = Craft::$app->getRequest();
393
        if (isset($settings) && !empty($settings->googleAnalyticsTracking)) {
394
            $analytics = new IAnalytics();
395
            if ($analytics) {
396
                $hostName = $request->getServerName();
397
                if (empty($hostName)) {
398
                    try {
399
                        $hostName = parse_url(UrlHelper::siteUrl(), PHP_URL_HOST);
400
                    } catch (Exception $e) {
401
                        Craft::error(
402
                            $e->getMessage(),
403
                            __METHOD__
404
                        );
405
                    }
406
                }
407
                $userAgent = $request->getUserAgent();
408
                if (empty($userAgent)) {
409
                    $userAgent = "User-Agent:Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US; rv:1.8.1.13) Gecko/20080311 Firefox/2.0.0.13\r\n";
410
                }
411
                $referrer = $request->getReferrer();
412
                if (empty($referrer)) {
413
                    $referrer = "";
414
                }
415
                $analytics->setProtocolVersion('1')
416
                    ->setTrackingId($settings->googleAnalyticsTracking)
417
                    ->setIpOverride($request->getUserIP())
418
                    ->setUserAgentOverride($userAgent)
419
                    ->setDocumentHostName($hostName)
420
                    ->setDocumentReferrer($referrer)
421
                    ->setAsyncRequest(false)
422
                    ->setClientId($this->gaParseCookie());
423
424
                // Set the gclid
425
                $gclid = $this->getGclid();
426
                if ($gclid) {
427
                    $analytics->setGoogleAdwordsId($gclid);
428
                }
429
430
                // Handle UTM parameters
431
                $utm_source = $request->getParam('utm_source');
432
                if (!empty($utm_source)) {
433
                    $analytics->setCampaignSource($utm_source);
434
                }
435
                $utm_medium = $request->getParam('utm_medium');
436
                if (!empty($utm_medium)) {
437
                    $analytics->setCampaignMedium($utm_medium);
438
                }
439
                $utm_campaign = $request->getParam('utm_campaign');
440
                if (!empty($utm_campaign)) {
441
                    $analytics->setCampaignName($utm_campaign);
442
                }
443
                $utm_content = $request->getParam('utm_content');
444
                if (!empty($utm_content)) {
445
                    $analytics->setCampaignContent($utm_content);
446
                }
447
448
                // If SEOmatic is installed, set the affiliation as well
449
                // TODO: handle Seomatic
450
                /*
451
                $seomatic = craft()->plugins->getPlugin('Seomatic');
452
                if ($seomatic && $seomatic->isInstalled && $seomatic->isEnabled) {
453
                    $seomaticSettings = craft()->seomatic->getSettings(craft()->language);
454
                    $analytics->setAffiliation($seomaticSettings['siteSeoName']);
455
                }
456
                */
457
            }
458
        }
459
460
        return $analytics;
461
    } /* -- _getAnalyticsObj */
462
463
    /**
464
     * _getGclid get the `gclid` and sets the 'gclid' cookie
465
     */
466
    /**
467
     * _getGclid get the `gclid` and sets the 'gclid' cookie
468
     *
469
     * @return string
470
     */
471
    private function getGclid()
472
    {
473
        $gclid = "";
474
        if (isset($_GET['gclid'])) {
475
            $gclid = $_GET['gclid'];
476
            if (!empty($gclid)) {
477
                setcookie("gclid", $gclid, time() + (10 * 365 * 24 * 60 * 60), "/");
478
            }
479
        }
480
481
        return $gclid;
482
    }
483
484
    /**
485
     * gaParseCookie handles the parsing of the _ga cookie or setting it to a
486
     * unique identifier
487
     *
488
     * @return string the cid
489
     */
490
    private function gaParseCookie()
491
    {
492
        $cid = '';
493
        if (isset($_COOKIE['_ga'])) {
494
            $parts = preg_split('[\.]', $_COOKIE["_ga"], 4);
495
            if ($parts !== false) {
496
                $cid = implode('.', array_slice($parts, 2));
497
            }
498
        } else {
499
            if (isset($_COOKIE['_ia']) && $_COOKIE['_ia'] != '') {
500
                $cid = $_COOKIE['_ia'];
501
            } else {
502
                $cid = $this->gaGenUUID();
503
            }
504
        }
505
        setcookie('_ia', $cid, time() + 60 * 60 * 24 * 730, "/"); // Two years
506
507
        return $cid;
508
    }
509
510
    /**
511
     * gaGenUUID Generate UUID v4 function - needed to generate a CID when one
512
     * isn't available
513
     *
514
     * @return string The generated UUID
515
     */
516
    private function gaGenUUID()
517
    {
518
        return sprintf(
519
            '%04x%04x-%04x-%04x-%04x-%04x%04x%04x',
520
            // 32 bits for "time_low"
521
            mt_rand(0, 0xffff),
522
            mt_rand(0, 0xffff),
523
            // 16 bits for "time_mid"
524
            mt_rand(0, 0xffff),
525
            // 16 bits for "time_hi_and_version",
526
            // four most significant bits holds version number 4
527
            mt_rand(0, 0x0fff) | 0x4000,
528
            // 16 bits, 8 bits for "clk_seq_hi_res",
529
            // 8 bits for "clk_seq_low",
530
            // two most significant bits holds zero and one for variant DCE1.1
531
            mt_rand(0, 0x3fff) | 0x8000,
532
            // 48 bits for "node"
533
            mt_rand(0, 0xffff),
534
            mt_rand(0, 0xffff),
535
            mt_rand(0, 0xffff)
536
        );
537
    }
538
}
539