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 | * Domain object for handling profile runs. |
||
4 | * |
||
5 | * Provides method to manipulate the data from a single profile run. |
||
6 | */ |
||
7 | class Xhgui_Profile |
||
8 | { |
||
9 | /** |
||
10 | * @const Key used for methods with no parent |
||
11 | */ |
||
12 | const NO_PARENT = '__xhgui_top__'; |
||
13 | |||
14 | protected $_data; |
||
15 | protected $_collapsed; |
||
16 | protected $_indexed; |
||
17 | protected $_visited; |
||
18 | |||
19 | protected $_keys = array('ct', 'wt', 'cpu', 'mu', 'pmu'); |
||
20 | protected $_exclusiveKeys = array('ewt', 'ecpu', 'emu', 'epmu'); |
||
21 | protected $_functionCount; |
||
22 | |||
23 | public function __construct(array $profile, $convert = true) |
||
24 | { |
||
25 | $this->_data = $profile; |
||
26 | |||
27 | // cast MongoIds to string |
||
28 | if (isset($this->_data['_id']) && !is_string($this->_data['_id'])) { |
||
29 | $this->_data['_id'] = (string) $this->_data['_id']; |
||
30 | } |
||
31 | |||
32 | if (!empty($profile['profile']) && $convert) { |
||
33 | $this->_process(); |
||
34 | } |
||
35 | } |
||
36 | |||
37 | /** |
||
38 | * Convert the raw data into a flatter list that is easier to use. |
||
39 | * |
||
40 | * This removes some of the parentage detail as all calls of a given |
||
41 | * method are aggregated. We are not able to maintain a full tree structure |
||
42 | * in any case, as xhprof only keeps one level of detail. |
||
43 | * |
||
44 | * @return void |
||
45 | */ |
||
46 | protected function _process() |
||
47 | { |
||
48 | $result = array(); |
||
49 | foreach ($this->_data['profile'] as $name => $values) { |
||
50 | list($parent, $func) = $this->splitName($name); |
||
51 | |||
52 | // Generate collapsed data. |
||
53 | if (isset($result[$func])) { |
||
54 | $result[$func] = $this->_sumKeys($result[$func], $values); |
||
55 | $result[$func]['parents'][] = $parent; |
||
56 | } else { |
||
57 | $result[$func] = $values; |
||
58 | $result[$func]['parents'] = array($parent); |
||
59 | } |
||
60 | |||
61 | // Build the indexed data. |
||
62 | if ($parent === null) { |
||
63 | $parent = self::NO_PARENT; |
||
64 | } |
||
65 | if (!isset($this->_indexed[$parent])) { |
||
66 | $this->_indexed[$parent] = array(); |
||
67 | } |
||
68 | $this->_indexed[$parent][$func] = $values; |
||
69 | } |
||
70 | $this->_collapsed = $result; |
||
71 | } |
||
72 | |||
73 | /** |
||
74 | * Sum up the values in $this->_keys; |
||
75 | * |
||
76 | * @param array $a The first set of profile data |
||
77 | * @param array $b The second set of profile data. |
||
78 | * @return array Merged profile data. |
||
79 | */ |
||
80 | protected function _sumKeys($a, $b) |
||
81 | { |
||
82 | foreach ($this->_keys as $key) { |
||
83 | if (!isset($a[$key])) { |
||
84 | $a[$key] = 0; |
||
85 | } |
||
86 | $a[$key] += isset($b[$key]) ? $b[$key] : 0; |
||
87 | } |
||
88 | return $a; |
||
89 | } |
||
90 | |||
91 | protected function _diffKeys($a, $b, $includeSelf = true) |
||
92 | { |
||
93 | $keys = $this->_keys; |
||
94 | if ($includeSelf) { |
||
95 | $keys = array_merge($keys, $this->_exclusiveKeys); |
||
96 | } |
||
97 | foreach ($keys as $key) { |
||
98 | $a[$key] -= $b[$key]; |
||
99 | } |
||
100 | return $a; |
||
101 | } |
||
102 | |||
103 | protected function _diffPercentKeys($a, $b, $includeSelf = true) |
||
104 | { |
||
105 | $out = array(); |
||
106 | $keys = $this->_keys; |
||
107 | if ($includeSelf) { |
||
108 | $keys = array_merge($keys, $this->_exclusiveKeys); |
||
109 | } |
||
110 | foreach ($keys as $key) { |
||
111 | if ($b[$key] != 0) { |
||
112 | $out[$key] = $a[$key] / $b[$key]; |
||
113 | } else { |
||
114 | $out[$key] = -1; |
||
115 | } |
||
116 | } |
||
117 | return $out; |
||
118 | } |
||
119 | |||
120 | /** |
||
121 | * Get the profile run data. |
||
122 | * |
||
123 | * TODO remove this and move all the features using it into this/ |
||
124 | * other classes. |
||
125 | * |
||
126 | * @return array |
||
127 | */ |
||
128 | public function getProfile() |
||
129 | { |
||
130 | return $this->_collapsed; |
||
131 | } |
||
132 | |||
133 | public function getId() |
||
134 | { |
||
135 | return $this->_data['_id']; |
||
136 | } |
||
137 | |||
138 | public function getDate() |
||
139 | { |
||
140 | $date = $this->getMeta('SERVER.REQUEST_TIME'); |
||
141 | if ($date) { |
||
142 | return new DateTime('@' . $date); |
||
143 | } |
||
144 | return new DateTime('now'); |
||
145 | } |
||
146 | |||
147 | /** |
||
148 | * Get meta data about the profile. Read's a . split path |
||
149 | * out of the meta data in a profile. For example `SERVER.REQUEST_TIME` |
||
150 | * |
||
151 | * @param string $key The dotted key to read. |
||
152 | * @return null|mixed Null on failure, otherwise the stored value. |
||
153 | */ |
||
154 | public function getMeta($key = null) |
||
155 | { |
||
156 | $data = $this->_data['meta']; |
||
157 | if ($key === null) { |
||
158 | return $data; |
||
159 | } |
||
160 | $parts = explode('.', $key); |
||
161 | foreach ($parts as $key) { |
||
162 | if (is_array($data) && isset($data[$key])) { |
||
163 | $data =& $data[$key]; |
||
164 | } else { |
||
165 | return null; |
||
166 | } |
||
167 | } |
||
168 | return $data; |
||
169 | } |
||
170 | |||
171 | /** |
||
172 | * Read data from the profile run. |
||
173 | * |
||
174 | * @param string $key The function key name to read. |
||
175 | * @param string $metric The metric to read. |
||
176 | * @return null|float |
||
177 | */ |
||
178 | public function get($key, $metric = null) |
||
179 | { |
||
180 | if (!isset($this->_collapsed[$key])) { |
||
181 | return null; |
||
182 | } |
||
183 | if (empty($metric)) { |
||
184 | return $this->_collapsed[$key]; |
||
185 | } |
||
186 | if (!isset($this->_collapsed[$key][$metric])) { |
||
187 | return null; |
||
188 | } |
||
189 | return $this->_collapsed[$key][$metric]; |
||
190 | } |
||
191 | |||
192 | /** |
||
193 | * Find a function matching a watched function. |
||
194 | * |
||
195 | * @param string $pattern The pattern to look for. |
||
196 | * @return null|array An list of matching functions |
||
197 | * or null. |
||
198 | */ |
||
199 | public function getWatched($pattern) |
||
200 | { |
||
201 | if (isset($this->_collapsed[$pattern])) { |
||
202 | $data = $this->_collapsed[$pattern]; |
||
203 | $data['function'] = $pattern; |
||
204 | return array($data); |
||
205 | } |
||
206 | $matches = array(); |
||
207 | $keys = array_keys($this->_collapsed); |
||
208 | foreach ($keys as $func) { |
||
209 | if (preg_match('`^' . $pattern . '$`', $func)) { |
||
210 | $data = $this->_collapsed[$func]; |
||
211 | $data['function'] = $func; |
||
212 | $matches[] = $data; |
||
213 | } |
||
214 | } |
||
215 | return $matches; |
||
216 | } |
||
217 | |||
218 | /** |
||
219 | * Find the parent and children method/functions for a given |
||
220 | * symbol. |
||
221 | * |
||
222 | * The parent/children arrays will contain all the callers + callees |
||
223 | * of the symbol given. The current index will give the total |
||
224 | * inclusive values for all properties. |
||
225 | * |
||
226 | * @param string $symbol The name of the function/method to find |
||
227 | * relatives for. |
||
228 | * @param string $metric The metric to compare $threshold with. |
||
229 | * @param float $threshold The threshold to exclude child functions at. Any |
||
230 | * function that represents less than this percentage of the current metric |
||
231 | * will be filtered out. |
||
232 | * @return array List of (parent, current, children) |
||
233 | */ |
||
234 | public function getRelatives($symbol, $metric = null, $threshold = 0) |
||
235 | { |
||
236 | $parents = array(); |
||
0 ignored issues
–
show
|
|||
237 | |||
238 | // If the function doesn't exist, it won't have parents/children |
||
239 | if (empty($this->_collapsed[$symbol])) { |
||
240 | return array( |
||
241 | array(), |
||
242 | array(), |
||
243 | array(), |
||
244 | ); |
||
245 | } |
||
246 | $current = $this->_collapsed[$symbol]; |
||
247 | $current['function'] = $symbol; |
||
248 | |||
249 | $parents = $this->_getParents($symbol); |
||
250 | $children = $this->_getChildren($symbol, $metric, $threshold); |
||
251 | return array($parents, $current, $children); |
||
252 | } |
||
253 | |||
254 | /** |
||
255 | * Get the parent methods for a given symbol. |
||
256 | * |
||
257 | * @param string $symbol The name of the function/method to find |
||
258 | * parents for. |
||
259 | * @return array List of parents |
||
260 | */ |
||
261 | protected function _getParents($symbol) |
||
262 | { |
||
263 | $parents = array(); |
||
264 | $current = $this->_collapsed[$symbol]; |
||
265 | foreach ($current['parents'] as $parent) { |
||
266 | if (isset($this->_collapsed[$parent])) { |
||
267 | $parents[] = array('function' => $parent) + $this->_collapsed[$parent]; |
||
268 | } |
||
269 | } |
||
270 | return $parents; |
||
271 | } |
||
272 | |||
273 | /** |
||
274 | * Find symbols that are the children of the given name. |
||
275 | * |
||
276 | * @param string $symbol The name of the function to find children of. |
||
277 | * @param string $metric The metric to compare $threshold with. |
||
278 | * @param float $threshold The threshold to exclude functions at. Any |
||
279 | * function that represents less than |
||
280 | * @return array An array of child methods. |
||
281 | */ |
||
282 | protected function _getChildren($symbol, $metric = null, $threshold = 0) |
||
283 | { |
||
284 | $children = array(); |
||
285 | if (!isset($this->_indexed[$symbol])) { |
||
286 | return $children; |
||
287 | } |
||
288 | |||
289 | $total = 0; |
||
290 | if (isset($metric)) { |
||
291 | $top = $this->_indexed[self::NO_PARENT]; |
||
292 | // Not always 'main()' |
||
293 | $mainFunc = current($top); |
||
294 | $total = $mainFunc[$metric]; |
||
295 | } |
||
296 | |||
297 | foreach ($this->_indexed[$symbol] as $name => $data) { |
||
298 | if ( |
||
299 | $metric && $total > 0 && $threshold > 0 && |
||
0 ignored issues
–
show
The expression
$metric of type string|null is loosely compared to true ; this is ambiguous if the string can be empty. You might want to explicitly use !== null instead.
In PHP, under loose comparison (like For '' == false // true
'' == null // true
'ab' == false // false
'ab' == null // false
// It is often better to use strict comparison
'' === false // false
'' === null // false
![]() |
|||
300 | ($this->_collapsed[$name][$metric] / $total) < $threshold |
||
301 | ) { |
||
302 | continue; |
||
303 | } |
||
304 | $children[] = $data + array('function' => $name); |
||
305 | } |
||
306 | return $children; |
||
307 | } |
||
308 | |||
309 | /** |
||
310 | * Extracts a single dimension of data |
||
311 | * from a profile run. |
||
312 | * |
||
313 | * Useful for creating bar/column graphs. |
||
314 | * The profile data will be sorted by the column |
||
315 | * and then the $limit records will be extracted. |
||
316 | * |
||
317 | * @param string $dimension The dimension to extract |
||
318 | * @param int $limit Number of elements to pull |
||
319 | * @return array Array of data with name = function name and |
||
320 | * value = the dimension. |
||
321 | */ |
||
322 | public function extractDimension($dimension, $limit) |
||
323 | { |
||
324 | $profile = $this->sort($dimension, $this->_collapsed); |
||
325 | $slice = array_slice($profile, 0, $limit); |
||
326 | $extract = array(); |
||
327 | foreach ($slice as $func => $funcData) { |
||
328 | $extract[] = array( |
||
329 | 'name' => $func, |
||
330 | 'value' => $funcData[$dimension] |
||
331 | ); |
||
332 | } |
||
333 | return $extract; |
||
334 | } |
||
335 | |||
336 | /** |
||
337 | * Generate the approximate exclusive values for each metric. |
||
338 | * |
||
339 | * We get a==>b as the name, we need a key for a and b in the array |
||
340 | * to get exclusive values for A we need to subtract the values of B (and any other children); |
||
341 | * call passing in the entire profile only, should return an array of |
||
342 | * functions with their regular timing, and exclusive numbers inside ['exclusive'] |
||
343 | * |
||
344 | * Consider: |
||
345 | * /---c---d---e |
||
346 | * a -/----b---d---e |
||
347 | * |
||
348 | * We have c==>d and b==>d, and in both instances d invokes e, yet we will |
||
349 | * have but a single d==>e result. This is a known and documented limitation of XHProf |
||
350 | * |
||
351 | * We have one d==>e entry, with some values, including ct=2 |
||
352 | * We also have c==>d and b==>d |
||
353 | * |
||
354 | * We should determine how many ==>d options there are, and equally |
||
355 | * split the cost of d==>e across them since d==>e represents the sum total of all calls. |
||
356 | * |
||
357 | * Notes: |
||
358 | * Function names are not unique, but we're merging them |
||
359 | * |
||
360 | * @return Xhgui_Profile A new instance with exclusive data set. |
||
361 | */ |
||
362 | public function calculateSelf() |
||
363 | { |
||
364 | // Init exclusive values |
||
365 | foreach ($this->_collapsed as &$data) { |
||
366 | $data['ewt'] = $data['wt']; |
||
367 | $data['emu'] = $data['mu']; |
||
368 | $data['ecpu'] = $data['cpu']; |
||
369 | $data['ect'] = $data['ct']; |
||
370 | $data['epmu'] = $data['pmu']; |
||
371 | } |
||
372 | unset($data); |
||
373 | |||
374 | // Go over each method and remove each childs metrics |
||
375 | // from the parent. |
||
376 | foreach ($this->_collapsed as $name => $data) { |
||
377 | $children = $this->_getChildren($name); |
||
378 | foreach ($children as $child) { |
||
379 | $this->_collapsed[$name]['ewt'] -= $child['wt']; |
||
380 | $this->_collapsed[$name]['emu'] -= $child['mu']; |
||
381 | $this->_collapsed[$name]['ecpu'] -= $child['cpu']; |
||
382 | $this->_collapsed[$name]['ect'] -= $child['ct']; |
||
383 | $this->_collapsed[$name]['epmu'] -= $child['pmu']; |
||
384 | } |
||
385 | } |
||
386 | return $this; |
||
387 | } |
||
388 | |||
389 | /** |
||
390 | * Sort data by a dimension. |
||
391 | * |
||
392 | * @param string $dimension The dimension to sort by. |
||
393 | * @param array $data The data to sort. |
||
394 | * @return array The sorted data. |
||
395 | */ |
||
396 | public function sort($dimension, $data) |
||
397 | { |
||
398 | $sorter = function ($a, $b) use ($dimension) { |
||
399 | if ($a[$dimension] == $b[$dimension]) { |
||
400 | return 0; |
||
401 | } |
||
402 | return $a[$dimension] > $b[$dimension] ? -1 : 1; |
||
403 | }; |
||
404 | uasort($data, $sorter); |
||
405 | return $data; |
||
406 | } |
||
407 | |||
408 | /** |
||
409 | * @param array $profileData |
||
410 | * @param array $filters |
||
411 | * |
||
412 | * @return array |
||
413 | */ |
||
414 | public function filter($profileData, $filters = []) |
||
415 | { |
||
416 | foreach ($filters as $key => $item) { |
||
417 | foreach ($profileData as $keyItem => $method) { |
||
418 | if (fnmatch($item, $keyItem)) { |
||
419 | unset($profileData[ $keyItem ]); |
||
420 | } |
||
421 | } |
||
422 | } |
||
423 | |||
424 | return $profileData; |
||
425 | } |
||
426 | |||
427 | /** |
||
428 | * Split a key name into the parent==>child format. |
||
429 | * |
||
430 | * @param string $name The name to split. |
||
431 | * @return array An array of parent, child. parent will be null if there |
||
432 | * is no parent. |
||
433 | */ |
||
434 | public function splitName($name) |
||
435 | { |
||
436 | $a = explode("==>", $name); |
||
437 | if (isset($a[1])) { |
||
438 | return $a; |
||
439 | } |
||
440 | return array(null, $a[0]); |
||
441 | } |
||
442 | |||
443 | /** |
||
444 | * Get the total number of tracked function calls in this run. |
||
445 | * |
||
446 | * @return int |
||
447 | */ |
||
448 | public function getFunctionCount() |
||
449 | { |
||
450 | if ($this->_functionCount) { |
||
451 | return $this->_functionCount; |
||
452 | } |
||
453 | $total = 0; |
||
454 | foreach ($this->_collapsed as $data) { |
||
455 | $total += $data['ct']; |
||
456 | } |
||
457 | $this->_functionCount = $total; |
||
458 | return $this->_functionCount; |
||
459 | } |
||
460 | |||
461 | /** |
||
462 | * Compare this run to another run. |
||
463 | * |
||
464 | * @param Xhgui_Profile $head The other run to compare with |
||
465 | * @return array An array of comparison data. |
||
466 | */ |
||
467 | public function compare(Xhgui_Profile $head) |
||
468 | { |
||
469 | $this->calculateSelf(); |
||
470 | $head->calculateSelf(); |
||
471 | |||
472 | $keys = array_merge($this->_keys, $this->_exclusiveKeys); |
||
473 | $emptyData = array_fill_keys($keys, 0); |
||
474 | |||
475 | $diffPercent = array(); |
||
476 | $diff = array(); |
||
477 | foreach ($this->_collapsed as $key => $baseData) { |
||
478 | $headData = $head->get($key); |
||
479 | if (!$headData) { |
||
480 | $diff[$key] = $this->_diffKeys($emptyData, $baseData); |
||
481 | continue; |
||
482 | } |
||
483 | $diff[$key] = $this->_diffKeys($headData, $baseData); |
||
484 | |||
485 | if ($key === 'main()') { |
||
486 | $diffPercent[$key] = $this->_diffPercentKeys($headData, $baseData); |
||
487 | } |
||
488 | } |
||
489 | |||
490 | $diff['functionCount'] = $head->getFunctionCount() - $this->getFunctionCount(); |
||
491 | $diffPercent['functionCount'] = $head->getFunctionCount() / $this->getFunctionCount(); |
||
492 | |||
493 | return array( |
||
494 | 'base' => $this, |
||
495 | 'head' => $head, |
||
496 | 'diff' => $diff, |
||
497 | 'diffPercent' => $diffPercent, |
||
498 | ); |
||
499 | } |
||
500 | |||
501 | /** |
||
502 | * Get the max value for any give metric. |
||
503 | * |
||
504 | * @param string $metric The metric to get a max value for. |
||
505 | */ |
||
506 | protected function _maxValue($metric) |
||
507 | { |
||
508 | return array_reduce( |
||
509 | $this->_collapsed, |
||
510 | function ($result, $item) use ($metric) { |
||
511 | if ($item[$metric] > $result) { |
||
512 | return $item[$metric]; |
||
513 | } |
||
514 | return $result; |
||
515 | }, |
||
516 | 0 |
||
517 | ); |
||
518 | } |
||
519 | |||
520 | /** |
||
521 | * Return a structured array suitable for generating callgraph visualizations. |
||
522 | * |
||
523 | * Functions whose inclusive time is less than 2% of the total time will |
||
524 | * be excluded from the callgraph data. |
||
525 | * |
||
526 | * @return array |
||
527 | */ |
||
528 | public function getCallgraph($metric = 'wt', $threshold = 0.01) |
||
529 | { |
||
530 | $valid = array_merge($this->_keys, $this->_exclusiveKeys); |
||
531 | if (!in_array($metric, $valid)) { |
||
532 | throw new Exception("Unknown metric '$metric'. Cannot generate callgraph."); |
||
533 | } |
||
534 | $this->calculateSelf(); |
||
535 | |||
536 | // Non exclusive metrics are always main() because it is the root call scope. |
||
537 | if (in_array($metric, $this->_exclusiveKeys)) { |
||
538 | $main = $this->_maxValue($metric); |
||
539 | } else { |
||
540 | $main = $this->_collapsed['main()'][$metric]; |
||
541 | } |
||
542 | |||
543 | $this->_visited = $this->_nodes = $this->_links = array(); |
||
0 ignored issues
–
show
The property
_nodes does not exist. Did you maybe forget to declare it?
In PHP it is possible to write to properties without declaring them. For example, the following is perfectly valid PHP code: class MyClass { }
$x = new MyClass();
$x->foo = true;
Generally, it is a good practice to explictly declare properties to avoid accidental typos and provide IDE auto-completion: class MyClass {
public $foo;
}
$x = new MyClass();
$x->foo = true;
![]() The property
_links does not exist. Did you maybe forget to declare it?
In PHP it is possible to write to properties without declaring them. For example, the following is perfectly valid PHP code: class MyClass { }
$x = new MyClass();
$x->foo = true;
Generally, it is a good practice to explictly declare properties to avoid accidental typos and provide IDE auto-completion: class MyClass {
public $foo;
}
$x = new MyClass();
$x->foo = true;
![]() |
|||
544 | $this->_callgraphData(self::NO_PARENT, $main, $metric, $threshold); |
||
545 | $out = array( |
||
546 | 'metric' => $metric, |
||
547 | 'total' => $main, |
||
548 | 'nodes' => $this->_nodes, |
||
549 | 'links' => $this->_links |
||
550 | ); |
||
551 | unset($this->_visited, $this->_nodes, $this->_links); |
||
552 | return $out; |
||
553 | } |
||
554 | |||
555 | protected function _callgraphData($parentName, $main, $metric, $threshold, $parentIndex = null) |
||
556 | { |
||
557 | // Leaves don't have children, and don't have links/nodes to add. |
||
558 | if (!isset($this->_indexed[$parentName])) { |
||
559 | return; |
||
560 | } |
||
561 | |||
562 | $children = $this->_indexed[$parentName]; |
||
563 | foreach ($children as $childName => $metrics) { |
||
564 | $metrics = $this->_collapsed[$childName]; |
||
565 | if ($metrics[$metric] / $main <= $threshold) { |
||
566 | continue; |
||
567 | } |
||
568 | $revisit = false; |
||
569 | |||
570 | // Keep track of which nodes we've visited and their position |
||
571 | // in the node list. |
||
572 | if (!isset($this->_visited[$childName])) { |
||
573 | $index = count($this->_nodes); |
||
574 | $this->_visited[$childName] = $index; |
||
575 | |||
576 | $this->_nodes[] = array( |
||
577 | 'name' => $childName, |
||
578 | 'callCount' => $metrics['ct'], |
||
579 | 'value' => $metrics[$metric], |
||
580 | ); |
||
581 | } else { |
||
582 | $revisit = true; |
||
583 | $index = $this->_visited[$childName]; |
||
584 | } |
||
585 | |||
586 | if ($parentIndex !== null) { |
||
587 | $this->_links[] = array( |
||
588 | 'source' => $parentName, |
||
589 | 'target' => $childName, |
||
590 | 'callCount' => $metrics['ct'], |
||
591 | ); |
||
592 | } |
||
593 | |||
594 | // If the current function has more children, |
||
595 | // walk that call subgraph. |
||
596 | if (isset($this->_indexed[$childName]) && !$revisit) { |
||
597 | $this->_callgraphData($childName, $main, $metric, $threshold, $index); |
||
598 | } |
||
599 | } |
||
600 | } |
||
601 | |||
602 | public function toArray() |
||
603 | { |
||
604 | return $this->_data; |
||
605 | } |
||
606 | } |
||
607 |
This check looks for variable assignements that are either overwritten by other assignments or where the variable is not used subsequently.
Both the
$myVar
assignment in line 1 and the$higher
assignment in line 2 are dead. The first because$myVar
is never used and the second because$higher
is always overwritten for every possible time line.