Skip to content

PHP 方法重写

概述

方法重写(Method Overriding)允许子类提供父类方法的替代实现。重写时必须遵循 PHP 的签名兼容性规则,包括参数类型、返回值类型和可见性约束。

版本要求

  • PHP 7.4+:参数类型逆变
  • PHP 8.0+:返回值类型协变
  • PHP 8.3+#[\Override] 注解

基础概念

重写规则

子类重写方法时必须满足以下条件:

  1. 参数数量:不能减少必选参数
  2. 参数类型:可以与父类相同或更宽泛(逆变)
  3. 返回类型:可以与父类相同或更具体(协变)
  4. 可见性:不能比父类更严格

语法与代码

基本重写

php
<?php
declare(strict_types=1);

class Animal
{
    public function speak(): string
    {
        return 'Some sound';
    }
}

class Dog extends Animal
{
    public function speak(): string
    {
        return 'Woof!';
    }
}

echo (new Dog())->speak(); // Woof!

参数放宽规则

子类方法可以将父类的必选参数变为可选参数,或添加新的可选参数。

php
<?php
declare(strict_types=1);

class Processor
{
    public function process(string $data): string
    {
        return strtoupper($data);
    }
}

class ExtendedProcessor extends Processor
{
    // OK: 添加可选参数
    public function process(string $data, bool $encode = false): string
    {
        $result = strtoupper($data);
        return $encode ? base64_encode($result) : $result;
    }
}

协变返回类型(PHP 8.0+)

子类方法的返回类型可以比父类更具体(子类型)。

php
<?php
declare(strict_types=1);

class Animal
{
    public function getOwner(): ?Person
    {
        return null;
    }
}

class Dog extends Animal
{
    // 协变:返回更具体的 DogOwner
    public function getOwner(): ?DogOwner
    {
        return null;
    }
}

class Person {}
class DogOwner extends Person {}

#[\Override] 注解(PHP 8.3+)

#[\Override] 注解明确标记方法意图重写父类方法。

php
<?php
declare(strict_types=1);

class BaseService
{
    protected function execute(): void
    {
        echo "BaseService::execute\n";
    }
}

class UserService extends BaseService
{
    #[\Override]
    protected function execute(): void
    {
        parent::execute();
        echo "UserService::execute\n";
    }

    // Error: #[\Override] 目标方法不存在于父类
    // #[\Override]
    // protected function notInParent(): void {}
}

参数逆变

子类方法的参数类型可以比父类更宽泛。

php
<?php
declare(strict_types=1);

class AnimalShelter
{
    public function adopt(Dog $dog): void
    {
        echo "Adopted a dog\n";
    }
}

class GeneralShelter extends AnimalShelter
{
    // 逆变:接受更宽泛的 Animal
    public function adopt(Animal $animal): void
    {
        echo "Adopted an animal\n";
    }
}

class Animal {}
class Dog extends Animal {}

详细说明

签名不兼容的例子

php
<?php
declare(strict_types=1);

class Base
{
    public function foo(int $a = 5): void {}
}

// Fatal: 移除参数
// class Bad1 extends Base { public function foo(): void {} }

// Fatal: 可选参数变必选
// class Bad2 extends Base { public function foo(int $a): void {} }

命名参数与重写

重写方法时不要重命名参数,否则会导致命名参数调用时出错。

php
<?php
declare(strict_types=1);

class A
{
    public function test(string $foo, string $bar): void {}
}

class B extends A
{
    // 不推荐重命名参数
    public function test(string $a, string $b): void {}
}

$obj = new B();
// Fatal error: Unknown named parameter $foo
// $obj->test(foo: 'hello', bar: 'world');

实战示例

模板方法模式

php
<?php
declare(strict_types=1);

abstract class DataExporter
{
    final public function export(array $data): string
    {
        $formatted = $this->formatData($data);
        $encoded = $this->encode($formatted);
        return $this->addHeaders($encoded);
    }

    abstract protected function formatData(array $data): string;
    abstract protected function encode(string $data): string;

    protected function addHeaders(string $data): string
    {
        return $data;
    }
}

class JsonExporter extends DataExporter
{
    #[\Override]
    protected function formatData(array $data): string
    {
        return json_encode($data, JSON_PRETTY_PRINT);
    }

    #[\Override]
    protected function encode(string $data): string
    {
        return base64_encode($data);
    }
}

注意事项

  1. 构造函数不受约束:子类构造函数可以完全不同
  2. private 方法不受约束:子类定义同名 private 方法不视为重写
  3. 命名参数重命名危险:保持参数名称一致
  4. #[\Override] 推荐使用:PHP 8.3+ 明确重写意图

最佳实践

  • PHP 8.3+ 使用 #[\Override]:防止意外的方法签名错误
  • 保持参数名称一致:避免命名参数调用时的错误
  • 使用 parent:: 保留父类逻辑:扩展而非替换
  • final 防止关键方法被重写:保证核心行为一致

进阶用法

调试与测试技巧

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');

参考链接