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的。

posted @ 2020-09-05 17:26  houxin  阅读(388)  评论(0编辑  收藏  举报