We have moved our forum to GitHub Discussions. For questions about Phalcon v3/v4/v5 you can visit here and for Phalcon v6 here.

View not rendered when forward is called from external class (no Controller)

Hello,

I have implemented a structure in order to handle errors and exceptions. I have an Mvc application. I have a class that handles all errors and exceptions, set using set_error_handler and set_exception_handler.

This class chooses when and how it has to display the error to the user. Specifically, when an exception occurs in production environment, I want to show a generic error page to the user and log the error details (for example in the database) for further investigations.

In order to do so, after having logged the error details, the exceptions handler class has access to the Dispatcher object invoking the following command:

    $this->dispatcher->forward(array( ... ));
    $this->dispatcher->dispatch();

Inside the array() I specify the Controller and the Action where the generic error page will be rendered. Then, I NEED to call ->dispatch() otherwise the forward wouldn't work.

That action contains something like that:

    $this->response->setStatusCode( 500, 'Internal Server Error' );

    $this->setPageDefaultTitle( 'Ooops! Something went wrong here...' );
    $this->setPageDefaultBody( 'Dear user, unfortunately an error occured. We\'re already working on it!' );

Ok, in my ControllerBase (extended by all my Controllers) I also have a afterExecuteRoute method that does some logic before sending the control to the view.

The problem is that when an error occurs everything works perfectly excepts that the view is totally not rendered. I see a blank page and no errors in Nginx log.

If, at the end of afterExecuteRoute method I invoke these commands:

    $this->response->send();
    $this->view->render( $this->dispatcher->getControllerName(), $this->dispatcher->getActionName() );

Everything works and the error page is rendered.

I can't understand why - and I also wouldn't like to execute those lines of code, because the forward works, the view is active and working... There's no reason for the view not to render.

Thank you very much.



79.0k
Accepted
answer

I think after thrown exception, execution stops. Thats why you need to return response manually. Under normal workflow, your handle() method would return it.



12.9k

I think after thrown exception, execution stops. Thats why you need to return response manually. Under normal workflow, your handle() method would return it.

Ok, but the forwarding process works, I arrive until the end of the Controller/Action execution... The view and the response objects are the only ones who seem not working.

Try with the following. Instead of calling send() method directly, return entire Response object: return $this->response;



12.9k

Hi, Actually maybe your first explanation was the right one. Apparently the framework leaves me alone in an Exception handling context when it's time to send the response (and its view).

Returning the response object didn't do the trick.

So, I created a an optional method call as last operation inside my ControllerBase's afterExecuteRoute. I called it afterExecuteRouteBeforeView, just to separate its name from the BeforeViewRender and BeforeView events:

$method = 'afterExecuteRouteBeforeView';
if( method_exists( $this, $method ) ) {
    $this->{$method}();
}

Inside my error handling controller I have written that method definition:

protected function afterExecuteRouteBeforeView() {
    $this->response->send();
    $this->view->render( $this->dispatcher->getControllerName(), $this->dispatcher->getActionName() );
}

Thank you.

Yeah, it's the PHP/Zend engine thing.

When an exception is thrown, code following the statement will not be executed, and PHP will attempt to find the first matching catch block. If an exception is not caught, a PHP Fatal Error will be issued with an "Uncaught Exception ..." message, unless a handler has been defined with set_exception_handler().

https://php.net/exceptions



12.9k

yes that's exactly what I'm doing.

code is executed by the handler (dispatcher forward) except the final response/view part.