简体   繁体   English

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

[英]Searching for an elegant way in PHP for loading dependencies/services/configuration?

I'm building a MVC PHP framework and I wonder which are the best practices to load what I need in my classes, be it other classes or plain configuration. 我正在构建一个MVC PHP框架,我想知道哪种最佳方法可以在我的类(无论是其他类还是纯配置)中加载所需的内容。

Till today I've used singletons, registry and lately a dependency injection container. 直到今天,我一直使用单例,注册表和最近的依赖项注入容器。 While many people claim that DI is the way to go, it seems to me like it justs moves the problem of coupling between components into another place. 尽管许多人声称DI是必经之路,但在我看来,这只是将组件之间的耦合问题转移到另一个地方。

Singletons introduce global state, registry introduces tight coupling and DI introduces... well, lots of complexity. Singletons引入了全局状态,registry引入了紧密耦合,而DI引入了……好,非常复杂。 I am still confused and can't find a proper way to wire my classes each other. 我仍然很困惑,找不到找到彼此联系的正确方法。

In the meanwhile, I came up with a custom solution. 同时,我想出了一个自定义解决方案。 Actually it's not a solution, it just abstracts the implementation of service loading from my code. 实际上,这不是解决方案,它只是从我的代码中抽象出了服务加载的实现。

I built an abstract class with _load_service and _load_config methods which all the components of my framework extend in order to load other services or configuration. 我用_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
    }
}

The implementation of loading them is now implemented in only one place, the base class, so at least I got rid of code lines like the following into my components: 现在,仅在一个位置(基类)中实现了加载它们的实现,因此至少我在组件中摆脱了如下代码行:

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

or 要么

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

Now if want a database instance I do this 现在,如果想要一个数据库实例,我可以这样做

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

and the implementation of service loader, container, registry or whatever can be easily changed in a single class method without having to search through all my code to change calls to whatever container implementation I was using before. 并且可以在单个类方法中轻松更改服务加载程序,容器,注册表或其他任何东西的实现,而无需搜索我的所有代码即可将调用更改为我以前使用的任何容器实现。

But as I said I'm not even close to sure about what method I will use for loading classes and configuration. 但是正如我所说,我什至还不确定要使用哪种方法来加载类和配置。

What are your opinions? 您对此有何看法?

Why reinvent the wheel? 为什么要重新发明轮子? Use Pimple as your DI container, and learn how to use it from its documentation. Pimple用作DI容器,并从其文档中学习如何使用它。

Or, use Silex microframework as a base to create your own framework. 或者,以Silex微框架为基础来创建自己的框架。 It extends Pimple functionality, so you can use dependency injection. 它扩展了Pimple功能,因此您可以使用依赖项注入。

To answer your question, this is how you use a DI without coupling your classes to it: 要回答您的问题,这是如何在不将类与其耦合的情况下使用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

This way, the DI container is decoupled and in the future you could use a different implementation. 这样,DI容器将解耦,将来您可以使用其他实现。 The only requirement is that it implements the ContainerInterface. 唯一的要求是它实现ContainerInterface。

Note that the container object is being pushed, and not pulled. 请注意,容器对象是被推动而不是被拉动的。 Avoid using singleton. 避免使用单例。 To get/set single-instance objects, use the container (that's its responsibility). 要获取/设置单实例对象,请使用容器(这是它的责任)。 And to get the container instance, just push it through constructors. 要获取容器实例,只需将其通过构造函数推送即可。

Answer to your question; 回答您的问题; Look at PHP autoloading . 看一下PHP 自动加载 Registering classes via autoloading makes it so you don't have to put require/includes everywhere, which really has a positive impact on RAD (rapid application development). 通过自动加载来注册类使您不必在任何地方都放置需求/包含,这确实对RAD(快速应用程序开发)产生积极影响。

My thoughts: 我的想法:

Kudos for attempting such a daunting task, your approach appears to based on good practices such as singletons and factories. 为完成如此艰巨的任务而感到荣誉,您的方法似乎基于诸如单例和工厂之类的良好实践。

I don't care for dependency injection. 我不在乎依赖注入。 OOP is based on encapsulation, injecting one object into another, imo, breaks that encapsulation. OOP基于封装,将一个对象注入到另一个对象imo中会破坏这种封装。 When you inject an object into another object, the target object has to 'trust' that nothing regarding the injected object has changed, otherwise you may get unusual behavior. 当您将一个对象注入另一个对象时,目标对象必须“相信”与注入的对象无关的任何内容,否则可能会出现异常行为。

Consider name-spacing your classes (not PHP name-spacing, but prefix your framework like Zend does, Zend_), this will help so you can register a namespace, then when a class is called the autoloader will ensure that the proper class is loaded. 考虑对类进行名称命名(而不是PHP名称),但要像Zend一样为您的框架加上前缀Zend_,这将有所帮助,以便您可以注册名称空间,然后在调用类时,自动加载器将确保加载正确的类。 。 This is how Zend_Framework works. 这就是Zend_Framework的工作方式。 For specifics check out Zend_Loader_Autoloader . 有关详细信息,请查看Zend_Loader_Autoloader The Symfony framework actually takes this one step further; 实际上,Symfony框架更进一步。 during the first request it will go through all known locations looking for class files, it will then build out an array of the classes and paths to the files then save the array to a file (file caching), so subsequent requests won't have the same overhead. 在第一个请求期间,它将遍历所有已知位置以查找类文件,然后将构建一个类和文件路径的数组,然后将该数组保存到文件中(文件缓存),因此后续请求不会相同的开销。 Something to consider for your framework. 需要为您的框架考虑的东西。

As far as config files go, Symfony uses YAML files, which I have found to be extremely flexible. 就配置文件而言,Symfony使用YAML文件,我发现它非常灵活。 You can even include PHP code for increased flexibility. 您甚至可以包含PHP代码以提高灵活性。 Symfony has provided a stand-alone YAML parser that is easy to use. Symfony提供了易于使用的独立YAML解析器 You can increase performance by adding a caching layer and caching parsed YAML files so you don't have to parse the files for every request. 您可以通过添加缓存层和缓存已解析的YAML文件来提高性能,从而不必为每个请求都解析文件。

I am assuming you are building your framework on top of an ORM. 我假设您是在ORM之上构建您的框架。 My recommendation would be not to any functionality specific to a version of the ORM, otherwise your framework becomes coupled with that version and you will have to upgrade both the ORM and the framework at the same time. 我的建议不是针对特定版本的ORM的任何功能,否则您的框架将与该版本耦合,并且您将必须同时升级ORM和该框架。

I would suggest looking under the hood at other frameworks and see if you can pick the best of each; 我建议在其他框架下进行深入研究,看看是否可以选择每个框架中的最佳框架。 resulting in a solid, easy to use framework. 从而形成一个坚实,易于使用的框架。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

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