Passed
Push — develop ( 740953...5aeb44 )
by Andrew
03:29
created

Webperf::getCpNavItem()   A

Complexity

Conditions 5
Paths 16

Size

Total Lines 39
Code Lines 26

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
eloc 26
dl 0
loc 39
rs 9.1928
c 0
b 0
f 0
cc 5
nc 16
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\base\CraftDataSample;
14
use nystudio107\webperf\log\ErrorsTarget;
15
use nystudio107\webperf\log\ProfileTarget;
16
use nystudio107\webperf\models\RecommendationDataSample;
17
use nystudio107\webperf\models\Settings;
18
use nystudio107\webperf\services\DataSamples as DataSamplesService;
19
use nystudio107\webperf\services\ErrorSamples as ErrorSamplesService;
20
use nystudio107\webperf\services\Beacons as BeaconsService;
21
use nystudio107\webperf\services\Recommendations as RecommendationsService;
22
use nystudio107\webperf\variables\WebperfVariable;
23
use nystudio107\webperf\widgets\Metrics as MetricsWidget;
24
25
use Craft;
0 ignored issues
show
Bug introduced by
The type Craft was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
26
use craft\base\Plugin;
0 ignored issues
show
Bug introduced by
The type craft\base\Plugin was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
27
use craft\services\Plugins;
0 ignored issues
show
Bug introduced by
The type craft\services\Plugins was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
28
use craft\events\PluginEvent;
0 ignored issues
show
Bug introduced by
The type craft\events\PluginEvent was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
29
use craft\events\RegisterComponentTypesEvent;
0 ignored issues
show
Bug introduced by
The type craft\events\RegisterComponentTypesEvent was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
30
use craft\events\RegisterUserPermissionsEvent;
0 ignored issues
show
Bug introduced by
The type craft\events\RegisterUserPermissionsEvent was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
31
use craft\events\RegisterUrlRulesEvent;
0 ignored issues
show
Bug introduced by
The type craft\events\RegisterUrlRulesEvent was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
32
use craft\helpers\UrlHelper;
0 ignored issues
show
Bug introduced by
The type craft\helpers\UrlHelper was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
33
use craft\services\Dashboard;
0 ignored issues
show
Bug introduced by
The type craft\services\Dashboard was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
34
use craft\services\UserPermissions;
0 ignored issues
show
Bug introduced by
The type craft\services\UserPermissions was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
35
use craft\web\Application;
0 ignored issues
show
Bug introduced by
The type craft\web\Application was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
36
use craft\web\twig\variables\CraftVariable;
0 ignored issues
show
Bug introduced by
The type craft\web\twig\variables\CraftVariable was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
37
use craft\web\UrlManager;
0 ignored issues
show
Bug introduced by
The type craft\web\UrlManager was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
38
use craft\web\View;
0 ignored issues
show
Bug introduced by
The type craft\web\View was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
39
40
use yii\base\Event;
0 ignored issues
show
Bug introduced by
The type yii\base\Event was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
41
use yii\base\InvalidConfigException;
0 ignored issues
show
Bug introduced by
The type yii\base\InvalidConfigException was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
42
43
/**
44
 * Class Webperf
45
 *
46
 * @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 indented incorrectly; expected 2 spaces but found 4
Loading history...
47
 * @package   Webperf
0 ignored issues
show
Coding Style introduced by
Tag value indented incorrectly; expected 1 spaces but found 3
Loading history...
48
 * @since     1.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 indented incorrectly; expected 3 spaces but found 5
Loading history...
49
 *
50
 * @property  RecommendationsService  $recommendations
0 ignored issues
show
Coding Style introduced by
Tag value indented incorrectly; expected 1 spaces but found 2
Loading history...
51
 * @property  DataSamplesService      $dataSamples
0 ignored issues
show
Coding Style introduced by
Tag value indented incorrectly; expected 1 spaces but found 2
Loading history...
52
 * @property  ErrorSamplesService     $errorSamples
0 ignored issues
show
Coding Style introduced by
Tag value indented incorrectly; expected 1 spaces but found 2
Loading history...
53
 * @property  BeaconsService          $beacons
0 ignored issues
show
Coding Style introduced by
Tag value indented incorrectly; expected 1 spaces but found 2
Loading history...
54
 * @property  ErrorsTarget            $errorsTarget
0 ignored issues
show
Coding Style introduced by
Tag value indented incorrectly; expected 1 spaces but found 2
Loading history...
55
 * @property  ProfileTarget           $profileTarget
0 ignored issues
show
Coding Style introduced by
Tag value indented incorrectly; expected 1 spaces but found 2
Loading history...
56
 */
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...
57
class Webperf extends Plugin
58
{
59
    // Constants
60
    // =========================================================================
61
62
    const RECOMMENDATIONS_CACHE_KEY = 'webperf-recommendations';
63
    const RECOMMENDATIONS_CACHE_DURATION = 60;
64
65
    const ERRORS_CACHE_KEY = 'webperf-errors';
66
    const ERRORS_CACHE_DURATION = 60;
67
68
    // Static Properties
69
    // =========================================================================
70
71
    /**
0 ignored issues
show
Coding Style introduced by
Missing short description in doc comment
Loading history...
72
     * @var Webperf
73
     */
74
    public static $plugin;
75
76
    /**
0 ignored issues
show
Coding Style introduced by
Missing short description in doc comment
Loading history...
77
     * @var Settings
78
     */
79
    public static $settings;
80
81
    /**
0 ignored issues
show
Coding Style introduced by
Missing short description in doc comment
Loading history...
82
     * @var int|null
83
     */
84
    public static $requestUuid;
85
86
    /**
0 ignored issues
show
Coding Style introduced by
Missing short description in doc comment
Loading history...
87
     * @var int|null
88
     */
89
    public static $requestUrl;
90
91
    /**
0 ignored issues
show
Coding Style introduced by
Missing short description in doc comment
Loading history...
92
     * @var bool
93
     */
94
    public static $beaconIncluded = false;
95
96
    /**
0 ignored issues
show
Coding Style introduced by
Missing short description in doc comment
Loading history...
97
     * @var string
98
     */
99
    public static $renderType = 'html';
100
101
    // Public Properties
102
    // =========================================================================
103
104
    /**
0 ignored issues
show
Coding Style introduced by
Missing short description in doc comment
Loading history...
105
     * @var string
106
     */
107
    public $schemaVersion = '1.0.0';
108
109
    // Public Methods
110
    // =========================================================================
111
112
    /**
0 ignored issues
show
Coding Style introduced by
Missing short description in doc comment
Loading history...
113
     * @inheritdoc
114
     */
0 ignored issues
show
Coding Style introduced by
Missing @return tag in function comment
Loading history...
115
    public function init()
116
    {
117
        parent::init();
118
        // Initialize properties
119
        self::$plugin = $this;
120
        self::$settings = $this->getSettings();
121
        try {
122
            self::$requestUuid = random_int(0, PHP_INT_MAX);
123
        } catch (\Exception $e) {
124
            self::$requestUuid = null;
125
        }
126
        $this->name = self::$settings->pluginName;
0 ignored issues
show
Bug Best Practice introduced by
The property name does not exist. Although not strictly required by PHP, it is generally a best practice to declare properties explicitly.
Loading history...
127
        // Handle any console commands
128
        $request = Craft::$app->getRequest();
129
        if ($request->getIsConsoleRequest()) {
130
            $this->controllerNamespace = 'nystudio107\webperf\console\controllers';
0 ignored issues
show
Bug Best Practice introduced by
The property controllerNamespace does not exist. Although not strictly required by PHP, it is generally a best practice to declare properties explicitly.
Loading history...
131
        }
132
        // Add in our components
133
        $this->addComponents();
134
        // Install event listeners
135
        $this->installEventListeners();
136
        // Load that we've loaded
137
        Craft::info(
138
            Craft::t(
139
                'webperf',
140
                '{name} plugin loaded',
141
                ['name' => $this->name]
142
            ),
143
            __METHOD__
144
        );
145
    }
146
147
    /**
0 ignored issues
show
Coding Style introduced by
Missing short description in doc comment
Loading history...
148
     * @inheritdoc
149
     */
0 ignored issues
show
Coding Style introduced by
Missing @return tag in function comment
Loading history...
150
    public function getSettingsResponse()
151
    {
152
        // Just redirect to the plugin settings page
153
        Craft::$app->getResponse()->redirect(UrlHelper::cpUrl('webperf/settings'));
154
    }
155
156
    /**
0 ignored issues
show
Coding Style introduced by
Missing short description in doc comment
Loading history...
157
     * @inheritdoc
158
     */
0 ignored issues
show
Coding Style introduced by
Missing @return tag in function comment
Loading history...
159
    public function getCpNavItem()
160
    {
161
        $subNavs = [];
162
        $navItem = parent::getCpNavItem();
163
        $recommendations = $this->getRecommendationsCount();
164
        $errors = $this->getErrorsCount();
165
        $navItem['badgeCount'] = $errors.$recommendations;
166
        $currentUser = Craft::$app->getUser()->getIdentity();
167
        // Only show sub-navs the user has permission to view
168
        if ($currentUser->can('webperf:dashboard')) {
169
            $subNavs['dashboard'] = [
170
                'label' => 'Dashboard',
171
                'url' => 'webperf/dashboard',
172
            ];
173
        }
174
        if ($currentUser->can('webperf:performance')) {
175
            $subNavs['performance'] = [
176
                'label' => 'Performance',
177
                'url' => 'webperf/performance',
178
            ];
179
        }
180
        if ($currentUser->can('webperf:errors')) {
181
            $subNavs['errors'] = [
182
                'label' => 'Errors',
183
                'url' => 'webperf/errors',
184
                'badge' => $errors,
185
            ];
186
        }
187
        if ($currentUser->can('webperf:settings')) {
188
            $subNavs['settings'] = [
189
                'label' => 'Settings',
190
                'url' => 'webperf/settings',
191
            ];
192
        }
193
        $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...
194
            'subnav' => $subNavs,
195
        ]);
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...
196
197
        return $navItem;
