-1

I have a PHP class where I have to invoke PHP header function to show a webpage. As pointed out from this post, header should be followed by exit callback. Since this situation is very common in my classes, I've defined a method in parent class:

class ParentClass
{
    protected function header($url)
    {
        header('Location:'.$url);
        exit;
    }
} 

I would like to invoke this method from children classes:

class ChildClass extends ParentClass
{
    public function someFunc()
    {
        $this->header($some_url);
    }
} 

PHP documentation says that exit terminates the current script. My question is: does the exit function terminate child script even if it is contained in parent class?

EDIT

In my specific situation, I am using a MVC design pattern and ChildClass is a controller. Inside it, sometimes I need to show a view, sometimes I need to redirect to another url. Let me explain it with a practical example.

Suppose to have a website with a login section. When login page is displayed to users (login data not submitted), login controller should show login view. This view contains a form with an action like action="login.html". When data is submitted, login controller is invoked and checks login data: if login is successful, user is redirected to his admin section.

class UsersController extends BaseController
{
  public function login()
  {
    try
    {
      if(isset($_POST['submit']))
      {
        // check login data, throw exception in case of error

        // if success, redirect to admin section
        $this->header('admin.html');
      }


      else
      {
        // show login view
      }
    }

    catch(Exception $e)
    {
      // show login view (with error)
    }
  }
}


class BaseController
{
  protected function header($url)
  {
    header('Location:'.$url);
    exit;
  }
}

Since this situation is quite common in my controllers, I've preferred to define a header method in BaseController instead of typing everytime

header('Location:someURL.html');
exit;

In my OP, I only wanted to be sure that $this->header('admin.html'); callback would terminate current login method, even if it is defined in BaseController script.

Hope it's a little clearer now.

Community
  • 1
  • 1
