[TOC]
# 依賴注入與服務定位
## DI 說明
以下示例有點長,但它試圖解釋為什么Phalcon使用服務定位器和依賴注入。首先,我們假設我們正在開發一個名為`SomeComponent`的組件。這執行一些任務。我們的組件具有依賴關系,即與數據庫的連接。
在第一個示例中,在組件內部創建連接。雖然這是一個非常有效的實現,但它是公正的,因為我們無法更改連接參數或數據庫系統的類型,因為組件只能按創建的方式工作。
```php
<?php
class SomeComponent
{
/**
* 連接的實例化在組件內部是硬編碼的,
* 因此很難在外部替換它或改變其行為
*/
public function someDbTask()
{
$connection = new Connection(
[
'host' => 'localhost',
'username' => 'root',
'password' => 'secret',
'dbname' => 'invo',
]
);
// ...
}
}
$some = new SomeComponent();
$some->someDbTask();
```
為了解決這個缺點,我們創建了一個在使用它之前在外部注入依賴項的setter。這也是一個有效的實現,但有其缺點:
```php
<?php
class SomeComponent
{
private $connection;
/**
* 在外部設置連接
*
* @param Connection $connection
*/
public function setConnection(Connection $connection)
{
$this->connection = $connection;
}
public function someDbTask()
{
$connection = $this->connection;
// ...
}
}
$some = new SomeComponent();
// 創建連接
$connection = new Connection(
[
'host' => 'localhost',
'username' => 'root',
'password' => 'secret',
'dbname' => 'invo',
]
);
// 注入組件中的連接
$some->setConnection($connection);
$some->someDbTask();
```
現在考慮我們在應用程序的不同部分使用此組件,然后在將其傳遞給組件之前需要多次創建連接。使用全局注冊表模式,我們可以在那里存儲連接對象,并在需要時重用它。
```php
<?php
class Registry
{
/**
* 返回連接
*/
public static function getConnection()
{
return new Connection(
[
'host' => 'localhost',
'username' => 'root',
'password' => 'secret',
'dbname' => 'invo',
]
);
}
}
class SomeComponent
{
protected $connection;
/**
* 在外部設置連接
*
* @param Connection $connection
*/
public function setConnection(Connection $connection)
{
$this->connection = $connection;
}
public function someDbTask()
{
$connection = $this->connection;
// ...
}
}
$some = new SomeComponent();
// 傳遞注冊表中定義的連接
$some->setConnection(Registry::getConnection());
$some->someDbTask();
```
現在,讓我們假設我們必須在組件中實現兩個方法,第一個總是需要創建一個新連接,第二個總是需要使用共享連接:
```php
<?php
class Registry
{
protected static $connection;
/**
* 創建連接
*
* @return Connection
*/
protected static function createConnection(): Connection
{
return new Connection(
[
'host' => 'localhost',
'username' => 'root',
'password' => 'secret',
'dbname' => 'invo',
]
);
}
/**
* 僅創建一次連接并返回它
*
* @return Connection
*/
public static function getSharedConnection(): Connection
{
if (self::$connection === null) {
self::$connection = self::createConnection();
}
return self::$connection;
}
/**
* 始終返回新連接
*
* @return Connection
*/
public static function getNewConnection(): Connection
{
return self::createConnection();
}
}
class SomeComponent
{
protected $connection;
/**
* 在外部設置連接
*
* @param Connection $connection
*/
public function setConnection(Connection $connection)
{
$this->connection = $connection;
}
/**
* 此方法始終需要共享連接
*/
public function someDbTask()
{
$connection = $this->connection;
// ...
}
/**
* 此方法始終需要新連接
*
* @param Connection $connection
*/
public function someOtherDbTask(Connection $connection)
{
}
}
$some = new SomeComponent();
// 這會注入共享連接
$some->setConnection(
Registry::getSharedConnection()
);
$some->someDbTask();
// 在這里,我們總是傳遞一個新的連接作為參數
$some->someOtherDbTask(
Registry::getNewConnection()
);
```
到目前為止,我們已經看到依賴注入如何解決我們的問將依賴項作為參數傳遞而不是在代碼內部創建它們使我們的應用程序更易于維護和解耦。然而,從長遠來看,這種形式的依賴注入有一些缺點。
例如,如果組件有很多依賴項,我們需要創建多個setter參數來傳遞依賴項或創建一個構造函數,用多個參數傳遞它們,另外在使用組件之前創建依賴項,使我們的代碼不像我們希望的那樣可維護:
```php
<?php
// 創建依賴項或從注冊表中檢索它們
$connection = new Connection();
$session = new Session();
$fileSystem = new FileSystem();
$filter = new Filter();
$selector = new Selector();
// 將它們作為構造函數參數傳遞
$some = new SomeComponent($connection, $session, $fileSystem, $filter, $selector);
// ... 或者使用setter
$some->setConnection($connection);
$some->setSession($session);
$some->setFileSystem($fileSystem);
$some->setFilter($filter);
$some->setSelector($selector);
```
想想我們是否必須在應用程序的許多部分中創建此對象。將來,如果我們不需要任何依賴項,我們需要遍歷整個代碼庫,以在我們注入代碼的任何構造函數或setter中刪除參數。為了解決這個問題,我們再次返回全局注冊表來創建組件。但是,它在創建對象之前添加了一個新的抽象層:
```php
<?php
class SomeComponent
{
// ...
/**
* 定義工廠方法以創建注入其依賴項的SomeComponent實例
*/
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);
}
}
```
現在我們發現自己回到了我們開始的地方,我們再次構建組件內部的依賴關系!我們必須找到一種解決方案,使我們不再反復陷入不良行為。
解決這些問題的一種實用而優雅的方法是使用容器進行依賴。容器充當我們之前看到的全局注冊表。使用依賴容器作為橋接來獲取依賴關系允許我們降低組件的復雜性:
```php
<?php
use Phalcon\Di;
use Phalcon\DiInterface;
class SomeComponent
{
protected $di;
public function __construct(DiInterface $di)
{
$this->di = $di;
}
public function someDbTask()
{
// 獲取連接服務
// 始終返回新連接
$connection = $this->di->get('db');
}
public function someOtherDbTask()
{
// 獲取共享連接服務,
// 這將每次返回相同的連接
$connection = $this->di->getShared('db');
// 此方法還需要輸入過濾服務
$filter = $this->di->get('filter');
}
}
$di = new Di();
// 在容器中注冊“db”服務
$di->set(
'db',
function () {
return new Connection(
[
'host' => 'localhost',
'username' => 'root',
'password' => 'secret',
'dbname' => 'invo',
]
);
}
);
// 在容器中注冊“過濾器”服務
$di->set(
'filter',
function () {
return new Filter();
}
);
// 在容器中注冊“session”服務
$di->set(
'session',
function () {
return new Session();
}
);
// 將服務容器作為唯一參數傳遞
$some = new SomeComponent($di);
$some->someDbTask();
```
現在,組件可以在需要時簡單地訪問所需的服務,如果它不需要服務,甚至不會初始化,從而節省資源。該組件現在高度分離。例如,我們可以替換創建連接的方式,它們的行為或它們的任何其他方面以及不會影響組件的方式。
`Phalcon\Di`是實現依賴注入和服務定位的組件,它本身就是它們的容器。
由于Phalcon高度解耦,`Phalcon\Di`對于整合框架的不同組件至關重要。開發人員還可以使用此組件注入依賴項并管理應用程序中使用的不同類的全局實例。
基本上,該組件實現了控制反轉模式。應用此方法,對象不會使用setter或構造函數接收它們的依賴項,而是請求服務依賴項注入器。這降低了整體復雜性,因為只有一種方法可以在組件中獲得所需的依賴關系。
此外,這種模式增加了代碼的可測試性,從而使其不易出錯。
## 在容器中注冊服務
框架本身或開發人員可以注冊服務。當組件A需要組件B(或其類的實例)進行操作時,它可以從容器請求組件B,而不是創建新的實例組件B.
這種工作方式給我們帶來了許多好處:
* 我們可以輕松地用自己或第三方創建的組件替換組件。
* 我們完全控制對象初始化,允許我們在將它們傳遞給組件之前根據需要設置這些對象。
* 我們可以以結構化和統一的方式獲取組件的全局實例。
可以使用幾種類型的定義注冊服務:
### 簡單注冊
如前所述,有幾種方法可以注冊服務。我們稱之為簡單注冊:
#### 字符串
此類型需要有效類的名稱,返回指定類的對象,如果未加載該類,則將使用自動加載器對其進行實例化。這種類型的定義不允許為類構造函數或參數指定參數:
```php
<?php
// Return new Phalcon\Http\Request();
$di->set(
'request',
'Phalcon\Http\Request'
);
```
#### 類實例
此類型需要一個對象。由于對象不需要被解析,因為它已經是一個對象,可以說它實際上不是一個依賴注入,但是,如果要強制返回的依賴項始終是相同的對象/值,則它很有用:
```php
<?php
use Phalcon\Http\Request;
// Return new Phalcon\Http\Request();
$di->set(
'request',
new Request()
);
```
#### 閉包/匿名函數
此方法提供了更大的自由度來構建依賴項,但是,很難在不必完全更改依賴項定義的情況下從外部更改某些參數:
```php
<?php
use Phalcon\Db\Adapter\Pdo\Mysql as PdoMysql;
$di->set(
'db',
function () {
return new PdoMysql(
[
'host' => 'localhost',
'username' => 'root',
'password' => 'secret',
'dbname' => 'blog',
]
);
}
);
```
通過將其他變量傳遞給閉包的環境可以克服一些限制:
```php
<?php
use Phalcon\Config;
use Phalcon\Db\Adapter\Pdo\Mysql as PdoMysql;
$config = new Config(
[
'host' => '127.0.0.1',
'username' => 'user',
'password' => 'pass',
'dbname' => 'my_database',
]
);
// 在當前作用域中使用 $config 變量
$di->set(
'db',
function () use ($config) {
return new PdoMysql(
[
'host' => $config->host,
'username' => $config->username,
'password' => $config->password,
'dbname' => $config->name,
]
);
}
);
```
您還可以使用`get()`方法訪問其他DI服務:
```php
<?php
use Phalcon\Config;
use Phalcon\Db\Adapter\Pdo\Mysql as PdoMysql;
$di->set(
'config',
function () {
return new Config(
[
'host' => '127.0.0.1',
'username' => 'user',
'password' => 'pass',
'dbname' => 'my_database',
]
);
}
);
// 使用DI中的'config'服務
$di->set(
'db',
function () {
$config = $this->get('config');
return new PdoMysql(
[
'host' => $config->host,
'username' => $config->username,
'password' => $config->password,
'dbname' => $config->name,
]
);
}
);
```
### 復雜注冊
如果需要在不實例化/解析服務的情況下更改服務定義,那么我們需要使用數組語法定義服務。使用數組定義定義服務可能會更冗長:
```php
<?php
use Phalcon\Logger\Adapter\File as LoggerFile;
// 使用類名及其參數注冊服務“logger”
$di->set(
'logger',
[
'className' => 'Phalcon\Logger\Adapter\File',
'arguments' => [
[
'type' => 'parameter',
'value' => '../apps/logs/error.log',
]
]
]
);
// 使用匿名函數
$di->set(
'logger',
function () {
return new LoggerFile('../apps/logs/error.log');
}
);
```
上述兩個服務注冊都會產生相同的結果。但是,數組定義允許在需要時更改服務參數:
```php
<?php
// 更改服務類名稱
$di
->getService('logger')
->setClassName('MyCustomLogger');
// 更改第一個參數而不實例化記錄器
$di
->getService('logger')
->setParameter(
0,
[
'type' => 'parameter',
'value' => '../apps/logs/error.log',
]
);
```
此外,通過使用數組語法,您可以使用三種類型的依賴注入:
#### 構造函數注入
此注入類型將依賴項/參數傳遞給類構造函數。讓我們假裝我們有以下組件:
```php
<?php
namespace SomeApp;
use Phalcon\Http\Response;
class SomeComponent
{
/**
* @var Response
*/
protected $response;
protected $someFlag;
public function __construct(Response $response, $someFlag)
{
$this->response = $response;
$this->someFlag = $someFlag;
}
}
```
該服務可以這種方式注冊:
```php
<?php
$di->set(
'response',
[
'className' => 'Phalcon\Http\Response'
]
);
$di->set(
'someComponent',
[
'className' => 'SomeApp\SomeComponent',
'arguments' => [
[
'type' => 'service',
'name' => 'response',
],
[
'type' => 'parameter',
'value' => true,
],
]
]
);
```
服務'response'(`Phalcon\Http\Response`)被解析為作為構造函數的第一個參數傳遞,而第二個是一個布爾值(true),它按原樣傳遞。
#### Setter 注入
類可能有setter來注入可選的依賴項,我們以前的類可以更改為接受setter的依賴項:
```php
<?php
namespace SomeApp;
use Phalcon\Http\Response;
class SomeComponent
{
/**
* @var Response
*/
protected $response;
protected $someFlag;
public function setResponse(Response $response)
{
$this->response = $response;
}
public function setFlag($someFlag)
{
$this->someFlag = $someFlag;
}
}
```
具有setter注入的服務可以注冊如下:
```php
<?php
$di->set(
'response',
[
'className' => 'Phalcon\Http\Response',
]
);
$di->set(
'someComponent',
[
'className' => 'SomeApp\SomeComponent',
'calls' => [
[
'method' => 'setResponse',
'arguments' => [
[
'type' => 'service',
'name' => 'response',
]
]
],
[
'method' => 'setFlag',
'arguments' => [
[
'type' => 'parameter',
'value' => true,
]
]
]
]
]
);
```
#### 屬性注入
一種不太常見的策略是將依賴項或參數直接注入到類的公共屬性中:
```php
<?php
namespace SomeApp;
use Phalcon\Http\Response;
class SomeComponent
{
/**
* @var Response
*/
public $response;
public $someFlag;
}
```
具有屬性注入的服務可以注冊如下:
```php
<?php
$di->set(
'response',
[
'className' => 'Phalcon\Http\Response',
]
);
$di->set(
'someComponent',
[
'className' => 'SomeApp\SomeComponent',
'properties' => [
[
'name' => 'response',
'value' => [
'type' => 'service',
'name' => 'response',
],
],
[
'name' => 'someFlag',
'value' => [
'type' => 'parameter',
'value' => true,
],
]
]
]
);
```
支持的參數類型包括以下內容:
| Type | 描述 | 例子 |
| :-------: | :------------------------: | :----------------------------------------------------------: |
| parameter | 表示要作為參數傳遞的文本值 | `php['type' => 'parameter', 'value' => 1234]` |
| service | 表示服務容器中的另一個服務 | `php['type' => 'service', 'name' => 'request']` |
| instance | 表示必須動態構建的對象 | `php['type' => 'instance', 'className' => 'DateTime', 'arguments' => ['now']]` |
解析定義復雜的服務可能比先前看到的簡單定義稍慢。但是,這些提供了一種更健壯的方法來定義和注入服務。允許混合使用不同類型的定義,每個人都可以根據應用程序需求決定注冊服務的最合適方式。
### Array Syntax
數組也允許注冊服務:
```php
<?php
use Phalcon\Di;
use Phalcon\Http\Request;
// 創建依賴注入器容器
$di = new Di();
// By its class name
$di['request'] = 'Phalcon\Http\Request';
// 使用匿名函數,實例將被延遲加載
$di['request'] = function () {
return new Request();
};
// 直接注冊實例
$di['request'] = new Request();
// 使用數組定義
$di['request'] = [
'className' => 'Phalcon\Http\Request',
];
```
在上面的示例中,當框架需要訪問請求數據時,它將要求在容器中標識為“請求”的服務。容器反過來將返回所需服務的實例。開發人員可能最終在他/她需要時替換組件。
用于設置/注冊服務的每種方法(在以上示例中說明)具有優點和缺點。由開發人員和將指定使用哪一個的特定要求決定。
通過字符串設置服務很簡單,但缺乏靈活性。使用數組設置服務提供了更大的靈活性,但使代碼更復雜。lambda函數在兩者之間是一個很好的平衡,但可能導致比預期更多的維護。
`Phalcon\Di` 為其存儲的每項服務提供延遲加載。除非開發人員選擇直接實例化對象并將其存儲在容器中,否則存儲在其中的任何對象(通過數組,字符串等)將被延遲加載,即僅在請求時進行實例化。
### 從YAML文件加載服務
此功能將允許您在`yaml`文件中或僅在純PHP中設置服務。例如,您可以使用`yaml`文件加載服務,如下所示:
```yaml
config:
className: \Phalcon\Config
shared: true
```
```php
<?php
use Phalcon\Di;
$di = new Di();
$di->loadFromYaml('services.yml');
$di->get('config'); // will properly return config service
```
>[danger] 此方法要求安裝模塊Yaml。 有關詳細信息,請參閱[this](http://php.net/manual/book.yaml.php)。
## 解析服務
從容器中獲取服務只需調用“get”方法即可。將返回該服務的新實例:
```php
$request = $di->get('request');
```
或者通過魔術方法調用:
```php
$request = $di->getRequest();
```
或者使用數組訪問:
```php
$request = $di['request'];
```
通過向方法'get'添加數組參數,可以將參數傳遞給構造函數:
```php
<?php
// new MyComponent('some-parameter', 'other')
$component = $di->get(
'MyComponent',
[
'some-parameter',
'other',
]
);
```
### Events
`Phalcon\Di` 能夠將事件發送到:`EventsManager <events>`(如果存在)。使用“di”類型觸發事件。返回布爾值false時的某些事件可能會停止活動操作。支持以下事件:
| Event Name | Triggered | Can stop operation? | Triggered on |
| -------------------- | -------------------------------------------------------- | :-----------------: | :----------: |
| beforeServiceResolve | 在解析服務之前觸發。監聽器接收服務名稱和傳遞給它的參數。 | No | Listeners |
| afterServiceResolve | 在解析服務之后觸發。監聽器接收服務名稱和傳遞給它的參數。 | No | Listeners |
## 共享服務
服務可以注冊為“共享”服務,這意味著它們將始終是單例。一旦第一次解析服務,每次使用者從容器中檢索服務時,都會返回相同的實例:
```php
<?php
use Phalcon\Session\Adapter\Files as SessionFiles;
// 將session服務注冊為“始終共享”
$di->setShared(
'session',
function () {
$session = new SessionFiles();
$session->start();
return $session;
}
);
// 首次查找服務
$session = $di->get('session');
// 返回第一個實例化對象
$session = $di->getSession();
```
注冊共享服務的另一種方法是將'true'作為'set'的第三個參數傳遞:
```php
<?php
// 將session服務注冊為“始終共享”
$di->set(
'session',
function () {
// ...
},
true
);
```
如果服務未注冊為共享,并且您希望確保每次從DI獲取服務時都將訪問共享實例,則可以使用“getShared”方法:
```php
$request = $di->getShared('request');
```
## 單獨操作服務
在服務容器中注冊服務后,您可以檢索它以單獨操作它:
```php
<?php
use Phalcon\Http\Request;
// 注冊 'request' 服務
$di->set('request', 'Phalcon\Http\Request');
// 獲取服務
$requestService = $di->getService('request');
// 改變它的定義
$requestService->setDefinition(
function () {
return new Request();
}
);
// 將其更改為共享
$requestService->setShared(true);
// 解析服務 (return 一個 Phalcon\Http\Request 實例)
$request = $requestService->resolve();
```
## 通過服務容器實例化類
當您向服務容器請求服務時,如果它找不到具有相同名稱的服務,它將嘗試加載具有相同名稱的類。通過這種行為,我們可以通過使用其名稱注冊服務來替換另一個類:
```php
<?php
// 注冊一個控制器作為服務
$di->set(
'IndexController',
function () {
$component = new Component();
return $component;
},
true
);
// 注冊一個組件作為服務
$di->set(
'MyOtherComponent',
function () {
// 實際返回另一個組件
$component = new AnotherComponent();
return $component;
}
);
// 通過服務容器創建實例
$myComponent = $di->get('MyOtherComponent');
```
您可以利用這一點,始終通過服務容器實例化您的類(即使它們未注冊為服務)。DI將回退到有效的自動加載器以最終加載該類。通過這樣做,您可以通過實現它的定義輕松替換任何類。
## 自動注入DI
如果類或組件需要DI本身來定位服務,DI可以自動將其自身注入到它創建的實例中,為此,您需要在類中實現`Phalcon\Di\InjectionAwareInterface`:
```php
<?php
use Phalcon\DiInterface;
use Phalcon\Di\InjectionAwareInterface;
class MyClass implements InjectionAwareInterface
{
/**
* @var DiInterface
*/
protected $di;
public function setDi(DiInterface $di)
{
$this->di = $di;
}
public function getDi()
{
return $this->di;
}
}
```
然后一旦服務被解析,`$di`將自動傳遞給`setDi()`:
```php
<?php
// 注冊服務
$di->set('myClass', 'MyClass');
// 解析服務 (NOTE: $myClass->setDi($di) 會自動調用)
$myClass = $di->get('myClass');
```
## 組織文件中的服務
您可以通過將服務注冊移動到單個文件而不是在應用程序的引導程序中執行所有操作來更好地組織應用程序:
```php
<?php
$di->set(
'router',
function () {
return include '../app/config/routes.php';
}
);
```
然后在文件(`'../app/config/routes.php'`)中返回已解析的對象:
```php
<?php
$router = new MyRouter();
$router->post('/login');
return $router;
```
## 以靜態方式訪問DI
如果需要,您可以通過以下方式訪問在靜態功能中創建的最新DI:
```php
<?php
use Phalcon\Di;
class SomeComponent
{
public static function someMethod()
{
// 獲得 session 服務
$session = Di::getDefault()->getSession();
}
}
```
## 服務提供者
使用`ServiceProviderInterface`,您現在可以按上下文注冊服務。您可以將所有`$di->set()`調用移動到這樣的類:
```php
<?php
use Phalcon\Di\ServiceProviderInterface;
use Phalcon\DiInterface;
use Phalcon\Di;
use Phalcon\Config\Adapter\Ini;
class SomeServiceProvider implements ServiceProviderInterface
{
public function register(DiInterface $di)
{
$di->set(
'config',
function () {
return new Ini('config.ini');
}
);
}
}
$di = new Di();
$di->register(new SomeServiceProvider());
var_dump($di->get('config')); // 將正確返回我們的配置
```
## Factory Default DI
盡管Phalcon的分離特性為我們提供了極大的自由和靈活性,但也許我們只是想將它用作全棧框架。為實現這一目標,該框架提供了一個名為`Phalcon\Di \FactoryDefault`的`Phalcon\Di`變體。此類自動注冊與框架捆綁在一起的相應服務,以充當全棧。
```php
<?php
use Phalcon\Di\FactoryDefault;
$di = new FactoryDefault();
```
## 服務名稱約定
雖然您可以使用您想要的名稱注冊服務,但Phalcon有幾個命名約定,允許它在您需要時獲得正確的(內置)服務。
| 服務名稱 | 描述 | 默認 | Shared |
| ------------------ | ---------------------- | --------------------------------------- | :----: |
| assets | 前端資源管理 | `Phalcon\Assets\Manager` | Yes |
| annotations | 注釋解析器 | `Phalcon\Annotations\Adapter\Memory` | Yes |
| cookies | HTTP Cookies 管理服務 | `Phalcon\Http\Response\Cookies` | Yes |
| crypt | 加密/解密數據 | `Phalcon\Crypt` | Yes |
| db | 底層數據庫連接服務 | `Phalcon\Db` | Yes |
| dispatcher | 控制器調度服務 | `Phalcon\Mvc\Dispatcher` | Yes |
| eventsManager | 事件管理服務 | `Phalcon\Events\Manager` | Yes |
| escaper | 上下文轉義 | `Phalcon\Escaper` | Yes |
| flash | Flash消息服務 | `Phalcon\Flash\Direct` | Yes |
| flashSession | Flash Session 消息服務 | `Phalcon\Flash\Session` | Yes |
| filter | 輸入過濾服務 | `Phalcon\Filter` | Yes |
| modelsCache | 模型緩存 | None | No |
| modelsManager | 模型管理服務 | `Phalcon\Mvc\Model\Manager` | Yes |
| modelsMetadata | 模型元數據服務 | `Phalcon\Mvc\Model\MetaData\Memory` | Yes |
| request | HTTP 請求環境服務 | `Phalcon\Http\Request` | Yes |
| response | HTTP 響應環境服務 | `Phalcon\Http\Response` | Yes |
| router | 路由服務 | `Phalcon\Mvc\Router` | Yes |
| security | 安全助手 | `Phalcon\Security` | Yes |
| session | Session 服務 | `Phalcon\Session\Adapter\Files` | Yes |
| sessionBag | Session Bag 服務 | `Phalcon\Session\Bag` | Yes |
| tag | HTML 生成助手 | `Phalcon\Tag` | Yes |
| transactionManager | 模型事務管理器服務 | `Phalcon\Mvc\Model\Transaction\Manager` | Yes |
| url | URL生成器服務 | `Phalcon\Mvc\Url` | Yes |
| viewsCache | 緩存視圖片段 | None | No |
## 實現自己的DI
必須實現`Phalcon\DiInterface`接口以創建自己的DI,替換Phalcon提供的DI或擴展當前的DI。
- 常規
- Welcome
- 貢獻
- 生成回溯
- 測試重現
- 單元測試
- 入門
- 安裝
- Web服務器設置
- WAMP
- XAMPP
- 教程
- 基礎教程
- 教程:創建一個簡單的REST API
- 教程:V?kuró
- 提升性能
- 教程:INVO
- 開發環境
- Phalcon Compose (Docker)
- Nanobox
- Phalcon Box (Vagrant)
- 開發工具
- Phalcon開發者工具的安裝
- Phalcon開發者工具的使用
- 調試應用程序
- 核心
- MVC應用
- 微應用
- 創建命令行(CLI)應用程序
- 依賴注入與服務定位
- MVC架構
- 服務
- 使用緩存提高性能
- 讀取配置
- 上下文轉義
- 類加載器
- 使用命名空間
- 日志
- 隊列
- 數據庫
- 數據庫抽象層
- Phalcon查詢語言(PHQL)
- ODM(對象文檔映射器)
- 使用模型
- 模型行為
- ORM緩存
- 模型事件
- 模型元數據
- 模型關系
- 模型事務
- 驗證模型
- 數據庫遷移
- 分頁
- 前端
- Assets管理
- 閃存消息
- 表單
- 圖像
- 視圖助手(標簽)
- 使用視圖
- Volt:模板引擎
- 業務邏輯
- 訪問控制列表(ACL)
- 注解解析器
- 控制器
- 調度控制器
- 事件管理器
- 過濾與清理
- 路由
- 在session中存儲數據
- 生成URL和路徑
- 驗證
- HTTP
- Cookies管理
- 請求環境
- 返回響應
- 安全
- 加密/解密
- 安全
- 國際化
- 國際化
- 多語言支持