繁体   English   中英

PHP对象传递给类,不被接受

[英]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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM