spl_autoload_register()会销毁__autoload()


function __autoload($c)
{
echo "old\n";
}

function a ($c) {
echo “a\n”;
}
function b ($c) {
echo “b\n”;
}

//调用了spl_autoload_register() 会注销掉__autoload()
//先注册的在前,多次注册已经存在的函数不返回false,不影响原来已经注册函数的原有的执行顺序。
//$x = spl_autoload_register(‘b’);
//var_dump($x);
$arr = spl_autoload_functions();
print_r($arr);

//$x = spl_autoload_register(‘a’);
//var_dump($x);
$arr = spl_autoload_functions();
print_r($arr);

//$x = spl_autoload_register(‘b’);
//var_dump($x);

$arr = spl_autoload_functions();
print_r($arr);

输出内容为:

Array
(
[0] => __autoload
)
Array
(
[0] => __autoload
)
Array
(
[0] => __autoload
)
old

Fatal error: Class ‘Bla’ not found in /Users/chenlong/php_workspace2/php/chenlong.php on line 43

打开注释内容执行输出为:

bool(true)
Array
(
[0] => b
)
bool(true)
Array
(
[0] => b
[1] => a
)
bool(true)
Array
(
[0] => b
[1] => a
)
b
a

Fatal error: Class ‘Bla’ not found in /Users/chenlong/php_workspace2/php/chenlong.php on line 43

研究原因:

这次升级php7,框架整体用的__autoload(),方法固定有一个require_once(file) ,找不到文件会抛错导致fatal error程序退出。

taobao-sdk也有个自动加载spl_autoload_register(‘Autoloader::autoload’);即使文件不存在也不会抛出fatal error 。

原来的同学在当时测试时可能发现加载了淘宝的sdk后,自身框架的__autoload()不生效了,就在下面补了一行spl_autoload_register(‘__autoload’);以恢复框架自动加载功能。

这次升级,我把__autoload()改名为了my_autoload(),并且用的spl_autoload_register(‘my_autoload’);注册的。

调用淘宝sdk后报错,感觉像是spl_autoload_register(‘Autoloader::autoload’);没注册成功,但是经过spl_autoload_functions()输出发现注册上了,后来排查到原因是因为spl_autoload_register注册的多个加载器是按顺序执行的,但是框架的__autoload() 固定有require_once(file)会导致fatal error程序中止,根本不能继续加载下一个加载器 “Autoloader::autoload”。

目前有2个解决思路:

1.修改框架的autoload函数,判断如果文件存在才 require_once,毕竟如果文件什么的没搞进来,接下来的代码调用这个文件的类或者方法,也会报fatal error(只是抛错位置在各自的业务文件,而非统一的底层文件那一行),但是不影响自动加载流程继续执行下一个加载器。

2.想办法让taobao的加载器排在前面,先注销框架的自动加载函数,再注册taobao-sdk的加载函数,再恢复注册框架的自动加载函数。
考虑到大家都习惯了某个文件没发布不存在时1方式粗暴简单明了的显示fatal error都固定报在require_once那一行,所以用第2种。

//1.旧款__autoload() 会被 spl_autoload_register() 注销掉。
//2.my_autoload() 的require_once() 会抛出fatal error, 导致不能传递到下一个自动加载器。

//注销原有autoload
$funcAry = spl_autoload_functions();
foreach($funcAry as $func)
{
spl_autoload_unregister($func);
}

spl_autoload_register(‘Autoloader::autoload’);

//加载原有autoload
foreach($funcAry as $func)
{
spl_autoload_register($func);
}

在处理框架函数的注销和恢复时本来考虑简单的指定具体类名的方式,如:
spl_autoload_unregister(‘my_autoload’);
spl_autoload_register(‘Autoloader::autoload’);
spl_autoload_register(‘my_autoload’);
但是又担心框架配置的函数名称my_autoload变化,这里很可能会忘记更改(实际上,谁没事改这么底层的名称),所以采用spl_autoload_functions()获取已注册的方式。

方式2不好在于,就是如果多次调用taobaoSdk::xx()方法会多次重复这个过程,并且一次请求内部 从第二次调用taobaoSdk::xx()开始每次 Autoloader::autoload都多注册了一次。不过应该也不差这点消耗。

另外lib包内部是不是不搞小范围的自动加载更好?可是人家某些sdk就是这样整的,人家也没在自己的加载器抛fatal error中断执行什么的。
要不要改成各种include?

另外官方在未来要抛弃__autoload(),spl_auto_register也方便能加载多个加载器。

参考:
http://php.net/manual/zh/function.autoload.php
(PHP 5, PHP 7)
__autoload — 尝试加载未定义的类
Warning This feature has been DEPRECATED as of PHP 7.2.0. Relying on this feature is highly discouraged.

http://php.net/manual/zh/language.oop5.autoload.php

关于 Administrator

爱拼才会赢!
此条目发表在 php7升级 分类目录。将固定链接加入收藏夹。

发表评论

电子邮件地址不会被公开。 必填项已用 * 标注

*

您可以使用这些 HTML 标签和属性: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>