PHP 属性
概述
类的变量成员叫做属性(Property),也称为字段(Field)。属性声明需要至少一个修饰符(访问控制、static 或 readonly),可选地跟类型声明和默认值。
版本要求
- PHP 7.4+:支持属性类型声明
- PHP 8.1+:支持
readonly属性 - PHP 8.2+:弃用动态属性
基础概念
属性声明
属性声明语法:修饰符 [类型] $变量名 [= 默认值];
php
<?php
declare(strict_types=1);
class User
{
public string $name;
public int $age = 0;
protected string $email;
private \DateTimeImmutable $createdAt;
public static int $count = 0;
public readonly int $id;
}默认值规则
属性默认值必须是常量表达式,不能是函数调用、变量引用等运行时才能确定的值。
php
<?php
declare(strict_types=1);
class Config
{
// 合法:常量表达式
public string $name = 'default';
public int $version = 1;
public array $options = ['debug' => false];
public bool $enabled = true;
// 非法:运行时表达式
// public string $time = date('Y-m-d'); // Fatal error
// public int $rand = rand(); // Fatal error
}语法与代码
类型声明(PHP 7.4+)
PHP 7.4 起支持属性类型声明(callable 除外)。类型化属性在访问前必须初始化,否则会抛出 Error。
php
<?php
declare(strict_types=1);
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);
$circle = new Shape();
$circle->setName('circle');
echo $circle->getName(); // circle
// Fatal error: Typed property Shape::$numberOfSides must not be accessed before initialization
// echo $circle->getNumberOfSides();只读属性(PHP 8.1+)
readonly 属性只能在初始化时赋值一次。PHP 8.4 之前隐式为私有设置,PHP 8.4 起隐式为 protected(set)。
php
<?php
declare(strict_types=1);
class Post
{
public function __construct(
public readonly int $id,
public readonly string $title,
public readonly \DateTimeImmutable $createdAt = new \DateTimeImmutable(),
) {}
// readonly 属性只能初始化一次,之后不可修改
}
$post = new Post(1, 'Hello World');
echo $post->title; // Hello World
// Fatal error: Cannot modify readonly property Post::$title
// $post->title = 'New Title';WARNING
readonly 属性不能有默认值(否则等同于常量)。readonly 不支持静态属性。类型化是 readonly 的前提。
readonly 属性与内部可变性
只读属性阻止的是属性本身的重新赋值,但不阻止内部可变对象的状态修改。
php
<?php
declare(strict_types=1);
class Configuration
{
public function __construct(
public readonly array $settings = [],
public readonly \stdClass $meta = new \stdClass(),
) {}
}
$config = new Configuration(['debug' => true]);
// 合法:修改内部可变对象的状态
$config->settings['debug'] = false;
$config->meta->foo = 'bar';
// 非法:替换整个属性
// $config->settings = ['new' => true]; // Error
// $config->meta = new \stdClass(); // Error详细说明
访问未初始化的类型化属性
PHP 对未初始化的类型化属性会抛出 Error。可以使用 isset() 检查属性是否已初始化。
php
<?php
declare(strict_types=1);
class Example
{
public string $name;
}
$obj = new Example();
var_dump(isset($obj->name)); // false
var_dump(property_exists($obj, 'name')); // true
// 访问未初始化属性 - Error
// echo $obj->name; // Error: Typed property must not be accessed before initialization属性的类型
PHP 支持多种属性类型:
| 类型 | 示例 | PHP 版本 |
|---|---|---|
| 标量类型 | int, string, bool, float | 7.4+ |
| 可空类型 | ?string, string|null | 7.4+ / 8.0+ |
| 复合类型 | array, object, iterable | 7.4+ |
| 类/接口类型 | \DateTimeImmutable, ?\stdClass | 7.4+ |
| 联合类型 | int|string, self|null | 8.0+ |
| 交集类型 | A&B, Countable&Traversable | 8.1+ |
mixed | mixed | 8.0+ |
动态属性(已弃用)
PHP 8.2 起弃用动态属性。如果确实需要,使用 #[\AllowDynamicProperties] 注解,或实现 __get/__set 魔术方法。
php
<?php
declare(strict_types=1);
// 推荐替代方案 1:使用 AllowDynamicProperties 注解
#[\AllowDynamicProperties]
class DynamicAllowed
{
// 允许动态属性,不产生警告
}
// 推荐替代方案 2:使用魔术方法
class MagicProperties
{
private array $data = [];
public function __get(string $name): mixed
{
return $this->data[$name] ?? null;
}
public function __set(string $name, mixed $value): void
{
$this->data[$name] = $value;
}
public function __isset(string $name): bool
{
return isset($this->data[$name]);
}
public function __unset(string $name): void
{
unset($this->data[$name]);
}
}实战示例
值对象模式
php
<?php
declare(strict_types=1);
class EmailAddress
{
public function __construct(
public readonly string $value,
) {
if (!filter_var($value, FILTER_VALIDATE_EMAIL)) {
throw new \InvalidArgumentException(
"Invalid email address: {$value}"
);
}
}
public function domain(): string
{
return substr($this->value, strpos($this->value, '@') + 1);
}
public function localPart(): string
{
return substr($this->value, 0, strpos($this->value, '@'));
}
public function equals(self $other): bool
{
return strtolower($this->value) === strtolower($other->value);
}
}
$email = new EmailAddress('user@example.com');
echo $email->domain(); // example.com
echo $email->localPart(); // user注意事项
- 类型化属性必须初始化:访问未初始化的类型化属性会抛出
Error - readonly 不能有默认值:
public readonly int $x = 42是非法的 - callable 不能用作属性类型:会导致引擎混淆
- 避免动态属性:PHP 8.2+ 已弃用,应显式声明属性
最佳实践
- 优先使用构造器属性提升(PHP 8.0+)简化属性声明和初始化
- 不可变属性使用
readonly,明确表达意图 - 始终声明类型,利用类型系统提升代码安全性
- 避免可变对象赋给 readonly:虽然合法,但容易产生混淆
- 使用值对象封装复杂概念:如 EmailAddress、Money 等
进阶用法
调试与测试技巧
php
<?php
declare(strict_types=1);
// 单元测试辅助函数
function createTestResource(): mixed
{
return match (true) {
default => new stdClass(),
};
}
// 调试输出函数
function debugOutput(mixed , string = ''): void
{
= ? ": " : '';
.= print_r(, true);
fwrite(STDERR, . "\n");
}
// 性能基准测试
function benchmark(callable , int = 1000): float
{
= hrtime(true);
for ($i = 0; $i < $iterations; $i++) {
$fn();
}
return (hrtime(true) - $start) / 1e9;
}日志记录实践
php
<?php
declare(strict_types=1);
/**
* 简易日志记录器
*/
class SimpleLogger
{
private string $logFile;
private string $level = 'INFO';
public function __construct(string $logFile)
{
$this->logFile = $logFile;
}
public function info(string $message, array $context = []): void
{
$this->log('INFO', $message, $context);
}
public function warning(string $message, array $context = []): void
{
$this->log('WARNING', $message, $context);
}
public function error(string $message, array $context = []): void
{
$this->log('ERROR', $message, $context);
}
private function log(string $level, string $message, array $context): void
{
$timestamp = date('Y-m-d H:i:s');
$contextStr = $context ? ' ' . json_encode($context, JSON_UNESCAPED_UNICODE) : '';
$line = "[{$timestamp}] [{$level}] {$message}{$contextStr}\n";
file_put_contents($this->logFile, $line, FILE_APPEND | LOCK_EX);
}
}配置与环境检测
php
<?php
declare(strict_types=1);
// 环境检测工具
class EnvironmentChecker
{
public static function checkRequirements(array $requirements): array
{
$results = [];
foreach ($requirements as $name => $check) {
$results[$name] = is_callable($check) ? $check() : false;
}
return $results;
}
public static function getSystemInfo(): array
{
return [
'php_version' => PHP_VERSION,
'os' => PHP_OS,
'sapi' => PHP_SAPI,
'memory_limit' => ini_get('memory_limit'),
'max_execution_time' => ini_get('max_execution_time'),
'loaded_extensions' => get_loaded_extensions(),
];
}
}常见问题排查
| 问题 | 可能原因 | 解决方案 |
|---|---|---|
| 连接超时 | 网络问题/配置错误 | 检查配置,增加超时时间 |
| 权限不足 | 文件/目录权限 | 使用 chmod/chown 修正 |
| 性能下降 | 索引缺失/数据量大 | 添加索引,优化查询 |
| 数据不一致 | 并发冲突/事务残留 | 使用锁机制和事务 |
| 内存溢出 | 大数据集/未释放资源 | 增大内存限制,分批处理 |
故障排除步骤
- 检查错误日志和异常信息
- 确认配置和环境是否正确
- 使用调试工具逐步排查
- 参考官方文档查找已知问题
版本兼容性说明
| 功能 | 最低版本 | 说明 |
|---|---|---|
| 基础功能 | PHP 8.1 | 本文档基准版本 |
| 只读属性 | PHP 8.1 | public readonly 修饰符 |
| 枚举类型 | PHP 8.1 | enum 类型和 match 表达式 |
| Fiber | PHP 8.1 | 协程/轻量级并发 |
| 命名参数 | PHP 8.0 | foo(arg_name: value) |
| 联合类型 | PHP 8.0 | `int |
| Null 安全运算符 | PHP 8.0 | $obj?->method() |
| 析构器 promotion | PHP 8.0 | __construct(public $x) |
php
<?php
declare(strict_types=1);
// 版本兼容性检测
function ensureVersion(string $minVersion): void
{
if (version_compare(PHP_VERSION, $minVersion, '<')) {
throw new RuntimeException(
sprintf('需要 PHP %s+, 当前版本: %s', $minVersion, PHP_VERSION)
);
}
}
ensureVersion('8.1.0');