Phalcon的依赖注入与服务位置【译】

The following example is a bit lengthy, but it attempts to explain why Phalcon uses service location and dependency injection. First, let’s pretend we are developing a component called SomeComponent. This performs a task that is not important now. Our component has some dependency that is a connection to a database.

In this first example, the connection is created inside the component. This approach is impractical; due to the fact we cannot change the connection parameters or the type of database system because the component only works as created.

以下的例子略显冗长,但其试图阐释Phalcon框架使用依赖注入与服务位置的原因。首先,假设我们要开发一个名为SomeComponent的组件,执行一个当前并不重要的任务,该组件依赖一些代码来链接数据库。

在首个例子中,数据库链接在我们的组件内部被创建。这种实践不切实际,由此导致在组件外部无法修改链接参数或数据库类型,组件与new出来的对象紧密耦合,绑定的死死地。

<?php

class SomeComponent {

    /**
     * The instantiation of the connection is hardcoded inside 
     * the component, therefore it's difficult replace it externally
     * or change its behavior
     */
    public function someDbTask() {

        $connection = new Connection( array (
                "host" => "localhost",
                "username" => "root",
                "password" => "secret",
                "dbname" => "invo") );
        
        // ...
    }
}
$some = new SomeComponent();
$some->someDbTask();

To solve this, we have created a setter that injects the dependency externally before using it. For now, this seems to be a good solution:

为应对此情况,我们创建一个setter方法将依赖的对象注入组件进而使用之,目前看,这方案还算不赖:

<?php

class SomeComponent {

    protected $_connection;
    
    /**
     * Sets the connection externally
     */
    public function setConnection( $connection ) {
        $this->_connection = $connection;
    }
    
    public function someDbTask() {
        $connection = $this->_connection;
        
        // ...
    }
}
$some = new SomeComponent();

// Create the connection
$connection = new Connection( array (
        "host" => "localhost",
        "username" => "root",
        "password" => "secret",
        "dbname" => "invo") );
        
// Inject the connection in the component
$some->setConnection( $connection );
$some->someDbTask();

Now consider that we use this component in different parts of the application and then we will need to create the connection several times before passing it to the component. Using some kind of global registry where we obtain the connection instance and not have to create it again and again could solve this:

进一步,考虑我们在程序的许多不同部分都使用此组件,因而我们需要许多次的创建链接对象并传递给该组件。使用一个包含链接对象实例的全局注册类可以避免一次又一次的创建该对象。

<?php

class Registry {

    /**
     * Returns the connection
     */
    public static function getConnection() {
        return new Connection( array (
                "host" => "localhost",
                "username" => "root",
                "password" => "secret",
                "dbname" => "invo") );
    }
}

class SomeComponent {

    protected $_connection;
    
    /**
     * Sets the connection externally
     */
    public function setConnection( $connection ) {
        $this->_connection = $connection;
    }
    
    public function someDbTask() {
        $connection = $this->_connection;
        
        // ...
    }
}
$some = new SomeComponent();

// Pass the connection defined in the registry
$some->setConnection( Registry::getConnection() );
$some->someDbTask();

Now, let’s imagine that we must implement two methods in the component, the first always needs to create a new connection and the second always needs to use a shared connection:

再进一步,假设我们必须在组件中实现两个方法,一个总是需要创建一个新的数据库链接,而另一个总是需要一个共享的数据库链接:

<?php

class Registry {

    protected static $_connection;
    
    /**
     * Creates a connection
     */
    protected static function _createConnection() {
        return new Connection( array (
                "host" => "localhost",
                "username" => "root",
                "password" => "secret",
                "dbname" => "invo") );
    }
    
    /**
     * Creates a connection only once and returns it
     */
    public static function getSharedConnection() {
        if ( self::$_connection === null ) {
            $connection = self::_createConnection();
            self::$_connection = $connection;
        }
        
        return self::$_connection;
    }
    
    /**
     * Always returns a new connection
     */
    public static function getNewConnection() {
        return self::_createConnection();
    }
}

