Yaf零基础学习总结8-Yaf中的路由和路由协议

路由器主要负责解析一个请求并且决定什么module、controller、action被请求;它同时也定义了一种方法来实现用户自定义路由,这也使得它成为最重要的一个MVC组组件。为了方便自定义路由, Yaf摒弃了0.1版本中的自定义路由器方式, 而采用了更为灵活的路由器和路由协议分离的模式。也就是一个固定不变的路由器, 配合各种可自定义的路由协议, 来实现灵活多变的路由策略.

作为一个应用中的路由组件是很重要的,理所当然的路由组件是抽象的,这样允许作为开发者的我们很容易的设计出我们自定义的路由协议.然而,默认的路由组件其实已经服务得我们很好了.记住,如果我们需要一个非标准的路由协议时候,我们就可以自定义一个自己的路由协议,而不用采用默认的路由协议。Yaf的路由组件由两部分组成,就是路由和路由协议

路由协议事实上主要负责匹配我们预先定义好的路由协议,意思就是我们只有一个路由器,但我们可以有许多路由协议. 路由器主要负责管理和运行路由链,它根据路由协议栈倒序依次调用各个路由协议, 一直到某一个路由协议返回成功以后, 就匹配成功.

路由的过程发生派遣过程的最开始,并且路由解析仅仅发生一次.路由过程在何控制器动作(Controller, Action)被派遣之前被执行,一旦路由成功,路由器将会把解析出得到的信息传递给请求对象(Yaf_Request_Abstract object), 这些信息包括moduel、controller、action、用户params等. 然后派遣器(Yaf_Dispatcher)就会按照这些信息派遣正确的控制器动作. 路由器也有插件钩子,就是routerStartup和routerShutdown,他们在路由解析前后分别被调用.

默认的路由协议

默认情况下,Yaf的路由器是Yaf_Router, 而默认使用的路由协议是Yaf_Route_Static,是基于HTTP路由的, 它期望一个请求是HTTP请求并且请求对象是使用Yaf_Request_Http

使用路由

使用路由既可以让之很复杂,同时也能让它很简单,这是归于你的应用。然而使用一个路由是很简单的,你可以添加你的路由协议给路由器,Yaf为我们提供了6种路由协议,分别如下:

Yaf_Route_Simple 
Yaf_Route_Supervar 
Yaf_Route_Static 
Yaf_Route_Map 
Yaf_Route_Rewrite 
Yaf_Route_Regex

下面我们会分别举例说明如何使用这些路由协议来达到我们的目的。

首先我们得了解路由器和路由协议是如何工作的,就是我们得先得到一个路由器实例,Yaf中通过派遣器的getRouter方法来得到默认的路由器,代码如下:

[php] view plaincopy

  1. <?php
  2. //通过派遣器得到默认的路由器
  3. $router = Yaf_Dispatcher::getInstance()->getRouter();
  4. ?>

得到路由器之后我们可以用路由器来添加路由协议了。我们可以通过配置文件来添加路由协议,示例如下

[common]
     ;自定义路由
     ;顺序很重要
     routes.regex.type="regex"
     routes.regex.match="#^/list/([^/]*)/([^/]*)#"
     routes.regex.route.controller=Index
     routes.regex.route.action=action
     routes.regex.map.1=name
     routes.regex.map.2=value
     ;添加一个名为simple的路由协议
     routes.simple.type="simple"
     routes.simple.controller=c
     routes.simple.module=m
     routes.simple.action=a
     ;添加一个名为supervar的路由协议
     routes.supervar.type="supervar"
     routes.supervar.varname=r

     [product : common]
     ;product节是Yaf默认关心的节, 添加一个名为rewrite的路由协议
     routes.rewrite.type="rewrite"
     routes.rewrite.match="/product/:name/:value"

然后在Bootstrap中通过调用Yaf_Router::addConfig添加定义在配置中的路由协议

