<?xml version="1.0" encoding="UTF-8"?>
    <!-- EN-Revision: 17593 -->
    <!-- Reviewed: no -->
<sect1 id="zend.controller.exceptions">
    <title>Excepciones MVC</title>

    <sect2 id="zend.controller.exceptions.introduction">
        <title>Introducción</title>

        <para>
            Los componentes
            <acronym>MVC</acronym>
            en Zend Framework utilizan
            un Front Controller, lo que significa que todas las
            solicitudes de
            un determinado sitio pasarán por un solo punto de entrada. Como
            resultado,
            todas las excepciones burbujearán eventualmente hacia
            arriba hasta el Front Controller,
            permitiendo al desarrollador
            manejarlos en un solo lugar.
        </para>

        <para>
            Sin embargo, los mensajes de excepción y la información de
            backtrace contienen a menudo
            información sensible del sistema, como
            declaraciones
            <acronym>SQL</acronym>
            , ubicaciones de archivos y
            otras cosas más. Para ayudar a proteger su sitio, por defecto
            <classname>Zend_Controller_Front</classname>
            captura todas las
            excepciones y las registra con el objeto respuesta; a su vez, y por
            defecto, el objeto respuesta no muestra mensajes de excepción.
        </para>
    </sect2>

    <sect2 id="zend.controller.exceptions.handling">
        <title>Manejando las Excepciones</title>

        <para>
            Ya hay varios mecanismos construidos en los componentes de
            <acronym>MVC</acronym>
            , que le permiten manejar excepciones.
        </para>

        <itemizedlist>
            <listitem>
                <para>
                    Por defecto, el
                    <link linkend="zend.controller.plugins.standard.errorhandler">error handler
                        plugin</link>
                    está registrado y activo.
                    Este plugin fue diseñado para manejar:
                </para>

                <itemizedlist>
                    <listitem>
                        <para>Errores debido a controladores o acciones
                            perdidas</para>
                    </listitem>

                    <listitem>
                        <para>Errores ocurriendo dentro de controladores de
                            acción</para>
                    </listitem>
                </itemizedlist>

                <para>
                    Operan como un plugin de
                    <methodname>postDispatch()</methodname>
                    , y comprueban
                    para ver si un despachador, controlador de acción, o de otra
                    excepción ha ocurrido. Si así fuera, lo remite a un
                    controlador de manejo de
                    errores.
                </para>

                <para>Este manejador abarcará la mayoría de las situaciones
                    excepcionales, y maneja
                    airosamente controladores y acciones
                    perdidos.</para>
            </listitem>

            <listitem>
                <para>
                    <methodname>Zend_Controller_Front::throwExceptions()</methodname>
                </para>

                <para>
                    Pasando a este método un valor booleano
                    <constant>TRUE</constant>
                    , puede decirle al front
                    controller que, en lugar de sumar excepciones en el
                    objeto
                    respuesta o utilizando el plugin de manejo de errores,
                    prefiere manejarlos
                    usted mismo. Como ejemplo:
                </para>

                <programlisting language="php"><![CDATA[
$front->throwExceptions(true);
try {
    $front->dispatch();
} catch (Exception $e) {
    // usted mismo maneja las excepciones
}
]]></programlisting>

                <para>Este método es probablemente la forma más fácil de añadir
                    un manejo de
                    excepciones personalizado que abarque toda la
                    gama de posibles excepciones a su
                    aplicación de front
                    controller.</para>
            </listitem>

            <listitem>
                <para>
                    <methodname>Zend_Controller_Response_Abstract::renderExceptions()</methodname>
                </para>

                <para>
                    Al pasar a este método un valor booleano
                    <constant>TRUE</constant>
                    , le esta diciendo al objeto
                    respuesta que debe emitir un mensaje de excepción y
                    backtrace cuando se renderiza a sí mismo. En este escenario,
                    se mostrará
                    cualquier excepción planteada por su aplicación.
                    Esto no es recomendable para
                    entornos de producción, pero sí
                    en desarrollo.
                </para>
            </listitem>

            <listitem>
                <para>
                    <methodname>Zend_Controller_Front::returnResponse()</methodname>
                    y
                    <methodname>Zend_Controller_Response_Abstract::isException()</methodname>
                    .
                </para>

                <para>
                    Pasando un valor booleano
                    <constant>TRUE</constant>
                    a
                    <methodname>Zend_Controller_Front::returnResponse()</methodname>
                    ,
                    <methodname>Zend_Controller_Front::dispatch()</methodname>
                    no renderizará la respuesta, sino que la devolverá. Una vez
                    que tiene la
                    respuesta, entonces puede probar ver si todas
                    las excepciones fueron atrapadas
                    usando su método
                    <methodname>isException()</methodname>
                    , y recuperando
                    las excepciones a través del método
                    <methodname>getException()</methodname>
                    . Como ejemplo:
                </para>

                <programlisting language="php"><![CDATA[
$front->returnResponse(true);
$response = $front->dispatch();
if ($response->isException()) {
    $exceptions = $response->getException();
    // maneje las excepciones ...
} else {
    $response->sendHeaders();
    $response->outputBody();
}
]]></programlisting>

                <para>
                    La principal ventaja que este método ofrece por sobre
                    <methodname>Zend_Controller_Front::throwExceptions()</methodname>
                    es que le permite renderizar condicionalmente la respuesta
                    después de manejar la
                    excepción. Esta capturará cualquier
                    excepción en la cadena de controladores, a
                    diferencia del
                    plugin de manejo de errores.
                </para>
            </listitem>
        </itemizedlist>
    </sect2>

    <sect2 id="zend.controller.exceptions.internal">
        <title>Excepciones MVC que Usted Pueda Encontrar</title>

        <para>
            Los diversos componentes de
            <acronym>MVC</acronym>
            -- solicitud,
            router, despachador, controlador de acción, y los objetos respuesta
            --
            pueden arrojar excepciones en ocasiones. Algunas excepciones
            puede ser condicionalmente
            anuladas, y otras se usan para indicar al
            desarrollador que puede necesitar re-considerar
            la estructura de su
            aplicación.
        </para>

        <para>Como algunos ejemplos:</para>

        <itemizedlist>
            <listitem>
                <para>
                    <methodname>Zend_Controller_Dispatcher::dispatch()</methodname>
                    hará, por defecto, arrojar una excepción si se hace un
                    requerimiento a un
                    controlador no válido. Hay dos maneras
                    recomendadas para lidiar con esto.
                </para>

                <itemizedlist>
                    <listitem>
                        <para>
                            Establecer el parámetro
                            <emphasis>useDefaultControllerAlways</emphasis>
                            .
                        </para>

                        <para>En su front controller, o en su despachador,
                            añada la siguiente
                            directiva:</para>

                        <programlisting language="php"><![CDATA[
$front->setParam('useDefaultControllerAlways', true);

// o

$dispatcher->setParam('useDefaultControllerAlways', true);
]]></programlisting>

                        <para>Cuando este flag está establecido, el despachador
                            utilizará el
                            controlador y la acción por defecto en
                            lugar de lanzar una excepción. La
                            desventaja de este
                            método es que cualquier error ortográfico que un
                            usuario haga cuando acceda a su sitio lo resolverá y
                            mostrará su página
                            de inicio, y que puede causar
                            estragos con la optimización para los
                            motores de
                            búsqueda.</para>
                    </listitem>

                    <listitem>
                        <para>
                            La excepción arrojada por
                            <methodname>dispatch()</methodname>
                            es una
                            <classname>Zend_Controller_Dispatcher_Exception</classname>
                            conteniendo el texto 'Invalid controller specified'.
                            Use uno de los
                            métodos descriptos de
                            <link linkend="zend.controller.exceptions.handling">la
                                sección anterior</link>
                            para atrapar la
                            excepción, y luego redireccionar a una página
                            genérica de
                            error o a la página de inicio.
                        </para>
                    </listitem>
                </itemizedlist>
            </listitem>

            <listitem>
                <para>
                    <methodname>Zend_Controller_Action::__call()</methodname>
                    arrojará una
                    <classname>Zend_Controller_Action_Exception</classname>
                    si no puede despachar una acción inexistente a un método. Es
                    probable que desee
                    utilizar alguna acción por defecto en el
                    controlador en casos como este. Formas
                    de lograr esto
                    incluyen:
                </para>

                <itemizedlist>
                    <listitem>
                        <para>
                            Subclasear
                            <classname>Zend_Controller_Action</classname>
                            y
                            anular el método
                            <methodname>__call()</methodname>
                            .
                            Como ejemplo:
                        </para>

                        <programlisting language="php"><![CDATA[
class My_Controller_Action extends Zend_Controller_Action
{
    public function __call($method, $args)
    {
        if ('Action' == substr($method, -6)) {
            $controller = $this->getRequest()->getControllerName();
            $url = '/' . $controller . '/index';
            return $this->_redirect($url);
        }

        throw new Exception('Invalid method');
    }
}
]]></programlisting>
                        <para>El ejemplo anterior intercepta cualquier llamada
                            a un método de acción
                            indefinido y redirecciona a la
                            acción predeterminada en el controlador.</para>
                    </listitem>

                    <listitem>
                        <para>
                            Subclasea a
                            <classname>Zend_Controller_Dispatcher</classname>
                            y anula el método
                            <methodname>getAction()</methodname>
                            para
                            verificar si la acción existe. Como ejemplo:
                        </para>

                        <programlisting language="php"><![CDATA[
class My_Controller_Dispatcher extends Zend_Controller_Dispatcher
{
    public function getAction($request)
    {
        $action = $request->getActionName();
        if (empty($action)) {
            $action = $this->getDefaultAction();
            $request->setActionName($action);
            $action = $this->formatActionName($action);
        } else {
            $controller = $this->getController();
            $action     = $this->formatActionName($action);
            if (!method_exists($controller, $action)) {
                $action = $this->getDefaultAction();
                $request->setActionName($action);
                $action = $this->formatActionName($action);
            }
        }

        return $action;
    }
}
]]></programlisting>

                        <para>El código anterior comprueba para ver que las
                            acciones solicitadas
                            existan en la clase del
                            controlador; si no, se restablece la acción a la
                            acción por defecto.</para>

                        <para>
                            Este método es agradable porque puede alterar
                            transparentemente la acción
                            antes del último
                            despacho. Sin embargo, también significa que errores
                            ortográficos en la
                            <acronym>URL</acronym>
                            todavía
                            pueden despacharse correctamente, lo que no es muy
                            bueno para la
                            optimización en un motor de búsqueda.
                        </para>
                    </listitem>

                    <listitem>
                        <para>
                            Use
                            <methodname>Zend_Controller_Action::preDispatch()</methodname>
                            o
                            <methodname>Zend_Controller_Plugin_Abstract::preDispatch()</methodname>
                            para identificar acciones inválidas.
                        </para>

                        <para>
                            Subclaseando
                            <classname>Zend_Controller_Action</classname>
                            y
                            modificando
                            <methodname>preDispatch()</methodname>
                            ,
                            puede modificar todos sus controladores que
                            transmitan a otra acción o
                            redireccionar antes de
                            despachar la acción. El código para hacer esto se
                            verá parecido al código de sustitución de arriba
                            <methodname>__call()</methodname>
                            .
                        </para>

                        <para>Alternativamente, puede verificar esta
                            información en un plugin global.
                            Esto tiene la
                            ventaja de ser independiente del controlador de
                            acción; si
                            su aplicación consiste en una variedad de
                            controladores de acción, y no
                            todos ellos heredan de
                            la misma clase, este método puede añadir
                            coherencia
                            a su manejo de clases diferentes.</para>

                        <para>Como ejemplo:</para>

                        <programlisting language="php"><![CDATA[
class My_Controller_PreDispatchPlugin extends Zend_Controller_Plugin_Abstract
{
    public function preDispatch(Zend_Controller_Request_Abstract $request)
    {
        $front      = Zend_Controller_Front::getInstance();
        $dispatcher = $front->getDispatcher();
        $class      = $dispatcher->getControllerClass($request);
        if (!$controller) {
            $class = $dispatcher->getDefaultControllerClass($request);
        }

        $r      = new ReflectionClass($class);
        $action = $dispatcher->getActionMethod($request);

        if (!$r->hasMethod($action)) {
            $defaultAction  = $dispatcher->getDefaultAction();
            $controllerName = $request->getControllerName();
            $response       = $front->getResponse();
            $response->setRedirect('/' . $controllerName
                                  . '/' . $defaultAction);
            $response->sendHeaders();
            exit;
        }
    }
}
]]></programlisting>

                        <para>En este ejemplo, vamos a consultar para ver si la
                            acción solicitada
                            está disponible en el controlador.
                            Si no, redireccionamos a la acción
                            predeterminada en
                            el controlador, y salimos inmediatamente de la
                            ejecución del script.</para>
                    </listitem>
                </itemizedlist>
            </listitem>
        </itemizedlist>
    </sect2>
</sect1>
