企业🤖AI智能体构建引擎,智能编排和调试,一键部署,支持知识库和私有化部署方案 广告
# (\*)观察者模式 ## **观察者模式** > 当一个对象状态发生改变的时候,依赖他的对象全部会收到通知,并且自动更新等等一连串的操作, 将这些一连串的操作(的逻辑代码)放到观察者列表里 > 场景:一个事件发生后,要执行一连串的更新操作,传统的编程方法,就是在事件的代码之后直接加入逻辑处理,当更新的逻辑增多之后,代码会变得难以维护。这种方式是耦合的,侵入式的。增加新的逻辑需要修改事件主体的代码 > 察者模式实现了低耦合,非侵入式的通知与更新机制 > 状态触发类的应用当你的程序要处理的对象有很多种状态,就可以考虑使用这个模式 > 如果想在框架任何一个地方里插入其他逻辑二无需大的改动,请用观察者模式,只需在需要的地方添加一段触发观察的代码就可以 ## **业务场景:** **窗体程序设计中的事件处理**-- 窗体中的所有组件都是“事件源”,也就是目标对象,而事件处理程序类的对象是具体观察者对象 **注册**-- 一般和活动挂钩如注册送金币和积分也可以送给推荐人 **下订单**--下订单就多了 活动是最起码的 其次 比如发短信发邮件存入日志 **插件** -- 比如许多框架的第三方插件就是这个原理 **订阅功能** --简单的是一些第三方的支付接入或者其他功能接入,他们都暴露一个订阅功能,你可以选择订阅,然后你就作为观察者,每次第三方有更新和通知的时候,所有订阅的人,都能收到更新通知了 ## **模式的应用场景** 1. 对象间存在一对多关系,一个对象的状态发生改变会影响其他对象。 2. 当一个抽象模型有两个方面,其中一个方面依赖于另一方面时,可将这二者封装在独立的对象中以使它们可以各自独立地改变和复用。 ## **模式的结构** 1. **抽象主题(Subject)角色**:也叫抽象目标类,它提供了一个用于**保存**观察者对象的聚集类和**增加**、**删除**观察者对象的方法,以及**通知**所有观察者的抽象方法。 2. **具体主题(Concrete Subject)角色**:也叫具体目标类,它实现抽象目标中的通知方法,当具体主题的内部状态发生改变时,通知所有注册过的观察者对象。 3. **抽象观察者(Observer)角色**:它是一个抽象类或接口,它包含了一个更新自己的抽象方法,当接到具体主题的更改通知时被调用。 4. **具体观察者(Concrete Observer)角色**:实现抽象观察者中定义的抽象方法,以便在得到目标的更改通知时更新自身的状态。 ![](https://img.kancloud.cn/9b/b5/9bb5f48938374190f51ee96ff500987d_725x474.png) ``` <pre class="calibre10">``` <span class="token">//抽象目标</span> abstract class <span class="token4">Subject</span> <span class="token3">{</span> protected $_observers<span class="token1">=</span><span class="token4">array</span><span class="token3">(</span><span class="token3">)</span><span class="token3">;</span> <span class="token">//增加观察者方法</span> public <span class="token5">function</span> <span class="token4">add</span><span class="token3">(</span>Observer $observer<span class="token3">)</span> <span class="token3">{</span> $this<span class="token1">-</span><span class="token1">></span>_observers<span class="token3">[</span><span class="token3">]</span><span class="token1">=</span>$observer<span class="token3">;</span> <span class="token3">}</span> <span class="token">//删除观察者方法</span> public <span class="token5">function</span> <span class="token4">remove</span><span class="token3">(</span>Observer $observer<span class="token3">)</span> <span class="token3">{</span> <span class="token4">foreach</span><span class="token3">(</span>$this<span class="token1">-</span><span class="token1">></span>_observers as $index <span class="token1">=</span><span class="token1">></span> $observer<span class="token3">)</span> <span class="token3">{</span> <span class="token5">if</span> <span class="token3">(</span>$observer <span class="token1">===</span> $observer<span class="token3">)</span> <span class="token3">{</span> <span class="token4">array_splice</span><span class="token3">(</span>$this<span class="token1">-</span><span class="token1">></span>_observers<span class="token3">,</span> $index<span class="token3">,</span> <span class="token6">1</span><span class="token3">)</span><span class="token3">;</span> <span class="token5">return</span><span class="token3">;</span> <span class="token3">}</span> <span class="token3">}</span> <span class="token3">}</span> <span class="token">//通知观察者方法</span> public abstract <span class="token5">function</span> <span class="token4">notifyObserver</span><span class="token3">(</span><span class="token3">)</span><span class="token3">;</span> <span class="token3">}</span> <span class="token">//具体目标</span> class <span class="token4">ConcreteSubject</span> extends <span class="token4">Subject</span> <span class="token3">{</span> public <span class="token5">function</span> <span class="token4">notifyObserver</span><span class="token3">(</span><span class="token3">)</span> <span class="token3">{</span> <span class="token4">print_r</span><span class="token3">(</span><span class="token2">"具体目标发生改变...<br>"</span><span class="token3">)</span><span class="token3">;</span> <span class="token4">print_r</span><span class="token3">(</span><span class="token2">"--------------<br>"</span><span class="token3">)</span><span class="token3">;</span> foreach <span class="token3">(</span>$this<span class="token1">-</span><span class="token1">></span>_observers as $observer<span class="token3">)</span> <span class="token3">{</span> $observer<span class="token1">-</span><span class="token1">></span><span class="token4">response</span><span class="token3">(</span><span class="token3">)</span><span class="token3">;</span> <span class="token3">}</span> <span class="token3">}</span> <span class="token3">}</span> <span class="token">//抽象观察者</span> interface <span class="token4">Observer</span> <span class="token3">{</span> <span class="token5">function</span> <span class="token4">response</span><span class="token3">(</span><span class="token3">)</span><span class="token3">;</span> <span class="token">//反应</span> <span class="token3">}</span> <span class="token">//具体观察者1</span> class <span class="token4">ConcreteObserver1</span> implements <span class="token4">Observer</span> <span class="token3">{</span> public <span class="token5">function</span> <span class="token4">response</span><span class="token3">(</span><span class="token3">)</span> <span class="token3">{</span> <span class="token4">print_r</span><span class="token3">(</span><span class="token2">"具体观察者1作出反应!"</span><span class="token3">)</span><span class="token3">;</span> <span class="token3">}</span> <span class="token3">}</span> <span class="token">//具体观察者1</span> class <span class="token4">ConcreteObserver2</span> implements <span class="token4">Observer</span> <span class="token3">{</span> public <span class="token5">function</span> <span class="token4">response</span><span class="token3">(</span><span class="token3">)</span> <span class="token3">{</span> <span class="token4">print_r</span><span class="token3">(</span><span class="token2">"具体观察者2作出反应!"</span><span class="token3">)</span><span class="token3">;</span> <span class="token3">}</span> <span class="token3">}</span> class <span class="token4">ObserverPattern</span> <span class="token3">{</span> public static <span class="token5">function</span> <span class="token4">main</span><span class="token3">(</span><span class="token3">)</span> <span class="token3">{</span> $subject<span class="token1">=</span><span class="token5">new</span> <span class="token4">ConcreteSubject</span><span class="token3">(</span><span class="token3">)</span><span class="token3">;</span> $subject<span class="token1">-</span><span class="token1">></span><span class="token4">add</span><span class="token3">(</span><span class="token5">new</span> <span class="token4">ConcreteObserver1</span><span class="token3">(</span><span class="token3">)</span><span class="token3">)</span><span class="token3">;</span> $subject<span class="token1">-</span><span class="token1">></span><span class="token4">add</span><span class="token3">(</span><span class="token5">new</span> <span class="token4">ConcreteObserver2</span><span class="token3">(</span><span class="token3">)</span><span class="token3">)</span><span class="token3">;</span> $subject<span class="token1">-</span><span class="token1">></span><span class="token4">notifyObserver</span><span class="token3">(</span><span class="token3">)</span><span class="token3">;</span> <span class="token3">}</span> <span class="token3">}</span> ObserverPattern<span class="token3">:</span><span class="token3">:</span><span class="token4">main</span><span class="token3">(</span><span class="token3">)</span><span class="token3">;</span> ``` ``` 例子: ``` <pre class="calibre10">``` <span class="token">//抽象主题角色</span> interface <span class="token4">ISubject</span><span class="token3">{</span> <span class="token5">function</span> <span class="token4">addObserver</span><span class="token3">(</span> $observer <span class="token3">)</span><span class="token3">;</span> <span class="token3">}</span> <span class="token">// 具体主题角色(被观察者) 用于添加、删除、保存、通知观察者</span> class <span class="token4">UserList</span> implements <span class="token4">ISubject</span><span class="token3">{</span> private $_observers <span class="token1">=</span> <span class="token4">array</span><span class="token3">(</span><span class="token3">)</span><span class="token3">;</span> public <span class="token5">function</span> <span class="token4">sendMsg</span><span class="token3">(</span> $name <span class="token3">)</span><span class="token3">{</span> <span class="token4">foreach</span><span class="token3">(</span> $this<span class="token1">-</span><span class="token1">></span>_observers as $obs <span class="token3">)</span><span class="token3">{</span> $obs<span class="token1">-</span><span class="token1">></span><span class="token4">onSendMsg</span><span class="token3">(</span> $this<span class="token3">,</span> $name <span class="token3">)</span><span class="token3">;</span> <span class="token3">}</span> <span class="token3">}</span> public <span class="token5">function</span> <span class="token4">addObserver</span><span class="token3">(</span> $observer <span class="token3">)</span><span class="token3">{</span> $this<span class="token1">-</span><span class="token1">></span>_observers<span class="token3">[</span><span class="token3">]</span><span class="token1">=</span> $observer<span class="token3">;</span> <span class="token3">}</span> public <span class="token5">function</span> <span class="token4">removeObserver</span><span class="token3">(</span>$observer_name<span class="token3">)</span> <span class="token3">{</span> <span class="token4">foreach</span><span class="token3">(</span>$this<span class="token1">-</span><span class="token1">></span>_observers as $index <span class="token1">=</span><span class="token1">></span> $observer<span class="token3">)</span> <span class="token3">{</span> <span class="token5">if</span> <span class="token3">(</span>$observer<span class="token1">-</span><span class="token1">></span><span class="token4">getName</span><span class="token3">(</span><span class="token3">)</span> <span class="token1">===</span> $observer_name<span class="token3">)</span> <span class="token3">{</span> <span class="token4">array_splice</span><span class="token3">(</span>$this<span class="token1">-</span><span class="token1">></span>_observers<span class="token3">,</span> $index<span class="token3">,</span> <span class="token6">1</span><span class="token3">)</span><span class="token3">;</span> <span class="token5">return</span><span class="token3">;</span> <span class="token3">}</span> <span class="token3">}</span> <span class="token3">}</span> <span class="token3">}</span> <span class="token">//抽象观察者</span> interface <span class="token4">IObserver</span><span class="token3">{</span> <span class="token5">function</span> <span class="token4">onSendMsg</span><span class="token3">(</span> $sender<span class="token3">,</span> $args <span class="token3">)</span><span class="token3">;</span> <span class="token5">function</span> <span class="token4">getName</span><span class="token3">(</span><span class="token3">)</span><span class="token3">;</span> <span class="token3">}</span> <span class="token">//具体观察者角色1</span> class <span class="token4">UserListLogger</span> implements <span class="token4">IObserver</span><span class="token3">{</span> public <span class="token5">function</span> <span class="token4">onSendMsg</span><span class="token3">(</span> $sender<span class="token3">,</span> $args <span class="token3">)</span><span class="token3">{</span> <span class="token4">echo</span><span class="token3">(</span> <span class="token2">"'$args' 以发送到 UserListLogger\n"</span> <span class="token3">)</span><span class="token3">;</span> <span class="token3">}</span> public <span class="token5">function</span> <span class="token4">getName</span><span class="token3">(</span><span class="token3">)</span><span class="token3">{</span> <span class="token5">return</span> static<span class="token3">:</span><span class="token3">:</span>class<span class="token3">;</span><span class="token">//同 return 'UserListLogger';</span> <span class="token3">}</span> <span class="token3">}</span> <span class="token">//具体观察者角色2</span> class <span class="token4">OtherObserver</span> implements <span class="token4">IObserver</span><span class="token3">{</span> public <span class="token5">function</span> <span class="token4">onSendMsg</span><span class="token3">(</span> $sender<span class="token3">,</span> $args <span class="token3">)</span><span class="token3">{</span> <span class="token4">echo</span><span class="token3">(</span> <span class="token2">"'$args' 已发送到 OtherObserver\n"</span> <span class="token3">)</span><span class="token3">;</span> <span class="token3">}</span> public <span class="token5">function</span> <span class="token4">getName</span><span class="token3">(</span><span class="token3">)</span><span class="token3">{</span> <span class="token5">return</span> static<span class="token3">:</span><span class="token3">:</span>class<span class="token3">;</span><span class="token">//同 return 'OtherObserver';</span> <span class="token3">}</span> <span class="token3">}</span> $ul <span class="token1">=</span> <span class="token5">new</span> <span class="token4">UserList</span><span class="token3">(</span><span class="token3">)</span><span class="token3">;</span><span class="token">//被观察者</span> $ul<span class="token1">-</span><span class="token1">></span><span class="token4">addObserver</span><span class="token3">(</span> <span class="token5">new</span> <span class="token4">UserListLogger</span><span class="token3">(</span><span class="token3">)</span> <span class="token3">)</span><span class="token3">;</span><span class="token">//增加观察者</span> $ul<span class="token1">-</span><span class="token1">></span><span class="token4">addObserver</span><span class="token3">(</span> <span class="token5">new</span> <span class="token4">OtherObserver</span><span class="token3">(</span><span class="token3">)</span> <span class="token3">)</span><span class="token3">;</span><span class="token">//增加观察者</span> $ul<span class="token1">-</span><span class="token1">></span><span class="token4">sendMsg</span><span class="token3">(</span> <span class="token2">"Jack"</span> <span class="token3">)</span><span class="token3">;</span><span class="token">//发送消息到观察者</span> echo <span class="token2">"<br>"</span><span class="token3">;</span> $ul<span class="token1">-</span><span class="token1">></span><span class="token4">removeObserver</span><span class="token3">(</span><span class="token2">'UserListLogger'</span><span class="token3">)</span><span class="token3">;</span><span class="token">//移除观察者</span> $ul<span class="token1">-</span><span class="token1">></span><span class="token4">sendMsg</span><span class="token3">(</span><span class="token2">"hello"</span><span class="token3">)</span><span class="token3">;</span><span class="token">//发送消息到观察者 </span> ``` ``` ## **例子** php 内置了SplSubject与SplObserver接口可以更简便的实现观察者模式 > newspaper是被观察的对象,reader是观察者。当报纸发布消息, 每一个用户都会得到通知。这就是观察者模式的使用场景 ``` <pre class="calibre10">``` class <span class="token4">Newspaper</span> implements <span class="token4">SplSubject</span> <span class="token3">{</span> private $name<span class="token3">;</span> private $observers<span class="token3">;</span> private $content<span class="token3">;</span> public <span class="token5">function</span> <span class="token4">__construct</span><span class="token3">(</span>$name<span class="token3">)</span><span class="token3">{</span> $this<span class="token1">-</span><span class="token1">></span>name <span class="token1">=</span> $name<span class="token3">;</span> $this<span class="token1">-</span><span class="token1">></span>observers <span class="token1">=</span> <span class="token5">new</span> <span class="token4">SplObjectStorage</span><span class="token3">(</span><span class="token3">)</span><span class="token3">;</span> <span class="token3">}</span> <span class="token">//增加一个观察者 </span> public <span class="token5">function</span> <span class="token4">attach</span><span class="token3">(</span>SplObserver $observer<span class="token3">)</span><span class="token3">{</span> <span class="token">//向SplObjectStorage中添加一个对象</span> <span class="token">//此处为SplObjectStorage对象存储类的attach方法而SplSubject 的</span> $this<span class="token1">-</span><span class="token1">></span>observers<span class="token1">-</span><span class="token1">></span><span class="token4">attach</span><span class="token3">(</span>$observer<span class="token3">)</span><span class="token3">;</span> <span class="token3">}</span> <span class="token">//移除一个观察者</span> public <span class="token5">function</span> <span class="token4">detach</span><span class="token3">(</span>SplObserver $observer<span class="token3">)</span><span class="token3">{</span> $this<span class="token1">-</span><span class="token1">></span>observers<span class="token1">-</span><span class="token1">></span><span class="token4">detach</span><span class="token3">(</span>$observer<span class="token3">)</span><span class="token3">;</span> <span class="token3">}</span> <span class="token">//通知所有观察者</span> public <span class="token5">function</span> <span class="token4">notify</span><span class="token3">(</span><span class="token3">)</span><span class="token3">{</span> foreach <span class="token3">(</span>$this<span class="token1">-</span><span class="token1">></span>observers as $observer<span class="token3">)</span> <span class="token3">{</span> $observer<span class="token1">-</span><span class="token1">></span><span class="token4">update</span><span class="token3">(</span>$this<span class="token3">)</span><span class="token3">;</span> <span class="token3">}</span> <span class="token3">}</span> public <span class="token5">function</span> <span class="token4">getContent</span><span class="token3">(</span><span class="token3">)</span><span class="token3">{</span> <span class="token5">return</span> $this<span class="token1">-</span><span class="token1">></span>content<span class="token3">.</span><span class="token2">"{$this->name}"</span><span class="token3">;</span> <span class="token3">}</span> public <span class="token5">function</span> <span class="token4">breakOutNews</span><span class="token3">(</span>$content<span class="token3">)</span> <span class="token3">{</span> $this<span class="token1">-</span><span class="token1">></span>content <span class="token1">=</span> $content<span class="token3">;</span> $this<span class="token1">-</span><span class="token1">></span><span class="token4">notify</span><span class="token3">(</span><span class="token3">)</span><span class="token3">;</span> <span class="token3">}</span> <span class="token3">}</span> <span class="token1"><</span><span class="token1">?</span>php class <span class="token4">Reader</span> implements <span class="token4">SplObserver</span> <span class="token3">{</span> private $name<span class="token3">;</span> public <span class="token5">function</span> <span class="token4">__construct</span><span class="token3">(</span>$name<span class="token3">)</span><span class="token3">{</span> $this<span class="token1">-</span><span class="token1">></span>name <span class="token1">=</span> $name<span class="token3">;</span> <span class="token3">}</span> public <span class="token5">function</span> <span class="token4">update</span><span class="token3">(</span>SplSubject $subject<span class="token3">)</span> <span class="token3">{</span> echo $this<span class="token1">-</span><span class="token1">></span>name<span class="token3">.</span><span class="token2">' is reading breakout news'</span><span class="token3">.</span>$subject<span class="token1">-</span><span class="token1">></span><span class="token4">getContent</span><span class="token3">(</span><span class="token3">)</span><span class="token3">;</span> <span class="token3">}</span> <span class="token3">}</span> <span class="token1"><</span><span class="token1">?</span>php include <span class="token2">"Newspaper.php"</span><span class="token3">;</span> include <span class="token2">"Reader.php"</span><span class="token3">;</span> class <span class="token4">WorkFlow</span> <span class="token3">{</span> public <span class="token5">function</span> <span class="token4">run</span><span class="token3">(</span><span class="token3">)</span> <span class="token3">{</span> $newspaper <span class="token1">=</span> <span class="token5">new</span> <span class="token4">Newspaper</span><span class="token3">(</span><span class="token2">'纽约时报'</span><span class="token3">)</span><span class="token3">;</span><span class="token">//SplSubject </span> $allen <span class="token1">=</span> <span class="token5">new</span> <span class="token4">Reader</span><span class="token3">(</span><span class="token2">"allen"</span><span class="token3">)</span><span class="token3">;</span><span class="token">//SplObserver</span> $jimmy <span class="token1">=</span> <span class="token5">new</span> <span class="token4">Reader</span><span class="token3">(</span><span class="token2">"jimmy"</span><span class="token3">)</span><span class="token3">;</span><span class="token">//SplObserver</span> $tom <span class="token1">=</span> <span class="token5">new</span> <span class="token4">Reader</span><span class="token3">(</span><span class="token2">"tom"</span><span class="token3">)</span><span class="token3">;</span><span class="token">//SplObserver</span> $newspaper<span class="token1">-</span><span class="token1">></span><span class="token4">attach</span><span class="token3">(</span>$allen<span class="token3">)</span><span class="token3">;</span><span class="token">//</span> $newspaper<span class="token1">-</span><span class="token1">></span><span class="token4">attach</span><span class="token3">(</span>$jimmy<span class="token3">)</span><span class="token3">;</span> $newspaper<span class="token1">-</span><span class="token1">></span><span class="token4">attach</span><span class="token3">(</span>$tom<span class="token3">)</span><span class="token3">;</span> $newspaper<span class="token1">-</span><span class="token1">></span><span class="token4">detach</span><span class="token3">(</span>$tom<span class="token3">)</span><span class="token3">;</span> $newspaper<span class="token1">-</span><span class="token1">></span><span class="token4">breakOutNews</span><span class="token3">(</span><span class="token2">'USA BREAK DOWN'</span><span class="token3">)</span><span class="token3">;</span> <span class="token3">}</span> <span class="token3">}</span> $work <span class="token1">=</span> <span class="token5">new</span> <span class="token4">WorkFlow</span><span class="token3">(</span><span class="token3">)</span><span class="token3">;</span> $work<span class="token1">-</span><span class="token1">></span><span class="token4">run</span><span class="token3">(</span><span class="token3">)</span><span class="token3">;</span> ``` ``` ## **php中的钩子(hook插件机制)** 插件管理器类作为具体主题(被观察者)角色:也叫具体目标类,用于保存、添加、删除及通知观察者(目的是执行入口函数) 插件作为观察者,一旦被注册则调用插件的入口函数 > /index.php ``` <pre class="calibre10">``` <span class="token1"><</span><span class="token1">?</span>php <span class="token4">define</span><span class="token3">(</span><span class="token2">'ROOTPATH'</span><span class="token3">,</span>__DIR__<span class="token3">.</span>DIRECTORY_SEPARATOR<span class="token3">)</span><span class="token3">;</span> <span class="token">/** * 获取激活的插件 * @return array 返回激活的插件信息数组 */</span> <span class="token5">function</span> <span class="token4">get_active_plugins</span><span class="token3">(</span><span class="token3">)</span><span class="token3">{</span> <span class="token">//一般从数据库拉出 这里用数据模拟</span> $plugins<span class="token1">=</span><span class="token3">[</span> <span class="token3">[</span> <span class="token2">'name'</span> <span class="token1">=</span><span class="token1">></span> <span class="token2">'demo'</span><span class="token3">,</span> <span class="token2">'directory'</span><span class="token1">=</span><span class="token1">></span><span class="token2">'demo'</span> <span class="token3">]</span><span class="token3">,</span> <span class="token3">]</span><span class="token3">;</span> <span class="token5">return</span> $plugins<span class="token3">;</span> <span class="token3">}</span> require_once <span class="token2">"./core/PluginManager.php"</span><span class="token3">;</span> $pluginManager<span class="token1">=</span><span class="token5">new</span> <span class="token4">PluginManager</span><span class="token3">(</span><span class="token3">)</span><span class="token3">;</span> <span class="token">//trigger通知观察者(插件)</span> $pluginManager<span class="token1">-</span><span class="token1">></span><span class="token4">trigger</span><span class="token3">(</span><span class="token2">'demo'</span><span class="token3">,</span><span class="token2">''</span><span class="token3">)</span><span class="token3">;</span> ``` ``` > 插件管理类 /core/PluginManager.php ``` <pre class="calibre10">``` <span class="token1"><</span><span class="token1">?</span>php class <span class="token4">PluginManager</span><span class="token3">{</span> private $_listeners<span class="token1">=</span><span class="token4">array</span><span class="token3">(</span><span class="token3">)</span><span class="token3">;</span> public <span class="token5">function</span> <span class="token4">__construct</span><span class="token3">(</span><span class="token3">)</span><span class="token3">{</span> $this<span class="token1">-</span><span class="token1">></span><span class="token4">_init</span><span class="token3">(</span><span class="token3">)</span><span class="token3">;</span> <span class="token3">}</span> private <span class="token5">function</span> <span class="token4">_init</span><span class="token3">(</span><span class="token3">)</span><span class="token3">{</span> <span class="token">//外部获取所有活动插件,返回包含插件名及插件目录的数组</span> $plugins<span class="token1">=</span><span class="token4">get_active_plugins</span><span class="token3">(</span><span class="token3">)</span><span class="token3">;</span> <span class="token">//循环实例激活</span> foreach <span class="token3">(</span>$plugins as $plugin<span class="token3">)</span> <span class="token3">{</span> $plugin_file<span class="token1">=</span>ROOTPATH<span class="token3">.</span><span class="token2">'plugins'</span><span class="token3">.</span>DIRECTORY_SEPARATOR<span class="token3">.</span>$plugin<span class="token3">[</span><span class="token2">'directory'</span><span class="token3">]</span><span class="token3">.</span>DIRECTORY_SEPARATOR<span class="token3">.</span><span class="token2">'actions.php'</span><span class="token3">;</span> <span class="token5">if</span> <span class="token3">(</span>@<span class="token4">file_exists</span><span class="token3">(</span>$plugin_file<span class="token3">)</span><span class="token3">)</span> <span class="token3">{</span> <span class="token">//加载这些插件的文件</span> <span class="token4">include_once</span><span class="token3">(</span>$plugin_file<span class="token3">)</span><span class="token3">;</span> <span class="token3">}</span><span class="token5">else</span><span class="token3">{</span> <span class="token5">throw</span> <span class="token5">new</span> <span class="token4">Exception</span><span class="token3">(</span><span class="token2">'文件'</span><span class="token3">.</span>$plugin_file<span class="token3">.</span><span class="token2">'不存在!'</span><span class="token3">)</span><span class="token3">;</span> <span class="token3">}</span> <span class="token">//解析出这些插件的插件类名</span> $class <span class="token1">=</span> <span class="token4">strtolower</span><span class="token3">(</span>$plugin<span class="token3">[</span><span class="token2">'name'</span><span class="token3">]</span><span class="token3">)</span><span class="token3">.</span><span class="token2">'_actions'</span><span class="token3">;</span><span class="token">//如demo_actions</span> <span class="token5">if</span><span class="token3">(</span><span class="token4">class_exists</span><span class="token3">(</span>$class<span class="token3">)</span><span class="token3">)</span><span class="token3">{</span> <span class="token">//实例化插件类 同时实例化的插件析构方法调用register注册插件(将实例放入$_listeners中)</span> <span class="token">//等同 new demo_actions(PluginManager $this); $pluginManager->register('demo', $demo_actions_instance, 'say_hello');</span> <span class="token5">new</span> $<span class="token4">class</span><span class="token3">(</span>$this<span class="token3">)</span><span class="token3">;</span> <span class="token3">}</span><span class="token5">else</span><span class="token3">{</span> <span class="token5">throw</span> <span class="token5">new</span> <span class="token4">Exception</span><span class="token3">(</span>$class<span class="token3">.</span><span class="token2">'不存在!'</span><span class="token3">)</span><span class="token3">;</span> <span class="token3">}</span> <span class="token3">}</span> <span class="token3">}</span> <span class="token">/** * 触发一个钩子 * * @param string $hook 钩子的名称 * @param mixed $data 钩子的入参 * @return mixed */</span> public <span class="token5">function</span> <span class="token4">trigger</span><span class="token3">(</span>$hook<span class="token3">,</span>$data<span class="token3">)</span><span class="token3">{</span> $result <span class="token1">=</span> <span class="token2">''</span><span class="token3">;</span> <span class="token5">if</span> <span class="token3">(</span><span class="token4">isset</span><span class="token3">(</span>$this<span class="token1">-</span><span class="token1">></span>_listeners<span class="token3">[</span>$hook<span class="token3">]</span><span class="token3">)</span> <span class="token1">&&</span> <span class="token4">is_array</span><span class="token3">(</span>$this<span class="token1">-</span><span class="token1">></span>_listeners<span class="token3">[</span>$hook<span class="token3">]</span><span class="token3">)</span> <span class="token1">&&</span> <span class="token4">count</span><span class="token3">(</span>$this<span class="token1">-</span><span class="token1">></span>_listeners<span class="token3">[</span>$hook<span class="token3">]</span><span class="token3">)</span> <span class="token1">></span> <span class="token6">0</span><span class="token3">)</span><span class="token3">{</span> <span class="token4">var_dump</span><span class="token3">(</span>$this<span class="token1">-</span><span class="token1">></span>_listeners<span class="token3">)</span><span class="token3">;</span> foreach <span class="token3">(</span>$this<span class="token1">-</span><span class="token1">></span>_listeners<span class="token3">[</span>$hook<span class="token3">]</span> as $listener<span class="token3">)</span> <span class="token3">{</span> <span class="token">//他的key就是 demo_actions->say_hello</span> <span class="token">// 取出插件对象的引用和方法</span> $class <span class="token1">=</span><span class="token1">&</span> $listener<span class="token3">[</span><span class="token6">0</span><span class="token3">]</span><span class="token3">;</span> $method <span class="token1">=</span> $listener<span class="token3">[</span><span class="token6">1</span><span class="token3">]</span><span class="token3">;</span> <span class="token5">if</span><span class="token3">(</span><span class="token4">method_exists</span><span class="token3">(</span>$class<span class="token3">,</span>$method<span class="token3">)</span><span class="token3">)</span> <span class="token3">{</span> <span class="token">// 动态调用插件的方法</span> $result <span class="token3">.</span><span class="token1">=</span> $class<span class="token1">-</span><span class="token1">></span>$<span class="token4">method</span><span class="token3">(</span>$data<span class="token3">)</span><span class="token3">;</span> <span class="token3">}</span> <span class="token3">}</span> <span class="token3">}</span> <span class="token5">return</span> $result<span class="token3">;</span> <span class="token3">}</span> <span class="token">/** * 注册插件 * @param string $hook 钩子的名称 * @param obj &$reference 插件类实例的引用 * @param string $method 插件所执行的方法(插件入口方法) * @return void */</span> public <span class="token5">function</span> <span class="token4">register</span><span class="token3">(</span>$hook<span class="token3">,</span> <span class="token1">&</span>$reference<span class="token3">,</span> $method<span class="token3">)</span><span class="token3">{</span> <span class="token">//获取插件要实现的方法</span> $key <span class="token1">=</span> <span class="token4">get_class</span><span class="token3">(</span>$reference<span class="token3">)</span><span class="token3">.</span><span class="token2">'->'</span><span class="token3">.</span>$method<span class="token3">;</span> <span class="token">//将插件的引用连同方法push进监听数组即$_listeners属性中</span> $this<span class="token1">-</span><span class="token1">></span>_listeners<span class="token3">[</span>$hook<span class="token3">]</span><span class="token3">[</span>$key<span class="token3">]</span> <span class="token1">=</span> <span class="token4">array</span><span class="token3">(</span><span class="token1">&</span>$reference<span class="token3">,</span> $method<span class="token3">)</span><span class="token3">;</span> #此处做些日志记录方面的东西 <span class="token3">}</span> <span class="token3">}</span> ``` ``` > /plugins/demo/actions.php ``` <pre class="calibre10">``` <span class="token1"><</span><span class="token1">?</span>php <span class="token">/** *需要注意的几个默认规则: * 1. 本插件类的文件名必须是action * 2. 插件类的名称必须是{插件名_actions} */</span> class <span class="token4">demo_actions</span> <span class="token3">{</span> <span class="token">//解析函数的参数是pluginManager的引用</span> <span class="token5">function</span> <span class="token4">__construct</span><span class="token3">(</span>PluginManager <span class="token1">&</span>$pluginManager<span class="token3">)</span> <span class="token3">{</span> <span class="token">//注册这个插件</span> <span class="token">//第一个参数是钩子的名称</span> <span class="token">//第二个参数是该类实例的引用</span> <span class="token">//第三个是插件所执行的方法</span> $pluginManager<span class="token1">-</span><span class="token1">></span><span class="token4">register</span><span class="token3">(</span><span class="token2">'demo'</span><span class="token3">,</span> $this<span class="token3">,</span> <span class="token2">'say_hello'</span><span class="token3">)</span><span class="token3">;</span> <span class="token3">}</span> <span class="token5">function</span> <span class="token4">say_hello</span><span class="token3">(</span><span class="token3">)</span> <span class="token3">{</span> echo <span class="token2">'Hello World'</span><span class="token3">;</span> <span class="token3">}</span> <span class="token3">}</span> ``` ``` ## [php插件机制原理之其他实现方式例子](https://www.jianshu.com/p/7747bbbd14c0)