Analytics::shouldSendAnalytics()   D
last analyzed

Complexity

Conditions 23
Paths 28

Size

Total Lines 93
Code Lines 51

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 23
eloc 51
c 1
b 0
f 0
nc 28
nop 0
dl 0
loc 93
rs 4.1666

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
4
 *
5
 * @author    nystudio107
0 ignored issues
show
Coding Style introduced by
The tag in position 1 should be the @package tag
Loading history...
Coding Style introduced by
Content of the @author tag must be in the form "Display Name <[email protected]>"
Loading history...
6
 * @copyright Copyright (c) 2017 nystudio107
0 ignored issues
show
Coding Style introduced by
The tag in position 2 should be the @author tag
Loading history...
Coding Style introduced by
@copyright tag must contain a year and the name of the copyright holder
Loading history...
7
 * @link      http://nystudio107.com
0 ignored issues
show
Coding Style introduced by
The tag in position 3 should be the @copyright tag
Loading history...
8
 * @package   InstantAnalytics
0 ignored issues
show
Coding Style introduced by
The tag in position 4 should be the @link tag
Loading history...
9
 * @since     1.0.0
10
 */
0 ignored issues
show
Coding Style introduced by
PHP version not specified
Loading history...
Coding Style introduced by
Missing @category tag in file comment
Loading history...
Coding Style introduced by
Missing @license tag in file comment
Loading history...
11
12
namespace nystudio107\instantanalyticsGa4\helpers;
13
14
use Craft;
15
use craft\elements\User as UserElement;
16
use craft\helpers\App;
17
use craft\helpers\StringHelper;
18
use craft\helpers\UrlHelper;
19
use Jaybizzle\CrawlerDetect\CrawlerDetect;
20
use nystudio107\instantanalyticsGa4\InstantAnalytics;
21
use nystudio107\seomatic\Seomatic;
22
use yii\base\Exception;
23
24
/**
0 ignored issues
show
Coding Style introduced by
Missing short description in doc comment
Loading history...
25
 * @author    nystudio107
0 ignored issues
show
Coding Style introduced by
The tag in position 1 should be the @package tag
Loading history...
Coding Style introduced by
Content of the @author tag must be in the form "Display Name <[email protected]>"
Loading history...
Coding Style introduced by
Tag value for @author tag indented incorrectly; expected 2 spaces but found 4
Loading history...
26
 * @package   InstantAnalytics
0 ignored issues
show
Coding Style introduced by
Tag value for @package tag indented incorrectly; expected 1 spaces but found 3
Loading history...
27
 * @since     5.0.0
0 ignored issues
show
Coding Style introduced by
The tag in position 3 should be the @author tag
Loading history...
Coding Style introduced by
Tag value for @since tag indented incorrectly; expected 3 spaces but found 5
Loading history...
28
 */
