PHP Conference Japan 2024

Static 關鍵字

提示

此頁面說明使用 static 關鍵字定義靜態方法和屬性。 static 也可用於定義靜態變數定義靜態匿名函式以及延遲靜態繫結。有關 static 的這些含義,請參閱這些頁面。

將類別屬性或方法宣告為 static,即可在不實例化類別的情況下存取它們。在實例化的類別物件中也可以靜態地存取它們。

靜態方法

由於靜態方法可以在不建立物件實例的情況下呼叫,因此在宣告為 static 的方法內無法使用虛擬變數 $this

警告

以靜態方式呼叫非靜態方法會擲出 Error

在 PHP 8.0.0 之前,以靜態方式呼叫非靜態方法已被棄用,並會產生 E_DEPRECATED 警告。

範例 #1 靜態方法範例

<?php
class Foo {
public static function
aStaticMethod() {
// ...
}
}

Foo::aStaticMethod();
$classname = 'Foo';
$classname::aStaticMethod();
?>

靜態屬性

靜態屬性是透過 範圍解析運算子 (::) 來存取,並且不能透過物件運算子 (->) 來存取。

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

範例 #2 靜態屬性範例

<?php
class Foo
{
public static
$my_static = 'foo';

public function
staticValue() {
return
self::$my_static;
}
}

class
Bar extends Foo
{
public function
fooStatic() {
return
parent::$my_static;
}
}


print
Foo::$my_static . "\n";

$foo = new Foo();
print
$foo->staticValue() . "\n";
print
$foo->my_static . "\n"; // 未定義的「屬性」my_static

print $foo::$my_static . "\n";
$classname = 'Foo';
print
$classname::$my_static . "\n";

print
Bar::$my_static . "\n";
$bar = new Bar();
print
$bar->fooStatic() . "\n";
?>

以上範例在 PHP 8 中的輸出類似於

foo
foo

Notice: Accessing static property Foo::$my_static as non static in /in/V0Rvv on line 23

Warning: Undefined property: Foo::$my_static in /in/V0Rvv on line 23

foo
foo
foo
foo
新增註釋

使用者貢獻的註釋 28 則註釋

inkredibl
16 年前
請注意,如果您正在尋找用於在函式(或方法)內宣告靜態變數的 static 關鍵字用法,則應閱讀「變數/變數範圍」。我自己直到最近才了解 PHP 的這個知識缺口,並且必須透過 Google 搜尋才能找到答案。我認為此頁面應該有一個指向靜態函式變數的「另請參閱」連結。
https://php.dev.org.tw/manual/en/language.variables.scope.php
payal001 at gmail dot com
13 年前
這裡靜態訪問的屬性會優先使用被調用類別的屬性。而 self 關鍵字則強制只使用當前類別的屬性。參考以下範例

<?php
class a{

static protected
$test="class a";

public function
static_test(){

echo static::
$test; // 結果為 class b
echo self::$test; // 結果為 class a

}

}

class
b extends a{

static protected
$test="class b";

}

$obj = new b();
$obj->static_test();
?>
artekpuck at gmail dot com
6 年前
值得一提的是,每個靜態變數都只有一個值,所有實例都共用這個值。
sideshowAnthony at googlemail dot com
9 年前
static 關鍵字仍然可以在函式內部使用(以非物件導向的方式)。所以如果你需要一個儲存在你的類別中的值,但它非常特定於函式,你可以這樣使用

class aclass {
public static function b(){
static $d=12; // 僅在第一次函式呼叫時設定為 12
$d+=12;
return "$d\n";
}
}

echo aclass::b(); //24
echo aclass::b(); //36
echo aclass::b(); //48
echo aclass::$d; //致命錯誤
rahul dot anand77 at gmail dot com
9 年前
要檢查在類別中宣告的方法是否為靜態方法,您可以使用以下程式碼。PHP5 有一個 Reflection Class,非常有用。

try {
$method = new ReflectionMethod( 'className::methodName' );
if ( $method->isStatic() )
{
// 方法是靜態的。
}
}
catch ( ReflectionException $e )
{
// 方法不存在
echo $e->getMessage();
}

