PHP静态属性中的子类改变父类、子类改变子类的问题。
一、静态实例
这里我定义了一个如下的类
class A{
protected static $instance;
public static function setInstance($ins){
static::$instance = $ins;
}
public static function getInstance(){
return static::$instance;
}
}
class B extends A
{
}
// 设置值
B::setInstance('hello');
var_dump(B::getInstance()); echo '<br>';
var_dump(A::getInstance()); echo '<br>';
这里
static::
的作用类似于self::
,但是是有区别的 。具体差别请自行查询了解。
你觉的会输出什么?也许你会觉得,第一个输出'hello',第二个应该输出'null'。
但是,实际输出
string(5) "hello"
string(5) "hello"
我们又加一个C类,继承与A类,然后通过C去修改静态变量$instance
的值。
...
class C extends A
{
}
C::setInstance('world');
var_dump(C::getInstance()); echo '<br>';
var_dump(B::getInstance()); echo '<br>';
var_dump(A::getInstance()); echo '<br>';
此时再打印,你怎么看?可能又要猜错了。
此时输出的是
string(5) "world"
string(5) "world"
string(5) "world"
此时你是不是已发现,子类改变了父类的值,并改变了和它一起继承的另外的一个子类的值。不知道有没有颠覆你的想象。先提一句,这只是静态属性的问题。具体请往下看。
二、普通实例
这次展示下普通实例下的效果
class A{
protected $instance;
public function setInstance($ins){
$this->instance = $ins;
}
public function getInstance(){
return $this->instance;
}
}
class B extends A
{
}
class C extends A
{
}
// 设置值
$b = new B;
$b->setInstance('hello');
var_dump($b->getInstance()); echo '<br>';
var_dump((new A)->getInstance()); echo '<br>';
echo '<hr>';
$c = new C;
$c->setInstance('world');
var_dump($c->getInstance()); echo '<br>';
var_dump((new B)->getInstance()); echo '<br>';
var_dump((new A)->getInstance()); echo '<br>';
结果如下
string(5) "hello"
NULL
string(5) "world"
NULL
NULL
这种情况,就比较符合大家的预期。
三、原因分析
所以,静态属性的继承跟我们默认想象的不一样哦,尽管我们在子类里可以获取它,可以修改它,但是这个它,指向的都是父类里的那个静态属性。
确实是子类能改变父类,子类能改变子类。也或者说,所有的父类和子类,都是共享这一个静态属性。
当然,上面的情况仅发生在你的子类里,没有额外定义一个同名静态属性的情况下。
如果这样:
class A{
protected static $instance;
public static function setInstance($ins){
static::$instance = $ins;
}
public static function getInstance(){
return static::$instance;
}
}
class B extends A
{
protected static $instance;
}
class C extends A
{
protected static $instance;
}
// 设置值
B::setInstance('hello');
var_dump(B::getInstance()); echo '<br>';
var_dump(A::getInstance()); echo '<br>';
echo '<hr>';
C::setInstance('world');
var_dump(C::getInstance()); echo '<br>';
var_dump(B::getInstance()); echo '<br>';
var_dump(A::getInstance()); echo '<br>';
此时打印的结果
string(5) "hello"
NULL
string(5) "world"
string(5) "hello"
NULL
我们在各个子类内部,定义了一个$instance
属性,这时候就是各自是各自的了,不再共享了。
用处:
很多知名的框架,比如laravel里面大量使用了静态属性,当然也包括静态调用绑定,在查看源码的时候就要注意这一点。父类和子类是不是共享的一个静态属性,子类里面有没有重新定义。子类更改静态属性是否会影响父类吗。最简单直接的就是laravel框架的$app
,它是如何保证整个程序那么多个类,在运行的时候都是用的同一个larave例本身的,都是指向同一个$app的。