Ejemplo de excepciones desde Zend Framework

Siguiendo con el post anterior sobre el tema, quería mostrarles otro ejemplo de cómo se manejan las excepciones desde Zend Framework, donde la diferencia principal radica en que obtenemos una lista enorme de clases preestablecidas para el manejo de excepciones (como sucede en lenguajes como Java), lo que nos permite usarlas de forma directa sin tener que implementar nada más que el try/catch.


try{

$this->view->clientes = Models_Clientes::getAll();

}catch(Zend_Db_Statement_Exception $e){

$this->view->mensajeError =
'Se ha producido un error interno al intentar recuperar datos.'
.' Por favor envíe un email a sistemas@surforce.com' ;

}catch(Zend_Db_Adapter_Exception $e){

$this->view->mensajeError =
'Se ha producido un error al conectar a la base de datos.'
.' Por favor reintente en unos minutos';

}catch(Zend_Exception $e){

$this->view->mensajeError =
'Se ha producido un error inesperado.'
.' Por favor reintente en unos minutos';
}

$this->render ();


Repasando: las excepciones deben ordenarse como si fueran reglas de un firewall, la regla más específica debe ir al principio y la más general al final (ya que si lo hacemos al revés siempre entrarán por la primer coincidencia en la regla).

Con Zend_Db_Statement_Exception entrarán todas las fallas que ocurran por errores de sintaxis en el ejecución de sentencias SQL (error bastante grave), en el segundo caso con Zend_Db_Adapter_Exception cualquier falla en la conexión al motor de base de datos y en el último, ya una excepción genérica como Zend_Exception, capturamos cualquier otro error que no pudimos especificar con detalle (algo así como la "pantalla azul de la muerte" de Windows ;-)).

Si no agregamos este try / catch en el momento de la falla enviará directamente en pantalla todo el "volcado" de la Excepción y concluirá la ejecución de la aplicación de forma abrupta.

Pero, si además de esto agregamos en la vista un código como el siguiente, podremos enviar un mensaje al usuario sin distorsionar la interfaz de nuestra aplicación.

En este ejemplo, el código para desplegar el mensaje de error se encuentra en el layout de la aplicación:

< ?php if(!is_null($this->mensajeError)): ?>
< ?php echo $this->mensajeError ?>
< ?php endif ?>

Y con un poco más de trabajo, puede quedar más estético y hasta con una animación (moviendo lateralmente el cuadro un par de veces, como para alertar al usuario) y se vería de esta forma:


Las excepciones son la única forma de tener un manejo uniforme de errores (algo que tienen todos los lenguajes POO modernos) y poder controlar mejor cómo responder a situaciones no esperadas y que nuestra interfaz no quede "desarmada" delante del usuario, lo cual podríamos hasta definir en la configuración que cada vez que ocurra un "error general" envíe un email al departamento de sistemas con todos los datos para poder analizar el fallo ni bien sucede.

¿Y ustedes, usan excepciones? ¿qué otras ideas han aprovechado para implementar y mejorar la funcionalidad de nuestros sistemas?

Último post del blog

(Suspendido Brasil, feliz Día de los Inocentes ;-))

... por razones de índole laboral voy a cambiar de país de residencia (nuevamente, no hace un año que nos vinimos de Uruguay a Argentina) y voy a cambiar de tecnología: me paso completamente a Java, más concretamente JEE.

Saludos, sepan disculpar, voy a estar bastante ocupado con la mudanza (vender todo nuevamente para irnos a Brasil), así que hasta la vista! (alguien más llenará este vacío, seguro).

"Rendimiento: ¿conviene utilizar POO?"

Ayer enviaron la siguiente consulta a un foro, lo cual generó que escribiera esta respuesta que casi se me convirtió en un post y para no perderla, lo dejo documentado aquí con links de referencia y un poco más actualizado su contenido:

