說明
hook,中文翻譯爲鉤子,編程中的鉤子相似咱們現實中的鉤子,須要掛在東西的時候
直接掛載到上面便可。程序中也是,須要運行的代碼掛載到上面便可。
具體思想就是:在項目代碼中,你認爲要擴展(暫時不擴展)的地方放置一個鉤子函數,
等須要擴展的時候,把須要實現的類和函數掛載到這個鉤子上,就能夠實現擴展了。php
實例
1,需求背景
1,產品剛開始提了一個需求,很簡單,就是獲取一個一維數組,再將數組打印出來
代碼以下實現便可
<?php
class printArr
{
public function main()
{
//獲取數組
$arr = $this->get_arr();
//打印數組
$this->print_arr($arr);
}
public function get_arr()
{
$arr = [9,5,6,0,1,4,7,34,67,28,105,278];
return $arr;
}
public function print_arr($arr)
{
echo "<pre>";
print_r($arr);
}
}
$printArrObj = new printArr;
$printArrObj->main();
2,好吧,功能完成後,產品以爲這樣輸出不太好,最好是有序輸出。熟悉冒泡排序的你,趕忙對代碼作了以下調整(以下虛線內代碼)
<?php
class printArr
{
public function main()
{
//獲取數組
$arr = $this->get_arr();
//----------------------------------------------------
$arr = $this->bubble_sort($arr);
//----------------------------------------------------
//打印數組
$this->print_arr($arr);
}
public function get_arr()
{
$arr = [9,5,6,0,1,4,7,34,67,28,105,278];
return $arr;
}
public function print_arr($arr)
{
echo "<pre>";
print_r($arr);
}
//----------------------------------------------------
public function bubble_sort($arr)
{
//自行完成
}
//----------------------------------------------------
}
$printArrObj = new printArr;
$printArrObj->main();
3,好吧,你的功能完成了。可是後來數據量變大了,產品說冒泡排序運行效率過低,讓你用插入排序法排序。
你怕無理的產品後面又換回冒泡,因而註釋掉冒泡,又寫了插入排序法
<?php
class printArr
{
public function main()
{
//獲取數組
$arr = $this->get_arr();
//----------------------------------------------------
//$arr = $this->bubble_sort($arr);
$arr = $this->insert_sort($arr);
//----------------------------------------------------
//打印數組
$this->print_arr($arr);
}
public function get_arr()
{
$arr = [9,5,6,0,1,4,7,34,67,28,105,278];
return $arr;
}
public function print_arr($arr)
{
echo "<pre>";
print_r($arr);
}
//----------------------------------------------------
/*public function bubble_sort($arr)
{
//自行完成
}*/
public function insert_sort($arr)
{
//自行完成
}
//----------------------------------------------------
}
4,嗯,後面項目愈來愈複雜,產品愈來愈變態。說4,這個數字很差,數組中帶4的unset掉。和上面代碼同樣,你又寫了一個方法,夾在數組的獲取和輸出的中間,用來unset掉4這個數字。
好吧,對你來講就價格方法而已,那就加唄
<?php
class printArr
{
public function main()
{
//獲取數組
$arr = $this->get_arr();
//----------------------------------------------------
//$arr = $this->bubble_sort($arr);
$arr = $this->insert_sort($arr);
$arr = $this->unset_4($arr);
//----------------------------------------------------
//打印數組
$this->print_arr($arr);
}
public function get_arr()
{
$arr = [9,5,6,0,1,4,7,34,67,28,105,278];
return $arr;
}
public function print_arr($arr)
{
echo "<pre>";
print_r($arr);
}
//----------------------------------------------------
/*public function bubble_sort($arr)
{
//自行完成
}*/
public function insert_sort($arr)
{
//自行完成
}
public function unset_4($arr)
{
//自行完成
}
//----------------------------------------------------
}
5,這個產品愈來愈變態,說3很差,也要unset掉,5很差也要unset掉。8這個數字好,給數組中加個8,又說666這個數字更好,要加個666。而且半個小時要作完。你終於受不了了,提議你一我的作不完。產品經理二話不說調來好幾我的,每人作一個。
1,這裏就要說出現的問題了。每一個工程師的習慣都不同
好比你封裝了方法,寫在了輸出數組這個主類的下面,main函數中直接調用了
小王同事不喜歡封裝方法,直接在獲取數組和打印數組之間添加了代碼段。
小李同事以爲輸出數組當前這個類,爲主類,處理過程本身封裝一個副類,副類中封裝方法,在輸出數組這個類引入調用便可。
等等等等方案。
2,而後產品更多的變態需求出來了,大家團隊也默默的維護着輸出數組中間的每一道工序。就這樣不知不覺兩年過去了,
忽然線上出現了一個小bug,你做爲技術大牛,排錯交給了你,
接下來你開始排錯
封裝方法的工程師,寫了500多個方法在下面,雖然難找不過耐心點還能夠慢慢找到維護排錯。
封裝副類的工程師,封裝了100多個副類,各種下都有各類方法,再耐心點吧,慢慢找還能夠維護排錯。
直接加代碼段的工程師,兩年積累了五六千行代碼,好吧,你須要讀懂每一行才能發現問題,
到了這個時候,我猜你直接想罵娘了吧,氣沖沖的找來加代碼段的工程師,讓他看看這段代碼
什麼意思,結果呢,兩年前的代碼,他本身都不知道什麼意思了,這下完了,你是否是心態崩
了,你是否是絕望了,你是否是直接想離職了?
6,如今想一想,大家公司內部兩年積累的代碼已經能讓你到了崩潰的邊緣,那若是開源代碼,天下人都能維護,而且維護了十多年,若是按照上面維護的路子來維護,那這個開源軟件早都嗝屁了吧。
故事講到這裏,就到此爲止。hook編程機制就是解決此類問題應運而生的。
2,解決方案
對,解決上面的問題,從三個方向入手。
1,對應產品提出不一樣需求改動,用插件的形式整齊劃一的放在對應插件庫裏。
2,插件管理類,用來註冊插件,監聽插件是否須要運行,須要的直接hook過來,運行便可。
3,在獲取數組和輸出數組中間放一段插件觸發器,用來觸發須要運行的插件。
以上也就是插件的三要素。
3,示例代碼
按照hook原理作一個簡單的插件用例
結構:hookDemo
|-----index.php//主運行代碼,觸發器hook放在可能須要利用插件的地方
|-----pluginManager.php//插件管理類
|-----plugin//插件目錄
|------sort.php//插件
|------unsetElement.php//插件
index.php
<?php
class printArr
{
public $pluginManagerObj;
public function __construct()
{
//凡事用到hook的類,優先加載hook類
require './pluginManager.php';
$this->pluginManagerObj = new pluginManager();
}
public function main()
{
//獲取數組
$arr = $this->get_arr();
//hook,也就是觸發函數
$arr = $this->pluginManagerObj->trigger('unsetElement',$arr);
$arr = $this->pluginManagerObj->trigger('sort',$arr);
$this->print_arr($arr);
//輸出數組
}
public function get_arr()
{
$arr = [9,5,6,0,1,4,7,34,67,28,105,278];
return $arr;
}
public function print_arr($arr)
{
echo "<pre>";
print_r($arr);
}
}
echo "<pre>";
$printArrObj = new printArr;
$printArrObj->main();
pluginManager.php
<?php
class pluginManager
{
//監聽數組
public $listen = [];
public function __construct()
{
//構造方法,引入插件以及監聽
$plugin_list = scandir('./plugin');
foreach($plugin_list as $k=>$v){
if($v == '.' || $v == '..'){
unset($plugin_list[$k]);
}
}
if($plugin_list){
foreach($plugin_list as $v){
if(file_exists('./plugin/'.$v)){
//引入插件
require_once('./plugin/'.$v);
//獲取插件名
$class = explode('.',$v);
$class = $class[0];
if(class_exists($class)){
new $class($this);
}
}
}
}
}
//註冊插件
public function register($hook,&$obj,$method)
{
$key = get_class($obj).'->'.$method;
$val = [$obj,$method];
$this->listen[$hook][$key] = $val;
}
//觸發插件
public function trigger($hook,$data)
{
if(isset($this->listen[$hook]) && is_array($this->listen[$hook]) && count($this->listen[$hook])>0){
foreach($this->listen[$hook] as $key => $val){
$hook_obj = &$val[0];
$method = $val[1];
$res = $hook_obj->$method($data);
}
}
return $res;
}
}
sort.php
<?php
class sort
{
public function __construct(&$pluginManagerObj)
{
$pluginManagerObj->register('sort',$this,'sort');
}
public function sort($arr)
{
for($i=1;$i<count($arr);$i++){
for($j=$i-1;$j>=0;$j--){
if($arr[$j+1] > $arr[$j]){
$tmp = $arr[$j];
$arr[$j] = $arr[$j+1];
$arr[$j+1] = $tmp;
}else{
break;
}
}
}
return $arr;
}
}
unsetElement.php
<?php
class unsetElement
{
public function __construct(&$pluginManagerObj)
{
$pluginManagerObj->register('unsetElement',$this,'unset_4');
}
public function unset_4($arr)
{
foreach($arr as $k=>$v){
if($v == 4){
unset($arr[$k]);
}
}
return array_values($arr);
}
}
#####心得
1,關於hook
結合上面的例子,hook最好的方面是,插件和項目互相獨立,下降耦合性。第三方開發人員也不
須要知道項目如何處理,相似api同樣,只須要知道插件需求便可開發。
二來,插件跟核心代碼分離,方便各個區間段排錯
三來,項目開發過程當中,防止了代碼的冗餘雜亂,更適用於開源項目
2,關於實現
三要素,
觸發函數,plugin管理類,plugin代碼。
實現過程當中,
插件管理類和插件類實例化的對象必須貫穿全局且惟一。由於註冊和執行過程當中,監聽信息
要保持全局。實現方案有三種:
1,對象的各類操做必須用引用&。
2,用單例模式獲取對象
3,$_GLOBAL;
上面的例子只是一個簡單的說明,
熟悉原理後,能夠拓展設置plugin配置文件對plugin管理,
對plugin自己作到互相聯繫,互相調用。具體拓展狀況按照需求而定
hook思路理解清楚後,不拘泥於我上面寫的這種方式,由於這種方式只是本身寫框架玩的時候
利用了面向對象方式。
總之hook最普遍的原理,就是
開局註冊全局監聽變量,
全局監聽變量負責監聽,
觸發函數負責觸發,
一旦觸發執行代碼
---------------------
做者:敦煌的駝鈴
來源:CSDN
原文:https://blog.csdn.net/SiuKong_Ngau/article/details/83587048
版權聲明:本文爲博主原創文章,轉載請附上博文連接!編程