ThinkPHP 5ソース学習編--Hook.php

11277 ワード

Hookクラス解析
TP 5のソースコードを学習する過程で、よくHook::listen()を実行する場所があり、もともとTP 5だった行為の拡張を調べると、アプリケーションが定義されたラベルを実行すると、いくつかの共通の論理を実行することができます.AOP(フェースプログラミング向け)についてはあまり知られていませんが、Javaの実装では、プロファイルを通じて、非常に自由に前置、後置、および囲まれるべき実行方法を決定することしか知られていません.この点で,TP 5の挙動はAOP機能に似ているが,実現度と完全性はやや不十分である.
Hookの機能は、追加、傍受、実行の3つに分けることができ、内容は多くありませんが、うまく利用すればビジネス開発の効率を高めることができるので、検討する必要があります.
Hookクラスのメソッドリストから入手
ラベルの追加動作の拡張
    /**
     *              
     * @access public
     * @param  string $tag          
     * @param  mixed  $behavior     
     * @param  bool   $first            
     * @return void
     */
    public static function add($tag, $behavior, $first = false)
    {
        //         behavior
        //          $tag,      $tag          
        isset(self::$tags[$tag]) || self::$tags[$tag] = [];

        if (is_array($behavior) && !is_callable($behavior)) {
            //     :
            /*
            [
                'app\\index\\behavior\\Test',
                'app\\index\\behavior\\Test2'
            ]
             *         
             * */

            //    _overlay         true
            if (!array_key_exists('_overlay', $behavior) || !$behavior['_overlay']) {
                //     
                unset($behavior['_overlay']);
                self::$tags[$tag] = array_merge(self::$tags[$tag], $behavior);
            } else {
                //_overlay  true,     
                unset($behavior['_overlay']);
                self::$tags[$tag] = $behavior;
            }
        } elseif ($first) {
            //array_unshift —               
            //       
            array_unshift(self::$tags[$tag], $behavior);
        } else {
            //         
            self::$tags[$tag][] = $behavior;
        }
    }

