Completed
Pull Request — master (#331)
by Elan
02:10 queued 57s
created

RunController::url()   B

Complexity

Conditions 5
Paths 4

Size

Total Lines 53

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 53
rs 8.7143
c 0
b 0
f 0
cc 5
nc 4
nop 0

How to fix   Long Method   

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:

1
<?php
2
3
namespace XHGui\Controller;
4
5
use Exception;
6
use Slim\Slim;
7
use XHGui\Searcher\SearcherInterface;
8
use XHGui\AbstractController;
9
10
class RunController extends AbstractController
11
{
12
    /**
13
     * HTTP GET attribute name for comma separated filters
14
     */
15
    const FILTER_ARGUMENT_NAME = 'filter';
16
17
    /**
18
     * @var SearcherInterface
19
     */
20
    private $searcher;
21
22
    public function __construct(Slim $app, SearcherInterface $searcher)
23
    {
24
        parent::__construct($app);
25
        $this->searcher = $searcher;
26
    }
27
28
    public function index()
29
    {
30
        $response = $this->app->response();
31
        // The list changes whenever new profiles are recorded.
32
        // Generally avoid caching, but allow re-use in browser's bfcache
33
        // and by cache proxies for concurrent requests.
34
        // https://github.com/perftools/xhgui/issues/261
35
        $response->headers->set('Cache-Control', 'public, max-age=0');
36
37
        $request = $this->app->request();
38
39
        $search = [];
40
        $keys = ['date_start', 'date_end', 'url'];
41
        foreach ($keys as $key) {
42
            if ($request->get($key)) {
43
                $search[$key] = $request->get($key);
44
            }
45
        }
46
        $sort = $request->get('sort');
47
48
        $result = $this->searcher->getAll([
49
            'sort' => $sort,
50
            'page' => $request->get('page'),
51
            'direction' => $request->get('direction'),
52
            'perPage' => $this->app->config('page.limit'),
53
            'conditions' => $search,
54
            'projection' => true,
55
        ]);
56
57
        $title = 'Recent runs';
58
        $titleMap = [
59
            'wt' => 'Longest wall time',
60
            'cpu' => 'Most CPU time',
61
            'mu' => 'Highest memory use',
62
        ];
63
        if (isset($titleMap[$sort])) {
64
            $title = $titleMap[$sort];
65
        }
66
67
        $paging = [
68
            'total_pages' => $result['totalPages'],
69
            'page' => $result['page'],
70
            'sort' => $sort,
71
            'direction' => $result['direction'],
72
        ];
73
74
        $this->_template = 'runs/list.twig';
75
        $this->set([
76
            'paging' => $paging,
77
            'base_url' => 'home',
78
            'runs' => $result['results'],
79
            'date_format' => $this->app->config('date.format'),
80
            'search' => $search,
81
            'has_search' => strlen(implode('', $search)) > 0,
82
            'title' => $title,
83
        ]);
84
    }
85
86
    public function view()
87
    {
88
        $response = $this->app->response();
89
        // Permalink views to a specific run are meant to be public and immutable.
90
        // But limit the cache to only a short period of time (enough to allow
91
        // handling of abuse or other stampedes). This way we don't have to
92
        // deal with any kind of purging system for when profiles are deleted,
93
        // or for after XHGui itself is upgraded and static assets may be
94
        // incompatible etc.
95
        // https://github.com/perftools/xhgui/issues/261
96
        $response->headers->set('Cache-Control', 'public, max-age=60, must-revalidate');
97
98
        $request = $this->app->request();
99
        $detailCount = $this->app->config('detail.count');
100
        $result = $this->searcher->get($request->get('id'));
101
102
        $result->calculateSelf();
103
104
        // Self wall time graph
105
        $timeChart = $result->extractDimension('ewt', $detailCount);
106
107
        // Memory Block
108
        $memoryChart = $result->extractDimension('emu', $detailCount);
109
110
        // Watched Functions Block
111
        $watchedFunctions = [];
112
        foreach ($this->searcher->getAllWatches() as $watch) {
113
            $matches = $result->getWatched($watch['name']);
114
            if ($matches) {
115
                $watchedFunctions = array_merge($watchedFunctions, $matches);
116
            }
117
        }
118
119
        if (false !== $request->get(self::FILTER_ARGUMENT_NAME, false)) {
120
            $profile = $result->sort('ewt', $result->filter($result->getProfile(), $this->getFilters()));
121
        } else {
122
            $profile = $result->sort('ewt', $result->getProfile());
123
        }
124
125
        $this->_template = 'runs/view.twig';
126
        $this->set([
127
            'profile' => $profile,
128
            'result' => $result,
129
            'wall_time' => $timeChart,
130
            'memory' => $memoryChart,
131
            'watches' => $watchedFunctions,
132
            'date_format' => $this->app->config('date.format'),
133
        ]);
134
    }
135
136
    /**
137
     * @return array
138
     */
139
    protected function getFilters()
140
    {
141
        $request = $this->app->request();
142
        $filterString = $request->get(self::FILTER_ARGUMENT_NAME);
143
        if (strlen($filterString) > 1 && $filterString !== 'true') {
144
            $filters = array_map('trim', explode(',', $filterString));
145
        } else {
146
            $filters = $this->app->config('run.view.filter.names');
147
        }
148
149
        return $filters;
150
    }
151
152
    public function deleteForm()
153
    {
154
        $request = $this->app->request();
155
        $id = $request->get('id');
156
        if (!is_string($id) || !strlen($id)) {
157
            throw new Exception('The "id" parameter is required.');
158
        }
159
160
        // Get details
161
        $result = $this->searcher->get($id);
162
163
        $this->_template = 'runs/delete-form.twig';
164
        $this->set([
165
            'run_id' => $id,
166
            'result' => $result,
167
        ]);
168
    }
169
170
    public function deleteSubmit()
171
    {
172
        $request = $this->app->request();
173
        $id = $request->post('id');
174
        // Don't call profilers->delete() unless $id is set,
175
        // otherwise it will turn the null into a MongoId and return "Sucessful".
176
        if (!is_string($id) || !strlen($id)) {
177
            // Form checks this already,
178
            // only reachable by handcrafted or malformed requests.
179
            throw new Exception('The "id" parameter is required.');
180
        }
181
182
        // Delete the profile run.
183
        $this->searcher->delete($id);
184
185
        $this->app->flash('success', 'Deleted profile ' . $id);
186
187
        $this->app->redirect($this->app->urlFor('home'));
188
    }
189
190
    public function deleteAllForm()
191
    {
192
        $this->_template = 'runs/delete-all-form.twig';
193
    }
194
195
    public function deleteAllSubmit()
196
    {
197
        // Delete all profile runs.
198
        $this->searcher->truncate();
199
200
        $this->app->flash('success', 'Deleted all profiles');
201
202
        $this->app->redirect($this->app->urlFor('home'));
203
    }
204
205
    public function url()
206
    {
207
        $request = $this->app->request();
208
        $pagination = [
209
            'sort' => $request->get('sort'),
210
            'direction' => $request->get('direction'),
211
            'page' => $request->get('page'),
212
            'perPage' => $this->app->config('page.limit'),
213
        ];
214
215
        $search = [];
216
        $keys = ['date_start', 'date_end', 'limit', 'limit_custom'];
217
        foreach ($keys as $key) {
218
            $search[$key] = $request->get($key);
219
        }
220
221
        $runs = $this->searcher->getForUrl(
222
            $request->get('url'),
223
            $pagination,
224
            $search
225
        );
226
227
        if (isset($search['limit_custom']) &&
228
            strlen($search['limit_custom']) > 0 &&
229
            $search['limit_custom'][0] === 'P'
230
        ) {
231
            $search['limit'] = $search['limit_custom'];
232
        }
233
234
        $chartData = $this->searcher->getPercentileForUrl(
235
            90,
236
            $request->get('url'),
237
            $search
238
        );
239
240
        $paging = [
241
            'total_pages' => $runs['totalPages'],
242
            'sort' => $pagination['sort'],
243
            'page' => $runs['page'],
244
            'direction' => $runs['direction'],
245
        ];
246
247
        $this->_template = 'runs/url.twig';
248
        $this->set([
249
            'paging' => $paging,
250
            'base_url' => 'url.view',
251
            'runs' => $runs['results'],
252
            'url' => $request->get('url'),
253
            'chart_data' => $chartData,
254
            'date_format' => $this->app->config('date.format'),
255
            'search' => array_merge($search, ['url' => $request->get('url')]),
256
        ]);
257
    }
258
259
    public function compare()
260
    {
261
        $request = $this->app->request();
262
263
        $baseRun = $headRun = $candidates = $comparison = null;
264
        $paging = [];
265
266
        if ($request->get('base')) {
267
            $baseRun = $this->searcher->get($request->get('base'));
268
        }
269
270
        if ($baseRun && !$request->get('head')) {
271
            $pagination = [
272
                'direction' => $request->get('direction'),
273
                'sort' => $request->get('sort'),
274
                'page' => $request->get('page'),
275
                'perPage' => $this->app->config('page.limit'),
276
            ];
277
            $candidates = $this->searcher->getForUrl(
278
                $baseRun->getMeta('simple_url'),
279
                $pagination
280
            );
281
282
            $paging = [
283
                'total_pages' => $candidates['totalPages'],
284
                'sort' => $pagination['sort'],
285
                'page' => $candidates['page'],
286
                'direction' => $candidates['direction'],
287
            ];
288
        }
289
290
        if ($request->get('head')) {
291
            $headRun = $this->searcher->get($request->get('head'));
292
        }
293
294
        if ($baseRun && $headRun) {
295
            $comparison = $baseRun->compare($headRun);
296
        }
297
298
        $this->_template = 'runs/compare.twig';
299
        $this->set([
300
            'base_url' => 'run.compare',
301
            'base_run' => $baseRun,
302
            'head_run' => $headRun,
303
            'candidates' => $candidates,
304
            'url_params' => $request->get(),
305
            'date_format' => $this->app->config('date.format'),
306
            'comparison' => $comparison,
307
            'paging' => $paging,
308
            'search' => [
309
                'base' => $request->get('base'),
310
                'head' => $request->get('head'),
311
            ],
312
        ]);
313
    }
314
315
    public function symbol()
316
    {
317
        $request = $this->app->request();
318
        $id = $request->get('id');
319
        $symbol = $request->get('symbol');
320
321
        $profile = $this->searcher->get($id);
322
        $profile->calculateSelf();
323
        list($parents, $current, $children) = $profile->getRelatives($symbol);
324
325
        $this->_template = 'runs/symbol.twig';
326
        $this->set([
327
            'symbol' => $symbol,
328
            'id' => $id,
329
            'main' => $profile->get('main()'),
330
            'parents' => $parents,
331
            'current' => $current,
332
            'children' => $children,
333
        ]);
334
    }
335
336
    public function symbolShort()
337
    {
338
        $request = $this->app->request();
339
        $id = $request->get('id');
340
        $threshold = $request->get('threshold');
341
        $symbol = $request->get('symbol');
342
        $metric = $request->get('metric');
343
344
        $profile = $this->searcher->get($id);
345
        $profile->calculateSelf();
346
        list($parents, $current, $children) = $profile->getRelatives($symbol, $metric, $threshold);
347
348
        $this->_template = 'runs/symbol-short.twig';
349
        $this->set([
350
            'symbol' => $symbol,
351
            'id' => $id,
352
            'main' => $profile->get('main()'),
353
            'parents' => $parents,
354
            'current' => $current,
355
            'children' => $children,
356
        ]);
357
    }
358
359
    public function callgraph()
360
    {
361
        $request = $this->app->request();
362
        $profile = $this->searcher->get($request->get('id'));
363
364
        $this->_template = 'runs/callgraph.twig';
365
        $this->set([
366
            'profile' => $profile,
367
            'date_format' => $this->app->config('date.format'),
368
        ]);
369
    }
370
371 View Code Duplication
    public function callgraphData()
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
372
    {
373
        $request = $this->app->request();
374
        $response = $this->app->response();
375
        $profile = $this->searcher->get($request->get('id'));
376
        $metric = $request->get('metric') ?: 'wt';
377
        $threshold = (float)$request->get('threshold') ?: 0.01;
378
        $callgraph = $profile->getCallgraph($metric, $threshold);
379
380
        $response['Content-Type'] = 'application/json';
381
382
        return $response->body(json_encode($callgraph));
0 ignored issues
show
Bug introduced by
The method body cannot be called on $response (of type array<string,string,{"Content-Type":"string"}>).

Methods can only be called on objects. This check looks for methods being called on variables that have been inferred to never be objects.

Loading history...
383
    }
384
385 View Code Duplication
    public function callgraphDataDot()
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
386
    {
387
        $request = $this->app->request();
388
        $response = $this->app->response();
389
        $profile = $this->searcher->get($request->get('id'));
390
        $metric = $request->get('metric') ?: 'wt';
391
        $threshold = (float)$request->get('threshold') ?: 0.01;
392
        $callgraph = $profile->getCallgraphNodes($metric, $threshold);
0 ignored issues
show
Bug introduced by
The method getCallgraphNodes() does not exist on XHGui\Profile. Did you maybe mean getCallgraph()?

This check marks calls to methods that do not seem to exist on an object.

This is most likely the result of a method being renamed without all references to it being renamed likewise.

Loading history...
393
394
        $response['Content-Type'] = 'application/json';
395
396
        return $response->body(json_encode($callgraph));
0 ignored issues
show
Bug introduced by
The method body cannot be called on $response (of type array<string,string,{"Content-Type":"string"}>).

Methods can only be called on objects. This check looks for methods being called on variables that have been inferred to never be objects.

Loading history...
397
    }
398
}
399