详解PHP后期静态绑定分析与应用

参考经典文章:https://www.jb51.net/article/136794.htm

Static(静态)关键字

作用:

- 在函数体内的修饰变量的static关键字用于定义静态局部变量。
- 用于修饰类成员函数和成员变量时用于声明静态成员。
- (PHP5.3之后)在作用域解析符(::)前又表示静态延迟绑定的特殊类。

例子:

定义静态局部变量(出现位置:局部函数中)

特征:静态变量仅在局部函数域中存在,但当程序执行离开此作用域时,其值并不丢失。

function test()
{
 static $count = 0;
 
 $count++;
 echo $count;
 if ($count < 10) {
  test();
 }
 $count--;
 echo $count;
}

echo test();//123456789109876543210

定义静态方法,静态属性

a)声明类属性或方法为静态,就可以不实例化类而直接访问。

b)静态属性不能通过一个类已实例化的对象来访问(但静态方法可以)

c)如果没有指定访问控制,属性和方法默认为公有。

d)由于静态方法不需要通过对象即可调用,所以伪变量 $this 在静态方法中不可用。

e)静态属性不可以由对象通过 -> 操作符来访问。

f)用静态方式调用一个非静态方法会导致一个 E_STRICT 级别的错误。

g)就像其它所有的 PHP 静态变量一样,静态属性只能被初始化为文字或常量,不能使用表达式。所以可以把静态属性初始化为整数或数组,但不能初始化为另一个变量或函数返回值,也不能指向一个对象。

a.静态方法例子(出现位置: 类的方法定义)

class Foo {
 public static function aStaticMethod() {
  // ...
 }
}
 
Foo::aStaticMethod();
$classname = 'Foo';
$classname::aStaticMethod(); // 自PHP 5.3.0后,可以通过变量引用类

b.静态属性例子(出现位置:类的属性定义)

class Foo
{
 public static $my_static = 'foo';
 
 public function staticValue() {
  return self::$my_static; //self 即 FOO类
 }
}
 
class Bar extends Foo
{
 public function fooStatic() {
  return parent::$my_static; //parent 即 FOO类
 }
}
 
print Foo::$my_static . "\n";//foo
 
$foo = new Foo();
print $foo->staticValue() . "\n";//foo
print $foo->my_static . "\n";  // Accessing static property Foo::$my_static as non static  静态属性
//不能通过一个类已实例化的对象来访问(但静态方法可以)
 
print $foo::$my_static . "\n";//foo
$classname = 'Foo';
print $classname::$my_static . "\n"; // As of PHP 5.3.0//foo
 
print Bar::$my_static . "\n";//foo
$bar = new Bar();
print $bar->fooStatic() . "\n";//foo

c.用于后期静态绑定(出现位置: 类的方法中,用于修饰变量或方法)

下面详细分析

后期静态绑定(late static binding)

自 PHP 5.3.0 起,PHP 增加了一个叫做后期静态绑定的功能,用于在继承范围内引用静态调用的类。

1.转发调用与非转发调用

转发调用 :

指的是通过以下几种方式进行的静态调用:self::,parent::,static:: 以及 forward_static_call()。

非转发调用 :

明确指定类名的静态调用(例如Foo::foo())

非静态调用(例如$foo->foo())

2.后期静态绑定工作原理

原理:存储了在上一个“非转发调用”(non-forwarding call)中的类名。意思是当我们调用一个转发调用的静态调用时,实际调用的类是上一个非转发调用的类。

class A {
 public static function foo() {
  echo __CLASS__."\n";
  static::who();
 }
 
 public static function who() {
  echo __CLASS__."\n";
 }
}
 
class B extends A {
 public static function test() {
  echo "A::foo()\n";
  A::foo();
  echo "parent::foo()\n";
  parent::foo();
  echo "self::foo()\n";
  self::foo();
 }
 
 public static function who() {
  echo __CLASS__."\n";
 }
}
 
class C extends B {
 public static function who() {
  echo __CLASS__."\n";
 }
}
 
C::test();
 
