关于autoload的思考
其实前面发过帖子求助。就在上个月吧。。事实上一直并没有实现。因为考虑到效用问题。
_autoload在单个的文件内使用倒没什么。比如说做一些系统的插件之类的。。。
但是,作为OOP程序员。肯定是类的大集群。由无数个小类组件起的大类。
那__autoload就不实用了。我实验过。。他是在每一个新的类或者接口实例化的时候在function __autoload内查找类。
那如果用xml存储或者文本存储。那可能会造成每次加载__autoload都要在列表或者文件中查询。这样很麻烦。
况且操作起来也很麻烦。。。。
今天在看Pro.php的时候发现SPL也能够提供自动加载的方案。。其实我看了两遍没看明白。只是觉得这个解决方案可行
[php]
spl_autoload_register(null,false);
spl_autoload_extensions('.php,.inc,.class,.interface');
function myLoader1($class) {
//可以在这里查询类的路径并引用
}
function myLoader2($class) {
//同上。。。
}
spl_autoload_register('myLoader1',false);
spl_autoload_register('myLoader2',false);
$test = new SomeClass();[/php]
简单解释一下。。
spl_autoload_extensions就是扩展名了。上面的代码就是说在new这个关键字的时候在myLoad1和myLoad2两个方法中定义的目录列表查找SomeClass.php/SomeClass.inc,SomeClass.class,SomeClass.interface这些文件。
先在myLoad1中进行查找。没有找到就在myLoad2中查找。如果还没找到就抛出错误。
使用起来感觉比_autoload更为灵活。
当然。有一个限制条件。一旦启用spl_autoload_register。所有的__autoload调用全部失效。如果我没猜错,这个方法是覆盖了_autoload的。这也容易理解为什么在ZF里面开启自动加载再用自己的autoload方法加载类会失效了。
如果想__autoload不失效。在上面代码前面加入:
[php]
if(false === spl_autoload_functions()) {
if(function_exists('__autoload')) {
spl_autoload_register('__autoload',false);
}
}
//继续进行前面那一段代码。
[/php]
因为这段代码是放在之前的。所以没有调用spl_autoload的方法。自然能够让其他的__autoload生效。。而他的生效方式。是将_autoload通过spl_autoload_register注册到类查询体系之中。其实这样很容易理解。因为__autoload也就是functio n__autoload的调用形式。所以能够将他注册到SPL的自动加载体系之中。
具体怎么用。。。可以在上面的 myLoad方法提里面采用目录的递归产生一个目录树,在目录树里面写查找这个类名。找到就引入就是了。 __autoload()必须和良好的命名习惯结合起来用 但是SPL内置了的解决方案也不错啊。思路更清晰一点。因为你不可能一个文件里面同时用几个function __autoload方法吧 这个函数是只有当类不存在的时候才会执行吧,你可以测试一下看看。
还有你说需要读取配置信息的问题,如果没有固定的文件命名规范的话,就必须得读了,可是也并不是加载一次就读取一次,正确的做法应该是读取配置并进行必要的格式化之后保存成静态变量,这样下次就不用读了。 你可以测试一下看看 我想楼主你完全理解错spl里几个autoload函数的用法了
你理解错的地方有两个,第一,被spl_autoload_register所注册的函数并不是返回一个目录列表,而是自己根据传入的文件名做判断并include对应的文件。第二,spl_autoload_register注册的函数不受spl_autoload_extensions影响。 另外,注册多个autoload函数后,会根据注册的顺序从最后注册的开始往前执行,每执行一个autoload函数就检查一次该类是否已读入,如果未读入再继续调用下一个autoload函数,不会存在太严重的性能问题。
如果是应用同时需要在多个不相干的目录里包含文件,比如同时引用了ZF框架和你自己写的一些类,那么最好的办法是用set_include_path多注册几个include路径 首先你说的第一。。。我没说返回目录列表。。只是在注册的函数里面可以整合进去对应列表的文件。。
第二嘛。。。。。。还不能确信回答你。
命名规范是肯定需要的。这个毋庸置疑。。。不过不在此帖讨论之内 说反了。。是文件的列表=。=
第二点。。没影响么? [quote]原帖由 [i]某个人[/i] 于 2008-11-18 17:12 发表 [url=http://bbs.phpchina.com/redirect.php?goto=findpost&pid=730222&ptid=91695][img]http://bbs.phpchina.com/images/common/back.gif[/img][/url]
首先你说的第一。。。我没说返回目录列表。。只是在注册的函数里面可以整合进去对应列表的文件。。
[/quote]
这个就是问题的所在了,用文件来保存这个列表是效率低下的本因,与autoload函数系列无关。可惜你最后要表达的还是用一个文件来保存这个这些路径信息。
即便是用一个文件,在代码开始执行的时候就处理进来然后写入include_path或者自己定义个变量保存更好。 哦。可能是我表达错误了。。
我的意思是在myload函数内可以封装好文件列表。。比如说在应用程序启动的时候就已经定义好一个变量来存储这个文件列表供autolaod使用。 或者更好的方式。
有点像模拟java包的形式。在没个类的顶部包含进入这个自动加载文件。有他注册这个文件在一个特定的目录结构里面。事实上也就是采用了变量存储。但不是所有程序的列表都在一个变量里面。而是以文件包为单位。
正在实验中 [quote]原帖由 [i]某个人[/i] 于 2008-11-18 17:12 发表 [url=http://bbs.phpchina.com/redirect.php?goto=findpost&pid=730222&ptid=91695][img]http://bbs.phpchina.com/images/common/back.gif[/img][/url]
首先你说的第一。。。我没说返回目录列表。。只是在注册的函数里面可以整合进去对应列表的文件。。
第二嘛。。。。。。还不能确信回答你。
命名规范是肯定需要的。这个毋庸置疑。。。不过不在此帖讨论之内 [/quote]
文件有固定的命名规则的话,就没有必要再读取文件名称与路径的对应列表了,可以看一下FLEA的文件存储机制,我忘记那种方式具体叫什么名字了。大体上就是比如类的名称叫 FLEA_Action 的话,她就会自动检索所有添加的目录,寻找文件:FLEA/Action.php [quote]原帖由 [i]异度冰晶[/i] 于 2008-11-18 17:17 发表 [url=http://bbs.phpchina.com/redirect.php?goto=findpost&pid=730233&ptid=91695][img]http://bbs.phpchina.com/images/common/back.gif[/img][/url]
这个就是问题的所在了,用文件来保存这个列表是效率低下的本因,与autoload函数系列无关。可惜你最后要表达的还是用一个文件来保存这个这些路径信息。
即便是用一个文件,在代码开始执行的时候就处理进来然后写 ... [/quote]
首先有一点要确定的是,使用include_path才是效率比较低下的,因为PHP会挨个目录查找是否有这个,当include_path比较多的时候,也许要进行好几次硬盘IO操作,效率低下。
而使用文件名称与文件路径匹配列表的方式,反而效率比较高。这个列表是只需要读取一次的,然后存入静态变量,下次不再读取,虽然不的不为此多消耗一点内存,但是总的来说,效率相对与include_path要高出不少。 具体效率差别,我也没有测试过,干脆写点代码测试一下好了,等下,放出测试实例。 [quote]原帖由 [i]fhjr999[/i] 于 2008-11-18 19:04 发表 [url=http://www.phpchina.com/bbs/redirect.php?goto=findpost&pid=730409&ptid=91695][img]http://www.phpchina.com/bbs/images/common/back.gif[/img][/url]
文件有固定的命名规则的话,就没有必要再读取文件名称与路径的对应列表了,可以看一下FLEA的文件存储机制,我忘记那种方式具体叫什么名字了。大体上就是比如类的名称叫 FLEA_Action 的话,她就会自动检索所有添加 ... [/quote]
我来补充,FLEA的命名格式是:文件夹名称_文件夹名称_文件名称,然后把'_'替换成DS,就成了:文件夹名称/文件夹名称/文件.php,然后再在公告变量里的路径数组里搜索,这个路径数组是有import导入的
这样的话,事实上当用户去new一个不存在的class,如果你注册了一个__autoload()动作的话,事实上__autoad()会根据你要new的class的名称去调用FLEA::loadFile()函数来动态加载文件 [php]
$beginTime = microtime(TRUE);
/*
function __autoload($className)
{
static $config = null;
if ($config == null)
{
$config = require(ROOT . '/config.php');
}
if (isset($config[$className]))
{
require($config[$className]);
}
else
{
exit('文件 ' . $className . ' 不存在');
}
}*/
define('ROOT', dirname(__FILE__));
require(ROOT . '/test1.php');
require(ROOT . '/test2.php');
require(ROOT . '/test3.php');
require(ROOT . '/test4.php');
require(ROOT . '/test5.php');
$test1 = new test1();
$test2 = new test2();
$test3 = new test3();
$test4 = new test4();
$test5 = new test5();
echo microtime(TRUE) - $beginTime;
[/php]
分别注释掉,autoload函数或者那5条require进行测试。
使用autoload:平均执行时间大概在0.00075。
使用require:平均执行时间大概在0.0006。
值得一提的是,这样测试对autoload并不公平,因为使用require的时候,不可能就这样简单的使用,你至少要用require_once,或者其他的保障一个文件只会被加载一次的方法。
而且更重要的,在一些特殊情况下,比如MVC框架中,我完全可以改进autoload,将每个页面使用这个加载的文件缓冲进运行缓存,也就是说,每个页面只需要加载一次运行缓存即可,而autoload一次也不会运行或者只运行一次,除非运行缓存失效,这样的话,绝对是使用autoload性能占优。
下面附上测试代码。
[[i] 本帖最后由 fhjr999 于 2008-11-18 19:38 编辑 [/i]]
异形 autoload
[php]/**
* 进化版的 AUTOLOAD,只有示意,具体代码自己实现
*
* @param string $className
*/
function __autoload($className)
{
$runtimeId = ''; // 运行缓存ID,在MVC框架中,使用控制器与操作名来设定
require_once(RUNTIME_DIR . '/' . $runtimeId . '.php'); // 加载运行缓存,在文件第一次运行之时,autoload函数会被执行多次,因此,就这么写了,避免重复加载。
if (class_exists($className, false)) // autoload加载完运行缓存时,不管是因为哪个类诱发了这个函数的运行,反正只要加载完运行缓存,还没有找到这个类的话,那就开始要将这个类写入运行缓存了
{
return true;
}
static $config = null;
if ($config == null)
{
$config = require(ROOT . '/config.php'); // 这种文件加载方式是根据LZ的意思来的,当然也可以使用FLEA中那种方式,而且那种方式跟这个变种autoload结合,也并不会带来性能上的损失。
}
if (!isset($config[$className])) // 如果配置文件钟没有这个类的路径的话,OK,当然要来一条错误消息了
{
exit('文件不存在:' . $className);
}
// 下面肉戏来了。
require($config[$className]); // 这个文件还是要加载一次,因为我可不想先写运行缓存,然后再加载运行缓存,那样可就真的完蛋大吉了。
// 下面开始写运行缓存
$content = php_strip_whitespace(file_get_contents($config[$className])); // 现将这次加载的类读进来,并且去掉里边的注释和空白字符,php_strip_whitespace函数是个好东西,可是只有PHP5才能用,想自己写一个,又爬影响到正常代码,郁闷
file_put_contents(RUNTIME_DIR . '/' . $runtimeId . '.php', $content, FILE_APPEND); // 这里还应该对文件内容处理一下,比如处理<?php 标记,我就不写了。
// 只要这个页面中的所有类都写入了运行缓存,那么autoload就只会在声明第一个类的时候运行一次即可
// 为什么要用运行缓存:首先运行缓存可以没有注释,没有空白字符,因为这个不需要你手动编写,这样在没有中间吗缓冲的服务器上可以大幅度提高性能。
// 其次,加载一批小文件要比加载一个体积为这些小文件之和的大文件,资源开销大的多。
// 但是,这样做,也同样带来了麻烦,那就是目前我还没有想到如何简单高效的在运行缓冲钟的那些文件的原文件被修改之后,自动更新缓存。
// 但是,我们可以换一种方式,就是给autoload增加一个开关,网站建设时,不使用运行缓存功能,而当网站建设完毕正式运行之后才开启。
}
[/php]
[[i] 本帖最后由 fhjr999 于 2008-11-18 20:09 编辑 [/i]] autoload首先肯定会带来效率上的问题,它是为了解决没有命名空间带来的麻烦(每个类都必须手动加载,非常麻烦)。但5.3之后有了命名空间。可以说autoload是个过渡性质的产品。。。
所以在使用autoload的时候,有几点要注意的:
第一、autoload尽可能的简单高效。
第二、没有规则的类尽可能的手动加载。 版主大人说得很有道理。。。
以上朋友的提议。。明天上午我全部实验过再来给大家说。。
现在我需要深入了解SQL的autolaod的机制。。看起来很强大。
页:
[1]
2
