Passed
Push — v1 ( fd0e2d...def257 )
by Andrew
09:12 queued 05:49
created

Webperf::excludeUri()   A

Complexity

Conditions 3
Paths 3

Size

Total Lines 11
Code Lines 6

Duplication

Lines 0
Ratio 0 %

Importance

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