CI框架允许你在不修改系统核心代码的基础上添加或者更改系统的核心功能(如重写缓存、输出等)。例如,在系统开启hook的条件下(config.php中$config['enable_hooks'] = TRUE;),通过添加特定的钩子,可以让系统在特定的时刻触发特定的脚本:
$hook['post_system'] = array( 'class' => 'frameLog', 'function' => 'postLog', 'filename' => 'post_system.php', 'filepath' => 'hooks',);
上述钩子定义了一个post_system的钩子,用于在最终的页面渲染之后的脚本处理(参数的含义可以参考后面或者手册,这里暂时不做更多解释)。
那么问题来了:
- 钩子是什么?
- CI中支持的钩子有哪些?
- CI中钩子是如何实现的?
我们一步步来看。
1. 钩子是什么
百度百科上对于钩子的定义是:
钩子实际上是一个处理消息的程序段,通过系统调用,把它挂入系统。每当特定的消息发出,在没有到达目的窗口前,钩子程序就先捕获该消息,亦即钩子函数先得到控制权。这时钩子函数即可以加工处理(改变)该消息,也可以不作处理而继续传递该消息,还可以强制结束消息的传递。
从上述定义我们可以看出几点:
- 钩子是一种事件驱动模式,它的核心自然是事件(CI中pre_system,pre_controller等都是特定的事件)。
- 既然是事件驱动,那么必然要包含最重要的两个步骤: (1)、事件注册。对于Hook而言,就是指Hook钩子的挂载。(2).事件触发。在特定的时间点call特定的钩子,执行相应的钩子程序。
- 既然是事件驱动,那么也应该支持统一挂钩点的多个注册事件。
- 启动Hook钩子之后,程序的流程可能会发生变化,且钩子之间可能有相互调用的可能性,如果处理不当,会有死循环的可能性。同时,钩子的启用使得程序在一定程度上变得复杂,难以调试。
2. CI中预定义钩子
CI中提供了7个可用的预设挂钩点,分别是:
pre_system: 指在系统加载前期的钩子
pre_controller:调用控制器之前的钩子,路由与安全性检查已经完毕
post_controller_constructor:控制器实例化之后,任何方法调用之前
post_controller:控制器完全运行之后
display_override:重写display
cache_override :重写缓存
post_system:最终的页面发送到客户端之后
3. CI中钩子的实现
CI中钩子的核心功能是由Hook组件完成的,先看该组件的类图:
其中:
enabled: 钩子功能是否开启的标志。
hooks :保存系统中启用的钩子列表
in_progress:之后我们会看到,这个标志位用于防止钩子之间的互相调用而导致的死循环。
_construct是Hook组件的构造函数,这其中调用了_initialize来完成初始化的工作
_call_hook: 调用_run_hook来call指定的钩子程序。之前CodeIgniter.php中我们已经看到,_call_hook是实际提供给外部调用的接口。
_run_hook: 实际执行钩子程序的函数
在开始之前,我们先贴出预定义钩子的结构。这个结构可能会贯穿在源代码的始终,因而我们有必要知道该结构的参数含义。
$hook['xx'] = array( 'class' => 'xx', //钩子调用的类名,可以为空 'function' => 'xx',//钩子调用的函数名 'filename' => 'xx',//该钩子的文件名 'filepath' => 'xx',//钩子的目录 'params' => 'xx'//传递给钩子的参数);
1). 钩子组件初始化
_initialize函数用于钩子组件的初始化,该函数主要完成的工作有:
(1) 检查配置文件中hook功能是否被启用,这需要加载Config(配置管理组件):
1 2 3 4 5 6 |
$CFG =& load_class( 'Config' , 'core' );
if ( $CFG ->item( 'enable_hooks' ) == FALSE) { return ; } |
(2) 加载定义的hook列表
同样,你可以设定不同的ENVIRONMENT启用不同的hook,如果有的话,优先加载ENVRIONMENT下的hook:
1 2 3 4 5 6 7 8 |
if (defined( 'ENVIRONMENT' ) AND is_file (APPPATH. 'config/' .ENVIRONMENT. '/hooks.php' )) { include (APPPATH. 'config/' .ENVIRONMENT. '/hooks.php' ); } elseif ( is_file (APPPATH. 'config/hooks.php' )) { include (APPPATH. 'config/hooks.php' ); } |
(3) Hook的检查。如果未设置任何hook,或者设置的hook格式错误,则不作任何处理,直接退出:
1 2 3 4 |
if ( ! isset( $hook ) OR ! is_array ( $hook )) { return ; } |
经过initialize之后,Hook::hooks中存储了已经定义的hook列表:
1 |
$this ->hooks =& $hook ; |
2. Call指定的钩子
_call_hook是主程序中直接调用的接口。该接口主要的工作有:
(1). 检查钩子是否被启用,以及call的钩子是否被预定义(如果未启用或者call的钩子不存在,则直接返回):
1 2 3 4 |
if ( ! $this ->enabled OR ! isset( $this ->hooks[ $which ])) { return FALSE; } |
(2). 检查同一个挂钩点是否启用了多个钩子,如果有,则依次执行之:
1 2 3 4 5 6 7 |
if (isset( $this ->hooks[ $which ][0]) AND is_array ( $this ->hooks[ $which ][0])) { foreach ( $this ->hooks[ $which ] as $val ) { $this ->_run_hook( $val ); } } |
(3). 否则,只有一个钩子,执行它
1 2 3 4 |
else { $this ->_run_hook( $this ->hooks[ $which ]); } |
_run_hook是实际执行hook的函数。
3. run执行特定的钩子程序
_run_hook函数是hook的实际执行者,该函数接收一个预定义的hook数组作为参数,实现如下:
(1). 如果传递的参数压根就不是数组(自然也就不是有效的hook),那么直接返回:
1 2 3 4 |
if ( ! is_array ( $data )) { return FALSE; } |
(2). 检查hook执行状态。
in_progress用于标志当前hook的执行状态。这个参数的主要作用,是防止hook之间的相互调用而导致的死循环。
1 2 3 4 |
if ( $this ->in_progress == TRUE) { return ; } |
(3). Hook的合法性检查。
为了方便讲述,我们再次提出一个预定义的hook需要的参数:
$hook['xx'] = array( 'class' => 'xx', //钩子调用的类名,可以为空 'function' => 'xx',//钩子调用的函数名 'filename' => 'xx',//该钩子的文件名 'filepath' => 'xx',//钩子的目录 'params' => 'xx'//传递给钩子的参数);
其中class和params是可选参数,其他3个参数为必选参数,如果不提供,则由于无法准确定位到hook程序,只能直接返回:
1 2 3 4 5 6 7 8 9 10 11 |
if ( ! isset( $data [ 'filepath' ]) OR ! isset( $data [ 'filename' ])) { return FALSE; }
$filepath = APPPATH. $data [ 'filepath' ]. '/' . $data [ 'filename' ];
if ( ! file_exists ( $filepath )) { return FALSE; } |
(4). 到这里,已经基本确认钩子程序的位置了,这里有两种情况:
a. 预定义的hook中class参数为空,表明使用的是过程式的调用方式,则直接执行hook文件中的function xxx
b. class参数不为空,提供的是面向对象的方式,则实际的钩子程序是$class->$function .同样,如果既没有设置class,也没有设置function参数,则无法执行hook,直接返回:
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 |
$class = FALSE; $function = FALSE; $params = '' ;
/* 获取 hook class */ if (isset( $data [ 'class' ]) AND $data [ 'class' ] != '' ) { $class = $data [ 'class' ]; }
/* 获取 hook function */ if (isset( $data [ 'function' ])) { $function = $data [ 'function' ]; }
/* 获取传递的 hook 参数 */ if (isset( $data [ 'params' ])) { $params = $data [ 'params' ]; }
/* 如果class和function都不存在,则无法定位hook程序,直接返回 */ if ( $class === FALSE AND $function === FALSE) { return FALSE; } |
(5). 设置执行标志in_progress,并执行上述两种情况下的hook:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 |
/* 面向对象的设置方式 */ if ( $class !== FALSE) { if ( ! class_exists ( $class )) { require ( $filepath ); }
$HOOK = new $class ; $HOOK -> $function ( $params ); } /* 过程式的执行方式 */ else { if ( ! function_exists( $function )) { require ( $filepath ); }
$function ( $params ); } |
最后,别忘了在hook执行完之后,设置标识位in_progress为false,并返回执行成功的标志:
1 2 |
$this ->in_progress = FALSE; return TRUE; |
Hook组件的完整源码:
+ View Code
参考文献
1. http://codeigniter.org.cn/user_guide/general/hooks.html 手册
2. http://itopic.org/codeigniter-hook.html
3. http://codeigniter.org.cn/forums/thread-4947-1-1.html 钩子实现的Layout

PHP는 주로 절차 적 프로그래밍이지만 객체 지향 프로그래밍 (OOP)도 지원합니다. Python은 OOP, 기능 및 절차 프로그래밍을 포함한 다양한 패러다임을 지원합니다. PHP는 웹 개발에 적합하며 Python은 데이터 분석 및 기계 학습과 같은 다양한 응용 프로그램에 적합합니다.

PHP는 1994 년에 시작되었으며 Rasmuslerdorf에 의해 개발되었습니다. 원래 웹 사이트 방문자를 추적하는 데 사용되었으며 점차 서버 측 스크립팅 언어로 진화했으며 웹 개발에 널리 사용되었습니다. Python은 1980 년대 후반 Guidovan Rossum에 의해 개발되었으며 1991 년에 처음 출시되었습니다. 코드 가독성과 단순성을 강조하며 과학 컴퓨팅, 데이터 분석 및 기타 분야에 적합합니다.

PHP는 웹 개발 및 빠른 프로토 타이핑에 적합하며 Python은 데이터 과학 및 기계 학습에 적합합니다. 1.PHP는 간단한 구문과 함께 동적 웹 개발에 사용되며 빠른 개발에 적합합니다. 2. Python은 간결한 구문을 가지고 있으며 여러 분야에 적합하며 강력한 라이브러리 생태계가 있습니다.

PHP는 현대화 프로세스에서 많은 웹 사이트 및 응용 프로그램을 지원하고 프레임 워크를 통해 개발 요구에 적응하기 때문에 여전히 중요합니다. 1.PHP7은 성능을 향상시키고 새로운 기능을 소개합니다. 2. Laravel, Symfony 및 Codeigniter와 같은 현대 프레임 워크는 개발을 단순화하고 코드 품질을 향상시킵니다. 3. 성능 최적화 및 모범 사례는 응용 프로그램 효율성을 더욱 향상시킵니다.

phphassignificallyimpactedwebdevelopmentandextendsbeyondit

PHP 유형은 코드 품질과 가독성을 향상시키기위한 프롬프트입니다. 1) 스칼라 유형 팁 : PHP7.0이므로 int, float 등과 같은 기능 매개 변수에 기본 데이터 유형을 지정할 수 있습니다. 2) 반환 유형 프롬프트 : 기능 반환 값 유형의 일관성을 확인하십시오. 3) Union 유형 프롬프트 : PHP8.0이므로 기능 매개 변수 또는 반환 값에 여러 유형을 지정할 수 있습니다. 4) Nullable 유형 프롬프트 : NULL 값을 포함하고 널 값을 반환 할 수있는 기능을 포함 할 수 있습니다.

