TypePHP 编译器 https://swoole.com/aot/
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 

12 KiB

AOT 编译器类继承规则

📋 概述

AOT(Ahead-Of-Time)编译器在编译阶段会对类的继承关系进行严格检查。与传统的 PHP 运行时检查不同,AOT 编译器要求在编译期就能确定所有父类和接口的存在性。


核心规则

规则一:只能继承已存在的类

// ✅ 正确:继承 PHP 内置类
class MyException extends Exception {}

// ✅ 正确:继承同一项目中定义的类
class BaseClass {}
class ChildClass extends BaseClass {}

// ❌ 错误:继承不存在的类
class MyClass extends NonExistentClass {}
// 编译错误:Class `MyClass` inherits from a non-existent class `NonExistentClass`

规则二:不能继承 autoload 运行时载入的类

// ❌ 错误:依赖 autoload 的类
class MyClass extends ExternalLibrary\BaseClass {}
// 如果 BaseClass 需要通过 autoload 在运行时加载,编译将失败

// ✅ 正确:使用接口代替
interface BaseInterface {}
class MyClass implements BaseInterface {}

规则三:不能继承内部类(Internal Class)

// ❌ 错误:继承 PHP 内部类
class MyArray extends ArrayObject {}
// 编译错误:Class `MyArray` Cannot inherit built-in class `ArrayObject`

// ✅ 正确:使用组合模式
class MyContainer {
    private ArrayObject $storage;
    
    public function __construct() {
        $this->storage = new ArrayObject();
    }
}

🔍 编译期检查机制

检查流程

  1. 词法分析阶段

    • 解析 extendsimplements 关键字
    • 提取父类和接口的名称
  2. 符号表构建

    • 收集当前文件中所有已定义的类
    • 查询 PHP 内置类列表
    • 验证继承关系的合法性
  3. 错误报告

    • 发现非法继承时立即报错
    • 提供详细的错误位置和原因

检查代码位置

相关实现在 src/Php/Translator.php

if ($extends) {
    $parentClass = $this->getParentClass($class->extends);
    if ($this->hasNativeClass($parentClass)) {
        // 父类是已定义的 native 类
        $parent = $this->getClassDef($parentClass);
        if ($parent->flags & Modifiers::FINAL) {
            $this->fatalError($class, "Class `{$this->class}` cannot extend final class `{$parentClass}`");
        }
        $this->classDef->extends = $parentClass;
    } else {
        // 检查是否为 PHP 内部类
        if (Reflection::isInternalClass($parentClass)) {
            $this->fatalError($class, "Class `{$this->class}` Cannot inherit built-in class `$parentClass`");
        } else {
            $this->fatalError($class, "Class `{$this->class}` inherits from a non-existent class `$parentClass`");
        }
    }
}

🎯 合法继承示例

示例一:继承项目内定义的类

<?php
// 定义基类
abstract class Shape {
    protected float $area = 0.0;
    
    abstract public function calculateArea(): float;
    
    public function getArea(): float {
        return $this->area;
    }
}

// 继承基类(✅ 允许)
class Circle extends Shape {
    private float $radius;
    
    public function __construct(float $radius) {
        $this->radius = $radius;
    }
    
    public function calculateArea(): float {
        $this->area = M_PI * $this->radius ** 2;
        return $this->area;
    }
}

function main() {
    $circle = new Circle(5);
    echo "Circle area: " . $circle->getArea() . "\n";
}

示例二:实现接口

<?php
// 定义接口
interface Renderable {
    public function render(): string;
}

interface Serializable {
    public function serialize(): string;
}

// 实现多个接口(✅ 允许)
class Product implements Renderable, Serializable {
    private string $name;
    private float $price;
    
    public function __construct(string $name, float $price) {
        $this->name = $name;
        $this->price = $price;
    }
    
    public function render(): string {
        return "<div>{$this->name} - \${$this->price}</div>";
    }
    
    public function serialize(): string {
        return json_encode(['name' => $this->name, 'price' => $this->price]);
    }
}

function main() {
    $product = new Product('Widget', 19.99);
    echo $product->render() . "\n";
    echo $product->serialize() . "\n";
}

示例三:多层继承

<?php
// 基础控制器
abstract class Controller {
    protected array $data = [];
    
    public function setData(string $key, mixed $value): void {
        $this->data[$key] = $value;
    }
    
    abstract public function execute(): void;
}

// 抽象的用户控制器
abstract class UserController extends Controller {
    protected ?string $userId = null;
    
    public function setUserId(string $id): void {
        $this->userId = $id;
    }
}

// 具体的用户列表控制器(✅ 允许)
class UserListController extends UserController {
    private array $users = [];
    
    public function execute(): void {
        echo "User List for ID: {$this->userId}\n";
        foreach ($this->users as $user) {
            echo " - {$user}\n";
        }
    }
}

function main() {
    $controller = new UserListController();
    $controller->setUserId('123');
    $controller->execute();
}

非法继承示例

示例一:继承不存在的类

<?php
// ❌ 错误:NonExistentBase 未定义
class MyClass extends NonExistentBase {
    // ...
}

function main() {
    $obj = new MyClass();
}
?>

编译错误:

Fatal error: Class `MyClass` inherits from a non-existent class `NonExistentBase` 
in /path/to/file.php on line X

解决方案:

<?php
// ✅ 先定义基类
class NonExistentBase {
    // ...
}

class MyClass extends NonExistentBase {
    // ...
}

示例二:依赖 autoload 的外部类

<?php
// ❌ 错误:ExternalLib\BaseClass 需要通过 autoload 加载
use ExternalLib\BaseClass;

class MyService extends BaseClass {
    // ...
}

