|
|
|
|
@ -0,0 +1,570 @@ |
|
|
|
|
# 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) |
|
|
|
|
- **语法限制**: [UNSUPPORTED_SYNTAX.md](UNSUPPORTED_SYNTAX.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 |