当前位置:网站首页>TP5 automatic registration hook mechanism hook extension, with a complete case
TP5 automatic registration hook mechanism hook extension, with a complete case
2022-07-04 01:20:00 【Programmer base camp】
There are two ways to use hook mechanism , notes : You can only choose one of them :
1、 use thinkphp5 Built in hook call scheme . Configuration parameters jntoo_hook_call by true It works only when , For compatibility, the use of hook mechanism has been written
2、 Adopt its own hook calling scheme , This method supports hook classification , Hook weight automatic sorting
Personal not recommended think Built in hook as an extension of the system .
How to write auto load file :
Create a new one under the module hook Catalog , And create... In the directory Category.php file , Here's the picture :
The class name is equal to the hook type , Method name equals hook name .( notes : Types only exist in hook mechanism methods 2. Method 1 Only method name takes effect )
As shown in the figure :
We created Category Type of hook
There are two ways :
index and index_5
Method 1 Call method of :\think\Hook::listen('index'); You can directly call Category class index Method .
Output results :
Method 2 Call method of :\app\common\Hook::call('Category','index'); You can call Category class index Methods and index_5 Method
Output results :
Method 2 Why can call Category.index Hook will call Category.index_5 This hook ? We will explain this later .
Tell me a little story
One day, the customer said that he wanted to implement a member registration module .
I've finished the registration module of code members after typing for half a day .
The next day the customer said “ Now there is no problem with customer registration , I wonder if I can add it to him after registration 50 integral , So that he can make some consumption .”, I said, “ That's all right. ”
After knocking on the code for half a day, I completed the customer's requirements . Now after registering for membership , Free 50 I've got points .
On the third day, my colleague asked me if you did not get a registration module and give points in the system two days ago ? I said yes . Colleagues said “ Submit this to you svn library , I also have a customer who wants to get a registration module and give away points , Also add send mail verification ”, I :“ well , But you should add email verification yourself .” Code commit to svn library ,
My colleague knocked the code for a long time , Finished sending email .
On the fourth day, I asked my colleague to submit SVN library , Let me synchronize the code .
On the fifth day, another customer asked me “ Does your registration module have a member registration verification mobile phone ”, Of course, I can only say that there is ( In fact, there is no SMS authentication ), Customer said ” Can you set up the environment for me to test ?“, I can only say :“ Sorry, sir , We haven't installed the environment yet , I'll fix this for you tomorrow , You are testing ”, Customer said “ good ”
It took a long time to add the member registration mobile phone verification to the member registration module .
On the sixth day, the customer thought it was very good .
Here's the story .
Suppose the formation of the above member registration module is such a code fragment :
namespace app\index\controller;
class member{
function register($username , $password , $tel , $email){
// The code added on the fifth day :
if( Whether SMS verification )
VerifTel($tel , Verification Code );
// Code written on day one :
if(table('member')->where('username' , $username)->count())
{
$this->error = ' User name already exists '
return false;
}
$uid = Write to the database to get new users UID
.... Omit most of the code
// The code added the next day :
if( Bonus points )
sendPoints($uid . Bonus points ); // Send points 50
// On the third day
if( Send E-mail )
sendEmail($uid , mail );
return true;
}
}
As we can see from the above , When you finish writing a function module , I don't know the situation of some customers' needs , We will add code to the current module .
If you keep on adding , When you look back at the code, you will find that you have too much code .
Can this code become more elegant
What if we add a hook mechanism to this code ?
Look at the code below
namespace app\index\controller;
class member{
function register($username , $password , $tel , $email){
// I don't know what it will be like in the future , First add a front hook , Parameters :$username,$password,$tel,$email
$param = [ $username,$password,$tel,$email ];
\app\common\Hook::call('member','registerPre',$param);
// Code written on day one :
if(table('member')->where('username' , $username)->count())
{
$this->error = ' User name already exists '
return false;
}
$uid = Write to the database to get new users UID
.... Omit most of the code
// I don't know what it will be like in the future , Add a hook after successful registration , Parameters $uid
$param = [ $uid ];
\app\common\Hook::call('member','registerSuccess',$param);
return true;
}
}
So the problem is how to solve the hook problem ?
Suppose our classes have been registered in the hook :
Let's finish the next day , The following code :
namespace app\index\hook;
class member{
// Hook type , Hang the member Type hook , Medium registerSuccess hook
function registerSuccess($uid)
{
if( Bonus points )
sendPoints($uid . Bonus points ); // Send points 50
}
}
What should my colleagues do with my code on the third day ? Then I will tell him :“ I have put two hooks here , One is to register the front hook , One is the hook after successful registration ” And tell him the type of hook , name , There are also corresponding parameters , Colleagues' code will become like this
namespace app\tongshi\hook;
class member{
// Hook type , Hang the member Type hook , Medium registerSuccess hook
function registerSuccess($uid)
{
if( Send E-mail )
sendEmail($uid , mail );
}
}
On the fifth day, it was like this :
namespace app\index\hook;
class member{
// Hang the member Type hook , Medium registerSuccess hook
function registerSuccess($uid)
{
if( Bonus points )
sendPoints($uid . Bonus points ); // Send points 50
}
// Hang the member Type hook registerPre hook
function registerPre()
{
if( Whether SMS verification )
VerifTel($tel , Verification Code );
}
}
Back to what we said above “ Why can call Category.index Hook will call Category.index_5 This hook ?”
When designing the hook mechanism, I think that hooks may have a sequence , And designed a number that can write weight sorting , Add a number at the end , Sort from small to large , The actual hook registration will automatically delete the tail “_ Numbers ”:
One : Let's talk about the hook mechanism first
In the project code , You think we need to expand ( Don't expand for the time being ) Place a hook function in place of , When you need to expand , Attach the classes and functions to this hook , You can expand .
That's the idea. It sounds general .
When redeveloping the code written by others , If there is a hook mechanism , The author will tell you where to hook , When you expand, you can upgrade and expand without changing the original code .
This plug-in extends the configuration parameter information :
1、jntoo_hook_cache
Boolean value , Whether to enable hook compilation cache , You only need to compile once after opening , In the future, it will become lazy loading , If a new hook is installed , You need to call Hook::clearCache() Clear cache
2、jntoo_hook_call
Boolean value , Whether to use think Hook system . If the value is true, use think After the hook , You will not be able to use weight sorting and hook automatic classification , Only the methods in the class will be automatically registered to think Hook mechanism
3、jntoo_hook_path
Under a folder PHP Files automatically register hooks for them
Configuration to realize
jntoo_hook_path => [
[
'path'=>' Your path ', // A slash must be added at the end of the path "/"
'pattern'=> ' The rules , Class matching rules ' for example :'/plugin\\\\module\\\\hook\\\\([0-9a-zA-Z_]+)/'
],
....
]
4、jntoo_hook_plugin
Automatic compilation of multi module directories , Add hook Catalog , Under this directory php The file will automatically register the hook
Configuration to realize :
'jntoo_hook_plugin' => [
[
'path'=>' Yours app The module path ',
'pattern'=> ' The rules , Class matching rules ' for example :'/plugin\\\\([0-9a-zA-Z_]+)\\\\hook\\\\([0-9a-zA-Z_]+)/'
],
....
]
Please be there. application/tags.php
'app_init' Add :'\\app\\common\\Hook'
for example :
// Application behavior extension definition file
return [
// Application initialization
'app_init' => [
'\\app\\common\\Hook'
],
// App start
'app_begin' => [],
// Module initialization
'module_init' => [],
// The operation begins
'action_begin' => [],
// View content filtering
'view_filter' => [],
// Log write
'log_write' => [],
// End of application
'app_end' => [],
];
Please create your own file :
application\index\controller\Index.php Hook file
namespace app\index\controller;
use app\common\Hook;
use think\Hook AS thinkHook;
class Index
{
public function index()
{
Hook::call('Category' , 'index');
}
}
application\index\hook\Category.php Hook file
<?php
/**
* Created by PhpStorm.
* User: JnToo
* Date: 2016/11/12
* Time: 1:11
*/
namespace app\index\hook;
class Category
{
function index()
{
echo ' I am a Category Type hook index Method <br>';
}
function index_5()
{
echo ' I am a Category Type hook index Method , My weight is relatively low <br>';
}
}
application\common\hook.php Master file
<?php
/**
* Created by PhpStorm.
* User: JnToo
* Date: 2016/11/11
* Time: 22:57
*/
namespace app\common;
use think\Config;
use think\Hook as thinkHook;
use think\Cache;
/**
* Please be there. application/tags.php
* 'app_init' Add :'\\app\\common\\Hook'
* for example :
* // Application behavior extension definition file
return [
// Application initialization
'app_init' => [
'\\app\\common\\Hook'
],
// App start
'app_begin' => [],
// Module initialization
'module_init' => [],
// The operation begins
'action_begin' => [],
// View content filtering
'view_filter' => [],
// Log write
'log_write' => [],
// End of application
'app_end' => [],
];
* Class Hook
* @package app\common
*/
class Hook
{
/**
* Use counters when compiling hooks
* @var int
*/
static protected $index = 0;
/**
* Add reference count
* @var int
*/
static protected $indexAdd = 1;
/**
* Compiled hook list
* @var array
*/
static protected $hookList = array();
/**
* application/config.php Add the following configuration information to the file
* @var array
*/
static protected $default =[
// Whether to enable hook compilation cache , You only need to compile once after opening , In the future, it will become lazy loading , If a new hook is installed , You need to call Hook::clearCache() Clear cache
'jntoo_hook_cache'=>false,
// Whether the hook is used think Hook system
'jntoo_hook_call'=>false ,
/**
* Under a folder hook load , Configuration file method implementation
* jntoo_hook_path => [
* [
* 'path'=>' Your path ', // A slash must be added at the end of the path "/"
* 'pattern'=> ' The rules , Class matching rules ' for example :'/plugin\\\\module\\\\hook\\\\([0-9a-zA-Z_]+)/'
* ],
* ....
* ]
*/
'jntoo_hook_plugin'=>[],
/**
* Automatic search under multi module directory , Configuration file method implementation
* 'jntoo_hook_plugin' => [
* [,
* 'path'=>' Yours app The module path '
* 'pattern'=> ' The rules , Class matching rules ' for example :'/plugin\\\\([0-9a-zA-Z_]+)\\\\hook\\\\([0-9a-zA-Z_]+)/'
* ],
* ....
* ]
*/
'jntoo_hook_plugin'=>[],
];
/**
* Provide behavior calls
*/
public function run()
{
self::init();
}
/**
* Register hook
* @param $type Hook type
* @param $name Hook name
* @param $param \Closure|array
*/
static public function add($type , $name , $param , $listorder = 1)
{
$key = strtolower($type .'_'.$name);
isset(self::$hookList[$key]) or self::$hookList[$key] = [];
self::$hookList[$key][$listorder.'_'.self::$indexAdd++] = $param;
ksort(self::$hookList[$key]);
// compatible
if(Config::get('jntoo_hook_call'))
{
thinkHook::add($name , $param);
}
return;
}
/**
* Clear the cache of the compilation hook
*/
static public function clearCache()
{
// Compile hook cache clearly
if(Config::get('jntoo_hook_cache')){
cache('jntoo_hook_cache' , null);
}
}
/**
* Carry out the hook
* @param $type string
* @param $name string
* @param array $array
* @param mixe
*/
static public function call($type , $name , &$array = array())
{
static $_cls = array();
$ret = '';
if(Config::get('jntoo_hook_call')){
return thinkHook::listen($name , $array);
}else{
$key = strtolower($type.'_'.$name);
// Own call scheme
if(isset(self::$hookList[$key]))
{
foreach(self::$hookList[$key] as $r){
// Closure processing
$result = '';
if(is_callable($r)){
$result = call_user_func_array($r, $array);
}elseif(is_object($r)){
// Define your own object hook
if(method_exists($r , $name)){
$result = call_user_func_array(array($r , $name), $array);
}
}else{
// Automatically searched hooks
$class = $r['class'];
if(class_exists($class , false)){
// If it doesn't exist
if($r['filename'])require_once(ROOT_PATH.$r['filename']);
}
if(class_exists($class , false)){
if(!isset($_cls[$class])){
$_cls[$class] = new $class();
}
$func = $r['func'];
$result = call_user_func_array(array($_cls[$class] , $func), $array);
}
}
if($result)$ret.=$result;
}
}
}
return $ret;
}
/**
* Initialization hook
*/
static protected function init()
{
// Get the cache of the hook
self::$hookList = self::getCache();
if(!self::$hookList)
{
// Save in the current variable
$saveArray = [];
// The hook does not exist , First search app Module under directory
//echo APP_PATH;
//echo ROOT_PATH;
$result = self::searchDir(APP_PATH);
// Compile this module first
self::compileHook($result , '/app\\\\([0-9a-zA-Z_]+)\\\\hook\\\\([0-9a-zA-Z_]+)/' , $saveArray);
//print_r($saveArray);
// Multiple modules realize search loading
$jntooHook = Config::get('jntoo_hook_plugin');
if($jntooHook){
foreach($jntooHook as $t){
$result = self::searchDir($t['path']);
self::compileHook($result , $t['pattern'] , $saveArray);
}
}
// Single path module search
$jntooHook = Config::get('jntoo_hook_path');
if($jntooHook){
foreach($jntooHook as $t){
$result = [];
self::searchHook($t['path'] , $result);
self::compileHook($result , $saveArray);
}
}
// Compile the complete , Now let's do a weight sort
foreach($saveArray as $k=>$t){
ksort($saveArray[$k]);
}
self::setCache($saveArray);
self::$hookList = $saveArray;
}
//print_r(self::$hookList);
$calltype = Config::get('jntoo_hook_call');
// Detect his calling method , Need to register to think in , It is not recommended to register to think in ,
// Because this system contains the form of classification , After you register, you will not be able to use the sorting function
if($calltype){
// Sign up for think In the hook
self::registorThink();
}else{
// Register system behavior hook
self::registorCall();
}
}
/**
* Register system behavior calls
*/
static protected function registorCall()
{
thinkHook::add('app_init' , function( &$params = null ){
$arg = [&$params];
Hook::call('system' , 'app_init' , $arg);
});
thinkHook::add('app_begin' , function( &$params = null ){
$arg = [&$params];
Hook::call('system' , 'app_begin' , $arg);
});
thinkHook::add('module_init' , function( &$params = null ){
$arg = [&$params];
Hook::call('system' , 'module_init' , $arg);
});
thinkHook::add('action_begin' , function( &$params = null ){
$arg = [&$params];
Hook::call('system' , 'action_begin' , $arg);
});
thinkHook::add('view_filter' , function( &$params = null ){
$arg = [&$params];
Hook::call('system' , 'view_filter' , $arg);
});
thinkHook::add('app_end' , function( &$params = null ){
$arg = [&$params];
Hook::call('system' , 'app_end' , $arg);
});
thinkHook::add('log_write' , function( &$params = null ){
$arg = [&$params];
Hook::call('system' , 'log_write' , $arg);
});
thinkHook::add('response_end' , function( &$params = null ){
$arg = [&$params];
Hook::call('system' , 'response_end' , $arg);
});
}
/**
* Register the hook in thinkHook In the hook
*/
static protected function registorThink()
{
foreach(self::$hookList as $key=>$list)
{
foreach($list as $r){
thinkHook::add($r['func'] , $r['class']);
}
}
}
/**
* Search the hook file in the directory
* @param $path string
* @param $saveArray array Saved file path
* @return null
*/
static protected function searchHook( $path , &$saveArray)
{
$fp = opendir($path);
if($fp){
while($file = readdir($fp))
{
if(substr($file , -4) == '.php')
{
$saveArray[] = $path.$file;
}
}
}
}
/**
* Compile hook , After compilation, it is directly saved in static member variables self::$hookList
* @param $filelist array File path
* @param $namespace string Namespace rules
* @param $saveHook array preservation Hook
* @return null
*/
static protected function compileHook($filelist , $namespace , &$saveHook)
{
$root_path = strtr(ROOT_PATH,'\\' , '/');
//print_r($filelist);
// Current reference count
$index = self::$index;
$indexAdd = self::$indexAdd;
foreach ($filelist as $file)
{
require_once($file);
// Get the loaded class
$class_list = get_declared_classes();
// Search counters
for($len = count($class_list);$index<$len;$index++)
{
$classname = $class_list[$index];
if(preg_match($namespace , $classname))
{
// This class meets our needs
$ec = new \ReflectionClass($classname);
// The type of hook
$type = basename(strtr($classname , '\\' , '/'));
foreach($ec->getMethods() as $r){
if($r->name[0] != '_' && $r->class == $classname){
// I don't know how to realize sorting yet The method name is followed by
$name = $r->name;
$listorder = 1;
if(strpos($name , '_') !== false){
// There is a sort
$temp = explode('_',$name);
$num = array_pop($temp);
if(is_numeric($num)){
$name = implode('_' , $temp);
$listorder = $num;
}
}
$typename = strtolower($type.'_'.$name);
!isset($saveHook[$typename]) AND $saveHook[$typename] = [];
$saveHook[$typename][$listorder.'_'.$indexAdd++] = [
'filename'=>str_replace($root_path,'',$file), // The advantage of saving the file path is convenient and fast loading , No need to search the path
'class'=>$classname, // Save the name of the class
'func'=>$r->name, // Save method name
'listorder'=>$listorder // Sort , After compilation , Sort the weights
];
}
}
}
}
}
self::$index = $index;
self::$indexAdd = $indexAdd;
}
/**
* @param $path Search module path
* @return array
*/
static protected function searchDir( $path )
{
// Automatic directory completion
$path = strtr(realpath($path),'\\' , '/');
$char = substr($path,-1);
if( $char != '/' || $char != '\\' ){
$path .= '/';
}
$path .= '*';
$dirs = glob($path, GLOB_ONLYDIR );
$result = array();
foreach($dirs as $dir){
if(is_dir($dir .'/hook'))
{
self::searchHook($dir .'/hook/' , $result);
}
}
return $result;
}
/**
* Get the compiled hook
* @return bool|array
*/
static protected function getCache()
{
if(Config::get('jntoo_hook_cache')){
// Access to the cache
return cache('jntoo_hook_cache');
}
return false;
}
/**
* Save the compiled cache
* @param $value array
* @return bool
*/
static protected function setCache( $value )
{
// Set to permanent cache
if(Config::get('jntoo_hook_cache')){
cache('jntoo_hook_cache' , $value , null);
}
return true;
}
}
边栏推荐
- GUI 应用:socket 网络聊天室
- Leetcode 121 best time to buy and sell stock (simple)
- Future source code view -juc series
- 功能:将主函数中输入的字符串反序存放。例如:输入字符串“abcdefg”,则应输出“gfedcba”。
- Employees' turnover intention is under the control of the company. After the dispute, the monitoring system developer quietly removed the relevant services
- GUI application: socket network chat room
- 1-redis architecture design to use scenarios - four deployment and operation modes (Part 1)
- 51 MCU external interrupt
- [common error] UART cannot receive data error
- Which insurance products can the elderly buy?
猜你喜欢
A dichotomy of Valentine's Day
Weekly open source project recommendation plan
Huawei BFD and NQA
CLP information - how does the digital transformation of credit business change from star to finger?
Regular expression of shell script value
Since the "epidemic", we have adhered to the "no closing" of data middle office services
HackTheBox-baby breaking grad
MySQL - use of aggregate functions and group by groups
Long article review: entropy, free energy, symmetry and dynamics in the brain
How to set the response description information when the response parameter in swagger is Boolean or integer
随机推荐
Future源码一观-JUC系列
Avoid playing with super high conversion rate in material minefields
A-Frame虚拟现实开发入门
MySQL -- Introduction and use of single line functions
The culprit of unrestrained consumption -- Summary
The difference between fetchtype lazy and eagle in JPA
中电资讯-信贷业务数字化转型如何从星空到指尖?
功能:求5行5列矩阵的主、副对角线上元素之和。注意, 两条对角线相交的元素只加一次。例如:主函数中给出的矩阵的两条对角线的和为45。
PMP 考试常见工具与技术点总结
Unity Shader入门精要读书笔记 第三章 Unity Shader基础
Sequence list and linked list
Cloud dial test helps Weidong cloud education to comprehensively improve the global user experience
[prefix and notes] prefix and introduction and use
Windos10 reinstallation system tutorial
MySQL deadly serial question 2 -- are you familiar with MySQL index?
How to use AHAS to ensure the stability of Web services?
1-redis architecture design to use scenarios - four deployment and operation modes (Part 1)
不得不会的Oracle数据库知识点(二)
51 single chip microcomputer timer 2 is used as serial port
查询效率提升10倍!3种优化方案,帮你解决MySQL深分页问题