Home Backend Development PHP Tutorial PHP hook mechanism principle and detailed explanation

PHP hook mechanism principle and detailed explanation

Jan 23, 2020 am 10:37 AM
php

PHP hook mechanism principle and detailed explanation

What is a hook?

You must have heard of plug-ins. There are many WordPress plug-ins. This is implemented using the hook mechanism.

When the code is running, we execute some special methods in advance at several special points of the operation: for example, recording input parameters and running methods before running methods (such as the add method of Blog::add) After that, the processing results are recorded. Before and after the running method, there is a simple hook (mount point). We place the hook function on this hook (record the input parameters and record the processing results) to perform some tasks that are not related to the running of the program. .

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

<?php

class Blog extends Controller{

     

    public function add(){

         

        //some code

        $res = $data;

         

        return $res;

    }

}

$obj = new Blog();

Log::write($_REQUEST);

$res $obj->add();

Log::write(json_encode($res));

Copy after login

If an OnBeforeRunActionCallback() method is placed before running the method, this method may be empty at the beginning, but we can directly use OnBeforeRunActionCallback() without modifying the original code in the future. Just add code logic inside, such as logging, parameter filtering, etc.

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

<?php

class Blog extends Controller{

     

    public function add(){

         

        //some code

        $res = $data;

         

        return $res;

    }

}

$obj = new Blog();

OnBeforeRunActionCallback($_REQUEST);

$obj->add();

OnAfterRunActionCallback($res);

function OnBeforeRunActionCallback($param){

    Log::write($param);

        FilterParams($param);

}

function OnAfterRunActionCallback($res){

    Log::write(json_encode($res));

}

Copy after login

In the project code, place a hook function where you think it needs to be extended (not extended yet). When it needs to be extended, mount the classes and functions that need to be implemented to this hook. The implementation has been expanded.

Principle

The actual hook is generally designed as a class Hook, which provides registration plug-ins to the hook (add_hook) and triggers the hook method (trigger_hook). When registering a plug-in, store the executable method to be run by the plug-in into the array corresponding to the hook.

1

2

3

4

5

6

7

8

9

10

11

$_listeners = array(

    &#39;OnBeforeRunAction&#39; => array(

        &#39;callback1&#39;,

        &#39;callback2&#39;,

        &#39;callback3&#39;,

    ),

);

//提前注册插件到钩子

