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

时间:2016-02-16 13:09:03

标签: php constructor

我有一个非常奇怪的问题,我不知道这是否归因于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构造函数?

3 个答案:

答案 0 :(得分:2)

我会在黑暗中捅一下,并说你实际上是这样做的:

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);
//                         ^^^

答案 1 :(得分:1)

作为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函数中给出,则它产生输出但是具有严格的标准错误,并且在创建产生致命错误的类的新对象时。

答案 2 :(得分:1)

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

City - Country
Country - Language

根据您的问题,我假设call_user_func_array(array($controller, $route['action']), $data); $controller类型的对象。它已经在您将其传递给AuthController时创建。在创建对象时调用了构造函数。

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

您可以将protected的可见性更改为Controller::__construct()(这样可以实例化这两个类,并为两者运行public的构造函数)。或者,如果您希望保持类Controller由于某种原因不可实例化,则为类Controller定义public构造函数,该构造函数调用类AuthController的受保护构造函数来完成工作:

Controller