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
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();
}
}
🔍 编译期检查机制
检查流程
-
词法分析阶段
- 解析
extends和implements关键字 - 提取父类和接口的名称
- 解析
-
符号表构建
- 收集当前文件中所有已定义的类
- 查询 PHP 内置类列表
- 验证继承关系的合法性
-
错误报告
- 发现非法继承时立即报错
- 提供详细的错误位置和原因
检查代码位置
相关实现在 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`
诊断步骤:
- 检查
ParentClass是否在编译单元中定义 - 确认没有拼写错误
- 验证类的命名空间是否正确
解决方案:
// 确保父类在子类之前定义或在同一文件中
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 支持 | ❌ 不支持作为父类 | ✅ 完全支持 |
| 继承内部类 | ❌ 禁止 | ✅ 允许(部分) |
| 性能影响 | 无运行时开销 | 有运行时检查开销 |
📚 相关资源
- 类型系统: NATIVE_TYPES.md
- 语法限制: UNSUPPORTED_SYNTAX.md
- 编译模式: COMPILATION_MODES.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