PHP靜態方法和屬性、延遲靜態綁定

靜態方法和屬性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繼承

爲何要使用靜態方法或屬性呢?作用域

  1. 在代碼中的任何地方均可用(假設你能夠訪問該類)。也就是說,你不須要在對象間傳遞類的實例,也不須要將實例存放在全局變量中,就能夠訪問類中方法。
  2. 類的每一個實例均可以訪問類中定義的靜態屬性,因此能夠利用靜態屬性來設置值,該值能夠被類的全部對象使用。
  3. 不須要實例對象就能訪問靜態屬性或方法,這樣就不用爲了獲取一個簡單的功能而實例化對象。

延遲靜態綁定: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
相關文章
相關標籤/搜索