Passed
Branch v1 (eb0178)
by Andrew
08:09
created

Webperf::installEventListeners()   B

Complexity

Conditions 7
Paths 4

Size

Total Lines 30
Code Lines 18

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
eloc 18
c 1
b 0
f 0
dl 0
loc 30
rs 8.8333
cc 7
nc 4
nop 0
1
<?php
2
/**
3
 * Webperf plugin for Craft CMS 3.x
4
 *
5
 * Monitor the performance of your webpages through real-world user timing data
6
 *
7
 * @link      https://nystudio107.com
0 ignored issues
show
Coding Style introduced by
The tag in position 1 should be the @copyright tag
Loading history...
8
 * @copyright Copyright (c) 2019 nystudio107
0 ignored issues
show
Coding Style introduced by
@copyright tag must contain a year and the name of the copyright holder
Loading history...
9
 */
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 @package tag in file comment
Loading history...
Coding Style introduced by
Missing @author tag in file comment
Loading history...
Coding Style introduced by
Missing @license tag in file comment
Loading history...
10
11
namespace nystudio107\webperf;
12
13
use nystudio107\webperf\assetbundles\webperf\WebperfAsset;
14
use nystudio107\webperf\base\CraftDataSample;
15
use nystudio107\webperf\helpers\PluginTemplate;
16
use nystudio107\webperf\log\ErrorsTarget;
17
use nystudio107\webperf\log\ProfileTarget;
18
use nystudio107\webperf\models\RecommendationDataSample;
19
use nystudio107\webperf\models\Settings;
20
use nystudio107\webperf\services\DataSamples as DataSamplesService;
21
use nystudio107\webperf\services\ErrorSamples as ErrorSamplesService;
22
use nystudio107\webperf\services\Beacons as BeaconsService;
23
use nystudio107\webperf\services\Recommendations as RecommendationsService;
24
use nystudio107\webperf\variables\WebperfVariable;
25
26
use nystudio107\pluginmanifest\services\ManifestService;
27
28
use Craft;
29
use craft\base\Element;
30
use craft\base\Plugin;
31
use craft\services\Plugins;
32
use craft\events\PluginEvent;
33
use craft\events\RegisterUserPermissionsEvent;
34
use craft\events\RegisterUrlRulesEvent;
35
use craft\helpers\UrlHelper;
36
use craft\services\UserPermissions;
37
use craft\web\Application;
38
use craft\web\twig\variables\CraftVariable;
39
use craft\web\UrlManager;
40
use craft\web\View;
41
42
use yii\base\Event;
43
use yii\base\InvalidConfigException;
44
45
/**
46
 * Class Webperf
47
 *
48
 * @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...
49
 * @package   Webperf
0 ignored issues
show
Coding Style introduced by
Tag value for @package tag indented incorrectly; expected 1 spaces but found 3
Loading history...
50
 * @since     1.0.0
0 ignored issues
show
Coding Style introduced by
Tag value for @since tag indented incorrectly; expected 3 spaces but found 5
Loading history...
Coding Style introduced by
The tag in position 3 should be the @author tag
Loading history...
51
 *
52
 * @property BeaconsService          $beacons
53
 * @property DataSamplesService      $dataSamples
54
 * @property ErrorSamplesService     $errorSamples
55
 * @property RecommendationsService  $recommendations
56
 * @property ErrorsTarget            $errorsTarget
57
 * @property ProfileTarget           $profileTarget
58
 * @property ManifestService         $manifest
59
 */
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...
60
class Webperf extends Plugin
61
{
62
    // Constants
63
    // =========================================================================
64
65
    const RECOMMENDATIONS_CACHE_KEY = 'webperf-recommendations';
66
    const RECOMMENDATIONS_CACHE_DURATION = 60;
67
68
    const ERRORS_CACHE_KEY = 'webperf-errors';
69
    const ERRORS_CACHE_DURATION = 60;
70
71
    // Static Properties
72
    // =========================================================================
73
74
    /**
0 ignored issues
show
Coding Style introduced by
Missing short description in doc comment
Loading history...
75
     * @var Webperf
76
     */
77
    public static $plugin;
78
79
    /**
0 ignored issues
show
Coding Style introduced by
Missing short description in doc comment
Loading history...
80
     * @var Settings
81
     */
82
    public static $settings;
83
84
    /**
0 ignored issues
show
Coding Style introduced by
Missing short description in doc comment
Loading history...
85
     * @var int|null
86
     */
87
    public static $requestUuid;
88
89
    /**
0 ignored issues
show
Coding Style introduced by
Missing short description in doc comment
Loading history...
90
     * @var int|null
91
     */
92
    public static $requestUrl;
93
94
    /**
0 ignored issues
show
Coding Style introduced by
Missing short description in doc comment
Loading history...
95
     * @var bool
96
     */
97
    public static $beaconIncluded = false;
98
99
    /**
0 ignored issues
show
Coding Style introduced by
Missing short description in doc comment
Loading history...
100
     * @var string
101
     */
102
    public static $renderType = 'html';
103
104
    /**
0 ignored issues
show
Coding Style introduced by
Missing short description in doc comment
Loading history...
105
     * @var bool
106
     */
107
    public static $craft31 = false;
108
109
    // Static Methods
110
    // =========================================================================
111
112
    /**
0 ignored issues
show
Coding Style introduced by
Missing short description in doc comment
Loading history...
Coding Style introduced by
Parameter $parent should have a doc-comment as per coding-style.
Loading history...
Coding Style introduced by
Parameter $id should have a doc-comment as per coding-style.
Loading history...
Coding Style introduced by
Parameter $config should have a doc-comment as per coding-style.
Loading history...
113
     * @inheritdoc
114
     */
115
    public function __construct($id, $parent = null, array $config = [])
116
    {
117
        $config['components'] = [
118
            'beacons' => BeaconsService::class,
119
            'dataSamples' => DataSamplesService::class,
120
            'errorSamples' => ErrorSamplesService::class,
121
            'recommendations' => RecommendationsService::class,
122
            // Register the manifest service
123
            'manifest' => [
124
                'class' => ManifestService::class,
125
                'assetClass' => WebperfAsset::class,
126
                'devServerManifestPath' => 'http://craft-webperf-buildchain:8080/',
127
                'devServerPublicPath' => 'http://craft-webperf-buildchain:8080/',
128
            ],
129
        ];
130
131
        parent::__construct($id, $parent, $config);
132
    }
133
134
    // Public Properties
135
    // =========================================================================
136
137
    /**
0 ignored issues
show
Coding Style introduced by
Missing short description in doc comment
Loading history...
138
     * @var string
139
     */
140
    public $schemaVersion = '1.0.1';
141
142
    /**
0 ignored issues
show
Coding Style introduced by
Missing short description in doc comment
Loading history...
143
     * @var bool
144
     */
145
    public $hasCpSection = true;
146
147
    /**
0 ignored issues
show
Coding Style introduced by
Missing short description in doc comment
Loading history...
148
     * @var bool
149
     */
150
    public $hasCpSettings = true;
151
152
    // Public Methods
153
    // =========================================================================
154
155
    /**
0 ignored issues
show
Coding Style introduced by
Missing short description in doc comment
Loading history...
156
     * @inheritdoc
157
     */
0 ignored issues
show
Coding Style introduced by
Missing @return tag in function comment
Loading history...
158
    public function init()
159
    {
160
        parent::init();
161
        // Initialize properties
162
        self::$plugin = $this;
163
        self::$settings = $this->getSettings();
164
        try {
165
            self::$requestUuid = random_int(0, PHP_INT_MAX);
166
        } catch (\Exception $e) {
167
            self::$requestUuid = null;
168
        }
169
        self::$craft31 = version_compare(Craft::$app->getVersion(), '3.1', '>=');
0 ignored issues
show
Documentation Bug introduced by
It seems like version_compare(Craft::a...Version(), '3.1', '>=') can also be of type integer. However, the property $craft31 is declared as type boolean. Maybe add an additional type check?

Our type inference engine has found a suspicous assignment of a value to a property. This check raises an issue when a value that can be of a mixed type is assigned to a property that is type hinted more strictly.

For example, imagine you have a variable $accountId that can either hold an Id object or false (if there is no account id yet). Your code now assigns that value to the id property of an instance of the Account class. This class holds a proper account, so the id value must no longer be false.

Either this assignment is in error or a type check should be added for that assignment.

class Id
{
    public $id;

    public function __construct($id)
    {
        $this->id = $id;
    }

}

class Account
{
    /** @var  Id $id */
    public $id;
}

$account_id = false;

if (starsAreRight()) {
    $account_id = new Id(42);
}

$account = new Account();
if ($account instanceof Id)
{
    $account->id = $account_id;
}
Loading history...
170
        $this->name = self::$settings->pluginName;
171
        // Handle any console commands
172
        $request = Craft::$app->getRequest();
173
        if ($request->getIsConsoleRequest()) {
174
            $this->controllerNamespace = 'nystudio107\webperf\console\controllers';
175
        }
176
        // Add in our components
177
        $this->addComponents();
178
        // Install event listeners
179
        $this->installEventListeners();
180
        // Load that we've loaded
181
        Craft::info(
182
            Craft::t(
183
                'webperf',
184
                '{name} plugin loaded',
185
                ['name' => $this->name]
186
            ),
187
            __METHOD__
188
        );
189
    }
190
191
    /**
0 ignored issues
show
Coding Style introduced by
Missing short description in doc comment
Loading history...
192
     * @inheritdoc
193
     */
0 ignored issues
show
Coding Style introduced by
Missing @return tag in function comment
Loading history...
194
    public function getSettingsResponse()
195
    {
196
        // Just redirect to the plugin settings page
197
        Craft::$app->getResponse()->redirect(UrlHelper::cpUrl('webperf/settings'));
198
    }
199
200
    /**
0 ignored issues
show
Coding Style introduced by
Missing short description in doc comment
Loading history...
201
     * @inheritdoc
202
     */
0 ignored issues
show
Coding Style introduced by
Missing @return tag in function comment
Loading history...
203
    public function getCpNavItem()
204
    {
205
        $subNavs = [];
206
        $navItem = parent::getCpNavItem();
207
        $recommendations = $this->getRecommendationsCount();
208
        $errors = $this->getErrorsCount();
209
        if (!empty($errors)) {
210
            $navItem['label'] .= ' '.$errors;
211
        }
212
        $navItem['badgeCount'] = $recommendations;
213
        $currentUser = Craft::$app->getUser()->getIdentity();
214
        if ($currentUser) {
0 ignored issues
show
introduced by
$currentUser is of type yii\web\IdentityInterface, thus it always evaluated to true.
Loading history...
215
            // Only show sub-navs the user has permission to view
216
            if ($currentUser->can('webperf:dashboard')) {
0 ignored issues
show
Bug introduced by
The method can() does not exist on yii\web\IdentityInterface. It seems like you code against a sub-type of yii\web\IdentityInterface such as craft\elements\User. ( Ignorable by Annotation )

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

216
            if ($currentUser->/** @scrutinizer ignore-call */ can('webperf:dashboard')) {
Loading history...
217
                $subNavs['dashboard'] = [
218
                    'label' => Craft::t('webperf', 'Dashboard'),
219
                    'url' => 'webperf/dashboard',
220
                ];
221
            }
222
            if ($currentUser->can('webperf:performance')) {
223
                $subNavs['performance'] = [
224
                    'label' => Craft::t('webperf', 'Performance'),
225
                    'url' => 'webperf/performance',
226
                ];
227
            }
228
            if ($currentUser->can('webperf:errors')) {
229
                $subNavs['errors'] = [
230
                    'label' => Craft::t('webperf', 'Errors').' '.$errors,
231
                    'url' => 'webperf/errors',
232
                    'badge' => $errors,
233
                ];
234
            }
235
            /* @TODO Alerts implementation
236
            if ($currentUser->can('webperf:alerts')) {
237
                $subNavs['alerts'] = [
238
                    'label' => 'Alerts',
239
                    'url' => 'webperf/alerts',
240
                ];
241
            }
242
             */
243
            $editableSettings = true;
244
            $general = Craft::$app->getConfig()->getGeneral();
245
            if (self::$craft31 && !$general->allowAdminChanges) {
246
                $editableSettings = false;
247
            }
248
            if ($currentUser->can('webperf:settings') && $editableSettings) {
249
                $subNavs['settings'] = [
250
                    'label' => Craft::t('webperf', 'Settings'),
251
                    'url' => 'webperf/settings',
252
                ];
253
            }
254
        }
255
        $navItem = array_merge($navItem, [
0 ignored issues
show
Coding Style introduced by
The opening parenthesis of a multi-line function call should be the last content on the line.
Loading history...
256
            'subnav' => $subNavs,
257
        ]);
0 ignored issues
show
Coding Style introduced by
For multi-line function calls, the closing parenthesis should be on a new line.

If a function call spawns multiple lines, the coding standard suggests to move the closing parenthesis to a new line:

someFunctionCall(
    $firstArgument,
    $secondArgument,
    $thirdArgument
); // Closing parenthesis on a new line.
Loading history...
258
259
        return $navItem;
260
    }
261
262
    /**
263
     * Clear all the caches!
264
     */
0 ignored issues
show
Coding Style introduced by
Missing @return tag in function comment
Loading history...
265
    public function clearAllCaches()
266
    {
267
    }
268
269
    // Protected Methods
270
    // =========================================================================
271
272
    /**
273
     * Add in our components
274
     */
0 ignored issues
show
Coding Style introduced by
Missing @return tag in function comment
Loading history...
275
    protected function addComponents()
276
    {
277
        $request = Craft::$app->getRequest();
278
        if ($request->getIsSiteRequest() && !$request->getIsConsoleRequest() && !$request->getIsLivePreview()) {
279
            $this->setRequestUrl();
280
            try {
281
                $uri = $request->getPathInfo();
282
            } catch (InvalidConfigException $e) {
283
                $uri = '';
284
            }
285
            // Ignore our own controllers
286
            if (self::$settings->includeCraftProfiling && !$this->excludeUri($uri)) {
287
                // Add in the ProfileTarget component
288
                try {
289
                    $this->set('profileTarget', [
0 ignored issues
show
Coding Style introduced by
The opening parenthesis of a multi-line function call should be the last content on the line.
Loading history...
290
                        'class' => ProfileTarget::class,
291
                        'levels' => ['profile'],
292
                        'categories' => [],
293
                        'logVars' => [],
294
                        'except' => [],
295
                    ]);
0 ignored issues
show
Coding Style introduced by
For multi-line function calls, the closing parenthesis should be on a new line.

If a function call spawns multiple lines, the coding standard suggests to move the closing parenthesis to a new line:

someFunctionCall(
    $firstArgument,
    $secondArgument,
    $thirdArgument
); // Closing parenthesis on a new line.
Loading history...
296
                } catch (InvalidConfigException $e) {
297
                    Craft::error($e->getMessage(), __METHOD__);
298
                }
299
                // Attach our log target
300
                Craft::$app->getLog()->targets['webperf-profile'] = $this->profileTarget;
301
                // Add in the ErrorsTarget component
302
                $except = [];
303
                // If devMode is on, exclude errors/warnings from `seomatic`
304
                if (Craft::$app->getConfig()->getGeneral()->devMode) {
305
                    $except = ['nystudio107\seomatic\*'];
306
                }
307
                $levels = ['error'];
308
                if (self::$settings->includeCraftWarnings) {
309
                    $levels[] = 'warning';
310
                }
311
                try {
312
                    $this->set('errorsTarget', [
0 ignored issues
show
Coding Style introduced by
The opening parenthesis of a multi-line function call should be the last content on the line.
Loading history...
313
                        'class' => ErrorsTarget::class,
314
                        'levels' => $levels,
315
                        'categories' => [],
316
                        'logVars' => [],
317
                        'except' => $except,
318
                    ]);
0 ignored issues
show
Coding Style introduced by
For multi-line function calls, the closing parenthesis should be on a new line.

If a function call spawns multiple lines, the coding standard suggests to move the closing parenthesis to a new line:

someFunctionCall(
    $firstArgument,
    $secondArgument,
    $thirdArgument
); // Closing parenthesis on a new line.
Loading history...
319
                } catch (InvalidConfigException $e) {
320
                    Craft::error($e->getMessage(), __METHOD__);
321
                }
322
                // Attach our log target
323
                Craft::$app->getLog()->targets['webperf-errors'] = $this->errorsTarget;
324
            }
325
        }
326
    }
327
328
    /**
329
     * Set the request URL
330
     *
331
     * @param bool $force
0 ignored issues
show
Coding Style introduced by
Missing parameter comment
Loading history...
332
     */
0 ignored issues
show
Coding Style introduced by
Missing @return tag in function comment
Loading history...
333
    protected function setRequestUrl(bool $force = false)
334
    {
335
        self::$requestUrl = CraftDataSample::PLACEHOLDER_URL;
0 ignored issues
show
Documentation Bug introduced by
It seems like nystudio107\webperf\base...Sample::PLACEHOLDER_URL of type string is incompatible with the declared type integer|null of property $requestUrl.

Our type inference engine has found an assignment to a property that is incompatible with the declared type of that property.

Either this assignment is in error or the assigned type should be added to the documentation/type hint for that property..

Loading history...
336
        if (!self::$settings->includeBeacon || $force || self::$settings->staticCachedSite) {
337
            $request = Craft::$app->getRequest();
338
            self::$requestUrl = UrlHelper::stripQueryString(
339
                urldecode($request->getAbsoluteUrl())
340
            );
341
        }
342
    }
343
344
    /**
345
     * Install our event listeners.
346
     */
0 ignored issues
show
Coding Style introduced by
Missing @return tag in function comment
Loading history...
347
    protected function installEventListeners()
348
    {
349
        $request = Craft::$app->getRequest();
350
        // Add in our event listeners that are needed for every request
351
        $this->installGlobalEventListeners();
352
        // Install only for non-console site requests
353
        if ($request->getIsSiteRequest() && !$request->getIsConsoleRequest()) {
354
            $this->installSiteEventListeners();
355
        }
356
        // Install only for non-console Control Panel requests
357
        if ($request->getIsCpRequest() && !$request->getIsConsoleRequest()) {
358
            $this->installCpEventListeners();
359
        }
360
        // Handler: EVENT_AFTER_INSTALL_PLUGIN
361
        Event::on(
362
            Plugins::class,
363
            Plugins::EVENT_AFTER_INSTALL_PLUGIN,
364
            function (PluginEvent $event) {
365
                if ($event->plugin === $this) {
366
                    // Invalidate our caches after we've been installed
367
                    $this->clearAllCaches();
368
                    // Send them to our welcome screen
369
                    $request = Craft::$app->getRequest();
370
                    if ($request->isCpRequest) {
371
                        Craft::$app->getResponse()->redirect(UrlHelper::cpUrl(
0 ignored issues
show
Coding Style introduced by
The opening parenthesis of a multi-line function call should be the last content on the line.
Loading history...
372
                            'webperf/dashboard',
373
                            [
374
                                'showWelcome' => true,
375
                            ]
376
                        ))->send();
0 ignored issues
show
Coding Style introduced by
For multi-line function calls, the closing parenthesis should be on a new line.

If a function call spawns multiple lines, the coding standard suggests to move the closing parenthesis to a new line:

someFunctionCall(
    $firstArgument,
    $secondArgument,
    $thirdArgument
); // Closing parenthesis on a new line.
Loading history...
377
                    }
378
                }
379
            }
380
        );
381
    }
382
383
    /**
384
     * Install global event listeners for all request types
385
     */
0 ignored issues
show
Coding Style introduced by
Missing @return tag in function comment
Loading history...
386
    protected function installGlobalEventListeners()
387
    {
388
        // Handler: CraftVariable::EVENT_INIT
389
        Event::on(
390
            CraftVariable::class,
391
            CraftVariable::EVENT_INIT,
392
            function (Event $event) {
393
                /** @var CraftVariable $variable */
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...
394
                $variable = $event->sender;
395
                $variable->set('webperf', [
0 ignored issues
show
Coding Style introduced by
The opening parenthesis of a multi-line function call should be the last content on the line.
Loading history...
396
                    'class' => WebperfVariable::class,
397
                    'manifestService' => $this->manifest,
398
                ]);
0 ignored issues
show
Coding Style introduced by
For multi-line function calls, the closing parenthesis should be on a new line.

If a function call spawns multiple lines, the coding standard suggests to move the closing parenthesis to a new line:

someFunctionCall(
    $firstArgument,
    $secondArgument,
    $thirdArgument
); // Closing parenthesis on a new line.
Loading history...
399
            }
400
        );
401
        // Handler: Plugins::EVENT_AFTER_LOAD_PLUGINS
402
        Event::on(
403
            Plugins::class,
404
            Plugins::EVENT_AFTER_LOAD_PLUGINS,
405
            function () {
406
                // Install these only after all other plugins have loaded
407
                $request = Craft::$app->getRequest();
408
                // Only respond to non-console site requests
409
                if ($request->getIsSiteRequest() && !$request->getIsConsoleRequest()  && !$request->getIsLivePreview()) {
410
                    $this->handleSiteRequest();
411
                }
412
                // Respond to Control Panel requests
413
                if ($request->getIsCpRequest() && !$request->getIsConsoleRequest()) {
414
                    $this->handleAdminCpRequest();
415
                }
416
            }
417
        );
418
    }
419
420
    /**
421
     * Install site event listeners for site requests only
422
     */
0 ignored issues
show
Coding Style introduced by
Missing @return tag in function comment
Loading history...
423
    protected function installSiteEventListeners()
424
    {
425
        // Handler: UrlManager::EVENT_REGISTER_SITE_URL_RULES
426
        Event::on(
427
            UrlManager::class,
428
            UrlManager::EVENT_REGISTER_SITE_URL_RULES,
429
            function (RegisterUrlRulesEvent $event) {
430
                Craft::debug(
431
                    'UrlManager::EVENT_REGISTER_SITE_URL_RULES',
432
                    __METHOD__
433
                );
434
                // Register our Control Panel routes
435
                $event->rules = array_merge(
436
                    $event->rules,
437
                    $this->customFrontendRoutes()
438
                );
439
            }
440
        );
441
    }
442
443
    /**
444
     * Install site event listeners for Control Panel requests only
445
     */
0 ignored issues
show
Coding Style introduced by
Missing @return tag in function comment
Loading history...
446
    protected function installCpEventListeners()
447
    {
448
        // Handler: UrlManager::EVENT_REGISTER_CP_URL_RULES
449
        Event::on(
450
            UrlManager::class,
451
            UrlManager::EVENT_REGISTER_CP_URL_RULES,
452
            function (RegisterUrlRulesEvent $event) {
453
                Craft::debug(
454
                    'UrlManager::EVENT_REGISTER_CP_URL_RULES',
455
                    __METHOD__
456
                );
457
                // Register our Control Panel routes
458
                $event->rules = array_merge(
459
                    $event->rules,
460
                    $this->customAdminCpRoutes()
461
                );
462
            }
463
        );
464
        // Handler: UserPermissions::EVENT_REGISTER_PERMISSIONS
465
        Event::on(
466
            UserPermissions::class,
467
            UserPermissions::EVENT_REGISTER_PERMISSIONS,
468
            function (RegisterUserPermissionsEvent $event) {
469
                Craft::debug(
470
                    'UserPermissions::EVENT_REGISTER_PERMISSIONS',
471
                    __METHOD__
472
                );
473
                // Register our custom permissions
474
                $event->permissions[Craft::t('webperf', 'Webperf')] = $this->customAdminCpPermissions();
475
            }
476
        );
477
    }
478
479
    /**
480
     * Handle site requests.  We do it only after we receive the event
481
     * EVENT_AFTER_LOAD_PLUGINS so that any pending db migrations can be run
482
     * before our event listeners kick in
483
     */
0 ignored issues
show
Coding Style introduced by
Missing @return tag in function comment
Loading history...
484
    protected function handleSiteRequest()
485
    {
486
        $request = Craft::$app->getRequest();
487
        try {
488
            $uri = $request->getPathInfo();
489
        } catch (InvalidConfigException $e) {
490
            $uri = '';
491
        }
492
        // Check for URIs to exclude
493
        if (!$this->excludeUri($uri)) {
494
            // Handler: View::EVENT_END_PAGE
495
            Event::on(
496
                View::class,
497
                View::EVENT_END_PAGE,
498
                function () {
499
                    Craft::debug(
500
                        'View::EVENT_END_PAGE',
501
                        __METHOD__
502
                    );
503
                    // Don't include the beacon for response codes >= 400
504
                    $response = Craft::$app->getResponse();
505
                    if ($response->statusCode < 400) {
506
                        $view = Craft::$app->getView();
507
                        // The page is done rendering, include our beacon
508
                        if (Webperf::$settings->includeBeacon && $view->getIsRenderingPageTemplate()) {
509
                            switch (self::$renderType) {
510
                                case 'html':
0 ignored issues
show
Coding Style introduced by
Line indented incorrectly; expected 28 spaces, found 32
Loading history...
511
                                    Webperf::$plugin->beacons->includeHtmlBeacon();
512
                                    self::$beaconIncluded = true;
513
                                    break;
514
                                case 'amp-html':
0 ignored issues
show
Coding Style introduced by
Line indented incorrectly; expected 28 spaces, found 32
Loading history...
515
                                    Webperf::$plugin->beacons->includeAmpHtmlScript();
516
                                    break;
517
                            }
518
                        }
519
                    }
520
                }
521
            );
522
            // Handler: View::EVENT_END_BODY
523
            Event::on(
524
                View::class,
525
                View::EVENT_END_BODY,
526
                function () {
527
                    Craft::debug(
528
                        'View::EVENT_END_BODY',
529
                        __METHOD__
530
                    );
531
                    $view = Craft::$app->getView();
532
                    // The page is done rendering, include our beacon
533
                    if (Webperf::$settings->includeBeacon && $view->getIsRenderingPageTemplate()) {
534
                        switch (self::$renderType) {
535
                            case 'html':
0 ignored issues
show
Coding Style introduced by
Line indented incorrectly; expected 24 spaces, found 28
Loading history...
536
                                break;
537
                            case 'amp-html':
0 ignored issues
show
Coding Style introduced by
Line indented incorrectly; expected 24 spaces, found 28
Loading history...
538
                                Webperf::$plugin->beacons->includeAmpHtmlBeacon();
539
                                self::$beaconIncluded = true;
540
                                break;
541
                        }
542
                    }
543
                }
544
            );
545
            // Handler: Application::EVENT_AFTER_REQUEST
546
            Event::on(
547
                Application::class,
548
                Application::EVENT_AFTER_REQUEST,
549
                function () {
550
                    Craft::debug(
551
                        'Application::EVENT_AFTER_REQUEST',
552
                        __METHOD__
553
                    );
554
                    // If the beacon wasn't included, allow for the Craft timings
555
                    if (!self::$beaconIncluded) {
556
                        $this->setRequestUrl(true);
557
                    }
558
                }
559
            );
560
        }
561
    }
562
563
    /**
564
     * Handle Control Panel requests. We do it only after we receive the event
565
     * EVENT_AFTER_LOAD_PLUGINS so that any pending db migrations can be run
566
     * before our event listeners kick in
567
     */
0 ignored issues
show
Coding Style introduced by
Missing @return tag in function comment
Loading history...
568
    protected function handleAdminCpRequest()
569
    {
570
        $currentUser = Craft::$app->getUser()->getIdentity();
571
        // Only show sub-navs the user has permission to view
572
        if (self::$settings->displaySidebar && $currentUser && $currentUser->can('webperf:sidebar')) {
573
            $view = Craft::$app->getView();
574
            // Entries sidebar
575
            $view->hook('cp.entries.edit.details', function (&$context) {
0 ignored issues
show
Coding Style introduced by
The opening parenthesis of a multi-line function call should be the last content on the line.
Loading history...
576
                /** @var  Element $element */
0 ignored issues
show
Coding Style introduced by
The close comment tag must be the only content on the line
Loading history...
Coding Style introduced by
Tag value for @var tag indented incorrectly; expected 1 spaces but found 2
Loading history...
Coding Style introduced by
Missing short description in doc comment
Loading history...
Coding Style introduced by
The open comment tag must be the only content on the line
Loading history...
577
                $element = $context['entry'] ?? null;
578
                $html = '';
579
                if ($element !== null && $element->uri !== null) {
580
                    $html = $this->renderSidebar($element);
581
                }
582
583
                return $html;
584
            });
0 ignored issues
show
Coding Style introduced by
For multi-line function calls, the closing parenthesis should be on a new line.

If a function call spawns multiple lines, the coding standard suggests to move the closing parenthesis to a new line:

someFunctionCall(
    $firstArgument,
    $secondArgument,
    $thirdArgument
); // Closing parenthesis on a new line.
Loading history...
585
            // Category Groups sidebar
586
            $view->hook('cp.categories.edit.details', function (&$context) {
0 ignored issues
show
Coding Style introduced by
The opening parenthesis of a multi-line function call should be the last content on the line.
Loading history...
587
                /** @var  Element $element */
0 ignored issues
show
Coding Style introduced by
The close comment tag must be the only content on the line
Loading history...
Coding Style introduced by
Tag value for @var tag indented incorrectly; expected 1 spaces but found 2
Loading history...
Coding Style introduced by
Missing short description in doc comment
Loading history...
Coding Style introduced by
The open comment tag must be the only content on the line
Loading history...
588
                $element = $context['category'] ?? null;
589
                $html = '';
590
                if ($element !== null && $element->uri !== null) {
591
                    $html = $this->renderSidebar($element);
592
                }
593
594
                return $html;
595
            });
0 ignored issues
show
Coding Style introduced by
For multi-line function calls, the closing parenthesis should be on a new line.

If a function call spawns multiple lines, the coding standard suggests to move the closing parenthesis to a new line:

someFunctionCall(
    $firstArgument,
    $secondArgument,
    $thirdArgument
); // Closing parenthesis on a new line.
Loading history...
596
            // Commerce Product Types sidebar
597
            $view->hook('cp.commerce.product.edit.details', function (&$context) {
0 ignored issues
show
Coding Style introduced by
The opening parenthesis of a multi-line function call should be the last content on the line.
Loading history...
598
                /** @var  Element $element */
0 ignored issues
show
Coding Style introduced by
Missing short description in doc comment
Loading history...
Coding Style introduced by
Tag value for @var tag indented incorrectly; expected 1 spaces but found 2
Loading history...
Coding Style introduced by
The close comment tag must be the only content on the line
Loading history...
Coding Style introduced by
The open comment tag must be the only content on the line
Loading history...
599
                $element = $context['product'] ?? null;
600
                $html = '';
601
                if ($element !== null && $element->uri !== null) {
602
                    $html = $this->renderSidebar($element);
603
                }
604
605
                return $html;
606
            });
0 ignored issues
show
Coding Style introduced by
For multi-line function calls, the closing parenthesis should be on a new line.

If a function call spawns multiple lines, the coding standard suggests to move the closing parenthesis to a new line:

someFunctionCall(
    $firstArgument,
    $secondArgument,
    $thirdArgument
); // Closing parenthesis on a new line.
Loading history...
607
        }
608
    }
609
610
    /**
0 ignored issues
show
Coding Style introduced by
Missing short description in doc comment
Loading history...
611
     * @param Element $element
0 ignored issues
show
Coding Style introduced by
Missing parameter comment
Loading history...
612
     *
613
     * @return string
614
     */
615
    protected function renderSidebar(Element $element): string
616
    {
617
        $html = '';
618
        if ($element !== null && $element->url !== null) {
619
            $view = Craft::$app->getView();
620
            try {
621
                $view->registerAssetBundle(WebperfAsset::class);
622
            } catch (InvalidConfigException $e) {
0 ignored issues
show
Coding Style Comprehensibility introduced by
Consider adding a comment why this CATCH block is empty.
Loading history...
623
            }
624
            try {
625
                $now = new \DateTime();
626
            } catch (\Exception $e) {
627
                return $html;
628
            }
629
            $end = $now->format('Y-m-d');
630
            $start = $now->modify('-30 days')->format('Y-m-d');
631
            $html .= PluginTemplate::renderPluginTemplate(
632
                '_sidebars/webperf-sidebar.twig',
633
                [
634
                    'settings' => self::$settings,
635
                    'pageUrl' => $element->url,
636
                    'start' => $start,
637
                    'end' => $end,
638
                    'currentSiteId' => $element->siteId ?? 0,
639
                ]
640
            );
641
        }
642
643
        return $html;
644
    }
645
646
    /**
0 ignored issues
show
Coding Style introduced by
Missing short description in doc comment
Loading history...
647
     * @param $uri
0 ignored issues
show
Coding Style introduced by
Missing parameter comment
Loading history...
648
     *
649
     * @return bool
650
     */
651
    protected function excludeUri($uri): bool
652
    {
653
        $uri = '/'.ltrim($uri, '/');
654
        if (!empty(self::$settings->excludePatterns)) {
655
            foreach (self::$settings->excludePatterns as $excludePattern) {
656
                $pattern = '`'.$excludePattern['pattern'].'`i';
657
                if (preg_match($pattern, $uri) === 1) {
658
                    return true;
659
                }
660
            }
661
        }
662
663
        return false;
664
    }
665
666
    /**
0 ignored issues
show
Coding Style introduced by
Missing short description in doc comment
Loading history...
667
     * @inheritdoc
668
     */
0 ignored issues
show
Coding Style introduced by
Missing @return tag in function comment
Loading history...
669
    protected function createSettingsModel()
670
    {
671
        return new Settings();
672
    }
673
674
    /**
0 ignored issues
show
Coding Style introduced by
Missing short description in doc comment
Loading history...
675
     * @inheritdoc
676
     */
0 ignored issues
show
Coding Style introduced by
Missing @return tag in function comment
Loading history...
677
    protected function settingsHtml(): string
678
    {
679
        return Craft::$app->view->renderTemplate(
680
            'webperf/settings',
681
            [
682
                'settings' => $this->getSettings()
683
            ]
684
        );
685
    }
686
687
    /**
688
     * Return the custom frontend routes
689
     *
690
     * @return array
691
     */
692
    protected function customFrontendRoutes(): array
693
    {
694
        return [
695
            // Beacon
696
            '/webperf/metrics/beacon' => 'webperf/metrics/beacon',
697
            // Render
698
            '/webperf/render/amp-iframe' => 'webperf/render/amp-iframe',
699
        ];
700
    }
701
    /**
702
     * Return the custom Control Panel routes
703
     *
704
     * @return array
705
     */
706
    protected function customAdminCpRoutes(): array
707
    {
708
        return [
709
            'webperf' => 'webperf/sections/dashboard',
710
            'webperf/dashboard' => 'webperf/sections/dashboard',
711
            'webperf/dashboard/<siteHandle:{handle}>' => 'webperf/sections/dashboard',
712
713
            'webperf/performance' => 'webperf/sections/pages-index',
714
            'webperf/performance/<siteHandle:{handle}>' => 'webperf/sections/pages-index',
715
716
            'webperf/performance/page-detail' => 'webperf/sections/page-detail',
717
            'webperf/performance/page-detail/<siteHandle:{handle}>' => 'webperf/sections/page-detail',
718
719
            'webperf/errors' => 'webperf/sections/errors-index',
720
            'webperf/errors/<siteHandle:{handle}>' => 'webperf/sections/errors-index',
721
722
            'webperf/errors/page-detail' => 'webperf/sections/errors-detail',
723
            'webperf/errors/page-detail/<siteHandle:{handle}>' => 'webperf/errors/page-detail',
724
725
            'webperf/alerts' => 'webperf/sections/alerts',
726
            'webperf/alerts/<siteHandle:{handle}>' => 'webperf/sections/alerts',
727
728
            'webperf/settings' => 'webperf/settings/plugin-settings',
729
        ];
730
    }
731
732
    /**
733
     * Returns the custom Control Panel user permissions.
734
     *
735
     * @return array
736
     */
737
    protected function customAdminCpPermissions(): array
738
    {
739
        return [
740
            'webperf:dashboard' => [
741
                'label' => Craft::t('webperf', 'Dashboard'),
742
            ],
743
            'webperf:performance' => [
744
                'label' => Craft::t('webperf', 'Performance'),
745
                'nested' => [
746
                    'webperf:performance-detail' => [
747
                        'label' => Craft::t('webperf', 'Performance Detail'),
748
                    ],
749
                    'webperf:delete-data-samples' => [
750
                        'label' => Craft::t('webperf', 'Delete Data Samples'),
751
                    ],
752
                ],
753
            ],
754
            'webperf:errors' => [
755
                'label' => Craft::t('webperf', 'Errors'),
756
                'nested' => [
757
                    'webperf:errors-detail' => [
758
                        'label' => Craft::t('webperf', 'Errors Detail'),
759
                    ],
760
                    'webperf:delete-error-samples' => [
761
                        'label' => Craft::t('webperf', 'Delete Error Samples'),
762
                    ],
763
                ],
764
            ],
765
            /* @TODO Alerts implementation
766
            'webperf:alerts' => [
767
                'label' => Craft::t('webperf', 'Alerts'),
768
            ],
769
            */
770
            'webperf:recommendations' => [
771
                'label' => Craft::t('webperf', 'Recommendations'),
772
            ],
773
            'webperf:sidebar' => [
774
                'label' => Craft::t('webperf', 'Performance Sidebar'),
775
            ],
776
            'webperf:settings' => [
777
                'label' => Craft::t('webperf', 'Settings'),
778
            ],
779
        ];
780
    }
781
782
    /**
783
     * Get a string value with the number of recommendations
784
     *
785
     * @return string
786
     */
787
    protected function getRecommendationsCount(): string
788
    {
789
        $cache = Craft::$app->getCache();
790
        // See if there are any recommendations to add as a badge
791
        $recommendations = $cache->getOrSet(self::RECOMMENDATIONS_CACHE_KEY, function () {
0 ignored issues
show
Coding Style introduced by
The opening parenthesis of a multi-line function call should be the last content on the line.
Loading history...
792
            $data = [];
793
            $now = new \DateTime();
794
            $end = $now->format('Y-m-d');
795
            $start = $now->modify('-30 days')->format('Y-m-d');
796
            $stats = Webperf::$plugin->recommendations->data('', $start, $end);
797
            if (!empty($stats)) {
798
                $recSample = new RecommendationDataSample($stats);
799
                $data = Webperf::$plugin->recommendations->list($recSample);
800
            }
801
802
            return count($data);
803
        }, self::RECOMMENDATIONS_CACHE_DURATION);
0 ignored issues
show
Coding Style introduced by
For multi-line function calls, the closing parenthesis should be on a new line.

If a function call spawns multiple lines, the coding standard suggests to move the closing parenthesis to a new line:

someFunctionCall(
    $firstArgument,
    $secondArgument,
    $thirdArgument
); // Closing parenthesis on a new line.
Loading history...
804
805
        if (!$recommendations) {
806
            $recommendations = '';
807
        }
808
809
        return (string)$recommendations;
810
    }
811
812
    /**
813
     * Get a string value with the number of errors
814
     *
815
     * @return string
816
     */
817
    protected function getErrorsCount(): string
818
    {
819
        $cache = Craft::$app->getCache();
820
        // See if there are any recommendations to add as a badge
821
        $errors = $cache->getOrSet(self::ERRORS_CACHE_KEY, function () {
0 ignored issues
show
Coding Style introduced by
The opening parenthesis of a multi-line function call should be the last content on the line.
Loading history...
822
            $now = new \DateTime();
823
            $end = $now->format('Y-m-d');
824
            $start = $now->modify('-30 days')->format('Y-m-d');
825
826
            return Webperf::$plugin->errorSamples->totalErrorSamplesRange(0, $start, $end);
827
        }, self::ERRORS_CACHE_DURATION);
0 ignored issues
show
Coding Style introduced by
For multi-line function calls, the closing parenthesis should be on a new line.

If a function call spawns multiple lines, the coding standard suggests to move the closing parenthesis to a new line:

someFunctionCall(
    $firstArgument,
    $secondArgument,
    $thirdArgument
); // Closing parenthesis on a new line.
Loading history...
828
829
        if (!$errors) {
830
            $errors = '';
831
        } else {
832
            $errors = '⚠';
833
        }
834
835
        return (string)$errors;
836
    }
837
}
838