Señales de que tu PHP necesita una refactorización

Dado que a bastante gente le gustó el Signs your php needs refactoring y unas cuantas personas insinuaron la necesidad de una traducción al español, he decidido actuar proactivamente y hacerlo yo misma. Y sin más dilación, sigue el artículo en castellano.

Recientemente, he tenido que trabajar con una aplicación en php que no sólo me ha dado más de un dolor de cabeza, sino que además me ha obligado a hacer uso de toda mi paciencia. Y mientras estaba en ello, pensé Esto es buen material para un artículo, de modo que nadie más haga lo mismo en el futuro, y que nadie más tenga que sufrir lo mismo que he tenido que sufrir yo.

Así que aquí están las señales de que tu PHP necesita una refactorización seria, ya mismo:

Usa variables globales

Esto tiene dos interpretaciones. En primer lugar, tenemos esa forma de programar en la que una serie de variables se pone en el ámbito global, cada función hace un php global $varA, $varB; etc, y procede entonces a hacer lo que quiera que deba hacer con las variables. Esta es la peor; montones de errores extremadamente difíciles de trazar plagan los programas escritos con esta técnica, más que nada porque las variables globalizadas acaban sobreescribiendo variables que no deberían haber sobreescrito. Es también muy complicado poder hacer un seguimiento de quién está modificando qué, porque no se usan llamadas a funciones para acceder a las variables, sólo declaraciones global. No hay visibilidad en absoluto.

Y luego tenemos la segunda interpretación, en la que las variables que ya son globales de por sí ($_REQUEST, $_POST, $_GET) son usadas (y se abusa de ellas) a lo largo y ancho de toda la aplicación. Te puedes encontrar, por ejemplo, modelos de datos accediendo directamente los arrays $_POST o $_GET, lo cual no sólo introduce complejidad extra en su lógica (por ejemplo: ¿qué ocurre si decidimos enviar los datos con GET en lugar de con POST?) pero además también complican el hacer pruebas unitarias a los diferentes componentes de la aplicación. No puedes simplemente enviar un array de datos a un modelo: ha de ser enviado con POST o GET.

Insisto, esta forma de hacer las cosas proporciona mala visibilidad, ya que no se puede realmente distinguir quién está cambiando qué.

La fiesta de los corchetes

Cada vez que me encuentro cosas como ésta:


$variable['field']['subfield']['subsubfield1'] = 'value1';
$variable['field']['subfield']['subsubfield2'] = 'value2';
$variable['field']['subfield']['subsubfield3'] = 'value3';
$variable['field']['subfield']['subsubfield4'] = 'value4';
...

... sólo tengo un adjetivo en mente: asqueroso.

Es más fácil y rápido hacer algo tal que esto:


$variable['field']['subfield'] = 
  array('subfield1'=>'value1', 'subfield2'=>'value2', 'subfield3'=>'value3', 'subfield4'=>'value4');

Puedes indentarlo con un par de valores por línea, al gusto. La clave está en que si en algún momento necesitas cambiar 'field' o 'subfield', no necesitas hacerlo cuatro veces (incluso si existen cosas como Buscar y Reemplazar). ¡He encontrado casos en los que la Fiesta de los Corchetes ha contaminado veinte e incluso más líneas!

Otro ejemplo de abuso de corchetes se da cuando se recuperan resultados de una base de datos en forma de arrays y son accedidos a saco. Entonces una acaba encontrándose con código como el siguiente para acceder al primer elemento:


$prop1 = $res['results'][0]['property1']
$prop2 = $res['results'][0]['property2']
...

Más fácil y corto:

list($prop1, $prop2...) = array_shift($res['results']);

¿Me podría alguien explicar por qué esa obsesión de la gente con las Fiestas de Corchetes?

Todo es un array

Los objetos se crearon por alguna razón. El hecho de que crear arrays es muy sencillo en php y no tienes que reservar memoria primero y todo eso, al contrario que en otros lenguajes, no quiere decir que debas estructurar todos tus datos con arrays.

El problema de usar arrays como contenedores estructurados de datos es que todo lo que hay dentro es público y no hay forma de usar esas fantásticas funciones set/get que se encargan de impedir que datos incorrectos entren en el array. Por tanto, ¡usa objetos!

Esa es una razón más para pasarte a php5: puedes tener propiedades realmente privadas, de modo que nadie modificará los datos internos de tu clase, tal y como ocurre con los objetos de php4.

Código duplicado

Esto sí que me saca de quicio. Ya no sé ni cuántas veces he leído el código de una aplicación y me he encontrado las mismas tres líneas copiadas y pegadas de función en función y de fichero en fichero. Y me pregunto: ¿les habrán enseñado el concepto de función?

Si hay algo que tiene un aire remotamente familiar a algo que has hecho un poco antes, ¡lo más probable es que esté pidiendo a gritos unos toques de refactorización! Y si necesitas cierta funcionalidad en dos lugares separados, no copies y pegues el código, ¡haz una función!

No sólo el código final acaba siendo más corto, sino que además es más fácil entender cinco líneas de código, que se pueden expandir a cien cuando miras dentro de cada función, que encontrarse de golpe con cien líneas de código y tratar de entender qué está ocurriendo ahí.

