Eval es malvado
En primer lugar:no use eval()
a menos que haya una buena razón. Y nunca hay una buena razón .
en el peor de los casos eval()
hace que su aplicación sea vulnerable a los ataques de inyección y también es muy lenta. Un poco de investigación revela muchas razones por las que eval es un gran no-no.
No guarde su código de cálculo en la base de datos
Si lo hace y desea cambiar de PHP a otro idioma, aún tendrá el código PHP en su base de datos. Hace que sea realmente difícil migrar idiomas. Siempre debe esforzarse por hacer que la mayor cantidad de partes de su aplicación sean lo más independientes posible.
En este caso, acoplaría estrechamente el idioma que usa a la base de datos. Esa es una mala práctica.
Además, las únicas posibilidades para ejecutar sus cálculos desde la base de datos serían evaluarlos (lo cual es malo, consulte más arriba) o desensamblar la cadena con operaciones de cadena o expresiones regulares, lo que genera un esfuerzo innecesario.
Se trata de Estrategia
Para resolver su problema, debe ejecutar el código según el cálculo que necesite. Eso podría hacerse con sentencias switch-case o sentencias if. Pero esa tampoco es una solución muy elegante. Imagine que necesitaría ejecutar otras operaciones antes de calcular en el futuro, o ampliar la funcionalidad. Deberá actualizar todos sus casos o declaraciones condicionales.
Hay un buen patrón de diseño que se llama Patrón de estrategia . El patrón de estrategia resuelve problemas cuando un caso de uso se puede manejar de manera diferente, lo que probablemente sea lo que desea.
Quieres calcular algo (caso de uso) y hay diferentes tipos de cálculo para ello (diferentes estrategias)
Cómo funciona
Para implementar el patrón de estrategia, básicamente necesitas tres cosas.
- Una clase en la que inyectas tus estrategias. Es básicamente un envoltorio para sus tareas de estrategia.
- Una interfaz que será implementada por sus estrategias
- Tus estrategias
Su interfaz podría verse así:
<?php
interface CalculatableInterface {
public function calculate();
}
La interfaz se asegurará de que todas sus estrategias proporcionen un método para ejecutar el cálculo. Nada especial.
A continuación, es posible que desee tener una clase base que tome sus operadores de cálculo como argumentos de construcción y los almacene en propiedades.
<?php
abstract class Calculatable {
protected $valueA;
protected $valueB;
public function __construct($valueA, $valueB)
{
$this->valueA = $valueA;
$this->valueB = $valueB;
}
}
Ahora se está poniendo serio. Estamos implementando nuestras estrategias.
<?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';
}
}
Por supuesto que podrías limpiar esto un poco, pero lo que quiero señalar aquí es la declaración de clase.
Estamos ampliando nuestro Calculatable
class para que podamos pasar las operaciones aritméticas a través del constructor y estamos implementando CalculatableInterface
que le dice a nuestra clase:"¡Oye! Debes proporcionar un método de cálculo, no me importa si quieres o no.
Más adelante veremos por qué esto es una parte integral del patrón.
Entonces tenemos dos clases concretas que contienen el código real para la operación aritmética real. Si alguna vez lo necesita, puede cambiarlo fácilmente como puede ver. Para agregar más operaciones, simplemente agregue otra clase.
Ahora crearemos una clase en la que se puedan inyectar nuestras estrategias. Posteriormente creará una instancia de un objeto de esta clase y trabajará con él.
Así es como se ve:
<?php
class Calculator {
protected $calculatable;
public function __construct( CalculatableInterface $calculatable )
{
$this->calculatable = $calculatable;
}
public function calculate()
{
return $this->calculatable->calculate();
}
}
La parte más importante aquí es el constructor. Vea cómo escribimos sugerencias en nuestra interfaz aquí. Al hacerlo, nos aseguramos de que solo se pueda inyectar un objeto (Inyección de dependencia ) cuya clase implementa la interfaz . No necesitamos exigir una clase concreta aquí. Ese es el punto crucial aquí.
También hay un método de cálculo allí. Es solo un contenedor para que nuestra estrategia ejecute su método de cálculo.
Envolviéndolo
Entonces ahora solo necesitamos crear un objeto de nuestra Calculator
class y pasar un objeto de una de nuestras clases de estrategia (que contienen el código para las operaciones aritméticas).
<?php
//The corresponding string is stored in your DB
$calculatable = 'Division';
$calc = new Calculator( new $calculatable(15, 100) );
echo $calc->calculate();
Intente reemplazar la cadena almacenada en $calculatable
a Percentage
y verá que se ejecutará la operación para calcular el porcentaje.
Conclusión
El patrón de estrategia le permitió crear una interfaz limpia para trabajar con tareas dinámicas que solo se concretan durante el tiempo de ejecución. Ni su base de datos necesita saber cómo calculamos las cosas, ni su calculadora real. Lo único que debemos asegurarnos es codificar contra una interfaz que proporciona un método que nos permite calcular cosas.