class SomeComponent {

    protected $_connection;
    
    /**
     * Sets the connection externally
     */
    public function setConnection( $connection ) {
        $this->_connection = $connection;
    }
    
    /**
     * This method always needs the shared connection
     */
    public function someDbTask() {
        $connection = $this->_connection;
        
        // ...
    }
    
    /**
     * This method always needs a new connection
     */
    public function someOtherDbTask( $connection ) {
    }
}

$some = new SomeComponent();

// This injects the shared connection
$some->setConnection( Registry::getSharedConnection() );
$some->someDbTask();

// Here, we always pass a new connection as parameter
$some->someOtherDbTask( Registry::getNewConnection() );

So far we have seen how dependency injection solved our problems. Passing dependencies as arguments instead of creating them internally in the code makes our application more maintainable and decoupled. However, in the long-term, this form of dependency injection has some disadvantages.

For instance, if the component has many dependencies, we will need to create multiple setter arguments to pass the dependencies or create a constructor that pass them with many arguments, additionally creating dependencies before using the component, every time, makes our code not as maintainable as we would like:

至此,我们看到依赖注入如何解决我们最初的难题,将依赖项以参数方式传递来替代将其创建在组件内部,我们的程序与依赖项解耦从而提高可扩展/维护性。然而,随着项目的推进,时日一长,依赖注入的方案会出现一些缺点。

例如,如果组件有许多依赖项,我们就须在组件内部编写许多的setter或者一个参数众多的构造函数,在每次使用组件前,还得创建所有的依赖项,使得代码再次难以维护:

<?php

// Create the dependencies or retrieve them from the registry
$connection = new Connection();
$session = new Session();
$fileSystem = new FileSystem();
$filter = new Filter();
$selector = new Selector();

// Pass them as constructor parameters
$some = new SomeComponent( $connection, $session, $fileSystem, $filter, $selector );

// ... Or using setters
$some->setConnection( $connection );
$some->setSession( $session );
$some->setFileSystem( $fileSystem );
$some->setFilter( $filter );
$some->setSelector( $selector );

Think if we had to create this object in many parts of our application. In the future, if we do not require any of the dependencies, we need to go through the entire code base to remove the parameter in any constructor or setter where we injected the code. To solve this, we return again to a global registry to create the component. However, it adds a new layer of abstraction before creating the object:

若我们的程序中多处用到此组件,而在将来某个时刻,组件的依赖发生变化(如:不再依赖某项),则我们需要遍历所有代码,修改当初注入给组件的setter或构造函数代码。为避免如此,我们试图采用前面的全局注册类方案,但这引入了新的抽象层来创建对象:

<?php

class SomeComponent {

    // ...
    
    /**
     * Define a factory method to create SomeComponent instances injecting its dependencies
     */
    public static function factory() {
        $connection = new Connection();
        $session = new Session();
        $fileSystem = new FileSystem();
        $filter = new Filter();
        $selector = new Selector();
        
        return new self( $connection, $session, $fileSystem, $filter, $selector );
    }
}

Now we find ourselves back where we started, we are again building the dependencies inside of the component! We must find a solution that keeps us from repeatedly falling into bad practices.

A practical and elegant way to solve these problems is using a container for dependencies. The containers act as the global registry that we saw earlier. Using the container for dependencies as a bridge to obtain the dependencies allows us to reduce the complexity of our component:

忽然之间,我们又回到了问题的起点,依赖项再一次在组件的内部被创建!我们必须找到两全之策,不可重蹈覆辙。

一个实用而又优雅的方案是:为依赖项使用一个容器。此容器类似之前我们看到的全局注册类。用此容器作为组件获得依赖项的桥梁,允许我们减少组件的复杂程度:

<?php

use Phalcon\DI;

class SomeComponent {

    protected $_di;

    public function __construct( $di ) {

        $this->_di = $di;
    }

    public function someDbTask() {
        // Get the connection service
        // Always returns a new connection
        $connection = $this->_di->get( 'db' );
    }

