PHP 之鉤子行爲

之前對鉤子還是有點感興趣,寫下了之前的設計模式,順手看了鉤子,其實你可以比對看一下。


鉤子和我上一篇的觀察者模式或者策略模式很有關聯,應該就是相通的。


應用場景:來之TP行爲的文字---》框架的執行流程中,例如路由檢測是一個行爲,靜態緩存是一個行爲,用戶權限檢測也是行爲,大到業務邏輯,小到瀏覽器檢測、多語言檢測等等都可以當做是一個行爲,甚至說你希望給你的網站用戶的第一次訪問彈出Hello,world!這些都可以看成是一種行爲,行爲的存在讓你無需改動框架和應用,而在外圍通過擴展或者配置來改變或者增加一些功能。

外加一幅圖:竟然需要用Flash來上傳圖片,可惜瀏覽器不支持,我也很討厭Flash。那麼就沒辦法來打字吧。鉤子程序對於我最大的體驗就是我第一次和老闆做一個簡單的手機編輯器,註冊繳費使用,所以每個頁面都要手動寫下固定的代碼,驗證是否繳費。所以每個頁面雷打不動的幾行代碼,如今覺得可以使用鉤子或者說鉤子應運而生。如果你也遇到這種情景,就可以考略,當然,寫了鉤子,也許你還需要每個地方寫一下,引用下,但是你對於驗證的程序實現的維護就很方便了。TP的行爲更進一步,在整個框架的幾個關鍵點設置了標籤,之要綁定行爲就可以。


幫你理解狗子的小程序如下:

<?php
	//統一對外的類
	class Hook{
		private $hooklist = null;

		//感覺這裏很像觀察者模式或者是策略模式
		public function add($action_hook){
			$this->hooklist[]=new $action_hook();
		}
		//觸發事件
		public function exec(){
			foreach($this->hooklist as $action_hook){
				$action_hook->act();//鉤子中統一的方法
			}
		}
	}
	//不同用途的鉤子具體對象,比如說驗證密碼,驗證權限,統一加密等等,
	class action_hook_1{
		public function act(){
			echo "我來做第一件事";
		}
	}
	class action_hook_2{
		public function act(){
			echo "我來做第2件事";
		}
	}
	class action_hook_3{
		public function act(){
			echo "我來做第3件事";
		}
	}
//需要綁定鉤子的具體對象
class Ball{
	public function down(){
		echo '我需要做一些通用的驗證工作';
		//註冊事件,這裏就可以加載相應的鉤子類,因爲在一個文件,直接使用
		$hook = new Hook();
		$hook->add("action_hook_1");
		$hook->add("action_hook_2");
		$hook->add("action_hook_3");
		$hook->exec();
	}
	//淡然也可以註冊完就直接執行鉤子,也可以寫到單獨方法
	public function exec(){
	}
}

$ball = new Ball();
$ball->down();

估計看完這個你應該可以理解八九不離十。不理解也沒關係,這並不影響你的程序書寫,過段時間 再看估計就懂了,不着急。


然後TP裏面的Hook類我也附錄在後面,方法都是通用的listen,add註冊,run執行,然後在相應的命名空間寫下你的鉤子想要實現的具體代碼。然後TP主要是利用配置文件來進行說明各個文件的關係,這個是很不錯,也是需要學習,也是很新手容易困惑的地方。(這件事我想當推薦看手冊,不用看雜七亂八的文章TP5.0的行爲在擴展裏面)思路就是這樣了。


附錄:TP  hook類

<?php
// +----------------------------------------------------------------------
// | ThinkPHP [ WE CAN DO IT JUST THINK ]
// +----------------------------------------------------------------------
// | Copyright (c) 2006~2017 http://thinkphp.cn All rights reserved.
// +----------------------------------------------------------------------
// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
// +----------------------------------------------------------------------
// | Author: liu21st <[email protected]>
// +----------------------------------------------------------------------

