PHP Conference Japan 2024

属性

类成员变量称为属性。它们可以使用其他术语(例如字段)来引用,但出于本参考的目的,将使用属性。它们通过使用至少一个修饰符(例如可见性静态关键字,或者从 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

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 属性隐式设置为私有设置,并且只能从同一类中写入。从 PHP 8.4.0 开始,readonly 属性隐式为 protected(set),因此可以在子类中设置。如果需要,可以显式覆盖它。

示例 #4 只读属性示例

<?php

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
Test1 {
public readonly
string $prop;
}

$test1 = new Test1;
// 非法初始化,不在私有作用域内。
$test1->prop = "foobar";
// 错误:无法从全局作用域初始化只读属性 Test1::$prop
?>

注意:

不允许在只读属性上指定显式默认值,因为具有默认值的只读属性本质上与常量相同,因此没有特别的有用之处。

<?php

Test {
// 严重错误:只读属性 Test::$prop 不能有默认值
public readonly int $prop = 42;
}
?>

注意:

一旦初始化,就无法 unset() 只读属性。但是,可以在初始化之前,从声明属性的作用域取消设置只读属性。

修改不一定是简单的赋值,以下所有操作都将导致 Error 异常

<?php

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

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
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 条注释

88
anca at techliminal dot com
9 年前
您可以通过以下方式访问包含连字符的属性名称(例如,因为您将 XML 文件转换为对象)

<?php
$ref
= new StdClass();
$ref->{'ref-type'} = 'Journal Article';
var_dump($ref);
?>
69
匿名用户
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 手册页面)。
28
zzzzBov
14 年前
不要将 php 中的属性版本与其他语言(例如 C++)中的属性混淆。在 php 中,属性与属性相同,是简单的变量,没有功能。它们应该称为属性,而不是属性。

属性具有隐式访问器和修改器功能。我创建了一个抽象类,允许隐式属性功能。

<?php

抽象类 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