    public function someOtherDbTask() {
        // Get a shared connection service,
        // this will return the same connection everytime
        $connection = $this->_di->getShared( 'db' );
        
        // This method also requires an input filtering service
        $filter = $this->_di->get( 'filter' );
    }
}

$di = new DI();

// Register a "db" service in the container
$di->set( 'db', function () {
    return new Connection( array (
            "host" => "localhost",
            "username" => "root",
            "password" => "secret",
            "dbname" => "invo") );
} );

// Register a "filter" service in the container
$di->set( 'filter', function () {
    return new Filter();
} );

// Register a "session" service in the container
$di->set( 'session', function () {
    return new Session();
} );

// Pass the service container as unique parameter
$some = new SomeComponent( $di );

$some->someDbTask();

The component can now simply access the service it requires when it needs it, if it does not require a service it is not even initialized, saving resources. The component is now highly decoupled. For example, we can replace the manner in which connections are created, their behavior or any other aspect of them and that would not affect the component.

终于,组件现在简单的按需获取依赖项,无需的服务甚至不会被初始化,这也节省了系统资源。组件而今已高度解耦。比如,我们可以替换所创建的链接的方式、行为或其他方面却不影响到组件。

Our approach

Phalcon\DI is a component implementing Dependency Injection and Location of services and it’s itself a container for them.

Since Phalcon is highly decoupled, Phalcon\DI is essential to integrate the different components of the framework. The developer can also use this component to inject dependencies and manage global instances of the different classes used in the application.

Basically, this component implements the Inversion of Control pattern. Applying this, the objects do not receive their dependencies using setters or constructors, but requesting a service dependency injector. This reduces the overall complexity since there is only one way to get the required dependencies within a component.

Additionally, this pattern increases testability in the code, thus making it less prone to errors.

我们的方针

Phalcon\DI是一个容器组件,实现并包含了依赖注入、服务位置和其自身

Phalcon是松耦合的,Phalcon\DI对于在框架中整合不同的组件至关重要。开发者可以使用DI来注入和管理不同类的全局对象实例。

基本上,此组件实现了“控制倒转”模式。藉此,各种对象不再需要setter和构造函数获取依赖项,而是向容器请求具体服务。这减少了整体的复杂性,因为现在组件只有一个方法来获取依赖项。

另外,此模式增强了代码可测试性,使代码不太容易出错。

控制倒转:IoC,直观地讲,就是容器控制程序之间的关系,而非传统实现中,由程序代码直接操控。这也就是所谓“控制反转”的概念所在。控制权由应用代码中转到了外部容器,控制权的转移是所谓反转。IoC还有另外一个名字——“依赖注入(Dependency Injection)”。从名字上理解,所谓依赖注入,即组件之间的依赖关系由容器在运行期决定,形象地说,即由容器动态地将某种依赖关系注入到组件之中。

Registering services in the Container

The framework itself or the developer can register services. When a component A requires component B (or an instance of its class) to operate, it can request component B from the container, rather than creating a new instance component B.

This way of working gives us many advantages:

  • We can easily replace a component with one created by ourselves or a third party.

  • We have full control of the object initialization, allowing us to set these objects, as needed before delivering them to components.

  • We can get global instances of components in a structured and unified way

Services can be registered using several types of definitions:

在容器中注册服务

框架自身和开发者都可以注册服务。当组件A需要组件B(或其类对象实例)时,其可以向容器请求组件B,而不是创建组件B的实例。

这样做又诸多好处:

  • 我们可以很容易的替换自己开发的组件或第三方组件.

  • 我们可以完整的控制对象初始化,动态的设置这些对象,而后再传递给请求对象的组件。(AOC)

  • 我们可以结构化且统一的方式得到组件的全局实例

服务可以通过多种定义方式注册给容器:

<?php

use Phalcon\Http\Request;

// Create the Dependency Injector Container
$di = new Phalcon\DI();

