Eval is Evil
First of all: don't use eval()
unless there is a good reason. And there is never a good reason.
in the worst case eval()
makes your application vulnerable to injection attacks and also it's very slow. A bit of research reveals plenty of reasons why eval is a big no-no.
Don't save your calculation code into the database
If you do so and you would like to switch from PHP to another language you would still have PHP code in your database. It makes it really hard to migrate languages. You should always strive to make as many parts of your application as independent as possible.
In this case you would tight-couple the language you use, to the database. That's a bad practice.
Also the only possibilities to run your calculations from the database would be to eval them (which is bad, see above) or to disassemble the string with string operations or regex which causes unnecessary effort.
In order to solve your problem you must execute code dependent of which calculation you need. That could be either done with switch-case-statements or if-statements. But that's also not a very elegant solution. Imagine you would need to execute other operations before calculating in the future, or extend functionality. You would need to update all your cases or if-statements.
There is a nice design-pattern which is called Strategy Pattern. The strategy pattern solves problems when one use-case can be handled differently which is probably what you want.
You want to calculate something (use-case) and there are different calculation types for it (different strategies)
How it works
To implement the Strategy pattern you basically need three things.
- A class where you inject your strategies. It's basically a wrapper for your strategy tasks.
- An interface which will be implemented by your strategies
- Your strategies
Your interface could look like this:
<?php
interface CalculatableInterface {
public function calculate();
}
The interface will make sure that all your strategies provide a method to actually run the calculation. Nothing special.
Next you may want to have a base class that takes your calculation operators as constructor arguments and stores them into properties.
<?php
abstract class Calculatable {
protected $valueA;
protected $valueB;
public function __construct($valueA, $valueB)
{
$this->valueA = $valueA;
$this->valueB = $valueB;
}
}
Now it's getting serious. We are implementing our strategies.
<?php
class Division extends Calculatable implements CalculatableInterface {
public function calculate()
{
return ($this->valueB != 0) ? $this->valueA / $this->valueB : 'NA';
}
}
class Percentage extends Calculatable implements CalculatableInterface {
public function calculate()
{
return ($this->valueB != 0) ? (100 / $this->valueB) * $this->valueA : 'NA';
}
}
Of course you could clean this one up a bit, but what I want to point out here is the class declaration.
We are extending our Calculatable
class so that we can pass the arithmetic operations via constructor and we are implementing the CalculatableInterface
which tells our class: "Hey! You must provide a calculate method, I don't care whether you want or not.
We'll see later why this is an integral part of the pattern.
So we have two concrete classes that contain the actual code for the actual arithmetic operation. If you would ever need to, you could change it easily as you see.
To add more operations just add another class.
Now we will create a class where our strategies can be injected. Later you will instantiate an object of this class and work with it.
Here is how it looks like:
<?php
class Calculator {
protected $calculatable;
public function __construct( CalculatableInterface $calculatable )
{
$this->calculatable = $calculatable;
}
public function calculate()
{
return $this->calculatable->calculate();
}
}
The most important part here is the constructor. See how we type-hint our interface here. By doing that we make sure that only an object can be injected (Dependency Injection) whose class implements the interface. We do not need to demand a concrete class here. That's the crucial point here.
Also there's a calculate method in there. It's just a wrapper for our strategy to execute it's calculate method.
Wrapping it up
So now we just need to create an object of our Calculator
class and pass an object of one of our strategy classes (that contain the code for the arithmetic operations).
<?php
//The corresponding string is stored in your DB
$calculatable = 'Division';
$calc = new Calculator( new $calculatable(15, 100) );
echo $calc->calculate();
Try replacing the string stored in $calculatable
to Percentage
and you see that the operation for calculating the percentage will be executed.
Conclusion
The strategy pattern allowed you to create a clean interface for working with dynamic tasks that are only made concrete during runtime. Neither your database needs to know how we calculate things, nor your actual calculator does. The only thing we need to make sure is to code against an interface that provides a method to let us calculate things.