Consulta:
"Buenas a todos. Hace unos meses me comencé a meter de lleno con php. Después tuve que dejar otro tiempo más porque me surgieron proyectos en otras plataformas, y ahora estoy retomando para hacer un proyecto personal en php.

Me surgieron algunas dudas con respecto a la verdadera utilidad, en relación al rendimiento, de la programación orientada a objetos en php. Si bien no conozco como trabaja internamente php, es un lenguaje interpretado, es decir, que en cada carga o recarga de la página las clases en cuestión deben ser interpretadas una y otra vez.

¿Es más ineficiente interpretar una clase, que interpretar una secuencia de comandos? ¿Alguien conoce de esta interna?. En .Net las clases son compiladas una vez que fueron solicitadas, por lo tanto, con cargas o recargas posteriores solo resta usarlas tranquilamente, ya que sabemos que ya ha pasado por esa etapa previamente.

Solo me estoy enfocando en el rendimiento de la aplicación. Si lo analizamos desde otro punto de vista, de seguro que encontramos muchas más ventajas a la programación orientada a objetos que a la programación procedimental.
Espero que alguien me oriente un poco en esto."


El moderador y amigo GatorV le responde:

"Es mínimo el impacto, si a caso lo que es más es la memoria ram que llegue a consumir, aunque no es nada que impacte, ya que el compilador de PHP esta muy optimizado para evitar desperdiciar memoria.

Por otro lado si te importa un poco el tema de estar re-interpretando las clases, una vez que termines puedes usar Zend Optimizer para guardar solamente las clases en estado compilado, y asi eliminar el overhead de estar interpretando y ejecutando".


Mi respuesta (actualizada):

"Tus comentarios (completamente justificados) me hacen acordar a los mitos de la "optimización extrema" en PHP, algo que veo sí completamente injustificado.

La forma en que se hace las pruebas es generalmente muy fuera de la realidad, como hacer un loop de millones de registros para luego contar las milésimas de segundo entre usar un echo o un print.

El peor mal que podemos tener en la ingeniería de software es ir hacia la "optimización temprana" (varios [1], [2], [3], etc, sin descontar que el lenguaje PHP sigue evolucionando y siendo optimizado), es decir, preocuparnos tempranamente del rendimiento y no de la funcionalidad o el diseño de un sistema.

La parte que no se llega a entender es que ya pasamos la época de hacer páginas PHP embebidas con HTML y conexiones SQL, actualmente los proyectos requieren SISTEMAS, y estos poder escalar y desarrollar rápidamente de acuerdo a las necesidades del mercado.

¿Cuanto cuesta modificar un producto web que no es un sistema? ¿cuanto nos cuesta desarrollar funcionalidades nuevas?

Si no tienes diseño, si no aplicamos metodologías que incluyan POO, dudo seriamente que podamos estar aptos de desarrollar un sistema "medio" para arriba.

Pero eso de "no uses require_once porque es caro", "echo es más rápido que print" ó "print es más rápido que echo" (he visto artículos sobre "optimización extrema" que dicen lo contrario entre ellos), "usa ++$i en vez de $i++", etcétera, las considero completamente tontas e inútiles.

Podemos discutir que si tienes POO + Persistencia (BD), es seguro tendrás más consultas a la base de datos que si hicieras un JOIN y luego recuperaras los datos y procesaras, pero hay muchas técnicas para evitar esto, como el "lazy loading", evitando tener que consultar todas las veces por un mismo valor a la base de datos si ya lo tienes la primera vez, o evitar consultas SQL innecesarias por valores que perfectamente pueden estar en un array ya que no cambian (o en un archivo plano, luego lo puedes actualizar, etc), etc.

Siempre hay que tener en cuenta la escala del sistema, si es para 10 usuarios concurrentes, no me molestes con estas tonterías. ;-)

Pero puedes estar tranquilo, el problema no pasa por usar puramente POO, el problema para por otros lados, la latencia se estudia desde que el usuario ejecuta una acción y recorre todos los servicios y servidores hasta que el sistema responde, no es solo PHP.