El switch interminable

Hablando de bloques de cien líneas, también me he encontrado repetidamente con switch's de trescientas líneas. Y no se lo desearía ni a mi peor enemigo. ¡Es crueldad y explotación mental y debería ser perseguido criminalmente!

Obviamente, los cientos de líneas deberían ser divididos en funciones, de modo que no haga falta usar media hora en un switch para hacerse una idea de qué hace.

Tienes que modificar demasiados ficheros

Si tienes que modificar demasiados ficheros para añadir o modificar incluso la funcionalidad más simple, hay algo que no funciona en tu diseño. Esto suele ocurrir cuando las diferentes partes de la aplicación están demasiado acopladas y cada una necesita saber demasiado acerca de las demás.

Lo peor de esto es que tiende a manifestarse cuando la aplicación se hace grande y está además en modo producción-a-tope, de modo que se piden nuevas funcionalidades constantemente, y la mayoría de programadores acaba mintiéndose a sí mismos: Bueno, no pasa nada por cambiar un par de docenas de ficheros para añadir esta funcionalidad nueva, ya lo simplificaré después. Y todos sabemos perfectamente que ese después nunca llega.

Valores fijos en el código

Las constantes se crearon por alguna razón. Es malo encontrarse cosas tal que


if($result==4) {
// bla
} else if($result==5) {
// bla bla
}

donde determinados valores tienen un cierto significado, pero es aún peor encontrarse cosas como


if($result=='row1') {
// bla
} else if($result=='row2') {
// bla bla
}

ya que por ejemplo, si tecleas algo mal y en lugar de row1 escribes rowl, PHP no se quejará porque es una comparación perfectamente válida. Lo que tú necesitas es usar constantes, de modo que cada vez que intentes usar una constante que no ha sido definida, PHP se queje y tú lo arregles inmediatamente.

Además, aunque parece sencillo distinguir entre "1" (el número uno) y "l" (L minúscula), no es tan sencillo para personas con problemas como dislexia, o simplemente, tú mismo, cuando estés cansado. Y los errores introducidos por culpa de no usar constantes son terriblemente difíciles de localizar; la mayoría de ocasiones hay que pasarse horas yendo línea por línea y tratando de averiguar por qué no funciona. Es mucho más fácil y preventivo definir constantes y dejar de preocuparse de tener que actualizar todos los valores puestos a mano en el código si hace falta cambiarlos luego.

Inconsistencias en la interfaz

Es algo de lo que no se suele hablar mucho, pero creo que es realmente preocupante en PHP. No estoy segura de si es algo inducido por el lenguaje en sí (no hay más que echar un vistazo a las funciones de cadenas), pero hay una extraña tendencia en las aplicaciones php que las lleva a no seguir convención alguna en lo que respecta a argumentos de funciones y tipos de valores devueltos.

A veces las funciones devuelven valores booleanos (true o false) o valores que pueden hacer de booleanos (0 ó 1), otras veces usan valores de retorno al estilo C (0 para éxito y 1 o más para los errores), que son obviamente lo contrario a usar el booleano true para éxito y el booleano false para error, ya que en PHPlandia 0==false y 1==true (a menos que uses los operadores === y !==, más estrictos, pero desafortunadamente ese no es el caso de la mayoría de desarrolladores en php)

En otros casos, las funciones no devuelven nada, pero en cambio usan un array pasado por referencia para escribir resultados en él, algo tal que esto: php function my_function(&$return_array)

Esta práctica es algo que realmente me desagrada, porque a menos que acabes de ver la definición de my_function, es imposible distinguir si php my_function($my_array) está usando $my_array como un parámetro de entrada o de salida.

Es mejor ponerse de acuerdo en un estilo para devolver valores, y usar ese estilo consistentemente en todas las funciones que escribas. Tal que: siempre devolveremos un objeto con dos propiedades, error que es booleano y representa si la función falló o no, más results que contendrá datos si necesitamos devolver algo.

Con eso, sabes que siempre puedes verificar el valor de $res->error en lugar de tener que verificar primero si existe o no, y entonces ver el valor en sí.

Ya captas la idea...

(Nota: en los comentarios de la versión en inglés se recomienda usar excepciones en lugar de esta solución. Lamentablemente, es sólo aplicable si usas php5)

Conclusión

Algunos de estos problemas prácticamente desaparecerían si tuviéramos un editor de php verdaderamente bueno. Aunque Eclipse cumple su labor decentemente con los plugins para php, la mayor parte del tiempo y debido a la forma en que php funciona, todo queda patas arriba y la funcionalidad de autocompletado es absolutamente inútil porque Eclipse no puede encontrar las clases. Refactorizar es algo que casi depende sólo de tu memoria a corto plazo (creo que ví algo parecido a esto en algún sitio...); otros problemas dependen de si la gente programa estando drogada (recordemos el switch de 300 líneas) o de la competencia en PHP y diseño de software en general.

¡Y ahora es el momento de volver a tu aplicación y mirarla con ojos críticos!