在网上找关于Yii的过滤器内容实在太少,而且大多数讲得不得要领。只好读源码来理解。现把笔记整理如下:
正常情况下,访问某个action
时,yii会通过调用CController
的 runActionWithFilters
方法来执行action
。核心代码如下:
1 | //CController.php CController是所有controller的基类 |
即:将当前controller
对象 + action
对象 + $filters
一起传递给CFilterChain
的create
方法,创建一个过滤器的链[filter-chain]–$filterChain
。然后执行该 $filterChain
的 run()
方法。
在此并没有看到执行 action
部分的代码,因为执行action
的代码在 $filterChain
的 run()
方法中实现。
过滤器链-FilterChain
上述用到的变量$filterChain
是一个 CFilterChain
对象,本质上是一个List
对象(CList
),一个链式结构。
该对象包含一个数组,数组元素是一个个filter
对象(CFilter
)。另外包含一个游标,用来保存当前指向的filter下标。
下面是CFilterChain
的 run()
方法
1 | public function run() |
该 run()
方法完成两件事:
- 如果有未执行的
filter
则执行filter $filter->filter($this)
- 执行完最后一个
filter
后执行action()
因此,每个 filter
对象 必须有filter()
方法。每个 filter
在完成业务逻辑后,要继续调用 $filterChain
的 run()
方法。直到最后一个 filter
通过后,执行 action
。
Yii中的filter
当前controller
中,各个action需要用到的filter
都在controller
的filters()
方法中定义。即:runActionWithFilters()
方法中的$filters
变量的值通过调用当前controller
的filters()
方法获得。
Yii中可调用的filter
分两类,在controller
中分别以字符串和数组的方式调用。两类filter
在controller
中的调用方法如下:
1 | public function filters(){ |
在创建CFilterChain
对象时,会将filters()
方法中定义的不同filter
分别以不同的方式生成。以字符串调用的会生成CFilterInline
对象;以数组调用的,必须是已定义的类,继承自CFilter
的类,会被yii以创建组件的方式创建为对象,本质上为使用new关键字。
CInlineFilter
这是第一类,以字符串方式调用的Filter
,会生成CInlineFilter
对象。如常见的postOnly
,accessControl
等。这类filter
的 filter()
方法实现很简单,本质上是调用当前controller
中以filter
开头的方法,如:
1 | //CInlineFilter.php |
该方式要求对应的filter
方法中必须有调用$filterChain->run()
的逻辑操作,如 postOnly
的实现:
1 | //CController.php |
Filter类
这是第二类以数组方式调用的Filter
。本质上为继承自CFilter
的类。只要该类实现的preFilter
方法即可。父类(CFilter
)中的run
方法会调用 $filterChain->run()
,如:
1 | //CFilter.php |
preFilter和postFilter
每个Filter类都包含preFilter和postFilter。FilterChain中的filter
执行顺序一般为:
1 | filter1.preFilter -> filter2.preFilter -> ... filterN.preFilter -> actioin -> filterN.postFilter -> ... filter2.postFilter -> filter1.postFilter |
因此为了在filter
中进行访问控制,一般都在preFilter()
方法中写过滤逻辑。如果写到postFilter()
,等到执行的时候action
已经执行完毕了。
常用filter及定义方式
最常用到的filter
就是yii自带的三个CInlineFilter
: postOnly
ajaxOnly
accessControl
,调用的操作如下:
1 | public function filters(){ |
如上述所述,三个filter
都是CInlineFilter
,本质上是调用CController
中定义的对应的filter
方法:filterPostOnly
filterAjaxOnly
filterAccessControl
。