### 個人理解
類似在方法這個層次的中間件,允許在被代理的方法前、后做一些其他事情。主要有 before、after 和 around 方法。可以對比 laravel 或者其他框架中的 middleware 來理解,或者類比設計模式中的裝飾器模式,或者 python 中的 decorator,只不過是在 method 這個層次。
### 適用場景
plugin 不能被用在以下類型中
> - Final method
> - Final class
> - 非 public 方法
> - 類方法(比如靜態方法)
> - __construct()
> - Virtual Types
> - 在 Framework\Interception 啟動之前初始化的對象
Plugin 可以被用在下面幾個情況中
> - class
> - interface
> - 抽象類
> - 父類
通過在 Magento 源碼中搜索 <plugin?就能在 di.xml 文件中搜索到很多 plugin 的例子。
### before plugin
before plugin 會在被監聽的方法之前運行。before plugin 有以下幾條規則
> - before 關鍵詞會被添加到被監聽的方法前面,比如如果監聽的是 getSomeValue 方法,那么在 plugin 中對應的方法名稱就是 beforeGetSomeValue (下稱為 before plugin method)
> - before plugin method 中的第一個參數是被監聽的對象實例,通常縮寫為 $subject?或者直接使用對應的類名,在例子中是 $processor?
> - before plugin method 中的剩余所有參數都必須和被監聽的方法中的參數一致。
> - before plugin method 返回的參數必須是一個數組,返回值類型和個數必須和被監聽的方法一致。
在<MAGENTO_DIR>module-payment/etc/frontend/di.xml 我們能看到類似下面的寫法
```
<type name="Magento\Checkout\Block\Checkout\LayoutProcessor">
<plugin name="ProcessPaymentConfiguration"
type="Magento\Payment\Plugin\PaymentConfigurationProcess"/>
</type>
```
上面代碼中 plugin 的 beforeProcess?方法監聽的?Magento\Checkout\Block\Checkout\LayoutProcessorMagento\Checkout\Block\Checkout\LayoutProcessor 中的 Magento\Checkout\Block\Checkout\LayoutProcessor?process?方法。
```php
public function process($jsLayout)
{
//代碼塊
return $jsLayout;
}
```
before plugin 的實現是通過 Magento\Payment\Plugin\PaymentConfigurationProcess 類中的 beforeProcess?方法來完成的。
```php
public function beforeProcess(
\Magento\Checkout\Block\Checkout\LayoutProcessor $processor,
$jsLayout) {
// 代碼塊...
return [$jsLayout];
}
```
### around plugin
around plugin 功能允許我們在被監聽的方法前、后運行一部分我們自己的代碼。這個功能使我們能夠在改變輸入參數的同時改變返回結果值。
關于 around plugin, 要記住的幾個要點有
> - plugin 中的第一個參數是監聽的類的實例
> - plugin 中的第二個參數是一個 callable/Closure 類型。通常寫作 callable $proceed?,調用 $proceed 時的入參需要和被監聽方法參一致。
> - 其余的參數需要和被監聽方法一致。
> - plugin 的返回值必須和原函數保持一致。通常是直接返回 return $proceed(…) 或者先調用 $returnValue = $proceed(); 后直接返回 $returnValue; 有時候我們也需要修改 $returnValue;
下面來看一個 around plugin 的例子。
<MAGENTO_DIR>module-grouped-product/etc/di.xml 文件中
```
<type name="Magento\Catalog\Model\ResourceModel\Product\Link">
<plugin name="groupedProductLinkProcessor" type="Magento\GroupedProduct\Model\ResourceModel\Product\Link\RelationPersister" />
</type>
```
plugin 中的方法監聽的是 Magento\GroupedProduct\Model\ResourceModel\Product\Link\RelationPersister 類中的 aroundDeleteProductLink 方法
```php
public function aroundDeleteProductLink(
\Magento\GroupedProduct\Model\ResourceModel\Product\Link $subject,
\Closure $proceed, $linkId) {
// The rest of the code...
$result = $proceed($linkId);
// The rest of the code...
return $result;
}
```
### after plugin
after plugin 主要是在被監聽的方法之后執行一部分代碼。
在寫 after plugin 的時候,要記住以下幾點:
> - plugin 的第一個參數是被監聽類型的實例
> - plugin 的第二個參數是被監聽方法的執行結果,通常叫做 $result?,也可以在被監聽方法返回值之后被調用。例如下面例子中的 $data?
> - 剩下的其他參數和被監聽方法一致
> - plugin 必須返回和?$result|$data?同類型的返回值
在 module-catalog/etc/di.xml 中的 after plugin 的例子如下:
```
<type name="Magento\Indexer\Model\Config\Data">
<plugin name="indexerProductFlatConfigGet"
type="Magento\Catalog\Model\Indexer\Product\Flat\Plugin\IndexerConfigData" />
</type>
```
plugin 中監聽的方法是 Magento\Indexer\Model\Config\Data 類中的 get?方法
```php
public function get($path = null, $default = null) {
// The rest of the code...
return $data;
}
```
Magento\Catalog\Model\Indexer\Product\Flat\Plugin\IndexerConfigData 類中的 afterGet?就是這里的 after plugin 的實現,具體如下:
```php
public function afterGet(Magento\Indexer\Model\Config\Data, $data, $path = null, $default = null) {
// The rest of the code...
return $data;
}
```
使用 plugin 時需要特別注意, 它十分靈活,但是也很容易產生 Bug 和造成性能瓶頸,尤其是在多個 plugin 監聽同一個方法的時候。
- Magento 基本概念
- Magento 中的 Plugin
- Magento 中的 Events 和 observers
- Magento 中的 Areas
- Magento 中的請求處理流程
- Magento 中的模塊(Modules)
- Magento 中的 Cache
- Magento 中的依賴注入
- Magento 中的 Console commands
- Magento 中的 Cron jobs
- 掌握 Entities
- 理解 model 的類型
- 理解 setup scripts
- 實體擴展
- 初入 web API
- users 的幾種不同類型
- 授權的不同類型
- APIs 的不同類型
- 使用 Magento 現有的 API
- 創建自定義 web APIs
- 理解 search criteria
- Magento 后臺開發
- 使用 listing 組件
- 使用 form 組件
- 開發前臺功能
- 搭建前臺開發環境
- 初始化 & 調用 JS 組件
- 開始使用 RequireJS
- 替換 jQuery widget 組件
- 擴展 jQuery widget 組件
- 創建 jQuery widgets 組件
- 創建 UI/KnockoutJS 組件
- 擴展 UI/KnockoutJS 組件
- 自定義 Catalog
- 創建參考尺寸
- 創建當天發貨
- 標識新產品
- Magento 性能最佳實踐
- Magento 硬件推薦配置
- 軟件推薦
- 架構參考
- 配置項最佳實踐
- 高級設置
- 其他
- 如何通過 Burp Suite 和 Xdebug 來調試 Magento
- Mageno checkout 必知必會
- Magento 安裝(新手必看)
- 安裝流程
- 系統要求