在 PHP 中从子类调用退出函数


Call exit function from child class in PHP

我有一个PHP类,我必须调用PHP header函数来显示网页。正如这篇文章所指出的,标头后面应该跟exit回调。由于这种情况在我的类中很常见,所以我在父类中定义了一个方法:

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

我想从儿童类调用此方法:

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

PHP 文档说exit终止当前脚本。我的问题是:退出函数是否终止子脚本,即使它包含在父类中?

编辑

在我的特定情况下,我使用的是 MVC 设计模式,ChildClass是一个控制器。在其中,有时我需要显示视图,有时我需要重定向到另一个 url。让我用一个实际的例子来解释它。

假设有一个带有登录部分的网站。向用户显示登录页面(未提交登录数据)时,登录控制器应显示登录视图。此视图包含一个具有类似 action="login.html" 的操作的窗体。提交数据时,将调用登录控制器并检查登录数据:如果登录成功,用户将被重定向到他的管理部分。

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;
  }
}

由于这种情况在我的控制器中很常见,因此我更喜欢在BaseController中定义一个header方法,而不是每次都键入

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

在我的OP中,我只想确保$this->header('admin.html');回调会终止当前的login方法,即使它是在脚本中定义的BaseController

希望现在更清楚一点。

正如注释中已经描述的那样,exit 将终止所有内容,即网页立即停止执行,包括清理函数和最后阻止。
因此,您应该非常谨慎地考虑使用 exit,因为可能会发生很多事情:当您不使用自动提交时,数据不会写入数据库(除非您在调用 exit 之前提交数据)。默认情况下,PHP 的 MySQL 模块中不启用自动提交(据我所知)。

下面是一个示例:

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.
}

与其调用退出方法,不如实现清晰的 MVC 设计模式。这是一个非常快速的示例:

<?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)
}
?>

请注意,这不是真正的MVC设计模式(模型丢失了,但是,不清楚控制器是什么,但是,django(Pyhton框架)也使用了什么)。你可能想看看PHP MVC框架(快速的谷歌搜索就可以了)或实现你自己的框架(我的例子可能是一个好的开始)。

编辑 1:使用 exit 可能没问题(但我不会使用它,因为我认为这是不好的做法)。如果您能够编辑正在使用的设计方法,我会使您的 View BaseClass get/post 方法(或任何返回响应对象的方法。注意:如果您使用打印件向用户显示响应,请尝试添加包含请求需要显示的所有数据的响应。这比在代码中随处放置 print s(或 echo s)要好)。
使用响应对象,您的代码将能够只设置位置标头(如果是重定向)或显示消息。因此,您无需"杀死"网页的任何部分,它将在执行结束时终止。但同样:您可能最好在方法中使用exit调用(除非您的代码遇到任何问题(未提交的数据库事务,未更新的统计数据(因为它在exit语句之后)。 Exit只会完全终止您的脚本。