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.
 
 

570 lines
12 KiB

# AOT 编译器类继承规则
## 📋 概述
AOT(Ahead-Of-Time)编译器在编译阶段会对类的继承关系进行严格检查。与传统的 PHP 运行时检查不同,AOT 编译器要求在**编译期**就能确定所有父类和接口的存在性。
---
## ⚠ 核心规则
### 规则一:只能继承已存在的类
```php
// ✅ 正确:继承 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 运行时载入的类
```php
// ❌ 错误:依赖 autoload 的类
class MyClass extends ExternalLibrary\BaseClass {}
// 如果 BaseClass 需要通过 autoload 在运行时加载,编译将失败
// ✅ 正确:使用接口代替
interface BaseInterface {}
class MyClass implements BaseInterface {}
```
### 规则三:不能继承内部类(Internal Class)
```php
// ❌ 错误:继承 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. **词法分析阶段**
- 解析 `extends``implements` 关键字
- 提取父类和接口的名称
2. **符号表构建**
- 收集当前文件中所有已定义的类
- 查询 PHP 内置类列表
- 验证继承关系的合法性
3. **错误报告**
- 发现非法继承时立即报错
- 提供详细的错误位置和原因
### 检查代码位置
相关实现在 `src/Php/Translator.php`
```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
<?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
<?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
<?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
<?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
<?php
// ✅ 先定义基类
class NonExistentBase {
// ...
}
class MyClass extends NonExistentBase {
// ...
}
```
### 示例二:依赖 autoload 的外部类
```php
<?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
<?php
interface BaseServiceInterface {
public function process(): mixed;
}
class MyService implements BaseServiceInterface {
public function process(): mixed {
// 实现逻辑
}
}
```
**方案 B - 使用组合**:
```php
<?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
<?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
<?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. 优先使用接口而非继承
```php
// ❌ 不推荐:深度继承链
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. 使用组合代替继承
```php
// ❌ 不推荐:继承内部类
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
<?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. 避免循环依赖
```php
// ❌ 错误:循环继承会导致编译失败
// 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. 验证类的命名空间是否正确
**解决方案**:
```php
// 确保父类在子类之前定义或在同一文件中
class ParentClass {}
class MyClass extends ParentClass {}
```
### 问题二:无法使用第三方库的类作为父类
**症状**:
需要使用外部库的功能,但不能直接继承
**解决方案**:
**方法 A - 适配器模式**:
```php
class ExternalAdapter {
private ExternalClass $external;
public function __construct() {
$this->external = new ExternalClass();
}
public function doWork(): mixed {
return $this->external->execute();
}
}
```
**方法 B - 委托模式**:
```php
class Wrapper {
public function __call(string $name, array $args): mixed {
$external = new ExternalClass();
return call_user_func_array([$external, $name], $args);
}
}
```
### 问题三:需要扩展 PHP 内置功能
**症状**:
想要增强或修改 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 支持** | ❌ 不支持作为父类 | ✅ 完全支持 |
| **继承内部类** | ❌ 禁止 | ✅ 允许(部分) |
| **性能影响** | 无运行时开销 | 有运行时检查开销 |
---
## 📚 相关资源
- **类型系统**: [NATIVE_TYPES.md](NATIVE_TYPES.md)
- **兼容性限制**: [INCOMPATIBLE_PHP_FEATURES.md](INCOMPATIBLE_PHP_FEATURES.md)
- **编译模式**: [COMPILATION_MODES.md](COMPILATION_MODES.md)
- **快速入门**: [QUICKSTART.md](QUICKSTART.md)
---
## ❓ 常见问题
### 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