PHP类依赖注入失败

时间:2017-02-02 17:15:12

标签: php oop dependency-injection

好的,在访问一个类中的其他几个类时,我经常遇到一些未解决的错误。 根据首先需要哪个类,它不会在其他文件的 __ construct 函数中访问。

Undefined property: Container::$Core in /usr/share/nginx/html/app/classes/class.users.php on line 9


class.container.php

public function __construct() {
    if(isset(self::$class)) {
        foreach(self::$class as $key => $value) {
            if(!isset($this->{$key})) {
                $this->{$key} = $value;
            }
        }
    }   
} 

public function setClassHandlers($class = []) {
            if(!isset(self::$class)) {
                foreach($class as $name => $file) {
                    $file = DIR . "app/classes/" . $file;

                    if(!isset(self::$library[$name])) {
                        // Get interface file
                        $interface = DIR . "app/classes/interfaces/interface.".strtolower($name).".php";
                        // Check if interface file exists
                        if(!file_exists($interface)) {
                            // Throw exception
                            $this->throwException("Unable to load interface file: {$interface}");
                        }

                        // Require interface
                        require_once $interface;
                        //Check if interface is set
                        if(!interface_exists("i".ucfirst($name))) {
                            // Throw exception
                            $this->throwException("Unable to find interface: {$interface}");
                        }

                        // Check if class file exists
                        if(!file_exists($file)) {
                            // Throw exception
                            $this->throwException("Unable to load class file: {$file}");
                        }
                        // Require class
                        require_once $file;
                        // Check if class file exists
                        if(class_exists($name)) {
                            // Set Library
                            self::$library[$name] = $file;
                            // Set interface
                            self::$interface[$name] = $interface;
                            // Set class        // class.container.php
                            self::$class[$name] = new $name(new self);
                            $this->{$name} = self::$class[$name];
                        } else {
                            // Thror error
                            $this->throwException("Unable to load class: {$name}", self::$library[$name]);
                        }

                    }
                }
            } 
        }

index.php

中的函数中插入参数
require_once DIR . 'app/management/dependency.php';

$container = new Container();

$container->setClassHandlers([
    // Name         // Path
    'Database'  => 'class.database.php',
    'Config'    => 'class.config.php',
    'Users'     => 'class.users.php',
    'Core'      => 'class.core.php',
    //'Admin'       => 'class.admin.php',
    //'Forum'       => 'class.forum.php',
    //'Template'    => 'class.template.php'
]);

class.users.php

public function __construct(Container $class) {
        $this->db   = $class->Database;
        $this->core = $class->Core;
        $this->ip   = $this->core->userIP();
        $this->container = $class;
    }

例如,用户核心在彼此的同一文件中使用,但如上所述,如果首先需要核心用户不是该类中的可用依赖项 我真的不太确定如何解决这个问题,所以每一个帮助都表示赞赏。

1 个答案:

答案 0 :(得分:1)

您的"容器"正在尝试在运行setClassHandlers方法时实例化包含的对象,到那时并非所有属性都可以设置。

另外,你的容器不是"懒惰"足够。正试图实例化所有现在,即使可能不需要它。

尝试以下

首先,删除以下行:

    // Set class        // class.container.php
    self::$class[$name] = new $name(new self);
    $this->{$name} = self::$class[$name];

然后,向Container类添加私有数组服务:

private $services = [];

最后,在容器中添加一个魔法吸气剂:

function __get($service)
    {
        if ( ! isset($this->services[$service]) ) {
            $this->services[$service] = new $service();
        }

        return $this->services[$service];
    }

再一次,了解这一点很棒,但我建议你看看其他一些实现,以便从中学习。 Pimple很棒,很简单,也很容易理解。

这里我们在所有对象上注入容器而不是对象具体依赖项,这通常(正确地)不受欢迎。但我不想对您的设计进行更多修改,您最好自己学习。

最重要的是,你的容器还处理autoloader更好处理的事情,以及混合责任。

另外,正如我在评论中所说,你正在重新发明PHP中已存在的__DIR__常量。您正在使用

define( 'DIR', dirname(__FILE__) . '/' );

几乎等同于__DIR__(或者更准确地说,等同于__DIR__ . '/')。

最后,您的大多数麻烦都是由循环依赖引起的,您的容器(也不是任何容器)无法修复。 A依赖于B,它依赖于A.唯一可以解决的方法是使用setter来注入依赖项。

更简单的实现,或多或少地遵循您的代码:

自动加载:

function autoload_so($class)
{
    $file = __DIR__ . "/app/classes/class." . strtolower($class) . '.php';
    if (file_exists($file)) {
        include_once($file);

        return true;
    }

    return false;
}

spl_autoload_register('autoload_so');

我没有使用名称空间,我忽略了你的界面逻辑。这也有点奇怪。您需要为接口实现自己的加载,您应该能够这样做。

对于容器:

class MyContainer
{
    public $services = [];

    function __get($service)
    {
        if ( ! isset($this->services[$service]) ) {
            $this->services[$service] = call_user_func([$this, "get_$service"]);
        }

        return $this->services[$service];
    }

    /**
     * @return Users
     */
    private function get_Users()
    {
        return new Users($this->Database, $this->Core);
    }

    /**
     * @return Core
     */
    private function get_Core()
    {
        return new Core();
    }

    /**
     * @return Database
     */
    private function get_Database()
    {
        return new Database($this->Core);
    }

}

请注意

  • 您需要为每个新服务提供一种新方法"你想要添加。
  • 我直接在构造函数
  • 中注入依赖项

最后你的课程会是这样的:

数据库

<?php

class Database
{
    public function __construct(Core $core)
    {
        $this->core  = $core;
    }
}

用户     

class Users
{
    public $ip;

    public function __construct(Database $db, Core $core)
    {
        $this->db   = $db;
        $this->core = $core;
        $this->ip   = $this->core->userIP();
    }

}

等。

同样,这一切都非常粗糙。我会使用名称空间和更通用的自动加载器和目录结构。

所有这一切都应该足以让你建立自己的起点。

但是没有容器会神奇地修复你的循环依赖