在PHP中搜索一种优雅的方式来加载依赖项/服务/配置?

时间:2011-11-08 21:31:56

标签: php oop dependency-injection serviceloader

我正在构建一个MVC PHP框架,我想知道哪些是在我的类中加载我需要的最佳实践,无论是其他类还是普通配置。

直到今天我使用了单身人士,注册表和最近的依赖注入容器。 虽然很多人声称DI是要走的路,但在我看来它只是将组件之间的耦合问题转移到另一个地方。

单身人士介绍全球状态,注册表介绍紧密耦合和DI介绍......好吧,很多复杂性。我仍然感到困惑,无法找到一种正确的方法来互相连接我的课程。

与此同时,我提出了一个自定义解决方案。实际上它不是一个解决方案,它只是从我的代码中抽象出服务加载的实现。

我使用_load_service和_load_config方法构建了一个抽象类,我的框架的所有组件都扩展了这些方法,以便加载其他服务或配置。

abstract class Base_Component {
    function _load_service($service) {
        // could be either
        return DI_container::getInstance()->$service;

        // or
        $class = '\services\\'.$service;
        return new $class;

        // or other implementation
    }
}

加载它们的实现现在只在一个地方实现,即基类,所以至少我把代码行除掉了以下代码:

$database = new Database(Registry::getInstance()->load('db_config'));

$database = DI_container::getInstance()->database;

现在如果想要一个数据库实例,我就这样做

$database = $this->_load_service('database');

并且可以在单个类方法中轻松更改服务加载器,容器,注册表或其他任何内容的实现,而无需搜索我的所有代码来更改对之前使用的任何容器实现的调用。

但正如我所说,我甚至不确定我将用于加载类和配置的方法。

你有什么看法?

2 个答案:

答案 0 :(得分:1)

为什么重新发明轮子?使用Pimple作为DI容器,并从文档中了解如何使用它。

或者,使用Silex微框架作为创建自己框架的基础。它扩展了Pimple功能,因此您可以使用依赖注入。

要回答你的问题,这就是你如何使用DI而不将你的类与它结合起来:

interface ContainerInterface {
    public function getService($service_name);
    public function registerService($service_name,Closure $service_definition);
}

class Application {
    public function __construct(ContainerInterface $container) {
        $this->container= $container;
    }

    public function run() {
        // very simple to use!
        $this->container->getService('db')->someDatabaseQuery();
    }
}

$c = new My_DI_Container;

// Service definitions could be in a separate file
$c->registerService('db',function() { return new Database('some config'); });

// Then you inject your DI container into the objects that need it
$app = new Application($c);
$app->run(); // or whatever

这样,DI容器就会分离,将来你可以使用不同的实现。唯一的要求是它实现了ContainerInterface。

请注意,正在推送容器对象,而不是拉出容器对象。避免使用单身人士。要获取/设置单实例对象,请使用容器(这是它的职责)。要获取容器实例,只需将其推送到构造函数。

答案 1 :(得分:0)

回答你的问题;看看PHP autoloading。通过自动加载注册类使得您无需在任何地方放置require / includes,这确实对RAD(快速应用程序开发)产生了积极影响。

我的想法:

为了尝试这样一项艰巨的任务,你的方法似乎是基于单身人士和工厂等良好做法。

我不关心依赖注入。 OOP基于封装,将一个对象注入另一个,imo,打破封装。将对象注入另一个对象时,目标对象必须“信任”没有关于注入对象的任何内容发生更改,否则您可能会遇到异常行为。

考虑你的类的名称间距(不是PHP名称间距,但是你的框架前缀像Zend一样,Zend_),这将有助于你可以注册一个名称空间,然后当一个类被调用时,自动加载器将确保正确类已加载。这就是Zend_Framework的工作原理。有关具体信息,请查看Zend_Loader_Autoloader。 Symfony框架实际上更进了一步;在第一个请求期间,它将遍历查找类文件的所有已知位置,然后它将构建一个类的数组和文件的路径,然后将数组保存到文件(文件缓存),因此后续请求将不会同样的开销。为您的框架考虑的事情。

就配置文件而言,Symfony使用YAML文件,我发现它非常灵活。您甚至可以包含PHP代码以提高灵活性。 Symfony提供了一个易于使用的独立YAML parser。您可以通过添加缓存层和缓存已解析的YAML文件来提高性能,这样您就不必为每个请求解析文件。

我假设您正在构建ORM之上的框架。我的建议不是针对ORM版本的任何特定功能,否则您的框架将与该版本相结合,您必须同时升级ORM和框架。

我建议在其他框架下查看内幕,看看你是否可以选择最好的每个框架;从而形成一个坚固,易于使用的框架。