I mean
虽然鸟哥的文档已经介绍了,但是在实际应用中,我们总是会有一些特殊,而且根据鸟哥的文档,无法推理得知,必须从源码得知,故下面的内容基本上从源码中取得。
Yaf/Loader自动加载
Yaf/Loader相关的几个配置
PHP运行时配置
1
2
3
yaf.use_namespace 开启命名空间
yaf.use_spl_autoload 开启之后,可由PHP的自动加载函数加载,关闭是为了高效,Yaf只加载一次。
yaf.library 全局类库目录路径
Yaf应用配置
1
2
3
application.library 本地类库目录路径
application.library.directory 本地类库目录路径
application.library.namespace 以逗号分隔的本地库命名空间前缀
Yaf/Loader自动加载策略
1)如果未配置yaf.library和application.library时,Yaf_Loader::$_library及Yaf_Loader::$_global_library都将设置为[application.directory]/library;故不管是否配置application.library.namespace或者Yaf/Loader::registerLocalNamespace()是否注册本地命名空间前缀,加载类文件时,自动到[application.directory]/library目录查找类并加载。
2)如果配置了application.library时,但未配置application.library.namespace时或者未通过Yaf/Loader::registerLocalNamespace()注册本地命名空间前缀,不管yaf.library是否配置都到yaf.library中加载相应类文件。
3)如果配置了application.library和application.library.namespace,且类名中包含配置的命名空间前缀,则到application.library加载相应的类文件,否则到yaf.library中加载相应类文件。
4)Yaf内部中加载文件时,类名中有”_”会转换为目录分隔符。
yaf_loader.c
1
int yaf_internal_autoload(char *file_name, uint name_len, char **directory TSRMLS_DC);
443-456行,可以看到:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
while (1) {
while(++q && *q != '_' && *q != '/0');
if (*q != '/0') {
seg_len = q - p;
seg = estrndup(p, seg_len);
smart_str_appendl(&buf, seg, seg_len);
efree(seg);
smart_str_appendc(&buf, DEFAULT_SLASH);
p = q + 1;
} else {
break;
}
}
完成了”_”转换为目录分隔符。
Yaf/Dispatcher路由分发
yaf_dispatcher.c
1
static inline void yaf_dispatcher_fix_default(yaf_dispatcher_t *dispatcher, yaf_request_t *request TSRMLS_DC);
260-274行,可以看到:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
q = p;
*q = toupper(*q);
while (*q != '/0') {
if (*q == '_'
#ifdef YAF_HAVE_NAMESPACE
|| *q == '//'
#endif
) {
if (*(q+1) != '/0') {
*(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做简单一个替换:
定义插件
1
2
3
4
5
6
7
8
9
10
11
12
13
use Yaf/Plugin_Abstract;
use Yaf/Request_Abstract;
use Yaf/Response_Abstract;
class RoutePlugin extends Plugin_Abstract
{
public function routerShutdown(Request_Abstract $request, Response_Abstract $response){
$controller = $request->getControllerName();
$controller = str_replace('_', '', $controller);
$request->setControllerName($controller);
}
}
注册插件
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
use Yaf/Bootstrap_Abstract;
use Yaf/Dispatcher;
class Bootstrap extends Bootstrap_Abstract
{
/**
* 注册插件
*
* @access public
* @param Yaf/Dispatcher $dispatcher
* @return void
*/
public function _initPlugin(Dispatcher $dispatcher)
{
$dispatcher->registerPlugin(new RoutePlugin ());
}
}
1
http://backend.phpboy.net/demo/aca_stat_job/write
我们来分析一下解析流程:
Yaf/Application::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不雅观,想把”_”换成”/“,如:
那么,在默认路由下你就不要传递url参数了,也就是说demo/aca/stat/job/write全部当作路由参数,不是数据参数,将头尾分别当前模块名和动作名,中间为控制器名,在routerShutdown拼接一个控制器,如:
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
28
29
30
31
use Yaf/Plugin_Abstract;
use Yaf/Request_Abstract;
use Yaf/Response_Abstract;
class RoutePlugin extends Plugin_Abstract
{
public function routerShutdown(Request_Abstract $request, Response_Abstract $response){
$controller = $request->getControllerName();
$controller .= ucfirst($request->getActionName());
$params = $request->getParams();
$length = count($params);
$action = '';
$i = 1;
foreach ($params as $key => $value) {
if ($length == $i) {
if (is_null($value)) {
$action = $key;
} else {
$controller .= ucfirst($key);
$action = $value;
}
} else {
$controller .= ucfirst($key).ucfirst($value);
}
$i++;
}
$request->setControllerName($controller);
$request->setActionName($action);
}
}
当然这是最蛋疼的用法 ,不是么?