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 ;-)

Propuesta de trabajo: Desarrollador PHP / PHP5 / POO / MySQL (Argentina)

Les comparto la siguiente solicitud que me hace llegar una empresa, si les interesa pueden enviarme sus cv's a mi cuenta de correo (si no la sabes, no calificas para el puesto ;-))

Copio el aviso:

"Incorporaremos a nuestro equipo de desarrollo 2 personas con el siguiente perfil:

Requisitos:

- Experiencia comprobable en PHP
- Programación PHP 5 orientada a objetos (POO avanzado)
- Base de datos MySQL 5

Deseables (no excluyente):

- Frameworks PHP (preferencia Zend)
- Frameworks JavaScript (preferencia JQuery)
- Manejo de entornos Linux
- AJAX (XML y JSON)
- Maquetado en XHTML/CSS, HTML, WML
- Estudiante avanzado o graduado de carreras de informática o similar
- Capacidad de trabajo en equipo.
- Compromiso con calidad de productos y manejo de tiempos.

Empresa del rubro mobile (SMS/MMS/WAP),

Zona Palermo / Belgrano"

Tienen mi absoluta reserva.

Cursos SURFORCE: viento en popa!

Sí, nuevamente estoy poco en el blog, con mis colegas (Andrés Guzman y Nicolás Fornaro) estamos en pleno trabajo por el dictado de los cursos ;-)

Como es de público conocimiento, la última fecha para inscribirse durante este año fue a principios de octubre ( ya que los cursos duran 2 meses) y estaríamos terminando las primeras semanas de diciembre.

En este momento están activos los siguientes grupos:
  • Introducción a Zend Framework (3 grupos, dos abiertos y uno privado)
  • POO para PHP5 (3 grupos, dos abiertos y uno privado intensivo)
  • Análisis y Diseño Orientado a Objetos (2 grupos)
  • Introducción a los Patrones de Diseño (1 grupo)
  • Taller Zend de Zend Framework (1 grupo)
De todas formas, les sugiero que se registren como usuarios, así cuando existan novedades les hago un envío de las próximas fechas de cursos (tentativamente finales de enero, principios de febrero, a confirmar).

En el caso de empresas, se pueden fijar otras fechas especiales, armar grupos privados, o hasta dictados intensivos (en vez de durar 2 meses, se puede hacer en 1 mes o hasta en 2 semanas, dependiendo el tiempo de dedicación diario del alumno).

Espero que para los próximos cursos no te quedes afuera! ;-)

[SURFORCE] Cambio de fecha de inicio de cursos: lunes 12 de Octubre

Hasta esta hora estuve confirmando los pagos de los cursos y aún hay alumnos atrasados, por lo que para poder iniciar con los grupos completos vamos a tener que postergar unos días el inicio de los cursos.

Se extenderá el plazo hasta el próximo viernes para estar al día con el pago del curso y posteriormente SIN EXCEPCIONES no se recibirán más pagos y los cursos darán inicio el próximo lunes 12 de Octubre.

Aún quedan algunos lugares en los cursos para llegar al tope máximo de 20 alumnos, así que quién envíe el pago inmediatamente obtiene el lugar disponible que aún no se ha pago.

¡Si aún no pudiste confirmar tu lugar, esta es la última semana!

Cualquier duda o problema estamos a sus órdenes.

Saludos!

[SURFORCE] El inicio de cursos de Octubre se retrasa 1 día

El fin de semana estuve confirmando pagos de los cursos y aún hay algunas vacantes y bastantes alumnos atrasados, por lo que para poder iniciar con los grupos completos vamos a tener que postergar provisoriamente un día el inicio de los cursos (martes 6/10).

Al finalizar el lunes (hoy) se evaluarán los cursos que inician mañana y cuales se postergan una semana hasta el próximo lunes para poder cerrar con el cupo previsto de alumnos.

¡Si aún no pudiste confirmar tu lugar, hoy es el día!

Ni bien tenga novedades les estoy enviando un email a todos los usuarios registrados

