2024 年日本 PHP 研討會

屬性

類別成員變數稱為屬性。它們也可能被稱為欄位等其他術語,但在本參考文件中將使用屬性。它們的定義至少使用一個修飾詞(例如 可見性靜態關鍵字,或者從 PHP 8.1.0 開始的 唯讀),可選地(除了 readonly 屬性之外),從 PHP 7.4 開始,後面接著類型聲明,再接著一般的變數聲明。此聲明可以包含初始化,但初始化必須是 常數 值。

注意:

一種過時的類別屬性聲明方式是使用 var 關鍵字而不是修飾詞。

注意未聲明 可見性 修飾詞的屬性將被聲明為 public(公開)。

在類別方法中,可以使用 ->(物件運算子)存取非靜態屬性:$this->property(其中 property 是屬性的名稱)。靜態屬性則使用 ::(雙冒號)存取:self::$property。有關靜態和非靜態屬性之間差異的更多資訊,請參閱 靜態關鍵字

當從物件上下文中呼叫類別方法時,虛擬變數 $this 在任何類別方法內都可用。 $this 是呼叫物件的值。

範例 #1 屬性聲明

<?php
class SimpleClass
{
public
$var1 = 'hello ' . 'world';
public
$var2 = <<<EOD
hello world
EOD;
public
$var3 = 1+2;
// 無效的屬性聲明:
public $var4 = self::myStaticMethod();
public
$var5 = $myVar;

// 有效的屬性聲明:
public $var6 = myConstant;
public
$var7 = [true, false];

public
$var8 = <<<'EOD'
hello world
EOD;

// 沒有可見性修飾詞:
static $var9;
readonly
int $var10;
}
?>

注意:

有各種函式可以處理類別和物件。請參閱類別/物件函式參考。

類型宣告

從 PHP 7.4.0 開始,屬性定義可以包含類型宣告,但 callable 例外。

範例 #2 類型化屬性範例

<?php

class User
{
public
int $id;
public ?
string $name;

public function
__construct(int $id, ?string $name)
{
$this->id = $id;
$this->name = $name;
}
}

$user = new User(1234, null);

var_dump($user->id);
var_dump($user->name);

?>

上述範例將輸出:

int(1234)
NULL

必須先初始化類型化屬性,才能存取它們,否則會拋出 Error

範例 #3 存取屬性

<?php

class Shape
{
public
int $numberOfSides;
public
string $name;

public function
setNumberOfSides(int $numberOfSides): void
{
$this->numberOfSides = $numberOfSides;
}

public function
setName(string $name): void
{
$this->name = $name;
}

public function
getNumberOfSides(): int
{
return
$this->numberOfSides;
}

public function
getName(): string
{
return
$this->name;
}
}

$triangle = new Shape();
$triangle->setName("triangle");
$triangle->setNumberofSides(3);
var_dump($triangle->getName());
var_dump($triangle->getNumberOfSides());

$circle = new Shape();
$circle->setName("circle");
var_dump($circle->getName());
var_dump($circle->getNumberOfSides());
?>

上述範例將輸出:

string(8) "triangle"
int(3)
string(6) "circle"

Fatal error: Uncaught Error: Typed property Shape::$numberOfSides must not be accessed before initialization

唯讀屬性

從 PHP 8.1.0 開始,屬性可以使用 `readonly` 修飾符宣告,這可以防止屬性在初始化後被修改。在 PHP 8.4.0 之前,`readonly` 屬性隱含為 private-set,並且只能從同一個類別中寫入。從 PHP 8.4.0 開始,`readonly` 屬性隱含為 protected(set),因此可以從子類別中設定。如果需要,可以明確地覆蓋此行為。

範例 #4 唯讀屬性範例

<?php

class Test {
public readonly
string $prop;

public function
__construct(string $prop) {
// 合法的初始化。
$this->prop = $prop;
}
}

$test = new Test("foobar");
// 合法的讀取。
var_dump($test->prop); // string(6) "foobar"

// 非法的重新賦值。即使賦值的值相同也無效。
$test->prop = "foobar";
// 錯誤:無法修改唯讀屬性 Test::$prop
?>

注意:

唯讀修飾詞只能應用於類型化屬性。可以使用 Mixed 類型來建立沒有類型限制的唯讀屬性。

注意:

不支援唯讀靜態屬性。

唯讀屬性只能初始化一次,而且只能在其宣告的範圍內初始化。任何其他對該屬性的賦值或修改都會導致 Error 例外。

範例 #5 非法初始化唯讀屬性

<?php
class Test1 {
public readonly
string $prop;
}

$test1 = new Test1;
// 在私有範圍外進行非法初始化。
$test1->prop = "foobar";
// 錯誤:無法從全域範圍初始化唯讀屬性 Test1::$prop
?>

注意:

不允許在唯讀屬性上指定明確的預設值,因為具有預設值的唯讀屬性本質上與常數相同,因此並不是特別有用。

