常用知识 - 常用的PHP系统回调 ¶
作者:KK
发表日期:2016.09.15
常用回调快速介绍 ¶
常用的回调注册函数有:
set_error_handler
:如果你想在程序发生错误时想做点什么处理就用这个set_exception_handler
:如果你想在程序发生异常时想做点什么处理就用这个register_shutdown_function
:当程序结束时想做点什么处理就用这个spl_autoload_register
:当调用一个不存在的类时自动加载这个类就用这个__autoload
:(旧版,不推荐)当调用一个不存在的类时自动加载这个类就用这个
下面一一解说(下面附加的测试代码直接复制粘贴到单独脚本就可以运行测试,不需要依赖任何其它东西)
set_error_handler ¶
设置错误的处理函数
函数原型:mixed set_error_handler(callable $error_handler [, int $error_types = E_ALL | E_STRICT ])
测试代码:
set_error_handler(function($code, $message, $file, $line){
echo <<<EOL
错误类型特征码:$code<br/>
错误消息:$message<br/><br/>
出错文件:$file<br/>
出错行号:$line
EOL;
});
error_reporting(-1); //只是为了保证会报错,以防php.ini配置屏蔽了下面的错误
$a = [];
echo $a[5];
用网页运行就会看到这样的报错:
错误代码:8
错误消息:Undefined offset: 5
出错文件:D:\phpStudy\www\test\index.php
出错行号:14
正是因为咱们通过set_error_handler设置了自定义的错误处理才有这样的输出
如果不set_error_handler的话,出错的时候会使用PHP自带的默认处理,输出内容大概是这样的:
Notice: Uninitialized string offset: 5 in D:\phpStudy\www\test\index.php on line 14
- 应用场景:出了错就会引发这个回调函数的执行,然而程序出错当然是程序员不希望出现的,既然出错了我们就希望尽量知道是什么错误,所以通常在这个时候做日志记录或者发邮件通知等都可能会出现
set_exception_handler ¶
设置异常的处理函数
函数原型:callable set_exception_handler(callable $exception_handler )
调用思路和set_error_handler一样,测试代码:
set_exception_handler(function($exception){
$code = $exception->getCode();
$message = $exception->getMessage();
$file = $exception->getFile();
$line = $exception->getLine();
echo <<<EOL
异常类型特征码:$code<br/>
异常消息:$message<br/><br/>
抛出异常的文件:$file<br/>
抛出异常的行号:$line
EOL;
});
error_reporting(-1);
throw new Exception('测试异常');
则输出:
错误类型特征码:0
错误消息:测试异常
出错文件:D:\phpStudy\www\test\index.php
出错行号:19
- 应用场景:跟set_error_handler一样
register_shutdown_function ¶
注册程序结束时的回调函数
函数原型:void register_shutdown_function( callable $callback [, mixed $parameter [, mixed $... ]] )
测试代码:
register_shutdown_function(function(){
echo '<br/>程序运行结束1,本次运行一共耗费内存:' . memory_get_peak_usage(true) . '字节';
});
register_shutdown_function(function(){
echo '<br/>程序运行结束2,本次运行一共耗费内存:' . memory_get_peak_usage(true) . '字节';
});
$random = mt_rand(99, 999);
$result = [];
for($i = 0; $i < $random; $i++){
$result[] = array_fill(0, $i, $i);
}
echo '一共创建了' . count($result) . '个数组';
输出:
一共创建了749个数组
程序运行结束1,本次运行一共耗费内存:15466496字节
程序运行结束2,本次运行一共耗费内存:15466496字节
上面的测试代码执行了两次register_shutdown_function,因为程序结束回调函数是可以注册多个的
另外就算中途执行exit
或die
也会触发这个回调
- 应用场景:当程序结束后,如果判断出是调试模式,会进行内存使用情况和运行时间的统计;另外如果通过
error_get_last
函数获取到错误的话会进行日志记录,让程序员知道去修复
spl_autoload_register ¶
注册自动加载函数
函数原型:bool spl_autoload_register([callable $autoload_function [, bool $throw = true [, bool $prepend = false ]]])
测试代码:
spl_autoload_register(function($className){
echo '代码加载了不存在的类:' . $className . '<br/>';
if(!class_exists($className, false)){
echo '经确认' . $className . ' 这个类确实是不存在的<br/>';
}
//创建这个类,实际应用中文件已经存在,我现在是动态创建文件
$classCode = <<<EOL
<?php
class $className{
public function test(){
echo 'hi,我是' . __CLASS__ . '<br/><br/><br/>';
}
}
EOL;
file_put_contents($className . '.php', $classCode);
include($className . '.php');
if(class_exists($className, false)){
echo '好了,' . $className . ' 这个类存在了<br/>';
}else{
return false; //如果加载不到类请返回false让系统自动调用下一个autoload回调
}
return new $className();
});
$a = new A();
$a->test();
$b = new B();
$b->test();
$c = new C();
$c->test();
输出结果:
代码加载了不存在的类:A
经确认A 这个类确实是不存在的
好了,A 这个类存在了
hi,我是A
代码加载了不存在的类:B
经确认B 这个类确实是不存在的
好了,B 这个类存在了
hi,我是B
代码加载了不存在的类:C
经确认C 这个类确实是不存在的
好了,C 这个类存在了
hi,我是C
并且和register_shutdown_function
一样,spl_autoload_register
可以注册多个自动加载函数,虽然很少见,但是偶尔还是有的
比如有两种情况是用spl_autoload_register注册多个自动加载函数的:
重构一个项目
项目的代码比较老,用spl_autoload_register注册过他们自己的加载逻辑,但重构时需要一部分一部分重构,所以要基于旧代码改,此时再通过spl_autoload_register添加了新的加载逻辑,在加载新的类时,旧的加载代码是找不到文件的,于是就靠新注册的加载函数去加载了,两个加载函数会被轮流调用
双框架项目
优秀的框架在本地化方面是做得很优秀的,基本没有全局变量和常量,所以有些特殊情况下会需要在A框架做的项目下引入B框架,利用B框架做它擅长的事,比如Codeception框架整合了Gazzle框架做Http处理
而我自己做的项目也经历过双框架,甚至三框架模式
- 应用场景:项目的类自动加载,现在已经越来越少人手动include一个类文件再new一个类了,都是直接new,不存在的时候就自动加载,并且加载逻辑方面开始有了PHP行业的统一规范:PSR4规范 自动载入类
__autoload ¶
自动加载不存在的类
函数原型:void __autoload(string $class)
这个在PHP5.1.2以后就被spl_autoload_register
取代了,所以不建议使用的,然而我为什么还要讲这个函数呢
其实由于大部分现存的PHP程序还是基于兼容旧版的设计的,所以在自动加载方面可能会有部分程序使用了__autoload
而不使用spl_autoload_register
测试代码:
//直接声明这个函数就可以
function __autoload($className){
file_put_contents($className . '.php', "<?php class $className{ public function test(){ echo 'hi wo shi A'; } }");
return new $className();
}
$a = new A();
echo $a->test();
结果输出:
hi wo shi A