function main() {
    $service = new MyService();
}
?>

编译错误:

Fatal error: Class `MyService` inherits from a non-existent class `ExternalLib\BaseClass`
in /path/to/file.php on line X

解决方案:

方案 A - 使用接口:

<?php
interface BaseServiceInterface {
    public function process(): mixed;
}

class MyService implements BaseServiceInterface {
    public function process(): mixed {
        // 实现逻辑
    }
}

方案 B - 使用组合:

<?php
use ExternalLib\BaseClass;

class MyService {
    private BaseClass $base;
    
    public function __construct() {
        $this->base = new BaseClass();
    }
    
    public function process(): mixed {
        return $this->base->doSomething();
    }
}

示例三:继承 PHP 内部类

<?php
// ❌ 错误:不能继承 PDO
class MyDatabase extends PDO {
    // ...
}

function main() {
    $db = new MyDatabase('mysql:host=localhost;dbname=test');
}
?>

编译错误:

Fatal error: Class `MyDatabase` Cannot inherit built-in class `PDO`
in /path/to/file.php on line X

解决方案:

<?php
// ✅ 使用组合模式
class MyDatabase {
    private PDO $connection;
    
    public function __construct(string $dsn) {
        $this->connection = new PDO($dsn);
    }
    
    public function query(string $sql): array {
        return $this->connection->query($sql)->fetchAll();
    }
}

💡 最佳实践

1. 优先使用接口而非继承

// ❌ 不推荐:深度继承链
class A {}
class B extends A {}
class C extends B {}
class D extends C {}

// ✅ 推荐:使用接口
interface ServiceInterface {}
class UserService implements ServiceInterface {}
class ProductService implements ServiceInterface {}

2. 使用组合代替继承

// ❌ 不推荐:继承内部类
class MyCollection extends ArrayCollection {
    // ...
}

// ✅ 推荐:组合模式
class Collection {
    private ArrayCollection $items;
    
    public function __construct() {
        $this->items = new ArrayCollection();
    }
    
    public function add(mixed $item): void {
        $this->items->add($item);
    }
}

3. 在项目内定义可继承的基类

<?php
// base.stub.php - 定义可继承的基类

abstract class BaseController {
    protected array $middlewares = [];
    
    abstract public function handle(): void;
    
    protected function middleware(string $name): void {
        echo "Running middleware: {$name}\n";
    }
}

// api.php - 继承项目内基类

class UserController extends BaseController {
    public function handle(): void {
        $this->middleware('auth');
        echo "Handling user request\n";
    }
}

function main() {
    $controller = new UserController();
    $controller->handle();
}

4. 避免循环依赖

// ❌ 错误:循环继承会导致编译失败
// file1.php
class A extends B {}

// file2.php  
class B extends A {}

// ✅ 正确:单向继承
// base.php
class Base {}

// child.php
class Child extends Base {}

🔧 故障排除

问题一:编译时报 "non-existent class"

症状:

Fatal error: Class `MyClass` inherits from a non-existent class `ParentClass`

诊断步骤:

  1. 检查 ParentClass 是否在编译单元中定义
  2. 确认没有拼写错误
  3. 验证类的命名空间是否正确

解决方案:

// 确保父类在子类之前定义或在同一文件中
class ParentClass {}
class MyClass extends ParentClass {}

问题二:无法使用第三方库的类作为父类

症状: 需要使用外部库的功能,但不能直接继承

解决方案:

方法 A - 适配器模式:

class ExternalAdapter {
    private ExternalClass $external;
    
    public function __construct() {
        $this->external = new ExternalClass();
    }
    
    public function doWork(): mixed {
        return $this->external->execute();
    }
}

方法 B - 委托模式:

class Wrapper {
    public function __call(string $name, array $args): mixed {
        $external = new ExternalClass();
        return call_user_func_array([$external, $name], $args);
    }
}

问题三:需要扩展 PHP 内置功能

症状: 想要增强或修改 PHP 内置类的行为

解决方案:

使用装饰器模式:

class EnhancedStorage {
    private ArrayObject $storage;
    
    public function __construct() {
        $this->storage = new ArrayObject();
    }
    
    public function set(string $key, mixed $value): void {
        echo "Setting {$key}\n";
        $this->storage[$key] = $value;
    }
    
    public function get(string $key): mixed {
        echo "Getting {$key}\n";
        return $this->storage[$key] ?? null;
    }
}

📊 对比:AOT vs 传统 PHP

特性 AOT 编译器 传统 PHP
检查时机 编译期 运行时
错误发现 编译时立即报错 执行到代码时才报错
autoload 支持 不支持作为父类 完全支持
继承内部类 禁止 允许(部分)
性能影响 无运行时开销 有运行时检查开销

📚 相关资源


常见问题

Q: 为什么 AOT 编译器不允许继承 autoload 类?

A: AOT 编译器在编译阶段就需要知道所有类的完整结构。autoload 是在运行时触发的机制,编译期无法获取类的定义信息,因此无法生成正确的 C++ 代码。

Q: 如何判断一个类是否是内部类?

A: AOT 编译器使用 Reflection::isInternalClass() 函数来判断。该函数会扫描所有已声明的类,并使用 ReflectionClass::isInternal() 方法检测。

Q: 可以实现接口吗?

A: 可以!AOT 编译器完全支持实现接口。接口只定义方法签名,不涉及具体实现,因此可以在编译期验证。

Q: trait 可以使用吗?

A: Trait 的使用同样受到限制。Trait 必须在编译期可见,不能依赖 autoload 动态加载。


最后更新: 2024 年 3 月 20 日
适用版本: PHP AOT Compiler v1.x