我有一个数据库类,其中包含一系列函数,我有一个Main类,其中包含其他类的依赖项,例如Users,Posts,Pages等。
这是注入数据库依赖关系的主类。
class Main {
protected $database;
public function __construct(Database $db)
{
$this->database = $db;
}
}
$database = new Database($database_host, $database_user, $database_password, $database_name);
$init = new Main($database);
然后,这是我正在扩展它的用户类。
class Users extends Main {
public function login() {
System::redirect('login.php');
}
public function view($username) {
$user = $this->database->findFirst('Users', 'username', $username);
if($user) {
print_r($user);
} else {
echo "User not found!";
}
}
}
但是,每当试图为User类调用view函数时,我都会收到此错误捕获致命错误:传递给Main :: __ construct()的参数1必须是Database的一个实例没有给出。并且,如果我从_ 构造参数中删除Database关键字,我会收到此错误警告:缺少Main :: _construct()的参数1。
如果我将一个变量从主类传递给User类,它可以正常工作,但是如果我试图传递Database对象,我就无法解决原因。
User类通过没有传递参数的路由器实例化。
答案 0 :(得分:1)
您可以在$database
班级静态中制作Main
变量。然后你只需要初始化一次:
class Main {
static $database = null;
public function __construct($db = null)
{
if (self::$database === null && $db instanceof Database) {
self::$database = $db;
}
}
}
答案 1 :(得分:0)
用户类扩展Main,因此它继承了Main类的__construct函数。
如果不传递其依赖项,数据库实例,则无法实例化User类。
$database = new Database($database_host, $database_user, $database_password, $database_name);
$user = new User($database);
然而,你可以像matewka暗示的那样做:
class Main {
static $database = null;
public function __construct($db = null)
{
if (self::$database === null && $db instanceof Database) {
self::$database = $db;
}
}
}
class User extends Main{
function test()
{
return parent::$database->test();
}
}
class Database{
function test()
{
return "DATABASE_TEST";
}
}
$db = new Database();
$main = new Main($db);
$user = new User();
var_dump($user->test());
答案 2 :(得分:0)
正如其他答案/评论中明确指出的那样,您没有将预期的依赖关系传递到Users
类,而是从__construct()
继承的Main
方法。
换句话说,Users
期望以与Main
相同的方式实例化;即:
$database = new Database();
$users = new Users($database);
但是,我想补充一些细节,因为在定义类之间的继承关系时,PHP中的构造函数与其他方法略有不同,我认为值得了解。
使用继承,你当然可以在继承类中覆盖父方法,在PHP中有一个特定的警告:如果你不匹配被覆盖方法的参数签名,你会触发E_STRICT
警告。这不是一个showstopper,但在努力编写稳定健壮的代码时最好避免使用:
E_STRICT
允许PHP建议对代码进行更改,以确保代码的最佳互操作性和向前兼容性。 Source
一个例子:
class Dependency
{
// implement
}
class Foo
{
function doSomething(Dependency $dep)
{
// parent implementation
}
}
class Bar extends Foo
{
function doSomething()
{
// overridden implementation
}
}
$bar = new Bar();
$bar->doSomething();
如果您使用php -l
在命令行上运行此代码或lint,您将获得类似的内容:
PHP Strict standards: Declaration of Bar::doSomething() should be compatible with Foo::doSomething(Dependency $dep) in cons.php on line 22
我之所以指出这一点是因为不适用到__construct
:
与其他方法不同,当使用与父__construct()方法不同的参数覆盖__construct()时,PHP不会生成E_STRICT级错误消息。 Source (#Example 1)
因此,您可以安全地覆盖继承类中的__construct
并定义不同的参数签名。
此示例完全有效:
class Foo
{
function __construct(Dependency $dep)
{
// parent constructor
}
}
class Bar extends Foo
{
function __construct()
{
// overridden constructor
}
}
$bar = new Bar();
当然,这很危险,因为在继承类Bar
的极有可能的事件中,调用依赖于Dependency
的代码会导致错误,因为您从未实例化或传递依赖项。
因此,您确实应该确保上述案例中的Dependency
传递给父类。所以你回到你开始的地方,在构造函数中传递依赖项,或执行以下操作,使用parent
keyword调用父项__construct
方法:
class Bar extends Main
{
public function __construct()
{
// call the Main constructor and ensure
// its expected dependencies are satisfied
parent::__construct(new Dependency());
}
}
但是,最佳实践要求您应将依赖项传递给对象,而不是在类中实例化它们,即a.k.a dependency injection。所以我们回到其他答案提供的原始解决方案:)
$database = new Database();
$users = new Users($database);
允许使用不同的方法签名覆盖__construct
只是在定义类时允许更大的灵活性 - 您可能继承了需要实例化更多信息的类。对于一个(荒谬的做作)的例子:
class Person
{
public function __construct($name)
{
// implement
}
}
class Prisoner extends Person
{
public function __construct($name, $number)
{
// implement
}
}
$p1 = new Person('Darragh');
$p2 = new Prisoner('Darragh', 'I AM NOT A NUMBER!');
我指出这一点是因为__construct
实际上非常灵活,如果您发现自己处于想要实例化具有不同父级参数的继承类的情况,您可以使用大警告你必须以某种方式确保你满足父类的所有预期的依赖关系。
希望这会有所帮助:)