PD: ahora me acuerdo que hace rato que quiero escribir un artículo sobre "lazy loading" ;-)


¿Tú qué opinas de la "optimización extrema"? ¿Crees en ella? ¿y de la "optimización temprana"? ¿te tomas el tiempo de verificar si realmente generan algún tipo de beneficios? ¿cómo optimizas tus sistemas? ¿cómo los diseñas?

Ejemplo de excepciones: "enviar un SMS"

Hace unos meses terminamos una aplicación de "trivias" (sistema de preguntas y respuestas) para celulares donde toda comunicación con el sistema se da a través de mensajes SMS.

Estimo que para sorpresa de muchos, está desarrollada enteramente en PHP5 + MySQL5, y se implementó un sistema en 3 capas para controlar toda la complejidad del sistema (que no era "trivial", el sistema está siendo usado para una campaña que aparece en televisión, publicidad impresa, web, etc e involucra marcas muy importantes, con un flujo enorme de usuarios participando lo que genera millones de mensajes).

Sé que en el mundo PHP no estamos tan acostumbrados a usar ni interfaces ni excepciones, así que me pareció oportuno comentar unos ejemplos extremadamente simples que pueden clarificar los beneficios de su uso.

Nuestra aplicación, como toda que trabaje con mensajes SMS, tiene un límite de 160 caracteres por mensaje, y para nuestro sistema, desbordar ese límite significa que existirán caracteres que se perderán (no es opcional enviar dos SMS en esa situación, el mensaje tiene que llegar al usuario en un solo SMS).

Por lo tanto, para la integridad de la aplicación debe poder saber en el mismo momento si ocurre esta situación "anómala" y tratar de resolverla de alguna forma.

A modo de ejemplo teórico
supongamos que nuestro sistema recupera de forma dinámica dos cadenas de texto, la primera es la presentación de la pregunta de la trivia (información de puntos, mensaje de aliento que se extrae de cientos de frases diferentes, etc) y la segunda parte será la pregunta en sí misma (nuevamente, una tabla con miles de preguntas que se obtienen al azar).

Definimos como regla que si ambas cadenas superan los 160 caracteres estamos delante de un problema "excepcional" que debemos resolver
, ya que -como comentamos anteriormente- no es solución ni cortar el mensaje, ni enviarlo en dos SMS o matar la aplicación y enviar información de log (ya que estamos en producción y el sistema tiene que responder al usuario a toda costa).

Determinamos que lo más importante es enviar la pregunta de la trivia y no tanto si una pregunta no llega con su primer parte informativa (se asume que la pregunta siguiente no entrará en una situación "excepcional"), por lo tanto podríamos hacer algo tan simple como:



/* [código del sistema] */

$informacion = $trivia->getMensajeInformativo();
$pregunta = $mensaje->getPreguntaSiguiente();

try{

Sms::enviar(
$informacion . $pregunta
);

}catch(Exception $e){

Sms::enviar(
$pregunta,
Sms::ENVIO_FORZADO
);

}


Lo que está entre medio de las llaves "try" es el código que puede fallar, dentro del envío de SMS revisa el tamaño del texto y si supera los 160 caracteres envía una excepción de esta forma (y de paso genera un log para poder tener registro de estas situaciones anómalas y solucionarlas a futuro):



/* [código del sistema] */

if(strlen($mensaje) > MAXIMO_TEXTO){

$error = 'Fallo grave, no pudo enviarse el SMS!';

Log::errorLog(__FILE__,__LINE__,$error);

throw new Exception($error);

}


El método enviar de Sms retorna una excepción, es capturada en un nivel más arriba y pasa a ejecutar un plan alternativo, enviar el mensaje SMS con solo la pregunta (fundamental para el juego de la trivia) y a su vez, en caso que pudiera ocurrir algún otro problema, activa una "bandera" de ENVIO_FORZADO (envía sin importar si tiene que cortar el texto).