// By its class name
$di->set( "request", 'Phalcon\Http\Request' );

// Using an anonymous function, the instance will be lazy loaded
$di->set( "request", function () {
    return new Request();
} );

// Registering an instance directly
$di->set( "request", new Request() );

// Using an array definition
$di->set( "request", array (
        "className" => 'Phalcon\Http\Request') );

The array syntax is also allowed to register services:

数组语法同样被支持:

<?php
use Phalcon\Http\Request; 

// Create the Dependency Injector Container
$di = new Phalcon\DI(); 

// By its class name
$di["request"] = 'Phalcon\Http\Request'; 

// Using an anonymous function, the instance will be lazy loaded
$di["request"] = function () {
    return new Request();
}; 

// Registering an instance directly
$di["request"] = new Request(); 

// Using an array definition
$di["request"] = array (
        "className" => 'Phalcon\Http\Request');

In the examples above, when the framework needs to access the request data, it will ask for the service identified as ‘request’ in the container. The container in turn will return an instance of the required service. A developer might eventually replace a component when he/she needs.

Each of the methods (demonstrated in the examples above) used to set/register a service has advantages and disadvantages. It is up to the developer and the particular requirements that will designate which one is used.

Setting a service by a string is simple, but lacks flexibility. Setting services using an array offers a lot more flexibility, but makes the code more complicated. The lambda function is a good balance between the two, but could lead to more maintenance than one would expect.

Phalcon\DI offers lazy loading for every service it stores. Unless the developer chooses to instantiate an object directly and store it in the container, any object stored in it (via array, string, etc.) will be lazy loaded i.e. instantiated only when requested.

在以上例子中,当框架需要访问所请求的数据,将向容器询问'request'所标识的服务。容器则将所请求服务的实例返回。开发人员可以按需替换组件。

每一种设置或注册服务的方法都有各自的优缺点。取舍时需按开发者与特定的需求而定。

用字符串设置服务名简单但缺乏灵活。用数组元素设置服务更灵活,但代码稍加复杂。Lambda函数的方式对简单与灵活性进行了折中,但可能会造成超出预期的维护成本。

Phalcon\DI 对其所保存的服务提供迟加载特性。除非开发者选择直接实例化一个对象并将其保存到容器,其余保存在容器中的对象(通过数组,字符串等)将迟加载,即:在被请求时才实例化。

Simple Registration 简单注册

As seen before, there are several ways to register services. These we call simple:

如前所述,注册服务的方法很多。首先是简单方法:

String 字符串

This type expects the name of a valid class, returning an object of the specified class, if the class is not loaded it will be instantiated using an auto-loader. This type of definition does not allow to specify arguments for the class constructor or parameters:

这种方法需要一个有效的类名,返回的是该类的对象,如果类未加载将由auto-loader实例化。这种方法不能给类的构造函数指定参数。

<?php
// Return new Phalcon\Http\Request();
$di->set( 'request', 'Phalcon\Http\Request' );
Object 对象

This type expects an object. Due to the fact that object does not need to be resolved as it is already an object, one could say that it is not really a dependency injection, however it is useful if you want to force the returned dependency to always be the same object/value:

这种方法需要一个对象。由于已经实例化了一个对象,因此不需要解析,或许有人认为这并非依赖注入,但这种方法在想强制返回一个相同的对象/值的时候很实用:

<?php

use Phalcon\Http\Request;

// Return new Phalcon\Http\Request();
$di->set( 'request', new Request() );
Closures/Anonymous functions 闭包/匿名函数

This method offers greater freedom to build the dependency as desired, however, it is difficult to change some of the parameters externally without having to completely change the definition of dependency:

这种方法更加自由的按需构造依赖项,然而,却难以在外部修改参数除非修改依赖项的完整定义:

<?php

use Phalcon\Db\Adapter\Pdo\Mysql as PdoMysql;

$di->set( "db", function () {
    return new PdoMysql( array (
            "host" => "localhost",
            "username" => "root",
            "password" => "secret",
            "dbname" => "blog") );
} );

