What are PHP Magic Numbers, and how to find and remove them

Mark Harrison
5 min readApr 8, 2019

--

What is a magic number?

Most software needs defaults.

Consider a simple case — a chart. There’s a limit to the number of things that can appear on a chart. For a bar chart, it might be 10 bars, for a scatter diagram, 100 points.

This chart, however, is a new chart type for your app: (in this case) a network chart.

So you get a programmer to write the code that generates the chart, and run it.

And the programmer asks “how many items should I display, then?”

And the only response is “I don’t know — try some and let’s see what looks right.” So you do what every MVP-loving CTO does… which is to get the product owner to make the decision, launch it, and then listen for feedback.

And what tends to happen is that the programmer writes most of the code, then the product owner wanders over and they look at it for a few minutes, trying different values. Jointly, they decide that a graph with 50 items on it “looks right.”

Somewhere in the class that generates the graph, is a loop that looks like this.

$index=0;
foreach ($dataset as $row) {
if ($index == 50) {
break;
}
<some code that gets the data for the next point>
<some code that renders that data>
$index++;
}

That 50 is a magic number.

A magic number is a configuration parameter typed as a number in the middle of some code.

And that magic number is a bad thing.

Why are magic numbers bad?

Good code has two different targets:

Firstly, it needs to be understood by the machine(s) running it.

Secondly, it needs to be understood by the people looking at it.

That group of “people looking at it” includes a whole bunch of different groups:

  • the programmer writing it at the time
  • the reviewer who is approving it
  • the programmer who comes back in 6 months time to change it

It’s the third group in particular for whom magic numbers cause a problem.

Because the scenario we’ve all seen is that the software runs fine, the customers are happy, but then after those six months, we get asked to display “a bit more detail”.

Note that this isn’t a big change — this isn’t the Product Owner asking for each customer to be able to set their own value, so we’re not going to reengineer the code to make it a parameter we pass in. We just want to show some more points.

There are a few steps to this process:

  • identifying the bit of code that is defining the number of points
  • trying new values until the Product Owner is happy

There’s no special coding technique to make Product Owners happy, but the embedded magic number slows down the first step.

Finding the bit of code that’s defining how many points are displayed can take time. Maybe only 5 minutes, but those blocks of 5 minutes add up.

And that’s where the “bad” comes from. Because in this context “bad” means “wastes the time of the programmer coming along to make changes.”

What should we be doing?

Instead of having the number 50 buried in the middle of the loop, make it a constant.

Assuming, for the moment, that we’re keeping the number hardwired into the class (which may be a perfectly reasonable thing to do for the moment), then in PHP7.2 we can use a private class constant.

So the code becomes something more like this:

class OurChart
{
private const DATAPOINTSINCHART = 50; public function RenderChart(<some parameters>)
{
$index=0;
foreach ($dataset as $row)
{
if ($index == self::DATAPOINTSINCHART) {
break;
}
<some code that gets the data for the next point>
<some code that renders that data>
$index++;
}
}
}

The benefit is that the programmer coming along in the future is likely to spot the constant DATAPOINTSINCHART more quickly than they would have been able to go through function code.

Shouldn’t I have the number of points as a parameter anyway?

Probably. But that just moves the problem of avoiding magic numbers out of the class and into the calling function.

How do I find magic numbers

The tool I use is called PHPMND.

This stands for PHP Magic Number Detector.

It’s well-named.

You can see the tool, including the detailed installation instructions for the current version at https://github.com/povils/phpmnd

But if you’re using Composer, then the quick way to install it is:

composer require --dev povils/phpmnd

And then you run it by doing something like:

cd <root directory of your code>
phpmnd . --non-zero-exit-on-violation --extensions=condition,operation,return,argument

As you might imagine, the more general case is:

phpmnd <directory to scan> <options>

The full documentation includes many options (including exclude paths, whitelisting, alternative file extensions and the like), but the two I find most useful are:

--non-zero-exit-on-violation

This means that, running in a shell script, it will return a shell error if it finds any magic numbers. This is what you want when you are using it in a CI tool like drone.

--extensions=condition,operation,return,argument

This means that it will check for the presence of magic numbers in conditions, function returns, operations, and function arguments.

I find that this combination works better for me than the defaults (which are to only check returns, conditions and switch_cases.)

Running on Drone.io (CI)

This isn’t the place to talk about why you should be running a CI tool, but simply note that adding Magic Number Detection to your CI tool is worth doing.

My preferred CI tool is drone.io which (to over-simplify) uses docker images as its payloads.

This isn’t intended as an introduction to drone, but if you do use drone, you can add the following to your .drone.yml to add Magic Number Detection to your CI tests automatically.

- name: Magic Numbers
image: dockerizedphp/phpmnd
commands:
- phpmnd /drone/src --non-zero-exit-on-violation --extensions=--extensions=condition,operation,return,argument

At that point, the next time you check your branch into GitHub (or whichever service you use), then you’ll find Magic Numbers appearing as an extra pipeline stage.

It’s worth noting that PHPMND is very quick. On an Amazon free tier micro-instance, running drone.io as the CI tool, the Magic Number Detection added about 2 seconds to the pipeline time, on a project with 15323 lines of code.

--

--

Mark Harrison
0 Followers

Writing code since the early 1980s, Mark now runs a boutique coding firm in London that specialises in software for regulated markets (finance and education.)