PHP 访问修饰符
概述
访问修饰符(Visibility Modifiers)控制类成员(属性、方法、常量)的访问范围。PHP 提供三种修饰符:public、protected、private。
版本要求
- PHP 7.1+:常量支持访问修饰符
- 未指定修饰符时,默认为
public
基础概念
三种访问修饰符
| 修饰符 | 类内部 | 子类 | 外部 |
|---|---|---|---|
public | 可以 | 可以 | 可以 |
protected | 可以 | 可以 | 不可以 |
private | 可以 | 不可以 | 不可以 |
语法与代码
属性的访问控制
php
<?php
declare(strict_types=1);
class BankAccount
{
public string $owner;
protected float $balance;
private string $accountNumber;
public function __construct(string $owner, float $balance, string $accountNumber)
{
$this->owner = $owner;
$this->balance = $balance;
$this->accountNumber = $accountNumber;
}
public function getBalance(): float
{
return $this->balance;
}
public function deposit(float $amount): void
{
if ($amount <= 0) {
throw new \InvalidArgumentException('Amount must be positive');
}
$this->balance += $amount;
}
public function withdraw(float $amount): void
{
if ($amount > $this->balance) {
throw new \RuntimeException('Insufficient funds');
}
$this->balance -= $amount;
}
}
$account = new BankAccount('Alice', 1000.00, 'ACC-001');
echo $account->owner; // Alice(public)
// echo $account->balance; // Fatal error: protected
// echo $account->accountNumber; // Fatal error: private
echo $account->getBalance(); // 1000(通过 public 方法访问)方法的访问控制
php
<?php
declare(strict_types=1);
class AuthService
{
public function login(string $username, string $password): bool
{
$user = $this->findUser($username);
if ($user === null) {
return false;
}
return $this->verifyPassword($password, $user->passwordHash);
}
protected function findUser(string $username): ?\stdClass
{
return new \stdClass();
}
private function verifyPassword(string $password, string $hash): bool
{
return password_verify($password, $hash);
}
}
$service = new AuthService();
$service->login('alice', 'password'); // public 方法可调用
// $service->findUser('alice'); // Fatal error: protected继承中的访问控制
子类可以访问 public 和 protected 成员,不能访问 private 成员。
php
<?php
declare(strict_types=1);
class ParentClass
{
public string $publicProp = 'public';
protected string $protectedProp = 'protected';
private string $privateProp = 'private';
public function showAll(): void
{
echo $this->publicProp;
echo $this->protectedProp;
echo $this->privateProp;
}
}
class ChildClass extends ParentClass
{
public function showAccessible(): void
{
echo $this->publicProp; // OK
echo $this->protectedProp; // OK
// echo $this->privateProp; // Fatal error: private
}
}同类对象的私有访问
同一个类的不同实例可以互相访问 private 成员。
php
<?php
declare(strict_types=1);
class Wallet
{
private float $amount;
public function __construct(float $amount)
{
$this->amount = $amount;
}
public function transferTo(Wallet $target, float $amount): void
{
if ($amount > $this->amount) {
throw new \RuntimeException('Insufficient funds');
}
$this->amount -= $amount;
$target->amount += $amount; // 访问另一个实例的 private 属性
}
public function getBalance(): float
{
return $this->amount;
}
}
$walletA = new Wallet(100.0);
$walletB = new Wallet(0.0);
$walletA->transferTo($walletB, 50.0);
echo $walletA->getBalance(); // 50
echo $walletB->getBalance(); // 50详细说明
重写时的可见性规则
子类重写方法时,可见性不能比父类更严格(只能放宽或保持)。
php
<?php
declare(strict_types=1);
class BaseService
{
protected function execute(): void
{
echo "BaseService::execute\n";
}
}
class PublicService extends BaseService
{
// OK: protected -> public(放宽)
public function execute(): void
{
parent::execute();
echo "PublicService::execute\n";
}
}未声明修饰符的默认行为
| 成员类型 | 未声明修饰符时默认值 |
|---|---|
| 属性 | public(PHP 8.2+ 动态属性已弃用) |
| 方法 | public |
| 常量 | public(PHP 7.1 之前无修饰符) |
实战示例
封装学生成绩管理
php
<?php
declare(strict_types=1);
class Student
{
private string $name;
private array $grades = [];
public function __construct(string $name)
{
$this->name = $name;
}
public function getName(): string
{
return $this->name;
}
public function addGrade(string $subject, float $grade): void
{
if ($grade < 0 || $grade > 100) {
throw new \InvalidArgumentException("Invalid grade: {$grade}");
}
$this->grades[$subject] = $grade;
}
public function getAverage(): float
{
if (empty($this->grades)) {
return 0.0;
}
return array_sum($this->grades) / count($this->grades);
}
}
$student = new Student('Alice');
$student->addGrade('Math', 95);
$student->addGrade('English', 88);
echo $student->getAverage(); // 91.5注意事项
- 私有方法不受签名兼容性规则约束:子类可以定义完全不同的私有方法
- 构造函数不受约束:子类构造函数签名可以完全不同
- 同类实例可互相访问私有成员:这是 PHP 的特殊设计
- 默认 public 可能导致封装不足:建议总是显式声明
最佳实践
- 属性优先 private:通过方法控制访问,提供更好的封装
- 方法按需公开:只暴露必要的 public 接口
- protected 用于继承扩展:子类需要访问但外部不需要的成员
- PHP 8.4+ 考虑 private(set):替代手动 getter
- 始终显式声明修饰符:避免依赖默认 public
进阶用法
调试与测试技巧
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');