Mostrando las entradas con la etiqueta debug. Mostrar todas las entradas
Mostrando las entradas con la etiqueta debug. Mostrar todas las entradas

Cambio de opinión: en algunos casos acepto el uso de null == $var

Hace un tiempo hice un extenso post comentando sobre el "difundido fanatismo" de algunos desarrolladores de usar al revés las condiciones de igualdad en un if , en mi opinión "contranatura", sin respetar como alguna vez nos enseñaron nuestros primeros docentes (ver artículos como "Código como documentación"):

if ($variable == "valor")

Por lo que este orden nos permite hacer una redacción natural de la lógica que estamos codificando, es decir:

if ($edad == 30)

Se traduciría como

"Si la edad es igual a 30"

Por lo que actualmente hay una "seudo-moda" de hacerlo al revés,

if (30 == $edad)

Se traduciría como

"Si 30 es igual a edad"

Algo que, luego de haber pasado algunos años dando clases a distintos niveles de desarrolladores, empezando por personas con cero conocimiento del tema, siento que esta práctica no hace más que dificultar la lectura natural de las condiciones, y en consecuencia, ofuscar el código.

Amante de lo simple como soy (considero que el verdadero arte y esencia de la programación es hacer las cosas simples, no complejas), me opuse completamente a esta práctica, aún con los argumentos técnicos de que esta técnica podría salvarnos en caso de asignaciones accidentales del tipo:

if ($edad = 30)

Lo cual en PHP terminaríamos modificando el valor de $edad (porque estamos usando un solo "=" y no dos), algo que no sucedería si hiciéramos

if (30 = $edad)

De todas formas me pareció exagerado al extremo, cambiar algo que se hizo por años, desde los inicios de los tiempos, solo para cubrir esta "hipotética situación tan poco probable", ni que hablar que todo IDE respetable nos detecta este error y es fácil de ver y corregir.

Bien, suerte que gracias a los errores uno aprende y me pasó hace relativamente poco este error con consecuencias bastante importantes, por lo que voy a describir en qué situación SI sugiero esta práctica (no siempre, no con todo el código):
  • Si por una razón extrema tienes que modificar código en producción
  • y además no puedes usar un IDE que te asista, y tienes que usar un editor sin asistencia en la codificación (como el Editplus, Notepad++, etc)
Sí, y solo sí, mientras tengas que alterar o agregar una condición que pueda generar este error, invierte la condición para estar cubierto ante cualquier distracción. Pero luego de hecho, fuera de producción, corrige el código, para clarificar su entendimiento.

De todas formas, lo veo complicado de hacer si no estás acostumbrado a ello.

Pero bueno, cuando me sucedió me hizo reconocer que había una situación muy específica que podría ser aplicable y útil, aún para una persona con experiencia como yo ;-)

¿Alguna vez te fue verdaderamente útil esta práctica? ¿alguna otra?

Excepciones: Cómo forzar un "backtrace" en un sistema que no usa try/catch

Para los que venimos de haber trabajado, aunque sea académicamente, en otros lenguajes/plataformas como Java o .Net, trabajar con excepciones es un tema de todos los días. Al principio, cuando uno está aprendiendo y surge el primer volcado de una excepción (aparecen en pantalla muchas líneas con la información del error) hay una que resalta sobre todas y es la que demoramos más aprender a interpretar: el "backtrace" o ruta de ejecución desde que se inicia el sistema, todas las invocaciones que van sucediendo, en qué línea salta la ejecución, hasta terminar en el lugar exacto donde falló el sistema.

Recuerdo cuando probé por primera vez PHP5 (una beta) y lo primero que fui a probar fue hacer un try / catch forzando el fallo de una conexión a la base de datos con funciones nativas del lenguaje. Mi sorpresa fue mayúscula al comprender que los try / catch no funcionan a menos que nos aseguremos que la función que estamos usando retorne una excepción, y para colmo, PHP no lo hace por defecto! ;-) Así que no quedó otra que dejar las excepciones para nuestros desarrollos donde todo método de nuestras clases debía tener un throw new Exception('mensaje de error');

A pesar de mi desilusión, esto no era problema para los sistemas que hacíamos de cero de ahora en más, pero... ¿cómo haríamos con los sistemas que ya están funcionando y que no puedes salir a modificar miles de líneas de código para que un try / catch funcione?

Particularmente considero que una de las informaciones más importantes para poder hacer un debug de qué falló es el "backtrace". No es lo mismo ver en el log del sistema donde falló algo que ver quién invocó antes y con qué información para que fallara esa rutina.

Bien, hace un tiempo que lo buscaba y lo encontré por accidente en un foro, así que les comento la forma de uso y cómo lo pueden aplicar a sus sistemas "legacy":


class Debug
{
public static function getBacktrace()
{
$ex = new Exception();
return $ex->getTrace();
}
public static function getBacktrace2String()
{
$ex = new Exception();
$trace = $ex->getTrace();

/* Elimino la primer linea que
siempre es la misma y
hace referencia a la
invocación de esta clase */

array_shift($trace);

$trace_ret = '';
$linea = 0 ;
foreach ($trace as $item){

$linea++;

$trace_ret .=
"(".$linea.")"
."[file: ".$item['file']
.":".$item['line']."]"
."[".$item['class']
.$item['type']
.$item['function']."]"
."[args: "
.implode(',',$item['args'])
."] ";
}
return $trace_ret;
}
}


Luego, a continuación, tenemos una clase Log que lo único que hace es persistir cualquier información del sistema que queramos en un archivo de log, por lo tanto ahora agregamos la ejecución del backtrace y lo registramos en el log tal cual nos llega:


/* Método modificado de la clase Log */

public static function setError()
{
$backtrace = Debug::getBacktrace2String();
self::logToFile('errores.log',$backtrace);
}


Listo, ahora automáticamente tenemos que cada vez que el sistema deba registrar un error, este, tendrá toda la información de backtrace, que se vería en nuestro sistema de la siguiente manera:

2009-10-26 09:40:11 (1)[file: /var/www/class/App.php:81][Log::errores][args: /var/www/class/Prueba.php,75,ERROR GRAVE: no se obtuvo el resultado esperado en el metodo: getProveedor,,0,SELECT * FROM proveedores WHERE estado = 1 AND id = 111 AND idCuenta = 1234] (2)[file: /var/www/public/sys/send.php:48][Prueba->getProveedor][args: 111,1234]

Lo cual si indentamos en base a los (n) que son los saltos que va dando la ejecución,

2009-10-26 09:40:11

(1)[file: /var/www/class/App.php:81][Log::errores][args: /var/www/class/Prueba.php,75,ERROR GRAVE: no se obtuvo el resultado esperado en el metodo: getProveedor,,0,SELECT * FROM proveedores WHERE estado = 1 AND id = "111" AND idCuenta = 1234]

(2)[file: /var/www/public/sys/send.php:48][Prueba->getProveedor][args: 111,1234]

Aquí se pueden ver dos saltos, el (1) es lo que veríamos siempre en nuestro log, la aplicación que propiamente falla, pero en el (2) estamos viendo desde donde realmente se inicio la ejecución que luego terminó fallando.

De todas formas, esto es un "parche", deberíamos usar try/catch en todos nuestros sistemas de ahora en más (si es que ya no lo estás usando), pero una forma de mejorar lo que ya existe es agregar un forzado "backtrace".

Espero que lo prueben en sus sistemas y les sea de utilidad ;-)

Entradas populares