在PHP中寻找加载依赖项/服务/配置的优雅方式?

在PHP中寻找加载依赖项/服务/配置的优雅方式?,php,oop,dependency-injection,serviceloader,Php,Oop,Dependency Injection,Serviceloader,我正在构建一个MVC PHP框架,我想知道在我的类中加载所需内容的最佳实践是什么,无论是其他类还是普通配置 abstract class Base_Component { function _load_service($service) { // could be either return DI_container::getInstance()->$service; // or $class = '\service

我正在构建一个MVC PHP框架,我想知道在我的类中加载所需内容的最佳实践是什么,无论是其他类还是普通配置

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
    }
}
直到今天,我还使用了单例、注册表和最近的依赖注入容器。 虽然许多人声称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 = $this->_load_service('database');
服务加载器、容器、注册表等的实现可以在单个类方法中轻松更改,而无需搜索所有代码来更改对我以前使用的任何容器实现的调用

但正如我所说的,我甚至不确定我将使用什么方法来加载类和配置

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
    }
}

你的意见是什么?

回答你的问题;看看PHP。通过自动加载注册类可以使您不必到处放置require/includes,这对RAD(快速应用程序开发)确实有积极的影响

我的想法:

由于您尝试了如此艰巨的任务,您的方法似乎基于良好的实践,如单身和工厂

我不喜欢依赖注入。OOP基于封装,将一个对象注入到另一个对象中,imo破坏了封装。当您将一个对象注入另一个对象时,目标对象必须“相信”与注入对象相关的任何内容都没有更改,否则您可能会出现异常行为

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

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

我假设您正在ORM之上构建框架。我的建议是不要使用特定于ORM版本的任何功能,否则您的框架将与该版本耦合,您将不得不同时升级ORM和框架


我建议你看看其他的框架,看看你是否能从中挑选出最好的;产生了一个坚实、易于使用的框架。

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

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

为了回答您的问题,以下是如何在不将类与DI耦合的情况下使用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


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

感谢您的贡献,但您实际上没有回答我的问题。这是我的错,我忘了提到我已经使用了名称空间和类自动加载。我的问题不是如何加载类,而是如何在不引入类之间的紧密耦合和难以维护的代码的情况下加载类。我正在“重新发明轮子”,以获得从中开发的知识和经验。否则,当有如此多的可用框架提供出色的服务时,我就不会费心制作一个框架了。我已经研究了pimple,并构建了一些类似但简单得多的东西。但在构建DI容器之前,您应该了解为什么以及如何使用它。而且您似乎一直通过静态调用引入隐式依赖项(这与您在使用DI时所希望的相反)。您还提到了DI引入了复杂性。这就是DI容器的用途:使DI简单化这似乎是一种比我目前所做的更干净的方法。答案已接受。这不是依赖项注入,这是一个名为“容器”的服务定位器…--@ChristofferBubach那么什么是依赖注入呢?你能给出一个简单的DI示例吗,就像HappyDeveloper对“容器”所做的那样?我编辑了我的答案,添加了一个代码示例