首頁 > web前端 > js教程 > 詳解PHP後期靜態綁定分析與應用

詳解PHP後期靜態綁定分析與應用

亚连
發布: 2018-05-26 17:45:36
原創
1154 人瀏覽過

這篇文章為大家總結了PHP後期靜態綁定分析與應用的相關知識點,對此有興趣的朋友可以學習下。

基礎

1. 範圍解析運算子(::)

  • #可以用來存取靜態成員,類別常數,也可以用來覆寫類別中的屬性和方法。

  • self,parent 和 static 這三個特殊的關鍵字是用於在類別定義的內部對其屬性或方法進行存取的。

  • parent用於呼叫父類別中被覆寫的屬性或方法(出現在哪裡,就將解析為對應類別的父類別)。

  • self用於呼叫本類別中的方法或屬性(出現在哪裡,就將解析為對應的類別;注意與$this區別,$this指向當前實例化的對象)。

  • 當子類別覆寫其父類別中的方法時,PHP 不會呼叫父類別中已被覆寫的方法。是否呼叫父類別的方法取決於子類別。

2. PHP核心將類別的繼承實作放在了"編譯階段"

<?php
class A{
 const H = &#39;A&#39;;

 const J = &#39;A&#39;;

 static function testSelf(){
  echo self::H; //在编译阶段就确定了 self解析为 A
 }
}

class B extends A{
 const H = "B";

 const J = &#39;B&#39;;

 static function testParent(){
  echo parent::J; //在编译阶段就确定了 parent解析为A
 }

 /* 若重写testSelf则能输出“B”, 且C::testSelf()也是输出“B”
 static function testSelf(){
  echo self::H;
 }
 */

}

class C extends B{
 const H = "C";

 const J = &#39;C&#39;;
}

B::testParent();
B::testSelf();

echo "\n";

C::testParent();
C::testSelf();
登入後複製

運行結果:

AA
AA


#結論:

self::和parent::出現在某個類別X的定義中,則會被解析為對應的類別X,除非在子類別中覆寫父類別的方法。

3.Static(靜態)關鍵字

作用:

- 在函數體內的修飾變數的static關鍵字用於定義靜態局部變數。
- 用於修飾類別成員函數和成員變數時用於宣告靜態成員。
- (PHP5.3之後)在作用域解析符(::)前又表示靜態延遲綁定的特殊類別。

範例:

定義靜態局部變數(出現位置:局部函數中)

#特徵:靜態變數只在局部函數域中存在,但當程序執行離開此作用域時,其值並不遺失。

<?php
function test()
{
 static $count = 0;

 $count++;
 echo $count;
 if ($count < 10) {
  test();
 }
 $count--;
}
登入後複製

定義靜態方法,靜態屬性

#a)聲明類別屬性或方法為靜態,就可以不實例化類而直接訪問。

b)靜態屬性不能透過一個類別已實例化的物件來存取(但靜態方法可以)

c)如果沒有指定存取控制,屬性和方法預設為公有。

d)由於靜態方法不需要透過物件即可調用,所以偽變數 $this 在靜態方法中不可用。

e)靜態屬性不可以由物件透過 -> 運算子來存取。

f)用靜態方式呼叫非靜態方法會導致一個 E_STRICT 等級的錯誤。

g)就像其它所有的 PHP 靜態變數一樣,靜態屬性只能被初始化為文字或常數,不能使用表達式。所以可以把靜態屬性初始化為整數或數組,但不能初始化為另一個變數或函數回傳值,也不能指向一個物件。

a.靜態方法範例(出現位置: 類別的方法定義)

<?php
class Foo {
 public static function aStaticMethod() {
  // ...
 }
}

Foo::aStaticMethod();
$classname = &#39;Foo&#39;;
$classname::aStaticMethod(); // 自PHP 5.3.0后,可以通过变量引用类
?>
登入後複製

b.靜態屬性範例(出現位置:類別的屬性定義)

