PHP Conference Japan 2024

範圍解析運算子 (::)

範圍解析運算子(也稱為 Paamayim Nekudotayim),簡而言之就是雙冒號,是一個允許存取類別或其父類別的常數靜態屬性或靜態方法的標記。此外,靜態屬性或方法可以透過延遲靜態繫結來覆寫。

從類別定義外部參考這些項目時,請使用類別的名稱。

可以使用變數來參考類別。變數的值不能是關鍵字(例如 selfparentstatic)。

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;
?>

三個特殊的關鍵字 selfparentstatic 用於在類別定義內部存取屬性或方法。

範例 #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();
?>

另請參閱一些靜態調用技巧的範例

新增註解

使用者貢獻的註解 11 則註解

Theriault
14 年前
類別常數、類別屬性(靜態)和類別函式(靜態)都可以使用相同的名稱,並使用雙冒號存取。

<?php

class A {

public static
$B = '1'; # 靜態類別變數

const B = '2'; # 類別常數

public static function B() { # 靜態類別函式
return '3';
}

}

echo
A::$B . A::B . A::B(); # 輸出:123
?>
1naveengiri at gmail dot com
7 年前
在 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。
guy at syntheticwebapps dot com
11 年前
似乎您可以使用類別名稱以外的名稱,透過 :: 從類別定義外部參考類別的靜態變數、常數和靜態函式。該語言似乎允許您使用物件本身。

例如
class horse {
{
static $props = ['order' => 'mammal'];
}
}
echo $animal::$props['order'];

// 輸出 'mammal'

這似乎沒有被記錄,但我認為這是該語言中一個重要的便利性。我希望看到它被記錄下來並被正式支援。

如果它沒有被正式支援,替代方案似乎會很麻煩,例如:

$animalClass = get_class($animal);
echo $animalClass::$props['order'];
jasverix at NOSPAM dot gmail dot com
11 年前
剛發現使用類別名稱也可以呼叫祖先類別的類似函式。

<?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() 從未被執行。
giovanni at gargani dot it
15 年前
嗯,這是一段「瑞士刀」般的程式碼,可以用來呼叫父類別方法。唯一的限制是不能用於「傳參考」的參數。
主要優點是您不需要知道父類別的「實際」簽名(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());
}

}
?>
remy dot damour at ----no-spam---laposte dot net
14 年前
從 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
?>
luka8088 at gmail dot com
15 年前
一個使用 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 !

沒有任何警告或錯誤!
csaba dot dobai at php-sparcle dot com
15 年前
針對「後期靜態綁定」這個主題,我在下方發佈了一段程式碼,示範如何在後期類別中設定變數值,並在父類別(或父類別的父類別等等)中印出該值。

<?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?! :)
*/

?>
wouter at interpotential dot com
15 年前
值得注意的是,上述變數也可以是一個物件實例。這似乎是從實例的角度來看,在繼承階層中盡可能高地引用靜態函數最簡單的方法。我在非靜態方法中使用 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` 對此没有任何影響。如果您剛好想知道這是否是正確的做法。
developit at mail dot ru
18 年前
您可以使用 `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]
gazianis2200 at gmail dot com
10 個月前
<?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();

//靜態屬性和靜態方法也遵循此原則。
To Top