/*
 * C::test(); //非转发调用 ,进入test()调用后,“上一次非转发调用”存储的类名为C
 *
 * //当前的“上一次非转发调用”存储的类名为C
 * public static function test() {
 *  A::foo(); //非转发调用, 进入foo()调用后,“上一次非转发调用”存储的类名为A,然后实际执行代码A::foo(), 转 0-0
 *  parent::foo(); //转发调用, 进入foo()调用后,“上一次非转发调用”存储的类名为C, 此处的parent解析为A ,转1-0
 *  self::foo(); //转发调用, 进入foo()调用后,“上一次非转发调用”存储的类名为C, 此处self解析为B, 转2-0
 * }
 *
 *
 * 0-0
 * //当前的“上一次非转发调用”存储的类名为A
 * public static function foo() {
 *  static::who(); //转发调用, 因为当前的“上一次非转发调用”存储的类名为A, 故实际执行代码A::who(),即static代表A,
进入who()调用后,“上一次非转发调用”存储的类名依然为A,因此打印 “A”
 * }
 *
 * 1-0
 * //当前的“上一次非转发调用”存储的类名为C
 * public static function foo() {
 *  static::who(); //转发调用, 因为当前的“上一次非转发调用”存储的类名为C, 故实际执行代码C::who(),即static代表C,
进入who()调用后,“上一次非转发调用”存储的类名依然为C,因此打印 “C”
 
 * }
 *
 * 2-0
 * //当前的“上一次非转发调用”存储的类名为C
 * public static function foo() {
 *  static::who(); //转发调用, 因为当前的“上一次非转发调用”存储的类名为C, 故实际执行代码C::who(),即static代表C,
进入who()调用后,“上一次非转发调用”存储的类名依然为C,因此打印 “C”
 * }
 */
 
 
故最终结果为:
A::foo()
A
A
parent::foo()
A
C
self::foo()
A
C

3.更多静态后期静态绑定的例子

a)Self, Parent 和 Static的对比

class Mango {

	function classname(){
		return __CLASS__;
	}

	function selfname(){
		return self::classname();
	}

	function staticname(){
		return static::classname();
	}

}
 
class Orange extends Mango {

	function parentname(){
		return parent::classname();
	}

	function classname(){
		return __CLASS__;
	}

}
 
class Apple extends Orange {

	function parentname(){
		return parent::classname();
	}

	function classname(){
		return __CLASS__;
	}
	
}
 
$apple = new Apple();
echo $apple->selfname() . "\n";//Mango 
echo $apple->parentname() . "\n";//Orange 
echo $apple->staticname();//Apple

b)使用forward_static_call()

c)使用get_called_class()

应用

前面已经提到过了,引入后期静态绑定的目的是:用于在继承范围内引用静态调用的类。
所以, 可以用后期静态绑定的办法解决单例继承问题。

先看一下使用self是一个什么样的情况:

// new self 得到的单例都为A。
class A
{
 protected static $_instance = null;
 
 protected function __construct()
 {
  //disallow new instance
 }
 
 protected function __clone(){
  //disallow clone
 }
 
 static public function getInstance()
 {
  if (self::$_instance === null) {
   self::$_instance = new self();
  }
  return self::$_instance;
 }
}
 
class B extends A
{
 protected static $_instance = null;
}
 
class C extends A{
 protected static $_instance = null;
}
 
$a = A::getInstance();
$b = B::getInstance();
$c = C::getInstance();
 
var_dump($a);
var_dump($b);
var_dump($c);

通过上面的例子可以看到,使用self,实例化得到的都是类A的同一个对象

再来看看使用static会得到什么样的结果

// new static 得到的单例分别为D,E和F。
class D
{
 protected static $_instance = null;
 
 protected function __construct(){}
 protected function __clone()
 {
  //disallow clone
 }
 
 static public function getInstance()
 {
  if (static::$_instance === null) {
   static::$_instance = new static();
  }
  return static::$_instance;
 }
}
 
class E extends D
{
 protected static $_instance = null;
}
 
class F extends D{
 protected static $_instance = null;
}
 
$d = D::getInstance();
$e = E::getInstance();
$f = F::getInstance();
 
var_dump($d);
var_dump($e);
var_dump($f);

可以看到,使用static可以解决self时出现的单例继承问题。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值