Magic number is a literal value with unexplained meaning, that appears in the code multiple times. Such values will show no intent, and should be replace by a constant: the name of the constant will make the code more readable, and easier to update in the future.
Hunting for Magic Numbers
Let’s take a look at some code, and extract the magic numbers to understand the situation. Here is a list of real numbers, found in a recently published Open Source application of about 120 kLOC.
Value | Number of occurrences |
0.0 | 1 |
1.0E-08 | 1 |
0.05 | 1 |
0.33 | 1 |
1.0 | 10 |
1.7 | 4 |
2.0 | 1 |
2.3 | 1 |
3.2 | 1 |
4.0 | 1 |
4.199999 | 1 |
4.299999 | 1 |
5.0 | 1 |
5.2 | 2 |
7.3 | 1 |
8.0 | 1 |
365.25 | 3 |
3600.0 | 1 |
A quick scan shows:
- 0.33 looks like 1/3, with some approximation. It may well be replaced by a division by 3.
- 365.25 looks like a year-time, expressed in days. What also raise suspicion is that the integers 365 and 366 used several time in the application.
- Several integers are expressed as reals (0, 1, 2, 4, 5, 8, 3600)
- Float with one decimal look like version numbers but may be something else too. We can recognize some PHP version (5.0, 5.2), or else.
Turning literals into constants
Numbers used only once may be safely ignored. They may be for testing purpose or edge case, and bear little importance. It takes at least two occurrences to create a situation worthy of a constant.
On the other hand, numbers that are used several times may show relations between various parts of the code. When the same value is used remote parts of the code, any update is suddenly very difficult to conduct. All those values must be searched and checked. Let see what may happen with 1.7.
1.7 appears 4 times in the code. There is no obvious meaning to this value: the best bet may be that it is a version number. Grep yields 19 locations in the code that use “1.7”: it is really necessary to use a static auditing tool to get the values. Here are the actual cases:
backend/external/misc/class.captcha.php#70: $fontsize = $this->height/1.7; backend/external/misc/class.captcha.php#124: $fontsize = $this->height/1.7; backend/external/misc/class.captcha.php#186: $fontsize = $this->height/1.7; backend/external/pear.php.net/HTML/Common.php#97: function apiVersion() { return 1.7; } // end func apiVersion
Three of the occurrences are calculating a font size. The last one is returned by a pear class as a version. There are two conflicting usage of this number, and it will be profitable to turn them into a constant.
Recommendations
- Spot literals, especially those that are used in calculations (such as the fontsize calculation above) and those that are used several times. They are prime target for constantization (if that exists as a word)
- Magic numbers may be reals (rare), integers (numerous), strings (very numerous). Process them in that order to get quicker code readability.
- Constants may be class constants (to use within a class) or interface constants (to use within several interface-related classes), or global constants (for the rest). Remember that const keyword makes the constant more efficient, but is less flexible than define.
- The same literal may be defined as different constant: that’s possible and quite often.
- “All constants and no literals” is a bad idea. In particular, 0, 1 and literals used only once may be left as is.
Now, it would also be nice to have constants to write:
mkdir('path/to/new/dir', MODE_ALL_READ | MODE_ALL_EXEC)
instead of
mkdir('path/to/new/dir', 0770).
Pingback: PHP里的神奇数字 - Exakat