This project does not seem to handle request data directly as such no vulnerable execution paths were found.
include
, or for example
via PHP's auto-loading mechanism.
These results are based on our legacy PHP analysis, consider migrating to our new PHP analysis engine instead. Learn more
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
|
|||
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
|
|||
377 | } |
||
378 | |||
379 | View Code Duplication | public function callgraphDataDot() |
|
0 ignored issues
–
show
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. ![]() |
|||
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
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. ![]() |
|||
387 | |||
388 | $response['Content-Type'] = 'application/json'; |
||
389 | return $response->body(json_encode($callgraph)); |
||
0 ignored issues
–
show
|
|||
390 | } |
||
391 | } |
||
392 |
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.