198
    }
199
200
    // Protected Methods
201
    // =========================================================================
202
203
    /**
204
     * Add in our components
205
     */
0 ignored issues
show
Coding Style introduced by
Missing @return tag in function comment
Loading history...
206
    protected function addComponents()
207
    {
208
        $request = Craft::$app->getRequest();
209
        if ($request->getIsSiteRequest() && !$request->getIsConsoleRequest()) {
210
            $this->setRequestUrl();
211
            try {
212
                $uri = $request->getPathInfo();
213
            } catch (InvalidConfigException $e) {
214
                $uri = '';
215
            }
216
            // Ignore our own controllers
217
            if (self::$settings->includeCraftProfiling && strpos($uri, 'webperf/') === false) {
218
                // Add in the ProfileTarget component
219
                try {
220
                    $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...
221
                        'class' => ProfileTarget::class,
222
                        'levels' => ['profile'],
223
                        'categories' => [],
224
                        'logVars' => [],
225
                        'except' => [],
226
                    ]);
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...
227
                } catch (InvalidConfigException $e) {
228
                    Craft::error($e->getMessage(), __METHOD__);
229
                }
230
                // Attach our log target
231
                Craft::$app->getLog()->targets['webperf-profile'] = $this->profileTarget;
232
                // Add in the ErrorsTarget component
233
                $except = [];
234
                // If devMode is on, exclude errors/warnings from `seomatic`
235
                if (Craft::$app->getConfig()->getGeneral()->devMode) {
236
                    $except = ['nystudio107\seomatic\*'];
237
                }
238
                $levels = ['error'];
239
                if (self::$settings->includeCraftWarnings) {
240
                    $levels[] = 'warning';
241
                }
242
                try {
243
                    $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...
244
                        'class' => ErrorsTarget::class,
245
                        'levels' => $levels,
246
                        'categories' => [],
247
                        'logVars' => [],
248
                        'except' => $except,
249
                    ]);
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...
250
                } catch (InvalidConfigException $e) {
251
                    Craft::error($e->getMessage(), __METHOD__);
252
                }