*您可以閱讀更多關於 Reflection 類別的資訊:https://php.dev.org.tw/manual/en/class.reflectionclass.php
匿名
19 年前
你誤解了繼承的意義:當你從基底類別繼承時,成員並不會被複製。成員是透過繼承共享的,並且可以根據可見性(公開、保護、私有)由衍生類別存取。

靜態成員和非靜態成員的區別僅在於非靜態成員與類別的實例綁定,而靜態成員與類別綁定,而不是與特定實例綁定。
也就是說,靜態成員由類別的所有實例共享,而非靜態成員則為每個類別實例存在。

因此,在您的範例中,根據物件導向設計的原則,靜態屬性具有正確的值。
class Base
{
public $a;
public static $b;
}

class Derived extends Base
{
public function __construct()
{
$this->a = 0;
parent::$b = 0;
}
public function f()
{
$this->a++;
parent::$b++;
}
}

$i1 = new Derived;
$i2 = new Derived;

$i1->f();
echo $i1->a, ' ', Derived::$b, "\n";
$i2->f();
echo $i2->a, ' ', Derived::$b, "\n";

輸出:
1 1
1 2
vinayak dot anivase at gmail dot com
6 年前
這也是可行的:

class Foo {
public static $bar = 'a static property';
}

$baz = (new Foo)::$bar;
echo $baz;
匿名
11 年前
需要注意的是,在「範例 #2」中,您也可以如下方式呼叫一個變數定義的靜態方法:

<?php
class Foo {
public static function
aStaticMethod() {
// ...
}
}

$classname = 'Foo';
$methodname = 'aStaticMethod';
$classname::{$methodname}(); // 相信從 PHP 5.3.0 開始支援
?>
ASchmidt at Anamera dot net
6 年前
理解靜態屬性在類別繼承中的行為非常重要:

- 在父類別和子類別中都定義的靜態屬性,每個類別將持有**不同的**值。在子類別方法中正確使用 self:: 和 static:: 至關重要,才能參考到預期的靜態屬性。

- **僅**在父類別中定義的靜態屬性將共享一個**共同的**值。

<?php
declare(strict_types=1);

class
staticparent {
static
$parent_only;
static
$both_distinct;

function
__construct() {
static::
$parent_only = 'fromparent';
static::
$both_distinct = 'fromparent';
}
}

class
staticchild extends staticparent {
static
$child_only;
static
$both_distinct;

function
__construct() {
static::
$parent_only = 'fromchild';
static::
$both_distinct = 'fromchild';
static::
$child_only = 'fromchild';
}
}

$a = new staticparent;
$a = new staticchild;

echo
'Parent: parent_only=', staticparent::$parent_only, ', both_distinct=', staticparent::$both_distinct, "<br/>\r\n";
echo
'Child: parent_only=', staticchild::$parent_only, ', both_distinct=', staticchild::$both_distinct, ', child_only=', staticchild::$child_only, "<br/>\r\n";
?>

將輸出:
Parent: parent_only=fromchild, both_distinct=fromparent
Child: parent_only=fromchild, both_distinct=fromchild, child_only=fromchild
b1tchcakes
8 年前
<?php

trait t {
protected
$p;
public function
testMe() {echo 'static:'.static::class. ' // self:'.self::class ."\n";}
}

class
a { use t; }
class
b extends a {}

echo (new
a)->testMe();
echo (new
b)->testMe();

輸出
static:a // self:t
static:b // self:t
davidn at xnet dot co dot nz
15 年前
靜態變數會在子類別之間共享

<?php
class MyParent {

protected static
$variable;
}

class
Child1 extends MyParent {

function
set() {

self::$variable = 2;
}
}

class
Child2 extends MyParent {

function
show() {

echo(
self::$variable);
}
}

$c1 = new Child1();
$c1->set();
$c2 = new Child2();
$c2->show(); // 顯示 2
?>
webmaster at removethis dot weird-webdesign dot de
14 年前
在 PHP 5.2.x 或更早的版本中,由於缺乏後期靜態綁定,您可能會在子類別中初始化靜態變數時遇到問題。