<?php

class Test {
// 致命錯誤:唯讀屬性 Test::$prop 不能有預設值
public readonly int $prop = 42;
}
?>

注意:

唯讀屬性一旦初始化後就不能使用 unset() 函式。但是,可以在初始化之前,從宣告屬性的範圍內,使用 unset() 函式來取消設定唯讀屬性。

修改唯讀屬性並不一定只是單純的賦值,以下所有操作都會導致 Error 例外

<?php

class Test {
public function
__construct(
public readonly
int $i = 0,
public readonly array
$ary = [],
) {}
}

$test = new Test;
$test->i += 1;
$test->i++;
++
$test->i;
$test->ary[] = 1;
$test->ary[0][] = 1;
$ref =& $test->i;
$test->i =& $ref;
byRef($test->i);
foreach (
$test as &$prop);
?>

然而,唯讀屬性並不排除內部可變性。儲存在唯讀屬性中的物件(或資源)內部仍然可以被修改。

<?php

class Test {
public function
__construct(public readonly object $obj) {}
}

$test = new Test(new stdClass);
// 合法的內部修改
$test->obj->foo = 1;
// 非法的重新賦值
$test->obj = new stdClass;
?>

從 PHP 8.3.0 版本開始,唯讀屬性可以在使用 __clone() 方法複製物件時重新初始化。

範例 #6 唯讀屬性與複製

<?php
class Test1 {
public readonly ?
string $prop;

public function
__clone() {
$this->prop = null;
}

public function
setProp(string $prop): void {
$this->prop = $prop;
}
}

$test1 = new Test1;
$test1->setProp('foobar');

$test2 = clone $test1;
var_dump($test2->prop); // NULL
?>

動態屬性

如果嘗試將值賦給一個物件上不存在的屬性,PHP 會自動建立一個對應的屬性。這個動態建立的屬性將*只*在這個類別實例上可用。

警告

自 PHP 8.2.0 起,動態屬性已被棄用。建議改為宣告屬性。要處理任意屬性名稱,類別應該實作魔術方法 __get()__set()。最後,可以將類別標記為 #[\AllowDynamicProperties] 屬性。

新增註解

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

anca at techliminal dot com
9 年前
您可以透過以下方式存取包含連字號的屬性名稱(例如,因為您將 XML 檔案轉換為物件)

<?php
$ref
= new StdClass();
$ref->{'ref-type'} = 'Journal Article';
var_dump($ref);
?>
匿名
13 年前
`$this` 可以被轉型成陣列。但是在這樣做的時候,它會根據屬性分類,在屬性名稱/新的陣列鍵值前面加上某些資料。公開屬性的名稱不會更改。保護屬性會在前面加上一個空格填充的 `*`。私有屬性會在前面加上空格填充的類別名稱…。

<?php

class test
{
public
$var1 = 1;
protected
$var2 = 2;
private
$var3 = 3;
static
$var4 = 4;

public function
toArray()
{
return (array)
$this;
}
}

$t = new test;
print_r($t->toArray());

/* 輸出:

Array
(
[var1] => 1
[ * var2] => 2
[ test var3] => 3
)

*/
?>

這是將任何物件轉換為陣列時的記錄行為(請參閱 </language.types.array.php#language.types.array.casting> PHP 手冊頁面)。轉換物件為陣列時,所有屬性,無論可見性如何,都會顯示(一些內建物件除外)。

要取得一個所有屬性名稱都未更改的陣列,請在類別範圍內的任何方法中使用 `get_object_vars($this)` 函式來取得所有屬性的陣列,無論外部可見性如何,或在類別範圍外使用 `get_object_vars($object)` 來取得僅包含公開屬性的陣列(請參閱:</function.get-object-vars.php> PHP 手冊頁面)。
zzzzBov
14 年前
不要將 PHP 的屬性版本與其他語言(例如 C++)中的屬性混淆。在 PHP 中,屬性與特性相同,是沒有功能的簡單變數。它們應該被稱為特性,而不是屬性。

屬性具有隱式的存取器和修改器功能。我建立了一個允許隱式屬性功能的抽象類別。

<?php

abstract class PropertyObject
{
public function
__get($name)
{
if (
method_exists($this, ($method = 'get_'.$name)))
{
return
$this->$method();
}
else return;
}

public function
__isset($name)
{
if (
method_exists($this, ($method = 'isset_'.$name)))
{
return
$this->$method();
}
else return;
}

public function
__set($name, $value)
{
if (
method_exists($this, ($method = 'set_'.$name)))
{
$this->$method($value);
}
}

public function
__unset($name)
{
if (
method_exists($this, ($method = 'unset_'.$name)))
{
$this->$method();
}
}
}

?>

繼承此類別後,您可以使用 PHP 的魔術方法建立存取器和修改器,當存取對應的屬性時,這些方法會自動被呼叫。
To Top