フックの理解と、Thinkphp で動作拡張を使用するためのフックの使用
個人的な理解: フックは、A が送信するときの「トラップ」または「リスナー」のようなものです。メッセージが B に到達すると、メッセージが宛先 B に到達していない場合は、フックによってインターセプトされ、処理のためにコードの一部が呼び出されます。コードのこの部分は、フック関数またはコールバック関数とも呼ばれます
オンラインの格言を参照してくださいたとえば、マウスで特定のウィンドウをダブルクリックしたり、特定のウィンドウに文字 A を入力したりする
その後、システムはウィンドウに「おい! 誰かにマウスをクリックするように頼んだのに、マウスを 2 回続けてクリックした。どうするつもりだ?
これらのイベントを最初に発見するのは、
」と言いました。窓に「おい! 誰かがあなたの家にレンガを投げた。信じられないなら、見てください。そのレンガは A です。
このとき、窓はいくつかのイベントを無視して反応します。」いくつかのイベント:
たとえば、マウス クリック イベントは無視される可能性があります。ウィンドウは次のように考えます。ダブルクリックしてください。すぐにアクションを実行し、色を表示します。
泥棒は、ここで特に情報を待っている可能性があります。泥棒は、知った後、事前に行動を起こすことがよくあります。窓は知っています。行動を起こしてください。
ここのウィンドウで実行されるアクションは、事前に記述したイベントです。
Windows 用語では、ウィンドウ イベントと呼ばれます。
しかし、多くの場合、システムはウィンドウに通知する必要があります。 「言葉」(メッセージ)は、他の男(例えば、泥棒)に事前に聞こえている可能性があります!そして、この泥棒はニュースごとに異なる行動計画を立てます。そして、その行動計画は通常、事前に準備されています。
要約: この「泥棒」は、設定したいフックです。フック関数、またはフック関数のコールバック。
もちろん、この泥棒は、すべてのニュースに興味があるわけではありません。興味があるかどうかに関係なく、対応するアクション プランを策定する必要はありません。
上記段落への元のリンク http://www.cnblogs.com/del/archive/2008/02/25/1080825.html
一般 言い換えると、フックは関数に取り付けられた「マウント ポイント」のようなもので、関数の実行中に「マウント ポイント」に遭遇すると、マウント ポイント (フック) がフックを引き出します。コードの一部。たとえば、いくつかのパラメータをスクリプトに渡してから挿入操作を実行します。挿入の前に、関数がこのフックを実行するときにフック (before_insert) を使用して調整します。フック関数のこの部分では、必要な操作を実現するためにデータを検証または処理できます。
フックを使用する理由
個人的には、フック関数は、関数内で別の関数を直接呼び出すよりも安全で便利だと思います。拡張関数を変更する必要がある場合、関数 B のフックを変更する必要はなく、関数 A を直接変更する場合は、関数 B が配置されているクラスを変更するだけで済みます。変更されました。閉鎖の原則に違反します。もう 1 つのポイントは、フックを使用した方が、後のメンテナンスや機能拡張に便利であることです。
Thinkphp でのフック (動作拡張) の使用
動作
Behavior(動作) は、アプリケーションの実行中のアクションまたはプロセスとして想像できます。たとえば、ルーティングの検出など、さまざまな場所で動作が生成されます。 Web サイトのユーザーに訪問するたびに、Hello, world! が最優先で表示されます。これらは一種の動作と考えることができます。 動作の存在により、フレームワークやアプリケーションを変更することなく、拡張や周辺設定を通じて一部の機能を変更または追加することができます。 さまざまな動作には位置の類似性もあります。たとえば、一部の動作はアプリケーションの実行前に適用され、一部の動作はテンプレートの出力後に適用されます。アプリケーションがこのタグを実行すると、そのタグがインターセプトされ、関連する動作が均一に実行されます。
ここでの動作は、フック関数と同等です。それを使用するには、Hook
を使用して動作拡張機能を追加する必要があります
<code class=" hljs java"> <span class="hljs-javadoc">/** * 运行应用实例 入口文件使用的快捷方法 *<span class="hljs-javadoctag"> @access</span> public *<span class="hljs-javadoctag"> @return</span> void */</span> <span class="hljs-keyword">static</span> <span class="hljs-keyword">public</span> function <span class="hljs-title">run</span>() { <span class="hljs-comment">// 应用初始化标签</span> Hook::listen(<span class="hljs-string">'app_init'</span>); App::init(); <span class="hljs-comment">// 应用开始标签</span> Hook::listen(<span class="hljs-string">'app_begin'</span>); <span class="hljs-comment">// Session初始化</span> <span class="hljs-keyword">if</span>(!IS_CLI){ session(C(<span class="hljs-string">'SESSION_OPTIONS'</span>)); } <span class="hljs-comment">// 记录应用初始化时间</span> G(<span class="hljs-string">'initTime'</span>); App::exec(); <span class="hljs-comment">// 应用结束标签</span> Hook::listen(<span class="hljs-string">'app_end'</span>); <span class="hljs-keyword">return</span> ; }</code>
其中里面的Hook::listen(‘app_init’),Hook::listen(‘app_begin’); 相当于钩子监听的这些tags(这几个tags是系统内置的行为,无需再注册)。要触发自定义行为,必须先注册行为,ThinkPHP中提供了自动注册和手动注册 。
TP里面Lib\Think目录下面Think.class.php中有这两行代码
<code class=" hljs php"> <span class="hljs-comment">// 加载模式行为定义</span> <span class="hljs-keyword">if</span>(<span class="hljs-keyword">isset</span>(<span class="hljs-variable">$mode</span>[<span class="hljs-string">'tags'</span>])) { Hook::import(is_array(<span class="hljs-variable">$mode</span>[<span class="hljs-string">'tags'</span>])?<span class="hljs-variable">$mode</span>[<span class="hljs-string">'tags'</span>]:<span class="hljs-keyword">include</span> <span class="hljs-variable">$mode</span>[<span class="hljs-string">'tags'</span>]); } <span class="hljs-comment">// 加载应用行为定义</span> <span class="hljs-keyword">if</span>(is_file(CONF_PATH.<span class="hljs-string">'tags.php'</span>)) <span class="hljs-comment">// 允许应用增加开发模式配置定义</span> Hook::import(<span class="hljs-keyword">include</span> CONF_PATH.<span class="hljs-string">'tags.php'</span>); </code>
模式行为是系统内置的,我们可以在Lib\Mode\common.php找到
<code class=" hljs php"> <span class="hljs-comment">// 行为扩展定义</span> <span class="hljs-string">'tags'</span> => <span class="hljs-keyword">array</span>( <span class="hljs-string">'app_init'</span> => <span class="hljs-keyword">array</span>( <span class="hljs-string">'Behavior\BuildLiteBehavior'</span>, <span class="hljs-comment">// 生成运行Lite文件</span> ), <span class="hljs-string">'app_begin'</span> => <span class="hljs-keyword">array</span>( <span class="hljs-string">'Behavior\ReadHtmlCacheBehavior'</span>, <span class="hljs-comment">// 读取静态缓存</span> ), <span class="hljs-string">'app_end'</span> => <span class="hljs-keyword">array</span>( <span class="hljs-string">'Behavior\ShowPageTraceBehavior'</span>, <span class="hljs-comment">// 页面Trace显示</span> ), <span class="hljs-string">'view_parse'</span> => <span class="hljs-keyword">array</span>( <span class="hljs-string">'Behavior\ParseTemplateBehavior'</span>, <span class="hljs-comment">// 模板解析 支持PHP、内置模板引擎和第三方模板引擎</span> ), <span class="hljs-string">'template_filter'</span>=> <span class="hljs-keyword">array</span>( <span class="hljs-string">'Behavior\ContentReplaceBehavior'</span>, <span class="hljs-comment">// 模板输出替换</span> ), <span class="hljs-string">'view_filter'</span> => <span class="hljs-keyword">array</span>( <span class="hljs-string">'Behavior\WriteHtmlCacheBehavior'</span>, <span class="hljs-comment">// 写入静态缓存</span> ), ),</code>
用户自定义的行为扩展,需要在CONF_PATH.’tags.php’也就是/Common/conf/tags.php自行创建,举例
<code class=" hljs xml"><span class="php"><span class="hljs-preprocessor"><?php</span> <span class="hljs-keyword">return</span> <span class="hljs-keyword">array</span>( <span class="hljs-string">"action_begin"</span> => <span class="hljs-keyword">array</span>(<span class="hljs-string">'Home\\Behaviors\\testBehavior'</span>) <span class="hljs-comment">//一个标签可以有多个行为,我们也可以这样</span> <span class="hljs-string">"action_begin"</span> => <span class="hljs-keyword">array</span>(<span class="hljs-string">'Home\Behaviors\test1Behavior'</span>,<span class="hljs-string">'Home\\Behaviors\\test2Behavior'</span>)); <span class="hljs-preprocessor">?></span></span></code>
TP在运行时为自动加载这些行为。我们只需写好自己的行为扩展,然后在某个地方监听(Hook::listen(tags)),就可以触发这些行为了
说到这,我们还是先看下Lib\Think\Hook.class.php的代码
<code class=" hljs php"> <span class="hljs-comment">/** * 动态添加插件到某个标签 *<span class="hljs-phpdoc"> @param</span> string $tag 标签名称 *<span class="hljs-phpdoc"> @param</span> mixed $name 插件名称 *<span class="hljs-phpdoc"> @return</span> void */</span> <span class="hljs-keyword">static</span> <span class="hljs-keyword">public</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">add</span><span class="hljs-params">(<span class="hljs-variable">$tag</span>,<span class="hljs-variable">$name</span>)</span> {</span> <span class="hljs-keyword">if</span>(!<span class="hljs-keyword">isset</span>(<span class="hljs-keyword">self</span>::<span class="hljs-variable">$tags</span>[<span class="hljs-variable">$tag</span>])){ <span class="hljs-keyword">self</span>::<span class="hljs-variable">$tags</span>[<span class="hljs-variable">$tag</span>] = <span class="hljs-keyword">array</span>(); } <span class="hljs-keyword">if</span>(is_array(<span class="hljs-variable">$name</span>)){ <span class="hljs-keyword">self</span>::<span class="hljs-variable">$tags</span>[<span class="hljs-variable">$tag</span>] = array_merge(<span class="hljs-keyword">self</span>::<span class="hljs-variable">$tags</span>[<span class="hljs-variable">$tag</span>],<span class="hljs-variable">$name</span>); }<span class="hljs-keyword">else</span>{ <span class="hljs-keyword">self</span>::<span class="hljs-variable">$tags</span>[<span class="hljs-variable">$tag</span>][] = <span class="hljs-variable">$name</span>; } }</code>
代码十分简单,我们注册行为只需要Hook::add(tags,name)
注意:这里面name必须是一个包含命名空间路径的类,比如Home\Behavior\testBehavior ,类名后面必须是Behavior结尾,类中必须实现run方法。原因:请看Hook类中代码
<code class=" hljs php"> <span class="hljs-comment">/** * 执行某个插件 *<span class="hljs-phpdoc"> @param</span> string $name 插件名称 *<span class="hljs-phpdoc"> @param</span> string $tag 方法名 *<span class="hljs-phpdoc"> @param</span> Mixed $params 传入的参数 */</span> <span class="hljs-keyword">static</span> <span class="hljs-keyword">public</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">exec</span><span class="hljs-params">(<span class="hljs-variable">$name</span>, <span class="hljs-variable">$tag</span>,&<span class="hljs-variable">$params</span>=NULL)</span> {</span> <span class="hljs-comment">//这里截取后八位类名字符串,所以必须是Behavior</span> <span class="hljs-keyword">if</span>(<span class="hljs-string">'Behavior'</span> == substr(<span class="hljs-variable">$name</span>,-<span class="hljs-number">8</span>) ){ <span class="hljs-comment">// if('testBehavior' == substr($name,-12)</span> <span class="hljs-comment">// {</span> <span class="hljs-comment">// $tag = 'test';</span> <span class="hljs-comment">// }</span> <span class="hljs-comment">// 行为扩展必须用run入口方法</span> <span class="hljs-variable">$tag</span> = <span class="hljs-string">'run'</span>; } <span class="hljs-keyword">echo</span> <span class="hljs-variable">$name</span>.<span class="hljs-string">'<br/>'</span>; <span class="hljs-variable">$addon</span> = <span class="hljs-keyword">new</span> <span class="hljs-variable">$name</span>(); <span class="hljs-keyword">return</span> <span class="hljs-variable">$addon</span>-><span class="hljs-variable">$tag</span>(<span class="hljs-variable">$params</span>); }</code>
当然,你也可以修改规则,比如我不想以Behavior为类名结尾,也不想调用run方法,这时候想修改只能在listen()方法里面进行判断修改,如果直接在exec()里面修改,立马报错,因为系统的内置行为都是按这个规则来的啊!除非你把源码中的行为类名和行为方法run改掉,当然我相信你不会这么傻
- 触发行为的关键方法是Hook类中的listen方法,它通过遍历某个行为标签下的所有行为,依次实例化并调用run方法
- listen方法中,如果之前在配置文件中开启了DEBUG模式,则它会生成日志记录你的行为,这里面牵涉到很多的IO操作,所以你的项目完成时建议取消DEBUG模式以提升速度
- listen方法中,允许传递参数且只允许传递一个参数(传多个可以用数组呢),不过这个参数是引用传值,所以只能传入变量,传入常量会报错
- 最后,Lib\Think\Behavior.class.php,这个抽象类中只有一个抽象方法run(),在你的自己行为扩展中建议继承它,尽管这不是必须的,但是这样更加规范。。。不过TP内置的系统行为都没继承这个抽象类,也不知道闹哪样
- 自动注册(Common/Conf/tags.php按格式自己添加),或者 手动注册(类中方法如初始方法,调用Hook::add(tags,name));
- 写好自己的行为类,类名以Behavior结尾,实现run方法
- 在需要添加行为的函数里 ,直接Hook::Listen(tags,prarm),注意一定要传变量,不需要传常量。
这样,整个过程就结束了,下面举个例子
鄙人最近写了个人网站(小博客) http://www.huangtianer.com/
我需要记录一下网站的PV,于是我在BaseController里面的初始化方法
<code class="language-php hljs "><span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">BaseController</span> <span class="hljs-keyword">extends</span> <span class="hljs-title">Controller</span>{</span> <span class="hljs-keyword">public</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">_initialize</span><span class="hljs-params">()</span> {</span> <span class="hljs-comment">//记录访问</span> <span class="hljs-comment">//这里我是手动注册的行为</span> Hook::add(<span class="hljs-string">'mark_pv'</span>,<span class="hljs-string">'Home\Behaviors\testBehavior'</span>); hook::listen(<span class="hljs-string">'mark_pv'</span>); }}</code>
然后我再Home\Behaviors\testBehavior.class.php中
<code class=" hljs php"><span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">testBehavior</span> <span class="hljs-keyword">extends</span> <span class="hljs-title">Behavior</span> {</span> <span class="hljs-keyword">public</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">run</span><span class="hljs-params">(&<span class="hljs-variable">$param</span>)</span> {</span> <span class="hljs-comment">//记录数据</span> <span class="hljs-variable">$data</span>[<span class="hljs-string">'client_ip'</span>] = get_client_ip(); <span class="hljs-variable">$data</span>[<span class="hljs-string">'page_view'</span>] = CONTROLLER_NAME.<span class="hljs-string">'/'</span>.ACTION_NAME; <span class="hljs-variable">$data</span>[<span class="hljs-string">'create_time'</span>] = time(); <span class="hljs-variable">$data</span>[<span class="hljs-string">'status'</span>] = <span class="hljs-number">0</span>; M(<span class="hljs-string">'page_view'</span>)->add(<span class="hljs-variable">$data</span>); }}</code>
运行后正常插入数据,而且如果后期我的个人网站数据库由于访问量太大(呵呵)顶不住压力,不能再记录PV了,我直接删除钩子函数里面的代码,不需要动钩子,就解决了问题,比较方便。
结束语:如有BUG或者建议,欢迎指正
版权声明:本文为博主原创文章,未经博主允许不得转载。