<?php class A{ const H = 'A'; const J = 'A'; static function testSelf(){ echo self::H; //在編譯階段就肯定了 self解析爲 A } } class B extends A{ const H = "B"; const J = 'B'; 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 = 'C'; } B::testParent(); B::testSelf(); echo "\n"; C::testParent(); C::testSelf();
運行結果:php
AA AA
結論:
self::和parent::出如今某個類X的定義中,則將被解析爲相應的類X,除非在子類中覆蓋父類的方法。數組
做用:app
- 在函數體內的修飾變量的static關鍵字用於定義靜態局部變量。 - 用於修飾類成員函數和成員變量時用於聲明靜態成員。 - (PHP5.3以後)在做用域解析符(::)前又表示靜態延遲綁定的特殊類。
例子:函數
定義靜態局部變量(出現位置:局部函數中)
特徵:靜態變量僅在局部函數域中存在,但當程序執行離開此做用域時,其值並不丟失。ui
<?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 靜態變量同樣,靜態屬性只能被初始化爲文字或常量,不能使用表達式。因此能夠把靜態屬性初始化爲整數或數組,但不能初始化爲另外一個變量或函數返回值,也不能指向一個對象。this
-------------------------------------------->
a.靜態方法例子(出現位置: 類的方法定義)code
<?php class Foo { public static function aStaticMethod() { // ... } } Foo::aStaticMethod(); $classname = 'Foo'; $classname::aStaticMethod(); // 自PHP 5.3.0後,能夠經過變量引用類 ?>
-------------------------------------------->
b.靜態屬性例子(出現位置:類的屬性定義)對象
<?php 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 = new Foo(); print $foo->staticValue() . "\n"; print $foo->my_static . "\n"; // Undefined "Property" my_static print $foo::$my_static . "\n"; $classname = 'Foo'; print $classname::$my_static . "\n"; // As of PHP 5.3.0 print Bar::$my_static . "\n"; $bar = new Bar(); print $bar->fooStatic() . "\n"; ?>
-------------------------------------------->
c.用於後期靜態綁定(出現位置: 類的方法中,用於修飾變量或方法)
下面詳細分析繼承
自 PHP 5.3.0 起,PHP 增長了一個叫作後期靜態綁定的功能,用於在繼承範圍內引用靜態調用的類。作用域
轉發調用 :
非轉發調用 :
原理:存儲了在上一個「非轉發調用」(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
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 = 'Mango is'; public static function fruit() { $args = func_get_args(); echo static::NAME, " " . join(' ', $args) . "\n"; } } class Orange extends Mango { const NAME = 'Orange is'; public static function fruit() { echo self::NAME, "\n"; forward_static_call(array('Mango', 'fruit'), 'my', 'favorite', 'fruit'); forward_static_call('fruit', 'my', 'father\'s', 'favorite', 'fruit'); } } Orange::fruit('NO'); function fruit() { $args = func_get_args(); echo "Apple is " . join(' ', $args). "\n"; } ?> 運行結果: Orange is Orange is my favorite fruit Apple is my father'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) { }
能夠看到,使用static能夠解決self時出現的單例繼承問題。