Passed
Push — v1 ( 7dfb05...0f87a2 )
by Andrew
07:09 queued 04:02
created

ChartsController::actionDashboardSlowestPages()   A

Complexity

Conditions 5
Paths 4

Size

Total Lines 50
Code Lines 31

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
eloc 31
dl 0
loc 50
rs 9.1128
c 0
b 0
f 0
cc 5
nc 4
nop 5
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\ArrayHelper;
0 ignored issues
show
Bug introduced by
The type craft\helpers\ArrayHelper 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\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...
22
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...
23
24
/**
0 ignored issues
show
Coding Style introduced by
Missing short description in doc comment
Loading history...
25
 * @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...
26
 * @package   Webperf
0 ignored issues
show
Coding Style introduced by
Tag value indented incorrectly; expected 1 spaces but found 3
Loading history...
27
 * @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...
28
 */
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...
29
class ChartsController extends Controller
30
{
31
    // Constants
32
    // =========================================================================
33
34
    // Protected Properties
35
    // =========================================================================
36
37
    /**
0 ignored issues
show
Coding Style introduced by
Missing short description in doc comment
Loading history...
38
     * @var    bool|array
0 ignored issues
show
Coding Style introduced by
Tag value indented incorrectly; expected 1 spaces but found 4
Loading history...
39
     */
40
    protected $allowAnonymous = [];
41
42
    // Public Methods
43
    // =========================================================================
44
45
    /**
46
     * The Dashboard stats average chart
47
     *
48
     * @param string $start
0 ignored issues
show
Coding Style introduced by
Missing parameter comment
Loading history...
49
     * @param string $end
0 ignored issues
show
Coding Style introduced by
Missing parameter comment
Loading history...
50
     * @param string $column
0 ignored issues
show
Coding Style introduced by
Missing parameter comment
Loading history...
51
     * @param string $pageUrl
0 ignored issues
show
Coding Style introduced by
Missing parameter comment
Loading history...
52
     * @param int    $siteId
0 ignored issues
show
Coding Style introduced by
Missing parameter comment
Loading history...
53
     *
54
     * @return Response
55
     * @throws ForbiddenHttpException
56
     */
57
    public function actionDashboardStatsAverage(
58
        string $start = '',
59
        string $end = '',
60
        string $column = 'pageLoad',
61
        $pageUrl = '',
62
        int $siteId = 0
63
    ): Response {
64
        PermissionHelper::controllerPermissionCheck('webperf:dashboard');
65
        $data = [];
66
        if (empty($end) || empty($start)) {
67
            return $this->asJson($data);
68
        }
69
        // Add a day since YYYY-MM-DD is really YYYY-MM-DD 00:00:00
70
        $end = date('Y-m-d', strtotime($end.'+1 day'));
71
        $pageUrl = urldecode($pageUrl);
72
        // Different dbs do it different ways
73
        $stats = null;
0 ignored issues
show
Unused Code introduced by
The assignment to $stats is dead and can be removed.
Loading history...
74
        $db = Craft::$app->getDb();
0 ignored issues
show
Unused Code introduced by
The assignment to $db is dead and can be removed.
Loading history...
75
        // Query the db
76
        $query = (new Query())
77
            ->from('{{%webperf_data_samples}}')
78
            ->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...
79
                'COUNT([[url]]) AS cnt',
80
                'AVG([['.$column.']]) AS avg',
81
            ])
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...
82
            ->where(['between', 'dateCreated', $start, $end])
83
            ->andWhere(['not', [$column => null]]);
84
        if ((int)$siteId !== 0) {
85
            $query->andWhere(['siteId' => $siteId]);
86
        }
87
        if ($pageUrl !== '') {
88
            $query->andWhere(['url' => $pageUrl]);
89
        }
90
        $stats = $query->all();
91
        // Massage the data
92
        if ($stats) {
93
            foreach ($stats as &$stat) {
94
                $stat['cnt'] = (int)$stat['cnt'];
95
            }
96
            $data = $stats[0];
97
        }
98
99
        return $this->asJson($data);
100
    }
101
102
    /**
103
     * The Dashboard stats slowest pages list
104
     *
105
     * @param string $start
0 ignored issues
show
Coding Style introduced by
Missing parameter comment
Loading history...
106
     * @param string $end
0 ignored issues
show
Coding Style introduced by
Missing parameter comment
Loading history...
107
     * @param string $column
0 ignored issues
show
Coding Style introduced by
Missing parameter comment
Loading history...
108
     * @param int    $limit
0 ignored issues
show
Coding Style introduced by
Missing parameter comment
Loading history...
109
     * @param int    $siteId
0 ignored issues
show
Coding Style introduced by
Missing parameter comment
Loading history...
110
     *
111
     * @return Response
112
     * @throws ForbiddenHttpException
113
     */