①isset()と‖を組み合わせてラベル配列を初期化する
②$behaviorのパラメータタイプを区別して異なる効果を実現
$behaviorは配列フォーマットであり、is_を呼び出すことはできません.callable()現在呼び出せる4つの状況が発見されました
  • 閉包関数
  • $callback = function () {
      return '123';
    };
    var_export(is_callable($callback));
    //    :true
    
  • 関数
  • function func()
    {
        return '123';
    }
    var_export(is_callable('func'));
    //    :true
    
  • クラスメソッド
  • class Obj
    {
        public function func()
        {
            return '123';
        }
    }
    $obj = new Obj();
    $param = [$obj, 'func'];
    var_export(is_callable($param));
    //    :true
    
  • 類静的方法
  • class Obj
    {
        public static function func()
        {
            return '123';
        }
    }
    $param = ['Obj', 'func'];
    var_export(is_callable($param));
    //    :true
    

    ③ラベル付与方式判断以上の判断を経て、$behaviorが配列フォーマットに入力されたと判断し、TP 5挙動開発準則に従い、以下の例を挙げる
    [
        'app\\index\\behavior\\Test',
        'app\\index\\behavior\\Test2'
    ]
    

    存在するか否かを判断するoverlayキー、キー値が真であるかどうか(trueなど)overlayキーは,ラベル動作を定義した上で,追加するか上書きするか,ソースコードに視線を戻すかをユーザに決定させるために,理解しやすい.
    さらに$first変数の判断です.デフォルトはfalseで、ラベル配列の末尾に新しい動作が追加されます.trueが入力されるとarray_unshift()配列ヘッダを挿入します.
    ちなみに,この4つの関数はPHPの配列にスタックとキューの構造を実現させる.array_unshift()、array_shift()、array_push()、array_pop()他から見てPHPが推奨されている点は文字列や配列の処理が極めて便利であることが認められているので,文字列や配列の処理関数をよく研究し,完全に把握する必要がある.
    一括インポート
        /**
         *       
         * @access public
         * @param  array   $tags          
         * @param  boolean $recursive       
         * @return void
         */
        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;
            }
        }
    

    まず、再帰的なマージを見てみましょう.つまり、2 D配列を分割し、前述したadd()メソッドを呼び出して、単一の動作をラベルにバインドします.2つの配列のマージ、配列間のプラス記号処理、array_merge()関数による配列オーバーライドの優先的な違いは見応えがある.
    ラベルの動作リストの取得
        /**
         *       
         * @access public
         * @param  string $tag     (      )
         * @return array
         */
        public static function get($tag = '')
        {
            if (empty($tag)) {
                return self::$tags;
            }
            return array_key_exists($tag, self::$tags) ? self::$tags[$tag] : [];
        }
    

    リスニングラベル
        /**
         *        
         * @access public
         * @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 = [];
    
            //             
            foreach (static::get($tag) as $key => $name) {
                //      ,          
                $results[$key] = self::exec($name, $tag, $params, $extra);
    
                //      false,                  
                if (false === $results[$key] || (!is_null($results[$key]) && $once)) {
                    break;
                }
            }
            //end()           
            return $once ? end($results) : $results;
        }
    

    いよいよ冒頭のHook::listen()メソッドへ.
    パラメータ:$tagラベル名$paramsはパラメータを参照し、動作内部で値を直接操作できます$extra追加パラメータ$onceは、動作実行が正常に有効値を返したときに、後続の動作の実行を中止するかどうかを決定します.
    foreachループを直接観察し、get()メソッドで1つのラベル内のすべての対応する動作を取得し、exec()メソッドを1つずつ呼び出して実行し、結果を$result配列に格納します.またif判定が追加されていることに注意してください.動作実行後にfalseまたは値が有効で、1つの結果しか取得されない場合は、後続の呼び出し対象動作の実行を中止し、最後に動作実行後のreturn結果の取得を行い、$onceを識別選択呼び出しend()関数として配列の最後の値を取得するか、配列を返します.
    行為執行過程の秘密を明らかにする
         /**
         *       
         * @access public
         * @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');
    
            //tag                 
            $method = Loader::parseName($tag, 1, false);
    
            if ($class instanceof \Closure) {
                //        ,  call_user_func_array()  
                $result = call_user_func_array($class, [ & $params, $extra]);
                //    
                $class  = 'Closure';
            } elseif (is_array($class)) {
                // class      
                //['\\app\\index\\behavior\\Test', 'myInit1']
                //   Test ,  myInit1  
                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, '::')) {
                //'\\app\\index\\behavior\\Test::myInit2'
                $result = call_user_func_array($class, [ & $params, $extra]);
            } else {
                $obj    = new $class();
                /*
                class Test
                {
                    public function appInit(&$params)
                    {
    
                    }
    
                    public function appEnd(&$params)
                    {
    
                    }
                }
                 *       ,   app_init,    appInit  ,      
                 * */
                $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;
        }
    

    注釈説明の比較で分かりました.細かい点分析はexec()メソッドを3つに分割することです.
    デバッグモードで動作開始実行時間記録実行動作論理デバッグモードで動作実行持続時間記録
    実行のいくつかの可能性を見てみましょう.
  • 閉包関数
  • Hook::add('callback', function (){
        return 'success';
    });
    var_export(Hook::listen('callback'));
    //    :array ( 0 => 'success', )
    

    2番目の閉パッケージの追加を続行
    Hook::add('callback', function (){
        return 'success';
    });
    Hook::add('callback', function (){
        return 'second success';
    });
    var_export(Hook::listen('callback'));
    //    :array ( 0 => 'success', 1 => 'second success', )
    

    2つの実行結果を比較して、1つのラベルが複数の動作に対応する意味を理解します.
  • クラス名およびメソッド名
  • が配列形式で入力される
    class Test
    {
        public function myInit1(&$param)
        {
            return 'myInit1';
        }
    }
    --------------------------------------
    $tags = ['\\app\\index\\behavior\\Test', 'myInit1'];
    Hook::add('HookTest', $tags);
    var_export(Hook::listen('HookTest'));
    //    :array ( 0 => 'myInit1', )
    
  • は、インスタンス化するオブジェクトを入力し、ラベル署名を方法として
  • を実行する.
    class Test
    {
        public function HookTest(&$param)
        {
            return 'HookTest';
        }
    }
    --------------------------------------
    $test = new Test();
    Hook::add('HookTest', $test);
    var_export(Hook::listen('HookTest'));
    //    :array ( 0 => 'HookTest', )
    
  • 静的方法
  • class Test
    {
        public static function staticHook(&$param)
        {
            return 'staticHook';
        }
    }
    --------------------------------------
    $static = '\\app\\index\\behavior\\Test::staticHook';
    Hook::add('HookTest', $static);
    var_export(Hook::listen('HookTest'));
    //    :array ( 0 => 'staticHook', )
    
  • タグ名はタグとして
  • を実行する.
    class Test
    {
        public function run(&$param)
        {
            return 'run';
        }
    }
    --------------------------------------
    Hook::add('HookTest', '\\app\\index\\behavior\\Test');
    var_export(Hook::listen('HookTest'));
    //    :array ( 0 => 'run', )
    ++++++++++++++++++++++++++++++++++++++
    class Test
    {
        public function hookTest(&$param)
        {
            return 'HookTest';
        }
    }
    --------------------------------------
    Hook::add('HookTest', '\\app\\index\\behavior\\Test');
    var_export(Hook::listen('HookTest'));
    //    :array ( 0 => 'HookTest', )
    

    実行結果とソースコードを参照して、ラベルを傍受し、ラベルに対応するメソッドがある場合はそのメソッドを実行し、そうでない場合はrun()を実行します.このメソッドについては、定義名はアルパカ法コマンドに従い、実行前にLoader::parseName()変換を使用します.
    まとめ
    Hookの機能はここまでで、個人的にはこの機能がソースコードからはっきりと展示されていると感じて、使用も便利で、私たちが学ぶ価値があります.別のことを言うと、動作を実行する方法では、$paramsと$extra変数が入力されますが、動作の実際の定義にはこの2つのパラメータが含まれていなくても面白いです.
    参照コード:
    public function index()
    {
        $this->test(null, null);
    }
    public function test()
    {
        echo 'text';
    }
    public function test($arg1)
    {
        echo 'text';
    }
    public function test($arg1, $arg2)
    {
        echo 'text';
    }
    

    以上の3つのtest()メソッドは正常に実行できますが、変調メソッドのパラメータが多くなるとだめです.
    public function index()
    {
        $this->test(null, null);
    }
    public function test($arg1, $arg2, $arg3)
    {
        echo 'text';
    }
    

    これは間違いを報告するだろう.