PHP Final 关键字
概述
final 关键字用于阻止类被继承或方法被重写。PHP 8.0 起还支持 final 属性。使用 final 可以明确表达设计意图,防止意外的继承和重写。
版本要求
- PHP 8.0+:支持 final 属性
- PHP 8.1+:支持 final 常量
基础概念
final 类
final 类不能被继承。
php
<?php
declare(strict_types=1);
final class ImmutableConfig
{
public function __construct(
public readonly array $settings,
) {}
}
// Fatal error: Class ChildConfig may not inherit from final class
// class ChildConfig extends ImmutableConfig {}final 方法
final 方法不能被子类重写。
php
<?php
declare(strict_types=1);
class SecurityService
{
final public function authenticate(string $token): bool
{
return hash_equals($token, $this->getSecretKey());
}
protected function getSecretKey(): string
{
return 'secret';
}
}
class CustomSecurity extends SecurityService
{
// Fatal: Cannot override final method
// public function authenticate(string $token): bool {}
// OK: 非final方法可以重写
protected function getSecretKey(): string
{
return 'custom-secret';
}
}语法与代码
final 属性(PHP 8.0+)
final 属性不能被子类重新声明。
php
<?php
declare(strict_types=1);
class BaseEntity
{
public function __construct(
public readonly int $id,
final public readonly string $type = 'entity',
) {}
}
class UserEntity extends BaseEntity
{
public function __construct(
int $id,
public readonly string $name,
) {
parent::__construct($id);
}
}final 常量(PHP 8.1+)
php
<?php
declare(strict_types=1);
class AppVersion
{
final public const VERSION = '1.0.0';
public const BUILD = '20240101';
}
class CustomApp extends AppVersion
{
// Fatal: Cannot override final constant
// public const VERSION = '2.0.0';
public const BUILD = '20240601'; // OK
}详细说明
何时使用 final
推荐使用 final 的场景:
- 安全敏感的方法(如认证、加密)
- 工具类(无子类需求)
- 值对象(不可变且无扩展点)
- 框架中的核心类
不推荐 final 的场景:
- 需要被扩展的基类
- 可能有多种实现的抽象
- 库的公共 API(可能限制用户扩展)
实战示例
不可变值对象
php
<?php
declare(strict_types=1);
final class Money
{
public function __construct(
public readonly int $amount,
public readonly string $currency = 'CNY',
) {
if ($amount < 0) {
throw new \InvalidArgumentException('Amount cannot be negative');
}
}
public function add(self $other): self
{
if ($other->currency !== $this->currency) {
throw new \InvalidArgumentException('Currency mismatch');
}
return new self($this->amount + $other->amount, $this->currency);
}
public function formatted(): string
{
return sprintf('%s %.2f', $this->currency, $this->amount / 100);
}
}
$price = new Money(10000);
$discount = new Money(1500);
echo $price->subtract($discount)->formatted(); // CNY 85.00注意事项
- final 类不能被继承:包括被测试中的 mock 替代
- final 方法不能被重写:即使签名完全相同
- final 属性不能被子类重新声明:PHP 8.0+
- 过度使用 final 会降低灵活性:需要权衡安全性和扩展性
最佳实践
- 默认使用 final 方法:除非明确需要被重写
- 值对象标记为 final:确保不可变性和不变式
- 安全关键代码用 final:认证、加密、权限检查
- 私有方法不需要 final:private 方法本身就无法被重写
进阶用法
调试与测试技巧
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');