Xhgui_Controller_Run::deleteAllForm()   A
last analyzed

Complexity

Conditions 1
Paths 1

Size

Total Lines 4

Duplication

Lines 0
Ratio 0 %

Importance

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