一、自动加载(YafLoader)
1.YafLoader相关的几个配置
PHP运行时配置
yaf.use_namespace 开启命名空间
yaf.use_spl_autoload 开启之后,可由PHP的自动加载函数加载,关闭是为了高效,Yaf只加载一次。
yaf.library 全局类库目录路径
Yaf应用配置
application.library 本地类库目录路径
application.library.directory 本地类库目录路径
application.library.namespace 以逗号分隔的本地库命名空间前缀
2.YafLoader自动加载策略
1)如果未配置yaf.library和application.library时,Yaf_Loader::$_library及Yaf_Loader::$_global_library都将设置为[application.directory]/library;故不管是否配置application.library.namespace或者YafLoader::registerLocalNamespace()是否注册本地命名空间前缀,加载类文件时,自动到[application.directory]/library目录查找类并加载。
2)如果配置了application.library时,但未配置application.library.namespace时或者未通过YafLoader::registerLocalNamespace()注册本地命名空间前缀,不管yaf.library是否配置都到yaf.library中加载相应类文件。
3)如果配置了application.library和application.library.namespace,且类名中包含配置的命名空间前缀,则到application.library加载相应的类文件,否则到yaf.library中加载相应类文件。
4)Yaf内部中加载文件时,类名中有"_"会转换为目录分隔符。
yaf_loader.c
int yaf_internal_autoload(char *file_name, uint name_len, char **directory TSRMLS_DC);
443-456行,可以看到:
代码如下 | 复制代码 |
while (1) {
|
完成了"_"转换为目录分隔符。
2.路由分发(YafDispatcher)
yaf_dispatcher.c
static inline void yaf_dispatcher_fix_default(yaf_dispatcher_t *dispatcher, yaf_request_t *request TSRMLS_DC);
260-274行,可以看到:
代码如下 | 复制代码 |
q = p; *q = toupper(*q); while (*q != '') { if (*q == '_' #ifdef YAF_HAVE_NAMESPACE || *q == '' #endif ) { if (*(q+1) != '') { *(q+1) = toupper(*(q+1)); q++; } } q++; } |
会将路由解析中的模块名、控制器名先转换为小写,然后将首字母大写,其中控制器名会将"_",""后面的一个字符转换为大写(源码展示了控制器名的处理)。
另外,还需要明确的是yaf支持的六个hook中的routerShutdown触发是在yaf_dispatcher_fix_default之后,而每次路由结束都会调用yaf_dispatcher_fix_default,这意味着什么?下面介绍。
二、实际应用
基于上述源码的分析,我们可以做什么呢?
默认情况下controllers下面的controller类文件,只能是首字母大写,如:
application/Modules/Demo/controllers/Acastatjob.php
可能有些强迫症患者就想这般:
application/Modules/Demo/controllers/AcaStatJob.php
对不起,直接这样会提示,无法找到类application/Modules/Demo/controllers/Acastatjob.php文件
如何搞?我们要写一个插件,在routerShutdown做简单一个替换:
定义插件
代码如下 | 复制代码 |
use YafPlugin_Abstract; class RoutePlugin extends Plugin_Abstract |
注册插件
代码如下 | 复制代码 |
|
我们来分析一下解析流程:
YafApplication::app()->bootstrap()->getDispatcher->dispatch();
1.在yaf_dispatcher_route中,完成路由解析,得到module=demo,controller=aca_stat_job,action=write
2.在yaf_dispatcher_fix_default中,通过其处理得到module=Demo,controller=Aca_Stat_Job,action=write
3.在2中完成之后,通过hook机制,Request_Abstract::getControllerName(),然后将"_"替换为"",并setControllerName,其结果为module=Demo,controller=AcaStatJob,action=write
4.在yaf_internal_autoload中完成自动加载类文件,application/Modules/Demo/controllers/AcaStatJob.php
5.执行writeAction
然后假如我们不写plugin会怎样?
1.在yaf_dispatcher_route中,完成路由解析,得到module=demo,controller=aca_stat_job,action=write
2.在yaf_dispatcher_fix_default中,通过其处理得到module=Demo,controller=Aca_Stat_Job,action=write
3.在yaf_internal_autoload中,将类名中的"_"替换为"/"
4.在yaf_internal_autoload中完成自动加载类文件,application/Modules/Demo/controllers/Aca/Stat/Job.php,加载失败。
虽然加载失败,却给出了我们一个信息,可以加载分级目录的controller,但是注意controllers下面的子目录首字母要大写。
那么,在默认路由下你就不要传递url参数了,也就是说demo/aca/stat/job/write全部当作路由参数,不是数据参数,将头尾分别当前模块名和动作名,中间为控制器名,在routerShutdown拼接一个控制器,如:
代码如下 | 复制代码 |
use YafPlugin_Abstract; class RoutePlugin extends Plugin_Abstract |
当然这是最蛋疼的用法 ,不是么?