Passed
Push — v1 ( 44bb2f...f1a3ae )
by Andrew
11:42 queued 08:28
created

Webperf::renderSidebar()   A

Complexity

Conditions 5
Paths 5

Size

Total Lines 29
Code Lines 20

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
eloc 20
dl 0
loc 29
rs 9.2888
c 0
b 0
f 0
cc 5
nc 5
nop 1
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
        $currentUser = Craft::$app->getUser()->getIdentity();
507
        // Only show sub-navs the user has permission to view
508
        if (self::$settings->displaySidebar && $currentUser->can('webperf:sidebar')) {
509
            $view = Craft::$app->getView();
510
            // Entries sidebar
511
            $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...
512
                /** @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...
513
                $element = $context['entry'] ?? null;
514
515
                return $this->renderSidebar($element);
516
            });
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...
517
            // Category Groups sidebar
518
            $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...
519
                /** @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...
520
                $element = $context['category'] ?? null;
521
522
                return $this->renderSidebar($element);
523
            });
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...
524
            // Commerce Product Types sidebar
525
            $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...
526
                /** @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...
527
                $element = $context['product'] ?? null;
528
529
                return $this->renderSidebar($element);
530
            });
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...
531
        }
532
    }
533
534
    /**
0 ignored issues
show
Coding Style introduced by
Missing short description in doc comment
Loading history...
535
     * @param Element $element
0 ignored issues
show
Coding Style introduced by
Missing parameter comment
Loading history...
536
     *
537
     * @return string
538
     */
539
    protected function renderSidebar(Element $element): string
540
    {
541
        $html = '';
542
        if ($element !== null && $element->url !== null) {
543
            $view = Craft::$app->getView();
544
            try {
545
                $view->registerAssetBundle(WebperfAsset::class);
546
            } catch (InvalidConfigException $e) {
0 ignored issues
show
Coding Style Comprehensibility introduced by
Consider adding a comment why this CATCH block is empty.
Loading history...
547
            }
548
            try {
549
                $now = new \DateTime();
550
            } catch (\Exception $e) {
551
                return $html;
552
            }
553
            $end = $now->format('Y-m-d');
554
            $start = $now->modify('-30 days')->format('Y-m-d');
555
            $html .= PluginTemplate::renderPluginTemplate(
556
                '_sidebars/webperf-sidebar.twig',
557
                [
558
                    'settings' => self::$settings,
559
                    'pageUrl' => $element->url,
560
                    'start' => $start,
561
                    'end' => $end,
562
                    'currentSiteId' => $element->siteId ?? 0,
563
                ]
564
            );
565
        }
566
567
        return $html;
568
    }
569
570
    /**
571
     * Clear all the caches!
572
     */
0 ignored issues
show
Coding Style introduced by
Missing @return tag in function comment
Loading history...
573
    public function clearAllCaches()
574
    {
575
    }
576
577
    /**
0 ignored issues
show
Coding Style introduced by
Missing short description in doc comment
Loading history...
578
     * @inheritdoc
579
     */
0 ignored issues
show
Coding Style introduced by
Missing @return tag in function comment
Loading history...
580
    protected function createSettingsModel()
581
    {
582
        return new Settings();
583
    }
584
585
    /**
0 ignored issues
show
Coding Style introduced by
Missing short description in doc comment
Loading history...
586
     * @inheritdoc
587
     */
0 ignored issues
show
Coding Style introduced by
Missing @return tag in function comment
Loading history...
588
    protected function settingsHtml(): string
589
    {
590
        return Craft::$app->view->renderTemplate(
591
            'webperf/settings',
592
            [
593
                'settings' => $this->getSettings()
594
            ]
595
        );
596
    }
597
598
    /**
599
     * Return the custom frontend routes
600
     *
601
     * @return array
602
     */
603
    protected function customFrontendRoutes(): array
604
    {
605
        return [
606
            // Beacon
607
            '/webperf/metrics/beacon' => 'webperf/metrics/beacon',
608
            // Render
609
            '/webperf/render/amp-iframe' => 'webperf/render/amp-iframe',
610
            // Tables
611
            '/webperf/tables/pages-index' => 'webperf/tables/pages-index',
612
            '/webperf/tables/page-detail' => 'webperf/tables/page-detail',
613
            '/webperf/tables/errors-index' => 'webperf/tables/errors-index',
614
            '/webperf/tables/errors-detail' => 'webperf/tables/errors-detail',
615
            // Charts
616
            '/webperf/charts/dashboard-stats-average/<column:{handle}>'
617
            => 'webperf/charts/dashboard-stats-average',
618
            '/webperf/charts/dashboard-stats-average/<column:{handle}>/<siteId:\d+>'
619
            => 'webperf/charts/dashboard-stats-average',
620
621
            '/webperf/charts/dashboard-slowest-pages/<column:{handle}>/<limit:\d+>'
622
            => 'webperf/charts/dashboard-slowest-pages',
623
            '/webperf/charts/dashboard-slowest-pages/<column:{handle}>/<limit:\d+>/<siteId:\d+>'
624
            => 'webperf/charts/dashboard-slowest-pages',
625
626
            '/webperf/charts/pages-area-chart'
627
            => 'webperf/charts/pages-area-chart',
628
            '/webperf/charts/pages-area-chart/<siteId:\d+>'
629
            => 'webperf/charts/pages-area-chart',
630
631
            '/webperf/charts/errors-area-chart'
632
            => 'webperf/charts/errors-area-chart',
633
            '/webperf/charts/errors-area-chart/<siteId:\d+>'
634
            => 'webperf/charts/errors-area-chart',
635
636
            '/webperf/recommendations/list'
637
            => 'webperf/recommendations/list',
638
            '/webperf/recommendations/list/<siteId:\d+>'
639
            => 'webperf/recommendations/list',
640
641
            '/webperf/charts/widget/<days>' => 'webperf/charts/widget',
642
        ];
643
    }
644
    /**
645
     * Return the custom Control Panel routes
646
     *
647
     * @return array
648
     */
649
    protected function customAdminCpRoutes(): array
650
    {
651
        return [
652
            'webperf' => 'webperf/sections/dashboard',
653
            'webperf/dashboard' => 'webperf/sections/dashboard',
654
            'webperf/dashboard/<siteHandle:{handle}>' => 'webperf/sections/dashboard',
655
656
            'webperf/performance' => 'webperf/sections/pages-index',
657
            'webperf/performance/<siteHandle:{handle}>' => 'webperf/sections/pages-index',
658
659
            'webperf/performance/page-detail' => 'webperf/sections/page-detail',
660
            'webperf/performance/page-detail/<siteHandle:{handle}>' => 'webperf/sections/page-detail',
661
662
            'webperf/errors' => 'webperf/sections/errors-index',
663
            'webperf/errors/<siteHandle:{handle}>' => 'webperf/sections/errors-index',
664
665
            'webperf/errors/page-detail' => 'webperf/sections/errors-detail',
666
            'webperf/errors/page-detail/<siteHandle:{handle}>' => 'webperf/errors/page-detail',
667
668
            'webperf/alerts' => 'webperf/sections/alerts',
669
            'webperf/alerts/<siteHandle:{handle}>' => 'webperf/sections/alerts',
670
671
            'webperf/settings' => 'webperf/settings/plugin-settings',
672
        ];
673
    }
674
675
    /**
676
     * Returns the custom Control Panel user permissions.
677
     *
678
     * @return array
679
     */
680
    protected function customAdminCpPermissions(): array
681
    {
682
        return [
683
            'webperf:dashboard' => [
684
                'label' => Craft::t('webperf', 'Dashboard'),
685
            ],
686
            'webperf:performance' => [
687
                'label' => Craft::t('webperf', 'Performance'),
688
                'nested' => [
689
                    'webperf:performance-detail' => [
690
                        'label' => Craft::t('webperf', 'Performance Detail'),
691
                    ],
692
                    'webperf:delete-data-samples' => [
693
                        'label' => Craft::t('webperf', 'Delete Data Samples'),
694
                    ],
695
                ],
696
            ],
697
            'webperf:errors' => [
698
                'label' => Craft::t('webperf', 'Errors'),
699
                'nested' => [
700
                    'webperf:errors-detail' => [
701
                        'label' => Craft::t('webperf', 'Errors Detail'),
702
                    ],
703
                    'webperf:delete-error-samples' => [
704
                        'label' => Craft::t('webperf', 'Delete Error Samples'),
705
                    ],
706
                ],
707
            ],
708
            'webperf:alerts' => [
709
                'label' => Craft::t('webperf', 'Alerts'),
710
            ],
711
            'webperf:recommendations' => [
712
                'label' => Craft::t('webperf', 'Recommendations'),
713
            ],
714
            'webperf:sidebar' => [
715
                'label' => Craft::t('webperf', 'Performance Sidebar'),
716
            ],
717
            'webperf:settings' => [
718
                'label' => Craft::t('webperf', 'Settings'),
719
            ],
720
        ];
721
    }
722
723
    /**
724
     * Get a string value with the number of recommendations
725
     *
726
     * @return string
727
     */
728
    protected function getRecommendationsCount(): string
729
    {
730
        $cache = Craft::$app->getCache();
731
        // See if there are any recommendations to add as a badge
732
        $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...
733
            $data = [];
734
            $now = new \DateTime();
735
            $end = $now->format('Y-m-d');
736
            $start = $now->modify('-30 days')->format('Y-m-d');
737
            $stats = Webperf::$plugin->recommendations->data('', $start, $end);
738
            if (!empty($stats)) {
739
                $recSample = new RecommendationDataSample($stats);
740
                $data = Webperf::$plugin->recommendations->list($recSample);
741
            }
742
743
            return count($data);
744
        }, 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...
745
746
        if (!$recommendations) {
747
            $recommendations = '';
748
        }
749
750
        return (string)$recommendations;
751
    }
752
753
    /**
754
     * Get a string value with the number of errors
755
     *
756
     * @return string
757
     */
758
    protected function getErrorsCount(): string
759
    {
760
        $cache = Craft::$app->getCache();
761
        // See if there are any recommendations to add as a badge
762
        $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...
763
            $now = new \DateTime();
764
            $end = $now->format('Y-m-d');
765
            $start = $now->modify('-30 days')->format('Y-m-d');
766
767
            return Webperf::$plugin->errorSamples->totalErrorSamplesRange(0, $start, $end);
768
        }, 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...
769
770
        if (!$errors) {
771
            $errors = '';
772
        } else {
773
            $errors = '⚠';
774
        }
775
776
        return (string)$errors;
777
    }
778
}
779