Some of the limitations can be overcome by passing additional variables to the closure’s environment:

通过传递额外的变量给闭包,可以突破一些限制:

<?php

use Phalcon\Db\Adapter\Pdo\Mysql as PdoMysql;

// Using the $config variable in the current scope
$di->set( "db", function () use($config ) {
    return new PdoMysql( array (
            "host" => $config->host,
            "username" => $config->username,
            "password" => $config->password,
            "dbname" => $config->name) );
} );

Complex Registration 复杂注册

If it is required to change the definition of a service without instantiating/resolving the service, then, we need to define the services using the array syntax. Define a service using an array definition can be a little more verbose:

如果需要改变服务的定义但不初始化/解析该服务,则需要用数组语法定义服务。这样的语法稍显啰嗦:

<?php

use Phalcon\Logger\Adapter\File as LoggerFile;

// Register a service 'logger' with a class name and its parameters
$di->set( 'logger', array (
        'className' => 'Phalcon\Logger\Adapter\File',
        'arguments' => array (
                array (
                        'type' => 'parameter',
                        'value' => '../apps/logs/error.log'))) );
                        
// Using an anonymous function
$di->set( 'logger', function () {
    return new LoggerFile( '../apps/logs/error.log' );
} );

Both service registrations above produce the same result. The array definition however, allows for alteration of the service parameters if needed:

上述两种注册服务的方法产生的结果完全相同。然而数组语法允许修改参数:

<?php

// Change the service class name
$di->getService( 'logger' )->setClassName( 'MyCustomLogger' );

// Change the first parameter without instantiating the logger
$di->getService( 'logger' )->setParameter( 0, array (
        'type' => 'parameter',
        'value' => '../apps/logs/error.log') );

In addition by using the array syntax you can use three types of dependency injection:

此外,数组语法提供了三种依赖注入的方式:

Constructor Injection 构造函数注入

This injection type passes the dependencies/arguments to the class constructor. Let’s pretend we have the following component:

这种注入传递依赖项/参数给类的构造函数。假设有如下组件:

<?php

namespace SomeApp;

use Phalcon\Http\Response;

class SomeComponent {

    protected $_response;

    protected $_someFlag;

    public function __construct( Response $response, $someFlag ) {

        $this->_response = $response;
        $this->_someFlag = $someFlag;
    }
}

The service can be registered this way:

该服务可以向下面这样注册:

<?php

$di->set( 'response', array (
        'className' => 'Phalcon\Http\Response') );
        
$di->set( 'someComponent', array (
        'className' => 'SomeApp\SomeComponent',
        'arguments' => array (
                array (
                        'type' => 'service',
                        'name' => 'response'),
                array (
                        'type' => 'parameter',
                        'value' => true))) );

The service “response” (Phalcon\Http\Response) is resolved to be passed as the first argument of the constructor, while the second is a boolean value (true) that is passed as it is.

response服务被解析(Phslcon\Http\Response)并作为第一个参数传递给构造函数,而布尔值true作为第二个参数传递。

Setter Injection Setter注入

Classes may have setters to inject optional dependencies, our previous class can be changed to accept the dependencies with setters:

类可以有若干setter用以注入可选的依赖项,前面那给类可以修改成下面这样用setter来接收依赖项:

<?php

namespace SomeApp;

use Phalcon\Http\Response;

class SomeComponent {

    protected $_response;

    protected $_someFlag;

    public function setResponse( Response $response ) {

        $this->_response = $response;
    }

    public function setFlag( $someFlag ) {

        $this->_someFlag = $someFlag;
    }
}

A service with setter injection can be registered as follows:

一个带有setter注入的服务可以像下面这样注册:

<?php

$di->set( 'response', array (
        'className' => 'Phalcon\Http\Response') );
        