[php] view plaincopy

  1. <?php
  2. class Bootstrap extends Yaf_Bootstrap_Abstract{
  3. public function _initRoute(Yaf_Dispatcher $dispatcher) {
  4. $router = Yaf_Dispatcher::getInstance()->getRouter();
  5. /**
  6. * 添加配置中的路由
  7. */
  8. $router->addConfig(Yaf_Registry::get("config")->routes);
  9. }
  10. }

默认路由协议Yaf_Route_Static

默认的路由协议Yaf_Route_Static, 就是分析请求中的request_uri, 在去除掉base_uri以后, 获取到真正的负载路由信息的request_uri片段, 具体的策略是, 根据"/"对request_uri分段, 依次得到Module,Controller,Action, 在得到Module以后, 还需要根据Yaf_Application::$modules来判断Module是否是合法的Module, 如果不是, 则认为Module并没有体现在request_uri中, 而把原Module当做Controller, 原Controller当做Action:

使用示例:

[php] view plaincopy

  1. <?php
  2. /**
  3. * 对于请求request_uri为"/ap/foo/bar/dummy/1"
  4. * base_uri为"/ap"
  5. * 则最后参加路由的request_uri为"/foo/bar/dummy/1"
  6. * 然后, 通过对URL分段, 得到如下分节
  7. * foo, bar, dummy, 1
  8. * 然后判断foo是不是一个合法的Module, 如果不是, 则认为结果如下:
  9. */
  10. array(
  11. ‘module‘     => ‘默认模块‘,
  12. ‘controller‘ => ‘foo‘,
  13. ‘action‘     => ‘bar‘,
  14. ‘params‘     => array(
  15. ‘dummy‘ => 1,
  16. )
  17. )
  18. /**
  19. * 而如果在配置文件中定义了ap.modules="Index,Foo",
  20. * 则此处就会认为foo是一个合法模块, 则结果如下
  21. */
  22. array(
  23. ‘module‘     => ‘foo‘,
  24. ‘controller‘ => ‘bar‘,
  25. ‘action‘     => ‘dummy‘,
  26. ‘params‘     => array(
  27. 1 => NULL,
  28. )
  29. )

当只有一段路由信息的时候, 比如对于上面的例子, 请求的URI为/ap/foo, 则默认路由和下面要提到的Yaf_Route_Supervar会首先判断ap.action_prefer, 如果为真, 则把foo当做Action, 否则当做Controller

Yaf_Route_Simple

Yaf_Route_Simple是基于请求中的query string来做路由的, 在初始化一个Yaf_Route_Simple路由协议的时候, 我们需要给出3个参数, 这3个参数分别代表在query string中Module, Controller, Action的变量名:

使用实例:

[php] view plaincopy

  1. <?php
  2. /**
  3. * 指定3个变量名
  4. */
  5. $route = new Yaf_Route_Simple("m", "c", "a");
  6. $router->addRoute("name", $route);
  7. /**
  8. * 对于如下请求: "http://domain.com/index.php?c=index&a=test
  9. * 能得到如下路由结果
  10. */
  11. array(
  12. ‘module‘     => ‘默认模块‘,
  13. ‘controller‘ => ‘index‘,
  14. ‘action‘     => ‘test‘,
  15. )

只有在query string中不包含任何3个参数之一的情况下, Yaf_Route_Simple才会返回失败, 将路由权交给下一个路由协议.

Yaf_Route_Supervar

Yaf_Route_Supervar和Yaf_Route_Simple相似, 都是在query string中获取路由信息, 不同的是, 它获取的是一个类似包含整个路由信息的request_uri

使用实例:

[php] view plaincopy

  1. <?php
  2. /**
  3. * 指定supervar变量名
  4. */
  5. $route = new Yaf_Route_Supervar("r");
  6. $router->addRoute("name", $route);
  7. /**
  8. * 对于如下请求: "http://domain.com/index.php?r=/a/b/c
  9. * 能得到如下路由结果
  10. */
  11. array(
  12. ‘module‘     => ‘a‘,
  13. ‘controller‘ => ‘b‘,
  14. ‘action‘     => ‘c‘,
  15. )

在query string中不包含supervar变量的时候, Yaf_Route_Supervar会返回失败, 将路由权交给下一个路由协议.

