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

Redirecting to 404 action results in blank page

I have setup a router, and in it, defined a route for 404s:

<?php

use Phalcon\Mvc\Router;
$router = new Router(FALSE);
$router->removeExtraSlashes(true);

$route = $router->add('/', ['controller' => 'index', 'action' => 'index']);
$route->setName("index");

// other routes defined here...

$router->notFound([
  "controller" => "index",
  "action" => "route404"
]);

?>

My IndexController:

<?php

class IndexController extends ControllerBase
{

    public function indexAction()
    {
        // code removd for berevity 
    }

    public function route404Action() {
      // no code here, I just need to show the view.
    }

}
?>

And I have a view @ /app/views/index/route404.phtml that just has a bit of HTML in it, I even tried making it a .volt file, no luck.

When I go to a page that doesn't match any routes, it works fine. But, if I try to redirect to it, I just get a blank page. For example, in one of my controllers I have this:

if (!$category) {
        // show 404

        //Tried this next line to test, and it indeed does what you'd expect, I see "Not Found". 
        // echo "Not Found"; exit;  

        $response = new \Phalcon\Http\Response();
        $response->redirect([
          "for" => "index", 
          "controller" => "index", 
          "action" => "route404"]
        );
        return; // i return here so it won't run the code after this if statement.
      }

Any ideas? The page is completely blank (nothing in source) and there are no errors in my apache logs.

edited Mar '16

From what I see in your code, you are trying to redirect to the route named index which is your index action. Remove the for index from the array.

edited Mar '16

It gives me this error:

It's necessary to define the route name with the parameter 'for'

I had it setup that way to start with, but got the above error, so I named the route and added the "for".

I also tried the simpler method of $response->redirect("/index/route404") but it gives the me the same blank page.

Since "true" 404s seem to be routed correctly, try adding the 404 route as "normal" route (just to debug):

$router->add("/index/route404", [
    "controller" => "index",
    "action" => "route404"
]);

Alternatively, try adding a generic /:controller/:action route:

$router->add("/:controller/:action", [
    "controller" => 1,
    "action" => 2
]);

Same result...

edited Mar '16

Hmmm... I added $response->send(); after the $response->redirect() call and now I'm getting a page with the 404 message, but it's also updating the URL at the top of the page to /dev... One step forward, 2 back? I don't want the URL changing, I just wan the main content to get set to my 404 message. Should I be doing this another way?

Here's an ugly hack... In my CustomView.php class I added a public property is404. I also added a notFound method that sets that to true, and sends the 404 header. Then in my controller I have it call that, and then return. In my view I check for $this->view->is404 first, include /app/view/404.inc.php and then return.

This works but it's pretty ugly... very hacky :|

Ok, got a much better solution, not sure if it's "right" but it's much cleaner...

In my CustomView class I changed my notFound method to this:

  public function notFound() {
    header("HTTP/1.0 404 Not Found");
    $this->pick('404');
  }

Now in the controller I just have to call $this->view->notFound() and then return;. Now I get the 404 page, the header is sent and the URL stays the same. How's that? Anywhere near "the correct" way?

edited Mar '16

I had posted this question on StackOverflow as well, and got the perfect answer. You can view it here.

I ended up moving the notFound() method to the ControllerBase class since it makes more sense, here's what that looks like:

  public function notFound() {
    $response = new \Phalcon\Http\Response();
    $response->setStatusCode(404, "File not Found");
    $this->view->pick('404');
    $this->response->send();
  }

Now in my controller I just have this:

      if (!$cat) {
        // show 404
        return $this->notFound();
      }

This makes it easy to use from multipe controllers & actions, or multiple locations within the same action!