add_hook(&#39;OnBeforeRunAction&#39;, &#39;callback4&#39;);

//特定地方执行钩子

trigger_hook(&#39;OnBeforeRunAction&#39;);

Copy after login

When the hook is triggered, the callback methods registered in OnBeforeRunAction will be traversed and the corresponding callback methods will be executed to implement dynamic expansion functions. The registered hook method is generally an anonymous function:

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

function trigger_hook($hook, $data=&#39;&#39;){

    //查看要实现的钩子,是否在监听数组之中

    if (isset($this->_listeners[$hook]) && is_array($this->_listeners[$hook]) && count($this->_listeners[$hook]) > 0)

    {

        // 循环调用开始

        foreach ($this->_listeners[$hook] as $listener)

        {

            if(is_callable()){

                call_user_func($listener, $data);

            }elseif(is_array($listener)){

                // 取出插件对象的引用和方法

                $class =& $listener[0];

                $method = $listener[1];

                if(method_exists($class,$method))

                {

                    // 动态调用插件的方法

                    $class->$method($data);

                }

            }

        }

    }

}

Copy after login

How to implement it?

Simple

1. Plug-in class Hook: Provides methods for registering plug-ins and executing plug-ins. It actually stores the corresponding mount points in an array. Executable methods.

2. Register plug-ins uniformly in a certain configuration file or function.

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

54

55

56

57

58

59

60

61

62

63

64

65

66

67

68

69

70

71

72

73

74

75

76

77

78

79

80

81

82

83

84

85

86

87

class Hook

{

    //action hooks array 

    private static $actions = array();

    /**

     * ads a function to an action hook

     * @param $hook

     * @param $function

     */

    public static function add_action($hook,$function)

    {  

        $hook=mb_strtolower($hook,CHARSET);

        // create an array of function handlers if it doesn&#39;t already exist

        if(!self::exists_action($hook))

        {

            self::$actions[$hook] = array();

        }

        // append the current function to the list of function handlers

        if (is_callable($function))

        {

            self::$actions[$hook][] = $function;

            return TRUE;

        }

        return FALSE ;

    }

    /**

     * executes the functions for the given hook

     * @param string $hook

     * @param array $params

     * @return boolean true if a hook was setted

     */

    public static function do_action($hook,$params=NULL)

    {

        $hook=mb_strtolower($hook,CHARSET);

        if(isset(self::$actions[$hook]))

        {

            // call each function handler associated with this hook

            foreach(self::$actions[$hook] as $function)

            {

                if (is_array($params))

                {

                    call_user_func_array($function,$params);

                }

                else

                {

                    call_user_func($function);

                }

                //cant return anything since we are in a loop! dude!

            }

            return TRUE;

        }

        return FALSE;

    }

    /**

     * gets the functions for the given hook

     * @param string $hook

     * @return mixed

     */

    public static function get_action($hook)

    {

        $hook=mb_strtolower($hook,CHARSET);

        return (isset(self::$actions[$hook]))? self::$actions[$hook]:FALSE;

    }

    /**

     * check exists the functions for the given hook

     * @param string $hook

     * @return boolean

     */

    public static function exists_action($hook)

    {

        $hook=mb_strtolower($hook,CHARSET);

        return (isset(self::$actions[$hook]))? TRUE:FALSE;

    }

}

  

    /**

     * Hooks Shortcuts not in class

     */

    function add_action($hook,$function)

    {

        return Hook::add_action($hook,$function);

    }

  

    function do_action($hook)

    {

        return Hook::do_action($hook);

    }

Copy after login

Usage example:

1

2

3

4

5

6

7

//添加钩子

Hook::add_action(&#39;unique_name_hook&#39;,&#39;some_class::hook_test&#39;);

//或使用快捷函数添加钩子:

add_action(&#39;unique_name_hook&#39;,&#39;other_class::hello&#39;);

add_action(&#39;unique_name_hook&#39;,&#39;some_public_function&#39;);

//执行钩子

do_action(&#39;unique_name_hook&#39;);//也可以使用 Hook::do_action();

Copy after login

With installation/uninstallation

When the hook class is initialized, register the plug-in that has been opened (such as database records) ;Set the mount point when the global situation is appropriate; trigger the mount point registration event when the operation is appropriate.

1. Plug-in class Hook: Provides methods for registering plug-ins and executing plug-ins. It actually stores the executable method corresponding to the mount point in an array.

2. Add an initialization method to the plug-in class to search for installed plug-ins, run the registration method (reg) that must be executed by the plug-in, and register the plug-in method to register the hook to the mount point.

3. Always place the plug-in in a certain directory and write the configuration file according to certain specifications. There is a plug-in list page in the background, which traverses the plug-ins in the specified directory. When installing, the plug-in information is recorded in the database, and when uninstalled, the database record information is deleted.

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

54

55

56

57

58

59

60

61

62

63

64

65

66

67

68

69

70

71

72

73

74

75

76

77

78

79

80

81

82

83

84

85

86

87

88

89

90

91

92

93

94

95

96

97

98

99

100

101

102

103

104

105

106

107

108

109

110

111

112

113

114

115

116

117

118

119

120

121

122

123

124

125

126

127

128

129

130

131

132

133

134

135

136

137

138

139

140

141

142

143

144

145

146

147

148

149

150

151

152

153

154

155

156

157

158

159

160

161

162

163

164

165

166

167

168

169

170

171

172

173

174

175

176

177

178

179

180

181

182

183

184

185

186

187

188

189

190

191

192

193

194

195

196

197

198

199

200

201

202

203

204

205

206

207

208

209

210

211

212

213

214

215

216

217

218

219

220

221

222

223

224

225

226

227

228

229

230

231

232

233

234

235

236

237

238

239

240

241

242

243

244

245

246

247

248

249

250

251

252

253

254

255

256

257

258

259

260

261

262

263

264

265

266

267

268

269

270

271

272

273

274

275

276

277

278

279

280

281

282

283

284

285

286

287

288

289

290

291

292

293

294

295

296

297

298

299

300

301

302

303

304

305

306

307

308

309

310

311

312

313

314

315

316

317

318

319

320

321

322

323

324

325

326

327

328

329

<?php

/**

 * @file plugin.php

 * @brief 插件核心类

 * @note 观察者模式,注册事件,触发事件

 */

class plugin extends IInterceptorBase

{

    //默认开启的插件列表

    private static $defaultList = array("_verification","_goodsCategoryWidget","_authorization","_userInfo","_initData");

    //已经注册监听

    private static $_listen = array();

    //加载插件

    public static function init()

    {

        $pluginDB    = new IModel(&#39;plugin&#39;);

        $pluginList  = $pluginDB->query("is_open = 1","class_name","sort asc");

        //加载默认插件

        foreach(self::$defaultList as $val)

        {

            $pluginList[]= array(&#39;class_name&#39; => $val);

        }

        foreach($pluginList as $key => $val)

        {

            $className = $val[&#39;class_name&#39;];

            $classFile = self::path().$className."/".$className.".php";

            if(is_file($classFile))

            {

                include_once($classFile);

                $pluginObj = new $className();

                $pluginObj->reg();

            }

        }

    }

    /**

     * @brief 注册事件

     * @param string $event 事件

     * @param object ro function $classObj 类实例 或者 匿名函数

     * @param string $method 方法名字

     */

    public static function reg($event,$classObj,$method = "")

    {

        if(!isset(self::$_listen[$event]))

        {

            self::$_listen[$event] = array();

        }

        self::$_listen[$event][] = array($classObj,$method);

    }

    /**

     * @brief 显示已注册事件

     * @param string $event 事件名称

     * @return array

     */

    public static function get($event = &#39;&#39;)

    {

        if($event)

        {

            if( isset(self::$_listen[$event]) )

            {

                return self::$_listen[$event];

            }

            return null;

        }

        return self::$_listen;

    }

    /**

     * @brief 触发事件

     * @param string $event 事件

     * @param mixed  $data  数据

     * @notice 可以调用匿名函数和方法

     */

    public static function trigger($event,$data = null)

    {

        $result = array();

        if(isset(self::$_listen[$event]))

        {

            foreach(self::$_listen[$event] as $key => $val)

            {

                list($pluginObj,$pluginMethod) = $val;

                $result[$key] = is_callable($pluginObj) ? call_user_func($pluginObj,$data):call_user_func(array($pluginObj,$pluginMethod),$data);

            }

        }

        return isset($result[1]) ? $result : current($result);

    }

    /**

     * @brief 插件物理路径

     * @return string 路径字符串

     */

    public static function path()

    {

        return IWeb::$app->getBasePath()."plugins/";

    }

    /**

     * @brief 插件WEB路径

     * @return string 路径字符串

     */

    public static function webPath()

    {

        return IUrl::creatUrl(&#39;&#39;)."plugins/";

    }

    /**

     * @brief 获取全部插件

     * @param string $name 插件名字,如果为空则获取全部插件信息

     * @return array 插件信息 array(

        "name"        => 插件名字,

        "description" => 插件描述,

        "explain"     => 使用说明,

        "class_name"  => 插件ID,

        "is_open"     => 是否开启,

        "is_install"  => 是否安装,

        "config_name" => 默认插件参数结构,

        "config_param"=> 已经保存的插件参数,

        "sort"        => 排序,

     )

     */

    public static function getItems($name = &#39;&#39;)

    {

        $result = array();

        $dirRes = opendir(self::path());

        //遍历目录读取配置文件

        $pluginDB = new IModel(&#39;plugin&#39;);

        while($dir = readdir($dirRes))

        {

            if($dir[0] == "." || $dir[0] == "_")

            {

                continue;

            }

            if($name && $result)

            {

                break;

            }

            if($name && $dir != $name)

            {

                continue;

            }

            $pluginIndex = self::path().$dir."/".$dir.".php";

            if(is_file($pluginIndex))

            {

                include_once($pluginIndex);

                if(get_parent_class($dir) == "pluginBase")

                {

                    $class_name   = $dir;

                    $pluginRow    = $pluginDB->getObj(&#39;class_name = "&#39;.$class_name.&#39;"&#39;);

                    $is_open      = $pluginRow ? $pluginRow[&#39;is_open&#39;] : 0;

                    $is_install   = $pluginRow ? 1                     : 0;

                    $sort         = $pluginRow ? $pluginRow[&#39;sort&#39;]    : 99;

                    $config_param = array();

                    if($pluginRow && $pluginRow[&#39;config_param&#39;])

                    {

                        $config_param = JSON::decode($pluginRow[&#39;config_param&#39;]);

                    }

                    $result[$dir] = array(

                        "name"        => $class_name::name(),

                        "description" => $class_name::description(),

                        "explain"     => $class_name::explain(),

                        "class_name"  => $class_name,

                        "is_open"     => $is_open,

                        "is_install"  => $is_install,

                        "config_name" => $class_name::configName(),

                        "config_param"=> $config_param,

                        "sort"        => $sort,

                    );

                }

            }

        }

        if(!$name)

        {

            return $result;

        }

        return isset($result[$name]) ? $result[$name] : array();

    }

    /**

     * @brief 系统内置的所有事件触发

     */

    public static function onCreateApp(){plugin::init();plugin::trigger("onCreateApp");}

    public static function onFinishApp(){plugin::trigger("onFinishApp");}

    public static function onBeforeCreateController($ctrlId){plugin::trigger("onBeforeCreateController",$ctrlId);plugin::trigger("onBeforeCreateController@".$ctrlId);}

    public static function onCreateController($ctrlObj){plugin::trigger("onCreateController");plugin::trigger("onCreateController@".$ctrlObj->getId());}

    public static function onFinishController($ctrlObj){plugin::trigger("onFinishController");plugin::trigger("onFinishController@".$ctrlObj->getId());}

    public static function onBeforeCreateAction($ctrlObj,$actionId){plugin::trigger("onBeforeCreateAction",$actionId);plugin::trigger("onBeforeCreateAction@".$ctrlObj->getId());plugin::trigger("onBeforeCreateAction@".$ctrlObj->getId()."@".$actionId);}

    public static function onCreateAction($ctrlObj,$actionObj){plugin::trigger("onCreateAction");plugin::trigger("onCreateAction@".$ctrlObj->getId());plugin::trigger("onCreateAction@".$ctrlObj->getId()."@".$actionObj->getId());}

    public static function onFinishAction($ctrlObj,$actionObj){plugin::trigger("onFinishAction");plugin::trigger("onFinishAction@".$ctrlObj->getId());plugin::trigger("onFinishAction@".$ctrlObj->getId()."@".$actionObj->getId());}

    public static function onCreateView($ctrlObj,$actionObj){plugin::trigger("onCreateView");plugin::trigger("onCreateView@".$ctrlObj->getId());plugin::trigger("onCreateView@".$ctrlObj->getId()."@".$actionObj->getId());}

    public static function onFinishView($ctrlObj,$actionObj){plugin::trigger("onFinishView");plugin::trigger("onFinishView@".$ctrlObj->getId());plugin::trigger("onFinishView@".$ctrlObj->getId()."@".$actionObj->getId());}

    public static function onPhpShutDown(){plugin::trigger("onPhpShutDown");}

}

/**

 * @brief 插件基类,所有插件必须继承此类

 * @notice 必须实现3个抽象方法: reg(),name(),description()

 */

abstract class pluginBase extends IInterceptorBase

{

    //错误信息

    protected $error = array();

    //注册事件接口,内部通过调用payment::reg(事件,对象实例,方法);

    public function reg(){}

    /**

     * @brief 默认插件参数信息,写入到plugin表config_param字段

     * @return array("字段名" => array(

         "name"    => "文字显示",

         "type"    => "数据类型【text,radio,checkbox,select】",

         "pattern" => "数据校验【int,float,date,datetime,require,正则表达式】",

         "value"   => "1,数组:枚举数据【radio,checkbox,select】的预设值,array(名字=>数据); 2,字符串:【text】默认数据",

        ))

     */

    public static function configName()

    {

        return array();

    }

    /**

     * @brief 插件安装

     * @return boolean

     */

    public static function install()

    {

        return true;

    }

    /**

     * @brief 插件卸载

     * @return boolean

     */

    public static function uninstall()

    {

        return true;

    }

    /**

     * @brief 插件名字

     * @return string

     */

    public static function name()

    {

        return "插件名称";

    }

    /**

     * @brief 插件功能描述

     * @return string

     */

    public static function description()

    {

        return "插件描述";

    }

    /**

     * @brief 插件使用说明

     * @return string

     */

    public static function explain()

    {

        return "";

    }

    /**

     * @brief 获取DB中录入的配置参数

     * @return array

     */

    public function config()

    {

        $className= get_class($this);

        $pluginDB = new IModel(&#39;plugin&#39;);

        $dataRow  = $pluginDB->getObj(&#39;class_name = "&#39;.$className.&#39;"&#39;);

        if($dataRow && $dataRow[&#39;config_param&#39;])

        {

            return JSON::decode($dataRow[&#39;config_param&#39;]);

        }

        return array();

    }

    /**

     * @brief 返回错误信息

     * @return array

     */

    public function getError()

    {

        return $this->error ? join("\r\n",$this->error) : "";

    }

    /**

     * @brief 写入错误信息

     * @return array

     */

    public function setError($error)

    {

        $this->error[] = $error;

    }

    /**

     * @brief 插件视图渲染有布局

     * @param string $view 视图名字

     * @param array  $data 视图里面的数据

     */

    public function redirect($view,$data = array())

    {

        if($data === true)

        {

            $this->controller()->redirect($view);

        }

        else

        {

            $__className      = get_class($this);

            $__pluginViewPath = plugin::path().$__className."/".$view;

            $result = self::controller()->render($__pluginViewPath,$data);

            if($result === false)

            {

                IError::show($__className."/".$view."插件视图不存在");

            }

        }

    }

    /**

     * @brief 插件视图渲染去掉布局

     * @param string $view 视图名字

     * @param array  $data 视图里面的数据

     */

    public function view($view,$data = array())

    {

        self::controller()->layout = "";

        $this->redirect($view,$data);

    }

    /**

     * @brief 插件物理目录

     * @param string 插件路径地址

     */

    public function path()

    {

        return plugin::path().get_class($this)."/";

    }

    /**

     * @brief 插件WEB目录

     * @param string 插件路径地址

     */

    public function webPath()

    {

        return plugin::webPath().get_class($this)."/";

    }

}

Copy after login

For more related php knowledge, please visit php tutorial!

The above is the detailed content of PHP hook mechanism principle and detailed explanation. For more information, please follow other related articles on the PHP Chinese website!

Statement of this Website
The content of this article is voluntarily contributed by netizens, and the copyright belongs to the original author. This site does not assume corresponding legal responsibility. If you find any content suspected of plagiarism or infringement, please contact admin@php.cn

Hot AI Tools

Undresser.AI Undress

Undresser.AI Undress

AI-powered app for creating realistic nude photos

AI Clothes Remover

AI Clothes Remover

Online AI tool for removing clothes from photos.

Undress AI Tool

Undress AI Tool

Undress images for free

Clothoff.io

Clothoff.io

AI clothes remover

Video Face Swap

Video Face Swap

Swap faces in any video effortlessly with our completely free AI face swap tool!

Hot Tools

Notepad++7.3.1

Notepad++7.3.1

Easy-to-use and free code editor

SublimeText3 Chinese version

SublimeText3 Chinese version

Chinese version, very easy to use

Zend Studio 13.0.1

Zend Studio 13.0.1

Powerful PHP integrated development environment

Dreamweaver CS6

Dreamweaver CS6

Visual web development tools

SublimeText3 Mac version

SublimeText3 Mac version

God-level code editing software (SublimeText3)

PHP 8.4 Installation and Upgrade guide for Ubuntu and Debian PHP 8.4 Installation and Upgrade guide for Ubuntu and Debian Dec 24, 2024 pm 04:42 PM

PHP 8.4 brings several new features, security improvements, and performance improvements with healthy amounts of feature deprecations and removals. This guide explains how to install PHP 8.4 or upgrade to PHP 8.4 on Ubuntu, Debian, or their derivati

7 PHP Functions I Regret I Didn't Know Before 7 PHP Functions I Regret I Didn't Know Before Nov 13, 2024 am 09:42 AM

If you are an experienced PHP developer, you might have the feeling that you’ve been there and done that already.You have developed a significant number of applications, debugged millions of lines of code, and tweaked a bunch of scripts to achieve op

How To Set Up Visual Studio Code (VS Code) for PHP Development How To Set Up Visual Studio Code (VS Code) for PHP Development Dec 20, 2024 am 11:31 AM

Visual Studio Code, also known as VS Code, is a free source code editor — or integrated development environment (IDE) — available for all major operating systems. With a large collection of extensions for many programming languages, VS Code can be c

Explain JSON Web Tokens (JWT) and their use case in PHP APIs. Explain JSON Web Tokens (JWT) and their use case in PHP APIs. Apr 05, 2025 am 12:04 AM

JWT is an open standard based on JSON, used to securely transmit information between parties, mainly for identity authentication and information exchange. 1. JWT consists of three parts: Header, Payload and Signature. 2. The working principle of JWT includes three steps: generating JWT, verifying JWT and parsing Payload. 3. When using JWT for authentication in PHP, JWT can be generated and verified, and user role and permission information can be included in advanced usage. 4. Common errors include signature verification failure, token expiration, and payload oversized. Debugging skills include using debugging tools and logging. 5. Performance optimization and best practices include using appropriate signature algorithms, setting validity periods reasonably,

PHP Program to Count Vowels in a String PHP Program to Count Vowels in a String Feb 07, 2025 pm 12:12 PM

A string is a sequence of characters, including letters, numbers, and symbols. This tutorial will learn how to calculate the number of vowels in a given string in PHP using different methods. The vowels in English are a, e, i, o, u, and they can be uppercase or lowercase. What is a vowel? Vowels are alphabetic characters that represent a specific pronunciation. There are five vowels in English, including uppercase and lowercase: a, e, i, o, u Example 1 Input: String = "Tutorialspoint" Output: 6 explain The vowels in the string "Tutorialspoint" are u, o, i, a, o, i. There are 6 yuan in total

How do you parse and process HTML/XML in PHP? How do you parse and process HTML/XML in PHP? Feb 07, 2025 am 11:57 AM

This tutorial demonstrates how to efficiently process XML documents using PHP. XML (eXtensible Markup Language) is a versatile text-based markup language designed for both human readability and machine parsing. It's commonly used for data storage an

Explain late static binding in PHP (static::). Explain late static binding in PHP (static::). Apr 03, 2025 am 12:04 AM

Static binding (static::) implements late static binding (LSB) in PHP, allowing calling classes to be referenced in static contexts rather than defining classes. 1) The parsing process is performed at runtime, 2) Look up the call class in the inheritance relationship, 3) It may bring performance overhead.

What are PHP magic methods (__construct, __destruct, __call, __get, __set, etc.) and provide use cases? What are PHP magic methods (__construct, __destruct, __call, __get, __set, etc.) and provide use cases? Apr 03, 2025 am 12:03 AM

What are the magic methods of PHP? PHP's magic methods include: 1.\_\_construct, used to initialize objects; 2.\_\_destruct, used to clean up resources; 3.\_\_call, handle non-existent method calls; 4.\_\_get, implement dynamic attribute access; 5.\_\_set, implement dynamic attribute settings. These methods are automatically called in certain situations, improving code flexibility and efficiency.

See all articles