IronWoods.es

Desarrollo web

Blog / PHP / Declaración de funciones

Gracias al uso de namespaces podemos evitar conflictos cuando añadimos nuestras clases a un proyecto. En el caso de las funciones se requiere una verificación previa a la declaración y evitar así un error en caso de que ya existan.

Mi solución...

En los últimos meses estuve estudiando como implementar una serie de módulos en PrestaShop. El problema fundamental era que podían trabajar juntos o por separado y tenían partes comunes, tanto clases como funciones.

Declaré las funciones sin más, en un fichero que llamaremos "functions.php", que es común a todos los módulos.

Una vez que se carga el primer módulo, las funciones contenidas por el fichero se encontrarán definidas. Si se instala un segundo módulo, al cargarse, cosa que PrestaShop hace en orden alfabético de acuerdo a su nombre, se produce un error con la primera función del citado fichero del segundo módulo, ya que tiene el mismo nombre que en el primer fichero y de hecho es la misma, por lo que una vez hagamos lo que sigue por esta parte no habría mayor problema.

Este error se evita declarando la función dentro de un condicional.

Las declaraciones de mi fichero "functions.php", son como esta:

$func = "functionName";

if (! function_exists($func)) {


/**

* Function description

*

* @param type $param_name

* @return type

*/

function functionName($param_name)

{


//

// Function logic here

//

//



return $res;

}

}

Convergencia...

Hace un par de días abrí el fichero dónde define sus helpers "Laravel 5.1" y así se ve la primera de sus declaraciones:

if (! function_exists("append_config")) {


/**

* Assign high numeric IDs to a config item to force appending.

*

* @param array $array

* @return array

*/

function append_config(array $array)

{


//

// Function logic here

//

//


return $array;

}

}

¿Coincidencias?

Si trabajas en algo durante suficiente tiempo siempre acabas encontrando las mismas soluciones a los mismos problemas. Nada nuevo bajo el sol como decían por ahí...

En realidad yo almacené el nombre de la función en una variable justo antes de la comprobación, son unos bytes más de memoria... Lo hice en parte por reutilizar la estructura pero sobre todo porque puedo guardar ese dato en un log de errores si se diera.

¿A prueba de fallos?

No existe nada a prueba de fallos, aunque puede parecerlo. Se trata siempre de minimizar la posibilidad de que algo falle.

Bien, hemos evitado la posibilidad de que el programa cruja porque cargamos dos o más veces una misma función. Y si la función que existe es la nuestra, como en mi ejemplo con los módulos de PrestaShop, no pasa nada.

Definir, cargar, usar... ¿Problemas?

Sí, siempre. Si algo funciona a la primera, ya puedes sentirte como un hámster sobre un queso manchego.

Nota: a los hámsteres les encanta el queso, aunque les acorta la vida considerablemente. Lo que sabe bien, no suele ser muy sano.


Bien, existe la posibilidad de que una función con el mismo nombre ya esté declarada en la aplicación a la que añadimos nuestra declaración.

En este caso, nuestro código usará la función pero fallará. Si no, en primera instancia, por una diferencia del número de argumentos y su tipo (lo que nos indicaría el tipo de fallo), porque la lógica contenida en la función no es la misma que la que nosotros implementamos, lo que podría ser difícil de descubrir.

Lógica, no lógica... ¿qué esta pasando?

Un ejemplo. Imaginemos que definimos una función a la que pasamos una secuencia de números y nos devuelve un array de primos. Pero ya existe una función de igual nombre que acepta una secuencia de números y devuelve no sólo los primos sino también los múltiplos de 2.

Lo anterior puede ser un problemón... y con todo lo que hicimos para evitar errores estamos silenciando un error de lógica que puede pasar desapercibido.

Tal vez, nuestra función pasó sin problemas test unitarios que nos "aseguran" que hace lo que debe hacer (sólo que en realidad no la estamos usando, cosa que aun no sabemos).

El programa falla unas veces y otras no. Esto depende de los números que pasemos. Sin múltiplos de 2 todo va como debería... Pero ¿y cuándo falla? ¿cómo podemos saberlo? Podemos poner en producción un código que parece correcto.

Vale, hay otros test automatizados más complejos que podemos implementar, pero me estoy yendo por otro lado...

¿Log de errores?

No hay nada más frustrante que tener un error y ningún tipo de feedback para encontrarlo.

Ya tenemos un bloque condicional, bastará con añadir una sentencia else y generar una línea de error, en el log, en caso de que la función esté definida con el punto en que lo ésta a ser posible... Estaremos alimentando el fichero si cargamos nuestra función varias veces pero podremos encontrar el problema fácilmente si no sabíamos de su previa definición.


21-10-2017