Yaf_Route_Map

Yaf_Route_Map议是一种简单的路由协议, 它将REQUEST_URI中以‘/‘分割的节, 组合在一起, 形成一个分层的控制器或者动作的路由结果. Yaf_Route_Map的构造函数接受俩个参数, 第一个参数表示路由结果是作为动作的路由结果,还是控制器的路由结果. 默认的是动作路由结果. 第二个参数是一个字符串, 表示一个分隔符, 如果设置了这个分隔符, 那么在REQUEST_URI中, 分隔符之前的作为路由信息载体, 而之后的作为请求参数.

使用实例:

[php] view plaincopy

  1. <?php
  2. /**
  3. * 对于请求request_uri为"/ap/foo/bar"
  4. * base_uri为"/ap"
  5. * 则最后参加路由的request_uri为"/foo/bar"
  6. * 然后, 通过对URL分段, 得到如下分节
  7. * foo, bar
  8. * 组合在一起以后, 得到路由结果foo_bar
  9. * 然后根据在构造Yaf_Route_Map的时候, 是否指明了控制器优先,
  10. * 如果没有, 则把结果当做是动作的路由结果
  11. * 否则, 则认为是控制器的路由结果
  12. * 默认的, 控制器优先为FALSE
  13. */

Yaf_Route_Rewrite

Yaf_Route_Rewrite是一个强大的路由协议, 它能满足我们绝大部分的路由需求:

使用实例:

[php] view plaincopy

  1. <?php
  2. //创建一个路由协议实例
  3. $route = new Yaf_Route_Rewrite(
  4. ‘product/:ident‘,
  5. array(
  6. ‘controller‘ => ‘products‘,
  7. ‘action‘ => ‘view‘
  8. )
  9. );
  10. //使用路由器装载路由协议
  11. $router->addRoute(‘product‘, $route);

在这个例子中, 我们试图匹配Url指定到一个单一的产品, 就像http://domain.com/product/choclolat-bar. 为了实现这点, 我们在路由协议中传递了2个变量到路由协议Yaf_Route_Rewrite的构造函数其中. 第一个变量(‘product/:indent‘)就是匹配的路径, 第二个变量(array变量)是路由到的动作控制器; 路径使用一个特别的标识来告诉路由协议如何匹配到路径中的每一个段,这个标识有有两种,可以帮助我们创建我们的路由协议,如下所示:

a) : 
b) * 
冒号(:)指定了一个段,这个段包含一个变量用于传递到我们动作控制器中的变量,我们要设置好事先的变量名,比如在上面我们的变量名就是‘ident‘,因此,倘若我们访问http://domian.com/product/chocoloate-bar将会创建一个变量名为ident并且其值是‘chocoloate-bar‘的变量,我们然后就可以在我们的动作控制器ProductsController/viewAction下获取到它的值:$this->getRequest()->getParam(‘ident‘);