114
    public function actionDashboardSlowestPages(
115
        string $start,
116
        string $end,
117
        string $column = 'pageLoad',
118
        int $limit = 3,
119
        int $siteId = 0
120
    ): Response {
121
        PermissionHelper::controllerPermissionCheck('webperf:dashboard');
122
        $data = [];
123
        // Add a day since YYYY-MM-DD is really YYYY-MM-DD 00:00:00
124
        $end = date('Y-m-d', strtotime($end.'+1 day'));
125
        // Different dbs do it different ways
126
        $stats = null;
0 ignored issues
show
Unused Code introduced by
The assignment to $stats is dead and can be removed.
Loading history...
127
        $db = Craft::$app->getDb();
0 ignored issues
show
Unused Code introduced by
The assignment to $db is dead and can be removed.
Loading history...
128
        // Query the db
129
        $query = (new Query())
130
            ->from('{{%webperf_data_samples}}')
131
            ->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...
132
                '[[url]]',
133
                'MIN([[title]]) AS title',
134
                'COUNT([[url]]) AS cnt',
135
                'AVG([['.$column.']]) AS avg',
136
            ])
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...
137
            ->where(['between', 'dateCreated', $start, $end])
138
            ->andWhere(['not', [$column => null]]);
139
        if ((int)$siteId !== 0) {
140
            $query->andWhere(['siteId' => $siteId]);
141
        }
142
        $query
143
            ->orderBy('avg DESC')
144
            ->groupBy('url')
145
            ->limit($limit);
146
        $stats = $query->all();
147
        // Massage the data
148
        if ($stats) {
149
            foreach ($stats as &$stat) {
150
                $stat['cnt'] = (int)$stat['cnt'];
151
                $stat['detailPageUrl'] = UrlHelper::cpUrl('webperf/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...
152
                    'pageUrl' => $stat['url'],
153
                    'siteId' => $siteId,
154
                ]);
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...
155
                // Decode any emojis in the title
156
                if (!empty($stat['title'])) {
157
                    $stat['title'] = html_entity_decode($stat['title'], ENT_NOQUOTES, 'UTF-8');
158
                }
159
            }
160
            $data = $stats;
161
        }
162
163
        return $this->asJson($data);
164
    }
165
166
    /**
167
     * The Dashboard stats average chart
168
     *
169
     * @param string $start
0 ignored issues
show
Coding Style introduced by
Missing parameter comment
Loading history...
170
     * @param string $end
0 ignored issues
show
Coding Style introduced by
Missing parameter comment
Loading history...
171
     * @param string $pageUrl
0 ignored issues
show
Coding Style introduced by
Missing parameter comment
Loading history...
172
     * @param int    $siteId
0 ignored issues
show
Coding Style introduced by
Missing parameter comment
Loading history...
173
     *
174
     * @return Response
175
     * @throws ForbiddenHttpException
176
     */
177
    public function actionPagesAreaChart(
178
        string $start,
179
        string $end,
180
        $pageUrl = '',
181
        int $siteId = 0
182
    ): Response {
183
        PermissionHelper::controllerPermissionCheck('webperf:dashboard');
184
        $data = [];
185
        // Add a day since YYYY-MM-DD is really YYYY-MM-DD 00:00:00
186
        $end = date('Y-m-d', strtotime($end.'+1 day'));
187
        $pageUrl = urldecode($pageUrl);
188
        $dateStart = new \DateTime($start);
189
        $dateEnd = new \DateTime($end);
190
        $interval = date_diff($dateStart, $dateEnd);
191
        $dateFormat = "'%Y-%m-%d %H'";
192
        if ($interval->days > 30) {
193
            $dateFormat = "'%Y-%m-%d'";
194
        }
195
            // Different dbs do it different ways
196
        $stats = null;
0 ignored issues
show
Unused Code introduced by
The assignment to $stats is dead and can be removed.
Loading history...
197
        $db = Craft::$app->getDb();
198
        if ($db->getIsPgsql()) {
199
            $dateFormat = "'yyyy-mm-dd HH:MM'";
200
            if ($interval->days > 30) {
201
                $dateFormat = "'yyyy-mm-dd'";
202
            }
203
        }
204
        // Query the db
205
        $query = (new Query())
206
            ->from('{{%webperf_data_samples}}')
207
            ->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...
208
                'AVG([[pageLoad]]) AS [[pageLoad]]',
209
                'AVG([[domInteractive]]) AS [[domInteractive]]',
210
                'AVG([[firstContentfulPaint]]) AS [[firstContentfulPaint]]',
211
                'AVG([[firstPaint]]) AS [[firstPaint]]',
212
                'AVG([[firstByte]]) AS [[firstByte]]',
213
                'AVG([[connect]]) AS [[connect]]',
214
                'AVG([[dns]]) AS [[dns]]',
215
                'AVG([[craftTotalMs]]) AS [[craftTotalMs]]',
216
                'AVG([[craftTwigMs]]) AS [[craftTwigMs]]',
217
                'AVG([[craftDbMs]]) AS [[craftDbMs]]',
218
            ])
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...
219
            ->where(['between', 'dateCreated', $start, $end])
