序
作為程序員,設計出優雅而完美的系統,永遠是讓我們非常興奮的事情。高手不在於你會多少語言,而在於你有多高的思想。
在設計中,怎么體現自身價值,那就是要比別人多想幾步。
講鈎子程序,起源是對用戶提交的參數校驗(永遠不要相信用戶),一開始為了趕工期,按照比較傳統的方式,每個接口里重復性的對參數進行過濾。后面隨着業務的發展(功能迭代),系統的維護成本越來越高,遂想一個更高級的方式進行處理。借鑒同事之前的代碼,使用鈎子方式進行重構。
之前寫過javascript 鈎子機制, 偏后鈎,可以互相借鑒下。
脈路

概念
把一段程序塊(執行體)通過某種方式掛入系統中,從而獲得對系統的控制權。
注意下圖掛鈎位置:

應用
小的方面: 進行基礎的入參校驗或消息過濾。
大的方面:組件化,可在系統中進行插拔管理。
優點:
1、降低系統的耦合度;
2、降低開發、測試人力成本,用少量的代碼實現高可用功能;
3、提高模塊間的可用性;
4、通過配置(配置文件or數據庫)的方式升級接口。
缺點:
學習成本過高;
系統復雜度提升;
實現思想
配置文件的方式進行鈎子定義、鈎子鏈管理(使用“組”的概念)、掛鈎。

鈎子:程序執行體;
鈎子組: 鈎子鏈的分類定義;
掛鈎: 入口(MVC中action或者controller)與鈎子組進行綁定。
實現方式
掛鈎器(繼承類):
<?php /** * @name Service_Page_Test * @desc page層對接第三方抽象類 * @author */ abstract class Service_Page_Test { public $hookGroupPrev = null; // 前鈎子組 public $hookGroupAfter = null; // 后鈎子組 public $hookReturn = array(); //鈎子返回值 public $reqData = null; // page模塊分析的數據 /** * 獲取需要驗證的參數配置 * @return array */ public function _getCheckParams() { return array(); } /** * 入口方法 * @param array $arrInput * @return array */ public function execute($arrInput) { $res = array( 'errno' => Test_Errno::ERRNO_SUCCESS, 'errmsg' => Test_Errno::$ERRMSG[Test_Errno::ERRNO_SUCCESS], ); try { $this->_init($arrInput); $this->_beforeExecute(); $res = $this->doExecute($arrInput); $this->_afterExecute(); } catch (Test_Exception $e) { $res = array( 'errno' => $e->getCode(), 'errmsg' => $e->getMessage(), ); } catch (Exception $e) { $res = array( 'errno' => $e->getCode(), 'errmsg' => $e->getMessage(), ); } return $res; } /** * auto exec * @param array $arrInput * @throws Exception * @return array */ protected function doExecute($arrInput){ } /** * 獲取權限信息 * @param array $arrInput * @return array */ public function _init($arrInput) { $pageModulesConf = Conf::getConf('page/' . get_class($this)); $this->reqData = $arrInput; $this->hookGroupPrev[] = $pageModulesConf['hook_group']['prev']; $this->hookGroupAfter[] = $pageModulesConf['hook_group']['after']; } /** * 執行filter * @param string */ public function _beforeExecute() { if (!empty($this->hookGroupPrev) && is_array($this->hookGroupPrev)) { foreach ($this->hookGroupPrev as $hookGroups) { foreach ($hookGroups as $hookGroup) { $this->_executeHook($hookGroup, $this->reqData); } } } } /** * @param array $arrInput * @return array */ public function _afterExecute() { if (!empty($this->hookGroupAfter) && is_array($this->hookGroupAfter)) { foreach ($this->hookGroupAfter as $hookGroups) { foreach ($hookGroups as $hookGroup) { $this->_executeHook($hookGroup, $this->reqData); } } } } /** * 執行filter * @param string */ public function _executeHook($hookGroup, $reqData) { $hookGroupConf = Conf::getConf('hook/group/' . $hookGroup); if(!empty($hookGroupConf)){ foreach($hookGroupConf as $hook){ $hookConf = Conf::getConf('hook/hook/' . $hook); $class = $hookConf['class']; $method = $hookConf['method']; $inputParams = isset($hookConf['getInputParams']) ? $this->{$hookConf['getInputParams']}() : null; if (class_exists($class)) { $obj = new $class(); if (method_exists($obj, $method)) { $this->hookReturn[$hook][] = $obj->$method($inputParams, $reqData); } } } } } }
hook.conf
# 鈎子組
[group] [.check_req_customer] 0 : checkReqCustomerBaseInfo [.after_demo] 0 : afterDemo # 鈎子 [hook] [.checkReqCustomerBaseInfo] class: Service_Page_Hook_Customer method: checkBaseInfo getInputParams: _getCheckParams [.afterDemo] class: Service_Page_Hook_Customer method: afterDemo getInputParams: _getCheckParams
page.conf
[Service_Page_Input] #綁定鈎子組 [.hook_group] [..prev] 0 : check_req_customer [..after] 0 : after_demo
推薦