Sepan disculpar estos inconvenientes.

Discusión: "¿cuál es tu grado de madurez en POO?"

Todo surgió respondiendo una consulta en un foro y me pareció interesante discutirlo aquí. Les comparto los "niveles" o "etapas" que considero que todo desarrollador pasa cuando decide dejar atrás la programación estructurada.

Generalmente todos empiezan por el primero y luego pocos llegan hasta el último (la mayoría solo logra llegar al 2 y se estancan en el 3):
  1. "Programación Estructurada": el inicio, desde donde parten, la "nada" ;-)
  2. "Programación Estructurada con uso de Objetos": usan objetos "sueltos / aislados", no los saben relacionar entre ellos, se reusan a leer material con "conceptos", creen que es mejor aprender a través del "prueba y error".
  3. "Programación Orientada a Objetos Sin Conceptos Claros": "creen que lo hacen bien" pero luego heredan clases mecánicamente pensando que es el mejor mecanismo para reusar código.
  4. "Desarrollo 100% OO": personas que invirtieron sabiamente su tiempo para aprender primero los conceptos para luego poder aplicarlos de forma correcta

Puedes seguir desarrollando estructurado y apoyarte en algunos objetos, pero lo ideal es que te vayas adecuando a la forma de trabajo con objetos y estructures todo el sistema de esta forma (y eso empieza por "pensar en objetos", no en código).

Nuevamente, "crear clases y jugar con objetos" es algo que todos podemos hacer, pero desarrollar "orientado a objetos" es un tema más de tener los conceptos claros que de saber la sintaxis de cómo se codifica.

Recomiendo que antes de preguntar qué es o qué no es POO, revisar el apartado de Wikipedia sobre POO y paradigmas de programación y las diferencias entre ellos.

¿Tú, en qué nivel de madurez estás? ¿crees que hay otros niveles o etapas más? ;-)

Primeros pasos en Zend Framework: cómo "aprender a pescar"

Una de las prácticas que trato constantemente de inculcar es que "aprendan a pescar" y a no "depender que los alimenten en la boca".

Durante un curso, y particularmente después que termine, deberían siempre seguir estos pasos respetando el orden:


Paso 1) "Siempre consulta primero el manual oficial"

Buscar en el manual oficial http://framework.zend.com/manual/en/

Intenta tratar de identificar el componente que provee la funcionalidad. Por ejemplo, si no sabes el significado de los parámetros del método render(). Este método es parte del componente Zend_Controller - zend.controller.action y concretamente aquí comenta los parámetros zend.controller.action.viewintegration.render



Paso 2) "Consulta la API online del Framework"


En caso de no encontrar una explicación o esta sea breve, consultar la API online que se genera a partir de los fuentes a través del uso de PHPdoc - http://framework.zend.com/apidoc/core/ (en el combo superior pueden seleccionar el componente que quieren visualizar, Zend_Controller, y luego buscar la clase Zend_Controller_Action).



Paso 3) "¡Usa el código, Luke!"


Como complemento hasta pueden consultar el mismo código fuente de la librería, que ahí se aprende también mucho de cómo funciona el framework y hasta buenas prácticas de desarrollo: revisa el código de /library/Zend/Controller/Action.php


Paso 4) "Usa Google"

Y recién, solo recién, navegar en Google buscando artículos (y con cuidado, hay que aprender a discernir qué es bueno y qué es malo, ya que hay mucha basura).


En Resumen


Todo esto lo sacan navegando el menú de documentación del sitio oficial framework.zend.com, y desaconsejo terminantemente empezar primero buscando en google y creyendo en el primer artículo que encuentran en el buscador (aunque sea de mi autoría ;-)), ya que hay demasiados charlatanes que hablan sin saber y conceptualmente pueden llevarnos a cometer errores o adoptar malas prácticas.


Que te sirva, aprende a pescar por tu cuenta y no esperes que te resuelvan tus propios problemas ;-)

Entradas populares