$di->set( 'someComponent', array (
        'className' => 'SomeApp\SomeComponent',
        'calls' => array (
                array (
                        'method' => 'setResponse',
                        'arguments' => array (
                                array (
                                        'type' => 'service',
                                        'name' => 'response'))),
                array (
                        'method' => 'setFlag',
                        'arguments' => array (
                                array (
                                        'type' => 'parameter',
                                        'value' => true))))) );
Properties Injection 属性(数据类成员)注入

A less common strategy is to inject dependencies or parameters directly into public attributes of the class:

一种不常见的策略是通过类的公有属性来注入依赖或参数:

<?php

namespace SomeApp;

use Phalcon\Http\Response;

class SomeComponent {

    public $response;

    public $someFlag;
}

A service with properties injection can be registered as follows:

一个允许公有属性注入的服务可以像下面这样注册:

<?php

$di->set( 'response', array (
        'className' => 'Phalcon\Http\Response') );
        
$di->set( 'someComponent', array (
        'className' => 'SomeApp\SomeComponent',
        'properties' => array (
                array (
                        'name' => 'response',
                        'value' => array (
                                'type' => 'service',
                                'name' => 'response')),
                array (
                        'name' => 'someFlag',
                        'value' => array (
                                'type' => 'parameter',
                                'value' => true)))) );

Supported parameter types include the following:

支持的参数类型如下:

Type 类型
Description 描述
Example 例子

parameter

参数

Represents a literal value to be passed as parameterarray(‘type’ => ‘parameter’, ‘value’ => 1234 )

service

服务

Represents another service in the service containerarray(‘type’ => ‘service’, ‘name’ => ‘request’)

instance

实例

Represents an object that must be built dynamicallyarray(‘type’ => ‘instance’, ‘className’ => ‘DateTime’, ‘arguments’ => array(‘now’) )

Resolving a service whose definition is complex may be slightly slower than simple definitions seen previously. However, these provide a more robust approach to define and inject services.

解析一个复杂定义的服务比解析简单定义的略慢。然而,这点效能牺牲却可换来定义和注入服务的鲁棒性。

Mixing different types of definitions is allowed, everyone can decide what is the most appropriate way to register the services according to the application needs.

混合不同类型的定义是允许的,任何人都可以根据应用程序的需求自行决定注册服务的最佳方式。

Resolving Services 解析服务

Obtaining a service from the container is a matter of simply calling the “get” method. A new instance of the service will be returned:

获取一个服务只需简单的调用"get"方法。该方法将返回该服务的新实例:

<?php

$request = $di->get( "request" );

Or by calling through the magic method:

或者通过PHP的魔术方法:

<?php

$request = $di->getRequest();

Or using the array-access syntax:

或者通过数组访问语法:

<?php

$request = $di['request'];

Arguments can be passed to the constructor by adding an array parameter to the method “get”:

参数可通过添加一个数组传递给构造函数:

<?php

// new MyComponent("some-parameter", "other")
$component = $di->get( "MyComponent", array (
        "some-parameter",
        "other") );

Phalcon\Di is able to send events to an EventsManager if it is present. Events are triggered using the type “di”. Some events when returning boolean false could stop the active operation. The following events are supported:

Phalcon\DI 能够发送事件到事件管理器EventsManager,前提是相应的事件在DI中已设置。事件由di触发。响应某事件后返回的真假值可以停止当前的操作。支持如下事件:

Event Name 事件名
Triggered 触发
Can stop operation? 是否停止操作
Triggered on 触发目标
beforeServiceResolveTriggered before resolve service. Listeners receive the service name and the parameters passed to it.NoListeners
afterServiceResolveTriggered after resolve service. Listeners receive the service name, instance, and the parameters passed to it.NoListeners

Shared services 共享服务

Services can be registered as “shared” services this means that they always will act as singletons. Once the service is resolved for the first time the same instance of it is returned every time a consumer retrieve the service from the container:

服务可以用共享方式注册,意即:总是用单例模式生产。自服务的对象首次实例化后,此唯一的对象将总是由容器返回给请求该服务的消费者:

<?php

use Phalcon\Session\Adapter\Files as SessionFiles;