PHP에서는 클론 키워드를 사용하여 객체 사본을 만들고 \ _ \ _ Clone Magic 메소드를 통해 클로닝 동작을 사용자 정의하십시오. 1. 복제 키워드를 사용하여 얕은 사본을 만들어 객체의 속성을 복제하지만 객체의 속성은 아닙니다. 2. \ _ \ _ 클론 방법은 얕은 복사 문제를 피하기 위해 중첩 된 물체를 깊이 복사 할 수 있습니다. 3. 복제의 순환 참조 및 성능 문제를 피하고 클로닝 작업을 최적화하여 효율성을 향상시키기 위해주의를 기울이십시오.

PHP는 웹 개발 및 컨텐츠 관리 시스템에 적합하며 Python은 데이터 과학, 기계 학습 및 자동화 스크립트에 적합합니다. 1.PHP는 빠르고 확장 가능한 웹 사이트 및 응용 프로그램을 구축하는 데 잘 작동하며 WordPress와 같은 CMS에서 일반적으로 사용됩니다. 2. Python은 Numpy 및 Tensorflow와 같은 풍부한 라이브러리를 통해 데이터 과학 및 기계 학습 분야에서 뛰어난 공연을했습니다.


핫 AI 도구

Undresser.AI Undress
사실적인 누드 사진을 만들기 위한 AI 기반 앱

AI Clothes Remover
사진에서 옷을 제거하는 온라인 AI 도구입니다.

Undress AI Tool
무료로 이미지를 벗다

Clothoff.io
AI 옷 제거제

AI Hentai Generator
AI Hentai를 무료로 생성하십시오.

인기 기사

뜨거운 도구

MinGW - Windows용 미니멀리스트 GNU
이 프로젝트는 osdn.net/projects/mingw로 마이그레이션되는 중입니다. 계속해서 그곳에서 우리를 팔로우할 수 있습니다. MinGW: GCC(GNU Compiler Collection)의 기본 Windows 포트로, 기본 Windows 애플리케이션을 구축하기 위한 무료 배포 가능 가져오기 라이브러리 및 헤더 파일로 C99 기능을 지원하는 MSVC 런타임에 대한 확장이 포함되어 있습니다. 모든 MinGW 소프트웨어는 64비트 Windows 플랫폼에서 실행될 수 있습니다.

드림위버 CS6
시각적 웹 개발 도구

WebStorm Mac 버전
유용한 JavaScript 개발 도구

ZendStudio 13.5.1 맥
강력한 PHP 통합 개발 환경

메모장++7.3.1
사용하기 쉬운 무료 코드 편집기