0 ignored issues
show
Coding Style introduced by
Space after closing parenthesis of function call prohibited
Loading history...
220
            ;
221
        if ((int)$siteId !== 0) {
222
            $query->andWhere(['siteId' => $siteId]);
223
        }
224
        if ($pageUrl !== '') {
225
            $query->andWhere(['url' => $pageUrl]);
226
        }
227
        if ($db->getIsMysql()) {
228
            $query
229
                ->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...
230
                    'DATE_FORMAT([[dateCreated]], '.$dateFormat.') AS [[sampleDate]]',
231
                ])
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...
232
                ->groupBy('sampleDate')
0 ignored issues
show
Coding Style introduced by
Space after closing parenthesis of function call prohibited
Loading history...
233
            ;
234
        }
235
        if ($db->getIsPgsql()) {
236
            $query
237
                ->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...
238
                    'to_char([[dateCreated]], '.$dateFormat.') AS [[sampleDate]]',
239
                ])
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...
240
                ->groupBy(['to_char([[dateCreated]], '.$dateFormat.')'])
0 ignored issues
show
Coding Style introduced by
Space after closing parenthesis of function call prohibited
Loading history...
241
            ;
242
        }
243
        $stats = $query
244
            ->all();
245
        // Massage the data
246
        if ($stats) {
247
            $data[] = [
248
                'name' => 'Database Queries',
249
                'data' => ArrayHelper::getColumn($stats, 'craftDbMs'),
250
                'labels' => ArrayHelper::getColumn($stats, 'sampleDate'),
251
            ];
252
            $data[] = [
253
                'name' => 'Twig Rendering',
254
                'data' => ArrayHelper::getColumn($stats, 'craftTwigMs'),
255
                'labels' => ArrayHelper::getColumn($stats, 'sampleDate'),
256
            ];
257
            $data[] = [
258
                'name' => 'Craft Rendering',
259
                'data' => ArrayHelper::getColumn($stats, 'craftTotalMs'),
260
                'labels' => ArrayHelper::getColumn($stats, 'sampleDate'),
261
            ];
262
            $data[] = [
263
                'name' => 'DNS Lookup',
264
                'data' => ArrayHelper::getColumn($stats, 'dns'),
265
                'labels' => ArrayHelper::getColumn($stats, 'sampleDate'),
266
            ];
267
            $data[] = [
268
                'name' => 'Connect',
269
                'data' => ArrayHelper::getColumn($stats, 'connect'),
270
                'labels' => ArrayHelper::getColumn($stats, 'sampleDate'),
271
            ];
272
            $data[] = [
273
                'name' => 'First Byte',
274
                'data' => ArrayHelper::getColumn($stats, 'firstByte'),
275
                'labels' => ArrayHelper::getColumn($stats, 'sampleDate'),
276
            ];
277
            $data[] = [
278
                'name' => 'First Paint',
279
                'data' => ArrayHelper::getColumn($stats, 'firstPaint'),
280
                'labels' => ArrayHelper::getColumn($stats, 'sampleDate'),
281
            ];
282
            $data[] = [
283
                'name' => 'First Contentful Paint',
284
                'data' => ArrayHelper::getColumn($stats, 'firstContentfulPaint'),
285
                'labels' => ArrayHelper::getColumn($stats, 'sampleDate'),
286
            ];
287
            $data[] = [
288
                'name' => 'DOM Interactive',
289
                'data' => ArrayHelper::getColumn($stats, 'domInteractive'),
290
                'labels' => ArrayHelper::getColumn($stats, 'sampleDate'),
291
            ];
292
            $data[] = [
293
                'name' => 'Page Load',
294
                'data' => ArrayHelper::getColumn($stats, 'pageLoad'),
295
                'labels' => ArrayHelper::getColumn($stats, 'sampleDate'),
296
            ];
297
        }
298
299
        return $this->asJson($data);
300
    }
301
302
    /**
303
     * The Dashboard chart
304
     *
305
     * @param int $days
0 ignored issues
show
Coding Style introduced by
Missing parameter comment
Loading history...
306
     *
307
     * @return Response
308
     */
309
    public function actionWidget($days = 1): Response
310
    {
311
        $data = [];
312
        return $this->asJson($data);
313
    }
314
315
    // Protected Methods
316
    // =========================================================================
317
}
318