// Register the session service as "always shared"
$di->setShared( 'session', function () {
    $session = new SessionFiles();
    $session->start();
    return $session;
} );

$session = $di->get( 'session' );    // Locates the service for the first time
$session = $di->getSession();    // Returns the first instantiated object

An alternative way to register shared services is to pass “true” as third parameter of “set”:

另一种注册共享服务的方式是,设set方法的第三个参数为true:

<?php

// Register the session service as "always shared"
$di->set( 'session', function () {
    // ...
}, true );

If a service isn’t registered as shared and you want to be sure that a shared instance will be accessed every time the service is obtained from the DI, you can use the ‘getShared’ method:

若起初服务并未注册为共享的,而你需要确保从DI获得的服务实例为共享的,则可以调用getSahred方法:

<?php

$request = $di->getShared( "request" );

Manipulating services individually 自行操纵服务

Once a service is registered in the service container, you can retrieve it to manipulate it individually:

注册在容器内的服务,你可以在获取到后自行操纵:

<?php

use Phalcon\Http\Request;

// Register the "register" service
$di->set( 'request', 'Phalcon\Http\Request' ); 

// Get the service
$requestService = $di->getService( 'request' ); 

// Change its definition
$requestService->setDefinition( function () {
    return new Request();
} ); 

// Change it to shared
$requestService->setShared( true ); 

// Resolve the service (return a Phalcon\Http\Request instance)
$request = $requestService->resolve();

Instantiating classes via the Service Container 通过容器实例化类

When you request a service to the service container, if it can’t find out a service with the same name it’ll try to load a class with the same name. With this behavior we can replace any class by another simply by registering a service with its name:

在向容器请求服务时,若容器中找不到名称匹配的服务,则将试图载入一个同名的类。我们可以利用此特性将任何类注册为服务。

<?php

// Register a controller as a service
$di->set( 'IndexController', function () {
    $component = new Component();
    return $component;
}, true );

// Register a controller as a service
$di->set( 'MyOtherComponent', function () {
    // Actually returns another component
    $component = new AnotherComponent();
    return $component;
} ); 

// Create an instance via the service container
$myComponent = $di->get( 'MyOtherComponent' );

You can take advantage of this, always instantiating your classes via the service container (even if they aren’t registered as services). The DI will fallback to a valid autoloader to finally load the class. By doing this, you can easily replace any class in the future by implementing a definition for it.

利用这个特性,即便是没有注册的类,依然可以通过容器实例化。DI将回滚有效的自动装载器最终载入该类。藉此,你可以轻松的在未来通过实现定义而替换任何类。

Automatic Injecting of the DI itself DI自身的自动注入

If a class or component requires the DI itself to locate services, the DI can automatically inject itself to the instances it creates, to do this, you need to implement the Phalcon\DI\InjectionAwareInterface in your classes:

若一个组件需要DI自身来定位服务,DI将自动注入自己来创建实例,要这样做,你需要在自己的类中实现 Phalcon\DI\InjectionAwreInterface接口:

<?php

use Phalcon\DI\InjectionAwareInterface;

class MyClass implements InjectionAwareInterface {

    protected $_di;

    public function setDi( $di ) {

        $this->_di = $di;
    }

    public function getDi() {

        return $this->_di;
    }
}

Then once the service is resolved, the $di will be passed to setDi automatically:

如此当服务被解析完后,$di变量将调用setDi方法将自身注入服务:

<?php

// Register the service
$di->set( 'myClass', 'MyClass' );

// Resolve the service (NOTE: $myClass->setDi($di) is automatically called)
$myClass = $di->get( 'myClass' );

Avoiding service resolution 避免服务解析

Some services are used in each of the requests made to the application, eliminate the process of resolving the service could add some small improvement in performance.

有些服务在应用程序的每次请求中都被调用,消除解析这些服务的过程可略微提升应用程序的性能。

<?php

// Resolve the object externally instead of using a definition for it:
$router = new MyRouter();