0 ignored issues
show
Coding Style introduced by
Missing @category tag in class comment
Loading history...
Coding Style introduced by
Missing @license tag in class comment
Loading history...
Coding Style introduced by
Missing @link tag in class comment
Loading history...
29
class Analytics
30
{
31
    /**
32
     * If SEOmatic is installed, set the page title from it
33
     */
0 ignored issues
show
Coding Style introduced by
Missing @return tag in function comment
Loading history...
34
    public static function getTitleFromSeomatic(): ?string
35
    {
36
        if (!InstantAnalytics::$seomaticPlugin) {
37
            return null;
38
        }
39
        if (!Seomatic::$settings->renderEnabled) {
40
            return null;
41
        }
42
        $titleTag = Seomatic::$plugin->title->get('title');
43
44
        if ($titleTag === null) {
45
            return null;
46
        }
47
48
        $titleArray = $titleTag->renderAttributes();
49
50
        if (empty($titleArray['title'])) {
51
            return null;
52
        }
53
54
        return $titleArray['title'];
55
    }
56
57
    /**
58
     * Return a sanitized documentPath from a URL
59
     *
60
     * @param string $url
0 ignored issues
show
Coding Style introduced by
Missing parameter comment
Loading history...
61
     *
62
     * @return string
63
     */
64
    public static function getDocumentPathFromUrl(string $url = ''): string
65
    {
66
        if ($url === '') {
67
            $url = Craft::$app->getRequest()->getFullPath();
68
        }
69
70
        // We want to send just a path to GA for page views
71
        if (UrlHelper::isAbsoluteUrl($url)) {
72
            $urlParts = parse_url($url);
73
            $url = $urlParts['path'] ?? '/';
74
            if (isset($urlParts['query'])) {
75
                $url .= '?' . $urlParts['query'];
76
            }
77
        }
78
79
        // We don't want to send protocol-relative URLs either
80
        if (UrlHelper::isProtocolRelativeUrl($url)) {
81
            $url = substr($url, 1);
82
        }
83
84
        // Strip the query string if that's the global config setting
85
        if (InstantAnalytics::$settings) {
86
            if (InstantAnalytics::$settings->stripQueryString !== null
87
                && InstantAnalytics::$settings->stripQueryString) {
0 ignored issues
show
Coding Style introduced by
Closing parenthesis of a multi-line IF statement must be on a new line
Loading history...
88
                $url = UrlHelper::stripQueryString($url);
89
            }
90
        }
91
92
        // We always want the path to be / rather than empty
93
        if ($url === '') {
94
            $url = '/';
95
        }
96
97
        return $url;
98
    }
99
100
    /**
101
     * Get a PageView tracking URL
102
     *
103
     * @param $url
0 ignored issues
show
Coding Style introduced by
Missing parameter comment
Loading history...
104
     * @param $title
0 ignored issues
show
Coding Style introduced by
Missing parameter comment
Loading history...
105
     *
106
     * @return string
107
     * @throws Exception
108
     */
109
    public static function getPageViewTrackingUrl($url, $title): string
110
    {
111
        $urlParams = compact('url', 'title');
112
113
        $path = parse_url($url, PHP_URL_PATH);
114
        $pathFragments = explode('/', rtrim($path, '/'));
115
        $fileName = end($pathFragments);
116
        $trackingUrl = UrlHelper::siteUrl('instantanalytics/pageViewTrack/' . $fileName, $urlParams);
117
118
        InstantAnalytics::$plugin->logAnalyticsEvent(
0 ignored issues
show
Bug introduced by
The method logAnalyticsEvent() does not exist on null. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

118
        InstantAnalytics::$plugin->/** @scrutinizer ignore-call */ 
119
                                   logAnalyticsEvent(

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
119
            'Created pageViewTrackingUrl for: {trackingUrl}',
120
            [
121
                'trackingUrl' => $trackingUrl,
122
            ],
123
            __METHOD__
124
        );
125
126
        return $trackingUrl;
127
    }
128
129
    /**
130
     * Get an Event tracking URL
131
     *
132
     * @param string $url
0 ignored issues
show
Coding Style introduced by
Missing parameter comment
Loading history...
Coding Style introduced by
Tag value for @param tag indented incorrectly; expected 2 spaces but found 1
Loading history...
133
     * @param string $eventName
0 ignored issues
show
Coding Style introduced by
Missing parameter comment
Loading history...
Coding Style introduced by
Tag value for @param tag indented incorrectly; expected 2 spaces but found 1
Loading history...
134
     * @param array $params
0 ignored issues
show
Coding Style introduced by
Missing parameter comment
Loading history...
Coding Style introduced by
Expected 2 spaces after parameter type; 1 found
Loading history...
Coding Style introduced by
Tag value for @param tag indented incorrectly; expected 2 spaces but found 1
Loading history...
135
     * @return string
0 ignored issues
show
Coding Style introduced by
Tag @return cannot be grouped with parameter tags in a doc comment
Loading history...
136
     * @throws Exception
0 ignored issues
show
Coding Style introduced by
Tag @throws cannot be grouped with parameter tags in a doc comment
Loading history...
137
     */
138
    public static function getEventTrackingUrl(
139
        string $url,
140
        string $eventName,
141
        array  $params = [],
142
    ): string {
143
        $urlParams = compact('url', 'eventName', 'params');
144
145
        $fileName = pathinfo(parse_url($url, PHP_URL_PATH), PATHINFO_BASENAME);
146
        $trackingUrl = UrlHelper::siteUrl('instantanalytics/eventTrack/' . $fileName, $urlParams);
0 ignored issues
show
Bug introduced by
Are you sure $fileName of type array|string can be used in concatenation? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

146
        $trackingUrl = UrlHelper::siteUrl('instantanalytics/eventTrack/' . /** @scrutinizer ignore-type */ $fileName, $urlParams);
Loading history...
147
148
        InstantAnalytics::$plugin->logAnalyticsEvent(
149
            'Created eventTrackingUrl for: {trackingUrl}',
150
            [
151
                'trackingUrl' => $trackingUrl,
152
            ],
153
            __METHOD__
154
        );
155
156
        return $trackingUrl;
157
    }
158
159
    /**
160
     * _shouldSendAnalytics determines whether we should be sending Google
161
     * Analytics data
162
     *
163
     * @return bool
164
     */
165
    public static function shouldSendAnalytics(): bool
166
    {
167
        $result = true;
168
        $request = Craft::$app->getRequest();
169
170
        $logExclusion = static function(string $setting) {
0 ignored issues
show
Coding Style introduced by
Expected 1 space after FUNCTION keyword; 0 found
Loading history...
171
            if (InstantAnalytics::$settings->logExcludedAnalytics) {
172
                $request = Craft::$app->getRequest();
173
                $requestIp = $request->getUserIP();
174
                InstantAnalytics::$plugin->logAnalyticsEvent(
175
                    'Analytics excluded for:: {requestIp} due to: `{setting}`',
176
                    compact('requestIp', 'setting'),
177
                    __METHOD__
178
                );
179
            }
180
        };
181
182
        if (!InstantAnalytics::$settings->sendAnalyticsData) {
183
            $logExclusion('sendAnalyticsData');
184
            return false;
185
        }
186
187
        if (!InstantAnalytics::$settings->sendAnalyticsInDevMode && Craft::$app->getConfig()->getGeneral()->devMode) {
188
            $logExclusion('sendAnalyticsInDevMode');
189
            return false;
190
        }
191
192
        if ($request->getIsConsoleRequest()) {
193
            $logExclusion('Craft::$app->getRequest()->getIsConsoleRequest()');
194
            return false;
195
        }
196
197
        if ($request->getIsCpRequest()) {
198
            $logExclusion('Craft::$app->getRequest()->getIsCpRequest()');
199
            return false;
200
        }
201
202
        if ($request->getIsLivePreview()) {
203
            $logExclusion('Craft::$app->getRequest()->getIsLivePreview()');
204
            return false;
205
        }
206
207
        // Check the $_SERVER[] super-global exclusions
208
        if (InstantAnalytics::$settings->serverExcludes !== null
209
            && !empty(InstantAnalytics::$settings->serverExcludes)) {
0 ignored issues
show
Coding Style introduced by
Closing parenthesis of a multi-line IF statement must be on a new line
Loading history...
210
            foreach (InstantAnalytics::$settings->serverExcludes as $match => $matchArray) {
211
                if (isset($_SERVER[$match])) {
212
                    foreach ($matchArray as $matchItem) {
213
                        if (preg_match($matchItem, $_SERVER[$match])) {
214
                            $logExclusion('serverExcludes');
215
216
                            return false;
217
                        }
218
                    }
219
                }
220
            }
221
        }
222
223
        // Filter out bot/spam requests via UserAgent
224
        if (InstantAnalytics::$settings->filterBotUserAgents) {
225
            $crawlerDetect = new CrawlerDetect();
226
            // Check the user agent of the current 'visitor'
227
            if ($crawlerDetect->isCrawler()) {
228
                $logExclusion('filterBotUserAgents');
229
230
                return false;
231
            }
232
        }
233
234
        // Filter by user group
235
        $userService = Craft::$app->getUser();
236
        /** @var ?UserElement $user */
0 ignored issues
show
Coding Style introduced by
The open comment tag must be the only content on the line
Loading history...
Coding Style introduced by
Missing short description in doc comment
Loading history...
Coding Style introduced by
The close comment tag must be the only content on the line
Loading history...
237
        $user = $userService->getIdentity();
238
        if ($user) {
239
            if (InstantAnalytics::$settings->adminExclude && $user->admin) {
240
                $logExclusion('adminExclude');
241
242
                return false;
243
            }
244
245
            if (InstantAnalytics::$settings->groupExcludes !== null
246
                && !empty(InstantAnalytics::$settings->groupExcludes)) {
0 ignored issues
show
Coding Style introduced by
Closing parenthesis of a multi-line IF statement must be on a new line
Loading history...
247
                foreach (InstantAnalytics::$settings->groupExcludes as $matchItem) {
248
                    if ($user->isInGroup($matchItem)) {
249
                        $logExclusion('groupExcludes');
250
251
                        return false;
252
                    }
253
                }
254
            }
255
        }
256
257
        return $result;
258
    }
259
260
    /**
261
     * getClientId handles the parsing of the _ga cookie or setting it to a
0 ignored issues
show
Coding Style introduced by
Doc comment short description must start with a capital letter
Loading history...
262
     * unique identifier
263
     *
264
     * @return string the cid
265
     */
266
    public static function getClientId(): string
267
    {
268
        $cid = '';
269
        if (isset($_COOKIE['_ga'])) {
270
            $parts = explode(".", $_COOKIE['_ga'], 4);
271
            if ($parts !== false) {
272
                $cid = implode('.', array_slice($parts, 2));
273
            }
274
        } elseif (isset($_COOKIE['_ia']) && $_COOKIE['_ia'] !== '') {
275
            $cid = $_COOKIE['_ia'];
276
        } else {
277
            // Generate our own client id, otherwise.
278
            $cid = static::gaGenUUID() . '.1';
279
        }
280
281
        if (InstantAnalytics::$settings->createGclidCookie && !empty($cid)) {
282
            setcookie('_ia', $cid, strtotime('+2 years'), '/'); // Two years
283
        }
284
285
        return $cid;
286
    }
287
288
    /**
289
     * Get the Google Analytics session string from the cookie.
290
     *
291
     * @return string
292
     */
293
    public static function getSessionString(): string
294
    {
295
        $sessionString = '';
296
        $measurementId = App::parseEnv(InstantAnalytics::$settings->googleAnalyticsMeasurementId);
297
        $cookieName = '_ga_' . StringHelper::removeLeft($measurementId, 'G-');
0 ignored issues
show
Bug introduced by
It seems like $measurementId can also be of type null; however, parameter $str of craft\helpers\StringHelper::removeLeft() does only seem to accept string, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

297
        $cookieName = '_ga_' . StringHelper::removeLeft(/** @scrutinizer ignore-type */ $measurementId, 'G-');
Loading history...
298
299
        if (isset($_COOKIE[$cookieName])) {
300
            $parts = explode(".", $_COOKIE[$cookieName], 5);
301
            if ($parts !== false) {
302
                $sessionString = implode('.', array_slice($parts, 2, 2));
303
            }
304
        }
305
306
        return $sessionString;
307
    }
308
309
    /**
310
     * Get the user id.
311
     *
312
     * @return string
313
     */
314
    public static function getUserId(): string
315
    {
316
        $userId = Craft::$app->getUser()->getId();
317
318
        if (!$userId) {
319
            return '';
320
        }
321
322
        return $userId;
323
    }
324
325
    /**
326
     * gaGenUUID Generate UUID v4 function - needed to generate a CID when one
0 ignored issues
show
Coding Style introduced by
Doc comment short description must start with a capital letter
Loading history...
327
     * isn't available
328
     *
329
     * @return string The generated UUID
330
     */
331
    protected static function gaGenUUID()
332
    {
333
        return sprintf(
334
            '%04x%04x-%04x-%04x-%04x-%04x%04x%04x',
335
            // 32 bits for "time_low"
336
            mt_rand(0, 0xffff),
337
            mt_rand(0, 0xffff),
338
            // 16 bits for "time_mid"
339
            mt_rand(0, 0xffff),
340
            // 16 bits for "time_hi_and_version",
341
            // four most significant bits holds version number 4
342
            mt_rand(0, 0x0fff) | 0x4000,
343
            // 16 bits, 8 bits for "clk_seq_hi_res",
344
            // 8 bits for "clk_seq_low",
345
            // two most significant bits holds zero and one for variant DCE1.1
346
            mt_rand(0, 0x3fff) | 0x8000,
347
            // 48 bits for "node"
348
            mt_rand(0, 0xffff),
349
            mt_rand(0, 0xffff),
350
            mt_rand(0, 0xffff)
351
        );
352
    }
353
}
354