[英]PHP Object being passed to class, not being accepted
我有一个数据库类,它具有一系列功能,还有一个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);
然后,这是我要扩展的Users类。
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类是通过路由器实例化的,没有向其传递任何参数。
您可以将Main
类中的$database
变量设置为static 。 然后,您只需要初始化一次:
class Main {
static $database = null;
public function __construct($db = null)
{
if (self::$database === null && $db instanceof Database) {
self::$database = $db;
}
}
}
User类扩展了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());
正如在其他答案/评论中明确指出的那样,您没有将预期的依赖项传递到您的Users
类中,而不将其传递给它从Main
继承的__construct()
方法。
换句话说, Users
期望以与Main
相同的方式实例化; 即:
$database = new Database();
$users = new Users($database);
但是,我想补充一些细节,因为在定义类之间的继承关系时,PHP中的构造函数与其他方法略有不同,我认为这是值得了解的。
使用继承,您当然可以在继承类中重写父类的方法,并在PHP中进行特定的警告:如果您不匹配被重写方法的参数签名,则会触发E_STRICT
警告。 这不是问题,但是在努力编写稳定的健壮代码时,最好避免:
E_STRICT
使PHP建议对代码进行更改,以确保最佳的互操作性和代码的前向兼容性。 资源
一个例子:
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
,则会得到类似以下内容的信息:
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级错误消息。 来源(示例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
关键字调用父级的__construct
方法:
class Bar extends Main
{
public function __construct()
{
// call the Main constructor and ensure
// its expected dependencies are satisfied
parent::__construct(new Dependency());
}
}
但是,最佳实践表明,您应该将依赖项传递到对象中,而不是在类中实例化它们,也就是依赖项注入 。 因此,我们回到了其他答案提供的原始解决方案:)
$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
实际上非常灵活,如果您发现自己想实例化一个对父类使用不同参数的继承类,则可以这样做,但要注意的是,必须以某种方式确保您满足父类的所有预期依赖项。
希望这可以帮助 :)
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.