星号(*)被用做一个通配符, 意思就是在Url中它后面的所有段都将作为一个通配数据被存储. 例如,如果我们有路径‘path/product/:ident/*‘(就是路由协议中设置的第一个变量), 并且我们访问的Url为http://domain.com/product/chocolate-bar/test/value1/another/value2,那么所有的在‘chocolate-bar‘后面的段都将被做成变量名/值对,因此这样会给我们下面的结果:

ident = chocolate-bar 
test = value1 
another = value2

这种行为也就是我们平常默认使用的路由协议的行为,记住变量名/值要成对出现,否则像/test/value1/这样的将不会这种另一个变量,我们有静态的路由协议部分,这些部分简单地被匹配来满足我们的路由协议,在我们的例子中,静态部分就是product; 就像你现在看到的那样,我们的Yaf_Route_Rewrite路由协议提供给我们极大的灵活性来控制我们的路由

Yaf_Route_Regex

到目前为止,我们之前的路由协议都很好的完成了基本的路由操作,我们常用的也是他们,然而它们会有一些限制,这就是我们为什么要引进正则路由(Yaf_Route_Regex)的原因. 正则路由给予我们preg正则的全部力量,但同时也使得我们的路由协议变得更加复杂了一些.即使是他们有点复杂,我还是希望你能好好掌握它,因为它比其他路由协议要灵活一点点. 一开始,我们先对之前的产品案例改用使用正则路由

使用实例:

[php] view plaincopy

  1. <?php
  2. $route = new Yaf_Route_Regex(
  3. ‘product/([a-zA-Z-_0-9]+)‘,
  4. array(
  5. ‘controller‘ => ‘products‘,
  6. ‘action‘ => ‘view‘
  7. )
  8. );
  9. $router->addRoute(‘product‘, $route);

你可以看到,我们现在移动我们的正则到我们的path(构造函数的第一个参数)中来了,就像之前的那样,这个正则路由协议现在应该是匹配是一个数字、字母、-和_组成的ident变量的字符提供给我们,但是,你一定会问,ident变量在哪呢?好,如果你使用了这个正则路由协议,我们可以通过变量1(one)来获取其值,即可以在控制器里用:$this->getRequest()->getParam(1)来获取,其实这里如果看过正则的都知道这就是反向引用中的\1.然而,你一定会想为什么要定义的这么的垃圾,我们不能够记住或者弄清每一个数字代表的是什么变量(其实我刚开始看的时候也是一样的感受).为了改变这点,正则路由协议的构造函数提供了第3个参数来完成数字到变量名的映射:

[php] view plaincopy

  1. <?php
  2. $route = new Yaf_Route_Regex(
  3. ‘product/([a-zA-Z-_0-9]+)‘,
  4. array(
  5. ‘controller‘ => ‘products‘,
  6. ‘action‘ => ‘view‘
  7. ),
  8. array(
  9. //完成数字到字符变量的映射
  10. 1 => ‘ident‘
  11. )
  12. );
  13. $router->addRoute(‘product‘, $route);

这样,我们就简单的将变量1映射到了ident变量名,这样就设置了ident变量,同时你也可以在控制器里面获取到它的值.

自定义路由协议

当然, 这个世界上没有绝对的事情. 所以万一现在所有的路由协议都不能满足你的需求, 那么你可以自己实现你自己的路由协议, 你要做的是, 申明你的路由协议实现了Yaf_Route_Interface接口即可。这里就不对自定义路由协议再做过多的说明了。

路由注册的顺序很重要, 最后注册的路由协议, 最先尝试路由, 这就有个陷阱. 请注意.

时间: 2024-12-28 05:02:40

Yaf零基础学习总结8-Yaf中的路由和路由协议的相关文章

Yaf零基础学习总结3-Hello Yaf

Yaf零基础学习总结3-Hello Yaf 上一次我们已经学习了如何安装yaf了,准备工作做好了之后我们来开始实际的编码了,码农都知道一个经典的语句就是“Hello World”了,今天我们开始入手Yaf,目标只要能得到“Hello Yaf”就可以了,不多说,下面开始了.(注:需要读者有一定的MVC知识基础,最好用过一些其他简单的框架) 1.组织目录结构 对于使用过框架的来说这点很好理解,对于没使用过框架的来说建议先别在这死磕,先记着吧,简单点就是对于我们的文件结构做一些规定,让自己和别人都能看

《Yaf零基础学习总结》系列技术文章整理收藏

<Yaf零基础学习总结>系列技术文章整理收藏 Yaf零基础学习总结系列整理了Yaf的基础知识,对于Yaf入门的学习有很大的参考借鉴,收藏在这里,自己用到Yaf的时候随时查阅 1关于Yaf的一些说明[转Yaf作者]http://www.lai18.com/content/407120.html 2Yaf零基础学习总结1-Yaf框架简介http://www.lai18.com/content/407123.html 3Yaf零基础学习总结2-Yaf框架的安装http://www.lai18.com

Yaf零基础学习总结5-Yaf类的自动加载

Yaf零基础学习总结5-Yaf类的自动加载 框架的一个重要功能就是类的自动加载了,在第一个demo的时候我们就约定自己的项目的目录结构,框架就基于这个目录结构来自动加载需要的类文件. Yaf在自启动的时候, 会通过SPL注册一个自己的Autoloader, 出于性能的考虑, 对于框架相关的MVC类, Yaf Autoloader只以目录映射的方式尝试一次. 具体的目录映射规则如下: 对于控制器 Controller 默认模块下为{项目路径}/controllers/, 否则为{项目路径}/mod

Yaf零基础学习总结4-Yaf的配置文件

在上一节的hello yaf当中我们已经接触过了yaf的配置文件了, Yaf和用户共用一个配置空间, 也就是在Yaf_Application初始化时刻给出的配置文件中的配置. 作为区别, Yaf的配置项都以ap开头. Yaf的核心必不可少的配置项只有一个(其实, 这个也可以有默认参数, 但是作者觉得完全没有配置, 显得太寒酸了). Yaf通过在不同的环境中, 选取不同的配置节, 再结合配置可继承, 来实现一套配置适应多种环境(线上,测试,开发). yaf一个必不可少的配置项,也就是配置应用项目的

Yaf零基础学习总结2-Yaf框架的安装

接着上一篇文章<Yaf零基础学习总结1-Yaf框架简介>我们对Yaf框架有那么一个大概的了解了,但是对于程序员来说,那些文字都是表面的,他们最想的就是开始敲代码了.当然这也是学习Yaf框架最有效的方法,敲得越多肯定学得越快,对于任何语言来说也是一样的,在写代码之前我们得把Yaf框架安装上. 相比于其它PHP框架,Yaf是作为PHP的一个扩展发行的,我们也知道他是基于C语言实现的,我们看不到它的PHP代码,当然有C语言背景的人可以去看下他的C源码.知道安装PHP扩展的人基本可以略过本文了,因为Y

[原]零基础学习视频解码之FFMpeg中比较重要的函数以及数据结构

在正式开始解码练习前先了解下关于FFmpeg中比较重要的函数以及数据结构. 1. 数据结构:  (1) AVFormatContext  AVFormatContext是一个贯穿始终的数据结构,很多函数都要用到它作为参数.FFmpeg代码中对这个数据结构的注释是:format I/O context 此结构包含了一个视频流的格式内容.其中存有了AVInputFormat(or AVOutputFormat同一时间AVFormatContext内只能存在其中一个),和AVStream.AVPack

yaf零基础学习总结6-学习使用Yaf中的插件

Bootstrap, 也叫做引导程序. 它是Yaf提供的一个全局配置的入口, 在Bootstrap中, 你可以做很多全局自定义的工作.在一个Yaf_Application被实例化之后, 运行(Yaf_Application::run)之前, 可选的我们可以运行Yaf_Application::bootstrap [php] view plaincopy <?php $app = new Yaf_Application("conf.ini"); $app ->bootstra

yaf零基础学习总结7-学习使用Yaf中的插件

Yaf支持用户定义插件来扩展Yaf的功能, 这些插件都是一些类. 它们都必须继承自Yaf_Plugin_Abstract. 插件要发挥功效, 也必须现实的在Yaf中进行注册, 然后在适当的实际, Yaf就会调用它. 也许大家会问这个插件是个什么概念,有什么用呢,其实我们用插件主要是用到Yaf框架中支持的Hook(钩子),Yaf中定义了6个Hook,他们分别是: Yaf中支持的6个Hook 1.routerStartup 这个会在路由之前出发,也就是路由之前会调用这个Hook ,这个是7个事件中,

零基础学习视频解码之FFMpeg中比较重要的函数以及数据结构(转)

在正式开始解码练习前先了解下关于FFmpeg中比较重要的函数以及数据结构. 1. 数据结构:  (1) AVFormatContext  AVFormatContext是一个贯穿始终的数据结构,很多函数都要用到它作为参数.FFmpeg代码中对这个数据结构的注释是:format I/O context 此结构包含了一个视频流的格式内容.其中存有了AVInputFormat(or AVOutputFormat同一时间AVFormatContext内只能存在其中一个),和AVStream.AVPack