【问题标题】:extended class constructor not fired when child class is called using call_user_func_array使用 call_user_func_array 调用子类时未触发扩展类构造函数
【发布时间】:2016-05-27 18:55:39
【问题描述】:

我有一个很奇怪的问题,我不知道这是否是由于call_user_func_array 造成的。

在 PHP 5.4 上测试

当前设置

例如,我有一个 AuthController 类,它扩展了一个名为 Controller 的基本控制器:

class AuthController extends Controller
{

    public function login() {
        return Response::json(array('error' => false, 'message' => 'I\'m in AuthController@login'));
    }

}

在扩展的Controller 类中,我有一个构造函数,用于设置例如数据库连接(我添加了一个 var_dump + exit 用于测试目的):

class Controller
{

    protected $db;

    protected function __construct() {
        $database = Config::env('database');

        var_dump($database);
        exit;
    }

}

现在拨打AuthController,我正在使用call_user_func_array

call_user_func_array(array($controller, $route['action']), $data);

现在应该发生什么:

应该发生的事情是Controller 的构造函数应该已经触发,在屏幕上产生一个转储并退出执行。

改为:

相反,我实际上是从AuthController 的登录方法Response::json() 获得响应。

Controller 的构造函数永远不会被触发。

我很难理解为什么它不起作用,因为 PHP 手册指出构造函数会在每个新对象实例上触发,并且如果父类有构造函数并且子类没有覆盖它,则父类类构造函数被自动调用。

call_user_func_array 不会自动触发父类构造函数,还是我完全误解了 PHP 构造函数?

【问题讨论】:

  • 你在哪里实例化AuthController?什么是$controller,一个实例或字符串"AuthController"?您是否在孩子中覆盖了__construct,或者它只是您显示的内容?

标签: php constructor


【解决方案1】:

我会在这里暗中刺伤,并说您实际上正在这样做:

call_user_func_array(array('AuthController', 'login'), $data);

换句话说,您没有将 instance 传递给call_user_func,您只是传递了字符串'AuthController'。这意味着您的方法将被静态地调用。如果您启用了错误报告和/或 strict 错误报告,您应该会看到一条通知,警告您关于静态调用非静态方法。

问题是(可能)你从来没有真正实例化你的类,所以没有构造函数运行过。 call_user_func 不会为您实例化它。你需要自己做:

call_user_func_array(array(new $controller, $route['action']), $data);
//                         ^^^

【讨论】:

  • 是的,当然,我完全忘记了 call_user_func_array 会静态调用它,因此它可能不起作用。用new 实例化$controller 变量解决了这个问题。谢谢你的帮助。
【解决方案2】:

作为http://php.net/manual/en/language.oop5.decon.php的PHP文档

PHP 5 允许开发人员为类声明构造方法。具有构造函数方法的类在每个新创建的对象上调用该方法,因此它适用于对象在使用之前可能需要的任何初始化。

注意:如果子类定义了构造函数,则不会隐式调用父构造函数。为了运行父构造函数,需要在子构造函数中调用 parent::__construct()。如果子类没有定义构造函数,那么它可以像普通类方法一样从父类继承(如果它没有声明为私有)。

示例 #1 使用新的统一构造函数

<?php
class BaseClass {
   function __construct() {
       print "In BaseClass constructor\n";
   }
}

class SubClass extends BaseClass {
   function __construct() {
       parent::__construct();
       print "In SubClass constructor\n";
   }
}

class OtherSubClass extends BaseClass {
    // inherits BaseClass's constructor
}

// In BaseClass constructor
$obj = new BaseClass();

// In BaseClass constructor
// In SubClass constructor
$obj = new SubClass();

// In BaseClass constructor
$obj = new OtherSubClass();
?>

希望这将向您解释如何在子类中使用父类构造函数。

你也可以参考http://php.net/manual/en/function.call-user-func-array.php,这里他们提到了如何在call_user_func_array中调用父类函数。

示例:

call_user_func_array(array($this, 'parent::__construct'), $args); 

编辑 1

见下例:

class BaseClass {
    protected function __construct() {
        print "In BaseClass constructor<br />";
    }
}

//class SubClass extends BaseClass {
//    function __construct() {
//        parent::__construct();
//        print "In SubClass constructor<br />";
//    }
//}

class OtherSubClass extends BaseClass {
    // inherits BaseClass's constructor
    function test(){
        echo 'in test';
    }
}
call_user_func_array(array('OtherSubClass', 'test'), array()); //
// In BaseClass constructor
$obj = new OtherSubClass(); //produce fatel error

输出:

Strict Standards: call_user_func_array() expects parameter 1 to be a valid callback, non-static method OtherSubClass::test() should not be called statically in /var/www/html/test/test1.php on line 22
in test
Fatal error: Call to protected BaseClass::__construct() from invalid context in /var/www/html/test/test1.php on line 24

所以如果字符串在call_user_func_array 函数中给出,那么它会产生输出但有严格的标准错误,并且在创建类的新对象时会产生致命错误。

【讨论】:

  • 嗯很有趣,但我当前的用例似乎与您的示例匹配 OtherSubClass。在我的情况下,AuthControllerOtherSubClass,它没有构造函数,只有扩展的“BaseClass”定义了构造函数。所以理论上它应该是继承构造函数吧?
  • 我认为是protected构造函数,改成public后试试
  • public 构造函数也会发生同样的事情。
  • $controller 有什么字符串或对象?我认为是字符串
【解决方案3】:

请记住,构造函数是在创建新对象时调用的特殊方法。 call_user_func_array() 不调用任何构造函数,因为它不创建任何新对象。

call_user_func_array(array($controller, $route['action']), $data);

根据您的问题,我假设$controllerAuthController 类型的对象。当您将它传递给call_user_func_array() 时,它已经创建好了。它的构造函数在对象创建时被调用。

但是等一下?什么构造函数? AuthController 类没有定义任何构造函数。它继承了父类构造函数protected。因为它不是公共的,所以不能被调用,也不能创建对象;脚本抛出致命错误并退出。

您可以将Controller::__construct() 的可见性更改为public(这样可以实例化两个类并且Controller 的构造函数同时运行)。或者,如果您想保持类 Controller 不可实例化,您可以为类 AuthController 定义一个 public 构造函数,该构造函数调用类 Controller 的受保护构造函数来完成这项工作:

class AuthController extends Controller
{
    public function __construct()
    {
        parent::__construct();
    }

    // other methods here...
}

【讨论】:

  • 如果问题是一个非公共构造函数,PHP 应该会失败并出现致命错误,因为它在尝试实例化类时无法调用构造函数。它不会简单地不调用它,它会失败。这个类更有可能根本没有被实例化。
  • 问题的一部分非公共构造函数。发帖人没有到达它,因为正如你所说,他们没有实例化任何 AuthController 对象。
猜你喜欢
  • 2014-05-29
  • 2013-11-15
  • 2021-02-13
  • 2014-04-18
  • 1970-01-01
  • 2018-09-26
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多