Skip to content

PHP 访问修饰符

概述

访问修饰符(Visibility Modifiers)控制类成员(属性、方法、常量)的访问范围。PHP 提供三种修饰符:publicprotectedprivate

版本要求

  • 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

继承中的访问控制

子类可以访问 publicprotected 成员,不能访问 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

注意事项

  1. 私有方法不受签名兼容性规则约束:子类可以定义完全不同的私有方法
  2. 构造函数不受约束:子类构造函数签名可以完全不同
  3. 同类实例可互相访问私有成员:这是 PHP 的特殊设计
  4. 默认 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 修正
性能下降索引缺失/数据量大添加索引,优化查询
数据不一致并发冲突/事务残留使用锁机制和事务
内存溢出大数据集/未释放资源增大内存限制,分批处理

故障排除步骤

  1. 检查错误日志和异常信息
  2. 确认配置和环境是否正确
  3. 使用调试工具逐步排查
  4. 参考官方文档查找已知问题

版本兼容性说明

功能最低版本说明
基础功能PHP 8.1本文档基准版本
只读属性PHP 8.1public readonly 修饰符
枚举类型PHP 8.1enum 类型和 match 表达式
FiberPHP 8.1协程/轻量级并发
命名参数PHP 8.0foo(arg_name: value)
联合类型PHP 8.0`int
Null 安全运算符PHP 8.0$obj?->method()
析构器 promotionPHP 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');

参考链接