您可以透過以下方式存取包含連字號的屬性名稱(例如,因為您將 XML 檔案轉換為物件)
<?php
$ref = new StdClass();
$ref->{'ref-type'} = 'Journal Article';
var_dump($ref);
?>
類別成員變數稱為屬性。它們也可能被稱為欄位等其他術語,但在本參考文件中將使用屬性。它們的定義至少使用一個修飾詞(例如 可見性、靜態關鍵字,或者從 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
?>
注意:
注意:
不支援唯讀靜態屬性。
唯讀屬性只能初始化一次,而且只能在其宣告的範圍內初始化。任何其他對該屬性的賦值或修改都會導致 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
?>
您可以透過以下方式存取包含連字號的屬性名稱(例如,因為您將 XML 檔案轉換為物件)
<?php
$ref = new StdClass();
$ref->{'ref-type'} = 'Journal Article';
var_dump($ref);
?>
`$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 手冊頁面)。
不要將 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 的魔術方法建立存取器和修改器,當存取對應的屬性時,這些方法會自動被呼叫。