253
                // Attach our log target
254
                Craft::$app->getLog()->targets['webperf-errors'] = $this->errorsTarget;
255
            }
256
        }
257
    }
258
259
    /**
260
     * Set the request URL
261
     *
262
     * @param bool $force
0 ignored issues
show
Coding Style introduced by
Missing parameter comment
Loading history...
263
     */
0 ignored issues
show
Coding Style introduced by
Missing @return tag in function comment
Loading history...
264
    protected function setRequestUrl(bool $force = false)
265
    {
266
        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...
267
        if (!self::$settings->includeBeacon || $force || self::$settings->staticCachedSite) {
268
            $request = Craft::$app->getRequest();
269
            self::$requestUrl = UrlHelper::stripQueryString(
270
                urldecode($request->getAbsoluteUrl())
271
            );
272
        }
273
    }
274
275
    /**
276
     * Install our event listeners.
277
     */
0 ignored issues
show
Coding Style introduced by
Missing @return tag in function comment
Loading history...
278
    protected function installEventListeners()
279
    {
280
        $request = Craft::$app->getRequest();
281
        // Add in our event listeners that are needed for every request
282
        $this->installGlobalEventListeners();
283
        // Install only for non-console site requests
284
        if ($request->getIsSiteRequest() && !$request->getIsConsoleRequest()) {
285
            $this->installSiteEventListeners();
286
        }
287
        // Install only for non-console Control Panel requests
288
        if ($request->getIsCpRequest() && !$request->getIsConsoleRequest()) {
289
            $this->installCpEventListeners();
290
        }
291
        // Handler: EVENT_AFTER_INSTALL_PLUGIN
292
        Event::on(
293
            Plugins::class,
294
            Plugins::EVENT_AFTER_INSTALL_PLUGIN,
295
            function (PluginEvent $event) {
296
                if ($event->plugin === $this) {
297
                    // Invalidate our caches after we've been installed
298
                    $this->clearAllCaches();
299
                    // Send them to our welcome screen
300
                    $request = Craft::$app->getRequest();
301
                    if ($request->isCpRequest) {
302
                        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...
303
                            'webperf/dashboard',
304
                            [
305
                                'showWelcome' => true,
306
                            ]
307
                        ))->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...
308
                    }
309
                }
310
            }
