PHP 类与对象基本概念
概述
PHP 是一门支持面向对象编程(OOP)的多范式语言。从 PHP 5 开始,OOP 支持逐渐成熟;PHP 7/8 更是大幅增强了类型系统和面向对象特性。本页介绍类定义、实例化、属性与方法、$this 引用,以及对象与类之间的关系。
版本要求
本教程以 PHP 8.1+ 为基准,部分特性标注引入版本。
基础概念
什么是类
类(Class)是对象的蓝图或模板,定义了对象的属性(数据)和方法(行为)。类本身不是具体的数据,而是用来创建具体实例的抽象描述。
<?php
declare(strict_types=1);
class User
{
// 属性(状态)
public string $name;
public int $age;
// 方法(行为)
public function greet(): string
{
return "Hello, my name is {$this->name}.";
}
}- 类名必须以字母或下划线开头,正则表达式为
^[a-zA-Z_\x80-\xff][a-zA-Z0-9_\x80-\xff]*$。 - 自 PHP 8.4.0 起,弃用使用单个下划线
_作为类名。 - 类名可以是任何非 PHP 保留字 的有效标签。
什么是对象
对象(Object)是类的具体实例。一个类可以创建多个对象,每个对象拥有独立的属性值,但共享类中定义的方法。
<?php
declare(strict_types=1);
class User
{
public function __construct(
public string $name,
public int $age,
) {}
}
$alice = new User('Alice', 30);
$bob = new User('Bob', 25);
// 两个独立的对象,各自拥有不同状态
echo $alice->name; // Alice
echo $bob->name; // Bob类与对象的关系
| 概念 | 类 | 对象 |
|---|---|---|
| 本质 | 模板/蓝图 | 具体实例 |
| 内存 | 存在于类定义区 | 每个实例独立内存 |
| 数量 | 通常只有一个定义 | 可以创建任意数量 |
| 关键字 | class | new |
语法与代码
使用 new 实例化对象
使用 new 关键字创建类的实例。如果构造函数不需要参数,可以省略括号(PHP 8.4.0 起,括号始终可选)。
<?php
declare(strict_types=1);
class SimpleClass
{
public string $var = 'a default value';
public function displayVar(): void
{
echo $this->var;
}
}
// 标准实例化
$instance = new SimpleClass();
// 变量中存储类名来实例化
$className = 'SimpleClass';
$instance = new $className();
// PHP 8.0+:任意表达式实例化
$instance = new ('Simple' . 'Class')();$this 伪变量
在类的方法内部,$this 是一个指向当前对象实例的引用。通过 $this->property 访问属性,通过 $this->method() 调用方法。
<?php
declare(strict_types=1);
class Person
{
public string $name;
public function __construct(string $name)
{
$this->name = $name;
}
public function introduce(): string
{
// $this 引用当前对象实例
return "I am {$this->name}.";
}
}
$person = new Person('Alice');
echo $person->introduce(); // I am Alice.注意
以静态方式调用非静态方法在 PHP 8.0+ 会抛出 Error。在 PHP 8 之前会产生废弃通知,$this 将被声明为未定义。
对象赋值与引用
将一个对象赋值给新变量时,新变量指向同一个对象实例(对象句柄拷贝),而非副本。使用 clone 关键字可以创建真正的副本。
<?php
declare(strict_types=1);
class SimpleClass
{
public string $var = 'default';
}
$instance = new SimpleClass();
$assigned = $instance; // 指向同一个对象
$reference = &$instance; // 引用赋值
$instance->var = 'changed';
$instance = null; // $instance 和 $reference 变为 null
var_dump($instance); // null
var_dump($reference); // null
var_dump($assigned); // object(SimpleClass) { var => "changed" }::class 常量
ClassName::class 在编译时解析为完全限定的类名(FQCN),即使类不存在也不会报错。
<?php
declare(strict_types=1);
namespace App\Models;
class User
{
}
// 编译时解析
echo User::class; // App\Models\User
// PHP 8.0+:也可用于对象实例
$user = new User();
echo $user::class; // App\Models\User
// 即使类不存在也能解析名称
echo Does\Not\Exist::class; // Does\Not\ExistNullsafe 操作符
PHP 8.0+ 引入了 ?-> nullsafe 操作符,当对象引用为 null 时不抛出异常,而是返回 null 并跳过后续链式调用。
<?php
declare(strict_types=1);
class UserRepository
{
public function findUser(int $id): ?User
{
return null; // 模拟未找到用户
}
}
class User
{
public ?string $name = null;
}
$repository = new UserRepository();
// nullsafe 操作符链式调用
$result = $repository?->findUser(1)?->name;
var_dump($result); // null详细说明
属性与方法的命名空间
类的属性和方法存在于不同的"命名空间"中,允许同名。访问时根据上下文决定是读取属性还是调用方法。
<?php
declare(strict_types=1);
class Foo
{
public string $bar = 'property';
public function bar(): string
{
return 'method';
}
}
$obj = new Foo();
echo $obj->bar; // property(读取属性)
echo $obj->bar(); // method(调用方法)TIP
如果类属性被赋值为匿名函数,需要用括号包裹才能调用:($obj->bar)()。
只读类(PHP 8.2+)
使用 readonly 修饰整个类,自动为所有声明的属性添加 readonly 修饰符,并禁止动态属性。
<?php
declare(strict_types=1);
readonly class ReadOnlyUser
{
public function __construct(
public string $name,
public int $age,
) {}
}
$user = new ReadOnlyUser('Alice', 30);
echo $user->name; // Alice
// Fatal error: Cannot modify readonly property
// $user->name = 'Bob';实战示例
实体类设计
<?php
declare(strict_types=1);
class Product
{
public function __construct(
public readonly int $id,
public string $name,
public float $price,
public readonly \DateTimeImmutable $createdAt,
) {}
public function applyDiscount(float $percentage): void
{
if ($percentage < 0 || $percentage > 100) {
throw new \InvalidArgumentException(
'Discount must be between 0 and 100'
);
}
$this->price *= (1 - $percentage / 100);
}
public function formattedPrice(): string
{
return sprintf('$%.2f', $this->price);
}
}
$product = new Product(
id: 1,
name: 'Laptop',
price: 999.99,
createdAt: new \DateTimeImmutable(),
);
echo $product->formattedPrice(); // $999.99
$product->applyDiscount(10);
echo $product->formattedPrice(); // $899.99注意事项
- 类名大小写不敏感:
new simpleclass()和new SimpleClass()等价,但建议始终使用首字母大写。 - 对象是引用语义:赋值不会复制对象,需要用
clone创建副本。 - 避免动态属性:PHP 8.2+ 已弃用动态属性,应显式声明所有属性。
- 类型声明:PHP 7.4+ 支持属性类型声明,PHP 8.1+ 支持
readonly,建议总是使用。
最佳实践
- 使用构造器属性提升(PHP 8.0+)减少样板代码
- 为所有属性添加类型声明,在构造函数中完成初始化
- 不可变数据使用
readonly,确保对象创建后状态一致 - 使用
declare(strict_types=1)强制严格类型模式 - 类名使用大驼峰(PascalCase),方法名使用小驼峰(camelCase)
进阶用法
调试与测试技巧
<?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
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
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
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');