Yaf Loader自动加载策略及Dispatcher路由分发策略

PHPABC PHP开发 2,844 次浏览 , , 没有评论

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
<?php
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
<?php
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下面的子目录首字母要大写。

最后还有强迫症患者说

http://backend.phpboy.net/demo/aca_stat_job/write

这样的Url不雅观,想把”_”换成”/“,如:

http://backend.phpboy.net/demo/aca/stat/job/write

那么,在默认路由下你就不要传递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
<?php
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);
    }
}

当然这是最蛋疼的用法 ,不是么?

发表评论

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

此站点使用Akismet来减少垃圾评论。了解我们如何处理您的评论数据

Go