311
        );
312
    }
313
314
    /**
315
     * Install global event listeners for all request types
316
     */
0 ignored issues
show
Coding Style introduced by
Missing @return tag in function comment
Loading history...
317
    protected function installGlobalEventListeners()
318
    {
319
        // Handler: CraftVariable::EVENT_INIT
320
        Event::on(
321
            CraftVariable::class,
322
            CraftVariable::EVENT_INIT,
323
            function (Event $event) {
324
                /** @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...
325
                $variable = $event->sender;
326
                $variable->set('webperf', WebperfVariable::class);
327
            }
328
        );
329
        // Handler: Plugins::EVENT_AFTER_LOAD_PLUGINS
330
        Event::on(
331
            Plugins::class,
332
            Plugins::EVENT_AFTER_LOAD_PLUGINS,
333
            function () {
334
                // Install these only after all other plugins have loaded
335
                $request = Craft::$app->getRequest();
336
                // Only respond to non-console site requests
337
                if ($request->getIsSiteRequest() && !$request->getIsConsoleRequest()) {
338
                    $this->handleSiteRequest();
339
                }
340
                // Respond to Control Panel requests
341
                if ($request->getIsCpRequest() && !$request->getIsConsoleRequest()) {
342
                    $this->handleAdminCpRequest();
343
                }
344
            }
345
        );
346
    }
347
348
    /**
349
     * Install site event listeners for site requests only
350
     */
0 ignored issues
show
Coding Style introduced by
Missing @return tag in function comment
Loading history...
351
    protected function installSiteEventListeners()
352
    {
353
        // Handler: UrlManager::EVENT_REGISTER_SITE_URL_RULES
354
        Event::on(
355
            UrlManager::class,
356
            UrlManager::EVENT_REGISTER_SITE_URL_RULES,
357
            function (RegisterUrlRulesEvent $event) {
358
                Craft::debug(
359
                    'UrlManager::EVENT_REGISTER_SITE_URL_RULES',
360
                    __METHOD__
361
                );
362
                // Register our Control Panel routes
363
                $event->rules = array_merge(
364
                    $event->rules,
365
                    $this->customFrontendRoutes()
366
                );
367
            }
368
        );
369
    }
370
371
    /**
372
     * Install site event listeners for Control Panel requests only
373
     */
0 ignored issues
show
Coding Style introduced by
Missing @return tag in function comment
Loading history...
374
    protected function installCpEventListeners()
375
    {
376
        // Handler: UrlManager::EVENT_REGISTER_CP_URL_RULES
377
        Event::on(
378
            UrlManager::class,
379
            UrlManager::EVENT_REGISTER_CP_URL_RULES,
380
            function (RegisterUrlRulesEvent $event) {
381
                Craft::debug(
382
                    'UrlManager::EVENT_REGISTER_CP_URL_RULES',
383
                    __METHOD__
384
                );
385
                // Register our Control Panel routes
386
                $event->rules = array_merge(
387
                    $event->rules,
388
                    $this->customAdminCpRoutes()
389
                );
390
            }
391
        );
392
        // Handler: Dashboard::EVENT_REGISTER_WIDGET_TYPES
393
        Event::on(
394
            Dashboard::class,
395
            Dashboard::EVENT_REGISTER_WIDGET_TYPES,
396
            function (RegisterComponentTypesEvent $event) {
397
                $event->types[] = MetricsWidget::class;
398
            }
399
        );
400
        // Handler: UserPermissions::EVENT_REGISTER_PERMISSIONS
401
        Event::on(
402
            UserPermissions::class,
403
            UserPermissions::EVENT_REGISTER_PERMISSIONS,
404
            function (RegisterUserPermissionsEvent $event) {
405
                Craft::debug(
406
                    'UserPermissions::EVENT_REGISTER_PERMISSIONS',
407
                    __METHOD__
408
                );
409
                // Register our custom permissions
410
                $event->permissions[Craft::t('webperf', 'Webperf')] = $this->customAdminCpPermissions();
411
            }
412
        );
413
    }
414
415
    /**
416
     * Handle site requests.  We do it only after we receive the event
417
     * EVENT_AFTER_LOAD_PLUGINS so that any pending db migrations can be run
418
     * before our event listeners kick in
419
     */
0 ignored issues
show
Coding Style introduced by
Missing @return tag in function comment
Loading history...
420
    protected function handleSiteRequest()
421
    {
422
        // Don't include the beacon for response codes >= 400
423
        $response = Craft::$app->getResponse();
424
        if ($response->statusCode < 400) {
425
            // Handler: View::EVENT_END_PAGE
426
            Event::on(
427
                View::class,
428
                View::EVENT_END_PAGE,
429
                function () {
430
                    Craft::debug(
431
                        'View::EVENT_END_PAGE',
432
                        __METHOD__
433
                    );
434
                    $view = Craft::$app->getView();
435
                    // The page is done rendering, include our beacon
436
                    if (Webperf::$settings->includeBeacon && $view->getIsRenderingPageTemplate()) {
437
                        switch (self::$renderType) {
438
                            case 'html':
0 ignored issues
show
Coding Style introduced by
Line indented incorrectly; expected 24 spaces, found 28
Loading history...
439
                                Webperf::$plugin->beacons->includeHtmlBeacon();
440
                                self::$beaconIncluded = true;
441
                                break;
442
                            case 'amp-html':
0 ignored issues
show
Coding Style introduced by
Line indented incorrectly; expected 24 spaces, found 28
Loading history...
443
                                Webperf::$plugin->beacons->includeAmpHtmlScript();
444
                                break;
445
                        }
446
                    }
447
                }
448
            );
449
            // Handler: View::EVENT_END_BODY
450
            Event::on(
451
                View::class,
452
                View::EVENT_END_BODY,
453
                function () {
454
                    Craft::debug(
455
                        'View::EVENT_END_BODY',
456
                        __METHOD__
457
                    );
458
                    $view = Craft::$app->getView();
459
                    // The page is done rendering, include our beacon
460
                    if (Webperf::$settings->includeBeacon && $view->getIsRenderingPageTemplate()) {
461
                        switch (self::$renderType) {
462
                            case 'html':
0 ignored issues
show
Coding Style introduced by
Line indented incorrectly; expected 24 spaces, found 28
Loading history...
463
                                break;
464
                            case 'amp-html':
0 ignored issues
show
Coding Style introduced by
Line indented incorrectly; expected 24 spaces, found 28
Loading history...
465
                                Webperf::$plugin->beacons->includeAmpHtmlBeacon();
466
                                self::$beaconIncluded = true;
467
                                break;
468
                        }
469
                    }
470
                }
471
            );
472
            // Handler: Application::EVENT_AFTER_REQUEST
473
            Event::on(
474
                Application::class,
475
                Application::EVENT_AFTER_REQUEST,
476
                function () {
477
                    Craft::debug(
478
                        'Application::EVENT_AFTER_REQUEST',
479
                        __METHOD__
480
                    );
481
                    // If the beacon wasn't included, allow for the Craft timings
482
                    if (!self::$beaconIncluded) {
483
                        $this->setRequestUrl(true);
484
                    }
485
                }
486
            );
487
        }
488
    }
489
490
    /**
491
     * Handle Control Panel requests. We do it only after we receive the event
492
     * EVENT_AFTER_LOAD_PLUGINS so that any pending db migrations can be run
493
     * before our event listeners kick in
494
     */
0 ignored issues
show
Coding Style introduced by
Missing @return tag in function comment
Loading history...
495
    protected function handleAdminCpRequest()
496
    {
497
    }
498
499
    /**
500
     * Clear all the caches!
501
     */
0 ignored issues
show
Coding Style introduced by
Missing @return tag in function comment
Loading history...
502
    public function clearAllCaches()
503
    {
504
    }
505
506
    /**
0 ignored issues
show
Coding Style introduced by
Missing short description in doc comment
Loading history...
507
     * @inheritdoc
508
     */
0 ignored issues
show
Coding Style introduced by
Missing @return tag in function comment
Loading history...
509
    protected function createSettingsModel()
510
    {
511
        return new Settings();
512
    }
513
514
    /**
0 ignored issues
show
Coding Style introduced by
Missing short description in doc comment
Loading history...
515
     * @inheritdoc
516
     */
0 ignored issues
show
Coding Style introduced by
Missing @return tag in function comment
Loading history...
517
    protected function settingsHtml(): string
518
    {
519
        return Craft::$app->view->renderTemplate(
520
            'webperf/settings',
521
            [
522
                'settings' => $this->getSettings()
523
            ]
524
        );
525
    }
526
527
    /**
528
     * Return the custom frontend routes
529
     *
530
     * @return array
531
     */
532
    protected function customFrontendRoutes(): array
533
    {
534
        return [
535
            // Beacon
536
            '/webperf/metrics/beacon' => 'webperf/metrics/beacon',
537
            // Render
538
            '/webperf/render/amp-iframe' => 'webperf/render/amp-iframe',
539
            // Tables
540
            '/webperf/tables/pages-index' => 'webperf/tables/pages-index',
541
            '/webperf/tables/page-detail' => 'webperf/tables/page-detail',
542
            '/webperf/tables/errors-index' => 'webperf/tables/errors-index',
543
            '/webperf/tables/errors-detail' => 'webperf/tables/errors-detail',
544
            // Charts
545
            '/webperf/charts/dashboard-stats-average/<column:{handle}>'
546
            => 'webperf/charts/dashboard-stats-average',
547
            '/webperf/charts/dashboard-stats-average/<column:{handle}>/<siteId:\d+>'
548
            => 'webperf/charts/dashboard-stats-average',
549
550
            '/webperf/charts/dashboard-slowest-pages/<column:{handle}>/<limit:\d+>'
551
            => 'webperf/charts/dashboard-slowest-pages',
552
            '/webperf/charts/dashboard-slowest-pages/<column:{handle}>/<limit:\d+>/<siteId:\d+>'
553
            => 'webperf/charts/dashboard-slowest-pages',
554
555
            '/webperf/charts/pages-area-chart'
556
            => 'webperf/charts/pages-area-chart',
557
            '/webperf/charts/pages-area-chart/<siteId:\d+>'
558
            => 'webperf/charts/pages-area-chart',
559
560
            '/webperf/charts/errors-area-chart'
561
            => 'webperf/charts/errors-area-chart',
562
            '/webperf/charts/errors-area-chart/<siteId:\d+>'
563
            => 'webperf/charts/errors-area-chart',
564
565
            '/webperf/recommendations/list'
566
            => 'webperf/recommendations/list',
567
            '/webperf/recommendations/list/<siteId:\d+>'
568
            => 'webperf/recommendations/list',
569
570
            '/webperf/charts/widget/<days>' => 'webperf/charts/widget',
571
        ];
572
    }
573
    /**
574
     * Return the custom Control Panel routes
575
     *
576
     * @return array
577
     */
578
    protected function customAdminCpRoutes(): array
579
    {
580
        return [
581
            'webperf' => 'webperf/sections/dashboard',
582
            'webperf/dashboard' => 'webperf/sections/dashboard',
583
            'webperf/dashboard/<siteHandle:{handle}>' => 'webperf/sections/dashboard',
584
585
            'webperf/performance' => 'webperf/sections/pages-index',
586
            'webperf/performance/<siteHandle:{handle}>' => 'webperf/sections/pages-index',
587
588
            'webperf/performance/page-detail' => 'webperf/sections/page-detail',
589
            'webperf/performance/page-detail/<siteHandle:{handle}>' => 'webperf/sections/page-detail',
590
591
            'webperf/errors' => 'webperf/sections/errors-index',
592
            'webperf/errors/<siteHandle:{handle}>' => 'webperf/sections/errors-index',
593
594
            'webperf/errors/page-detail' => 'webperf/sections/errors-detail',
595
            'webperf/errors/page-detail/<siteHandle:{handle}>' => 'webperf/errors/page-detail',
596
597
            'webperf/settings' => 'webperf/settings/plugin-settings',
598
        ];
599
    }
600
601
    /**
602
     * Returns the custom Control Panel user permissions.
603
     *
604
     * @return array
605
     */
606
    protected function customAdminCpPermissions(): array
607
    {
608
        return [
609
            'webperf:dashboard' => [
610
                'label' => Craft::t('webperf', 'Dashboard'),
611
            ],
612
            'webperf:performance' => [
613
                'label' => Craft::t('webperf', 'Performance'),
614
            ],
615
            'webperf:performance-detail' => [
616
                'label' => Craft::t('webperf', 'Performance Detail'),
617
            ],
618
            'webperf:delete-data-samples' => [
619
                'label' => Craft::t('webperf', 'Delete Data Samples'),
620
            ],
621
            'webperf:errors' => [
622
                'label' => Craft::t('webperf', 'Errors'),
623
            ],
624
            'webperf:errors-detail' => [
625
                'label' => Craft::t('webperf', 'Errors Detail'),
626
            ],
627
            'webperf:delete-error-samples' => [
628
                'label' => Craft::t('webperf', 'Delete Error Samples'),
629
            ],
630
            'webperf:recommendations' => [
631
                'label' => Craft::t('webperf', 'Recommendations'),
632
            ],
633
            'webperf:settings' => [
634
                'label' => Craft::t('webperf', 'Settings'),
635
            ],
636
        ];
637
    }
638
639
    /**
640
     * Get a string value with the number of recommendations
641
     *
642
     * @return string
643
     */
644
    protected function getRecommendationsCount(): string
645
    {
646
        $cache = Craft::$app->getCache();
647
        // See if there are any recommendations to add as a badge
648
        $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...
649
            $data = [];
650
            $now = new \DateTime();
651
            $end = $now->format('Y-m-d');
652
            $start = $now->modify('-30 days')->format('Y-m-d');
653
            $stats = Webperf::$plugin->recommendations->data('', $start, $end);
654
            if (!empty($stats)) {
655
                $recSample = new RecommendationDataSample($stats);
656
                $data = Webperf::$plugin->recommendations->list($recSample);
657
            }
658
659
            return count($data);
660
        }, 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...
661
662
        if (!$recommendations) {
663
            $recommendations = '';
664
        }
665
666
        return (string)$recommendations;
667
    }
668
669
    /**
670
     * Get a string value with the number of errors
671
     *
672
     * @return string
673
     */
674
    protected function getErrorsCount(): string
675
    {
676
        $cache = Craft::$app->getCache();
677
        // See if there are any recommendations to add as a badge
678
        $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...
679
            $now = new \DateTime();
680
            $end = $now->format('Y-m-d');
681
            $start = $now->modify('-30 days')->format('Y-m-d');
682
            
683
            return Webperf::$plugin->errorSamples->totalErrorSamplesRange(0, $start, $end);
684
        }, 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...
685
686
        if (!$errors) {
687
            $errors = '';
688
        } else {
689
            $errors = '⚠';
690
        }
691
692
        return (string)$errors;
693
    }
694
}
695