類別常數、類別屬性(靜態)和類別函式(靜態)都可以使用相同的名稱,並使用雙冒號存取。
<?php
class A {
public static $B = '1'; # 靜態類別變數
const B = '2'; # 類別常數
public static function B() { # 靜態類別函式
return '3';
}
}
echo A::$B . A::B . A::B(); # 輸出:123
?>
範圍解析運算子(也稱為 Paamayim Nekudotayim),簡而言之就是雙冒號,是一個允許存取類別或其父類別的常數、靜態屬性或靜態方法的標記。此外,靜態屬性或方法可以透過延遲靜態繫結來覆寫。
從類別定義外部參考這些項目時,請使用類別的名稱。
可以使用變數來參考類別。變數的值不能是關鍵字(例如 self
、parent
和 static
)。
Paamayim Nekudotayim 乍看之下似乎是一個奇怪的雙冒號名稱。然而,在撰寫 Zend Engine 0.5(PHP 3 的核心)時,Zend 團隊決定這樣稱呼它。它實際上在希伯來語中就是雙冒號的意思!
範例 #1 從類別定義外部使用 ::
<?php
class MyClass {
const CONST_VALUE = 'A constant value';
}
$classname = 'MyClass';
echo $classname::CONST_VALUE;
echo MyClass::CONST_VALUE;
?>
三個特殊的關鍵字 self、parent 和 static 用於在類別定義內部存取屬性或方法。
範例 #2:在類別定義內部使用 ::
<?php
class OtherClass extends MyClass
{
public static $my_static = 'static var';
public static function doubleColon() {
echo parent::CONST_VALUE . "\n";
echo self::$my_static . "\n";
}
}
$classname = 'OtherClass';
$classname::doubleColon();
OtherClass::doubleColon();
?>
當子類別覆寫(override)父類別的方法定義時,PHP 不會呼叫父類別的方法。是否要呼叫父類別的方法由子類別決定。這也適用於建構子和解構子、多載和魔術方法的定義。
範例 #3:呼叫父類別的方法
<?php
class MyClass
{
protected function myFunc() {
echo "MyClass::myFunc()\n";
}
}
class OtherClass extends MyClass
{
// 覆寫父類別的定義
public function myFunc()
{
// 但仍然呼叫父類別的函式
parent::myFunc();
echo "OtherClass::myFunc()\n";
}
}
$class = new OtherClass();
$class->myFunc();
?>
另請參閱一些靜態調用技巧的範例。
類別常數、類別屬性(靜態)和類別函式(靜態)都可以使用相同的名稱,並使用雙冒號存取。
<?php
class A {
public static $B = '1'; # 靜態類別變數
const B = '2'; # 類別常數
public static function B() { # 靜態類別函式
return '3';
}
}
echo A::$B . A::B . A::B(); # 輸出:123
?>
在 PHP 中,您可以使用 self 關鍵字來存取靜態屬性和方法。
問題是您可以用 self::method() 取代 $this->method(),無論 method() 是否宣告為靜態。那麼您應該使用哪一個?
請參考以下程式碼
class ParentClass {
function test() {
self::who(); // 將輸出 'parent'
$this->who(); // 將輸出 'child'
}
function who() {
echo 'parent';
}
}
}
function who() {
function who() {
}
}
$obj = new ChildClass();
$obj->test();
在此範例中,self::who() 將始終輸出「parent」,而 $this->who() 將取決於物件所屬的類別。
現在我們可以看到 self 指的是呼叫它的類別,而 $this 指的是目前物件的類別。
因此,只有在 $this 不可用時,或者您不希望允許子類別覆寫目前方法時,才應該使用 self。
似乎您可以使用類別名稱以外的名稱,透過 :: 從類別定義外部參考類別的靜態變數、常數和靜態函式。該語言似乎允許您使用物件本身。
例如
class horse {
{
static $props = ['order' => 'mammal'];
}
}
echo $animal::$props['order'];
// 輸出 'mammal'
這似乎沒有被記錄,但我認為這是該語言中一個重要的便利性。我希望看到它被記錄下來並被正式支援。
如果它沒有被正式支援,替代方案似乎會很麻煩,例如:
$animalClass = get_class($animal);
echo $animalClass::$props['order'];
剛發現使用類別名稱也可以呼叫祖先類別的類似函式。
<?php
class Anchestor {
public $Prefix = '';
private $_string = 'Bar';
public function Foo() {
return $this->Prefix.$this->_string;
}
}
class MyParent extends Anchestor {
public function Foo() {
$this->Prefix = null;
return parent::Foo().'Baz';
}
}
class Child extends MyParent {
public function Foo() {
$this->Prefix = 'Foo';
return Anchestor::Foo();
}
}
$c = new Child();
echo $c->Foo(); //會回傳 FooBar,因為 Prefix 是在 Anchestor::Foo() 中設定的
?>
Child 類別呼叫了 Anchestor::Foo(),因此 MyParent::Foo() 從未被執行。
嗯,這是一段「瑞士刀」般的程式碼,可以用來呼叫父類別方法。唯一的限制是不能用於「傳參考」的參數。
主要優點是您不需要知道父類別的「實際」簽名(signature),只需要知道您需要哪些參數。
<?php
class someclass extends some superclass {
// 可用於建構子
function __construct($ineedthisone) {
$args=func_get_args();
/* $args 將包含傳遞給 __construct 的任何參數。
* 您的形式參數不會影響 func_get_args() 的運作方式
*/
call_user_func_array(array('parent',__FUNCTION__),$args);
}
// 但這不僅限於 __construct
function anyMethod() {
$args=func_get_args();
call_user_func_array(array('parent',__FUNCTION__),$args);
}
// 注意:php 5.3.0 甚至允許您這樣做
function anyMethod() {
//需要 php >=5.3.x
call_user_func_array(array('parent',__FUNCTION__),func_get_args());
}
}
?>
從 PHP 5.3.0 開始,您可以使用 'static' 作為作用域值,如下例所示(與 'self' 關鍵字相比,增加了繼承機制的彈性…)
<?php
class A {
const C = 'constA';
public function m() {
echo static::C;
}
}
class B extends A {
const C = 'constB';
}
$b = new B();
$b->m();
// 輸出:constB
?>
一個使用 static 的小技巧,可以避免 PHP 的嚴格標準…
函式呼叫者會找到呼叫它的物件,以便靜態方法可以修改它,在靜態函式中作為 $this 的替代方案,但沒有嚴格標準的警告 :)
<?php
error_reporting(E_ALL + E_STRICT);
function caller () {
$backtrace = debug_backtrace();
$object = isset($backtrace[0]['object']) ? $backtrace[0]['object'] : null;
$k = 1;
while (isset($backtrace[$k]) && (!isset($backtrace[$k]['object']) || $object === $backtrace[$k]['object']))
$k++;
return isset($backtrace[$k]['object']) ? $backtrace[$k]['object'] : null;
}
class a {
public $data = 'Empty';
function set_data () {
b::set();
}
}
class b {
static function set () {
// $this->data = 'Data from B !';
// 在靜態函式中使用 this 會拋出警告 ...
caller()->data = 'Data from B !';
}
}
$a = new a();
$a->set_data();
echo $a->data;
?>
輸出:Data from B !
沒有任何警告或錯誤!
針對「後期靜態綁定」這個主題,我在下方發佈了一段程式碼,示範如何在後期類別中設定變數值,並在父類別(或父類別的父類別等等)中印出該值。
<?php
class cA
{
/**
* Test property for using direct default value
*/
protected static $item = 'Foo';
/**
* Test property for using indirect default value
*/
protected static $other = 'cA';
public static function method()
{
print self::$item."\r\n"; // It prints 'Foo' on everyway... :(
print self::$other."\r\n"; // We just think that, this one prints 'cA' only, but... :)
}
public static function setOther($val)
{
self::$other = $val; // Set a value in this scope.
}
}
class cB extends cA
{
/**
* Test property with redefined default value
*/
protected static $item = 'Bar';
public static function setOther($val)
{
self::$other = $val;
}
}
class cC extends cA
{
/**
* Test property with redefined default value
*/
protected static $item = 'Tango';
public static function method()
{
print self::$item."\r\n"; // It prints 'Foo' on everyway... :(
print self::$other."\r\n"; // We just think that, this one prints 'cA' only, but... :)
}
/**
* Now we drop redeclaring the setOther() method, use cA with 'self::' just for fun.
*/
}
class cD extends cA
{
/**
* Test property with redefined default value
*/
protected static $item = 'Foxtrot';
/**
* Now we drop redeclaring all methods to complete this issue.
*/
}
cB::setOther('cB'); // It's cB::method()!
cB::method(); // It's cA::method()!
cC::setOther('cC'); // It's cA::method()!
cC::method(); // It's cC::method()!
cD::setOther('cD'); // It's cA::method()!
cD::method(); // It's cA::method()!
/**
* Results: ->
* Foo
* cB
* Tango
* cC
* Foo
* cD
*
* What the hell?! :)
*/
?>
值得注意的是,上述變數也可以是一個物件實例。這似乎是從實例的角度來看,在繼承階層中盡可能高地引用靜態函數最簡單的方法。我在非靜態方法中使用 static::something() 時遇到了一些奇怪的行為。
請參考以下範例程式碼
<?php
class FooClass {
public function testSelf() {
return self::t();
}
public function testThis() {
return $this::t();
}
public static function t() {
return 'FooClass';
}
function __toString() {
return 'FooClass';
}
}
class BarClass extends FooClass {
public static function t() {
return 'BarClass';
}
}
$obj = new BarClass();
print_r(Array(
$obj->testSelf(), $obj->testThis(),
));
?>
輸出結果如下:
<pre>
Array
(
[0] => FooClass
[1] => BarClass
)
</pre>
如您所見,`__toString` 對此没有任何影響。如果您剛好想知道這是否是正確的做法。
您可以使用 `self` 來存取這個類別,使用 `parent` 來存取父類別,那麼您要如何存取父類別的父類別呢?或者要如何存取深層類別繼承體系的最根類別?答案是使用類別名稱。這與使用 `parent` 的效果相同。以下範例說明我的意思。以下程式碼
<?php
類別 A
{
保護的 $x = 'A';
公開 函數 f()
{
返回 '['.$this->x.']';
}
}
類別 B 延伸 A
{
保護的 $x = 'B';
公開 函數 f()
{
返回 '{'.$this->x.'}';
}
}
類別 C 延伸 B
{
保護的 $x = 'C';
公開 函數 f()
{
返回 '('.$this->x.')'.父類別::f().B::f().A::f();
}
}
$a = 新建 A();
$b = 新建 B();
$c = 新建 C();
印出 $a->f().'<br/>';
印出 $b->f().'<br/>';
印出 $c->f().'<br/>';
?>
將輸出
[A] -- {B} -- (C){C}{C}[C]
<?php
/**
* 從類別外部存取常數
*/
class Foo{
public const A = "Constant A";
}
echo Foo::A;
echo "\n";
/**
* 在類別內部存取常數
*/
class Bar{
public const A = "Constant A";
public function abc(){
echo self::A;
echo "\n";
}
}
$obj = new Bar;
$obj->abc();
/**
* 在子類別中存取常數
*/
class Baz extends Bar{
public function abc(){
echo parent::A;
}
}
$obj = new Baz;
$obj->abc();
//靜態屬性和靜態方法也遵循此原則。