// Pass the resolved object to the service registration
$di->set( 'router', $router );

Organizing services in files 在文件中组织服务

You can better organize your application by moving the service registration to individual files instead of doing everything in the application’s bootstrap:

宜将需要被注册的服务置于独立的文件中,而不是全放在应用程序的引导代码部分:

<?php

$di->set( 'router', function () {
    return include "../app/config/routes.php";
} );

Then in the file (”../app/config/routes.php”) return the object resolved:

于是从文件(”../app/config/routes.php”)中返回解析好的对象:

<?php

$router = new MyRouter();
$router->post( '/login' );
return $router;

Accessing the DI in a static way 静态访问DI

If needed you can access the latest DI created in a static function in the following way:

若需要,你可以像下面这样在静态函数中访问最新的DI:

<?php

use Phalcon\DI;

class SomeComponent {

    public static function someMethod() {
        // Get the session service
        $session = DI::getDefault()->getSession();
    }
}

Factory Default DI 默认工厂DI

Although the decoupled character of Phalcon offers us great freedom and flexibility, maybe we just simply want to use it as a full-stack framework. To achieve this, the framework provides a variant of Phalcon\DI called Phalcon\DI\FactoryDefault. This class automatically registers the appropriate services bundled with the framework to act as full-stack.

尽管Phalcon中的解耦角色给我们提供了高度的自由和灵活性,但也许我们只是简单的需要其作为一个完整的框架。要实现如此,框架提供一个默认DI的变体,称作Phalcon\DI\FactoryDefault。这个类自动注册一些常规的服务集合使得框架达到全栈运作。

<?php

use Phalcon\DI\FactoryDefault;

$di = new FactoryDefault();

Service Name Conventions 服务名称约定

Although you can register services with the names you want, Phalcon has a several naming conventions that allow it to get the the correct (built-in) service when you need it.

诚然,你可以自行决定注册服务的名称,Phalcon有若干命名约定允许你得到内置的服务。

Service NameDescriptionDefaultShared
dispatcherControllers Dispatching ServicePhalcon\Mvc\Dispatcher Yes
routerRouting ServicePhalcon\Mvc\Router Yes
urlURL Generator ServicePhalcon\Mvc\Url Yes
requestHTTP Request Environment ServicePhalcon\Http\Request Yes
responseHTTP Response Environment ServicePhalcon\Http\Response Yes
cookiesHTTP Cookies Management ServicePhalcon\Http\Response\Cookies Yes
filterInput Filtering ServicePhalcon\Filter Yes
flashFlash Messaging ServicePhalcon\Flash\Direct Yes
flashSessionFlash Session Messaging ServicePhalcon\Flash\Session Yes
sessionSession ServicePhalcon\Session\Adapter\Files Yes
eventsManagerEvents Management ServicePhalcon\Events\Manager Yes
dbLow-Level Database Connection ServicePhalcon\Db Yes
securitySecurity helpersPhalcon\Security Yes
cryptEncrypt/Decrypt dataPhalcon\Crypt Yes
tagHTML generation helpersPhalcon\Tag Yes
escaperContextual EscapingPhalcon\Escaper Yes
annotationsAnnotations ParserPhalcon\Annotations\Adapter\Memory Yes
modelsManagerModels Management ServicePhalcon\Mvc\Model\Manager Yes
modelsMetadataModels Meta-Data ServicePhalcon\Mvc\Model\MetaData\Memory Yes
transactionManagerModels Transaction Manager ServicePhalcon\Mvc\Model\Transaction\Manager Yes
modelsCacheCache backend for models cacheNone

viewsCacheCache backend for views fragmentsNone

Implementing your own DI 实现你自己的DI

The Phalcon\DiInterface interface must be implemented to create your own DI replacing the one provided by Phalcon or extend the current one.

要创建你自己的DI并让Phalcon支持之,或扩展现有的DI,必须实现Phalcon\DiInterface接口。


转载于:https://my.oschina.net/newlooper/blog/502701

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值