相对于Magento1版本,2版本增加很多的特性,plugin就是其中一项,允许我们对原来的方法进行重写、覆盖,还是比较实用,在此记录一下plugin的使用经验。

来自官方的说明:plugin插件或拦截器是一个类,它通过拦截函数调用并在该函数调用之前、之后或前后运行代码来修改公共类函数的行为。这允许您替换扩展任何接口的原始公共方法的行为。

一、基础情况

1、需要注意的是以下情况是无法使用plugin进行重写的:Final methods(最终方法)、Final classes(最终类)、Non-public methods(非公共方法)、Class methods (such as static methods)(非公共类)、__construct(构造函数) 、 __destruct(析构函数)、Virtual types(虚拟类型)、Objects that are instantiated before Magento\Framework\Interception is bootstrapped(此类之前的实例化对象)。

2、有两个属性可以在xml配置里面声明,分别是sortorder(调用顺序,多次重写时可用此参数指定运行顺序)、disabled(是否禁用:可以不用改代码直接在xml指定停用)

3、有before、after、around三种方法,分别对应着方法之前、之后、修改当前方法,在此方法加上要修改的方法(原方法第一字符改为大写),再在di.xml声明一下就可以了

<type name="sna\test\Model\Point">
        <plugin name="snatestpoing" type="Sna\test\Plugin\Point"/>
</type>
di.xml示例
<?php
namespace Sna\Test\Plugin;
class Point
{
	protected $_session;
    
    public function __construct(
    \Magento\Customer\Model\Session $session
    ){
          $this->_session      = $session;
    }

    public function beforeGetPoint(
      \sna\test\Model\Point $subject,
      $user
    ){
    	........
    }

    public function aroundGetPoint(
      \sna\test\Model\Point $subject,
      \Closure $proceed,
      $user
    ){
    	........
    }

    public function afterGetPoint(
      \sna\test\Model\Point $subject,
      $user
    ){
    	........
    }
}
plugin改写示例

二、plugin应该怎么开发?

第一种before

这是在原方法执行之前调用,一般可以用来改变参数,达到让原方法结果有不同的返回值,或者基于参数做其它逻辑的处理。

before方法:第一个参数传入原方法对应的类,第二个参数对应原方法参数名,最后return的时候,要用[]包含原参数名,如果原方法没参数,return []就行。
<?php
#原方法
namespace Sna\Test\Model;
class Point
{
    public function getPoint($user
    ){
    	if($user->getPoint()){
           $this->point = $user->getPoint();
        }
    	return $this->point;
    }
}


namespace Sna\Test\Plugin\Point;
class Point
{
    public function beforeGetPoint(
    \Sna\Test\Model\Point $subject,
    $user
    ){
    	if($user->getPoint()){
           $this->point = 1;
        }
    	return [$user];
    }
}

before示例

第二种after

这是在原方法执行之后调用,一般可以用来修改/改变结果(如某一组用户返回多一些信息),或者基于结果做其它逻辑的处理。

after方法:第一个参数传入原方法对应的类,第二个参数对应原方法参数名,最后直接return 原方法的参数,不同于before方法,是不需要用[]包含起来
<?php
#原方法
namespace Sna\Test\Model;
class Point
{
    public function getPoint($user
    ){
    	if($user->getPoint()){
           $this->point = $user->getPoint();
        }
    	return $this->point;
    }
}


namespace Sna\Test\Plugin\Point;
class Point
{
    public function afterGetPoint(
    \Sna\Test\Model\Point $subject,
    $user
    ){
    	if($user->getPoint()==1){
           return fasle;//中断返回
        }
    	return $user;//保持原来的结果
    }
}

after示例

第三种around

这是可以直接修改原方法逻辑,当before及after都无法实现你的要求,必需调整原方法的逻辑时(如原方法直接查询用户信息再做处理,你的需求是基于用户信息再次调整逻辑)

around方法:第一个参数传入原方法对应的类,第二个参数需要为\Closure $proceed(对应为原方法的执行结果),第三个参数才是原方法的参数,最后直接return 结果,或return $proceed()走原方法的逻辑
<?php
#原方法
namespace Sna\Test\Model;
class Point
{
    public function getPoint($user
    ){
    	if($user->getPoint()){
           $this->point = $user->getPoint();
        }
    	return $this->point;
    }
}


namespace Sna\Test\Plugin\Point;
class Point
{
    public function afterGetPoint(
    \Sna\Test\Model\Point $subject,
    \Closure $proceed,
    $user
    ){
    	if($user->getPoint()==1 && $subject->getCustomerId()>0){
           return fasle;//中断返回
        }
    	return $proceed($user);//让程序走原来的逻辑
    }
}

before、after、around这几种方法,需要注意的是:前一个参数都需要对应原方法的类,第二个参数之后保持跟原方法的参数数量一致就好,如原参数有3个参数,那么在修改后应该有4个参数,around也是如上,只不过还要多一个\Closure $proceed占用第二个参数位,如果是原方法并没有参数也是可以不传参。