namespace think;

class Hook
{

    private static $tags = [];

    /**
     * 動態添加行爲擴展到某個標籤
     * @param string    $tag 標籤名稱
     * @param mixed     $behavior 行爲名稱
     * @param bool      $first 是否放到開頭執行
     * @return void
     */
    public static function add($tag, $behavior, $first = false)
    {
        isset(self::$tags[$tag]) || self::$tags[$tag] = [];
        if (is_array($behavior) && !is_callable($behavior)) {
            if (!array_key_exists('_overlay', $behavior) || !$behavior['_overlay']) {
                unset($behavior['_overlay']);
                self::$tags[$tag] = array_merge(self::$tags[$tag], $behavior);
            } else {
                unset($behavior['_overlay']);
                self::$tags[$tag] = $behavior;
            }
        } elseif ($first) {
            array_unshift(self::$tags[$tag], $behavior);
        } else {
            self::$tags[$tag][] = $behavior;
        }
    }

    /**
     * 批量導入插件
     * @param array        $tags 插件信息
     * @param boolean     $recursive 是否遞歸合併
     */
    public static function import(array $tags, $recursive = true)
    {
        if ($recursive) {
            foreach ($tags as $tag => $behavior) {
                self::add($tag, $behavior);
            }
        } else {
            self::$tags = $tags + self::$tags;
        }
    }

    /**
     * 獲取插件信息
     * @param string $tag 插件位置 留空獲取全部
     * @return array
     */
    public static function get($tag = '')
    {
        if (empty($tag)) {
            //獲取全部的插件信息
            return self::$tags;
        } else {
            return array_key_exists($tag, self::$tags) ? self::$tags[$tag] : [];
        }
    }

    /**
     * 監聽標籤的行爲
     * @param string $tag    標籤名稱
     * @param mixed  $params 傳入參數
     * @param mixed  $extra  額外參數
     * @param bool   $once   只獲取一個有效返回值
     * @return mixed
     */
    public static function listen($tag, &$params = null, $extra = null, $once = false)
    {
        $results = [];
        $tags    = static::get($tag);
        foreach ($tags as $key => $name) {
            $results[$key] = self::exec($name, $tag, $params, $extra);
            if (false === $results[$key]) {
                // 如果返回false 則中斷行爲執行
                break;
            } elseif (!is_null($results[$key]) && $once) {
                break;
            }
        }
        return $once ? end($results) : $results;
    }

    /**
     * 執行某個行爲
     * @param mixed     $class 要執行的行爲
     * @param string    $tag 方法名(標籤名)
     * @param Mixed     $params 傳人的參數
     * @param mixed     $extra 額外參數
     * @return mixed
     */
    public static function exec($class, $tag = '', &$params = null, $extra = null)
    {
        App::$debug && Debug::remark('behavior_start', 'time');
        $method = Loader::parseName($tag, 1, false);
        if ($class instanceof \Closure) {
            $result = call_user_func_array($class, [ & $params, $extra]);
            $class  = 'Closure';
        } elseif (is_array($class)) {
            list($class, $method) = $class;

            $result = (new $class())->$method($params, $extra);
            $class  = $class . '->' . $method;
        } elseif (is_object($class)) {
            $result = $class->$method($params, $extra);
            $class  = get_class($class);
        } elseif (strpos($class, '::')) {
            $result = call_user_func_array($class, [ & $params, $extra]);
        } else {
            $obj    = new $class();
            $method = ($tag && is_callable([$obj, $method])) ? $method : 'run';
            $result = $obj->$method($params, $extra);
        }
        if (App::$debug) {
            Debug::remark('behavior_end', 'time');
            Log::record('[ BEHAVIOR ] Run ' . $class . ' @' . $tag . ' [ RunTime:' . Debug::getRangeTime('behavior_start', 'behavior_end') . 's ]', 'info');
        }
        return $result;
    }

}


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章