Passed
Push — develop ( 44bb2f...63a776 )
by Andrew
03:35
created

Webperf::installCpEventListeners()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 37
Code Lines 23

Duplication

Lines 0
Ratio 0 %

Importance

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