使用 call_user_func_array 调用子类时不触发扩展类构造函数


extended class constructor not fired when child class is called using call_user_func_array

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

在 PHP 5.4 上测试

当前设置

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

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 构造函数?

我会在这里在黑暗中试一试,并说你实际上是在这样做:

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

换句话说,您不是将实例传递给call_user_func,而只是将字符串传递'AuthController'。这意味着您的方法将被静态调用。如果启用了错误报告和/或严格错误报告,则应看到一条通知,警告您有关静态调用非静态方法的信息。

问题在于(可能(你从未真正实例化你的类,所以从来没有运行过构造函数。 call_user_func不会为您实例化它。您需要自己执行此操作:

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

作为PHP文档在 http://php.net/manual/en/language.oop5.decon.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那么它正在产生输出,但具有严格的标准错误,并且在创建产生致命错误的类的新对象时

请记住,构造函数是在创建新对象时调用的特殊方法。 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...
}