接触Laravel一段时间,对它的一些操作的原理比较感兴趣,比如Cache::get('a','b')
。
这短短的一条语句背后,Laravel进行了哪些工作,让它变成现实呢?今天,我特地研究了一下它的工作原理。理解了这些原理之后,我们也可以实现自己的Facade
。
要想操作Cache
的get
方法,必须先有Cache
这个类。那么这个类从哪来呢?打开项目app/config/app.php
可以看到有一个aliase
数组,这里定义了一些框架自带类的别名。我们可以看到,调用Cache
类,其实是调用了Illuminate\Support\Facades\Cache这
个类。
给类定义别名,可以使用class_alias这个函数:
class_alias('Illuminate\Support\Facades\Cache','Cache',true);
来看看Illuminate\Support\Facades\Cache
这个类是如何定义的:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
|
<?php
namespace Illuminate\Support\Facades;
/**
* @see \Illuminate\Cache\CacheManager
* @see \Illuminate\Cache\Repository
*/
class Cache extends Facade {
/**
* Get the registered name of the component.
*
* @return string
*/
protected static function getFacadeAccessor() { return 'cache'; }
}
|
有没有发现这个类里根本没有get
这个方法?这到底是怎么回事?这就引出了今天的主题Facde
。
可以看到,Cache
类继承了Facade
类。Facade
其实是一个静态代理(static proxy)
,它并不提供具体的操作方法,而是实例化一个真正具有该方法的对象,并调用这个方法。我们不妨打开Facade类的定义文件看看:
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
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
|
<?php
//摘录部分关键代码
namespace Illuminate\Support\Facades;
abstract class Facade {
protected static $app;
/**
* Get the root object behind the facade.
*
* @return mixed
*/
public static function getFacadeRoot()
{
return static::resolveFacadeInstance(static::getFacadeAccessor());
}
/**
* Get the registered name of the component.
*
* @return string
*
* @throws \RuntimeException
*/
protected static function getFacadeAccessor()
{
throw new \RuntimeException("Facade does not implement getFacadeAccessor method.");
}
/**
* Resolve the facade root instance from the container.
*
* @param string $name
* @return mixed
*/
protected static function resolveFacadeInstance($name)
{
if (is_object($name)) return $name;
if (isset(static::$resolvedInstance[$name]))
{
return static::$resolvedInstance[$name];
}
return static::$resolvedInstance[$name] = static::$app[$name];
}
/**
* Handle dynamic, static calls to the object.
*
* @param string $method
* @param array $args
* @return mixed
*/
public static function __callStatic($method, $args)
{
$instance = static::getFacadeRoot();
switch (count($args))
{
case 0:
return $instance->$method();
case 1:
return $instance->$method($args[0]);
case 2:
return $instance->$method($args[0], $args[1]);
case 3:
return $instance->$method($args[0], $args[1], $args[2]);
case 4:
return $instance->$method($args[0], $args[1], $args[2], $args[3]);
default:
return call_user_func_array(array($instance, $method), $args);
}
}
}
|
既然Cache
类没有get
这个方法,Facade
必然得实现__callStatic
方法。__callStatic
方法很简单——获取对象、调用对象的相应方法。而Cache
类实现的getFacadeAccessor
方法只不过是告诉Facade
应该去获取哪个对象。
所以看到这里,我们可以试着写一个自己的Facade
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
32
33
|
<?php
class Facade {
public static function __callStatic($method,$args){
$instance=static::getInstance(static::getFacadeAccessor());
return $instance->$method($args);
}
public static function getInstance($name){
$class=$name.'Real'; //这里只是为了演示,随便定了一个真正调用类的名字
return new $class;
}
}
class CacheReal {
public function test(){
echo 'hello world';
}
}
class Cache extends Facade {
public static function getFacadeAccessor(){
return 'Cache'; //返回真正执行操作的类的信息
}
}
function __autoload($class){
class_alias('Cache',$class,true); //为了简单演示,所有请求的类定义为Cache类的别名
}
AAA::test(); //屏幕打印出Hello world
|
看到这里,你应该明白Facade到底是怎么回事了吧?那么使用Facade有什么好处呢?在Laravel里,Facade其实是和IOC联合发挥威力的,我们这里单独讨论Facade。
以我目前粗浅的理解,能感受到的好处有:
- 语句更简短。不需要非得new一个对象出来再执行。
- 解除调用对象的强依赖关系。当我们执行
Cache::set()
的方法时,并不需要关心具体是哪个后端的类真正做了set这个操作。如果将来想把缓存从文件改到redis里,只需要在配置文件里修改一下,业务代码完全不受影响。
参考内容:
- 官方文档