<?php
class Foo
{
 public static $my_static = &#39;foo&#39;;

 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 = new Foo();
print $foo->staticValue() . "\n";
print $foo->my_static . "\n";  // Undefined "Property" my_static 

print $foo::$my_static . "\n";
$classname = &#39;Foo&#39;;
print $classname::$my_static . "\n"; // As of PHP 5.3.0

print Bar::$my_static . "\n";
$bar = new Bar();
print $bar->fooStatic() . "\n";
?>
登入後複製

c.用於後期靜態綁定(出現位置: 類別的方法中,用於修飾變數或方法)

下面詳細分析

後期靜態綁定(late static binding)

自PHP 5.3.0 起,PHP 增加了一個叫做後期靜態綁定的功能,用於在繼承範圍內引用靜態呼叫的類別。

1.轉送呼叫與非轉送呼叫

轉送呼叫:

指的是透過以下幾種方式進行的靜態呼叫:self::,parent::,static:: 以及forward_static_call()。

非轉送呼叫:

明確指定類別名稱的靜態呼叫(例如Foo::foo())

非靜態呼叫(例如$ foo->foo())

2.後期靜態綁定工作原理

原理:儲存了在上一個「非轉發呼叫」(non-forwarding call)中的類別名稱。意思是當我們呼叫一個轉發呼叫的靜態呼叫時,實際呼叫的類別是上一個非轉發呼叫的類別。

範例分析:

<?php
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的比較

<?php
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";
echo $apple->parentname() . "\n";
echo $apple->staticname();

?>

运行结果:
Mango
Orange
Apple
登入後複製

b)使用forward_static_call()

#
<?php
class Mango
{
 const NAME = &#39;Mango is&#39;;
 public static function fruit() {
  $args = func_get_args();
  echo static::NAME, " " . join(&#39; &#39;, $args) . "\n";
 }
}

class Orange extends Mango
{
 const NAME = &#39;Orange is&#39;;

 public static function fruit() {
  echo self::NAME, "\n";

  forward_static_call(array(&#39;Mango&#39;, &#39;fruit&#39;), &#39;my&#39;, &#39;favorite&#39;, &#39;fruit&#39;);
  forward_static_call(&#39;fruit&#39;, &#39;my&#39;, &#39;father\&#39;s&#39;, &#39;favorite&#39;, &#39;fruit&#39;);
 }
}

Orange::fruit(&#39;NO&#39;);

function fruit() {
 $args = func_get_args();
 echo "Apple is " . join(&#39; &#39;, $args). "\n";
}

?>


运行结果:
Orange is
Orange is my favorite fruit
Apple is my father&#39;s favorite fruit
登入後複製

c)使用get_called_class()

<?php


class Mango {
 static public function fruit() {
  echo get_called_class() . "\n";
 }
}

class Orange extends Mango {
 //
}

Mango::fruit();
Orange::fruit();

?>



运行结果:
Mango
Orange
登入後複製

應用程式

## 前面已經提到過了,引入後期靜態綁定的目的是:用於在繼承範圍內引用靜態呼叫的類別。

所以, 可以用後期靜態綁定的辦法解決單例繼承問題。

先看一下使用self是一個什麼樣的情況:

<?php
// 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);




运行结果:
E:\code\php_test\apply\self.php:37:
class A#1 (0) {
}
E:\code\php_test\apply\self.php:38:
class A#1 (0) {
}
E:\code\php_test\apply\self.php:39:
class A#1 (0) {
}
登入後複製

透過上面的例子可以看到,使用self,實例化得到的都是類別A的同一個物件

#再來看看使用static會得到什麼樣的結果

<?php
// 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);




运行结果:
E:\code\php_test\apply\static.php:35:
class D#1 (0) {
}
E:\code\php_test\apply\static.php:36:
class E#2 (0) {
}
E:\code\php_test\apply\static.php:37:
class F#3 (0) {
}
登入後複製

上面是我整理給大家的,希望今後對大家有幫助。

相關文章:

AjaxSubmit()提交file檔案

#Ajax解決多餘刷新的兩種方法

# Ajax同步與非同步問題淺析及解決方法

以上是詳解PHP後期靜態綁定分析與應用的詳細內容。更多資訊請關注PHP中文網其他相關文章!

相關標籤:
來源:php.cn
本網站聲明
本文內容由網友自願投稿,版權歸原作者所有。本站不承擔相應的法律責任。如發現涉嫌抄襲或侵權的內容,請聯絡admin@php.cn
熱門教學
更多>
最新下載
更多>
網站特效
網站源碼
網站素材
前端模板