Giorgio
  • 1,940
  • 5
  • 39
  • 64
  • 1
    exit makes your whole webpage stop (i.e. code after exit will not be executed at all (even when it's in a child class), but I'm not quite sure what you're trying to accomplish.' – Matt3o12 Jan 13 '15 at 19:38
  • Why not `parent::header($url)`? – Ding Jan 13 '15 at 19:40
  • @Matt3o12 my goal is to terminate child execution after callback. Thanks for the answer. – Giorgio Jan 13 '15 at 19:43
  • Giorgio: by child execute, do you mean the child method, you're calling? If so, let me create a complete answer. – Matt3o12 Jan 13 '15 at 19:44
  • @Matt3o12, you're correct: I would like to terminate current child method. – Giorgio Jan 13 '15 at 19:45
  • I think your code is OK. –  Jan 13 '15 at 19:53
  • As long as in your header will be only Location: http://www.com, exit() is fine. But for other headers, shouldn't be exit() – dikirill Jan 13 '15 at 19:55
  • @dikirill could you be more precise, please? I'm not sure I've catched your point. – Giorgio Jan 13 '15 at 19:58
  • I mean, if you gonna use only `header('Location: ...');` it's fine to use exit (and highly recommended). But, if you will do `exit()` after `header('Content-Type: text/json');` you will break your site. So, do `exit()` only if you redirect using `header()` function. – dikirill Jan 13 '15 at 23:39
  • @dikirill thanks it's clear now. I'm using header just for redirect, so it's fine. Thanks. – Giorgio Jan 14 '15 at 07:38
  • You confuse script with class. The function `exit()` terminates the script. It does not matter if you call it from a method of the child class or of the parent class or from code outside any class. `exit()` is the last thing that your script does (except for destructors and the registered shutdown function). – axiac Jan 14 '15 at 08:16
  • @axiac thanks for the comment. Back to OP, what I should do to perform a redirect in a correct way, in this specific case? – Giorgio Jan 14 '15 at 08:24
  • As long as you remember that on `exit()` the interpreter calls the destructors for all the object instances (if there are destructors defined for their classes) and the function registered using [`register_shutdown_function()`](http://php.net/manual/en/function.register-shutdown-function.php) then it **exits** (i.e. stops the execution), I think you decide what is the "correct way" to do it. For me, it seems ok as you are doing it now. But if you are using a framework then it probably already has a method for that, either in the base Controller class or in base Response class. – axiac Jan 14 '15 at 08:32
  • @axiac I'm using a self-made MVC framework, so I think it may be ok. Thanks for the clarification. – Giorgio Jan 14 '15 at 08:37

1 Answers1

1

As already descripted in the comment, exit will terminate everything, i.e. the webpage immediately stops executing, including clean up functions and finally blocks.
So, you should consider using exit very carefully because many things might happen: data doesn't get written to the database when you're not using auto-commit (unless you commit the data before calling exit). Auto-commit is not enabled by default in PHP's MySQL module (as far as I know).

Here is an example:

class B extends A {
    public function someFunc() {
        # you might wanna use partent instead as 
        # pointed out by Ding in the comments, but
        # maybe someFunc does more that just doing 
        # the redirect.

        $this->header("http://google.com");
    }
}

try {
    print("Ok...");
    $obj = new B();
    $obj->someFunc();
    print("Nahh...");  # doesn't get called/
} finally {
    print("Cleaning up.");  # doesn't get called, either.
}

Instead of calling the exit method, you should rather implement a clear MVC design pattern. Here is a very quick example:

<?php
class Response {
    # use private here and use appropriate
    # getters and setters.
    public $status_code = 200;
    public $content = "";
    public $headers = array();
}

class HomeView extends View {  
    # called when a get request is made.
    public function get() {
        $response = new Response();
        $response->content = "Hello world."
    }
}

class RedirectionView {
    public function get() {
        $response = new Response();
        $response->status_code = 301;  # use 302 if moved only temporarily.
        $response->headers["Location"] = "http://google.com";
    }
}

function processRequest() {
    # load appropriate view programatically
    $view = new RedirectionView();
    $response = $view->get();
    http_response_code($response->status_code);
    foreach ($response->headers as $headerName => $headerValue) {
        header(sprintf("%s: %s", $headerName, $headerValue));
    }
    print($view->content)
}
?>

Note that this is not really a MVC design pattern (the model is missing and, well, it's not clear what the controller, however, that's what django (a Pyhton framework) uses, too). You might wanna check out PHP MVC frameworks (a quick google search will do the trick) or implement your own (my example might be a good start).

Edit 1: Using exit is probably ok (but I wouldn't use it because I believe it is bad practice). If you're able to edit the design approach you're using, I'd make your View BaseClass get/post method (or whatever you're the method that returns response object. Note: if you're using prints to show the response to the user, try to add a Response which contains all the data you're request needs to show. This is better than having prints (or echos) everywhere in your code).
Using the response object, your code would then be able to either just set the location header (if it's a redirect) or show the message. So you don't need to "kill" any part of the webpage and it will terminal when the execution ends. But again: you're probably good to go with an exit call in your method (unless you're encountering any issues with your code (database transactions that aren't committed, statistic data that is not updated (because its after the exit statement). Exit will simply terminate your script completely.

Matt3o12
  • 4,192
  • 6
  • 32
  • 47
  • I am using a self implemented MVC pattern, and someFunc is actually a controller method. My goal in this case is not to simply show a view, but to invoke an url, which both calls a controller method and changes URL in browser bar. That's why I want to completely terminate someFunc, the current controller method. – Giorgio Jan 13 '15 at 20:57
  • Ok, I'm still not sure if I understood your question completely, sorry for that. If you want the header function to somehow tell any function it is being called by to do something (in your case, stop executing). You could of course, use exit but I believe it's bad practice (I named a few arguments above). Anyway this might help you: http://pastebin.com/EJPz4bEv. If it does, let me know and I'll include that in my answer. If it doesn't, please provide more code. – Matt3o12 Jan 13 '15 at 21:24
  • I've edited my OP, giving a practical use case example. Let me know if it's clear now. Thanks. – Giorgio Jan 14 '15 at 08:09
  • @Giorgio I've edited my answer as well. Hope it helps a little bit. – Matt3o12 Jan 15 '15 at 19:35