<?php
class A {
protected static
$a;

public static function
init($value) { self::$a = $value; }
public static function
getA() { return self::$a; }
}

class
B extends A {
protected static
$a; // 重新定義 $a 供自己使用

// 繼承 init() 方法
public static function getA() { return self::$a; }
}

B::init('lala');
echo
'A::$a = '.A::getA().'; B::$a = '.B::getA();
?>

這將輸出
A::$a = lala; B::$a =

如果 (幾乎) 所有子類別的 init() 方法都相同,則不需在每個子類別中都實作 init() 方法,如此可避免產生冗餘程式碼。

解決方案 1
將所有成員變數改為非靜態。但是:這會在每個類別物件中產生冗餘資料。

解決方案 2
將 A 類別中的靜態變數 $a 改為陣列,使用子類別的名稱作為索引。如此一來,您就不必為子類別重新定義 $a,並且父類別的 $a 可以設為私有。

DataRecord 類別的簡短範例(無錯誤檢查)

<?php
abstract class DataRecord {
private static
$db; // MySQLi 連線,所有子類別都相同
private static $table = array(); // 子類別的資料表陣列

public static function init($classname, $table, $db = false) {
if (!(
$db === false)) self::$db = $db;
self::$table[$classname] = $table;
}

public static function
getDB() { return self::$db; }
public static function
getTable($classname) { return self::$table[$classname]; }
}

class
UserDataRecord extends DataRecord {
public static function
fetchFromDB() {
$result = parent::getDB()->query('select * from '.parent::getTable('UserDataRecord').';');

// 等等...
return $result; // UserDataRecord 物件的陣列
}
}

$db = new MySQLi(...);
UserDataRecord::init('UserDataRecord', 'users', $db);
$users = UserDataRecord::fetchFromDB();
?>

希望這能幫助一些因為某些原因需要在 PHP 5.2.x 伺服器上操作的人。當然,後期靜態綁定讓這種解決方法變得過時。
aidan at php dot net
19 年前
要檢查函式是否以靜態方式呼叫,您需要執行

<?php
函式 foo () {
$isStatic = !(isset($this) && get_class($this) == __CLASS__);
}
?>