Un punto interesante a tener en cuenta es el uso de constantes en la propia clase como para que ella misma "auto-documente" sus opciones posibles, ya que estoy ejecutando el método de la clase Sms pero a su vez uso la clase Sms para que me diga cuales son las opciones posibles y no tengo que estar recordando y/o investigando el código interno para colocar "números mágicos" del tipo 0,1,2,3, etc.


class Sms
{
const ENVIO_FORZADO = true;
const ENVIO_NORMAL = false;

/* [código de la clase] */
}


Nota: en este punto es importante destacar la función de un buen IDE, como podría ser Netbeans, que al teclear Sms:: nos mostrará toda la lista de opciones disponibles, entre ellas, las constantes.

También hay que tener en cuenta que es una situación "teórica" y a modo de ejemplo, ya que habría que implementar un sistema que controle los tamaños posibles de ambas frases, que la suma no supere 160 caracteres, si lo hace, que vuelva a solicitar una de las dos frases, etc. Para este ejemplo nos concentramos desde el punto de vista del enviador de mensajes, ya que a pesar que se hagan distintos controles para el mismo tema en cada capa, este tema igual lo tiene que poder contemplar la clase Sms.

Este ejemplo se puede profundizar, agregando distintos tipos de excepciones para cada tipo de error y solo forzar cuando es un error genérico, así "hilar más fino" y tratar de controlar la situación "excepcional".

Empezando con Zend Framework 1.7 + jQuery + ZendX


Por un tema de carga de trabajo no había podido entrar a experimentar con la versión beta de Zend Framework 1.7, pero el tiempo me ha ganado y ahora ya es estable y oficial, así que vamos a optar (en la empresa donde estoy trabajando actualmente) por armar la nueva plataforma de desarrollo con esta última versión (hicimos sistemas nuevos muy especializados, pero la idea ahora es tirar todos los sistemas "legacy" y empezar a reusar absolutamente todo lo que se haga).

Estamos tomando como base la idea inicial de SURFORCE y armando una estructura "CORE" (lo que era antes SURFORCE-BASE) que contendrá la nueva disposición de directorios según la documentación de Zend (como por ejemplo el directorio "html" fuera del acceso al resto de la aplicación), posteriormente los módulos generales de la aplicación (SURFORCE-MODULES) y una librería general del proyecto (lo que era antes SURFORCE-LIBRARY).

La filosofía que mantenemos, al igual que con los proyectos de SURFORCE, es seguir al máximo el aprovechamiento del Framework, si este -por ejemplo- sugiere usar Dojo, usaremos esta herramienta (así con todo).

Para empezar, tuvimos que ajustar el plan gracias al poco tiempo que teníamos para hacer las primeras entregas de funcionalidad de la nueva plataforma, así que optamos por una opción alternativa (dada la complejidad y nuestra falta de experiencia en Dojo buscamos bajar riesgos para no atrasarnos) y elegir en primera instancia jQuery (que también tiene integración con el framework, pero en menor medida).

Por lo tanto tuvimos que instalar, además de Zend como librería, ZendX, ya que ahí se encuentran todos los view helpers para jQuery.

Si quieres bajarlo, debes hacer un checkout con el svn en la url:

http://framework.zend.com/svn/framework/extras/trunk/library/ZendX/

Posteriormente, si quieres tener acceso tradicional a la liberería jQuery (invocarla directamente como un javascripts) puedes hacer uso de Google AJAX Libraries API, sin necesidad de instalar ninguna de ellas físicamente, accediendo de forma remota de esta manera:

En tu HEAD:
Y Zend Framework ya trae la última versión 1.7, por lo tanto haciendo update a la url del proyecto es suficiente.

En los próximos días actualizaré los proyectos de SURFORCE con estas mejoras para que sea de provecho para todos los que quieran iniciar con Zend Framework sin tanta dificultad.

Entradas populares