Passed
Push — develop ( 2cb041...56d118 )
by Andrew
02:49
created

TablesController::actionPageDetail()   D

Complexity

Conditions 19
Paths 63

Size

Total Lines 124
Code Lines 80

Duplication

Lines 0
Ratio 0 %

Importance

Changes 12
Bugs 0 Features 0
Metric Value
eloc 80
c 12
b 0
f 0
dl 0
loc 124
rs 4.5166
cc 19
nc 63
nop 8

How to fix   Long Method    Complexity    Many Parameters   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

Many Parameters

Methods with many parameters are not only hard to understand, but their parameters also often become inconsistent when you need more, or different data.

There are several approaches to avoid long parameter lists:

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\controllers;
12
13
use nystudio107\webperf\helpers\Permission as PermissionHelper;
14
15
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...
16
use craft\db\Query;
0 ignored issues
show
Bug introduced by
The type craft\db\Query 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...
17
use craft\helpers\DateTimeHelper;
0 ignored issues
show
Bug introduced by
The type craft\helpers\DateTimeHelper 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...
18
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...
19
use craft\web\Controller;
0 ignored issues
show
Bug introduced by
The type craft\web\Controller 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...
20
21
use yii\web\BadRequestHttpException;
0 ignored issues
show
Bug introduced by
The type yii\web\BadRequestHttpException 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...
22
use yii\web\ForbiddenHttpException;
0 ignored issues
show
Bug introduced by
The type yii\web\ForbiddenHttpException 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...
23
use yii\web\Response;
0 ignored issues
show
Bug introduced by
The type yii\web\Response 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...
24
25
/**
0 ignored issues
show
Coding Style introduced by
Missing short description in doc comment
Loading history...
26
 * @author    nystudio107
0 ignored issues
show
Coding Style introduced by
The tag in position 1 should be the @package tag
Loading history...
Coding Style introduced by
Content of the @author tag must be in the form "Display Name <[email protected]>"
Loading history...
Coding Style introduced by
Tag value for @author tag indented incorrectly; expected 2 spaces but found 4
Loading history...
27
 * @package   Webperf
0 ignored issues
show
Coding Style introduced by
Tag value for @package tag indented incorrectly; expected 1 spaces but found 3
Loading history...
28
 * @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 for @since tag indented incorrectly; expected 3 spaces but found 5
Loading history...
29
 */
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...
30
class TablesController extends Controller
31
{
32
    // Constants
33
    // =========================================================================
34
35
    const SORT_MAP = [
36
        'DESC' => SORT_DESC,
37
        'ASC' => SORT_ASC,
38
    ];
39
40
    const ALLOWED_PAGE_INDEX_SORT_FIELDS = [
41
        'url',
42
        'pageLoad',
43
        'craftDbCnt',
44
        'craftTwigCnt',
45
        'craftOtherCnt',
46
        'craftTotalMemory',
47
        'cnt',
48
    ];
49
50
    const ALLOWED_PAGE_DETAIL_SORT_FIELDS = [
51
        'dateCreated',
52
        'pageLoad',
53
        'craftDbCnt',
54
        'craftTwigCnt',
55
        'craftOtherCnt',
56
        'craftTotalMemory',
57
        'device',
58
        'os',
59
        'browser',
60
        'countryCode',
61
    ];
62
63
    const ALLOWED_ERRORS_INDEX_SORT_FIELDS = [
64
        'url',
65
        'latestErrorDate',
66
        'craftCount',
67
        'boomerangCount',
68
        'cnt',
69
    ];
70
71
    const ALLOWED_ERRORS_DETAIL_SORT_FIELDS = [
72
        'dateCreated',
73
        'pageErrors',
74
        'device',
75
        'os',
76
        'browser',
77
        'countryCode',
78
    ];
79
80
    // Protected Properties
81
    // =========================================================================
82
83
    /**
0 ignored issues
show
Coding Style introduced by
Missing short description in doc comment
Loading history...
84
     * @var    bool|array
0 ignored issues
show
Coding Style introduced by
Tag value for @var tag indented incorrectly; expected 1 spaces but found 4
Loading history...
85
     */
86
    protected $allowAnonymous = [];
87
88
    // Public Methods
89
    // =========================================================================
90
91
    /**
92
     * Handle requests for the performance index table
93
     *
94
     * @param string $sort
0 ignored issues
show
Coding Style introduced by
Missing parameter comment
Loading history...
Coding Style introduced by
Doc comment for parameter $sort does not match actual variable name $start
Loading history...
95
     * @param int    $page
0 ignored issues
show
Coding Style introduced by
Missing parameter comment
Loading history...
Coding Style introduced by
Doc comment for parameter $page does not match actual variable name $end
Loading history...
96
     * @param int    $per_page
0 ignored issues
show
Coding Style introduced by
Missing parameter comment
Loading history...
Coding Style introduced by
Doc comment for parameter $per_page does not match actual variable name $sort
Loading history...
97
     * @param string $filter
0 ignored issues
show
Coding Style introduced by
Missing parameter comment
Loading history...
Coding Style introduced by
Doc comment for parameter $filter does not match actual variable name $page
Loading history...
98
     * @param string $start
0 ignored issues
show
Coding Style introduced by
Missing parameter comment
Loading history...
Coding Style introduced by
Doc comment for parameter $start does not match actual variable name $per_page
Loading history...
99
     * @param string $end
0 ignored issues
show
Coding Style introduced by
Missing parameter comment
Loading history...
Coding Style introduced by
Doc comment for parameter $end does not match actual variable name $filter
Loading history...
100
     * @param int    $siteId
0 ignored issues
show
Coding Style introduced by
Missing parameter comment
Loading history...
101
     *
102
     * @return Response
103
     * @throws ForbiddenHttpException
104
     * @throws BadRequestHttpException
105
     */
106
    public function actionPagesIndex(
107
        string $start = '',
108
        string $end = '',
109
        string $sort = 'pageLoad|DESC',
110
        int $page = 1,
111
        int $per_page = 20,
112
        $filter = '',
113
        $siteId = 0
114
    ): Response {
115
        PermissionHelper::controllerPermissionCheck('webperf:performance');
116
        $data = [];
117
        $sortField = 'pageLoad';
118
        $sortType = 'DESC';
119
        // Add a day since YYYY-MM-DD is really YYYY-MM-DD 00:00:00
120
        $end = date('Y-m-d', strtotime($end.'+1 day'));
121
        // Figure out the sorting type
122
        if ($sort !== '') {
123
            if (strpos($sort, '|') === false) {
124
                $sortField = $sort;
125
            } else {
126
                list($sortField, $sortType) = explode('|', $sort);
127
            }
128
        }
129
        $sortType = strtoupper($sortType);
130
        $sortType = self::SORT_MAP[$sortType] ?? self::SORT_MAP['DESC'];
131
        // Validate untrusted data
132
        if (!in_array($sortField, self::ALLOWED_PAGE_INDEX_SORT_FIELDS, true)) {
133
            throw new BadRequestHttpException(Craft::t('webperf', 'Invalid sort field specified.'));
134
        }
135
        // Query the db table
136
        $offset = ($page - 1) * $per_page;
137
        $query = (new Query())
138
            ->select([
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...
139
                '[[url]]',
140
                'MIN([[title]]) AS [[title]]',
141
                'COUNT([[url]]) AS [[cnt]]',
142
                'AVG([[pageLoad]]) AS [[pageLoad]]',
143
                'AVG([[domInteractive]]) AS [[domInteractive]]',
144
                'AVG([[firstContentfulPaint]]) AS [[firstContentfulPaint]]',
145
                'AVG([[firstPaint]]) AS [[firstPaint]]',
146
                'AVG([[firstByte]]) AS [[firstByte]]',
147
                'AVG([[connect]]) AS [[connect]]',
148
                'AVG([[dns]]) AS [[dns]]',
149
                'AVG([[craftTotalMs]]) AS [[craftTotalMs]]',
150
                'AVG([[craftDbCnt]]) AS [[craftDbCnt]]',
151
                'AVG([[craftDbMs]]) AS [[craftDbMs]]',
152
                'AVG([[craftTwigCnt]]) AS [[craftTwigCnt]]',
153
                'AVG([[craftTwigMs]]) AS [[craftTwigMs]]',
154
                'AVG([[craftTotalMemory]]) AS [[craftTotalMemory]]',
155
            ])
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...
156
            ->from(['{{%webperf_data_samples}}'])
157
            ->offset($offset)
158
            ->where(['between', 'dateCreated', $start, $end])
0 ignored issues
show
Coding Style introduced by
Space after closing parenthesis of function call prohibited
Loading history...
159
        ;
160
        if ((int)$siteId !== 0) {
161
            $query->andWhere(['siteId' => $siteId]);
162
        }
163
        if ($filter !== '') {
164
            $query
165
                ->andWhere(['like', 'url', $filter])
166
                ->orWhere(['like', 'title', $filter])
0 ignored issues
show
Coding Style introduced by
Space after closing parenthesis of function call prohibited
Loading history...
167
            ;
168
        }
169
        $query
170
            ->orderBy([$sortField => $sortType])
171
            ->groupBy('url')
172
            ->limit($per_page)
0 ignored issues
show
Coding Style introduced by
Space after closing parenthesis of function call prohibited
Loading history...
173
        ;
174
175
        $stats = $query->all();
176
        if ($stats) {
177
            $user = Craft::$app->getUser()->getIdentity();
178
            // Compute the largest page load time
179
            $maxTotalPageLoad = 0;
180
            foreach ($stats as &$stat) {
181
             // Determine the stat type
0 ignored issues
show
Coding Style introduced by
Line indented incorrectly; expected at least 16 spaces, found 13
Loading history...
182
                if (!empty($stat['pageLoad']) && !empty($stat['craftTotalMs'])) {
183
                    $stat['type'] = 'both';
184
                }
185
                if (empty($stat['firstByte'])) {
186
                    $stat['type'] = 'craft';
187
                }
188
                if (empty($stat['craftTotalMs'])) {
189
                    $stat['type'] = 'frontend';
190
                }
191
                if ($stat['pageLoad'] > $maxTotalPageLoad) {
192
                    $maxTotalPageLoad = $stat['pageLoad'];
193
                }
194
            }
195
            // Massage the stats
196
            $index = 1;
197
            foreach ($stats as &$stat) {
198
                $stat['id'] = $index++;
199
                $stat['cnt'] = (int)$stat['cnt'];
200
                $stat['maxTotalPageLoad'] = (int)$maxTotalPageLoad;
201
                // Decode any emojis in the title
202
                if (!empty($stat['title'])) {
203
                    $stat['title'] = html_entity_decode($stat['title'], ENT_NOQUOTES, 'UTF-8');
204
                }
205
                // Set up the appropriate helper links
206
                $stat['deleteLink'] = UrlHelper::actionUrl('webperf/data-samples/delete-samples-by-url', [
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...
207
                    'pageUrl' => $stat['url'],
208
                    'siteId' => $siteId
209
                ]);
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...
210
                $stat['detailPageUrl'] = UrlHelper::cpUrl('webperf/performance/page-detail', [
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
                    'pageUrl' => $stat['url'],
212
                    'siteId' => $siteId,
213
                ]);
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...
214
                // Override based on permissions
215
                if (!$user->can('webperf:delete-data-samples')) {
216
                    $stat['deleteLink'] = '';
217
                }
218
                if (!$user->can('webperf:performance-detail')) {
219
                    $stat['detailPageUrl'] = '';
220
                }
221
            }
222
            // Format the data for the API
223
            $data['data'] = $stats;
224
            $query = (new Query())
225
                ->select(['[[url]]'])
226
                ->from(['{{%webperf_data_samples}}'])
227
                ->groupBy('[[url]]')
228
                ->where(['between', 'dateCreated', $start, $end])
0 ignored issues
show
Coding Style introduced by
Space after closing parenthesis of function call prohibited
Loading history...
229
                ;
230
            if ($filter !== '') {
231
                $query->andWhere(['like', 'url', $filter]);
232
                $query->orWhere(['like', 'title', $filter]);
233
            }
234
            $count = $query->count();
235
            $data['links']['pagination'] = [
236
                'total' => $count,
237
                'per_page' => $per_page,
238
                'current_page' => $page,
239
                'last_page' => ceil($count / $per_page),
240
                'next_page_url' => null,
241
                'prev_page_url' => null,
242
                'from' => $offset + 1,
243
                'to' => $offset + ($count > $per_page ? $per_page : $count),
244
            ];
245
        }
246
247
        return $this->asJson($data);
248
    }
249
250
    /**
251
     * Handle requests for the performance detail table
252
     *
253
     * @param string $sort
0 ignored issues
show
Coding Style introduced by
Missing parameter comment
Loading history...
Coding Style introduced by
Doc comment for parameter $sort does not match actual variable name $start
Loading history...
254
     * @param int    $page
0 ignored issues
show
Coding Style introduced by
Missing parameter comment
Loading history...
Coding Style introduced by
Doc comment for parameter $page does not match actual variable name $end
Loading history...
255
     * @param int    $per_page
0 ignored issues
show
Coding Style introduced by
Missing parameter comment
Loading history...
Coding Style introduced by
Doc comment for parameter $per_page does not match actual variable name $sort
Loading history...
256
     * @param string $filter
0 ignored issues
show
Coding Style introduced by
Missing parameter comment
Loading history...
Coding Style introduced by
Doc comment for parameter $filter does not match actual variable name $page
Loading history...
257
     * @param string $pageUrl
0 ignored issues
show
Coding Style introduced by
Missing parameter comment
Loading history...
Coding Style introduced by
Doc comment for parameter $pageUrl does not match actual variable name $per_page
Loading history...
258
     * @param string $start
0 ignored issues
show
Coding Style introduced by
Missing parameter comment
Loading history...
Coding Style introduced by
Doc comment for parameter $start does not match actual variable name $filter
Loading history...
259
     * @param string $end
0 ignored issues
show
Coding Style introduced by
Missing parameter comment
Loading history...
Coding Style introduced by
Doc comment for parameter $end does not match actual variable name $pageUrl
Loading history...
260
     * @param int    $siteId
0 ignored issues
show
Coding Style introduced by
Missing parameter comment
Loading history...
261
     *
262
     * @return Response
263
     * @throws ForbiddenHttpException
264
     * @throws BadRequestHttpException
265
     */
266
    public function actionPageDetail(
267
        string $start = '',
268
        string $end = '',
269
        string $sort = 'pageLoad|DESC',
270
        int $page = 1,
271
        int $per_page = 20,
272
        $filter = '',
273
        $pageUrl = '',
274
        $siteId = 0
275
    ): Response {
276
        PermissionHelper::controllerPermissionCheck('webperf:performance');
277
        $data = [];
278
        $sortField = 'pageLoad';
279
        $sortType = 'DESC';
280
        // Add a day since YYYY-MM-DD is really YYYY-MM-DD 00:00:00
281
        $end = date('Y-m-d', strtotime($end.'+1 day'));
282
        $pageUrl = urldecode($pageUrl);
283
        // Figure out the sorting type
284
        if ($sort !== '') {
285
            if (strpos($sort, '|') === false) {
286
                $sortField = $sort;
287
            } else {
288
                list($sortField, $sortType) = explode('|', $sort);
289
            }
290
        }
291
        $sortType = strtoupper($sortType);
292
        $sortType = self::SORT_MAP[$sortType] ?? self::SORT_MAP['DESC'];
293
        // Validate untrusted data
294
        if (!in_array($sortField, self::ALLOWED_PAGE_DETAIL_SORT_FIELDS, true)) {
295
            throw new BadRequestHttpException(Craft::t('webperf', 'Invalid sort field specified.'));
296
        }
297
        // Query the db table
298
        $offset = ($page - 1) * $per_page;
299
        $query = (new Query())
300
            ->from(['{{%webperf_data_samples}}'])
301
            ->offset($offset)
302
            ->where(['url' => $pageUrl])
303
            ->andWhere(['between', 'dateCreated', $start, $end])
0 ignored issues
show
Coding Style introduced by
Space after closing parenthesis of function call prohibited
Loading history...
304
        ;
305
        if ((int)$siteId !== 0) {
306
            $query->andWhere(['siteId' => $siteId]);
307
        }
308
        if ($filter !== '') {
309
            $query
310
                ->andWhere(['like', 'device', $filter])
311
                ->orWhere(['like', 'os', $filter])
312
                ->orWhere(['like', 'browser', $filter])
313
                ->orWhere(['like', 'countryCode', $filter])
0 ignored issues
show
Coding Style introduced by
Space after closing parenthesis of function call prohibited
Loading history...
314
            ;
315
        }
316
        $query
317
            ->orderBy([$sortField => $sortType])
318
            ->limit($per_page)
0 ignored issues
show
Coding Style introduced by
Space after closing parenthesis of function call prohibited
Loading history...
319
        ;
320
        $stats = $query->all();
321
        if ($stats) {
322
            $user = Craft::$app->getUser()->getIdentity();
323
            // Compute the largest page load time
324
            $maxTotalPageLoad = 0;
325
            foreach ($stats as &$stat) {
326
                // Determine the stat type
327
                if (!empty($stat['pageLoad']) && !empty($stat['craftTotalMs'])) {
328
                    $stat['type'] = 'both';
329
                }
330
                if (empty($stat['firstByte'])) {
331
                    $stat['type'] = 'craft';
332
                }
333
                if (empty($stat['craftTotalMs'])) {
334
                    $stat['type'] = 'frontend';
335
                }
336
                if ($stat['pageLoad'] > $maxTotalPageLoad) {
337
                    $maxTotalPageLoad = (int)$stat['pageLoad'];
338
                }
339
            }
340
            // Massage the stats
341
            foreach ($stats as &$stat) {
342
                if (!empty($stats['dateCreated'])) {
343
                    $date = DateTimeHelper::toDateTime($stats['dateCreated']);
344
                    $stats['dateCreated'] = $date->format('Y-m-d H:i:s');
345
                }
346
                $stat['mobile'] = (bool)$stat['mobile'];
347
                $stat['maxTotalPageLoad'] = (int)$maxTotalPageLoad;
348
                // Decode any emojis in the title
349
                if (!empty($stat['title'])) {
350
                    $stat['title'] = html_entity_decode($stat['title'], ENT_NOQUOTES, 'UTF-8');
351
                }
352
                $stat['deleteLink'] = UrlHelper::actionUrl('webperf/data-samples/delete-sample-by-id', [
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...
353
                    'id' => $stat['id']
354
                ]);
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...
355
                // Override based on permissions
356
                if (!$user->can('webperf:delete-data-samples')) {
357
                    $stat['deleteLink'] = '';
358
                }
359
            }
360
            // Format the data for the API
361
            $data['data'] = $stats;
362
            $query = (new Query())
363
                ->select(['[[url]]'])
364
                ->from(['{{%webperf_data_samples}}'])
365
                ->where(['url' => $pageUrl])
366
                ->andWhere(['between', 'dateCreated', $start, $end])
0 ignored issues
show
Coding Style introduced by
Space after closing parenthesis of function call prohibited
Loading history...
367
            ;
368
            if ($filter !== '') {
369
                $query
370
                    ->andWhere(['like', 'device', $filter])
371
                    ->orWhere(['like', 'os', $filter])
372
                    ->orWhere(['like', 'browser', $filter])
373
                    ->orWhere(['like', 'countryCode', $filter])
0 ignored issues
show
Coding Style introduced by
Space after closing parenthesis of function call prohibited
Loading history...
374
                ;
375
            }
376
            $count = $query->count();
377
            $data['links']['pagination'] = [
378
                'total' => $count,
379
                'per_page' => $per_page,
380
                'current_page' => $page,
381
                'last_page' => ceil($count / $per_page),
382
                'next_page_url' => null,
383
                'prev_page_url' => null,
384
                'from' => $offset + 1,
385
                'to' => $offset + ($count > $per_page ? $per_page : $count),
386
            ];
387
        }
388
389
        return $this->asJson($data);
390
    }
391
392
    /**
393
     * Handle requests for the pages index table
394
     *
395
     * @param string $sort
0 ignored issues
show
Coding Style introduced by
Missing parameter comment
Loading history...
Coding Style introduced by
Doc comment for parameter $sort does not match actual variable name $start
Loading history...
396
     * @param int    $page
0 ignored issues
show
Coding Style introduced by
Missing parameter comment
Loading history...
Coding Style introduced by
Doc comment for parameter $page does not match actual variable name $end
Loading history...
397
     * @param int    $per_page
0 ignored issues
show
Coding Style introduced by
Missing parameter comment
Loading history...
Coding Style introduced by
Doc comment for parameter $per_page does not match actual variable name $sort
Loading history...
398
     * @param string $filter
0 ignored issues
show
Coding Style introduced by
Missing parameter comment
Loading history...
Coding Style introduced by
Doc comment for parameter $filter does not match actual variable name $page
Loading history...
399
     * @param string $start
0 ignored issues
show
Coding Style introduced by
Missing parameter comment
Loading history...
Coding Style introduced by
Doc comment for parameter $start does not match actual variable name $per_page
Loading history...
400
     * @param string $end
0 ignored issues
show
Coding Style introduced by
Missing parameter comment
Loading history...
Coding Style introduced by
Doc comment for parameter $end does not match actual variable name $filter
Loading history...
401
     * @param int    $siteId
0 ignored issues
show
Coding Style introduced by
Missing parameter comment
Loading history...
402
     *
403
     * @return Response
404
     * @throws ForbiddenHttpException
405
     * @throws BadRequestHttpException
406
     */
407
    public function actionErrorsIndex(
408
        string $start = '',
409
        string $end = '',
410
        string $sort = 'url|DESC',
411
        int $page = 1,
412
        int $per_page = 20,
413
        $filter = '',
414
        $siteId = 0
415
    ): Response {
416
        PermissionHelper::controllerPermissionCheck('webperf:errors');
417
        $data = [];
418
        $sortField = 'url';
419
        $sortType = 'DESC';
420
        // Add a day since YYYY-MM-DD is really YYYY-MM-DD 00:00:00
421
        $end = date('Y-m-d', strtotime($end.'+1 day'));
422
        // Figure out the sorting type
423
        if ($sort !== '') {
424
            if (strpos($sort, '|') === false) {
425
                $sortField = $sort;
426
            } else {
427
                list($sortField, $sortType) = explode('|', $sort);
428
            }
429
        }
430
        $sortType = strtoupper($sortType);
431
        $sortType = self::SORT_MAP[$sortType] ?? self::SORT_MAP['DESC'];
432
        // Validate untrusted data
433
        if (!in_array($sortField, self::ALLOWED_ERRORS_INDEX_SORT_FIELDS, true)) {
434
            throw new BadRequestHttpException(Craft::t('webperf', 'Invalid sort field specified.'));
435
        }
436
        $db = Craft::$app->getDb();
437
        // Query the db table
438
        $offset = ($page - 1) * $per_page;
439
        $query = (new Query())
440
            ->select([
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...
441
                '[[url]]',
442
                'MIN([[title]]) as [[title]]',
443
                'MAX([[dateCreated]]) as [[latestErrorDate]]',
444
                'COUNT([[url]]) AS cnt',
445
            ])
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...
446
            ->from(['{{%webperf_error_samples}}'])
447
            ->offset($offset)
448
            ->where(['between', 'dateCreated', $start, $end])
0 ignored issues
show
Coding Style introduced by
Space after closing parenthesis of function call prohibited
Loading history...
449
        ;
450
        if ($db->getIsMysql()) {
451
            $query
452
                ->addSelect([
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...
453
                    'SUM([[type]] = \'craft\') as [[craftCount]]',
454
                    'SUM([[type]] = \'boomerang\') as [[boomerangCount]]',
455
                ]);
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...
456
        }
457
        if ($db->getIsPgsql()) {
458
            $query
459
                ->addSelect([
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...
460
                    'SUM(case when [[type]] = \'craft\' then 1 else 0 end) as [[craftCount]]',
461
                    'SUM(case when [[type]] = \'boomerang\' then 1 else 0 end) as [[boomerangCount]]',
462
                ]);
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...
463
        }
464
        if ((int)$siteId !== 0) {
465
            $query->andWhere(['siteId' => $siteId]);
466
        }
467
        if ($filter !== '') {
468
            $query
469
                ->andWhere(['like', 'url', $filter])
470
                ->orWhere(['like', 'title', $filter])
471
                ->orWhere(['like', 'pageErrors', $filter])
0 ignored issues
show
Coding Style introduced by
Space after closing parenthesis of function call prohibited
Loading history...
472
            ;
473
        }
474
        $query
475
            ->orderBy([$sortField => $sortType])
476
            ->groupBy('url')
477
            ->limit($per_page)
0 ignored issues
show
Coding Style introduced by
Space after closing parenthesis of function call prohibited
Loading history...
478
        ;
479
480
        $stats = $query->all();
481
        if ($stats) {
482
            $user = Craft::$app->getUser()->getIdentity();
483
            // Massage the stats
484
            foreach ($stats as &$stat) {
485
                $stat['cnt'] = (int)$stat['cnt'];
486
                $stat['craftCount'] = (int)$stat['craftCount'];
487
                $stat['boomerangCount'] = (int)$stat['boomerangCount'];
488
                // Decode any emojis in the title
489
                if (!empty($stat['title'])) {
490
                    $stat['title'] = html_entity_decode($stat['title'], ENT_NOQUOTES, 'UTF-8');
491
                }
492
                // Set up the appropriate helper links
493
                $stat['deleteLink'] = UrlHelper::actionUrl('webperf/error-samples/delete-samples-by-url', [
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...
494
                    'pageUrl' => $stat['url'],
495
                    'siteId' => $siteId
496
                ]);
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...
497
                $stat['detailPageUrl'] = UrlHelper::cpUrl('webperf/errors/page-detail', [
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...
498
                    'pageUrl' => $stat['url'],
499
                    'siteId' => $siteId,
500
                ]);
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...
501
                // Override based on permissions
502
                if (!$user->can('webperf:delete-error-samples')) {
503
                    $stat['deleteLink'] = '';
504
                }
505
                if (!$user->can('webperf:errors-detail')) {
506
                    $stat['detailPageUrl'] = '';
507
                }
508
            }
509
            // Format the data for the API
510
            $data['data'] = $stats;
511
            $query = (new Query())
512
                ->select(['[[url]]'])
513
                ->from(['{{%webperf_error_samples}}'])
514
                ->groupBy('[[url]]')
515
                ->where(['between', 'dateCreated', $start, $end])
0 ignored issues
show
Coding Style introduced by
Space after closing parenthesis of function call prohibited
Loading history...
516
            ;
517
            if ($filter !== '') {
518
                $query
519
                    ->andWhere(['like', 'url', $filter])
520
                    ->orWhere(['like', 'title', $filter])
521
                    ->orWhere(['like', 'pageErrors', $filter])
0 ignored issues
show
Coding Style introduced by
Space after closing parenthesis of function call prohibited
Loading history...
522
                ;
523
            }
524
            $count = $query->count();
525
            $data['links']['pagination'] = [
526
                'total' => $count,
527
                'per_page' => $per_page,
528
                'current_page' => $page,
529
                'last_page' => ceil($count / $per_page),
530
                'next_page_url' => null,
531
                'prev_page_url' => null,
532
                'from' => $offset + 1,
533
                'to' => $offset + ($count > $per_page ? $per_page : $count),
534
            ];
535
        }
536
537
        return $this->asJson($data);
538
    }
539
540
541
    /**
542
     * Handle requests for the performance detail table
543
     *
544
     * @param string $sort
0 ignored issues
show
Coding Style introduced by
Missing parameter comment
Loading history...
Coding Style introduced by
Doc comment for parameter $sort does not match actual variable name $start
Loading history...
545
     * @param int    $page
0 ignored issues
show
Coding Style introduced by
Missing parameter comment
Loading history...
Coding Style introduced by
Doc comment for parameter $page does not match actual variable name $end
Loading history...
546
     * @param int    $per_page
0 ignored issues
show
Coding Style introduced by
Missing parameter comment
Loading history...
Coding Style introduced by
Doc comment for parameter $per_page does not match actual variable name $sort
Loading history...
547
     * @param string $filter
0 ignored issues
show
Coding Style introduced by
Missing parameter comment
Loading history...
Coding Style introduced by
Doc comment for parameter $filter does not match actual variable name $page
Loading history...
548
     * @param string $pageUrl
0 ignored issues
show
Coding Style introduced by
Missing parameter comment
Loading history...
Coding Style introduced by
Doc comment for parameter $pageUrl does not match actual variable name $per_page
Loading history...
549
     * @param string $start
0 ignored issues
show
Coding Style introduced by
Missing parameter comment
Loading history...
Coding Style introduced by
Doc comment for parameter $start does not match actual variable name $filter
Loading history...
550
     * @param string $end
0 ignored issues
show
Coding Style introduced by
Missing parameter comment
Loading history...
Coding Style introduced by
Doc comment for parameter $end does not match actual variable name $pageUrl
Loading history...
551
     * @param int    $siteId
0 ignored issues
show
Coding Style introduced by
Missing parameter comment
Loading history...
552
     *
553
     * @return Response
554
     * @throws ForbiddenHttpException
555
     * @throws BadRequestHttpException
556
     */
557
    public function actionErrorsDetail(
558
        string $start = '',
559
        string $end = '',
560
        string $sort = 'dateCreated|DESC',
561
        int $page = 1,
562
        int $per_page = 20,
563
        $filter = '',
564
        $pageUrl = '',
565
        $siteId = 0
566
    ): Response {
567
        PermissionHelper::controllerPermissionCheck('webperf:errors');
568
        $data = [];
569
        $sortField = 'dateCreated';
570
        $sortType = 'DESC';
571
        // Add a day since YYYY-MM-DD is really YYYY-MM-DD 00:00:00
572
        $end = date('Y-m-d', strtotime($end.'+1 day'));
573
        $pageUrl = urldecode($pageUrl);
574
        // Figure out the sorting type
575
        if ($sort !== '') {
576
            if (strpos($sort, '|') === false) {
577
                $sortField = $sort;
578
            } else {
579
                list($sortField, $sortType) = explode('|', $sort);
580
            }
581
        }
582
        $sortType = strtoupper($sortType);
583
        $sortType = self::SORT_MAP[$sortType] ?? self::SORT_MAP['DESC'];
584
        // Validate untrusted data
585
        if (!in_array($sortField, self::ALLOWED_ERRORS_DETAIL_SORT_FIELDS, true)) {
586
            throw new BadRequestHttpException(Craft::t('webperf', 'Invalid sort field specified.'));
587
        }
588
        // Query the db table
589
        $offset = ($page - 1) * $per_page;
590
        $query = (new Query())
591
            ->from(['{{%webperf_error_samples}} webperf_error_samples'])
592
            ->select([
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...
593
                '[[webperf_error_samples.url]]',
594
                '[[webperf_error_samples.id]]',
595
                '[[webperf_error_samples.type]]',
596
                '[[webperf_error_samples.dateCreated]]',
597
                '[[webperf_error_samples.pageErrors]]',
598
                '[[webperf_error_samples.id]]',
599
600
                '[[webperf_data_samples.device]]',
601
                '[[webperf_data_samples.os]]',
602
                '[[webperf_data_samples.browser]]',
603
                '[[webperf_data_samples.countryCode]]',
604
                '[[webperf_data_samples.mobile]]',
605
            ])
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...
606
            ->offset($offset)
607
            ->where(['[[webperf_error_samples.url]]' => $pageUrl])
608
            ->andWhere(['between', '[[webperf_error_samples.dateCreated]]', $start, $end])
609
            ->leftJoin('{{%webperf_data_samples}} webperf_data_samples', '[[webperf_data_samples.requestId]] = [[webperf_error_samples.requestId]]')
0 ignored issues
show
Coding Style introduced by
Space after closing parenthesis of function call prohibited
Loading history...
610
        ;
611
        if ((int)$siteId !== 0) {
612
            $query->andWhere(['siteId' => $siteId]);
613
        }
614
        if ($filter !== '') {
615
            $query
616
                ->andWhere(['like', 'pageErrors', $filter])
617
                /*
618
                ->orWhere(['like', 'device', $filter])
619
                ->orWhere(['like', 'os', $filter])
620
                ->orWhere(['like', 'browser', $filter])
621
                ->orWhere(['like', 'countryCode', $filter])
622
                */
623
            ;
624
        }
625
        $query
626
            ->orderBy([$sortField => $sortType])
627
            ->limit($per_page)
0 ignored issues
show
Coding Style introduced by
Space after closing parenthesis of function call prohibited
Loading history...
628
        ;
629
        $stats = $query->all();
630
        if ($stats) {
631
            $user = Craft::$app->getUser()->getIdentity();
632
            // Massage the stats
633
            foreach ($stats as &$stat) {
634
                if (!empty($stats['dateCreated'])) {
635
                    $date = DateTimeHelper::toDateTime($stats['dateCreated']);
636
                    $stats['dateCreated'] = $date->format('Y-m-d H:i:s');
637
                }
638
                if (isset($stat['mobile'])) {
639
                    $stat['mobile'] = (bool)$stat['mobile'];
640
                }
641
                // Decode any emojis in the title
642
                if (!empty($stat['title'])) {
643
                    $stat['title'] = html_entity_decode($stat['title'], ENT_NOQUOTES, 'UTF-8');
644
                }
645
                $stat['deleteLink'] = UrlHelper::actionUrl('webperf/error-samples/delete-sample-by-id', [
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...
646
                    'id' => $stat['id']
647
                ]);
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...
648
                // Override based on permissions
649
                if (!$user->can('webperf:delete-error-samples')) {
650
                    $stat['deleteLink'] = '';
651
                }
652
            }
653
            // Format the data for the API
654
            $data['data'] = $stats;
655
            $query = (new Query())
656
                ->select(['[[url]]'])
657
                ->from(['{{%webperf_error_samples}}'])
658
                ->where(['url' => $pageUrl])
659
                ->andWhere(['between', 'dateCreated', $start, $end])
0 ignored issues
show
Coding Style introduced by
Space after closing parenthesis of function call prohibited
Loading history...
660
            ;
661
            if ($filter !== '') {
662
                $query
663
                    ->andWhere(['like', 'pageErrors', $filter])
664
                    /*
665
                    ->orWhere(['like', 'device', $filter])
666
                    ->orWhere(['like', 'os', $filter])
667
                    ->orWhere(['like', 'browser', $filter])
668
                    ->orWhere(['like', 'countryCode', $filter])
669
                    */
670
                ;
671
            }
672
            $count = $query->count();
673
            $data['links']['pagination'] = [
674
                'total' => $count,
675
                'per_page' => $per_page,
676
                'current_page' => $page,
677
                'last_page' => ceil($count / $per_page),
678
                'next_page_url' => null,
679
                'prev_page_url' => null,
680
                'from' => $offset + 1,
681
                'to' => $offset + ($count > $per_page ? $per_page : $count),
682
            ];
683
        }
684
685
        return $this->asJson($data);
686
    }
687
688
    // Protected Methods
689
    // =========================================================================
690
}
691