靜態方法和屬性php
靜態方法是以類做爲做用域
的函數。靜態方法不能訪問這個類中的普通屬性
,由於那些屬性屬於一個對象
,但能夠訪問靜態屬性。若是修改了一個靜態屬性,那麼這個類的全部實例
都能訪問到這個新值。
由於是經過類而不是實例來訪問靜態元素,因此訪問靜態元素時再也不須要引用對象的變量,而是使用::來鏈接類名和屬性或類名和方法。函數
class StaicExample { static public $aNum = 0; static public function sayHello() { print "hello"; } } print StaicExample::$aNum; StaicExample::sayHello();
一個子類可使用parent
關鍵字來訪問父類,而不使用其類名。要從當前類(不是子類)中訪問靜態方法或屬性,可使用self
關鍵字。self
指向當前類,就像僞變量$this
指向當前對象同樣。所以,在StaticExample
類的外部可使用其類名訪問屬性$aNum
:this
StaicExample::$aNum;
而在StaicExample
類內部,可使用self
關鍵字:code
class StaicExample { static public $aNum = 0; static public function sayHello() { self::$aNum++; print "hello (".self::$aNum.")\n"; } }
只有在使用parent
關鍵字調用方法的時候,才能對一個非靜態方法進行靜態形式的調用(使用::
)。除非是訪問一個被覆寫的方法,不然永遠只能使用::
訪問被明確聲明爲static
的方法或屬性。有時看到使用static
語法來引用方法或屬性,可能並不意味着其中的方法或屬性必須是靜態的,只不過說明它屬於特定的類。對象
根據定義,不能在對象中調用靜態方法。所以靜態方法和屬性又被稱爲類變量和屬性
,也就不能在靜態方法中使用僞變量$this
。繼承
爲何要使用靜態方法或屬性呢?作用域
延遲靜態綁定:static關鍵字
靜態方法能夠用做工廠方法,工廠方法是生成包含類的實例的一種方法。
先看下面的重複代碼:get
abstract class DomainObject { } class User extends DomainObject { public static function create() { return new User(); } } class Document extends DomainObject { public static function create() { return new Document(); } }
想必你們都不想爲每一個DomainObject
子類都建立與上面代碼相似的標準代碼。若是把create()
放在超類呢?io
abstract class DomainObject { public static function create() { return new self(); } } class User extends DomainObject { } class Document extends DomainObject { } Document::create();
這回看起來簡潔多了。如今把常見的代碼放在一個位置,並使用self
做爲對該類的引用。實際上,self對該類所起的做用與$this對對象所起的做用並不徹底相同。self指的不是調用上下文,而是解析上下文
。所以,運行剛纔上面的代碼會獲得:function
PHP Fatal error: Cannot instantiate abstract class DomainObject in ...
所以,self被解析爲定義create()的DomainObject,而不是解析爲調用self的Document類
。PHP5.3以前,在這方面有嚴格的限制,產生不少笨拙的解決方案。PHP5.3引入了延遲靜態綁定
的概念。該特性最明顯的標誌就是新關鍵字static。static相似於self,但它指的是被調用的類而不是包含類
。
在本例中,它的意思是調用Document::create()將生成一個新的Document對象,而不是試圖實例化一個DomainObject對象。
所以,如今在靜態上下文使用繼承關係。
abstract class DomainObject { public static function create() { return new static(); } } class User extends DomainObject { } class Document extends DomainObject { } print_r(Document::create());//Document Object {}
static關鍵字不單單能夠用於實例化。和self和parent同樣,static還能夠做爲靜態方法調用的標識符,甚至是從非靜態上下文中調用。
若是想爲DomainObject引入組(group)的概念。默認狀況下,全部類都屬於default類別,但想能夠爲繼承層次結構的某些分支重寫類別。
abstract class DomainObject { private $group; public function __construct() { $this->group = static::getGroup(); } public static function create() { return new static(); } static function getGroup() { return "default"; } } class User extends DomainObject { } class Document extends DomainObject { static function getGroup() { return "document"; } } class SpreadSheet extends Document { } print_r(User::create()); print_r(SpreadSheet::create());
在DomainObject類中定義了構造函數。該構造函數使用static關鍵字調用靜態方法getGroup()。DomainObject提供了默認實現,但Document將其覆蓋了。建立的SpreadSheet新類擴展了Document類。下面是打印結果:
User Object ( [group:DomainObject:private] => default ) SpreadSheet Object ( [group:DomainObject:private] => document )
最後根據本身的理解,寫個例子:
<?php class Model { protected $model; public function __construct() { $this->model = static::getModel(); //延遲綁定 } public function __get($name) { return $this->{'get'.ucfirst($name)}(); } public static function getModel() { return 'Model'; } public function getModelName() { return $this->model; } public function __toString() { return $this->modelName; } } class User extends Model { public static function getModel() { return 'User'; } } class Book extends Model { public static function getModel() { return 'Book'; } } $model = new Model; $user = new User; $book = new Book; echo $model . PHP_EOL; // Model echo $user . PHP_EOL; // User echo $book . PHP_EOL; // Book
當把Model類的構造方法中的static::getModel()改成self::getModel()後:
echo $model . PHP_EOL; // Model echo $user . PHP_EOL; // Model echo $book . PHP_EOL; // Model