更多資訊請參考 (http://blog.phpdoc.info/archives/4-Schizophrenic-Methods.html)。

(我很快就會將這個添加到手冊中)。
tolean_dj at yahoo dot com
13 年前
從 PHP 5.3 開始,您可以使用新的 static 關鍵字功能。以下是一個抽象單例類別的範例

<?php

抽象類別 Singleton {

protected static
$_instance = NULL;

/**
* 阻止直接建立物件
*/
final private 函式 __construct() { }

/**
* 阻止物件複製
*/
final private 函式 __clone() { }

/**
* 返回新的或現有的 Singleton 實例
* @return Singleton
*/
final public static 函式 getInstance(){
if(
null !== static::$_instance){
return static::
$_instance;
}
static::
$_instance = new static();
return static::
$_instance;
}

}
?>
gratcypalma at gmail dot om
13 年前
<?php
類別 foo {
private static
$getInitial;

public static 函式
getInitial() {
if (
self::$getInitial == null)
self::$getInitial = new foo();
return
self::$getInitial;
}
}

foo::getInitial();

/*
這是使用帶有靜態方法的新類別的範例..
希望它有幫助
*/

?>
ssj dot narutovash at gmail dot com
16 年前
我注意到您不能在 HEREDOC 字串中使用靜態成員。以下程式碼

類別 A
{
public static $BLAH = "user";

函式 __construct()
{
echo <<<EOD
<h1>Hello {self::$BLAH}</h1>
EOD;
}
}

$blah = new A();

在原始碼中產生以下內容

<h1>Hello {self::}</h1>

解決方案

在使用靜態成員之前,將其儲存在區域變數中,如下所示

類別 B
{
public static $BLAH = "user";

函式 __construct()
{
$blah = self::$BLAH;
echo <<<EOD

哈囉 {$blah}


EOD;
}
}

輸出的原始碼會是

哈囉 user

Jay Cain
14 年前
關於類別中複雜靜態變數的初始化,您可以透過建立一個名為 init() 之類的靜態函式,並在類別定義之後立即呼叫它,來模擬靜態建構函式。

<?php
class Example {
private static
$a = "Hello";
private static
$b;

public static function
init() {
self::$b = self::$a . " World!";
}
}
Example::init();
?>
manishpatel2280 at gmail dot com
11 年前
實際上,我們可以說當我們不想建立物件實例時,會使用靜態方法。

例如:

validateEmail($email) {
if(T) return true;
return false;
}

//這樣做不太合理
$obj = new Validate();
$result = $obj->validateEmail($email);

//這樣做更合理
$result = Validate::validateEmail($email);
zerocool at gameinsde dot ru
16 年前
嗨,這是我簡單的單例模式範例,我想它對某些人可能有用。您可以使用此模式來連接到資料庫,例如。

<?php

類別 MySingleton
{
私有 靜態
$instance = null;

私有 函數
__construct()
{
$this-> name = 'Freddy';

}

公開 靜態 函數
getInstance()
{
if(
self::$instance == null)
{
echo
"已建立物件!<br>";
self::$instance = new self;

}

return
self::$instance;

}

公開 函數
sayHello()
{
echo
"嗨,我的名字是 {$this-> name}!<br>";

}

公開 函數
setName($name)
{
$this-> name = $name;

}

}

//

$objA = MySingleton::getInstance(); // 已建立物件!

$objA-> sayHello(); // 嗨,我的名字是 Freddy!

$objA-> setName("Alex");

$objA-> sayHello(); // 嗨,我的名字是 Alex!

$objB = MySingleton::getInstance();

$objB-> sayHello(); // 嗨,我的名字是 Alex!

$objB-> setName("Bob");

$objA-> sayHello(); // 嗨,我的名字是 Bob!

?>
michalf at ncac dot torun dot pl
19 年前
在 PHP 中,使用靜態元素的繼承是一個惡夢。請參考以下程式碼:

<?php
類別 BaseClass{
公開 靜態
$property;
}

類別
DerivedClassOne 繼承 BaseClass{
}

類別
DerivedClassTwo 繼承 BaseClass{
}

DerivedClassOne::$property = "foo";
DerivedClassTwo::$property = "bar";

echo
DerivedClassOne::$property; //你可能天真地預期輸出 "foo"...
?>

你預期會輸出什麼? "foo"?錯了。 答案是 "bar"!!!靜態變數不會被繼承,它們指向的是 BaseClass::$property。

在這個例子中,我認為繼承在靜態變數/方法的情況下無法運作,實在很可惜。請記住這一點,並在除錯時節省你的時間。

致上最誠摯的問候 - michal
Mirco
14 年前
最簡單的靜態建構子。

因為 PHP 沒有靜態建構子,而您可能需要初始化靜態類別變數,有一個簡單的方法,就是在類別定義之後直接呼叫您自己的函式。

例如:

<?php
function Demonstration()
{
return
'This is the result of demonstration()';
}

class
MyStaticClass
{
//public static $MyStaticVar = Demonstration(); //!!! 失敗:語法錯誤
public static $MyStaticVar = null;

public static function
MyStaticInit()
{
//這是靜態建構子
//因為在函式中,所有操作都是允許的,包括使用其他函式進行初始化

self::$MyStaticVar = Demonstration();
}
}
MyStaticClass::MyStaticInit(); //呼叫靜態建構子

echo MyStaticClass::$MyStaticVar;
//This is the result of demonstration()
?>
valentin at balt dot name
14 年前
如何基於靜態屬性實現單一儲存位置。

<?php
class a {

public function
get () {
echo
$this->connect();
}
}
class
b extends a {
private static
$a;

public function
connect() {
return
self::$a = 'b';
}
}
class
c extends a {
private static
$a;

public function
connect() {
return
self::$a = 'c';
}
}
$b = new b ();
$c = new c ();

$b->get();
$c->get();
?>
jkenigso at utk dot edu
10 年前
值得一提的是,靜態變數(以下意義)會持續存在

<?php
class StaticVars
{
public static
$a=1;
}
$b=new StaticVars;
$c=new StaticVars;

echo
$b::$a; //輸出 1
$c::$a=2;
echo
$b::$a; //輸出 2!
?>

請注意,即使 $b 和 $c 是完全不同的物件,$c::$a=2 也會更改 $b::$a 的值。
admin at shopinson dot com
4 年前
我使用實例化來直接訪問靜態屬性。

一個簡單的小技巧,您可以應用(使用物件來訪問類別中的靜態屬性)範圍解析運算符
<?php
class Shopinson {
const
MY_CONSTANT = 'MY_CONSTANT 的值 ';
}

class
Godwin extends Shopinson
{
public static
$myconstant = ' Paamayim Nekudotayim 或雙冒號。';
public function
SaySomething(){
echo
parent::MY_CONSTANT .PHP_EOL; // 輸出:MY_CONSTANT 的值
echo self::$myconstant; // 輸出:Paamayim Nekudotayim 或雙冒號。
}
}

$my_class = new Godwin();
print
$my_class::$myconstant;
$my_class::SaySomething();
echo
Godwin::$myconstant;
Godwin::SaySomething();

?>

print $my_class::$myconstant;
michael at digitalgnosis dot removethis dot com
19 年前
如果您嘗試編寫這樣做的類別

<?php

class Base
{
static function
Foo ()
{
self::Bar();
}
}

class
Derived extends Base
{
function
Bar ()
{
echo
"Derived::Bar()";
}
}

Derived::Foo(); // 我們希望它印出 "Derived::Bar()"

?>

那麼你會發現 PHP 無法做到(除非有人知道正確的方法?),因為 'self::' 指的是擁有程式碼的類別,而不是在運行時實際調用的類別。(__CLASS__ 也不起作用,因為:A. 它不能出現在 :: 之前,以及 B. 它的行為類似於 'self')

但如果您必須這樣做,那麼這裡有一個(只是稍微有點討厭的)解決方法

<?php

類別 Base
{
函式
Foo ( $class = __CLASS__ )
{
call_user_func(array($class,'Bar'));
}
}

類別
Derived 繼承 Base
{
函式
Foo ( $class = __CLASS__ )
{
parent::Foo($class);
}

函式
Bar ()
{
echo
"Derived::Bar()";
}
}

Derived::Foo(); // 這次可以正常運作了。

?>

請注意,Base::Foo() 不可以再宣告為 'static',因為靜態方法無法被覆寫(這表示如果錯誤級別包含 E_STRICT,將會觸發錯誤)。

如果 Foo() 需要參數,請在 $class=__CLASS__ 之前列出它們,並且在大多數情況下,您可以在整個程式碼中忽略該參數。

主要的注意事項是,您必須在每個子類別中覆寫 Foo(),並且在呼叫 parent::Foo() 時必須始終包含 $class 參數。
vvikramraj at yahoo dot com
16 年前
當嘗試實作單例類別時,您可能還想要
a) 將 __clone 設為私有來禁用它
b) 透過定義 __clone 來丟出例外,阻止使用者嘗試複製
Mathijs Vos
16 年前
<?php
類別 foo
{
公開 靜態
$myStaticClass;

公開 函式
__construct()
{
self::$myStaticClass = new bar();
}
}

類別
bar
{
公開 函式
__construct(){}
}
?>

請注意,這樣做無效。
請使用 self::$myStaticClass = new bar(); 而不是 self::myStaticClass = new bar();(注意 $ 符號)。
我花了一個小時才弄清楚這一點。
fakhar_anwar123 at hotmail dot com
4 年前
選擇的答案解決了問題。有一個有效的用例(設計模式),其中具有靜態成員函式的類別需要呼叫非靜態成員函式,並且在此之前,此靜態成員也應使用建構子來實例化單例。

**案例:**
例如,我正在實作 Swoole HTTP 請求事件,並將其回呼作為具有靜態成員的類別提供。靜態成員執行兩件事;它透過在類別建構子中進行初始化來建立類別的單例物件,而此靜態成員執行的第二件事是呼叫非靜態方法 'run()' 來處理請求(透過與 Phalcon 橋接)。因此,沒有建構子和非靜態呼叫的靜態類別對我來說是行不通的。
To Top